@felixgeelhaar/govee-api-client 1.1.0 → 2.0.1

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.
Files changed (68) hide show
  1. package/README.md +160 -7
  2. package/dist/GoveeClient.d.ts +31 -0
  3. package/dist/GoveeClient.d.ts.map +1 -1
  4. package/dist/GoveeClient.js +33 -0
  5. package/dist/GoveeClient.js.map +1 -1
  6. package/dist/errors/GoveeApiError.d.ts +1 -1
  7. package/dist/errors/GoveeApiError.d.ts.map +1 -1
  8. package/dist/errors/GoveeApiError.js +9 -2
  9. package/dist/errors/GoveeApiError.js.map +1 -1
  10. package/dist/errors/ValidationError.d.ts +28 -0
  11. package/dist/errors/ValidationError.d.ts.map +1 -0
  12. package/dist/errors/ValidationError.js +44 -0
  13. package/dist/errors/ValidationError.js.map +1 -0
  14. package/dist/errors/index.d.ts +1 -0
  15. package/dist/errors/index.d.ts.map +1 -1
  16. package/dist/errors/index.js +1 -0
  17. package/dist/errors/index.js.map +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/infrastructure/GoveeDeviceRepository.d.ts.map +1 -1
  23. package/dist/infrastructure/GoveeDeviceRepository.js +26 -3
  24. package/dist/infrastructure/GoveeDeviceRepository.js.map +1 -1
  25. package/dist/infrastructure/SlidingWindowRateLimiter.d.ts +83 -0
  26. package/dist/infrastructure/SlidingWindowRateLimiter.d.ts.map +1 -0
  27. package/dist/infrastructure/SlidingWindowRateLimiter.js +218 -0
  28. package/dist/infrastructure/SlidingWindowRateLimiter.js.map +1 -0
  29. package/dist/infrastructure/index.d.ts +2 -0
  30. package/dist/infrastructure/index.d.ts.map +1 -1
  31. package/dist/infrastructure/index.js +2 -0
  32. package/dist/infrastructure/index.js.map +1 -1
  33. package/dist/infrastructure/response-schemas.d.ts +82 -0
  34. package/dist/infrastructure/response-schemas.d.ts.map +1 -0
  35. package/dist/infrastructure/response-schemas.js +59 -0
  36. package/dist/infrastructure/response-schemas.js.map +1 -0
  37. package/dist/infrastructure/retry/IntegrationGuide.d.ts +133 -0
  38. package/dist/infrastructure/retry/IntegrationGuide.d.ts.map +1 -0
  39. package/dist/infrastructure/retry/IntegrationGuide.js +295 -0
  40. package/dist/infrastructure/retry/IntegrationGuide.js.map +1 -0
  41. package/dist/infrastructure/retry/RetryConfigPresets.d.ts +114 -0
  42. package/dist/infrastructure/retry/RetryConfigPresets.d.ts.map +1 -0
  43. package/dist/infrastructure/retry/RetryConfigPresets.js +406 -0
  44. package/dist/infrastructure/retry/RetryConfigPresets.js.map +1 -0
  45. package/dist/infrastructure/retry/RetryPolicy.d.ts +148 -0
  46. package/dist/infrastructure/retry/RetryPolicy.d.ts.map +1 -0
  47. package/dist/infrastructure/retry/RetryPolicy.js +373 -0
  48. package/dist/infrastructure/retry/RetryPolicy.js.map +1 -0
  49. package/dist/infrastructure/retry/RetryableRepository.d.ts +75 -0
  50. package/dist/infrastructure/retry/RetryableRepository.d.ts.map +1 -0
  51. package/dist/infrastructure/retry/RetryableRepository.js +142 -0
  52. package/dist/infrastructure/retry/RetryableRepository.js.map +1 -0
  53. package/dist/infrastructure/retry/RetryableRequest.d.ts +132 -0
  54. package/dist/infrastructure/retry/RetryableRequest.d.ts.map +1 -0
  55. package/dist/infrastructure/retry/RetryableRequest.js +248 -0
  56. package/dist/infrastructure/retry/RetryableRequest.js.map +1 -0
  57. package/dist/infrastructure/retry/index.d.ts +35 -0
  58. package/dist/infrastructure/retry/index.d.ts.map +1 -0
  59. package/dist/infrastructure/retry/index.js +36 -0
  60. package/dist/infrastructure/retry/index.js.map +1 -0
  61. package/dist/services/GoveeControlService.d.ts +53 -0
  62. package/dist/services/GoveeControlService.d.ts.map +1 -1
  63. package/dist/services/GoveeControlService.js +138 -12
  64. package/dist/services/GoveeControlService.js.map +1 -1
  65. package/docs/EXAMPLES.md +799 -0
  66. package/docs/LLM_API_REFERENCE.md +425 -0
  67. package/docs/TYPE_DEFINITIONS.md +803 -0
  68. package/package.json +25 -16
