@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,653 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Device Control Operations
|
|
3
|
+
* Provides high-level device control with type-safe parameters
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ServiceCallResponse } from '../types/index.js';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import type { ServiceOperations } from './services.js';
|
|
9
|
+
import type { StateOperations } from './states.js';
|
|
10
|
+
|
|
11
|
+
export interface LightControlOptions {
|
|
12
|
+
brightness?: number; // 0-255
|
|
13
|
+
brightness_pct?: number; // 0-100
|
|
14
|
+
color_temp?: number; // Mireds
|
|
15
|
+
color_temp_kelvin?: number;
|
|
16
|
+
rgb_color?: [number, number, number];
|
|
17
|
+
rgbw_color?: [number, number, number, number];
|
|
18
|
+
rgbww_color?: [number, number, number, number, number];
|
|
19
|
+
hs_color?: [number, number]; // Hue (0-360), Saturation (0-100)
|
|
20
|
+
xy_color?: [number, number];
|
|
21
|
+
color_name?: string;
|
|
22
|
+
effect?: string;
|
|
23
|
+
flash?: 'short' | 'long';
|
|
24
|
+
transition?: number; // Seconds
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ClimateControlOptions {
|
|
28
|
+
temperature?: number;
|
|
29
|
+
target_temp_high?: number;
|
|
30
|
+
target_temp_low?: number;
|
|
31
|
+
hvac_mode?: 'off' | 'heat' | 'cool' | 'heat_cool' | 'auto' | 'dry' | 'fan_only';
|
|
32
|
+
fan_mode?: string;
|
|
33
|
+
swing_mode?: string;
|
|
34
|
+
preset_mode?: string;
|
|
35
|
+
humidity?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MediaPlayerControlOptions {
|
|
39
|
+
volume_level?: number; // 0-1
|
|
40
|
+
is_volume_muted?: boolean;
|
|
41
|
+
media_content_id?: string;
|
|
42
|
+
media_content_type?: string;
|
|
43
|
+
enqueue?: 'play' | 'next' | 'add' | 'replace';
|
|
44
|
+
seek_position?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface CoverControlOptions {
|
|
48
|
+
position?: number; // 0-100 (0 = closed, 100 = open)
|
|
49
|
+
tilt_position?: number; // 0-100
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface FanControlOptions {
|
|
53
|
+
percentage?: number; // 0-100
|
|
54
|
+
preset_mode?: string;
|
|
55
|
+
oscillating?: boolean;
|
|
56
|
+
direction?: 'forward' | 'reverse';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Enhanced device control operations
|
|
61
|
+
*/
|
|
62
|
+
export class DeviceOperations {
|
|
63
|
+
constructor(
|
|
64
|
+
private readonly serviceOps: ServiceOperations,
|
|
65
|
+
private readonly stateOps: StateOperations
|
|
66
|
+
) {
|
|
67
|
+
// stateOps reserved for future use (e.g., checking device capabilities before control)
|
|
68
|
+
void this.stateOps;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ===== Light Control =====
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Turn on a light with optional settings
|
|
75
|
+
*/
|
|
76
|
+
async turnOnLight(entityId: string, options?: LightControlOptions): Promise<ServiceCallResponse> {
|
|
77
|
+
logger.info('Turning on light', { entityId, options });
|
|
78
|
+
|
|
79
|
+
return this.serviceOps.callService({
|
|
80
|
+
domain: 'light',
|
|
81
|
+
service: 'turn_on',
|
|
82
|
+
target: { entity_id: entityId },
|
|
83
|
+
service_data: options as Record<string, unknown>,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Turn off a light
|
|
89
|
+
*/
|
|
90
|
+
async turnOffLight(entityId: string, transition?: number): Promise<ServiceCallResponse> {
|
|
91
|
+
logger.info('Turning off light', { entityId, transition });
|
|
92
|
+
|
|
93
|
+
return this.serviceOps.callService({
|
|
94
|
+
domain: 'light',
|
|
95
|
+
service: 'turn_off',
|
|
96
|
+
target: { entity_id: entityId },
|
|
97
|
+
service_data: transition ? { transition } : undefined,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Toggle a light
|
|
103
|
+
*/
|
|
104
|
+
async toggleLight(entityId: string): Promise<ServiceCallResponse> {
|
|
105
|
+
logger.info('Toggling light', { entityId });
|
|
106
|
+
|
|
107
|
+
return this.serviceOps.callService({
|
|
108
|
+
domain: 'light',
|
|
109
|
+
service: 'toggle',
|
|
110
|
+
target: { entity_id: entityId },
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Set light brightness (percentage)
|
|
116
|
+
*/
|
|
117
|
+
async setLightBrightness(entityId: string, brightness: number, transition?: number): Promise<ServiceCallResponse> {
|
|
118
|
+
logger.info('Setting light brightness', { entityId, brightness, transition });
|
|
119
|
+
|
|
120
|
+
return this.serviceOps.callService({
|
|
121
|
+
domain: 'light',
|
|
122
|
+
service: 'turn_on',
|
|
123
|
+
target: { entity_id: entityId },
|
|
124
|
+
service_data: {
|
|
125
|
+
brightness_pct: Math.max(0, Math.min(100, brightness)),
|
|
126
|
+
...(transition ? { transition } : {}),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Set light color (RGB)
|
|
133
|
+
*/
|
|
134
|
+
async setLightColor(entityId: string, rgb: [number, number, number], brightness?: number): Promise<ServiceCallResponse> {
|
|
135
|
+
logger.info('Setting light color', { entityId, rgb, brightness });
|
|
136
|
+
|
|
137
|
+
return this.serviceOps.callService({
|
|
138
|
+
domain: 'light',
|
|
139
|
+
service: 'turn_on',
|
|
140
|
+
target: { entity_id: entityId },
|
|
141
|
+
service_data: {
|
|
142
|
+
rgb_color: rgb,
|
|
143
|
+
...(brightness !== undefined ? { brightness_pct: brightness } : {}),
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Set light color temperature (Kelvin)
|
|
150
|
+
*/
|
|
151
|
+
async setLightColorTemp(entityId: string, kelvin: number, brightness?: number): Promise<ServiceCallResponse> {
|
|
152
|
+
logger.info('Setting light color temperature', { entityId, kelvin, brightness });
|
|
153
|
+
|
|
154
|
+
return this.serviceOps.callService({
|
|
155
|
+
domain: 'light',
|
|
156
|
+
service: 'turn_on',
|
|
157
|
+
target: { entity_id: entityId },
|
|
158
|
+
service_data: {
|
|
159
|
+
color_temp_kelvin: kelvin,
|
|
160
|
+
...(brightness !== undefined ? { brightness_pct: brightness } : {}),
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ===== Climate Control =====
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Set climate temperature
|
|
169
|
+
*/
|
|
170
|
+
async setClimateTemperature(entityId: string, temperature: number): Promise<ServiceCallResponse> {
|
|
171
|
+
logger.info('Setting climate temperature', { entityId, temperature });
|
|
172
|
+
|
|
173
|
+
return this.serviceOps.callService({
|
|
174
|
+
domain: 'climate',
|
|
175
|
+
service: 'set_temperature',
|
|
176
|
+
target: { entity_id: entityId },
|
|
177
|
+
service_data: { temperature },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Set climate HVAC mode
|
|
183
|
+
*/
|
|
184
|
+
async setClimateMode(entityId: string, mode: ClimateControlOptions['hvac_mode']): Promise<ServiceCallResponse> {
|
|
185
|
+
logger.info('Setting climate mode', { entityId, mode });
|
|
186
|
+
|
|
187
|
+
return this.serviceOps.callService({
|
|
188
|
+
domain: 'climate',
|
|
189
|
+
service: 'set_hvac_mode',
|
|
190
|
+
target: { entity_id: entityId },
|
|
191
|
+
service_data: { hvac_mode: mode },
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Set climate fan mode
|
|
197
|
+
*/
|
|
198
|
+
async setClimateFanMode(entityId: string, fanMode: string): Promise<ServiceCallResponse> {
|
|
199
|
+
logger.info('Setting climate fan mode', { entityId, fanMode });
|
|
200
|
+
|
|
201
|
+
return this.serviceOps.callService({
|
|
202
|
+
domain: 'climate',
|
|
203
|
+
service: 'set_fan_mode',
|
|
204
|
+
target: { entity_id: entityId },
|
|
205
|
+
service_data: { fan_mode: fanMode },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Set climate preset mode
|
|
211
|
+
*/
|
|
212
|
+
async setClimatePreset(entityId: string, preset: string): Promise<ServiceCallResponse> {
|
|
213
|
+
logger.info('Setting climate preset', { entityId, preset });
|
|
214
|
+
|
|
215
|
+
return this.serviceOps.callService({
|
|
216
|
+
domain: 'climate',
|
|
217
|
+
service: 'set_preset_mode',
|
|
218
|
+
target: { entity_id: entityId },
|
|
219
|
+
service_data: { preset_mode: preset },
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Turn off climate
|
|
225
|
+
*/
|
|
226
|
+
async turnOffClimate(entityId: string): Promise<ServiceCallResponse> {
|
|
227
|
+
logger.info('Turning off climate', { entityId });
|
|
228
|
+
|
|
229
|
+
return this.serviceOps.callService({
|
|
230
|
+
domain: 'climate',
|
|
231
|
+
service: 'turn_off',
|
|
232
|
+
target: { entity_id: entityId },
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ===== Media Player Control =====
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Play media
|
|
240
|
+
*/
|
|
241
|
+
async mediaPlay(entityId: string): Promise<ServiceCallResponse> {
|
|
242
|
+
logger.info('Playing media', { entityId });
|
|
243
|
+
|
|
244
|
+
return this.serviceOps.callService({
|
|
245
|
+
domain: 'media_player',
|
|
246
|
+
service: 'media_play',
|
|
247
|
+
target: { entity_id: entityId },
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Pause media
|
|
253
|
+
*/
|
|
254
|
+
async mediaPause(entityId: string): Promise<ServiceCallResponse> {
|
|
255
|
+
logger.info('Pausing media', { entityId });
|
|
256
|
+
|
|
257
|
+
return this.serviceOps.callService({
|
|
258
|
+
domain: 'media_player',
|
|
259
|
+
service: 'media_pause',
|
|
260
|
+
target: { entity_id: entityId },
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Stop media
|
|
266
|
+
*/
|
|
267
|
+
async mediaStop(entityId: string): Promise<ServiceCallResponse> {
|
|
268
|
+
logger.info('Stopping media', { entityId });
|
|
269
|
+
|
|
270
|
+
return this.serviceOps.callService({
|
|
271
|
+
domain: 'media_player',
|
|
272
|
+
service: 'media_stop',
|
|
273
|
+
target: { entity_id: entityId },
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Next track
|
|
279
|
+
*/
|
|
280
|
+
async mediaNext(entityId: string): Promise<ServiceCallResponse> {
|
|
281
|
+
logger.info('Next track', { entityId });
|
|
282
|
+
|
|
283
|
+
return this.serviceOps.callService({
|
|
284
|
+
domain: 'media_player',
|
|
285
|
+
service: 'media_next_track',
|
|
286
|
+
target: { entity_id: entityId },
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Previous track
|
|
292
|
+
*/
|
|
293
|
+
async mediaPrevious(entityId: string): Promise<ServiceCallResponse> {
|
|
294
|
+
logger.info('Previous track', { entityId });
|
|
295
|
+
|
|
296
|
+
return this.serviceOps.callService({
|
|
297
|
+
domain: 'media_player',
|
|
298
|
+
service: 'media_previous_track',
|
|
299
|
+
target: { entity_id: entityId },
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Set media player volume
|
|
305
|
+
*/
|
|
306
|
+
async setMediaVolume(entityId: string, volume: number): Promise<ServiceCallResponse> {
|
|
307
|
+
logger.info('Setting media volume', { entityId, volume });
|
|
308
|
+
|
|
309
|
+
return this.serviceOps.callService({
|
|
310
|
+
domain: 'media_player',
|
|
311
|
+
service: 'volume_set',
|
|
312
|
+
target: { entity_id: entityId },
|
|
313
|
+
service_data: { volume_level: Math.max(0, Math.min(1, volume)) },
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Mute/unmute media player
|
|
319
|
+
*/
|
|
320
|
+
async setMediaMute(entityId: string, mute: boolean): Promise<ServiceCallResponse> {
|
|
321
|
+
logger.info('Setting media mute', { entityId, mute });
|
|
322
|
+
|
|
323
|
+
return this.serviceOps.callService({
|
|
324
|
+
domain: 'media_player',
|
|
325
|
+
service: 'volume_mute',
|
|
326
|
+
target: { entity_id: entityId },
|
|
327
|
+
service_data: { is_volume_muted: mute },
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Play specific media content
|
|
333
|
+
*/
|
|
334
|
+
async playMedia(entityId: string, contentId: string, contentType: string): Promise<ServiceCallResponse> {
|
|
335
|
+
logger.info('Playing specific media', { entityId, contentId, contentType });
|
|
336
|
+
|
|
337
|
+
return this.serviceOps.callService({
|
|
338
|
+
domain: 'media_player',
|
|
339
|
+
service: 'play_media',
|
|
340
|
+
target: { entity_id: entityId },
|
|
341
|
+
service_data: {
|
|
342
|
+
media_content_id: contentId,
|
|
343
|
+
media_content_type: contentType,
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ===== Cover/Blind Control =====
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Open a cover
|
|
352
|
+
*/
|
|
353
|
+
async openCover(entityId: string): Promise<ServiceCallResponse> {
|
|
354
|
+
logger.info('Opening cover', { entityId });
|
|
355
|
+
|
|
356
|
+
return this.serviceOps.callService({
|
|
357
|
+
domain: 'cover',
|
|
358
|
+
service: 'open_cover',
|
|
359
|
+
target: { entity_id: entityId },
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Close a cover
|
|
365
|
+
*/
|
|
366
|
+
async closeCover(entityId: string): Promise<ServiceCallResponse> {
|
|
367
|
+
logger.info('Closing cover', { entityId });
|
|
368
|
+
|
|
369
|
+
return this.serviceOps.callService({
|
|
370
|
+
domain: 'cover',
|
|
371
|
+
service: 'close_cover',
|
|
372
|
+
target: { entity_id: entityId },
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Stop a cover
|
|
378
|
+
*/
|
|
379
|
+
async stopCover(entityId: string): Promise<ServiceCallResponse> {
|
|
380
|
+
logger.info('Stopping cover', { entityId });
|
|
381
|
+
|
|
382
|
+
return this.serviceOps.callService({
|
|
383
|
+
domain: 'cover',
|
|
384
|
+
service: 'stop_cover',
|
|
385
|
+
target: { entity_id: entityId },
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Set cover position
|
|
391
|
+
*/
|
|
392
|
+
async setCoverPosition(entityId: string, position: number): Promise<ServiceCallResponse> {
|
|
393
|
+
logger.info('Setting cover position', { entityId, position });
|
|
394
|
+
|
|
395
|
+
return this.serviceOps.callService({
|
|
396
|
+
domain: 'cover',
|
|
397
|
+
service: 'set_cover_position',
|
|
398
|
+
target: { entity_id: entityId },
|
|
399
|
+
service_data: { position: Math.max(0, Math.min(100, position)) },
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Set cover tilt position
|
|
405
|
+
*/
|
|
406
|
+
async setCoverTilt(entityId: string, tiltPosition: number): Promise<ServiceCallResponse> {
|
|
407
|
+
logger.info('Setting cover tilt', { entityId, tiltPosition });
|
|
408
|
+
|
|
409
|
+
return this.serviceOps.callService({
|
|
410
|
+
domain: 'cover',
|
|
411
|
+
service: 'set_cover_tilt_position',
|
|
412
|
+
target: { entity_id: entityId },
|
|
413
|
+
service_data: { tilt_position: Math.max(0, Math.min(100, tiltPosition)) },
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ===== Fan Control =====
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Turn on a fan
|
|
421
|
+
*/
|
|
422
|
+
async turnOnFan(entityId: string, options?: FanControlOptions): Promise<ServiceCallResponse> {
|
|
423
|
+
logger.info('Turning on fan', { entityId, options });
|
|
424
|
+
|
|
425
|
+
return this.serviceOps.callService({
|
|
426
|
+
domain: 'fan',
|
|
427
|
+
service: 'turn_on',
|
|
428
|
+
target: { entity_id: entityId },
|
|
429
|
+
service_data: options as Record<string, unknown>,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Turn off a fan
|
|
435
|
+
*/
|
|
436
|
+
async turnOffFan(entityId: string): Promise<ServiceCallResponse> {
|
|
437
|
+
logger.info('Turning off fan', { entityId });
|
|
438
|
+
|
|
439
|
+
return this.serviceOps.callService({
|
|
440
|
+
domain: 'fan',
|
|
441
|
+
service: 'turn_off',
|
|
442
|
+
target: { entity_id: entityId },
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Set fan speed percentage
|
|
448
|
+
*/
|
|
449
|
+
async setFanSpeed(entityId: string, percentage: number): Promise<ServiceCallResponse> {
|
|
450
|
+
logger.info('Setting fan speed', { entityId, percentage });
|
|
451
|
+
|
|
452
|
+
return this.serviceOps.callService({
|
|
453
|
+
domain: 'fan',
|
|
454
|
+
service: 'set_percentage',
|
|
455
|
+
target: { entity_id: entityId },
|
|
456
|
+
service_data: { percentage: Math.max(0, Math.min(100, percentage)) },
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Set fan oscillation
|
|
462
|
+
*/
|
|
463
|
+
async setFanOscillation(entityId: string, oscillating: boolean): Promise<ServiceCallResponse> {
|
|
464
|
+
logger.info('Setting fan oscillation', { entityId, oscillating });
|
|
465
|
+
|
|
466
|
+
return this.serviceOps.callService({
|
|
467
|
+
domain: 'fan',
|
|
468
|
+
service: 'oscillate',
|
|
469
|
+
target: { entity_id: entityId },
|
|
470
|
+
service_data: { oscillating },
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Set fan direction
|
|
476
|
+
*/
|
|
477
|
+
async setFanDirection(entityId: string, direction: 'forward' | 'reverse'): Promise<ServiceCallResponse> {
|
|
478
|
+
logger.info('Setting fan direction', { entityId, direction });
|
|
479
|
+
|
|
480
|
+
return this.serviceOps.callService({
|
|
481
|
+
domain: 'fan',
|
|
482
|
+
service: 'set_direction',
|
|
483
|
+
target: { entity_id: entityId },
|
|
484
|
+
service_data: { direction },
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// ===== Lock Control =====
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Lock a lock
|
|
492
|
+
*/
|
|
493
|
+
async lock(entityId: string): Promise<ServiceCallResponse> {
|
|
494
|
+
logger.info('Locking', { entityId });
|
|
495
|
+
|
|
496
|
+
return this.serviceOps.callService({
|
|
497
|
+
domain: 'lock',
|
|
498
|
+
service: 'lock',
|
|
499
|
+
target: { entity_id: entityId },
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Unlock a lock
|
|
505
|
+
*/
|
|
506
|
+
async unlock(entityId: string): Promise<ServiceCallResponse> {
|
|
507
|
+
logger.info('Unlocking', { entityId });
|
|
508
|
+
|
|
509
|
+
return this.serviceOps.callService({
|
|
510
|
+
domain: 'lock',
|
|
511
|
+
service: 'unlock',
|
|
512
|
+
target: { entity_id: entityId },
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ===== Vacuum Control =====
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Start vacuum
|
|
520
|
+
*/
|
|
521
|
+
async startVacuum(entityId: string): Promise<ServiceCallResponse> {
|
|
522
|
+
logger.info('Starting vacuum', { entityId });
|
|
523
|
+
|
|
524
|
+
return this.serviceOps.callService({
|
|
525
|
+
domain: 'vacuum',
|
|
526
|
+
service: 'start',
|
|
527
|
+
target: { entity_id: entityId },
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Stop vacuum
|
|
533
|
+
*/
|
|
534
|
+
async stopVacuum(entityId: string): Promise<ServiceCallResponse> {
|
|
535
|
+
logger.info('Stopping vacuum', { entityId });
|
|
536
|
+
|
|
537
|
+
return this.serviceOps.callService({
|
|
538
|
+
domain: 'vacuum',
|
|
539
|
+
service: 'stop',
|
|
540
|
+
target: { entity_id: entityId },
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Pause vacuum
|
|
546
|
+
*/
|
|
547
|
+
async pauseVacuum(entityId: string): Promise<ServiceCallResponse> {
|
|
548
|
+
logger.info('Pausing vacuum', { entityId });
|
|
549
|
+
|
|
550
|
+
return this.serviceOps.callService({
|
|
551
|
+
domain: 'vacuum',
|
|
552
|
+
service: 'pause',
|
|
553
|
+
target: { entity_id: entityId },
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Return vacuum to base
|
|
559
|
+
*/
|
|
560
|
+
async returnVacuumToBase(entityId: string): Promise<ServiceCallResponse> {
|
|
561
|
+
logger.info('Returning vacuum to base', { entityId });
|
|
562
|
+
|
|
563
|
+
return this.serviceOps.callService({
|
|
564
|
+
domain: 'vacuum',
|
|
565
|
+
service: 'return_to_base',
|
|
566
|
+
target: { entity_id: entityId },
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Set vacuum fan speed
|
|
572
|
+
*/
|
|
573
|
+
async setVacuumFanSpeed(entityId: string, fanSpeed: string): Promise<ServiceCallResponse> {
|
|
574
|
+
logger.info('Setting vacuum fan speed', { entityId, fanSpeed });
|
|
575
|
+
|
|
576
|
+
return this.serviceOps.callService({
|
|
577
|
+
domain: 'vacuum',
|
|
578
|
+
service: 'set_fan_speed',
|
|
579
|
+
target: { entity_id: entityId },
|
|
580
|
+
service_data: { fan_speed: fanSpeed },
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ===== Scene Control =====
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Activate a scene
|
|
588
|
+
*/
|
|
589
|
+
async activateScene(entityId: string): Promise<ServiceCallResponse> {
|
|
590
|
+
logger.info('Activating scene', { entityId });
|
|
591
|
+
|
|
592
|
+
return this.serviceOps.callService({
|
|
593
|
+
domain: 'scene',
|
|
594
|
+
service: 'turn_on',
|
|
595
|
+
target: { entity_id: entityId },
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// ===== Script Control =====
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Run a script
|
|
603
|
+
*/
|
|
604
|
+
async runScript(entityId: string, variables?: Record<string, unknown>): Promise<ServiceCallResponse> {
|
|
605
|
+
logger.info('Running script', { entityId, variables });
|
|
606
|
+
|
|
607
|
+
return this.serviceOps.callService({
|
|
608
|
+
domain: 'script',
|
|
609
|
+
service: 'turn_on',
|
|
610
|
+
target: { entity_id: entityId },
|
|
611
|
+
service_data: variables,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Stop a running script
|
|
617
|
+
*/
|
|
618
|
+
async stopScript(entityId: string): Promise<ServiceCallResponse> {
|
|
619
|
+
logger.info('Stopping script', { entityId });
|
|
620
|
+
|
|
621
|
+
return this.serviceOps.callService({
|
|
622
|
+
domain: 'script',
|
|
623
|
+
service: 'turn_off',
|
|
624
|
+
target: { entity_id: entityId },
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// ===== Notification =====
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Send a notification
|
|
632
|
+
*/
|
|
633
|
+
async sendNotification(
|
|
634
|
+
message: string,
|
|
635
|
+
title?: string,
|
|
636
|
+
target?: string,
|
|
637
|
+
data?: Record<string, unknown>
|
|
638
|
+
): Promise<ServiceCallResponse> {
|
|
639
|
+
logger.info('Sending notification', { title, target });
|
|
640
|
+
|
|
641
|
+
const service = target ?? 'notify';
|
|
642
|
+
|
|
643
|
+
return this.serviceOps.callService({
|
|
644
|
+
domain: 'notify',
|
|
645
|
+
service,
|
|
646
|
+
service_data: {
|
|
647
|
+
message,
|
|
648
|
+
...(title ? { title } : {}),
|
|
649
|
+
...(data ? { data } : {}),
|
|
650
|
+
},
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|