package iotquery import ( "context" "fmt" "git.yeij.top/AskaEth/Cyrene-Plugins/sdk" ) // IoTClient is the interface for IoT device access. type IoTClient interface { GetAllDevices(ctx context.Context) ([]sdk.IoTDeviceState, error) GetDevice(ctx context.Context, deviceID string) (*sdk.IoTDeviceState, error) } type IoTQueryPlugin struct { sdk.BasePlugin iotClient IoTClient } func NewIoTQueryPlugin(client IoTClient) *IoTQueryPlugin { return &IoTQueryPlugin{iotClient: client} } func (p *IoTQueryPlugin) Metadata() sdk.PluginMetadata { return sdk.PluginMetadata{ Name: "iot_query", DisplayName: "IoT Device Query", Version: "1.0.0", Description: "Query smart home device status (single device or all devices)", Category: "iot", Author: sdk.PluginAuthor{Name: "Cyrene Team"}, } } func (p *IoTQueryPlugin) Tools() []sdk.Tool { return []sdk.Tool{&IoTQueryTool{iotClient: p.iotClient}} } type IoTQueryTool struct { sdk.BaseTool iotClient IoTClient } func (t *IoTQueryTool) Definition() sdk.ToolDefinition { return sdk.ToolDefinition{ ID: "iot_query", Name: "iot_query", DisplayName: "IoT Device Query", Description: "Query smart home device status. Device status is typically auto-injected; use this only when status is stale.", Category: "iot", Complexity: sdk.ComplexitySimple, Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{"device_id": map[string]interface{}{"type": "string"}}, }, } } func (t *IoTQueryTool) Validate(args map[string]interface{}) error { return nil } func (t *IoTQueryTool) Execute(ctx context.Context, args map[string]interface{}) (*sdk.ToolResult, error) { if t.iotClient == nil { return &sdk.ToolResult{ToolName: "iot_query", Success: false, Error: "IoT client not configured"}, nil } deviceID, _ := args["device_id"].(string) if deviceID != "" { dev, err := t.iotClient.GetDevice(ctx, deviceID) if err != nil { return &sdk.ToolResult{ToolName: "iot_query", Success: false, Error: err.Error()}, nil } return &sdk.ToolResult{ToolName: "iot_query", Success: true, Output: formatDevice(dev)}, nil } devices, err := t.iotClient.GetAllDevices(ctx) if err != nil { return &sdk.ToolResult{ToolName: "iot_query", Success: false, Error: err.Error()}, nil } if len(devices) == 0 { return &sdk.ToolResult{ToolName: "iot_query", Success: true, Output: "No devices found"}, nil } var out string for _, d := range devices { out += formatDeviceLine(&d) + "\n" } return &sdk.ToolResult{ToolName: "iot_query", Success: true, Output: out}, nil } func formatDevice(d *sdk.IoTDeviceState) string { emoji := deviceEmoji(d.Type) return fmt.Sprintf("%s %s (%s)\n Status: %s\n ID: %s", emoji, d.Name, d.Type, d.Status, d.ID) } func formatDeviceLine(d *sdk.IoTDeviceState) string { emoji := deviceEmoji(d.Type) switch d.Type { case "light": return fmt.Sprintf("%s %s: %s (brightness: %d, color: %s)", emoji, d.Name, d.Status, d.Brightness, d.Color) case "ac": return fmt.Sprintf("%s %s: %s (mode: %s, temp: %.1fC)", emoji, d.Name, d.Status, d.Mode, d.Temperature) case "curtain": return fmt.Sprintf("%s %s: %s (position: %d%%)", emoji, d.Name, d.Status, d.Position) case "sensor": return fmt.Sprintf("%s %s: %.1f%s", emoji, d.Name, d.Value, d.Unit) case "lock": return fmt.Sprintf("%s %s: %s (battery: %d%%)", emoji, d.Name, d.Status, d.Battery) default: return fmt.Sprintf("%s %s: %s", emoji, d.Name, d.Status) } } func deviceEmoji(t string) string { switch t { case "light": return "\U0001F4A1" case "ac": return "❄️" case "curtain": return "\U0001F3E0" case "sensor": return "\U0001F4CA" case "lock": return "\U0001F512" default: return "\U0001F4E6" } }