@@ -0,0 +1,799 @@
1
+ # Govee API Client - Examples
2
+
3
+ This document provides comprehensive examples for using the Govee API Client library. Each example is complete and can be run as-is with a valid API key.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Basic Setup](#basic-setup)
8
+ - [Device Discovery](#device-discovery)
9
+ - [Device Control](#device-control)
10
+ - [Color Management](#color-management)
11
+ - [State Management](#state-management)
12
+ - [Error Handling](#error-handling)
13
+ - [Rate Limiting & Monitoring](#rate-limiting--monitoring)
14
+ - [Retry Configuration](#retry-configuration)
15
+ - [Advanced Usage](#advanced-usage)
16
+
17
+ ## Basic Setup
18
+
19
+ ### Simple Client Initialization
20
+
21
+ ```typescript
22
+ import { GoveeClient } from '@felixgeelhaar/govee-api-client';
23
+
24
+ const client = new GoveeClient({
25
+ apiKey: 'your-govee-api-key-here',
26
+ });
27
+
28
+ // Test connection
29
+ try {
30
+ const devices = await client.getDevices();
31
+ console.log(`Successfully connected! Found ${devices.length} devices.`);
32
+ } catch (error) {
33
+ console.error('Failed to connect:', error.message);
34
+ }
35
+ ```
36
+
37
+ ### Production Configuration
38
+
39
+ ```typescript
40
+ import { GoveeClient } from '@felixgeelhaar/govee-api-client';
41
+ import pino from 'pino';
42
+
43
+ const client = new GoveeClient({
44
+ apiKey: process.env.GOVEE_API_KEY!,
45
+ timeout: 30000, // 30 second timeout
46
+ rateLimit: 90, // 90 requests per minute (conservative)
47
+ logger: pino({
48
+ // Structured logging
49
+ level: 'info',
50
+ transport: {
51
+ target: 'pino-pretty',
52
+ options: { colorize: true },
53
+ },
54
+ }),
55
+ enableRetries: true, // Enable retry logic
56
+ retryPolicy: 'production', // Production retry settings
57
+ });
58
+ ```
59
+
60
+ ## Device Discovery
61
+
62
+ ### List All Devices
63
+
64
+ ```typescript
65
+ async function listAllDevices() {
66
+ try {
67
+ const devices = await client.getDevices();
68
+
69
+ console.log(`Found ${devices.length} devices:`);
70
+ devices.forEach(device => {
71
+ console.log(`- ${device.deviceName} (${device.model})`);
72
+ console.log(` ID: ${device.deviceId}`);
73
+ console.log(` Controllable: ${device.controllable}`);
74
+ console.log(` Supported commands: ${device.supportedCmds.join(', ')}`);
75
+ console.log('');
76
+ });
77
+ } catch (error) {
78
+ console.error('Failed to list devices:', error.message);
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### Find Specific Devices
84
+
85
+ ```typescript
86
+ async function findDevicesByName() {
87
+ // Case-insensitive search
88
+ const livingRoomLight = await client.findDeviceByName('living room');
89
+
90
+ if (livingRoomLight) {
91
+ console.log('Found living room light:', livingRoomLight.deviceName);
92
+ } else {
93
+ console.log('Living room light not found');
94
+ }
95
+ }
96
+
97
+ async function findDevicesByModel() {
98
+ const h6159Devices = await client.findDevicesByModel('H6159');
99
+
100
+ console.log(`Found ${h6159Devices.length} H6159 devices:`);
101
+ h6159Devices.forEach(device => {
102
+ console.log(`- ${device.deviceName} (${device.deviceId})`);
103
+ });
104
+ }
105
+ ```
106
+
107
+ ### Filter Controllable Devices
108
+
109
+ ```typescript
110
+ async function getControllableDevices() {
111
+ const controllableDevices = await client.getControllableDevices();
112
+
113
+ console.log('Controllable devices:');
114
+ controllableDevices.forEach(device => {
115
+ console.log(`- ${device.deviceName}: ${device.supportedCmds.join(', ')}`);
116
+ });
117
+ }
118
+ ```
119
+
120
+ ## Device Control
121
+
122
+ ### Basic Power Control
123
+
124
+ ```typescript
125
+ async function basicPowerControl(deviceId: string, model: string) {
126
+ try {
127
+ // Turn on
128
+ await client.turnOn(deviceId, model);
129
+ console.log('Device turned on');
130
+
131
+ // Wait a moment
132
+ await new Promise(resolve => setTimeout(resolve, 1000));
133
+
134
+ // Turn off
135
+ await client.turnOff(deviceId, model);
136
+ console.log('Device turned off');
137
+ } catch (error) {
138
+ console.error('Power control failed:', error.message);
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Brightness Control
144
+
145
+ ```typescript
146
+ import { Brightness } from '@felixgeelhaar/govee-api-client';
147
+
148
+ async function brightnessControl(deviceId: string, model: string) {
149
+ try {
150
+ // Set to 25% brightness
151
+ await client.setBrightness(deviceId, model, Brightness.dim());
152
+ console.log('Set to dim (25%)');
153
+
154
+ // Set to 50% brightness
155
+ await client.setBrightness(deviceId, model, Brightness.medium());
156
+ console.log('Set to medium (50%)');
157
+
158
+ // Set to 75% brightness
159
+ await client.setBrightness(deviceId, model, Brightness.bright());
160
+ console.log('Set to bright (75%)');
161
+
162
+ // Custom brightness
163
+ await client.setBrightness(deviceId, model, new Brightness(85));
164
+ console.log('Set to custom brightness (85%)');
165
+ } catch (error) {
166
+ console.error('Brightness control failed:', error.message);
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Turn On With Settings
172
+
173
+ ```typescript
174
+ import { ColorRgb, ColorTemperature, Brightness } from '@felixgeelhaar/govee-api-client';
175
+
176
+ async function turnOnWithSettings(deviceId: string, model: string) {
177
+ try {
178
+ // Turn on with brightness
179
+ await client.turnOnWithBrightness(deviceId, model, new Brightness(60));
180
+ console.log('Turned on with 60% brightness');
181
+
182
+ // Turn on with red color
183
+ const red = new ColorRgb(255, 0, 0);
184
+ await client.turnOnWithColor(deviceId, model, red, new Brightness(80));
185
+ console.log('Turned on with red color at 80% brightness');
186
+
187
+ // Turn on with warm white
188
+ const warmWhite = ColorTemperature.warmWhite();
189
+ await client.turnOnWithColorTemperature(deviceId, model, warmWhite, new Brightness(100));
190
+ console.log('Turned on with warm white at 100% brightness');
191
+ } catch (error) {
192
+ console.error('Turn on with settings failed:', error.message);
193
+ }
194
+ }
195
+ ```
196
+
197
+ ## Color Management
198
+
199
+ ### RGB Color Control
200
+
201
+ ```typescript
202
+ import { ColorRgb } from '@felixgeelhaar/govee-api-client';
203
+
204
+ async function rgbColorControl(deviceId: string, model: string) {
205
+ try {
206
+ // Primary colors
207
+ await client.setColor(deviceId, model, new ColorRgb(255, 0, 0)); // Red
208
+ console.log('Set to red');
209
+
210
+ await new Promise(resolve => setTimeout(resolve, 1000));
211
+
212
+ await client.setColor(deviceId, model, new ColorRgb(0, 255, 0)); // Green
213
+ console.log('Set to green');
214
+
215
+ await new Promise(resolve => setTimeout(resolve, 1000));
216
+
217
+ await client.setColor(deviceId, model, new ColorRgb(0, 0, 255)); // Blue
218
+ console.log('Set to blue');
219
+
220
+ // Create color from hex
221
+ const purple = ColorRgb.fromHex('#800080');
222
+ await client.setColor(deviceId, model, purple);
223
+ console.log('Set to purple from hex');
224
+
225
+ // Create color from object
226
+ const cyan = ColorRgb.fromObject({ r: 0, g: 255, b: 255 });
227
+ await client.setColor(deviceId, model, cyan);
228
+ console.log('Set to cyan from object');
229
+ } catch (error) {
230
+ console.error('RGB color control failed:', error.message);
231
+ }
232
+ }
233
+ ```
234
+
235
+ ### Color Temperature Control
236
+
237
+ ```typescript
238
+ import { ColorTemperature } from '@felixgeelhaar/govee-api-client';
239
+
240
+ async function colorTemperatureControl(deviceId: string, model: string) {
241
+ try {
242
+ // Preset temperatures
243
+ await client.setColorTemperature(deviceId, model, ColorTemperature.warmWhite());
244
+ console.log('Set to warm white (2700K)');
245
+
246
+ await new Promise(resolve => setTimeout(resolve, 1000));
247
+
248
+ await client.setColorTemperature(deviceId, model, ColorTemperature.daylight());
249
+ console.log('Set to daylight (5600K)');
250
+
251
+ await new Promise(resolve => setTimeout(resolve, 1000));
252
+
253
+ await client.setColorTemperature(deviceId, model, ColorTemperature.coolWhite());
254
+ console.log('Set to cool white (6500K)');
255
+
256
+ // Custom temperature
257
+ const customTemp = new ColorTemperature(4000);
258
+ await client.setColorTemperature(deviceId, model, customTemp);
259
+ console.log('Set to custom temperature (4000K)');
260
+
261
+ // Check if temperature is warm or cool
262
+ console.log(`Is warm: ${customTemp.isWarm()}`); // true (< 4000K)
263
+ console.log(`Is cool: ${customTemp.isCool()}`); // false (> 5000K)
264
+ } catch (error) {
265
+ console.error('Color temperature control failed:', error.message);
266
+ }
267
+ }
268
+ ```
269
+
270
+ ### Color Utilities
271
+
272
+ ```typescript
273
+ import { ColorRgb } from '@felixgeelhaar/govee-api-client';
274
+
275
+ function colorUtilities() {
276
+ const red = new ColorRgb(255, 0, 0);
277
+
278
+ // Color information
279
+ console.log('RGB values:', red.r, red.g, red.b);
280
+ console.log('Hex representation:', red.toHex()); // "#ff0000"
281
+ console.log('String representation:', red.toString()); // "rgb(255, 0, 0)"
282
+ console.log('Object representation:', red.toObject()); // { r: 255, g: 0, b: 0 }
283
+
284
+ // Color comparison
285
+ const anotherRed = new ColorRgb(255, 0, 0);
286
+ const blue = new ColorRgb(0, 0, 255);
287
+
288
+ console.log('Colors equal:', red.equals(anotherRed)); // true
289
+ console.log('Colors equal:', red.equals(blue)); // false
290
+ }
291
+ ```
292
+
293
+ ## State Management
294
+
295
+ ### Check Device State
296
+
297
+ ```typescript
298
+ async function checkDeviceState(deviceId: string, model: string) {
299
+ try {
300
+ const state = await client.getDeviceState(deviceId, model);
301
+
302
+ console.log('Device State:');
303
+ console.log(`- Online: ${state.isOnline()}`);
304
+ console.log(`- Power: ${state.getPowerState()}`);
305
+ console.log(`- Brightness: ${state.getBrightness()}%`);
306
+
307
+ const color = state.getColor();
308
+ if (color) {
309
+ console.log(`- Color: rgb(${color.r}, ${color.g}, ${color.b})`);
310
+ }
311
+
312
+ const temp = state.getColorTemperature();
313
+ if (temp) {
314
+ console.log(`- Color Temperature: ${temp}K`);
315
+ }
316
+ } catch (error) {
317
+ console.error('Failed to get device state:', error.message);
318
+ }
319
+ }
320
+ ```
321
+
322
+ ### Monitor Device Status
323
+
324
+ ```typescript
325
+ async function monitorDeviceStatus(deviceId: string, model: string) {
326
+ try {
327
+ // Check if device is online
328
+ const isOnline = await client.isDeviceOnline(deviceId, model);
329
+ console.log(`Device online: ${isOnline}`);
330
+
331
+ // Check if device is powered on
332
+ const isPoweredOn = await client.isDevicePoweredOn(deviceId, model);
333
+ console.log(`Device powered on: ${isPoweredOn}`);
334
+
335
+ // Only control if device is online
336
+ if (isOnline) {
337
+ await client.turnOn(deviceId, model);
338
+ console.log('Device turned on');
339
+ } else {
340
+ console.log('Device is offline, cannot control');
341
+ }
342
+ } catch (error) {
343
+ console.error('Device monitoring failed:', error.message);
344
+ }
345
+ }
346
+ ```
347
+
348
+ ## Error Handling
349
+
350
+ ### Comprehensive Error Handling
351
+
352
+ ```typescript
353
+ import {
354
+ GoveeApiError,
355
+ InvalidApiKeyError,
356
+ RateLimitError,
357
+ NetworkError,
358
+ ValidationError,
359
+ } from '@felixgeelhaar/govee-api-client';
360
+
361
+ async function comprehensiveErrorHandling() {
362
+ try {
363
+ const devices = await client.getDevices();
364
+ // ... perform operations
365
+ } catch (error) {
366
+ if (error instanceof InvalidApiKeyError) {
367
+ console.error('❌ Invalid API Key');
368
+ console.log('💡 Suggestion:', error.getRecommendation());
369
+ // Handle: Get new API key, update configuration
370
+ } else if (error instanceof RateLimitError) {
371
+ console.error('⏱️ Rate Limited');
372
+ console.log(`⏳ Retry after: ${error.getRetryAfterMs()}ms`);
373
+ // Handle: Wait and retry, or queue for later
374
+ } else if (error instanceof GoveeApiError) {
375
+ console.error('🔌 API Error:', error.message);
376
+ console.log('💡 Suggestion:', error.getRecommendation());
377
+
378
+ if (error.isDeviceOffline()) {
379
+ console.log('📴 Device is currently offline');
380
+ // Handle: Skip this device, notify user
381
+ }
382
+
383
+ if (error.statusCode) {
384
+ console.log(`🔢 Status Code: ${error.statusCode}`);
385
+ }
386
+ } else if (error instanceof NetworkError) {
387
+ console.error('🌐 Network Error:', error.message);
388
+ console.log(`🔄 Retryable: ${error.isRetryable()}`);
389
+
390
+ if (error.isRetryable()) {
391
+ // Implement retry logic
392
+ console.log('Will retry this operation...');
393
+ }
394
+ } else if (error instanceof ValidationError) {
395
+ console.error('✅ Validation Error:', error.message);
396
+ if (error.field) {
397
+ console.log(`📍 Field: ${error.field}`);
398
+ console.log(`💥 Value: ${error.value}`);
399
+ }
400
+ } else {
401
+ console.error('❓ Unknown Error:', error);
402
+ }
403
+ }
404
+ }
405
+ ```
406
+
407
+ ### Retry Pattern
408
+
409
+ ```typescript
410
+ async function retryPattern(operation: () => Promise<void>, maxRetries = 3) {
411
+ let attempt = 0;
412
+
413
+ while (attempt < maxRetries) {
414
+ try {
415
+ await operation();
416
+ return; // Success!
417
+ } catch (error) {
418
+ attempt++;
419
+
420
+ if (error instanceof RateLimitError && attempt < maxRetries) {
421
+ const delay = error.getRetryAfterMs();
422
+ console.log(`Rate limited, waiting ${delay}ms before retry ${attempt}/${maxRetries}`);
423
+ await new Promise(resolve => setTimeout(resolve, delay));
424
+ continue;
425
+ }
426
+
427
+ if (error instanceof NetworkError && error.isRetryable() && attempt < maxRetries) {
428
+ const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
429
+ console.log(`Network error, waiting ${delay}ms before retry ${attempt}/${maxRetries}`);
430
+ await new Promise(resolve => setTimeout(resolve, delay));
431
+ continue;
432
+ }
433
+
434
+ // Not retryable or max retries reached
435
+ throw error;
436
+ }
437
+ }
438
+ }
439
+ ```
440
+
441
+ ## Rate Limiting & Monitoring
442
+
443
+ ### Monitor Rate Limiter
444
+
445
+ ```typescript
446
+ function monitorRateLimiter() {
447
+ const stats = client.getRateLimiterStats();
448
+
449
+ console.log('Rate Limiter Status:');
450
+ console.log(`- Current requests: ${stats.currentRequests}/${stats.maxRequests}`);
451
+ console.log(`- Utilization: ${stats.utilizationPercent.toFixed(1)}%`);
452
+ console.log(`- Queue size: ${stats.queueSize}`);
453
+ console.log(`- Can execute immediately: ${stats.canExecuteImmediately}`);
454
+
455
+ if (stats.nextAvailableSlot) {
456
+ console.log(`- Next slot available: ${stats.nextAvailableSlot.toISOString()}`);
457
+ }
458
+
459
+ // Alert if utilization is high
460
+ if (stats.utilizationPercent > 80) {
461
+ console.warn('⚠️ High rate limiter utilization, consider reducing request frequency');
462
+ }
463
+ }
464
+ ```
465
+
466
+ ### Monitor Service Performance
467
+
468
+ ```typescript
469
+ function monitorServicePerformance() {
470
+ const serviceStats = client.getServiceStats();
471
+
472
+ console.log('Service Statistics:');
473
+ console.log('Rate Limiter:', {
474
+ utilization: `${serviceStats.rateLimiter.utilizationPercent.toFixed(1)}%`,
475
+ queue: serviceStats.rateLimiter.queueSize,
476
+ canExecute: serviceStats.rateLimiter.canExecuteImmediately,
477
+ });
478
+
479
+ if (serviceStats.retries) {
480
+ console.log('Retry Metrics:', {
481
+ attempts: serviceStats.retries.totalAttempts,
482
+ successful: serviceStats.retries.successfulRetries,
483
+ failed: serviceStats.retries.failedRetries,
484
+ averageDelay: `${serviceStats.retries.averageRetryDelayMs.toFixed(0)}ms`,
485
+ circuitBreaker: serviceStats.retries.circuitBreakerState,
486
+ });
487
+ }
488
+
489
+ console.log('Configuration:', serviceStats.configuration);
490
+ }
491
+ ```
492
+
493
+ ## Retry Configuration
494
+
495
+ ### Development Environment
496
+
497
+ ```typescript
498
+ const devClient = new GoveeClient({
499
+ apiKey: process.env.GOVEE_API_KEY!,
500
+ enableRetries: true,
501
+ retryPolicy: 'development', // More retries, shorter delays
502
+ logger: pino({ level: 'debug' }),
503
+ });
504
+ ```
505
+
506
+ ### Custom Retry Policy
507
+
508
+ ```typescript
509
+ import { GoveeClient, RetryPolicy } from '@felixgeelhaar/govee-api-client';
510
+
511
+ const customRetryPolicy = new RetryPolicy({
512
+ backoff: {
513
+ type: 'exponential',
514
+ initialDelayMs: 2000, // Start with 2 second delay
515
+ maxDelayMs: 60000, // Max 1 minute delay
516
+ multiplier: 2.0, // Double delay each retry
517
+ },
518
+ jitter: {
519
+ type: 'equal', // Add randomization
520
+ factor: 0.1, // ±10% jitter
521
+ },
522
+ condition: {
523
+ maxAttempts: 5, // Maximum 5 attempts
524
+ maxTotalTimeMs: 300000, // Give up after 5 minutes total
525
+ retryableStatusCodes: [408, 429, 502, 503, 504],
526
+ retryableErrorTypes: [RateLimitError, NetworkError],
527
+ },
528
+ circuitBreaker: {
529
+ enabled: true,
530
+ failureThreshold: 10, // Open after 10 failures
531
+ recoveryTimeoutMs: 60000, // Try again after 1 minute
532
+ halfOpenSuccessThreshold: 3, // Need 3 successes to close
533
+ },
534
+ enableMetrics: true,
535
+ });
536
+
537
+ const client = new GoveeClient({
538
+ apiKey: process.env.GOVEE_API_KEY!,
539
+ enableRetries: true,
540
+ retryPolicy: customRetryPolicy,
541
+ });
542
+ ```
543
+
544
+ ## Advanced Usage
545
+
546
+ ### Command Pattern
547
+
548
+ ```typescript
549
+ import { CommandFactory, ColorRgb, Brightness } from '@felixgeelhaar/govee-api-client';
550
+
551
+ async function useCommandPattern(deviceId: string, model: string) {
552
+ // Create commands manually
553
+ const powerOn = CommandFactory.powerOn();
554
+ const setBrightness = CommandFactory.brightness(new Brightness(75));
555
+ const setColor = CommandFactory.color(new ColorRgb(255, 0, 0));
556
+
557
+ // Send commands
558
+ await client.sendCommand(deviceId, model, powerOn);
559
+ await client.sendCommand(deviceId, model, setBrightness);
560
+ await client.sendCommand(deviceId, model, setColor);
561
+ }
562
+ ```
563
+
564
+ ### Batch Operations
565
+
566
+ ```typescript
567
+ async function batchOperations() {
568
+ const devices = await client.getControllableDevices();
569
+
570
+ // Turn on all devices with same color
571
+ const red = new ColorRgb(255, 0, 0);
572
+ const promises = devices.map(device =>
573
+ client.turnOnWithColor(device.deviceId, device.model, red)
574
+ );
575
+
576
+ try {
577
+ await Promise.all(promises);
578
+ console.log('All devices set to red');
579
+ } catch (error) {
580
+ console.error('Some operations failed:', error.message);
581
+ }
582
+ }
583
+ ```
584
+
585
+ ### Scene Management
586
+
587
+ ```typescript
588
+ import { ColorRgb, ColorTemperature, Brightness } from '@felixgeelhaar/govee-api-client';
589
+
590
+ interface Scene {
591
+ name: string;
592
+ devices: Array<{
593
+ deviceId: string;
594
+ model: string;
595
+ color?: ColorRgb;
596
+ colorTemperature?: ColorTemperature;
597
+ brightness: Brightness;
598
+ }>;
599
+ }
600
+
601
+ async function applyScene(scene: Scene) {
602
+ console.log(`Applying scene: ${scene.name}`);
603
+
604
+ const promises = scene.devices.map(async device => {
605
+ if (device.color) {
606
+ await client.turnOnWithColor(device.deviceId, device.model, device.color, device.brightness);
607
+ } else if (device.colorTemperature) {
608
+ await client.turnOnWithColorTemperature(
609
+ device.deviceId,
610
+ device.model,
611
+ device.colorTemperature,
612
+ device.brightness
613
+ );
614
+ } else {
615
+ await client.turnOnWithBrightness(device.deviceId, device.model, device.brightness);
616
+ }
617
+ });
618
+
619
+ await Promise.all(promises);
620
+ console.log(`Scene "${scene.name}" applied successfully`);
621
+ }
622
+
623
+ // Example scenes
624
+ const movieScene: Scene = {
625
+ name: 'Movie Night',
626
+ devices: [
627
+ {
628
+ deviceId: 'living-room-main',
629
+ model: 'H6159',
630
+ color: new ColorRgb(100, 0, 200), // Purple
631
+ brightness: new Brightness(20),
632
+ },
633
+ {
634
+ deviceId: 'living-room-accent',
635
+ model: 'H6003',
636
+ colorTemperature: ColorTemperature.warmWhite(),
637
+ brightness: new Brightness(10),
638
+ },
639
+ ],
640
+ };
641
+ ```
642
+
643
+ ### Environment-Specific Configuration
644
+
645
+ ```typescript
646
+ import { GoveeClient } from '@felixgeelhaar/govee-api-client';
647
+ import pino from 'pino';
648
+
649
+ function createClient() {
650
+ const isDevelopment = process.env.NODE_ENV === 'development';
651
+ const isProduction = process.env.NODE_ENV === 'production';
652
+
653
+ return new GoveeClient({
654
+ apiKey: process.env.GOVEE_API_KEY!,
655
+ timeout: isDevelopment ? 10000 : 30000,
656
+ rateLimit: isDevelopment ? 50 : 95,
657
+ logger: isProduction
658
+ ? pino({ level: 'warn' })
659
+ : pino({ level: 'debug', transport: { target: 'pino-pretty' } }),
660
+ enableRetries: isProduction,
661
+ retryPolicy: isProduction ? 'production' : 'development',
662
+ });
663
+ }
664
+ ```
665
+
666
+ ## Complete Application Example
667
+
668
+ ```typescript
669
+ import {
670
+ GoveeClient,
671
+ ColorRgb,
672
+ ColorTemperature,
673
+ Brightness,
674
+ GoveeApiError,
675
+ RateLimitError,
676
+ InvalidApiKeyError,
677
+ } from '@felixgeelhaar/govee-api-client';
678
+ import pino from 'pino';
679
+
680
+ class GoveeLightController {
681
+ private client: GoveeClient;
682
+ private logger = pino({ level: 'info' });
683
+
684
+ constructor(apiKey: string) {
685
+ this.client = new GoveeClient({
686
+ apiKey,
687
+ logger: this.logger,
688
+ enableRetries: true,
689
+ retryPolicy: 'production',
690
+ });
691
+ }
692
+
693
+ async initialize() {
694
+ try {
695
+ const devices = await this.client.getDevices();
696
+ this.logger.info(`Connected successfully. Found ${devices.length} devices.`);
697
+ return devices;
698
+ } catch (error) {
699
+ if (error instanceof InvalidApiKeyError) {
700
+ this.logger.error('Invalid API key. Please check your configuration.');
701
+ throw error;
702
+ }
703
+ throw error;
704
+ }
705
+ }
706
+
707
+ async setRoomColor(roomName: string, color: ColorRgb, brightness?: Brightness) {
708
+ try {
709
+ const device = await this.client.findDeviceByName(roomName);
710
+ if (!device) {
711
+ throw new Error(`Device not found: ${roomName}`);
712
+ }
713
+
714
+ if (!device.canControl()) {
715
+ throw new Error(`Device not controllable: ${roomName}`);
716
+ }
717
+
718
+ await this.client.turnOnWithColor(device.deviceId, device.model, color, brightness);
719
+
720
+ this.logger.info(`Set ${roomName} to ${color.toHex()}`);
721
+ } catch (error) {
722
+ if (error instanceof RateLimitError) {
723
+ this.logger.warn(`Rate limited, retrying in ${error.getRetryAfterMs()}ms`);
724
+ await new Promise(resolve => setTimeout(resolve, error.getRetryAfterMs()));
725
+ return this.setRoomColor(roomName, color, brightness);
726
+ }
727
+ throw error;
728
+ }
729
+ }
730
+
731
+ async getStats() {
732
+ return this.client.getServiceStats();
733
+ }
734
+ }
735
+
736
+ // Usage
737
+ async function main() {
738
+ const controller = new GoveeLightController(process.env.GOVEE_API_KEY!);
739
+
740
+ try {
741
+ await controller.initialize();
742
+
743
+ // Set living room to warm red
744
+ await controller.setRoomColor('Living Room', new ColorRgb(255, 100, 100), new Brightness(70));
745
+
746
+ // Monitor performance
747
+ const stats = await controller.getStats();
748
+ console.log('Performance:', stats);
749
+ } catch (error) {
750
+ console.error('Application error:', error.message);
751
+ }
752
+ }
753
+
754
+ if (require.main === module) {
755
+ main().catch(console.error);
756
+ }
757
+ ```
758
+
759
+ ## Testing Examples
760
+
761
+ ```typescript
762
+ import { describe, it, expect, beforeEach } from 'vitest';
763
+ import { GoveeClient, ColorRgb, Brightness } from '@felixgeelhaar/govee-api-client';
764
+
765
+ describe('Govee Client Integration', () => {
766
+ let client: GoveeClient;
767
+
768
+ beforeEach(() => {
769
+ client = new GoveeClient({
770
+ apiKey: process.env.GOVEE_TEST_API_KEY!,
771
+ });
772
+ });
773
+
774
+ it('should list devices successfully', async () => {
775
+ const devices = await client.getDevices();
776
+ expect(devices).toBeInstanceOf(Array);
777
+ expect(devices.length).toBeGreaterThan(0);
778
+ });
779
+
780
+ it('should control device color', async () => {
781
+ const devices = await client.getControllableDevices();
782
+ const device = devices[0];
783
+
784
+ if (device && device.supportsCommand('color')) {
785
+ const red = new ColorRgb(255, 0, 0);
786
+ await client.setColor(device.deviceId, device.model, red);
787
+
788
+ // Verify state change
789
+ const state = await client.getDeviceState(device.deviceId, device.model);
790
+ const currentColor = state.getColor();
791
+
792
+ expect(currentColor).toBeDefined();
793
+ expect(currentColor!.r).toBe(255);
794
+ expect(currentColor!.g).toBe(0);
795
+ expect(currentColor!.b).toBe(0);
796
+ }
797
+ });
798
+ });
799
+ ```