homebridge-myleviton 3.0.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/LICENSE +202 -0
- package/README.md +112 -0
- package/config.schema.json +136 -0
- package/dist/api/cache.d.ts +108 -0
- package/dist/api/cache.d.ts.map +1 -0
- package/dist/api/cache.js +206 -0
- package/dist/api/cache.js.map +1 -0
- package/dist/api/circuit-breaker.d.ts +118 -0
- package/dist/api/circuit-breaker.d.ts.map +1 -0
- package/dist/api/circuit-breaker.js +223 -0
- package/dist/api/circuit-breaker.js.map +1 -0
- package/dist/api/client.d.ts +116 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +358 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/index.d.ts +23 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +47 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/persistence.d.ts +107 -0
- package/dist/api/persistence.d.ts.map +1 -0
- package/dist/api/persistence.js +285 -0
- package/dist/api/persistence.js.map +1 -0
- package/dist/api/rate-limiter.d.ts +102 -0
- package/dist/api/rate-limiter.d.ts.map +1 -0
- package/dist/api/rate-limiter.js +173 -0
- package/dist/api/rate-limiter.js.map +1 -0
- package/dist/api/request-queue.d.ts +104 -0
- package/dist/api/request-queue.d.ts.map +1 -0
- package/dist/api/request-queue.js +223 -0
- package/dist/api/request-queue.js.map +1 -0
- package/dist/api/websocket.d.ts +116 -0
- package/dist/api/websocket.d.ts.map +1 -0
- package/dist/api/websocket.js +319 -0
- package/dist/api/websocket.js.map +1 -0
- package/dist/errors/index.d.ts +182 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +273 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +139 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +664 -0
- package/dist/platform.js.map +1 -0
- package/dist/types/index.d.ts +225 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +34 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +52 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +103 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +184 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +56 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +141 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/sanitizers.d.ts +37 -0
- package/dist/utils/sanitizers.d.ts.map +1 -0
- package/dist/utils/sanitizers.js +128 -0
- package/dist/utils/sanitizers.js.map +1 -0
- package/dist/utils/validators.d.ts +51 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +243 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 tbaur
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0
|
|
6
|
+
* See LICENSE file for full license text
|
|
7
|
+
*
|
|
8
|
+
* @fileoverview Request queue with priority and deduplication
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.RequestDeduplicator = exports.RequestQueue = exports.DEFAULT_REQUEST_QUEUE_CONFIG = void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Default request queue configuration
|
|
14
|
+
*/
|
|
15
|
+
exports.DEFAULT_REQUEST_QUEUE_CONFIG = {
|
|
16
|
+
maxConcurrent: 5,
|
|
17
|
+
maxQueueSize: 100,
|
|
18
|
+
requestTimeout: 30000,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Request queue with priority ordering and deduplication
|
|
22
|
+
*/
|
|
23
|
+
class RequestQueue {
|
|
24
|
+
maxConcurrent;
|
|
25
|
+
maxQueueSize;
|
|
26
|
+
requestTimeout;
|
|
27
|
+
queue = [];
|
|
28
|
+
inFlight = new Map();
|
|
29
|
+
processing = false;
|
|
30
|
+
constructor(config = {}) {
|
|
31
|
+
const merged = { ...exports.DEFAULT_REQUEST_QUEUE_CONFIG, ...config };
|
|
32
|
+
this.maxConcurrent = merged.maxConcurrent;
|
|
33
|
+
this.maxQueueSize = merged.maxQueueSize;
|
|
34
|
+
this.requestTimeout = merged.requestTimeout;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get queue length
|
|
38
|
+
*/
|
|
39
|
+
get length() {
|
|
40
|
+
return this.queue.length;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get number of in-flight requests
|
|
44
|
+
*/
|
|
45
|
+
get inFlightCount() {
|
|
46
|
+
return this.inFlight.size;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if queue is full
|
|
50
|
+
*/
|
|
51
|
+
get isFull() {
|
|
52
|
+
return this.queue.length >= this.maxQueueSize;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Add a request to the queue
|
|
56
|
+
*/
|
|
57
|
+
add(execute, options = {}) {
|
|
58
|
+
const { priority = 'normal', dedupeKey } = options;
|
|
59
|
+
// Check for duplicate in-flight request
|
|
60
|
+
if (dedupeKey && this.inFlight.has(dedupeKey)) {
|
|
61
|
+
return this.inFlight.get(dedupeKey);
|
|
62
|
+
}
|
|
63
|
+
// Check queue size limit
|
|
64
|
+
if (this.isFull) {
|
|
65
|
+
return Promise.reject(new Error('Request queue is full'));
|
|
66
|
+
}
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const request = {
|
|
69
|
+
id: dedupeKey || `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
70
|
+
priority,
|
|
71
|
+
execute,
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
resolve: resolve,
|
|
74
|
+
reject,
|
|
75
|
+
};
|
|
76
|
+
this.enqueue(request);
|
|
77
|
+
this.processQueue();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Insert request into queue by priority
|
|
82
|
+
*/
|
|
83
|
+
enqueue(request) {
|
|
84
|
+
// Priority order: high > normal > low
|
|
85
|
+
// Within same priority, FIFO ordering
|
|
86
|
+
const priorityOrder = { high: 0, normal: 1, low: 2 };
|
|
87
|
+
const requestPriority = priorityOrder[request.priority];
|
|
88
|
+
const insertIndex = this.queue.findIndex(r => priorityOrder[r.priority] > requestPriority);
|
|
89
|
+
if (insertIndex === -1) {
|
|
90
|
+
this.queue.push(request);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this.queue.splice(insertIndex, 0, request);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Process queued requests
|
|
98
|
+
*/
|
|
99
|
+
async processQueue() {
|
|
100
|
+
if (this.processing) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.processing = true;
|
|
104
|
+
try {
|
|
105
|
+
while (this.queue.length > 0 && this.inFlight.size < this.maxConcurrent) {
|
|
106
|
+
const request = this.queue.shift();
|
|
107
|
+
if (!request) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
const promise = this.executeRequest(request);
|
|
111
|
+
this.inFlight.set(request.id, promise);
|
|
112
|
+
// Don't await - let it run concurrently
|
|
113
|
+
promise.finally(() => {
|
|
114
|
+
this.inFlight.delete(request.id);
|
|
115
|
+
// Continue processing after completion
|
|
116
|
+
setImmediate(() => this.processQueue());
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
this.processing = false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Execute a single request with timeout
|
|
126
|
+
*/
|
|
127
|
+
async executeRequest(request) {
|
|
128
|
+
let timeoutId;
|
|
129
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
130
|
+
timeoutId = setTimeout(() => {
|
|
131
|
+
reject(new Error(`Request timed out after ${this.requestTimeout}ms`));
|
|
132
|
+
}, this.requestTimeout);
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
const result = await Promise.race([request.execute(), timeoutPromise]);
|
|
136
|
+
request.resolve(result);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
request.reject(error);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
// Always clear the timeout to prevent memory leaks and open handles
|
|
143
|
+
if (timeoutId) {
|
|
144
|
+
clearTimeout(timeoutId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Clear the queue
|
|
150
|
+
*/
|
|
151
|
+
clear() {
|
|
152
|
+
// Reject all pending requests
|
|
153
|
+
for (const request of this.queue) {
|
|
154
|
+
request.reject(new Error('Request queue cleared'));
|
|
155
|
+
}
|
|
156
|
+
this.queue = [];
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get queue statistics
|
|
160
|
+
*/
|
|
161
|
+
getStats() {
|
|
162
|
+
return {
|
|
163
|
+
queueLength: this.length,
|
|
164
|
+
inFlight: this.inFlightCount,
|
|
165
|
+
maxConcurrent: this.maxConcurrent,
|
|
166
|
+
maxQueueSize: this.maxQueueSize,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Wait for all in-flight requests to complete
|
|
171
|
+
*/
|
|
172
|
+
async drain() {
|
|
173
|
+
await Promise.all(this.inFlight.values());
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.RequestQueue = RequestQueue;
|
|
177
|
+
/**
|
|
178
|
+
* In-flight request deduplication map
|
|
179
|
+
*/
|
|
180
|
+
class RequestDeduplicator {
|
|
181
|
+
inFlight = new Map();
|
|
182
|
+
maxSize;
|
|
183
|
+
constructor(maxSize = 100) {
|
|
184
|
+
this.maxSize = maxSize;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Execute a request with deduplication
|
|
188
|
+
*/
|
|
189
|
+
async execute(key, fn) {
|
|
190
|
+
// Check for existing in-flight request
|
|
191
|
+
const existing = this.inFlight.get(key);
|
|
192
|
+
if (existing) {
|
|
193
|
+
return existing;
|
|
194
|
+
}
|
|
195
|
+
// Prevent unbounded growth
|
|
196
|
+
if (this.inFlight.size >= this.maxSize) {
|
|
197
|
+
const firstKey = this.inFlight.keys().next().value;
|
|
198
|
+
if (firstKey) {
|
|
199
|
+
this.inFlight.delete(firstKey);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Create and track new request
|
|
203
|
+
const promise = fn().finally(() => {
|
|
204
|
+
this.inFlight.delete(key);
|
|
205
|
+
});
|
|
206
|
+
this.inFlight.set(key, promise);
|
|
207
|
+
return promise;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get number of in-flight requests
|
|
211
|
+
*/
|
|
212
|
+
get size() {
|
|
213
|
+
return this.inFlight.size;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Clear all tracked requests
|
|
217
|
+
*/
|
|
218
|
+
clear() {
|
|
219
|
+
this.inFlight.clear();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
exports.RequestDeduplicator = RequestDeduplicator;
|
|
223
|
+
//# sourceMappingURL=request-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-queue.js","sourceRoot":"","sources":["../../src/api/request-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 tbaur
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0
|
|
5
|
+
* See LICENSE file for full license text
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview WebSocket client for real-time device updates
|
|
8
|
+
*/
|
|
9
|
+
import type { WebSocketPayload, DeviceInfo, Logger } from '../types';
|
|
10
|
+
/**
|
|
11
|
+
* WebSocket configuration
|
|
12
|
+
*/
|
|
13
|
+
export interface WebSocketConfig {
|
|
14
|
+
/** Socket URL */
|
|
15
|
+
socketUrl: string;
|
|
16
|
+
/** Connection timeout in ms */
|
|
17
|
+
connectionTimeout: number;
|
|
18
|
+
/** Maximum reconnection attempts */
|
|
19
|
+
maxReconnectAttempts: number;
|
|
20
|
+
/** Initial reconnection delay in ms */
|
|
21
|
+
initialReconnectDelay: number;
|
|
22
|
+
/** Maximum reconnection delay in ms */
|
|
23
|
+
maxReconnectDelay: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Default WebSocket configuration
|
|
27
|
+
*/
|
|
28
|
+
export declare const DEFAULT_WEBSOCKET_CONFIG: WebSocketConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Logger interface for WebSocket
|
|
31
|
+
*/
|
|
32
|
+
interface WebSocketLogger {
|
|
33
|
+
debug: (message: string) => void;
|
|
34
|
+
info: (message: string) => void;
|
|
35
|
+
warn: (message: string) => void;
|
|
36
|
+
error: (message: string) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* WebSocket connection for real-time updates
|
|
40
|
+
*/
|
|
41
|
+
export declare class LevitonWebSocket {
|
|
42
|
+
private readonly config;
|
|
43
|
+
private readonly logger;
|
|
44
|
+
private ws;
|
|
45
|
+
private token;
|
|
46
|
+
private devices;
|
|
47
|
+
private callback;
|
|
48
|
+
private reconnectAttempt;
|
|
49
|
+
private timers;
|
|
50
|
+
private isConnecting;
|
|
51
|
+
private isClosed;
|
|
52
|
+
constructor(token: string, devices: DeviceInfo[], callback: (payload: WebSocketPayload) => void, logger: WebSocketLogger | Logger, config?: Partial<WebSocketConfig>);
|
|
53
|
+
/**
|
|
54
|
+
* Normalize logger to standard interface
|
|
55
|
+
*/
|
|
56
|
+
private normalizeLogger;
|
|
57
|
+
/**
|
|
58
|
+
* Update token (after refresh)
|
|
59
|
+
*/
|
|
60
|
+
updateToken(token: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Connect to WebSocket
|
|
63
|
+
*/
|
|
64
|
+
connect(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Setup WebSocket event handlers
|
|
67
|
+
*/
|
|
68
|
+
private setupEventHandlers;
|
|
69
|
+
/**
|
|
70
|
+
* Handle incoming message
|
|
71
|
+
*/
|
|
72
|
+
private handleMessage;
|
|
73
|
+
/**
|
|
74
|
+
* Subscribe to device updates
|
|
75
|
+
*/
|
|
76
|
+
private subscribeToDevices;
|
|
77
|
+
/**
|
|
78
|
+
* Handle notification message
|
|
79
|
+
*/
|
|
80
|
+
private handleNotification;
|
|
81
|
+
/**
|
|
82
|
+
* Schedule reconnection
|
|
83
|
+
*/
|
|
84
|
+
private scheduleReconnect;
|
|
85
|
+
/**
|
|
86
|
+
* Remove a timer from tracking
|
|
87
|
+
*/
|
|
88
|
+
private removeTimer;
|
|
89
|
+
/**
|
|
90
|
+
* Clear all timers
|
|
91
|
+
*/
|
|
92
|
+
private clearTimers;
|
|
93
|
+
/**
|
|
94
|
+
* Close the WebSocket connection
|
|
95
|
+
*/
|
|
96
|
+
close(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Check if connected
|
|
99
|
+
*/
|
|
100
|
+
get isConnected(): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Get connection status
|
|
103
|
+
*/
|
|
104
|
+
getStatus(): {
|
|
105
|
+
isConnected: boolean;
|
|
106
|
+
isConnecting: boolean;
|
|
107
|
+
isClosed: boolean;
|
|
108
|
+
reconnectAttempt: number;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create and connect a WebSocket
|
|
113
|
+
*/
|
|
114
|
+
export declare function createWebSocket(token: string, devices: DeviceInfo[], callback: (payload: WebSocketPayload) => void, logger: WebSocketLogger | Logger, config?: Partial<WebSocketConfig>): LevitonWebSocket;
|
|
115
|
+
export {};
|
|
116
|
+
//# sourceMappingURL=websocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/api/websocket.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEpE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,+BAA+B;IAC/B,iBAAiB,EAAE,MAAM,CAAA;IACzB,oCAAoC;IACpC,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uCAAuC;IACvC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,uCAAuC;IACvC,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,eAMtC,CAAA;AAgBD;;GAEG;AACH,UAAU,eAAe;IACvB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IAExC,OAAO,CAAC,EAAE,CAAyC;IACnD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,QAAQ,CAAqC;IAErD,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAQ;gBAGtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,EAAE,EACrB,QAAQ,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,EAC7C,MAAM,EAAE,eAAe,GAAG,MAAM,EAChC,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM;IASvC;;OAEG;IACH,OAAO,CAAC,eAAe;IAcvB;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhC;;OAEG;IACH,OAAO,IAAI,IAAI;IAqBf;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoE1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAkCrB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuBzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAOnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAOnB;;OAEG;IACH,KAAK,IAAI,IAAI;IAcb;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;OAEG;IACH,SAAS,IAAI;QACX,WAAW,EAAE,OAAO,CAAA;QACpB,YAAY,EAAE,OAAO,CAAA;QACrB,QAAQ,EAAE,OAAO,CAAA;QACjB,gBAAgB,EAAE,MAAM,CAAA;KACzB;CAQF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,EAAE,EACrB,QAAQ,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,EAC7C,MAAM,EAAE,eAAe,GAAG,MAAM,EAChC,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAChC,gBAAgB,CAIlB"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 tbaur
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0
|
|
6
|
+
* See LICENSE file for full license text
|
|
7
|
+
*
|
|
8
|
+
* @fileoverview WebSocket client for real-time device updates
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.LevitonWebSocket = exports.DEFAULT_WEBSOCKET_CONFIG = void 0;
|
|
15
|
+
exports.createWebSocket = createWebSocket;
|
|
16
|
+
const sockjs_client_1 = __importDefault(require("sockjs-client"));
|
|
17
|
+
const sanitizers_1 = require("../utils/sanitizers");
|
|
18
|
+
/**
|
|
19
|
+
* Default WebSocket configuration
|
|
20
|
+
*/
|
|
21
|
+
exports.DEFAULT_WEBSOCKET_CONFIG = {
|
|
22
|
+
socketUrl: 'https://my.leviton.com/socket',
|
|
23
|
+
connectionTimeout: 10000,
|
|
24
|
+
maxReconnectAttempts: 10,
|
|
25
|
+
initialReconnectDelay: 1000,
|
|
26
|
+
maxReconnectDelay: 60000,
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* WebSocket message types
|
|
30
|
+
*/
|
|
31
|
+
var MessageType;
|
|
32
|
+
(function (MessageType) {
|
|
33
|
+
MessageType["CHALLENGE"] = "challenge";
|
|
34
|
+
MessageType["STATUS"] = "status";
|
|
35
|
+
MessageType["NOTIFICATION"] = "notification";
|
|
36
|
+
})(MessageType || (MessageType = {}));
|
|
37
|
+
/**
|
|
38
|
+
* WebSocket status values
|
|
39
|
+
*/
|
|
40
|
+
const STATUS_READY = 'ready';
|
|
41
|
+
/**
|
|
42
|
+
* WebSocket connection for real-time updates
|
|
43
|
+
*/
|
|
44
|
+
class LevitonWebSocket {
|
|
45
|
+
config;
|
|
46
|
+
logger;
|
|
47
|
+
ws = null;
|
|
48
|
+
token;
|
|
49
|
+
devices;
|
|
50
|
+
callback;
|
|
51
|
+
reconnectAttempt = 0;
|
|
52
|
+
timers = [];
|
|
53
|
+
isConnecting = false;
|
|
54
|
+
isClosed = false;
|
|
55
|
+
constructor(token, devices, callback, logger, config = {}) {
|
|
56
|
+
this.config = { ...exports.DEFAULT_WEBSOCKET_CONFIG, ...config };
|
|
57
|
+
this.logger = this.normalizeLogger(logger);
|
|
58
|
+
this.token = token;
|
|
59
|
+
this.devices = devices;
|
|
60
|
+
this.callback = callback;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Normalize logger to standard interface
|
|
64
|
+
*/
|
|
65
|
+
normalizeLogger(logger) {
|
|
66
|
+
if ('debug' in logger && 'info' in logger && 'warn' in logger && 'error' in logger) {
|
|
67
|
+
return logger;
|
|
68
|
+
}
|
|
69
|
+
// Wrap basic logger
|
|
70
|
+
const baseLogger = logger;
|
|
71
|
+
return {
|
|
72
|
+
debug: (msg) => { baseLogger.info?.(`[debug] ${msg}`) ?? console.log(msg); },
|
|
73
|
+
info: (msg) => { baseLogger.info?.(msg) ?? console.log(msg); },
|
|
74
|
+
warn: (msg) => { baseLogger.warn?.(msg) ?? console.warn(msg); },
|
|
75
|
+
error: (msg) => { baseLogger.error?.(msg) ?? console.error(msg); },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Update token (after refresh)
|
|
80
|
+
*/
|
|
81
|
+
updateToken(token) {
|
|
82
|
+
this.token = token;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Connect to WebSocket
|
|
86
|
+
*/
|
|
87
|
+
connect() {
|
|
88
|
+
if (this.isConnecting || this.isClosed) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.isConnecting = true;
|
|
92
|
+
this.logger.debug('Connecting to WebSocket...');
|
|
93
|
+
try {
|
|
94
|
+
this.ws = new sockjs_client_1.default(this.config.socketUrl, undefined, {
|
|
95
|
+
transports: ['websocket', 'xhr-streaming', 'xhr-polling'],
|
|
96
|
+
});
|
|
97
|
+
this.setupEventHandlers();
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
this.isConnecting = false;
|
|
101
|
+
this.logger.error(`Failed to create WebSocket: ${error.message}`);
|
|
102
|
+
this.scheduleReconnect();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Setup WebSocket event handlers
|
|
107
|
+
*/
|
|
108
|
+
setupEventHandlers() {
|
|
109
|
+
if (!this.ws) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
let isOpen = false;
|
|
113
|
+
// Connection timeout
|
|
114
|
+
const connectionTimeout = setTimeout(() => {
|
|
115
|
+
if (!isOpen && this.ws) {
|
|
116
|
+
this.logger.error('WebSocket connection timeout');
|
|
117
|
+
try {
|
|
118
|
+
this.ws.close();
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Ignore
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}, this.config.connectionTimeout);
|
|
125
|
+
this.timers.push(connectionTimeout);
|
|
126
|
+
this.ws.onopen = () => {
|
|
127
|
+
clearTimeout(connectionTimeout);
|
|
128
|
+
this.removeTimer(connectionTimeout);
|
|
129
|
+
isOpen = true;
|
|
130
|
+
this.isConnecting = false;
|
|
131
|
+
this.reconnectAttempt = 0;
|
|
132
|
+
this.logger.debug(`WebSocket connected (token: ${(0, sanitizers_1.maskToken)(this.token)})`);
|
|
133
|
+
};
|
|
134
|
+
this.ws.onclose = (event) => {
|
|
135
|
+
this.clearTimers();
|
|
136
|
+
isOpen = false;
|
|
137
|
+
this.isConnecting = false;
|
|
138
|
+
const code = event?.code;
|
|
139
|
+
const wasClean = event?.wasClean;
|
|
140
|
+
// Normal close
|
|
141
|
+
if (wasClean && code === 1000) {
|
|
142
|
+
this.logger.debug('WebSocket closed normally');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Auth failure - don't reconnect
|
|
146
|
+
if (code === 401) {
|
|
147
|
+
this.logger.info('WebSocket auth failed (expected - device control still works)');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Closed externally
|
|
151
|
+
if (this.isClosed) {
|
|
152
|
+
this.logger.debug('WebSocket closed by user');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.logger.debug(`WebSocket closed: code=${code} wasClean=${wasClean}`);
|
|
156
|
+
this.scheduleReconnect();
|
|
157
|
+
};
|
|
158
|
+
this.ws.onerror = (error) => {
|
|
159
|
+
clearTimeout(connectionTimeout);
|
|
160
|
+
this.removeTimer(connectionTimeout);
|
|
161
|
+
this.logger.error(`WebSocket error: ${error.message || 'Unknown error'}`);
|
|
162
|
+
};
|
|
163
|
+
this.ws.onmessage = (message) => {
|
|
164
|
+
this.handleMessage(message);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Handle incoming message
|
|
169
|
+
*/
|
|
170
|
+
handleMessage(message) {
|
|
171
|
+
let data;
|
|
172
|
+
try {
|
|
173
|
+
data = JSON.parse(message.data);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
this.logger.error(`Failed to parse WebSocket message: ${message.data}`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (!data || typeof data !== 'object') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Handle challenge
|
|
183
|
+
if (data.type === MessageType.CHALLENGE) {
|
|
184
|
+
this.logger.debug(`Received challenge, responding with token: ${(0, sanitizers_1.maskToken)(this.token)}`);
|
|
185
|
+
this.ws?.send(JSON.stringify([{ token: this.token }]));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// Handle ready status
|
|
189
|
+
if (data.type === MessageType.STATUS && data.status === STATUS_READY) {
|
|
190
|
+
this.logger.debug('WebSocket ready, subscribing to devices');
|
|
191
|
+
this.subscribeToDevices();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Handle notifications
|
|
195
|
+
if (data.type === MessageType.NOTIFICATION) {
|
|
196
|
+
this.handleNotification(data);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Subscribe to device updates
|
|
201
|
+
*/
|
|
202
|
+
subscribeToDevices() {
|
|
203
|
+
for (const device of this.devices) {
|
|
204
|
+
if (device?.id) {
|
|
205
|
+
this.ws?.send(JSON.stringify([{
|
|
206
|
+
type: 'subscribe',
|
|
207
|
+
subscription: {
|
|
208
|
+
modelName: 'IotSwitch',
|
|
209
|
+
modelId: device.id,
|
|
210
|
+
},
|
|
211
|
+
}]));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Handle notification message
|
|
217
|
+
*/
|
|
218
|
+
handleNotification(data) {
|
|
219
|
+
const notification = data.notification;
|
|
220
|
+
if (!notification?.data || !notification.modelId) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const notificationData = notification.data;
|
|
224
|
+
if (!notificationData.power) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const payload = {
|
|
228
|
+
id: notification.modelId,
|
|
229
|
+
power: notificationData.power,
|
|
230
|
+
};
|
|
231
|
+
if (notificationData.brightness !== undefined) {
|
|
232
|
+
payload.brightness = notificationData.brightness;
|
|
233
|
+
}
|
|
234
|
+
if (notificationData.occupancy !== undefined) {
|
|
235
|
+
payload.occupancy = notificationData.occupancy;
|
|
236
|
+
}
|
|
237
|
+
this.callback(payload);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Schedule reconnection
|
|
241
|
+
*/
|
|
242
|
+
scheduleReconnect() {
|
|
243
|
+
if (this.isClosed) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (this.reconnectAttempt >= this.config.maxReconnectAttempts) {
|
|
247
|
+
this.logger.warn(`WebSocket reconnection failed after ${this.config.maxReconnectAttempts} attempts`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const delay = Math.min(this.config.initialReconnectDelay * Math.pow(2, this.reconnectAttempt), this.config.maxReconnectDelay);
|
|
251
|
+
this.logger.info(`WebSocket reconnecting in ${Math.round(delay / 1000)}s (${this.reconnectAttempt + 1}/${this.config.maxReconnectAttempts})`);
|
|
252
|
+
const timer = setTimeout(() => {
|
|
253
|
+
this.reconnectAttempt++;
|
|
254
|
+
this.connect();
|
|
255
|
+
}, delay);
|
|
256
|
+
this.timers.push(timer);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Remove a timer from tracking
|
|
260
|
+
*/
|
|
261
|
+
removeTimer(timer) {
|
|
262
|
+
const index = this.timers.indexOf(timer);
|
|
263
|
+
if (index !== -1) {
|
|
264
|
+
this.timers.splice(index, 1);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Clear all timers
|
|
269
|
+
*/
|
|
270
|
+
clearTimers() {
|
|
271
|
+
for (const timer of this.timers) {
|
|
272
|
+
clearTimeout(timer);
|
|
273
|
+
}
|
|
274
|
+
this.timers = [];
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Close the WebSocket connection
|
|
278
|
+
*/
|
|
279
|
+
close() {
|
|
280
|
+
this.isClosed = true;
|
|
281
|
+
this.clearTimers();
|
|
282
|
+
if (this.ws) {
|
|
283
|
+
try {
|
|
284
|
+
this.ws.close();
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// Ignore
|
|
288
|
+
}
|
|
289
|
+
this.ws = null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Check if connected
|
|
294
|
+
*/
|
|
295
|
+
get isConnected() {
|
|
296
|
+
return this.ws?.readyState === sockjs_client_1.default.OPEN;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Get connection status
|
|
300
|
+
*/
|
|
301
|
+
getStatus() {
|
|
302
|
+
return {
|
|
303
|
+
isConnected: this.isConnected,
|
|
304
|
+
isConnecting: this.isConnecting,
|
|
305
|
+
isClosed: this.isClosed,
|
|
306
|
+
reconnectAttempt: this.reconnectAttempt,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
exports.LevitonWebSocket = LevitonWebSocket;
|
|
311
|
+
/**
|
|
312
|
+
* Create and connect a WebSocket
|
|
313
|
+
*/
|
|
314
|
+
function createWebSocket(token, devices, callback, logger, config) {
|
|
315
|
+
const ws = new LevitonWebSocket(token, devices, callback, logger, config);
|
|
316
|
+
ws.connect();
|
|
317
|
+
return ws;
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=websocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/api/websocket.ts"],"names":[],"mappings":"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"}
|