Initial commit: Cyrene Plugins SDK + Plugin Manager
Extracted from Cyrene main repo (backend/pkg/plugins + backend/plugin-manager). Contains SDK interfaces (Plugin/Tool/HostAPI), 13 built-in plugins, ToolRegistry with call log ring buffer, and Plugin Manager REST API service. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user