@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,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home Assistant WebSocket Event Subscription
|
|
3
|
+
* Connects to Home Assistant WebSocket API to receive real-time events
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
export interface HaEvent {
|
|
7
|
+
event_type: string;
|
|
8
|
+
data: {
|
|
9
|
+
entity_id?: string;
|
|
10
|
+
old_state?: {
|
|
11
|
+
entity_id: string;
|
|
12
|
+
state: string;
|
|
13
|
+
attributes: Record<string, unknown>;
|
|
14
|
+
last_changed: string;
|
|
15
|
+
last_updated: string;
|
|
16
|
+
} | null;
|
|
17
|
+
new_state?: {
|
|
18
|
+
entity_id: string;
|
|
19
|
+
state: string;
|
|
20
|
+
attributes: Record<string, unknown>;
|
|
21
|
+
last_changed: string;
|
|
22
|
+
last_updated: string;
|
|
23
|
+
} | null;
|
|
24
|
+
domain?: string;
|
|
25
|
+
service?: string;
|
|
26
|
+
service_data?: Record<string, unknown>;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
origin: string;
|
|
30
|
+
time_fired: string;
|
|
31
|
+
context: {
|
|
32
|
+
id: string;
|
|
33
|
+
parent_id: string | null;
|
|
34
|
+
user_id: string | null;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface EventSubscription {
|
|
38
|
+
id: string;
|
|
39
|
+
subscriptionId: number;
|
|
40
|
+
eventType?: string;
|
|
41
|
+
domain?: string;
|
|
42
|
+
entityId?: string;
|
|
43
|
+
callback: (event: HaEvent) => void;
|
|
44
|
+
}
|
|
45
|
+
export interface EventSubscriberConfig {
|
|
46
|
+
baseUrl: string;
|
|
47
|
+
token: string;
|
|
48
|
+
reconnectInterval?: number;
|
|
49
|
+
maxReconnectAttempts?: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Home Assistant Event Subscriber
|
|
53
|
+
* Subscribes to real-time events via WebSocket
|
|
54
|
+
*/
|
|
55
|
+
export declare class EventSubscriber extends EventEmitter {
|
|
56
|
+
private ws;
|
|
57
|
+
private messageId;
|
|
58
|
+
private authenticated;
|
|
59
|
+
private subscriptions;
|
|
60
|
+
private pendingSubscriptions;
|
|
61
|
+
private reconnectAttempts;
|
|
62
|
+
private reconnectTimer;
|
|
63
|
+
private isConnecting;
|
|
64
|
+
private shouldReconnect;
|
|
65
|
+
private readonly config;
|
|
66
|
+
private readonly reconnectInterval;
|
|
67
|
+
private readonly maxReconnectAttempts;
|
|
68
|
+
constructor(config: EventSubscriberConfig);
|
|
69
|
+
/**
|
|
70
|
+
* Connect to Home Assistant WebSocket API
|
|
71
|
+
*/
|
|
72
|
+
connect(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Handle incoming WebSocket messages
|
|
75
|
+
*/
|
|
76
|
+
private handleMessage;
|
|
77
|
+
/**
|
|
78
|
+
* Handle result messages (responses to commands)
|
|
79
|
+
*/
|
|
80
|
+
private handleResult;
|
|
81
|
+
/**
|
|
82
|
+
* Handle event messages
|
|
83
|
+
*/
|
|
84
|
+
private handleEvent;
|
|
85
|
+
/**
|
|
86
|
+
* Check if an event matches a subscription's filters
|
|
87
|
+
*/
|
|
88
|
+
private eventMatchesSubscription;
|
|
89
|
+
/**
|
|
90
|
+
* Handle disconnection and attempt reconnect
|
|
91
|
+
*/
|
|
92
|
+
private handleDisconnect;
|
|
93
|
+
/**
|
|
94
|
+
* Subscribe to all events
|
|
95
|
+
*/
|
|
96
|
+
subscribeEvents(callback: (event: HaEvent) => void): Promise<string>;
|
|
97
|
+
/**
|
|
98
|
+
* Subscribe to specific event type
|
|
99
|
+
*/
|
|
100
|
+
subscribeEventType(eventType: string, callback: (event: HaEvent) => void): Promise<string>;
|
|
101
|
+
/**
|
|
102
|
+
* Subscribe to state changes for a specific domain
|
|
103
|
+
*/
|
|
104
|
+
subscribeDomain(domain: string, callback: (event: HaEvent) => void): Promise<string>;
|
|
105
|
+
/**
|
|
106
|
+
* Subscribe to state changes for a specific entity
|
|
107
|
+
*/
|
|
108
|
+
subscribeEntity(entityId: string, callback: (event: HaEvent) => void): Promise<string>;
|
|
109
|
+
/**
|
|
110
|
+
* Internal subscribe method
|
|
111
|
+
*/
|
|
112
|
+
private subscribe;
|
|
113
|
+
/**
|
|
114
|
+
* Send subscribe_events command
|
|
115
|
+
*/
|
|
116
|
+
private sendSubscribeEvents;
|
|
117
|
+
/**
|
|
118
|
+
* Unsubscribe from events
|
|
119
|
+
*/
|
|
120
|
+
unsubscribe(subscriptionId: string): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Send a message over WebSocket
|
|
123
|
+
*/
|
|
124
|
+
private sendMessage;
|
|
125
|
+
/**
|
|
126
|
+
* Disconnect from Home Assistant
|
|
127
|
+
*/
|
|
128
|
+
disconnect(): void;
|
|
129
|
+
/**
|
|
130
|
+
* Check if connected
|
|
131
|
+
*/
|
|
132
|
+
isConnected(): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Get active subscription count
|
|
135
|
+
*/
|
|
136
|
+
getSubscriptionCount(): number;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/haClient/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home Assistant WebSocket Event Subscription
|
|
3
|
+
* Connects to Home Assistant WebSocket API to receive real-time events
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
import WebSocket from 'ws';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* Home Assistant Event Subscriber
|
|
10
|
+
* Subscribes to real-time events via WebSocket
|
|
11
|
+
*/
|
|
12
|
+
export class EventSubscriber extends EventEmitter {
|
|
13
|
+
ws = null;
|
|
14
|
+
messageId = 1;
|
|
15
|
+
authenticated = false;
|
|
16
|
+
subscriptions = new Map();
|
|
17
|
+
pendingSubscriptions = [];
|
|
18
|
+
reconnectAttempts = 0;
|
|
19
|
+
reconnectTimer = null;
|
|
20
|
+
isConnecting = false;
|
|
21
|
+
shouldReconnect = true;
|
|
22
|
+
config;
|
|
23
|
+
reconnectInterval;
|
|
24
|
+
maxReconnectAttempts;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
super();
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.reconnectInterval = config.reconnectInterval ?? 5000;
|
|
29
|
+
this.maxReconnectAttempts = config.maxReconnectAttempts ?? 10;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Connect to Home Assistant WebSocket API
|
|
33
|
+
*/
|
|
34
|
+
async connect() {
|
|
35
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
36
|
+
logger.debug('WebSocket already connected');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (this.isConnecting) {
|
|
40
|
+
logger.debug('WebSocket connection already in progress');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.isConnecting = true;
|
|
44
|
+
this.shouldReconnect = true;
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
try {
|
|
47
|
+
// Convert HTTP URL to WebSocket URL
|
|
48
|
+
const wsUrl = this.config.baseUrl
|
|
49
|
+
.replace(/^http/, 'ws')
|
|
50
|
+
.replace(/\/$/, '') + '/api/websocket';
|
|
51
|
+
logger.info('Connecting to Home Assistant WebSocket', { url: wsUrl });
|
|
52
|
+
this.ws = new WebSocket(wsUrl);
|
|
53
|
+
this.ws.on('open', () => {
|
|
54
|
+
logger.info('WebSocket connection established');
|
|
55
|
+
this.reconnectAttempts = 0;
|
|
56
|
+
});
|
|
57
|
+
this.ws.on('message', (data) => {
|
|
58
|
+
try {
|
|
59
|
+
const message = JSON.parse(data.toString());
|
|
60
|
+
this.handleMessage(message, resolve, reject);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger.error('Failed to parse WebSocket message', { error });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
this.ws.on('close', (code, reason) => {
|
|
67
|
+
logger.info('WebSocket connection closed', { code, reason: reason.toString() });
|
|
68
|
+
this.authenticated = false;
|
|
69
|
+
this.isConnecting = false;
|
|
70
|
+
this.handleDisconnect();
|
|
71
|
+
});
|
|
72
|
+
this.ws.on('error', (error) => {
|
|
73
|
+
logger.error('WebSocket error', { error: error.message });
|
|
74
|
+
this.isConnecting = false;
|
|
75
|
+
reject(error);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
this.isConnecting = false;
|
|
80
|
+
reject(error);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Handle incoming WebSocket messages
|
|
86
|
+
*/
|
|
87
|
+
handleMessage(message, resolveConnect, rejectConnect) {
|
|
88
|
+
const type = message.type;
|
|
89
|
+
switch (type) {
|
|
90
|
+
case 'auth_required':
|
|
91
|
+
// Send authentication
|
|
92
|
+
this.sendMessage({
|
|
93
|
+
type: 'auth',
|
|
94
|
+
access_token: this.config.token,
|
|
95
|
+
});
|
|
96
|
+
break;
|
|
97
|
+
case 'auth_ok':
|
|
98
|
+
logger.info('WebSocket authentication successful');
|
|
99
|
+
this.authenticated = true;
|
|
100
|
+
this.isConnecting = false;
|
|
101
|
+
this.emit('connected');
|
|
102
|
+
resolveConnect();
|
|
103
|
+
break;
|
|
104
|
+
case 'auth_invalid':
|
|
105
|
+
logger.error('WebSocket authentication failed', { message: message.message });
|
|
106
|
+
this.isConnecting = false;
|
|
107
|
+
rejectConnect(new Error(`Authentication failed: ${message.message}`));
|
|
108
|
+
break;
|
|
109
|
+
case 'result':
|
|
110
|
+
this.handleResult(message);
|
|
111
|
+
break;
|
|
112
|
+
case 'event':
|
|
113
|
+
this.handleEvent(message);
|
|
114
|
+
break;
|
|
115
|
+
default:
|
|
116
|
+
logger.debug('Unhandled WebSocket message type', { type, message });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Handle result messages (responses to commands)
|
|
121
|
+
*/
|
|
122
|
+
handleResult(message) {
|
|
123
|
+
const id = message.id;
|
|
124
|
+
const success = message.success;
|
|
125
|
+
if (this.pendingSubscriptions.length > 0) {
|
|
126
|
+
const pending = this.pendingSubscriptions.shift();
|
|
127
|
+
if (success) {
|
|
128
|
+
pending.resolve(id);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const error = message.error;
|
|
132
|
+
pending.reject(new Error(error?.message ?? 'Subscription failed'));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Handle event messages
|
|
138
|
+
*/
|
|
139
|
+
handleEvent(message) {
|
|
140
|
+
const event = message.event;
|
|
141
|
+
if (!event)
|
|
142
|
+
return;
|
|
143
|
+
// Emit to all matching subscriptions
|
|
144
|
+
for (const sub of this.subscriptions.values()) {
|
|
145
|
+
if (this.eventMatchesSubscription(event, sub)) {
|
|
146
|
+
try {
|
|
147
|
+
sub.callback(event);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
logger.error('Error in event callback', { error, subscriptionId: sub.id });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Also emit on the EventEmitter for generic listeners
|
|
155
|
+
this.emit('event', event);
|
|
156
|
+
this.emit(`event:${event.event_type}`, event);
|
|
157
|
+
if (event.data.entity_id) {
|
|
158
|
+
this.emit(`entity:${event.data.entity_id}`, event);
|
|
159
|
+
}
|
|
160
|
+
if (event.data.domain) {
|
|
161
|
+
this.emit(`domain:${event.data.domain}`, event);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if an event matches a subscription's filters
|
|
166
|
+
*/
|
|
167
|
+
eventMatchesSubscription(event, sub) {
|
|
168
|
+
// Check event type filter
|
|
169
|
+
if (sub.eventType && event.event_type !== sub.eventType) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
// Check domain filter (for state_changed events)
|
|
173
|
+
if (sub.domain && event.event_type === 'state_changed') {
|
|
174
|
+
const entityDomain = event.data.entity_id?.split('.')[0];
|
|
175
|
+
if (entityDomain !== sub.domain) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Check entity filter
|
|
180
|
+
if (sub.entityId && event.data.entity_id !== sub.entityId) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Handle disconnection and attempt reconnect
|
|
187
|
+
*/
|
|
188
|
+
handleDisconnect() {
|
|
189
|
+
if (!this.shouldReconnect) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
193
|
+
logger.error('Max reconnect attempts reached, giving up');
|
|
194
|
+
this.emit('disconnected', new Error('Max reconnect attempts reached'));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
this.reconnectAttempts++;
|
|
198
|
+
const delay = this.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1);
|
|
199
|
+
logger.info('Scheduling reconnect', {
|
|
200
|
+
attempt: this.reconnectAttempts,
|
|
201
|
+
maxAttempts: this.maxReconnectAttempts,
|
|
202
|
+
delayMs: delay,
|
|
203
|
+
});
|
|
204
|
+
this.reconnectTimer = setTimeout(() => {
|
|
205
|
+
this.connect().catch((error) => {
|
|
206
|
+
logger.error('Reconnect failed', { error: error.message });
|
|
207
|
+
});
|
|
208
|
+
}, delay);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Subscribe to all events
|
|
212
|
+
*/
|
|
213
|
+
async subscribeEvents(callback) {
|
|
214
|
+
return this.subscribe({
|
|
215
|
+
callback,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Subscribe to specific event type
|
|
220
|
+
*/
|
|
221
|
+
async subscribeEventType(eventType, callback) {
|
|
222
|
+
return this.subscribe({
|
|
223
|
+
eventType,
|
|
224
|
+
callback,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Subscribe to state changes for a specific domain
|
|
229
|
+
*/
|
|
230
|
+
async subscribeDomain(domain, callback) {
|
|
231
|
+
return this.subscribe({
|
|
232
|
+
eventType: 'state_changed',
|
|
233
|
+
domain,
|
|
234
|
+
callback,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Subscribe to state changes for a specific entity
|
|
239
|
+
*/
|
|
240
|
+
async subscribeEntity(entityId, callback) {
|
|
241
|
+
return this.subscribe({
|
|
242
|
+
eventType: 'state_changed',
|
|
243
|
+
entityId,
|
|
244
|
+
callback,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Internal subscribe method
|
|
249
|
+
*/
|
|
250
|
+
async subscribe(options) {
|
|
251
|
+
if (!this.authenticated) {
|
|
252
|
+
await this.connect();
|
|
253
|
+
}
|
|
254
|
+
const subscriptionId = await this.sendSubscribeEvents(options.eventType);
|
|
255
|
+
const id = `sub_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
256
|
+
this.subscriptions.set(id, {
|
|
257
|
+
id,
|
|
258
|
+
subscriptionId,
|
|
259
|
+
eventType: options.eventType,
|
|
260
|
+
domain: options.domain,
|
|
261
|
+
entityId: options.entityId,
|
|
262
|
+
callback: options.callback,
|
|
263
|
+
});
|
|
264
|
+
logger.info('Created event subscription', {
|
|
265
|
+
id,
|
|
266
|
+
eventType: options.eventType,
|
|
267
|
+
domain: options.domain,
|
|
268
|
+
entityId: options.entityId,
|
|
269
|
+
});
|
|
270
|
+
return id;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Send subscribe_events command
|
|
274
|
+
*/
|
|
275
|
+
sendSubscribeEvents(eventType) {
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
this.pendingSubscriptions.push({ resolve, reject });
|
|
278
|
+
const message = {
|
|
279
|
+
id: this.messageId++,
|
|
280
|
+
type: 'subscribe_events',
|
|
281
|
+
};
|
|
282
|
+
if (eventType) {
|
|
283
|
+
message.event_type = eventType;
|
|
284
|
+
}
|
|
285
|
+
this.sendMessage(message);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Unsubscribe from events
|
|
290
|
+
*/
|
|
291
|
+
async unsubscribe(subscriptionId) {
|
|
292
|
+
const sub = this.subscriptions.get(subscriptionId);
|
|
293
|
+
if (!sub) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// Send unsubscribe command
|
|
297
|
+
this.sendMessage({
|
|
298
|
+
id: this.messageId++,
|
|
299
|
+
type: 'unsubscribe_events',
|
|
300
|
+
subscription: sub.subscriptionId,
|
|
301
|
+
});
|
|
302
|
+
this.subscriptions.delete(subscriptionId);
|
|
303
|
+
logger.info('Removed event subscription', { id: subscriptionId });
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Send a message over WebSocket
|
|
307
|
+
*/
|
|
308
|
+
sendMessage(message) {
|
|
309
|
+
if (this.ws?.readyState !== WebSocket.OPEN) {
|
|
310
|
+
throw new Error('WebSocket is not connected');
|
|
311
|
+
}
|
|
312
|
+
this.ws.send(JSON.stringify(message));
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Disconnect from Home Assistant
|
|
316
|
+
*/
|
|
317
|
+
disconnect() {
|
|
318
|
+
this.shouldReconnect = false;
|
|
319
|
+
if (this.reconnectTimer) {
|
|
320
|
+
clearTimeout(this.reconnectTimer);
|
|
321
|
+
this.reconnectTimer = null;
|
|
322
|
+
}
|
|
323
|
+
if (this.ws) {
|
|
324
|
+
this.ws.close();
|
|
325
|
+
this.ws = null;
|
|
326
|
+
}
|
|
327
|
+
this.authenticated = false;
|
|
328
|
+
this.subscriptions.clear();
|
|
329
|
+
logger.info('Disconnected from Home Assistant WebSocket');
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Check if connected
|
|
333
|
+
*/
|
|
334
|
+
isConnected() {
|
|
335
|
+
return this.ws?.readyState === WebSocket.OPEN && this.authenticated;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get active subscription count
|
|
339
|
+
*/
|
|
340
|
+
getSubscriptionCount() {
|
|
341
|
+
return this.subscriptions.size;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/haClient/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History Operations for Home Assistant API
|
|
3
|
+
* Handles historical data and system logs
|
|
4
|
+
*/
|
|
5
|
+
import type { Entity, LogbookEntry } from '../types/index.js';
|
|
6
|
+
import type { RequestHandler } from './request.js';
|
|
7
|
+
/**
|
|
8
|
+
* History-related operations for the Home Assistant API.
|
|
9
|
+
*/
|
|
10
|
+
export declare class HistoryOperations {
|
|
11
|
+
private readonly request;
|
|
12
|
+
constructor(request: RequestHandler);
|
|
13
|
+
/**
|
|
14
|
+
* Get historical data for an entity
|
|
15
|
+
*/
|
|
16
|
+
getHistory(entityId: string, hours?: number): Promise<Entity[][]>;
|
|
17
|
+
/**
|
|
18
|
+
* Get system log entries from the logbook
|
|
19
|
+
* Returns recent events/state changes, optionally filtered by entity
|
|
20
|
+
*/
|
|
21
|
+
getSystemLog(options?: {
|
|
22
|
+
hours?: number;
|
|
23
|
+
entity_id?: string;
|
|
24
|
+
}): Promise<LogbookEntry[]>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/haClient/history.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;GAEG;AACH,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAEpD;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAqB3E;;;OAGG;IACG,YAAY,CAAC,OAAO,GAAE;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAqBjC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History Operations for Home Assistant API
|
|
3
|
+
* Handles historical data and system logs
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* History-related operations for the Home Assistant API.
|
|
8
|
+
*/
|
|
9
|
+
export class HistoryOperations {
|
|
10
|
+
request;
|
|
11
|
+
constructor(request) {
|
|
12
|
+
this.request = request;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get historical data for an entity
|
|
16
|
+
*/
|
|
17
|
+
async getHistory(entityId, hours = 24) {
|
|
18
|
+
logger.debug('Fetching history', { entityId, hours });
|
|
19
|
+
const endTime = new Date();
|
|
20
|
+
const startTime = new Date(endTime.getTime() - hours * 60 * 60 * 1000);
|
|
21
|
+
const path = `/history/period/${startTime.toISOString()}`;
|
|
22
|
+
const history = await this.request.get(path, {
|
|
23
|
+
filter_entity_id: entityId,
|
|
24
|
+
end_time: endTime.toISOString(),
|
|
25
|
+
});
|
|
26
|
+
logger.info('Fetched history', {
|
|
27
|
+
entityId,
|
|
28
|
+
hours,
|
|
29
|
+
records: history[0]?.length ?? 0
|
|
30
|
+
});
|
|
31
|
+
return history;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get system log entries from the logbook
|
|
35
|
+
* Returns recent events/state changes, optionally filtered by entity
|
|
36
|
+
*/
|
|
37
|
+
async getSystemLog(options = {}) {
|
|
38
|
+
const hours = options.hours || 24;
|
|
39
|
+
logger.debug('Fetching system log', { hours, entity_id: options.entity_id });
|
|
40
|
+
const endTime = new Date();
|
|
41
|
+
const startTime = new Date(endTime.getTime() - hours * 60 * 60 * 1000);
|
|
42
|
+
const path = `/logbook/${startTime.toISOString()}`;
|
|
43
|
+
const params = {
|
|
44
|
+
end_time: endTime.toISOString(),
|
|
45
|
+
};
|
|
46
|
+
if (options.entity_id) {
|
|
47
|
+
params.entity = options.entity_id;
|
|
48
|
+
}
|
|
49
|
+
const entries = await this.request.get(path, params);
|
|
50
|
+
logger.info('Fetched system log', { count: entries.length, hours });
|
|
51
|
+
return entries;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/haClient/history.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C;;GAEG;AACH,MAAM,OAAO,iBAAiB;IACC;IAA7B,YAA6B,OAAuB;QAAvB,YAAO,GAAP,OAAO,CAAgB;IAAG,CAAC;IAExD;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,QAAgB,EAAE,QAAgB,EAAE;QACnD,MAAM,CAAC,KAAK,CAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAEtD,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAEvE,MAAM,IAAI,GAAG,mBAAmB,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAa,IAAI,EAAE;YACvD,gBAAgB,EAAE,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,WAAW,EAAE;SAChC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAC7B,QAAQ;YACR,KAAK;YACL,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC;SACjC,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAAC,UAGf,EAAE;QACJ,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;QAE7E,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAEvE,MAAM,IAAI,GAAG,YAAY,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;QACnD,MAAM,MAAM,GAA2B;YACrC,QAAQ,EAAE,OAAO,CAAC,WAAW,EAAE;SAChC,CAAC;QAEF,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAiB,IAAI,EAAE,MAAM,CAAC,CAAC;QAErE,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|