Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(new-plugin): eliza samsung smarthings plugin #2678

Merged
merged 7 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/client-eliza-home/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@elizaos/client-eliza-home",
"version": "0.1.0",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*"
},
"devDependencies": {
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"lint": "eslint . --fix"
}
}
109 changes: 109 additions & 0 deletions packages/client-eliza-home/src/actions/control_device.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
Action,
ActionExample,
Content,
HandlerCallback,
IAgentRuntime,
Memory,
State,
} from "@elizaos/core";
import { SmartHomeManager } from "../smart_home.ts";
import { smartThingsApi } from "../services/smart_things_api.ts";
import { deviceStateProvider } from "../providers/device_state.ts";

export const controlDeviceAction = {
name: "CONTROL_DEVICE",
similes: ["DEVICE_CONTROL", "SMART_HOME_CONTROL", "HOME_CONTROL"],
description: "Controls smart home devices with specific commands",
validate: async (runtime: IAgentRuntime, message: Memory) => {
const keywords = [
"turn on",
"turn off",
"switch",
"toggle",
"set",
"change",
"adjust",
"dim",
"brighten",
"lock",
"unlock",
];
return keywords.some(keyword =>
message.content.text.toLowerCase().includes(keyword)
);
},
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
options: any,
callback: HandlerCallback
) => {
const smartHomeManager = new SmartHomeManager(runtime);
const result = await smartHomeManager.handleCommand(message.content.text, message.userId);

const response: Content = {
text: `Command executed: ${result.message || "Success"}`,
action: "DEVICE_CONTROL_RESPONSE",
source: "home-assistant"
};

await callback(response);
return response;
},
examples: [
[
{
user: "{{user1}}",
content: {
text: "Turn on the living room lights",
},
},
{
user: "{{user2}}",
content: {
text: "I'll turn on the living room lights for you",
action: "CONTROL_DEVICE",
},
},
],
] as ActionExample[][],
} as Action;

export async function controlDevice(deviceId: string, command: string, args: any = {}) {
try {
// Map internal commands to SmartThings commands
const smartThingsCommand = mapCommand(command, args);

await smartThingsApi.devices.executeCommand(deviceId, smartThingsCommand);

// Update local device state
await deviceStateProvider.updateDeviceState(deviceId, command, args);

} catch (error) {
console.error(`Failed to control device ${deviceId}:`, error);
throw error;
}
}

function mapCommand(command: string, args: any) {
// Map our internal commands to SmartThings command format
switch (command) {
case 'turnOn':
return { capability: 'switch', command: 'on' };
case 'turnOff':
return { capability: 'switch', command: 'off' };
case 'setLevel':
return {
capability: 'switchLevel',
command: 'setLevel',
arguments: [args.level]
};
// ... map other commands
default:
throw new Error(`Unknown command: ${command}`);
}
}

export default controlDeviceAction;
113 changes: 113 additions & 0 deletions packages/client-eliza-home/src/actions/discover_devices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
Action,
ActionExample,
Content,
HandlerCallback,
IAgentRuntime,
Memory,
State,
} from "@elizaos/core";
import { EntityManager } from "../entities.ts";
import { CAPABILITIES } from '../capabilities';
import { smartThingsApi } from "../services/smart_things_api.ts";

export const discoverDevicesAction = {
name: "DISCOVER_DEVICES",
similes: ["SCAN_DEVICES", "FIND_DEVICES", "LIST_DEVICES"],
description: "Discovers and lists all available smart home devices",
validate: async (runtime: IAgentRuntime, message: Memory) => {
const keywords = [
"discover",
"find",
"scan",
"list",
"show",
"what",
"devices",
"lights",
"switches",
];
return keywords.some(keyword =>
message.content.text.toLowerCase().includes(keyword)
);
},
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
options: any,
callback: HandlerCallback
) => {
const entityManager = new EntityManager(runtime);
await entityManager.discoverEntities();

const entities = entityManager.getAllEntities();
const deviceList = entities
.map(entity => `- ${entity.name} (${entity.entityId}): ${entity.state}`)
.join('\n');

const response: Content = {
text: `Here are all the available devices:\n\n${deviceList}`,
action: "DEVICE_LIST_RESPONSE",
source: "home-assistant"
};

await callback(response);
return response;
},
examples: [
[
{
user: "{{user1}}",
content: {
text: "What devices do you see?",
},
},
{
user: "{{user2}}",
content: {
text: "Let me check what devices are available...",
action: "DISCOVER_DEVICES",
},
},
],
] as ActionExample[][],
} as Action;

