@felixgeelhaar/govee-api-client 1.0.2 โ 2.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/README.md +160 -7
- package/dist/GoveeClient.d.ts +31 -0
- package/dist/GoveeClient.d.ts.map +1 -1
- package/dist/GoveeClient.js +33 -0
- package/dist/GoveeClient.js.map +1 -1
- package/dist/domain/entities/GoveeDevice.d.ts +22 -4
- package/dist/domain/entities/GoveeDevice.d.ts.map +1 -1
- package/dist/domain/entities/GoveeDevice.js +73 -22
- package/dist/domain/entities/GoveeDevice.js.map +1 -1
- package/dist/domain/repositories/IGoveeDeviceRepository.d.ts +2 -2
- package/dist/domain/repositories/IGoveeDeviceRepository.d.ts.map +1 -1
- package/dist/errors/ValidationError.d.ts +28 -0
- package/dist/errors/ValidationError.d.ts.map +1 -0
- package/dist/errors/ValidationError.js +44 -0
- package/dist/errors/ValidationError.js.map +1 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +1 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/GoveeDeviceRepository.d.ts +5 -4
- package/dist/infrastructure/GoveeDeviceRepository.d.ts.map +1 -1
- package/dist/infrastructure/GoveeDeviceRepository.js +83 -57
- package/dist/infrastructure/GoveeDeviceRepository.js.map +1 -1
- package/dist/infrastructure/SlidingWindowRateLimiter.d.ts +83 -0
- package/dist/infrastructure/SlidingWindowRateLimiter.d.ts.map +1 -0
- package/dist/infrastructure/SlidingWindowRateLimiter.js +218 -0
- package/dist/infrastructure/SlidingWindowRateLimiter.js.map +1 -0
- package/dist/infrastructure/index.d.ts +2 -0
- package/dist/infrastructure/index.d.ts.map +1 -1
- package/dist/infrastructure/index.js +2 -0
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/infrastructure/response-schemas.d.ts +82 -0
- package/dist/infrastructure/response-schemas.d.ts.map +1 -0
- package/dist/infrastructure/response-schemas.js +59 -0
- package/dist/infrastructure/response-schemas.js.map +1 -0
- package/dist/infrastructure/retry/IntegrationGuide.d.ts +133 -0
- package/dist/infrastructure/retry/IntegrationGuide.d.ts.map +1 -0
- package/dist/infrastructure/retry/IntegrationGuide.js +295 -0
- package/dist/infrastructure/retry/IntegrationGuide.js.map +1 -0
- package/dist/infrastructure/retry/RetryConfigPresets.d.ts +114 -0
- package/dist/infrastructure/retry/RetryConfigPresets.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryConfigPresets.js +406 -0
- package/dist/infrastructure/retry/RetryConfigPresets.js.map +1 -0
- package/dist/infrastructure/retry/RetryPolicy.d.ts +148 -0
- package/dist/infrastructure/retry/RetryPolicy.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryPolicy.js +373 -0
- package/dist/infrastructure/retry/RetryPolicy.js.map +1 -0
- package/dist/infrastructure/retry/RetryableRepository.d.ts +75 -0
- package/dist/infrastructure/retry/RetryableRepository.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryableRepository.js +142 -0
- package/dist/infrastructure/retry/RetryableRepository.js.map +1 -0
- package/dist/infrastructure/retry/RetryableRequest.d.ts +132 -0
- package/dist/infrastructure/retry/RetryableRequest.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryableRequest.js +248 -0
- package/dist/infrastructure/retry/RetryableRequest.js.map +1 -0
- package/dist/infrastructure/retry/index.d.ts +35 -0
- package/dist/infrastructure/retry/index.d.ts.map +1 -0
- package/dist/infrastructure/retry/index.js +36 -0
- package/dist/infrastructure/retry/index.js.map +1 -0
- package/dist/services/GoveeControlService.d.ts +53 -0
- package/dist/services/GoveeControlService.d.ts.map +1 -1
- package/dist/services/GoveeControlService.js +138 -12
- package/dist/services/GoveeControlService.js.map +1 -1
- package/package.json +16 -13
package/README.md
CHANGED
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
[](https://github.com/felixgeelhaar/govee-api-client/actions)
|
|
6
6
|
[](https://codecov.io/gh/felixgeelhaar/govee-api-client)
|
|
7
7
|
|
|
8
|
-
An enterprise-grade TypeScript client library for the Govee Developer REST API. Built with Domain-Driven Design (DDD) principles
|
|
8
|
+
An enterprise-grade TypeScript client library for the Govee Developer REST API. Built with Domain-Driven Design (DDD) principles, comprehensive error handling, and production-ready rate limiting and retry capabilities.
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
12
|
- ๐ฏ **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
13
13
|
- ๐๏ธ **Domain-Driven Design**: Clean architecture following DDD principles
|
|
14
|
-
- โก **Rate Limiting**:
|
|
14
|
+
- โก **Rate Limiting**: High-performance sliding window rate limiter with burst capability
|
|
15
|
+
- ๐ **Retry Logic**: Enterprise-grade retry with exponential backoff, jitter, and circuit breaker
|
|
15
16
|
- ๐ก๏ธ **Error Handling**: Comprehensive error hierarchy with specific error types
|
|
17
|
+
- ๐ **Observability**: Built-in metrics and monitoring for rate limiting and retries
|
|
16
18
|
- ๐ **Logging**: Configurable logging with Pino integration
|
|
17
19
|
- ๐งช **Well Tested**: >95% test coverage with unit and integration tests
|
|
18
20
|
- ๐ **Production Ready**: Enterprise-grade reliability and performance
|
|
@@ -60,12 +62,15 @@ if (livingRoomLight) {
|
|
|
60
62
|
|
|
61
63
|
```typescript
|
|
62
64
|
import pino from 'pino';
|
|
65
|
+
import { GoveeClient, RetryPolicy } from '@felixgeelhaar/govee-api-client';
|
|
63
66
|
|
|
64
67
|
const client = new GoveeClient({
|
|
65
68
|
apiKey: 'your-govee-api-key',
|
|
66
69
|
timeout: 30000, // Request timeout in milliseconds (default: 30000)
|
|
67
|
-
rateLimit:
|
|
70
|
+
rateLimit: 95, // Requests per minute (default: 95, with 5 buffer under Govee's limit)
|
|
68
71
|
logger: pino({ level: 'info' }), // Optional logger (silent by default)
|
|
72
|
+
enableRetries: true, // Enable retry functionality (default: false)
|
|
73
|
+
retryPolicy: 'production', // Retry policy preset or custom RetryPolicy instance
|
|
69
74
|
});
|
|
70
75
|
```
|
|
71
76
|
|
|
@@ -187,18 +192,166 @@ try {
|
|
|
187
192
|
}
|
|
188
193
|
```
|
|
189
194
|
|
|
190
|
-
## Rate Limiting
|
|
195
|
+
## Rate Limiting & Retry Features
|
|
191
196
|
|
|
192
|
-
The client
|
|
197
|
+
The client includes enterprise-grade rate limiting and retry capabilities designed for production environments.
|
|
198
|
+
|
|
199
|
+
### Rate Limiting
|
|
200
|
+
|
|
201
|
+
Uses a high-performance sliding window rate limiter that allows bursts up to the limit while maintaining the average rate over time:
|
|
193
202
|
|
|
194
203
|
```typescript
|
|
195
|
-
// Customize rate limiting
|
|
196
204
|
const client = new GoveeClient({
|
|
197
205
|
apiKey: 'your-api-key',
|
|
198
|
-
rateLimit:
|
|
206
|
+
rateLimit: 95, // Default: 95 req/min (5 request buffer under Govee's 100/min limit)
|
|
199
207
|
});
|
|
208
|
+
|
|
209
|
+
// Monitor rate limiter performance
|
|
210
|
+
const stats = client.getRateLimiterStats();
|
|
211
|
+
console.log(`Current utilization: ${stats.utilizationPercent}%`);
|
|
212
|
+
console.log(`Queue size: ${stats.queueSize}`);
|
|
213
|
+
console.log(`Can execute immediately: ${stats.canExecuteImmediately}`);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Retry Logic
|
|
217
|
+
|
|
218
|
+
Comprehensive retry functionality with exponential backoff, jitter, and circuit breaker:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
const client = new GoveeClient({
|
|
222
|
+
apiKey: 'your-api-key',
|
|
223
|
+
enableRetries: true,
|
|
224
|
+
retryPolicy: 'production', // 'development', 'testing', 'production'
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Monitor retry performance
|
|
228
|
+
const retryMetrics = client.getRetryMetrics();
|
|
229
|
+
if (retryMetrics) {
|
|
230
|
+
console.log(`Total retry attempts: ${retryMetrics.totalAttempts}`);
|
|
231
|
+
console.log(`Success rate: ${retryMetrics.successfulRetries}/${retryMetrics.totalAttempts}`);
|
|
232
|
+
console.log(`Circuit breaker state: ${retryMetrics.circuitBreakerState}`);
|
|
233
|
+
}
|
|
200
234
|
```
|
|
201
235
|
|
|
236
|
+
### Custom Retry Policies
|
|
237
|
+
|
|
238
|
+
Create custom retry policies for specific requirements:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import {
|
|
242
|
+
GoveeClient,
|
|
243
|
+
RetryPolicy,
|
|
244
|
+
RateLimitError,
|
|
245
|
+
NetworkError,
|
|
246
|
+
GoveeApiError,
|
|
247
|
+
} from '@felixgeelhaar/govee-api-client';
|
|
248
|
+
|
|
249
|
+
const customRetryPolicy = new RetryPolicy({
|
|
250
|
+
backoff: {
|
|
251
|
+
type: 'exponential',
|
|
252
|
+
initialDelayMs: 1000,
|
|
253
|
+
maxDelayMs: 30000,
|
|
254
|
+
multiplier: 2.0,
|
|
255
|
+
},
|
|
256
|
+
jitter: {
|
|
257
|
+
type: 'equal',
|
|
258
|
+
factor: 0.1,
|
|
259
|
+
},
|
|
260
|
+
condition: {
|
|
261
|
+
maxAttempts: 3,
|
|
262
|
+
maxTotalTimeMs: 60000,
|
|
263
|
+
retryableStatusCodes: [408, 429, 502, 503, 504],
|
|
264
|
+
retryableErrorTypes: [RateLimitError, NetworkError, GoveeApiError],
|
|
265
|
+
},
|
|
266
|
+
circuitBreaker: {
|
|
267
|
+
enabled: true,
|
|
268
|
+
failureThreshold: 5,
|
|
269
|
+
recoveryTimeoutMs: 30000,
|
|
270
|
+
halfOpenSuccessThreshold: 2,
|
|
271
|
+
},
|
|
272
|
+
enableMetrics: true,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const client = new GoveeClient({
|
|
276
|
+
apiKey: 'your-api-key',
|
|
277
|
+
enableRetries: true,
|
|
278
|
+
retryPolicy: customRetryPolicy,
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Monitoring & Observability
|
|
283
|
+
|
|
284
|
+
Get comprehensive metrics for monitoring and debugging:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// Get complete service statistics
|
|
288
|
+
const serviceStats = client.getServiceStats();
|
|
289
|
+
console.log('Rate Limiter:', serviceStats.rateLimiter);
|
|
290
|
+
console.log('Retry Metrics:', serviceStats.retries);
|
|
291
|
+
console.log('Configuration:', serviceStats.configuration);
|
|
292
|
+
|
|
293
|
+
// Rate limiter specific stats
|
|
294
|
+
const rateLimiterStats = client.getRateLimiterStats();
|
|
295
|
+
console.log({
|
|
296
|
+
currentRequests: rateLimiterStats.currentRequests,
|
|
297
|
+
maxRequests: rateLimiterStats.maxRequests,
|
|
298
|
+
utilizationPercent: rateLimiterStats.utilizationPercent,
|
|
299
|
+
queueSize: rateLimiterStats.queueSize,
|
|
300
|
+
canExecuteImmediately: rateLimiterStats.canExecuteImmediately,
|
|
301
|
+
nextAvailableSlot: rateLimiterStats.nextAvailableSlot,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Retry metrics (when retries are enabled)
|
|
305
|
+
const retryMetrics = client.getRetryMetrics();
|
|
306
|
+
if (retryMetrics) {
|
|
307
|
+
console.log({
|
|
308
|
+
totalAttempts: retryMetrics.totalAttempts,
|
|
309
|
+
successfulRetries: retryMetrics.successfulRetries,
|
|
310
|
+
failedRetries: retryMetrics.failedRetries,
|
|
311
|
+
totalRetryTimeMs: retryMetrics.totalRetryTimeMs,
|
|
312
|
+
averageRetryDelayMs: retryMetrics.averageRetryDelayMs,
|
|
313
|
+
circuitBreakerState: retryMetrics.circuitBreakerState,
|
|
314
|
+
lastError: retryMetrics.lastError?.message,
|
|
315
|
+
lastRetryTimestamp: retryMetrics.lastRetryTimestamp,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Reset metrics for clean monitoring periods
|
|
320
|
+
client.resetRetryMetrics();
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Retry Policy Presets
|
|
324
|
+
|
|
325
|
+
The library includes three built-in retry policy presets optimized for different environments:
|
|
326
|
+
|
|
327
|
+
#### Production (Default)
|
|
328
|
+
|
|
329
|
+
- **Max attempts**: 3
|
|
330
|
+
- **Backoff**: Exponential with 1s initial delay, 30s max
|
|
331
|
+
- **Circuit breaker**: Enabled (5 failures to open, 30s recovery)
|
|
332
|
+
- **Jitter**: Equal jitter to prevent thundering herd
|
|
333
|
+
|
|
334
|
+
#### Development
|
|
335
|
+
|
|
336
|
+
- **Max attempts**: 5
|
|
337
|
+
- **Backoff**: Exponential with 500ms initial delay, 15s max
|
|
338
|
+
- **Circuit breaker**: Disabled for easier debugging
|
|
339
|
+
- **Jitter**: Full jitter for maximum randomization
|
|
340
|
+
|
|
341
|
+
#### Testing
|
|
342
|
+
|
|
343
|
+
- **Max attempts**: 2
|
|
344
|
+
- **Backoff**: Exponential with 2s initial delay, 60s max
|
|
345
|
+
- **Circuit breaker**: Enabled with conservative settings
|
|
346
|
+
- **Jitter**: Decorrelated jitter for sophisticated patterns
|
|
347
|
+
|
|
348
|
+
### Performance Characteristics
|
|
349
|
+
|
|
350
|
+
- **Rate Limiter**: High-performance sliding window allows concurrent execution within limits
|
|
351
|
+
- **Memory Efficient**: Automatic cleanup of expired timestamps and bounded queue sizes
|
|
352
|
+
- **Production Ready**: Circuit breaker prevents cascade failures
|
|
353
|
+
- **Observable**: Comprehensive metrics for monitoring and alerting
|
|
354
|
+
|
|
202
355
|
## Domain-Driven Design
|
|
203
356
|
|
|
204
357
|
The library follows DDD principles with clear separation of concerns:
|
package/dist/GoveeClient.d.ts
CHANGED
|
@@ -3,11 +3,14 @@ import { GoveeDevice } from './domain/entities/GoveeDevice';
|
|
|
3
3
|
import { DeviceState } from './domain/entities/DeviceState';
|
|
4
4
|
import { Command } from './domain/entities/Command';
|
|
5
5
|
import { ColorRgb, ColorTemperature, Brightness } from './domain/value-objects';
|
|
6
|
+
import { RetryPolicy } from './infrastructure/retry';
|
|
6
7
|
export interface GoveeClientConfig {
|
|
7
8
|
apiKey: string;
|
|
8
9
|
timeout?: number;
|
|
9
10
|
rateLimit?: number;
|
|
10
11
|
logger?: Logger;
|
|
12
|
+
enableRetries?: boolean;
|
|
13
|
+
retryPolicy?: 'development' | 'testing' | 'production' | 'custom' | RetryPolicy;
|
|
11
14
|
}
|
|
12
15
|
export declare class GoveeClient {
|
|
13
16
|
private readonly controlService;
|
|
@@ -31,5 +34,33 @@ export declare class GoveeClient {
|
|
|
31
34
|
turnOnWithColorTemperature(deviceId: string, model: string, colorTemperature: ColorTemperature, brightness?: Brightness): Promise<void>;
|
|
32
35
|
isDeviceOnline(deviceId: string, model: string): Promise<boolean>;
|
|
33
36
|
isDevicePoweredOn(deviceId: string, model: string): Promise<boolean>;
|
|
37
|
+
getRateLimiterStats(): {
|
|
38
|
+
currentRequests: number;
|
|
39
|
+
maxRequests: number;
|
|
40
|
+
queueSize: number;
|
|
41
|
+
windowMs: number;
|
|
42
|
+
utilizationPercent: number;
|
|
43
|
+
canExecuteImmediately: boolean;
|
|
44
|
+
nextAvailableSlot: number;
|
|
45
|
+
};
|
|
46
|
+
getRetryMetrics(): Readonly<import("./infrastructure/retry").RetryMetrics> | null;
|
|
47
|
+
getServiceStats(): {
|
|
48
|
+
rateLimiter: {
|
|
49
|
+
currentRequests: number;
|
|
50
|
+
maxRequests: number;
|
|
51
|
+
queueSize: number;
|
|
52
|
+
windowMs: number;
|
|
53
|
+
utilizationPercent: number;
|
|
54
|
+
canExecuteImmediately: boolean;
|
|
55
|
+
nextAvailableSlot: number;
|
|
56
|
+
};
|
|
57
|
+
retries: Readonly<import("./infrastructure/retry").RetryMetrics> | null;
|
|
58
|
+
configuration: {
|
|
59
|
+
enableRetries: boolean;
|
|
60
|
+
rateLimit: number;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
resetRetryMetrics(): void;
|
|
64
|
+
isRetryEnabled(): boolean;
|
|
34
65
|
}
|
|
35
66
|
//# sourceMappingURL=GoveeClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoveeClient.d.ts","sourceRoot":"","sources":["../src/GoveeClient.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGpC,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"GoveeClient.d.ts","sourceRoot":"","sources":["../src/GoveeClient.ts"],"names":[],"mappings":"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"}
|
package/dist/GoveeClient.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pino from 'pino';
|
|
2
2
|
import { GoveeDeviceRepository } from './infrastructure/GoveeDeviceRepository';
|
|
3
3
|
import { GoveeControlService } from './services/GoveeControlService';
|
|
4
|
+
import { RetryPolicy } from './infrastructure/retry';
|
|
4
5
|
export class GoveeClient {
|
|
5
6
|
constructor(config) {
|
|
6
7
|
this.validateConfig(config);
|
|
@@ -23,6 +24,12 @@ export class GoveeClient {
|
|
|
23
24
|
if (config.rateLimit !== undefined) {
|
|
24
25
|
serviceConfig.rateLimit = config.rateLimit;
|
|
25
26
|
}
|
|
27
|
+
if (config.enableRetries !== undefined) {
|
|
28
|
+
serviceConfig.enableRetries = config.enableRetries;
|
|
29
|
+
}
|
|
30
|
+
if (config.retryPolicy !== undefined) {
|
|
31
|
+
serviceConfig.retryPolicy = config.retryPolicy;
|
|
32
|
+
}
|
|
26
33
|
this.controlService = new GoveeControlService(serviceConfig);
|
|
27
34
|
this.logger.info('GoveeClient initialized successfully');
|
|
28
35
|
}
|
|
@@ -38,6 +45,16 @@ export class GoveeClient {
|
|
|
38
45
|
(!Number.isInteger(config.rateLimit) || config.rateLimit <= 0)) {
|
|
39
46
|
throw new Error('Rate limit must be a positive integer');
|
|
40
47
|
}
|
|
48
|
+
if (config.enableRetries !== undefined && typeof config.enableRetries !== 'boolean') {
|
|
49
|
+
throw new Error('enableRetries must be a boolean');
|
|
50
|
+
}
|
|
51
|
+
if (config.retryPolicy !== undefined) {
|
|
52
|
+
const validPolicies = ['development', 'testing', 'production', 'custom'];
|
|
53
|
+
if (!(config.retryPolicy instanceof RetryPolicy) &&
|
|
54
|
+
!validPolicies.includes(config.retryPolicy)) {
|
|
55
|
+
throw new Error('retryPolicy must be a RetryPolicy instance or one of: development, testing, production, custom');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
41
58
|
}
|
|
42
59
|
// Device management methods
|
|
43
60
|
async getDevices() {
|
|
@@ -93,5 +110,21 @@ export class GoveeClient {
|
|
|
93
110
|
async isDevicePoweredOn(deviceId, model) {
|
|
94
111
|
return this.controlService.isDevicePoweredOn(deviceId, model);
|
|
95
112
|
}
|
|
113
|
+
// Monitoring and debugging methods
|
|
114
|
+
getRateLimiterStats() {
|
|
115
|
+
return this.controlService.getRateLimiterStats();
|
|
116
|
+
}
|
|
117
|
+
getRetryMetrics() {
|
|
118
|
+
return this.controlService.getRetryMetrics();
|
|
119
|
+
}
|
|
120
|
+
getServiceStats() {
|
|
121
|
+
return this.controlService.getServiceStats();
|
|
122
|
+
}
|
|
123
|
+
resetRetryMetrics() {
|
|
124
|
+
return this.controlService.resetRetryMetrics();
|
|
125
|
+
}
|
|
126
|
+
isRetryEnabled() {
|
|
127
|
+
return this.controlService.isRetryEnabled();
|
|
128
|
+
}
|
|
96
129
|
}
|
|
97
130
|
//# sourceMappingURL=GoveeClient.js.map
|
package/dist/GoveeClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoveeClient.js","sourceRoot":"","sources":["../src/GoveeClient.ts"],"names":[],"mappings":"AAAA,OAAO,IAAgB,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"GoveeClient.js","sourceRoot":"","sources":["../src/GoveeClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,34 @@
|
|
|
1
|
+
interface GoveeCapability {
|
|
2
|
+
type: string;
|
|
3
|
+
instance: string;
|
|
4
|
+
parameters?: {
|
|
5
|
+
dataType: string;
|
|
6
|
+
options?: Array<{
|
|
7
|
+
name: string;
|
|
8
|
+
value: unknown;
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
1
12
|
export declare class GoveeDevice {
|
|
2
13
|
private readonly _deviceId;
|
|
3
|
-
private readonly
|
|
14
|
+
private readonly _sku;
|
|
4
15
|
private readonly _deviceName;
|
|
16
|
+
private readonly _capabilities;
|
|
5
17
|
private readonly _controllable;
|
|
6
18
|
private readonly _retrievable;
|
|
7
19
|
private readonly _supportedCmds;
|
|
8
|
-
constructor(deviceId: string,
|
|
20
|
+
constructor(deviceId: string, sku: string, deviceName: string, capabilities: GoveeCapability[]);
|
|
9
21
|
private validateDeviceId;
|
|
10
|
-
private
|
|
22
|
+
private validateSku;
|
|
11
23
|
private validateDeviceName;
|
|
12
|
-
private
|
|
24
|
+
private validateCapabilities;
|
|
25
|
+
private deriveControllable;
|
|
26
|
+
private deriveRetrievable;
|
|
27
|
+
private deriveSupportedCommands;
|
|
13
28
|
get deviceId(): string;
|
|
14
29
|
get model(): string;
|
|
30
|
+
get sku(): string;
|
|
31
|
+
get capabilities(): readonly GoveeCapability[];
|
|
15
32
|
get deviceName(): string;
|
|
16
33
|
get controllable(): boolean;
|
|
17
34
|
get retrievable(): boolean;
|
|
@@ -38,4 +55,5 @@ export declare class GoveeDevice {
|
|
|
38
55
|
supportedCmds: string[];
|
|
39
56
|
}): GoveeDevice;
|
|
40
57
|
}
|
|
58
|
+
export {};
|
|
41
59
|
//# sourceMappingURL=GoveeDevice.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoveeDevice.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/GoveeDevice.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"GoveeDevice.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/GoveeDevice.ts"],"names":[],"mappings":"AAAA,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,KAAK,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,OAAO,CAAC;SAChB,CAAC,CAAC;KACJ,CAAC;CACH;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAW;gBAE9B,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE;IAe9F,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,uBAAuB;IAgB/B,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,YAAY,IAAI,SAAS,eAAe,EAAE,CAE7C;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,aAAa,IAAI,SAAS,MAAM,EAAE,CAErC;IAED,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAInC,QAAQ,IAAI,MAAM;IAIlB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIzC,UAAU,IAAI,OAAO;IAIrB,WAAW,IAAI,OAAO;IAItB,QAAQ,IAAI;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,CAAC;QACtB,WAAW,EAAE,OAAO,CAAC;QACrB,aAAa,EAAE,MAAM,EAAE,CAAC;KACzB;IAWD,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,CAAC;QACtB,WAAW,EAAE,OAAO,CAAC;QACrB,aAAa,EAAE,MAAM,EAAE,CAAC;KACzB,GAAG,WAAW;CAsBhB"}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
export class GoveeDevice {
|
|
2
|
-
constructor(deviceId,
|
|
2
|
+
constructor(deviceId, sku, deviceName, capabilities) {
|
|
3
3
|
this.validateDeviceId(deviceId);
|
|
4
|
-
this.
|
|
4
|
+
this.validateSku(sku);
|
|
5
5
|
this.validateDeviceName(deviceName);
|
|
6
|
-
this.
|
|
6
|
+
this.validateCapabilities(capabilities);
|
|
7
7
|
this._deviceId = deviceId;
|
|
8
|
-
this.
|
|
8
|
+
this._sku = sku;
|
|
9
9
|
this._deviceName = deviceName;
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
10
|
+
this._capabilities = [...capabilities];
|
|
11
|
+
this._controllable = this.deriveControllable(capabilities);
|
|
12
|
+
this._retrievable = this.deriveRetrievable(capabilities);
|
|
13
|
+
this._supportedCmds = this.deriveSupportedCommands(capabilities);
|
|
13
14
|
}
|
|
14
15
|
validateDeviceId(deviceId) {
|
|
15
16
|
if (!deviceId || typeof deviceId !== 'string' || deviceId.trim().length === 0) {
|
|
16
17
|
throw new Error('Device ID must be a non-empty string');
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
if (!
|
|
21
|
-
throw new Error('
|
|
20
|
+
validateSku(sku) {
|
|
21
|
+
if (!sku || typeof sku !== 'string' || sku.trim().length === 0) {
|
|
22
|
+
throw new Error('SKU must be a non-empty string');
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
validateDeviceName(deviceName) {
|
|
@@ -26,21 +27,55 @@ export class GoveeDevice {
|
|
|
26
27
|
throw new Error('Device name must be a non-empty string');
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
if (!Array.isArray(
|
|
31
|
-
throw new Error('
|
|
30
|
+
validateCapabilities(capabilities) {
|
|
31
|
+
if (!Array.isArray(capabilities)) {
|
|
32
|
+
throw new Error('Capabilities must be an array');
|
|
32
33
|
}
|
|
33
|
-
for (const
|
|
34
|
-
if (
|
|
35
|
-
|
|
34
|
+
for (const capability of capabilities) {
|
|
35
|
+
if (!capability.type ||
|
|
36
|
+
typeof capability.type !== 'string' ||
|
|
37
|
+
capability.type.trim().length === 0) {
|
|
38
|
+
throw new Error('All capabilities must have non-empty type strings');
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
42
|
+
deriveControllable(capabilities) {
|
|
43
|
+
// Device is controllable if it has any control capabilities
|
|
44
|
+
return capabilities.some(cap => cap.type.includes('on_off') ||
|
|
45
|
+
cap.type.includes('range') ||
|
|
46
|
+
cap.type.includes('color_setting'));
|
|
47
|
+
}
|
|
48
|
+
deriveRetrievable(capabilities) {
|
|
49
|
+
// Most devices with capabilities are retrievable
|
|
50
|
+
return capabilities.length > 0;
|
|
51
|
+
}
|
|
52
|
+
deriveSupportedCommands(capabilities) {
|
|
53
|
+
const commands = new Set();
|
|
54
|
+
for (const capability of capabilities) {
|
|
55
|
+
if (capability.type.includes('on_off'))
|
|
56
|
+
commands.add('turn');
|
|
57
|
+
if (capability.type.includes('range') && capability.instance === 'brightness')
|
|
58
|
+
commands.add('brightness');
|
|
59
|
+
if (capability.type.includes('color_setting')) {
|
|
60
|
+
if (capability.instance === 'colorRgb')
|
|
61
|
+
commands.add('color');
|
|
62
|
+
if (capability.instance === 'colorTemperatureK')
|
|
63
|
+
commands.add('colorTem');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return Array.from(commands);
|
|
67
|
+
}
|
|
39
68
|
get deviceId() {
|
|
40
69
|
return this._deviceId;
|
|
41
70
|
}
|
|
42
71
|
get model() {
|
|
43
|
-
return this.
|
|
72
|
+
return this._sku; // For backward compatibility
|
|
73
|
+
}
|
|
74
|
+
get sku() {
|
|
75
|
+
return this._sku;
|
|
76
|
+
}
|
|
77
|
+
get capabilities() {
|
|
78
|
+
return Object.freeze([...this._capabilities]);
|
|
44
79
|
}
|
|
45
80
|
get deviceName() {
|
|
46
81
|
return this._deviceName;
|
|
@@ -55,10 +90,10 @@ export class GoveeDevice {
|
|
|
55
90
|
return Object.freeze([...this._supportedCmds]);
|
|
56
91
|
}
|
|
57
92
|
equals(other) {
|
|
58
|
-
return this._deviceId === other._deviceId && this.
|
|
93
|
+
return this._deviceId === other._deviceId && this._sku === other._sku;
|
|
59
94
|
}
|
|
60
95
|
toString() {
|
|
61
|
-
return `GoveeDevice(${this._deviceId}, ${this.
|
|
96
|
+
return `GoveeDevice(${this._deviceId}, ${this._sku}, "${this._deviceName}")`;
|
|
62
97
|
}
|
|
63
98
|
supportsCommand(command) {
|
|
64
99
|
return this._supportedCmds.includes(command);
|
|
@@ -72,7 +107,7 @@ export class GoveeDevice {
|
|
|
72
107
|
toObject() {
|
|
73
108
|
return {
|
|
74
109
|
deviceId: this._deviceId,
|
|
75
|
-
model: this.
|
|
110
|
+
model: this._sku,
|
|
76
111
|
deviceName: this._deviceName,
|
|
77
112
|
controllable: this._controllable,
|
|
78
113
|
retrievable: this._retrievable,
|
|
@@ -80,8 +115,24 @@ export class GoveeDevice {
|
|
|
80
115
|
};
|
|
81
116
|
}
|
|
82
117
|
static fromObject(obj) {
|
|
83
|
-
|
|
84
|
-
|
|
118
|
+
// Convert old format to new capabilities format for backward compatibility
|
|
119
|
+
const capabilities = [];
|
|
120
|
+
if (obj.supportedCmds.includes('turn')) {
|
|
121
|
+
capabilities.push({ type: 'devices.capabilities.on_off', instance: 'powerSwitch' });
|
|
122
|
+
}
|
|
123
|
+
if (obj.supportedCmds.includes('brightness')) {
|
|
124
|
+
capabilities.push({ type: 'devices.capabilities.range', instance: 'brightness' });
|
|
125
|
+
}
|
|
126
|
+
if (obj.supportedCmds.includes('color')) {
|
|
127
|
+
capabilities.push({ type: 'devices.capabilities.color_setting', instance: 'colorRgb' });
|
|
128
|
+
}
|
|
129
|
+
if (obj.supportedCmds.includes('colorTem')) {
|
|
130
|
+
capabilities.push({
|
|
131
|
+
type: 'devices.capabilities.color_setting',
|
|
132
|
+
instance: 'colorTemperatureK',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return new GoveeDevice(obj.deviceId, obj.model, obj.deviceName, capabilities);
|
|
85
136
|
}
|
|
86
137
|
}
|
|
87
138
|
//# sourceMappingURL=GoveeDevice.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoveeDevice.js","sourceRoot":"","sources":["../../../src/domain/entities/GoveeDevice.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GoveeDevice.js","sourceRoot":"","sources":["../../../src/domain/entities/GoveeDevice.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,10 +9,10 @@ export interface IGoveeDeviceRepository {
|
|
|
9
9
|
/**
|
|
10
10
|
* Retrieves the current state of a specific device
|
|
11
11
|
*/
|
|
12
|
-
findState(deviceId: string,
|
|
12
|
+
findState(deviceId: string, sku: string): Promise<DeviceState>;
|
|
13
13
|
/**
|
|
14
14
|
* Sends a command to control a specific device
|
|
15
15
|
*/
|
|
16
|
-
sendCommand(deviceId: string,
|
|
16
|
+
sendCommand(deviceId: string, sku: string, command: Command): Promise<void>;
|
|
17
17
|
}
|
|
18
18
|
//# sourceMappingURL=IGoveeDeviceRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IGoveeDeviceRepository.d.ts","sourceRoot":"","sources":["../../../src/domain/repositories/IGoveeDeviceRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAElC;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"IGoveeDeviceRepository.d.ts","sourceRoot":"","sources":["../../../src/domain/repositories/IGoveeDeviceRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAElC;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE/D;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ZodError } from 'zod';
|
|
2
|
+
import { GoveeApiClientError } from './GoveeApiClientError';
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown when API response validation fails using Zod schemas
|
|
5
|
+
*/
|
|
6
|
+
export declare class ValidationError extends GoveeApiClientError {
|
|
7
|
+
readonly code = "VALIDATION_ERROR";
|
|
8
|
+
readonly zodError: ZodError;
|
|
9
|
+
readonly rawData: unknown;
|
|
10
|
+
constructor(message: string, zodError: ZodError, rawData: unknown);
|
|
11
|
+
/**
|
|
12
|
+
* Create a ValidationError from a Zod validation failure
|
|
13
|
+
*/
|
|
14
|
+
static fromZodError(zodError: ZodError, rawData: unknown): ValidationError;
|
|
15
|
+
/**
|
|
16
|
+
* Get a detailed breakdown of validation errors
|
|
17
|
+
*/
|
|
18
|
+
getValidationDetails(): Array<{
|
|
19
|
+
path: string;
|
|
20
|
+
message: string;
|
|
21
|
+
received: unknown;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Get a summary of validation errors for logging
|
|
25
|
+
*/
|
|
26
|
+
getValidationSummary(): string;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=ValidationError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValidationError.d.ts","sourceRoot":"","sources":["../../src/errors/ValidationError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D;;GAEG;AACH,qBAAa,eAAgB,SAAQ,mBAAmB;IACtD,QAAQ,CAAC,IAAI,sBAAsB;IACnC,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IACnC,SAAgB,OAAO,EAAE,OAAO,CAAC;gBAErB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO;IAOjE;;OAEG;WACW,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,eAAe;IAUjF;;OAEG;IACI,oBAAoB,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAQ1F;;OAEG;IACI,oBAAoB,IAAI,MAAM;CAQtC"}
|