@coffeerunhobby/mcp-ha-connect 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +8 -0
- package/.env.example +149 -0
- package/.gitattributes +30 -0
- package/Dockerfile +33 -0
- package/LICENSE +21 -0
- package/README.md +394 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +183 -0
- package/dist/config.js.map +1 -0
- package/dist/haClient/automations.d.ts +67 -0
- package/dist/haClient/automations.d.ts.map +1 -0
- package/dist/haClient/automations.js +181 -0
- package/dist/haClient/automations.js.map +1 -0
- package/dist/haClient/config.d.ts +28 -0
- package/dist/haClient/config.d.ts.map +1 -0
- package/dist/haClient/config.js +38 -0
- package/dist/haClient/config.js.map +1 -0
- package/dist/haClient/devices.d.ts +219 -0
- package/dist/haClient/devices.d.ts.map +1 -0
- package/dist/haClient/devices.js +506 -0
- package/dist/haClient/devices.js.map +1 -0
- package/dist/haClient/entities.d.ts +35 -0
- package/dist/haClient/entities.d.ts.map +1 -0
- package/dist/haClient/entities.js +97 -0
- package/dist/haClient/entities.js.map +1 -0
- package/dist/haClient/events.d.ts +138 -0
- package/dist/haClient/events.d.ts.map +1 -0
- package/dist/haClient/events.js +344 -0
- package/dist/haClient/events.js.map +1 -0
- package/dist/haClient/history.d.ts +26 -0
- package/dist/haClient/history.d.ts.map +1 -0
- package/dist/haClient/history.js +54 -0
- package/dist/haClient/history.js.map +1 -0
- package/dist/haClient/index.d.ts +153 -0
- package/dist/haClient/index.d.ts.map +1 -0
- package/dist/haClient/index.js +223 -0
- package/dist/haClient/index.js.map +1 -0
- package/dist/haClient/request.d.ts +34 -0
- package/dist/haClient/request.d.ts.map +1 -0
- package/dist/haClient/request.js +93 -0
- package/dist/haClient/request.js.map +1 -0
- package/dist/haClient/services.d.ts +22 -0
- package/dist/haClient/services.d.ts.map +1 -0
- package/dist/haClient/services.js +39 -0
- package/dist/haClient/services.js.map +1 -0
- package/dist/haClient/states.d.ts +26 -0
- package/dist/haClient/states.d.ts.map +1 -0
- package/dist/haClient/states.js +53 -0
- package/dist/haClient/states.js.map +1 -0
- package/dist/haClient/updates.d.ts +22 -0
- package/dist/haClient/updates.d.ts.map +1 -0
- package/dist/haClient/updates.js +48 -0
- package/dist/haClient/updates.js.map +1 -0
- package/dist/haClient.d.ts +97 -0
- package/dist/haClient.d.ts.map +1 -0
- package/dist/haClient.js +344 -0
- package/dist/haClient.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/localAI/index.d.ts +37 -0
- package/dist/localAI/index.d.ts.map +1 -0
- package/dist/localAI/index.js +73 -0
- package/dist/localAI/index.js.map +1 -0
- package/dist/localAI/prompts.d.ts +20 -0
- package/dist/localAI/prompts.d.ts.map +1 -0
- package/dist/localAI/prompts.js +48 -0
- package/dist/localAI/prompts.js.map +1 -0
- package/dist/localAI/providers/base.d.ts +51 -0
- package/dist/localAI/providers/base.d.ts.map +1 -0
- package/dist/localAI/providers/base.js +80 -0
- package/dist/localAI/providers/base.js.map +1 -0
- package/dist/localAI/providers/ollama.d.ts +22 -0
- package/dist/localAI/providers/ollama.d.ts.map +1 -0
- package/dist/localAI/providers/ollama.js +63 -0
- package/dist/localAI/providers/ollama.js.map +1 -0
- package/dist/localAI/providers/openai.d.ts +23 -0
- package/dist/localAI/providers/openai.d.ts.map +1 -0
- package/dist/localAI/providers/openai.js +63 -0
- package/dist/localAI/providers/openai.js.map +1 -0
- package/dist/localAI/types.d.ts +61 -0
- package/dist/localAI/types.d.ts.map +1 -0
- package/dist/localAI/types.js +6 -0
- package/dist/localAI/types.js.map +1 -0
- package/dist/ollamaClient.d.ts +37 -0
- package/dist/ollamaClient.d.ts.map +1 -0
- package/dist/ollamaClient.js +110 -0
- package/dist/ollamaClient.js.map +1 -0
- package/dist/resources/index.d.ts +11 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +238 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server/common.d.ts +9 -0
- package/dist/server/common.d.ts.map +1 -0
- package/dist/server/common.js +27 -0
- package/dist/server/common.js.map +1 -0
- package/dist/server/eventSubscription.d.ts +53 -0
- package/dist/server/eventSubscription.d.ts.map +1 -0
- package/dist/server/eventSubscription.js +257 -0
- package/dist/server/eventSubscription.js.map +1 -0
- package/dist/server/http.d.ts +12 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +491 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/rateLimiter.d.ts +70 -0
- package/dist/server/rateLimiter.d.ts.map +1 -0
- package/dist/server/rateLimiter.js +156 -0
- package/dist/server/rateLimiter.js.map +1 -0
- package/dist/server/sse.d.ts +32 -0
- package/dist/server/sse.d.ts.map +1 -0
- package/dist/server/sse.js +76 -0
- package/dist/server/sse.js.map +1 -0
- package/dist/server/stdio.d.ts +8 -0
- package/dist/server/stdio.d.ts.map +1 -0
- package/dist/server/stdio.js +16 -0
- package/dist/server/stdio.js.map +1 -0
- package/dist/server/stream.d.ts +26 -0
- package/dist/server/stream.d.ts.map +1 -0
- package/dist/server/stream.js +81 -0
- package/dist/server/stream.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +30 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/callService.d.ts +8 -0
- package/dist/tools/callService.d.ts.map +1 -0
- package/dist/tools/callService.js +100 -0
- package/dist/tools/callService.js.map +1 -0
- package/dist/tools/getClient.d.ts +8 -0
- package/dist/tools/getClient.d.ts.map +1 -0
- package/dist/tools/getClient.js +94 -0
- package/dist/tools/getClient.js.map +1 -0
- package/dist/tools/getDevice.d.ts +8 -0
- package/dist/tools/getDevice.d.ts.map +1 -0
- package/dist/tools/getDevice.js +94 -0
- package/dist/tools/getDevice.js.map +1 -0
- package/dist/tools/getEntitiesByDomain.d.ts +8 -0
- package/dist/tools/getEntitiesByDomain.d.ts.map +1 -0
- package/dist/tools/getEntitiesByDomain.js +80 -0
- package/dist/tools/getEntitiesByDomain.js.map +1 -0
- package/dist/tools/getState.d.ts +8 -0
- package/dist/tools/getState.d.ts.map +1 -0
- package/dist/tools/getState.js +90 -0
- package/dist/tools/getState.js.map +1 -0
- package/dist/tools/getStates.d.ts +8 -0
- package/dist/tools/getStates.d.ts.map +1 -0
- package/dist/tools/getStates.js +70 -0
- package/dist/tools/getStates.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/listClients.d.ts +8 -0
- package/dist/tools/listClients.d.ts.map +1 -0
- package/dist/tools/listClients.js +73 -0
- package/dist/tools/listClients.js.map +1 -0
- package/dist/tools/listDevices.d.ts +8 -0
- package/dist/tools/listDevices.d.ts.map +1 -0
- package/dist/tools/listDevices.js +73 -0
- package/dist/tools/listDevices.js.map +1 -0
- package/dist/tools/listSites.d.ts +8 -0
- package/dist/tools/listSites.d.ts.map +1 -0
- package/dist/tools/listSites.js +67 -0
- package/dist/tools/listSites.js.map +1 -0
- package/dist/tools/registry.d.ts +9 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +1382 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/searchDevices.d.ts +8 -0
- package/dist/tools/searchDevices.d.ts.map +1 -0
- package/dist/tools/searchDevices.js +84 -0
- package/dist/tools/searchDevices.js.map +1 -0
- package/dist/tools/searchEntities.d.ts +8 -0
- package/dist/tools/searchEntities.d.ts.map +1 -0
- package/dist/tools/searchEntities.js +80 -0
- package/dist/tools/searchEntities.js.map +1 -0
- package/dist/types/entity.d.ts +98 -0
- package/dist/types/entity.d.ts.map +1 -0
- package/dist/types/entity.js +2 -0
- package/dist/types/entity.js.map +1 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +32 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/haConfig.d.ts +8 -0
- package/dist/types/haConfig.d.ts.map +1 -0
- package/dist/types/haConfig.js +2 -0
- package/dist/types/haConfig.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/request.d.ts +7 -0
- package/dist/types/request.d.ts.map +1 -0
- package/dist/types/request.js +2 -0
- package/dist/types/request.js.map +1 -0
- package/dist/types/serviceCall.d.ts +18 -0
- package/dist/types/serviceCall.d.ts.map +1 -0
- package/dist/types/serviceCall.js +2 -0
- package/dist/types/serviceCall.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config-validations.d.ts +12 -0
- package/dist/utils/config-validations.d.ts.map +1 -0
- package/dist/utils/config-validations.js +43 -0
- package/dist/utils/config-validations.js.map +1 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +94 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/wrapper.d.ts +8 -0
- package/dist/wrapper.d.ts.map +1 -0
- package/dist/wrapper.js +301 -0
- package/dist/wrapper.js.map +1 -0
- package/docker-compose.yml +17 -0
- package/docs/CHANGELOG.md +86 -0
- package/docs/LOCAL_CLIENT.md +268 -0
- package/docs/SSE_API.md +341 -0
- package/eslint.config.js +33 -0
- package/n8n/ai-agent-safety-monitor.json +355 -0
- package/openapi.json +166 -0
- package/package.json +51 -0
- package/src/config.ts +260 -0
- package/src/haClient/automations.ts +217 -0
- package/src/haClient/config.ts +41 -0
- package/src/haClient/devices.ts +653 -0
- package/src/haClient/entities.ts +128 -0
- package/src/haClient/events.ts +456 -0
- package/src/haClient/history.ts +68 -0
- package/src/haClient/index.ts +287 -0
- package/src/haClient/request.ts +127 -0
- package/src/haClient/services.ts +45 -0
- package/src/haClient/states.ts +60 -0
- package/src/haClient/updates.ts +58 -0
- package/src/index.ts +99 -0
- package/src/localAI/index.ts +88 -0
- package/src/localAI/prompts.ts +50 -0
- package/src/localAI/providers/base.ts +120 -0
- package/src/localAI/providers/ollama.ts +78 -0
- package/src/localAI/providers/openai.ts +86 -0
- package/src/localAI/types.ts +67 -0
- package/src/resources/index.ts +266 -0
- package/src/server/common.ts +38 -0
- package/src/server/eventSubscription.ts +318 -0
- package/src/server/http.ts +546 -0
- package/src/server/rateLimiter.ts +195 -0
- package/src/server/sse.ts +120 -0
- package/src/server/stdio.ts +22 -0
- package/src/server/stream.ts +113 -0
- package/src/tools/callService.ts +114 -0
- package/src/tools/getEntitiesByDomain.ts +92 -0
- package/src/tools/getState.ts +103 -0
- package/src/tools/getStates.ts +80 -0
- package/src/tools/index.ts +9 -0
- package/src/tools/registry.ts +1521 -0
- package/src/tools/searchEntities.ts +92 -0
- package/src/types/entity.ts +110 -0
- package/src/types/errors.ts +33 -0
- package/src/types/haConfig.ts +7 -0
- package/src/types/index.ts +5 -0
- package/src/types/request.ts +6 -0
- package/src/types/serviceCall.ts +18 -0
- package/src/types.ts +2 -0
- package/src/utils/config-validations.ts +46 -0
- package/src/utils/logger.ts +114 -0
- package/src/wrapper.ts +340 -0
- package/tests/config.test.ts +332 -0
- package/tests/haClient/automations.test.ts +133 -0
- package/tests/haClient/config.test.ts +96 -0
- package/tests/haClient/entities.test.ts +188 -0
- package/tests/haClient/history.test.ts +170 -0
- package/tests/haClient/index.test.ts +221 -0
- package/tests/haClient/request.test.ts +217 -0
- package/tests/haClient/services.test.ts +120 -0
- package/tests/haClient/states.test.ts +119 -0
- package/tests/haClient/updates.test.ts +193 -0
- package/tests/index.test.ts +164 -0
- package/tests/localAI/index.test.ts +168 -0
- package/tests/localAI/ollama.test.ts +187 -0
- package/tests/localAI/openai.test.ts +257 -0
- package/tests/server/common.test.ts +64 -0
- package/tests/server/http.test.ts +326 -0
- package/tests/server/sse.test.ts +240 -0
- package/tests/server/stdio.test.ts +102 -0
- package/tests/server/stream.test.ts +287 -0
- package/tests/tools/registry.test.ts +355 -0
- package/tests/utils/config-validations.test.ts +289 -0
- package/tests/utils/logger.test.ts +327 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +26 -0
|
@@ -0,0 +1,1382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized tool registry
|
|
3
|
+
* Handles all tool registration and execution in one place
|
|
4
|
+
*/
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
export function registerAllTools(server, client, aiClient) {
|
|
8
|
+
logger.debug('Registering all tools');
|
|
9
|
+
// List all available tools
|
|
10
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
11
|
+
return {
|
|
12
|
+
tools: [
|
|
13
|
+
{
|
|
14
|
+
name: 'getStates',
|
|
15
|
+
description: 'Get all entity states from Home Assistant.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {},
|
|
19
|
+
required: [],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'getState',
|
|
24
|
+
description: 'Get the state of a specific entity by entity_id.',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
entity_id: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'The entity ID (e.g., "light.living_room", "sensor.temperature")',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: ['entity_id'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'callService',
|
|
38
|
+
description: 'Call a Home Assistant service (e.g., turn on a light, set temperature).',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
domain: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Service domain (e.g., "light", "switch", "climate", "notify")',
|
|
45
|
+
},
|
|
46
|
+
service: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'Service name (e.g., "turn_on", "turn_off", "set_temperature")',
|
|
49
|
+
},
|
|
50
|
+
service_data: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
description: 'Optional service data (e.g., {"brightness": 255})',
|
|
53
|
+
},
|
|
54
|
+
target: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
description: 'Optional target (entity_id, device_id, or area_id)',
|
|
57
|
+
properties: {
|
|
58
|
+
entity_id: {
|
|
59
|
+
type: ['string', 'array'],
|
|
60
|
+
description: 'Entity ID or array of entity IDs',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
required: ['domain', 'service'],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'getEntitiesByDomain',
|
|
70
|
+
description: 'Get all entities for a specific domain (e.g., all lights, all sensors).',
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
domain: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
description: 'Domain name (e.g., "light", "sensor", "switch", "climate")',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
required: ['domain'],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'searchEntities',
|
|
84
|
+
description: 'Search for entities by name or entity_id. Returns all matching entities.',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
query: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Search query (searches in entity_id and friendly_name)',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
required: ['query'],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'getAllSensors',
|
|
98
|
+
description: 'Get all sensor states from Home Assistant (sensor.* and binary_sensor.* entities).',
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {},
|
|
102
|
+
required: [],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'analyzeSensors',
|
|
107
|
+
description: 'Analyze sensor data using AI (Ollama) to detect issues and provide recommendations.',
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
sensors: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
description: 'Sensor data to analyze. Object with entity_id keys and {state, unit, attributes} values.',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
required: ['sensors'],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'getHistory',
|
|
121
|
+
description: 'Get historical data for an entity.',
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
entity_id: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
description: 'The entity ID to get history for',
|
|
128
|
+
},
|
|
129
|
+
hours: {
|
|
130
|
+
type: 'number',
|
|
131
|
+
description: 'Number of hours of history to retrieve (default: 24)',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
required: ['entity_id'],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'getVersion',
|
|
139
|
+
description: 'Get Home Assistant version and configuration information.',
|
|
140
|
+
inputSchema: {
|
|
141
|
+
type: 'object',
|
|
142
|
+
properties: {},
|
|
143
|
+
required: [],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'entityAction',
|
|
148
|
+
description: 'Perform a simple action on an entity (turn_on, turn_off, toggle). Automatically detects the domain.',
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
entity_id: {
|
|
153
|
+
type: 'string',
|
|
154
|
+
description: 'The entity ID to perform the action on (e.g., "light.living_room", "switch.fan")',
|
|
155
|
+
},
|
|
156
|
+
action: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
enum: ['turn_on', 'turn_off', 'toggle'],
|
|
159
|
+
description: 'The action to perform',
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
required: ['entity_id', 'action'],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'listEntities',
|
|
167
|
+
description: 'List entities with optional filtering by domain, state, or search query.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
domain: {
|
|
172
|
+
type: 'string',
|
|
173
|
+
description: 'Filter by domain (e.g., "light", "switch", "sensor")',
|
|
174
|
+
},
|
|
175
|
+
search: {
|
|
176
|
+
type: 'string',
|
|
177
|
+
description: 'Search query to filter by entity_id or friendly_name',
|
|
178
|
+
},
|
|
179
|
+
state: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
description: 'Filter by state (e.g., "on", "off", "unavailable")',
|
|
182
|
+
},
|
|
183
|
+
limit: {
|
|
184
|
+
type: 'number',
|
|
185
|
+
description: 'Maximum number of entities to return',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
required: [],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'getDomainSummary',
|
|
193
|
+
description: 'Get a summary of entities in a domain, including counts and state breakdown.',
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
domain: {
|
|
198
|
+
type: 'string',
|
|
199
|
+
description: 'Domain name (e.g., "light", "sensor", "switch", "automation")',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
required: ['domain'],
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: 'listAutomations',
|
|
207
|
+
description: 'List all Home Assistant automations with their status and last triggered time.',
|
|
208
|
+
inputSchema: {
|
|
209
|
+
type: 'object',
|
|
210
|
+
properties: {
|
|
211
|
+
state: {
|
|
212
|
+
type: 'string',
|
|
213
|
+
enum: ['on', 'off'],
|
|
214
|
+
description: 'Filter by automation state (enabled/disabled)',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
required: [],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'restartHomeAssistant',
|
|
222
|
+
description: 'Restart the Home Assistant server. Use with caution.',
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
properties: {},
|
|
226
|
+
required: [],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: 'getSystemLog',
|
|
231
|
+
description: 'Get system log entries from the Home Assistant logbook. Shows recent events and state changes.',
|
|
232
|
+
inputSchema: {
|
|
233
|
+
type: 'object',
|
|
234
|
+
properties: {
|
|
235
|
+
hours: {
|
|
236
|
+
type: 'number',
|
|
237
|
+
description: 'Number of hours of history to retrieve (default: 24)',
|
|
238
|
+
},
|
|
239
|
+
entity_id: {
|
|
240
|
+
type: 'string',
|
|
241
|
+
description: 'Optional entity ID to filter logs for a specific entity',
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
required: [],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'checkUpdates',
|
|
249
|
+
description: 'Check for available updates for Home Assistant Core, Supervisor, and OS. Requires Home Assistant OS or Supervised installation.',
|
|
250
|
+
inputSchema: {
|
|
251
|
+
type: 'object',
|
|
252
|
+
properties: {},
|
|
253
|
+
required: [],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: 'triggerAutomation',
|
|
258
|
+
description: 'Manually trigger a Home Assistant automation. Can optionally pass variables to the automation.',
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: 'object',
|
|
261
|
+
properties: {
|
|
262
|
+
entity_id: {
|
|
263
|
+
type: 'string',
|
|
264
|
+
description: 'The automation entity ID (e.g., "automation.turn_on_lights")',
|
|
265
|
+
},
|
|
266
|
+
variables: {
|
|
267
|
+
type: 'object',
|
|
268
|
+
description: 'Optional variables to pass to the automation',
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
required: ['entity_id'],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'enableAutomation',
|
|
276
|
+
description: 'Enable a disabled Home Assistant automation.',
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: 'object',
|
|
279
|
+
properties: {
|
|
280
|
+
entity_id: {
|
|
281
|
+
type: 'string',
|
|
282
|
+
description: 'The automation entity ID to enable',
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
required: ['entity_id'],
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'disableAutomation',
|
|
290
|
+
description: 'Disable a Home Assistant automation.',
|
|
291
|
+
inputSchema: {
|
|
292
|
+
type: 'object',
|
|
293
|
+
properties: {
|
|
294
|
+
entity_id: {
|
|
295
|
+
type: 'string',
|
|
296
|
+
description: 'The automation entity ID to disable',
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
required: ['entity_id'],
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'toggleAutomation',
|
|
304
|
+
description: 'Toggle a Home Assistant automation (enable if disabled, disable if enabled).',
|
|
305
|
+
inputSchema: {
|
|
306
|
+
type: 'object',
|
|
307
|
+
properties: {
|
|
308
|
+
entity_id: {
|
|
309
|
+
type: 'string',
|
|
310
|
+
description: 'The automation entity ID to toggle',
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
required: ['entity_id'],
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'reloadAutomations',
|
|
318
|
+
description: 'Reload all automations from the Home Assistant configuration. Useful after editing automation YAML files.',
|
|
319
|
+
inputSchema: {
|
|
320
|
+
type: 'object',
|
|
321
|
+
properties: {},
|
|
322
|
+
required: [],
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'createAutomation',
|
|
327
|
+
description: 'Create a new Home Assistant automation. Requires specifying triggers, conditions (optional), and actions.',
|
|
328
|
+
inputSchema: {
|
|
329
|
+
type: 'object',
|
|
330
|
+
properties: {
|
|
331
|
+
alias: {
|
|
332
|
+
type: 'string',
|
|
333
|
+
description: 'Friendly name for the automation',
|
|
334
|
+
},
|
|
335
|
+
description: {
|
|
336
|
+
type: 'string',
|
|
337
|
+
description: 'Optional description of what the automation does',
|
|
338
|
+
},
|
|
339
|
+
mode: {
|
|
340
|
+
type: 'string',
|
|
341
|
+
enum: ['single', 'restart', 'queued', 'parallel'],
|
|
342
|
+
description: 'Execution mode (default: single)',
|
|
343
|
+
},
|
|
344
|
+
trigger: {
|
|
345
|
+
type: ['object', 'array'],
|
|
346
|
+
description: 'Trigger(s) that start the automation. Each trigger needs a "platform" property.',
|
|
347
|
+
},
|
|
348
|
+
condition: {
|
|
349
|
+
type: ['object', 'array'],
|
|
350
|
+
description: 'Optional condition(s) that must be true for actions to run.',
|
|
351
|
+
},
|
|
352
|
+
action: {
|
|
353
|
+
type: ['object', 'array'],
|
|
354
|
+
description: 'Action(s) to perform when triggered. Each action typically has "service" and "target" properties.',
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
required: ['alias', 'trigger', 'action'],
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: 'deleteAutomation',
|
|
362
|
+
description: 'Delete a Home Assistant automation. Only works for automations created via the UI/API.',
|
|
363
|
+
inputSchema: {
|
|
364
|
+
type: 'object',
|
|
365
|
+
properties: {
|
|
366
|
+
automation_id: {
|
|
367
|
+
type: 'string',
|
|
368
|
+
description: 'The unique ID of the automation to delete',
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
required: ['automation_id'],
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: 'getAutomationTrace',
|
|
376
|
+
description: 'Get the execution trace (history) of an automation. Shows when it was triggered and what actions were executed.',
|
|
377
|
+
inputSchema: {
|
|
378
|
+
type: 'object',
|
|
379
|
+
properties: {
|
|
380
|
+
entity_id: {
|
|
381
|
+
type: 'string',
|
|
382
|
+
description: 'The automation entity ID to get trace for',
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
required: ['entity_id'],
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'controlLight',
|
|
390
|
+
description: 'Control a light with advanced options like brightness, color, and color temperature.',
|
|
391
|
+
inputSchema: {
|
|
392
|
+
type: 'object',
|
|
393
|
+
properties: {
|
|
394
|
+
entity_id: {
|
|
395
|
+
type: 'string',
|
|
396
|
+
description: 'The light entity ID (e.g., "light.living_room")',
|
|
397
|
+
},
|
|
398
|
+
action: {
|
|
399
|
+
type: 'string',
|
|
400
|
+
enum: ['turn_on', 'turn_off', 'toggle'],
|
|
401
|
+
description: 'The action to perform',
|
|
402
|
+
},
|
|
403
|
+
brightness_pct: {
|
|
404
|
+
type: 'number',
|
|
405
|
+
description: 'Brightness percentage (0-100)',
|
|
406
|
+
},
|
|
407
|
+
color_temp_kelvin: {
|
|
408
|
+
type: 'number',
|
|
409
|
+
description: 'Color temperature in Kelvin (e.g., 2700 for warm, 6500 for cool)',
|
|
410
|
+
},
|
|
411
|
+
rgb_color: {
|
|
412
|
+
type: 'array',
|
|
413
|
+
items: { type: 'number' },
|
|
414
|
+
description: 'RGB color as [red, green, blue] (0-255 each)',
|
|
415
|
+
},
|
|
416
|
+
transition: {
|
|
417
|
+
type: 'number',
|
|
418
|
+
description: 'Transition time in seconds',
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
required: ['entity_id', 'action'],
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
name: 'controlClimate',
|
|
426
|
+
description: 'Control a climate/thermostat device with temperature and mode settings.',
|
|
427
|
+
inputSchema: {
|
|
428
|
+
type: 'object',
|
|
429
|
+
properties: {
|
|
430
|
+
entity_id: {
|
|
431
|
+
type: 'string',
|
|
432
|
+
description: 'The climate entity ID (e.g., "climate.living_room")',
|
|
433
|
+
},
|
|
434
|
+
temperature: {
|
|
435
|
+
type: 'number',
|
|
436
|
+
description: 'Target temperature',
|
|
437
|
+
},
|
|
438
|
+
hvac_mode: {
|
|
439
|
+
type: 'string',
|
|
440
|
+
enum: ['off', 'heat', 'cool', 'heat_cool', 'auto', 'dry', 'fan_only'],
|
|
441
|
+
description: 'HVAC mode',
|
|
442
|
+
},
|
|
443
|
+
fan_mode: {
|
|
444
|
+
type: 'string',
|
|
445
|
+
description: 'Fan mode (device-specific)',
|
|
446
|
+
},
|
|
447
|
+
preset_mode: {
|
|
448
|
+
type: 'string',
|
|
449
|
+
description: 'Preset mode (e.g., "home", "away", "eco")',
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
required: ['entity_id'],
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: 'controlMediaPlayer',
|
|
457
|
+
description: 'Control a media player with playback, volume, and media selection.',
|
|
458
|
+
inputSchema: {
|
|
459
|
+
type: 'object',
|
|
460
|
+
properties: {
|
|
461
|
+
entity_id: {
|
|
462
|
+
type: 'string',
|
|
463
|
+
description: 'The media player entity ID',
|
|
464
|
+
},
|
|
465
|
+
action: {
|
|
466
|
+
type: 'string',
|
|
467
|
+
enum: ['play', 'pause', 'stop', 'next', 'previous', 'volume_set', 'volume_mute'],
|
|
468
|
+
description: 'The action to perform',
|
|
469
|
+
},
|
|
470
|
+
volume_level: {
|
|
471
|
+
type: 'number',
|
|
472
|
+
description: 'Volume level (0.0 to 1.0)',
|
|
473
|
+
},
|
|
474
|
+
is_volume_muted: {
|
|
475
|
+
type: 'boolean',
|
|
476
|
+
description: 'Mute state',
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
required: ['entity_id', 'action'],
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
name: 'controlCover',
|
|
484
|
+
description: 'Control a cover/blind with position and tilt settings.',
|
|
485
|
+
inputSchema: {
|
|
486
|
+
type: 'object',
|
|
487
|
+
properties: {
|
|
488
|
+
entity_id: {
|
|
489
|
+
type: 'string',
|
|
490
|
+
description: 'The cover entity ID (e.g., "cover.living_room_blinds")',
|
|
491
|
+
},
|
|
492
|
+
action: {
|
|
493
|
+
type: 'string',
|
|
494
|
+
enum: ['open', 'close', 'stop', 'set_position', 'set_tilt'],
|
|
495
|
+
description: 'The action to perform',
|
|
496
|
+
},
|
|
497
|
+
position: {
|
|
498
|
+
type: 'number',
|
|
499
|
+
description: 'Cover position (0 = closed, 100 = open)',
|
|
500
|
+
},
|
|
501
|
+
tilt_position: {
|
|
502
|
+
type: 'number',
|
|
503
|
+
description: 'Tilt position (0-100)',
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
required: ['entity_id', 'action'],
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: 'controlFan',
|
|
511
|
+
description: 'Control a fan with speed, oscillation, and direction settings.',
|
|
512
|
+
inputSchema: {
|
|
513
|
+
type: 'object',
|
|
514
|
+
properties: {
|
|
515
|
+
entity_id: {
|
|
516
|
+
type: 'string',
|
|
517
|
+
description: 'The fan entity ID',
|
|
518
|
+
},
|
|
519
|
+
action: {
|
|
520
|
+
type: 'string',
|
|
521
|
+
enum: ['turn_on', 'turn_off', 'toggle', 'set_speed', 'oscillate', 'set_direction'],
|
|
522
|
+
description: 'The action to perform',
|
|
523
|
+
},
|
|
524
|
+
percentage: {
|
|
525
|
+
type: 'number',
|
|
526
|
+
description: 'Speed percentage (0-100)',
|
|
527
|
+
},
|
|
528
|
+
oscillating: {
|
|
529
|
+
type: 'boolean',
|
|
530
|
+
description: 'Oscillation state',
|
|
531
|
+
},
|
|
532
|
+
direction: {
|
|
533
|
+
type: 'string',
|
|
534
|
+
enum: ['forward', 'reverse'],
|
|
535
|
+
description: 'Fan direction',
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
required: ['entity_id', 'action'],
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: 'activateScene',
|
|
543
|
+
description: 'Activate a Home Assistant scene.',
|
|
544
|
+
inputSchema: {
|
|
545
|
+
type: 'object',
|
|
546
|
+
properties: {
|
|
547
|
+
entity_id: {
|
|
548
|
+
type: 'string',
|
|
549
|
+
description: 'The scene entity ID (e.g., "scene.movie_night")',
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
required: ['entity_id'],
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
name: 'runScript',
|
|
557
|
+
description: 'Run a Home Assistant script with optional variables.',
|
|
558
|
+
inputSchema: {
|
|
559
|
+
type: 'object',
|
|
560
|
+
properties: {
|
|
561
|
+
entity_id: {
|
|
562
|
+
type: 'string',
|
|
563
|
+
description: 'The script entity ID (e.g., "script.morning_routine")',
|
|
564
|
+
},
|
|
565
|
+
variables: {
|
|
566
|
+
type: 'object',
|
|
567
|
+
description: 'Optional variables to pass to the script',
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
required: ['entity_id'],
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: 'sendNotification',
|
|
575
|
+
description: 'Send a notification through Home Assistant.',
|
|
576
|
+
inputSchema: {
|
|
577
|
+
type: 'object',
|
|
578
|
+
properties: {
|
|
579
|
+
message: {
|
|
580
|
+
type: 'string',
|
|
581
|
+
description: 'The notification message',
|
|
582
|
+
},
|
|
583
|
+
title: {
|
|
584
|
+
type: 'string',
|
|
585
|
+
description: 'Optional notification title',
|
|
586
|
+
},
|
|
587
|
+
target: {
|
|
588
|
+
type: 'string',
|
|
589
|
+
description: 'Notification service target (e.g., "mobile_app_phone")',
|
|
590
|
+
},
|
|
591
|
+
data: {
|
|
592
|
+
type: 'object',
|
|
593
|
+
description: 'Additional notification data (device-specific)',
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
required: ['message'],
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
};
|
|
601
|
+
});
|
|
602
|
+
// Handle tool calls
|
|
603
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
604
|
+
const toolName = request.params.name;
|
|
605
|
+
logger.info('Executing tool', { toolName });
|
|
606
|
+
try {
|
|
607
|
+
switch (toolName) {
|
|
608
|
+
case 'getStates': {
|
|
609
|
+
const states = await client.getStates();
|
|
610
|
+
return {
|
|
611
|
+
content: [
|
|
612
|
+
{
|
|
613
|
+
type: 'text',
|
|
614
|
+
text: JSON.stringify({
|
|
615
|
+
count: states.length,
|
|
616
|
+
entities: states,
|
|
617
|
+
}, null, 2),
|
|
618
|
+
},
|
|
619
|
+
],
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
case 'getState': {
|
|
623
|
+
const { entity_id } = request.params.arguments;
|
|
624
|
+
if (!entity_id) {
|
|
625
|
+
throw new Error('entity_id is required');
|
|
626
|
+
}
|
|
627
|
+
const state = await client.getState(entity_id);
|
|
628
|
+
if (!state) {
|
|
629
|
+
return {
|
|
630
|
+
content: [
|
|
631
|
+
{
|
|
632
|
+
type: 'text',
|
|
633
|
+
text: JSON.stringify({
|
|
634
|
+
error: 'Entity not found',
|
|
635
|
+
entity_id,
|
|
636
|
+
}),
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
isError: true,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
content: [
|
|
644
|
+
{
|
|
645
|
+
type: 'text',
|
|
646
|
+
text: JSON.stringify(state, null, 2),
|
|
647
|
+
},
|
|
648
|
+
],
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
case 'callService': {
|
|
652
|
+
const args = request.params.arguments;
|
|
653
|
+
if (!args.domain || !args.service) {
|
|
654
|
+
throw new Error('domain and service are required');
|
|
655
|
+
}
|
|
656
|
+
const result = await client.callService(args);
|
|
657
|
+
return {
|
|
658
|
+
content: [
|
|
659
|
+
{
|
|
660
|
+
type: 'text',
|
|
661
|
+
text: JSON.stringify({
|
|
662
|
+
success: true,
|
|
663
|
+
context: result.context,
|
|
664
|
+
}, null, 2),
|
|
665
|
+
},
|
|
666
|
+
],
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
case 'getEntitiesByDomain': {
|
|
670
|
+
const { domain } = request.params.arguments;
|
|
671
|
+
if (!domain) {
|
|
672
|
+
throw new Error('domain is required');
|
|
673
|
+
}
|
|
674
|
+
const entities = await client.getEntitiesByDomain(domain);
|
|
675
|
+
return {
|
|
676
|
+
content: [
|
|
677
|
+
{
|
|
678
|
+
type: 'text',
|
|
679
|
+
text: JSON.stringify({
|
|
680
|
+
domain,
|
|
681
|
+
count: entities.length,
|
|
682
|
+
entities,
|
|
683
|
+
}, null, 2),
|
|
684
|
+
},
|
|
685
|
+
],
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
case 'searchEntities': {
|
|
689
|
+
const { query } = request.params.arguments;
|
|
690
|
+
if (!query) {
|
|
691
|
+
throw new Error('query is required');
|
|
692
|
+
}
|
|
693
|
+
const entities = await client.searchEntities(query);
|
|
694
|
+
return {
|
|
695
|
+
content: [
|
|
696
|
+
{
|
|
697
|
+
type: 'text',
|
|
698
|
+
text: JSON.stringify({
|
|
699
|
+
query,
|
|
700
|
+
count: entities.length,
|
|
701
|
+
entities,
|
|
702
|
+
}, null, 2),
|
|
703
|
+
},
|
|
704
|
+
],
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
case 'getAllSensors': {
|
|
708
|
+
const sensors = await client.getAllSensors();
|
|
709
|
+
return {
|
|
710
|
+
content: [
|
|
711
|
+
{
|
|
712
|
+
type: 'text',
|
|
713
|
+
text: JSON.stringify(sensors, null, 2),
|
|
714
|
+
},
|
|
715
|
+
],
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
case 'analyzeSensors': {
|
|
719
|
+
if (!aiClient) {
|
|
720
|
+
return {
|
|
721
|
+
content: [
|
|
722
|
+
{
|
|
723
|
+
type: 'text',
|
|
724
|
+
text: JSON.stringify({
|
|
725
|
+
error: 'AI client not configured',
|
|
726
|
+
message: 'AI analysis requires AI_URL to be set (or legacy OLLAMA_URL)',
|
|
727
|
+
}),
|
|
728
|
+
},
|
|
729
|
+
],
|
|
730
|
+
isError: true,
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
const { sensors } = request.params.arguments;
|
|
734
|
+
if (!sensors || typeof sensors !== 'object') {
|
|
735
|
+
throw new Error('sensors object is required');
|
|
736
|
+
}
|
|
737
|
+
const analysis = await aiClient.analyzeSensors(sensors);
|
|
738
|
+
return {
|
|
739
|
+
content: [
|
|
740
|
+
{
|
|
741
|
+
type: 'text',
|
|
742
|
+
text: JSON.stringify(analysis, null, 2),
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
case 'getHistory': {
|
|
748
|
+
const { entity_id, hours = 24 } = request.params.arguments;
|
|
749
|
+
if (!entity_id) {
|
|
750
|
+
throw new Error('entity_id is required');
|
|
751
|
+
}
|
|
752
|
+
const history = await client.getHistory(entity_id, hours);
|
|
753
|
+
return {
|
|
754
|
+
content: [
|
|
755
|
+
{
|
|
756
|
+
type: 'text',
|
|
757
|
+
text: JSON.stringify(history, null, 2),
|
|
758
|
+
},
|
|
759
|
+
],
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
case 'getVersion': {
|
|
763
|
+
const version = await client.getVersion();
|
|
764
|
+
return {
|
|
765
|
+
content: [
|
|
766
|
+
{
|
|
767
|
+
type: 'text',
|
|
768
|
+
text: JSON.stringify(version, null, 2),
|
|
769
|
+
},
|
|
770
|
+
],
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
case 'entityAction': {
|
|
774
|
+
const { entity_id, action } = request.params.arguments;
|
|
775
|
+
if (!entity_id || !action) {
|
|
776
|
+
throw new Error('entity_id and action are required');
|
|
777
|
+
}
|
|
778
|
+
// Extract domain from entity_id
|
|
779
|
+
const domain = entity_id.split('.')[0];
|
|
780
|
+
if (!domain) {
|
|
781
|
+
throw new Error('Invalid entity_id format');
|
|
782
|
+
}
|
|
783
|
+
const result = await client.callService({
|
|
784
|
+
domain,
|
|
785
|
+
service: action,
|
|
786
|
+
target: { entity_id },
|
|
787
|
+
});
|
|
788
|
+
return {
|
|
789
|
+
content: [
|
|
790
|
+
{
|
|
791
|
+
type: 'text',
|
|
792
|
+
text: JSON.stringify({
|
|
793
|
+
success: true,
|
|
794
|
+
entity_id,
|
|
795
|
+
action,
|
|
796
|
+
context: result.context,
|
|
797
|
+
}, null, 2),
|
|
798
|
+
},
|
|
799
|
+
],
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
case 'listEntities': {
|
|
803
|
+
const options = request.params.arguments;
|
|
804
|
+
const entities = await client.listEntities(options);
|
|
805
|
+
return {
|
|
806
|
+
content: [
|
|
807
|
+
{
|
|
808
|
+
type: 'text',
|
|
809
|
+
text: JSON.stringify({
|
|
810
|
+
count: entities.length,
|
|
811
|
+
filters: options,
|
|
812
|
+
entities,
|
|
813
|
+
}, null, 2),
|
|
814
|
+
},
|
|
815
|
+
],
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
case 'getDomainSummary': {
|
|
819
|
+
const { domain } = request.params.arguments;
|
|
820
|
+
if (!domain) {
|
|
821
|
+
throw new Error('domain is required');
|
|
822
|
+
}
|
|
823
|
+
const summary = await client.getDomainSummary(domain);
|
|
824
|
+
return {
|
|
825
|
+
content: [
|
|
826
|
+
{
|
|
827
|
+
type: 'text',
|
|
828
|
+
text: JSON.stringify(summary, null, 2),
|
|
829
|
+
},
|
|
830
|
+
],
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
case 'listAutomations': {
|
|
834
|
+
const { state } = request.params.arguments;
|
|
835
|
+
let automations = await client.getAutomations();
|
|
836
|
+
if (state) {
|
|
837
|
+
automations = automations.filter(a => a.state === state);
|
|
838
|
+
}
|
|
839
|
+
return {
|
|
840
|
+
content: [
|
|
841
|
+
{
|
|
842
|
+
type: 'text',
|
|
843
|
+
text: JSON.stringify({
|
|
844
|
+
count: automations.length,
|
|
845
|
+
filter: state ? { state } : null,
|
|
846
|
+
automations,
|
|
847
|
+
}, null, 2),
|
|
848
|
+
},
|
|
849
|
+
],
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
case 'restartHomeAssistant': {
|
|
853
|
+
await client.restartServer();
|
|
854
|
+
return {
|
|
855
|
+
content: [
|
|
856
|
+
{
|
|
857
|
+
type: 'text',
|
|
858
|
+
text: JSON.stringify({
|
|
859
|
+
success: true,
|
|
860
|
+
message: 'Home Assistant restart initiated. The server will be unavailable for a short period.',
|
|
861
|
+
}, null, 2),
|
|
862
|
+
},
|
|
863
|
+
],
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
case 'getSystemLog': {
|
|
867
|
+
const { hours, entity_id } = request.params.arguments;
|
|
868
|
+
const entries = await client.getSystemLog({ hours, entity_id });
|
|
869
|
+
return {
|
|
870
|
+
content: [
|
|
871
|
+
{
|
|
872
|
+
type: 'text',
|
|
873
|
+
text: JSON.stringify({
|
|
874
|
+
count: entries.length,
|
|
875
|
+
hours: hours || 24,
|
|
876
|
+
entity_id: entity_id || null,
|
|
877
|
+
entries,
|
|
878
|
+
}, null, 2),
|
|
879
|
+
},
|
|
880
|
+
],
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
case 'checkUpdates': {
|
|
884
|
+
const updateInfo = await client.getAvailableUpdates();
|
|
885
|
+
// Extract summary info for key components
|
|
886
|
+
const coreEntity = updateInfo.entities.find(e => e.entity_id === 'update.home_assistant_core_update');
|
|
887
|
+
const supervisorEntity = updateInfo.entities.find(e => e.entity_id === 'update.home_assistant_supervisor_update');
|
|
888
|
+
const osEntity = updateInfo.entities.find(e => e.entity_id === 'update.home_assistant_operating_system_update');
|
|
889
|
+
return {
|
|
890
|
+
content: [
|
|
891
|
+
{
|
|
892
|
+
type: 'text',
|
|
893
|
+
text: JSON.stringify({
|
|
894
|
+
updates_available: updateInfo.updates.length,
|
|
895
|
+
updates: updateInfo.updates,
|
|
896
|
+
core: coreEntity ? {
|
|
897
|
+
version: coreEntity.attributes.installed_version,
|
|
898
|
+
version_latest: coreEntity.attributes.latest_version,
|
|
899
|
+
update_available: coreEntity.state === 'on',
|
|
900
|
+
} : null,
|
|
901
|
+
supervisor: supervisorEntity ? {
|
|
902
|
+
version: supervisorEntity.attributes.installed_version,
|
|
903
|
+
version_latest: supervisorEntity.attributes.latest_version,
|
|
904
|
+
update_available: supervisorEntity.state === 'on',
|
|
905
|
+
} : null,
|
|
906
|
+
os: osEntity ? {
|
|
907
|
+
version: osEntity.attributes.installed_version,
|
|
908
|
+
version_latest: osEntity.attributes.latest_version,
|
|
909
|
+
update_available: osEntity.state === 'on',
|
|
910
|
+
} : null,
|
|
911
|
+
total_update_entities: updateInfo.entities.length,
|
|
912
|
+
}, null, 2),
|
|
913
|
+
},
|
|
914
|
+
],
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
case 'triggerAutomation': {
|
|
918
|
+
const { entity_id, variables } = request.params.arguments;
|
|
919
|
+
if (!entity_id) {
|
|
920
|
+
throw new Error('entity_id is required');
|
|
921
|
+
}
|
|
922
|
+
const result = await client.triggerAutomation(entity_id, variables);
|
|
923
|
+
return {
|
|
924
|
+
content: [
|
|
925
|
+
{
|
|
926
|
+
type: 'text',
|
|
927
|
+
text: JSON.stringify({
|
|
928
|
+
success: true,
|
|
929
|
+
entity_id,
|
|
930
|
+
message: 'Automation triggered successfully',
|
|
931
|
+
context: result.context,
|
|
932
|
+
}, null, 2),
|
|
933
|
+
},
|
|
934
|
+
],
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
case 'enableAutomation': {
|
|
938
|
+
const { entity_id } = request.params.arguments;
|
|
939
|
+
if (!entity_id) {
|
|
940
|
+
throw new Error('entity_id is required');
|
|
941
|
+
}
|
|
942
|
+
const result = await client.enableAutomation(entity_id);
|
|
943
|
+
return {
|
|
944
|
+
content: [
|
|
945
|
+
{
|
|
946
|
+
type: 'text',
|
|
947
|
+
text: JSON.stringify({
|
|
948
|
+
success: true,
|
|
949
|
+
entity_id,
|
|
950
|
+
action: 'enabled',
|
|
951
|
+
context: result.context,
|
|
952
|
+
}, null, 2),
|
|
953
|
+
},
|
|
954
|
+
],
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
case 'disableAutomation': {
|
|
958
|
+
const { entity_id } = request.params.arguments;
|
|
959
|
+
if (!entity_id) {
|
|
960
|
+
throw new Error('entity_id is required');
|
|
961
|
+
}
|
|
962
|
+
const result = await client.disableAutomation(entity_id);
|
|
963
|
+
return {
|
|
964
|
+
content: [
|
|
965
|
+
{
|
|
966
|
+
type: 'text',
|
|
967
|
+
text: JSON.stringify({
|
|
968
|
+
success: true,
|
|
969
|
+
entity_id,
|
|
970
|
+
action: 'disabled',
|
|
971
|
+
context: result.context,
|
|
972
|
+
}, null, 2),
|
|
973
|
+
},
|
|
974
|
+
],
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
case 'toggleAutomation': {
|
|
978
|
+
const { entity_id } = request.params.arguments;
|
|
979
|
+
if (!entity_id) {
|
|
980
|
+
throw new Error('entity_id is required');
|
|
981
|
+
}
|
|
982
|
+
const result = await client.toggleAutomation(entity_id);
|
|
983
|
+
return {
|
|
984
|
+
content: [
|
|
985
|
+
{
|
|
986
|
+
type: 'text',
|
|
987
|
+
text: JSON.stringify({
|
|
988
|
+
success: true,
|
|
989
|
+
entity_id,
|
|
990
|
+
action: 'toggled',
|
|
991
|
+
context: result.context,
|
|
992
|
+
}, null, 2),
|
|
993
|
+
},
|
|
994
|
+
],
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
case 'reloadAutomations': {
|
|
998
|
+
await client.reloadAutomations();
|
|
999
|
+
return {
|
|
1000
|
+
content: [
|
|
1001
|
+
{
|
|
1002
|
+
type: 'text',
|
|
1003
|
+
text: JSON.stringify({
|
|
1004
|
+
success: true,
|
|
1005
|
+
message: 'Automations reloaded from configuration',
|
|
1006
|
+
}, null, 2),
|
|
1007
|
+
},
|
|
1008
|
+
],
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
case 'createAutomation': {
|
|
1012
|
+
const config = request.params.arguments;
|
|
1013
|
+
if (!config.alias || !config.trigger || !config.action) {
|
|
1014
|
+
throw new Error('alias, trigger, and action are required');
|
|
1015
|
+
}
|
|
1016
|
+
const result = await client.createAutomation({
|
|
1017
|
+
alias: config.alias,
|
|
1018
|
+
description: config.description,
|
|
1019
|
+
mode: config.mode,
|
|
1020
|
+
trigger: config.trigger,
|
|
1021
|
+
condition: config.condition,
|
|
1022
|
+
action: config.action,
|
|
1023
|
+
});
|
|
1024
|
+
return {
|
|
1025
|
+
content: [
|
|
1026
|
+
{
|
|
1027
|
+
type: 'text',
|
|
1028
|
+
text: JSON.stringify({
|
|
1029
|
+
success: true,
|
|
1030
|
+
automation_id: result.id,
|
|
1031
|
+
alias: config.alias,
|
|
1032
|
+
message: 'Automation created successfully',
|
|
1033
|
+
}, null, 2),
|
|
1034
|
+
},
|
|
1035
|
+
],
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
case 'deleteAutomation': {
|
|
1039
|
+
const { automation_id } = request.params.arguments;
|
|
1040
|
+
if (!automation_id) {
|
|
1041
|
+
throw new Error('automation_id is required');
|
|
1042
|
+
}
|
|
1043
|
+
await client.deleteAutomation(automation_id);
|
|
1044
|
+
return {
|
|
1045
|
+
content: [
|
|
1046
|
+
{
|
|
1047
|
+
type: 'text',
|
|
1048
|
+
text: JSON.stringify({
|
|
1049
|
+
success: true,
|
|
1050
|
+
automation_id,
|
|
1051
|
+
message: 'Automation deleted successfully',
|
|
1052
|
+
}, null, 2),
|
|
1053
|
+
},
|
|
1054
|
+
],
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
case 'getAutomationTrace': {
|
|
1058
|
+
const { entity_id } = request.params.arguments;
|
|
1059
|
+
if (!entity_id) {
|
|
1060
|
+
throw new Error('entity_id is required');
|
|
1061
|
+
}
|
|
1062
|
+
const trace = await client.getAutomationTrace(entity_id);
|
|
1063
|
+
return {
|
|
1064
|
+
content: [
|
|
1065
|
+
{
|
|
1066
|
+
type: 'text',
|
|
1067
|
+
text: JSON.stringify({
|
|
1068
|
+
entity_id,
|
|
1069
|
+
trace_count: Array.isArray(trace) ? trace.length : 0,
|
|
1070
|
+
traces: trace,
|
|
1071
|
+
}, null, 2),
|
|
1072
|
+
},
|
|
1073
|
+
],
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
case 'controlLight': {
|
|
1077
|
+
const args = request.params.arguments;
|
|
1078
|
+
if (!args.entity_id || !args.action) {
|
|
1079
|
+
throw new Error('entity_id and action are required');
|
|
1080
|
+
}
|
|
1081
|
+
let result;
|
|
1082
|
+
if (args.action === 'turn_off') {
|
|
1083
|
+
result = await client.devices.turnOffLight(args.entity_id, args.transition);
|
|
1084
|
+
}
|
|
1085
|
+
else if (args.action === 'toggle') {
|
|
1086
|
+
result = await client.devices.toggleLight(args.entity_id);
|
|
1087
|
+
}
|
|
1088
|
+
else {
|
|
1089
|
+
result = await client.devices.turnOnLight(args.entity_id, {
|
|
1090
|
+
brightness_pct: args.brightness_pct,
|
|
1091
|
+
color_temp_kelvin: args.color_temp_kelvin,
|
|
1092
|
+
rgb_color: args.rgb_color,
|
|
1093
|
+
transition: args.transition,
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
return {
|
|
1097
|
+
content: [
|
|
1098
|
+
{
|
|
1099
|
+
type: 'text',
|
|
1100
|
+
text: JSON.stringify({
|
|
1101
|
+
success: true,
|
|
1102
|
+
entity_id: args.entity_id,
|
|
1103
|
+
action: args.action,
|
|
1104
|
+
context: result.context,
|
|
1105
|
+
}, null, 2),
|
|
1106
|
+
},
|
|
1107
|
+
],
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
case 'controlClimate': {
|
|
1111
|
+
const args = request.params.arguments;
|
|
1112
|
+
if (!args.entity_id) {
|
|
1113
|
+
throw new Error('entity_id is required');
|
|
1114
|
+
}
|
|
1115
|
+
const results = [];
|
|
1116
|
+
if (args.hvac_mode) {
|
|
1117
|
+
await client.devices.setClimateMode(args.entity_id, args.hvac_mode);
|
|
1118
|
+
results.push({ action: 'set_hvac_mode', success: true });
|
|
1119
|
+
}
|
|
1120
|
+
if (args.temperature !== undefined) {
|
|
1121
|
+
await client.devices.setClimateTemperature(args.entity_id, args.temperature);
|
|
1122
|
+
results.push({ action: 'set_temperature', success: true });
|
|
1123
|
+
}
|
|
1124
|
+
if (args.fan_mode) {
|
|
1125
|
+
await client.devices.setClimateFanMode(args.entity_id, args.fan_mode);
|
|
1126
|
+
results.push({ action: 'set_fan_mode', success: true });
|
|
1127
|
+
}
|
|
1128
|
+
if (args.preset_mode) {
|
|
1129
|
+
await client.devices.setClimatePreset(args.entity_id, args.preset_mode);
|
|
1130
|
+
results.push({ action: 'set_preset_mode', success: true });
|
|
1131
|
+
}
|
|
1132
|
+
return {
|
|
1133
|
+
content: [
|
|
1134
|
+
{
|
|
1135
|
+
type: 'text',
|
|
1136
|
+
text: JSON.stringify({
|
|
1137
|
+
success: true,
|
|
1138
|
+
entity_id: args.entity_id,
|
|
1139
|
+
actions: results,
|
|
1140
|
+
}, null, 2),
|
|
1141
|
+
},
|
|
1142
|
+
],
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
case 'controlMediaPlayer': {
|
|
1146
|
+
const args = request.params.arguments;
|
|
1147
|
+
if (!args.entity_id || !args.action) {
|
|
1148
|
+
throw new Error('entity_id and action are required');
|
|
1149
|
+
}
|
|
1150
|
+
let result;
|
|
1151
|
+
switch (args.action) {
|
|
1152
|
+
case 'play':
|
|
1153
|
+
result = await client.devices.mediaPlay(args.entity_id);
|
|
1154
|
+
break;
|
|
1155
|
+
case 'pause':
|
|
1156
|
+
result = await client.devices.mediaPause(args.entity_id);
|
|
1157
|
+
break;
|
|
1158
|
+
case 'stop':
|
|
1159
|
+
result = await client.devices.mediaStop(args.entity_id);
|
|
1160
|
+
break;
|
|
1161
|
+
case 'next':
|
|
1162
|
+
result = await client.devices.mediaNext(args.entity_id);
|
|
1163
|
+
break;
|
|
1164
|
+
case 'previous':
|
|
1165
|
+
result = await client.devices.mediaPrevious(args.entity_id);
|
|
1166
|
+
break;
|
|
1167
|
+
case 'volume_set':
|
|
1168
|
+
if (args.volume_level === undefined) {
|
|
1169
|
+
throw new Error('volume_level is required for volume_set action');
|
|
1170
|
+
}
|
|
1171
|
+
result = await client.devices.setMediaVolume(args.entity_id, args.volume_level);
|
|
1172
|
+
break;
|
|
1173
|
+
case 'volume_mute':
|
|
1174
|
+
if (args.is_volume_muted === undefined) {
|
|
1175
|
+
throw new Error('is_volume_muted is required for volume_mute action');
|
|
1176
|
+
}
|
|
1177
|
+
result = await client.devices.setMediaMute(args.entity_id, args.is_volume_muted);
|
|
1178
|
+
break;
|
|
1179
|
+
}
|
|
1180
|
+
return {
|
|
1181
|
+
content: [
|
|
1182
|
+
{
|
|
1183
|
+
type: 'text',
|
|
1184
|
+
text: JSON.stringify({
|
|
1185
|
+
success: true,
|
|
1186
|
+
entity_id: args.entity_id,
|
|
1187
|
+
action: args.action,
|
|
1188
|
+
context: result?.context,
|
|
1189
|
+
}, null, 2),
|
|
1190
|
+
},
|
|
1191
|
+
],
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
case 'controlCover': {
|
|
1195
|
+
const args = request.params.arguments;
|
|
1196
|
+
if (!args.entity_id || !args.action) {
|
|
1197
|
+
throw new Error('entity_id and action are required');
|
|
1198
|
+
}
|
|
1199
|
+
let result;
|
|
1200
|
+
switch (args.action) {
|
|
1201
|
+
case 'open':
|
|
1202
|
+
result = await client.devices.openCover(args.entity_id);
|
|
1203
|
+
break;
|
|
1204
|
+
case 'close':
|
|
1205
|
+
result = await client.devices.closeCover(args.entity_id);
|
|
1206
|
+
break;
|
|
1207
|
+
case 'stop':
|
|
1208
|
+
result = await client.devices.stopCover(args.entity_id);
|
|
1209
|
+
break;
|
|
1210
|
+
case 'set_position':
|
|
1211
|
+
if (args.position === undefined) {
|
|
1212
|
+
throw new Error('position is required for set_position action');
|
|
1213
|
+
}
|
|
1214
|
+
result = await client.devices.setCoverPosition(args.entity_id, args.position);
|
|
1215
|
+
break;
|
|
1216
|
+
case 'set_tilt':
|
|
1217
|
+
if (args.tilt_position === undefined) {
|
|
1218
|
+
throw new Error('tilt_position is required for set_tilt action');
|
|
1219
|
+
}
|
|
1220
|
+
result = await client.devices.setCoverTilt(args.entity_id, args.tilt_position);
|
|
1221
|
+
break;
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
content: [
|
|
1225
|
+
{
|
|
1226
|
+
type: 'text',
|
|
1227
|
+
text: JSON.stringify({
|
|
1228
|
+
success: true,
|
|
1229
|
+
entity_id: args.entity_id,
|
|
1230
|
+
action: args.action,
|
|
1231
|
+
context: result?.context,
|
|
1232
|
+
}, null, 2),
|
|
1233
|
+
},
|
|
1234
|
+
],
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
case 'controlFan': {
|
|
1238
|
+
const args = request.params.arguments;
|
|
1239
|
+
if (!args.entity_id || !args.action) {
|
|
1240
|
+
throw new Error('entity_id and action are required');
|
|
1241
|
+
}
|
|
1242
|
+
let result;
|
|
1243
|
+
switch (args.action) {
|
|
1244
|
+
case 'turn_on':
|
|
1245
|
+
result = await client.devices.turnOnFan(args.entity_id, { percentage: args.percentage });
|
|
1246
|
+
break;
|
|
1247
|
+
case 'turn_off':
|
|
1248
|
+
result = await client.devices.turnOffFan(args.entity_id);
|
|
1249
|
+
break;
|
|
1250
|
+
case 'toggle':
|
|
1251
|
+
result = await client.callService({ domain: 'fan', service: 'toggle', target: { entity_id: args.entity_id } });
|
|
1252
|
+
break;
|
|
1253
|
+
case 'set_speed':
|
|
1254
|
+
if (args.percentage === undefined) {
|
|
1255
|
+
throw new Error('percentage is required for set_speed action');
|
|
1256
|
+
}
|
|
1257
|
+
result = await client.devices.setFanSpeed(args.entity_id, args.percentage);
|
|
1258
|
+
break;
|
|
1259
|
+
case 'oscillate':
|
|
1260
|
+
if (args.oscillating === undefined) {
|
|
1261
|
+
throw new Error('oscillating is required for oscillate action');
|
|
1262
|
+
}
|
|
1263
|
+
result = await client.devices.setFanOscillation(args.entity_id, args.oscillating);
|
|
1264
|
+
break;
|
|
1265
|
+
case 'set_direction':
|
|
1266
|
+
if (!args.direction) {
|
|
1267
|
+
throw new Error('direction is required for set_direction action');
|
|
1268
|
+
}
|
|
1269
|
+
result = await client.devices.setFanDirection(args.entity_id, args.direction);
|
|
1270
|
+
break;
|
|
1271
|
+
}
|
|
1272
|
+
return {
|
|
1273
|
+
content: [
|
|
1274
|
+
{
|
|
1275
|
+
type: 'text',
|
|
1276
|
+
text: JSON.stringify({
|
|
1277
|
+
success: true,
|
|
1278
|
+
entity_id: args.entity_id,
|
|
1279
|
+
action: args.action,
|
|
1280
|
+
context: result?.context,
|
|
1281
|
+
}, null, 2),
|
|
1282
|
+
},
|
|
1283
|
+
],
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
case 'activateScene': {
|
|
1287
|
+
const { entity_id } = request.params.arguments;
|
|
1288
|
+
if (!entity_id) {
|
|
1289
|
+
throw new Error('entity_id is required');
|
|
1290
|
+
}
|
|
1291
|
+
const result = await client.devices.activateScene(entity_id);
|
|
1292
|
+
return {
|
|
1293
|
+
content: [
|
|
1294
|
+
{
|
|
1295
|
+
type: 'text',
|
|
1296
|
+
text: JSON.stringify({
|
|
1297
|
+
success: true,
|
|
1298
|
+
entity_id,
|
|
1299
|
+
message: 'Scene activated',
|
|
1300
|
+
context: result.context,
|
|
1301
|
+
}, null, 2),
|
|
1302
|
+
},
|
|
1303
|
+
],
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
case 'runScript': {
|
|
1307
|
+
const { entity_id, variables } = request.params.arguments;
|
|
1308
|
+
if (!entity_id) {
|
|
1309
|
+
throw new Error('entity_id is required');
|
|
1310
|
+
}
|
|
1311
|
+
const result = await client.devices.runScript(entity_id, variables);
|
|
1312
|
+
return {
|
|
1313
|
+
content: [
|
|
1314
|
+
{
|
|
1315
|
+
type: 'text',
|
|
1316
|
+
text: JSON.stringify({
|
|
1317
|
+
success: true,
|
|
1318
|
+
entity_id,
|
|
1319
|
+
message: 'Script started',
|
|
1320
|
+
context: result.context,
|
|
1321
|
+
}, null, 2),
|
|
1322
|
+
},
|
|
1323
|
+
],
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
case 'sendNotification': {
|
|
1327
|
+
const { message, title, target, data } = request.params.arguments;
|
|
1328
|
+
if (!message) {
|
|
1329
|
+
throw new Error('message is required');
|
|
1330
|
+
}
|
|
1331
|
+
const result = await client.devices.sendNotification(message, title, target, data);
|
|
1332
|
+
return {
|
|
1333
|
+
content: [
|
|
1334
|
+
{
|
|
1335
|
+
type: 'text',
|
|
1336
|
+
text: JSON.stringify({
|
|
1337
|
+
success: true,
|
|
1338
|
+
message: 'Notification sent',
|
|
1339
|
+
target: target ?? 'notify',
|
|
1340
|
+
context: result.context,
|
|
1341
|
+
}, null, 2),
|
|
1342
|
+
},
|
|
1343
|
+
],
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
default:
|
|
1347
|
+
return {
|
|
1348
|
+
content: [
|
|
1349
|
+
{
|
|
1350
|
+
type: 'text',
|
|
1351
|
+
text: JSON.stringify({ error: 'Unknown tool', toolName }),
|
|
1352
|
+
},
|
|
1353
|
+
],
|
|
1354
|
+
isError: true,
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
catch (error) {
|
|
1359
|
+
logger.error('Tool execution failed', {
|
|
1360
|
+
toolName,
|
|
1361
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1362
|
+
});
|
|
1363
|
+
return {
|
|
1364
|
+
content: [
|
|
1365
|
+
{
|
|
1366
|
+
type: 'text',
|
|
1367
|
+
text: JSON.stringify({
|
|
1368
|
+
error: `Failed to execute ${toolName}`,
|
|
1369
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1370
|
+
}),
|
|
1371
|
+
},
|
|
1372
|
+
],
|
|
1373
|
+
isError: true,
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
logger.info('All tools registered successfully', {
|
|
1378
|
+
toolCount: 33,
|
|
1379
|
+
aiEnabled: !!aiClient,
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
//# sourceMappingURL=registry.js.map
|