export default discoverDevicesAction;

export async function discoverDevices() {
// Implementation to discover SmartThings devices
try {
const devices = await smartThingsApi.devices.list();

return devices.map(device => ({
id: device.deviceId,
name: device.label || device.name,
capabilities: device.capabilities.map(cap => ({
id: cap.id,
version: cap.version
})),
room: device.roomId,
status: parseDeviceStatus(device.status)
}));
} catch (error) {
console.error('Failed to discover devices:', error);
throw error;
}
}

function parseDeviceStatus(status: any) {
// Convert SmartThings status format to our internal format
const deviceStatus: any = {};

if (status.switch) {
deviceStatus.switch = status.switch.value;
}
if (status.level) {
deviceStatus.level = status.level.value;
}
// ... parse other status values

return deviceStatus;
}
109 changes: 109 additions & 0 deletions packages/client-eliza-home/src/capabilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { IAgentRuntime } from "@elizaos/core";

// Define SmartThings capability interfaces
export interface SmartThingsCapability {
id: string;
version: number;
}

export const CAPABILITIES = {
// Basic capabilities
SWITCH: { id: 'switch', version: 1 },
POWER_METER: { id: 'powerMeter', version: 1 },
ENERGY_METER: { id: 'energyMeter', version: 1 },

// Climate capabilities
THERMOSTAT: { id: 'thermostat', version: 1 },
TEMPERATURE_MEASUREMENT: { id: 'temperatureMeasurement', version: 1 },
HUMIDITY_MEASUREMENT: { id: 'humidityMeasurement', version: 1 },

// Lighting capabilities
SWITCH_LEVEL: { id: 'switchLevel', version: 1 },
COLOR_CONTROL: { id: 'colorControl', version: 1 },
COLOR_TEMPERATURE: { id: 'colorTemperature', version: 1 },

// Security capabilities
LOCK: { id: 'lock', version: 1 },
MOTION_SENSOR: { id: 'motionSensor', version: 1 },
CONTACT_SENSOR: { id: 'contactSensor', version: 1 },
PRESENCE_SENSOR: { id: 'presenceSensor', version: 1 },

// Media capabilities
MEDIA_PLAYBACK: { id: 'mediaPlayback', version: 1 },
VOLUME: { id: 'volume', version: 1 },

// Window/Door capabilities
WINDOW_SHADE: { id: 'windowShade', version: 1 },
GARAGE_DOOR: { id: 'garageDoor', version: 1 },

// Fan capabilities
FAN_SPEED: { id: 'fanSpeed', version: 1 },

// Battery capabilities
BATTERY: { id: 'battery', version: 1 },
} as const;

export type CapabilityType = keyof typeof CAPABILITIES;

export interface Capability {
interface: string;
version: string;
type: string;
properties: {
supported: Array<{
name: string;
}>;
proactivelyReported: boolean;
retrievable: boolean;
};
}

export class CapabilityManager {
private runtime: IAgentRuntime;
private capabilities: Map<string, Capability>;

constructor(runtime: IAgentRuntime) {
this.runtime = runtime;
this.capabilities = new Map();
this.initializeCapabilities();
}

private initializeCapabilities() {
// Add standard capabilities
this.addCapability({
interface: "Alexa.PowerController",
version: "3",
type: "AlexaInterface",
properties: {
supported: [{ name: "powerState" }],
proactivelyReported: true,
retrievable: true,
},
});

this.addCapability({
interface: "Alexa.BrightnessController",
version: "3",
type: "AlexaInterface",
properties: {
supported: [{ name: "brightness" }],
proactivelyReported: true,
retrievable: true,
},
});

// Add more capabilities as needed
}

addCapability(capability: Capability) {
this.capabilities.set(capability.interface, capability);
}

getCapability(interfaceName: string): Capability | undefined {
return this.capabilities.get(interfaceName);
}

getAllCapabilities(): Capability[] {
return Array.from(this.capabilities.values());
}
}
Loading
Loading