UNPKG

@felixgeelhaar/govee-api-client

Version:

Enterprise-grade TypeScript client library for the Govee Developer REST API

800 lines (650 loc) 22.5 kB
# Govee API Client - Examples 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. ## Table of Contents - [Basic Setup](#basic-setup) - [Device Discovery](#device-discovery) - [Device Control](#device-control) - [Color Management](#color-management) - [State Management](#state-management) - [Error Handling](#error-handling) - [Rate Limiting & Monitoring](#rate-limiting--monitoring) - [Retry Configuration](#retry-configuration) - [Advanced Usage](#advanced-usage) ## Basic Setup ### Simple Client Initialization ```typescript import { GoveeClient } from '@felixgeelhaar/govee-api-client'; const client = new GoveeClient({ apiKey: 'your-govee-api-key-here', }); // Test connection try { const devices = await client.getDevices(); console.log(`Successfully connected! Found ${devices.length} devices.`); } catch (error) { console.error('Failed to connect:', error.message); } ``` ### Production Configuration ```typescript import { GoveeClient } from '@felixgeelhaar/govee-api-client'; import pino from 'pino'; const client = new GoveeClient({ apiKey: process.env.GOVEE_API_KEY!, timeout: 30000, // 30 second timeout rateLimit: 90, // 90 requests per minute (conservative) logger: pino({ // Structured logging level: 'info', transport: { target: 'pino-pretty', options: { colorize: true }, }, }), enableRetries: true, // Enable retry logic retryPolicy: 'production', // Production retry settings }); ``` ## Device Discovery ### List All Devices ```typescript async function listAllDevices() { try { const devices = await client.getDevices(); console.log(`Found ${devices.length} devices:`); devices.forEach(device => { console.log(`- ${device.deviceName} (${device.model})`); console.log(` ID: ${device.deviceId}`); console.log(` Controllable: ${device.controllable}`); console.log(` Supported commands: ${device.supportedCmds.join(', ')}`); console.log(''); }); } catch (error) { console.error('Failed to list devices:', error.message); } } ``` ### Find Specific Devices ```typescript async function findDevicesByName() { // Case-insensitive search const livingRoomLight = await client.findDeviceByName('living room'); if (livingRoomLight) { console.log('Found living room light:', livingRoomLight.deviceName); } else { console.log('Living room light not found'); } } async function findDevicesByModel() { const h6159Devices = await client.findDevicesByModel('H6159'); console.log(`Found ${h6159Devices.length} H6159 devices:`); h6159Devices.forEach(device => { console.log(`- ${device.deviceName} (${device.deviceId})`); }); } ``` ### Filter Controllable Devices ```typescript async function getControllableDevices() { const controllableDevices = await client.getControllableDevices(); console.log('Controllable devices:'); controllableDevices.forEach(device => { console.log(`- ${device.deviceName}: ${device.supportedCmds.join(', ')}`); }); } ``` ## Device Control ### Basic Power Control ```typescript async function basicPowerControl(deviceId: string, model: string) { try { // Turn on await client.turnOn(deviceId, model); console.log('Device turned on'); // Wait a moment await new Promise(resolve => setTimeout(resolve, 1000)); // Turn off await client.turnOff(deviceId, model); console.log('Device turned off'); } catch (error) { console.error('Power control failed:', error.message); } } ``` ### Brightness Control ```typescript import { Brightness } from '@felixgeelhaar/govee-api-client'; async function brightnessControl(deviceId: string, model: string) { try { // Set to 25% brightness await client.setBrightness(deviceId, model, Brightness.dim()); console.log('Set to dim (25%)'); // Set to 50% brightness await client.setBrightness(deviceId, model, Brightness.medium()); console.log('Set to medium (50%)'); // Set to 75% brightness await client.setBrightness(deviceId, model, Brightness.bright()); console.log('Set to bright (75%)'); // Custom brightness await client.setBrightness(deviceId, model, new Brightness(85)); console.log('Set to custom brightness (85%)'); } catch (error) { console.error('Brightness control failed:', error.message); } } ``` ### Turn On With Settings ```typescript import { ColorRgb, ColorTemperature, Brightness } from '@felixgeelhaar/govee-api-client'; async function turnOnWithSettings(deviceId: string, model: string) { try { // Turn on with brightness await client.turnOnWithBrightness(deviceId, model, new Brightness(60)); console.log('Turned on with 60% brightness'); // Turn on with red color const red = new ColorRgb(255, 0, 0); await client.turnOnWithColor(deviceId, model, red, new Brightness(80)); console.log('Turned on with red color at 80% brightness'); // Turn on with warm white const warmWhite = ColorTemperature.warmWhite(); await client.turnOnWithColorTemperature(deviceId, model, warmWhite, new Brightness(100)); console.log('Turned on with warm white at 100% brightness'); } catch (error) { console.error('Turn on with settings failed:', error.message); } } ``` ## Color Management ### RGB Color Control ```typescript import { ColorRgb } from '@felixgeelhaar/govee-api-client'; async function rgbColorControl(deviceId: string, model: string) { try { // Primary colors await client.setColor(deviceId, model, new ColorRgb(255, 0, 0)); // Red console.log('Set to red'); await new Promise(resolve => setTimeout(resolve, 1000)); await client.setColor(deviceId, model, new ColorRgb(0, 255, 0)); // Green console.log('Set to green'); await new Promise(resolve => setTimeout(resolve, 1000)); await client.setColor(deviceId, model, new ColorRgb(0, 0, 255)); // Blue console.log('Set to blue'); // Create color from hex const purple = ColorRgb.fromHex('#800080'); await client.setColor(deviceId, model, purple); console.log('Set to purple from hex'); // Create color from object const cyan = ColorRgb.fromObject({ r: 0, g: 255, b: 255 }); await client.setColor(deviceId, model, cyan); console.log('Set to cyan from object'); } catch (error) { console.error('RGB color control failed:', error.message); } } ``` ### Color Temperature Control ```typescript import { ColorTemperature } from '@felixgeelhaar/govee-api-client'; async function colorTemperatureControl(deviceId: string, model: string) { try { // Preset temperatures await client.setColorTemperature(deviceId, model, ColorTemperature.warmWhite()); console.log('Set to warm white (2700K)'); await new Promise(resolve => setTimeout(resolve, 1000)); await client.setColorTemperature(deviceId, model, ColorTemperature.daylight()); console.log('Set to daylight (5600K)'); await new Promise(resolve => setTimeout(resolve, 1000)); await client.setColorTemperature(deviceId, model, ColorTemperature.coolWhite()); console.log('Set to cool white (6500K)'); // Custom temperature const customTemp = new ColorTemperature(4000); await client.setColorTemperature(deviceId, model, customTemp); console.log('Set to custom temperature (4000K)'); // Check if temperature is warm or cool console.log(`Is warm: ${customTemp.isWarm()}`); // true (< 4000K) console.log(`Is cool: ${customTemp.isCool()}`); // false (> 5000K) } catch (error) { console.error('Color temperature control failed:', error.message); } } ``` ### Color Utilities ```typescript import { ColorRgb } from '@felixgeelhaar/govee-api-client'; function colorUtilities() { const red = new ColorRgb(255, 0, 0); // Color information console.log('RGB values:', red.r, red.g, red.b); console.log('Hex representation:', red.toHex()); // "#ff0000" console.log('String representation:', red.toString()); // "rgb(255, 0, 0)" console.log('Object representation:', red.toObject()); // { r: 255, g: 0, b: 0 } // Color comparison const anotherRed = new ColorRgb(255, 0, 0); const blue = new ColorRgb(0, 0, 255); console.log('Colors equal:', red.equals(anotherRed)); // true console.log('Colors equal:', red.equals(blue)); // false } ``` ## State Management ### Check Device State ```typescript async function checkDeviceState(deviceId: string, model: string) { try { const state = await client.getDeviceState(deviceId, model); console.log('Device State:'); console.log(`- Online: ${state.isOnline()}`); console.log(`- Power: ${state.getPowerState()}`); console.log(`- Brightness: ${state.getBrightness()}%`); const color = state.getColor(); if (color) { console.log(`- Color: rgb(${color.r}, ${color.g}, ${color.b})`); } const temp = state.getColorTemperature(); if (temp) { console.log(`- Color Temperature: ${temp}K`); } } catch (error) { console.error('Failed to get device state:', error.message); } } ``` ### Monitor Device Status ```typescript async function monitorDeviceStatus(deviceId: string, model: string) { try { // Check if device is online const isOnline = await client.isDeviceOnline(deviceId, model); console.log(`Device online: ${isOnline}`); // Check if device is powered on const isPoweredOn = await client.isDevicePoweredOn(deviceId, model); console.log(`Device powered on: ${isPoweredOn}`); // Only control if device is online if (isOnline) { await client.turnOn(deviceId, model); console.log('Device turned on'); } else { console.log('Device is offline, cannot control'); } } catch (error) { console.error('Device monitoring failed:', error.message); } } ``` ## Error Handling ### Comprehensive Error Handling ```typescript import { GoveeApiError, InvalidApiKeyError, RateLimitError, NetworkError, ValidationError, } from '@felixgeelhaar/govee-api-client'; async function comprehensiveErrorHandling() { try { const devices = await client.getDevices(); // ... perform operations } catch (error) { if (error instanceof InvalidApiKeyError) { console.error('❌ Invalid API Key'); console.log('💡 Suggestion:', error.getRecommendation()); // Handle: Get new API key, update configuration } else if (error instanceof RateLimitError) { console.error('⏱️ Rate Limited'); console.log(`⏳ Retry after: ${error.getRetryAfterMs()}ms`); // Handle: Wait and retry, or queue for later } else if (error instanceof GoveeApiError) { console.error('🔌 API Error:', error.message); console.log('💡 Suggestion:', error.getRecommendation()); if (error.isDeviceOffline()) { console.log('📴 Device is currently offline'); // Handle: Skip this device, notify user } if (error.statusCode) { console.log(`🔢 Status Code: ${error.statusCode}`); } } else if (error instanceof NetworkError) { console.error('🌐 Network Error:', error.message); console.log(`🔄 Retryable: ${error.isRetryable()}`); if (error.isRetryable()) { // Implement retry logic console.log('Will retry this operation...'); } } else if (error instanceof ValidationError) { console.error('✅ Validation Error:', error.message); if (error.field) { console.log(`📍 Field: ${error.field}`); console.log(`💥 Value: ${error.value}`); } } else { console.error('❓ Unknown Error:', error); } } } ``` ### Retry Pattern ```typescript async function retryPattern(operation: () => Promise<void>, maxRetries = 3) { let attempt = 0; while (attempt < maxRetries) { try { await operation(); return; // Success! } catch (error) { attempt++; if (error instanceof RateLimitError && attempt < maxRetries) { const delay = error.getRetryAfterMs(); console.log(`Rate limited, waiting ${delay}ms before retry ${attempt}/${maxRetries}`); await new Promise(resolve => setTimeout(resolve, delay)); continue; } if (error instanceof NetworkError && error.isRetryable() && attempt < maxRetries) { const delay = Math.pow(2, attempt) * 1000; // Exponential backoff console.log(`Network error, waiting ${delay}ms before retry ${attempt}/${maxRetries}`); await new Promise(resolve => setTimeout(resolve, delay)); continue; } // Not retryable or max retries reached throw error; } } } ``` ## Rate Limiting & Monitoring ### Monitor Rate Limiter ```typescript function monitorRateLimiter() { const stats = client.getRateLimiterStats(); console.log('Rate Limiter Status:'); console.log(`- Current requests: ${stats.currentRequests}/${stats.maxRequests}`); console.log(`- Utilization: ${stats.utilizationPercent.toFixed(1)}%`); console.log(`- Queue size: ${stats.queueSize}`); console.log(`- Can execute immediately: ${stats.canExecuteImmediately}`); if (stats.nextAvailableSlot) { console.log(`- Next slot available: ${stats.nextAvailableSlot.toISOString()}`); } // Alert if utilization is high if (stats.utilizationPercent > 80) { console.warn('⚠️ High rate limiter utilization, consider reducing request frequency'); } } ``` ### Monitor Service Performance ```typescript function monitorServicePerformance() { const serviceStats = client.getServiceStats(); console.log('Service Statistics:'); console.log('Rate Limiter:', { utilization: `${serviceStats.rateLimiter.utilizationPercent.toFixed(1)}%`, queue: serviceStats.rateLimiter.queueSize, canExecute: serviceStats.rateLimiter.canExecuteImmediately, }); if (serviceStats.retries) { console.log('Retry Metrics:', { attempts: serviceStats.retries.totalAttempts, successful: serviceStats.retries.successfulRetries, failed: serviceStats.retries.failedRetries, averageDelay: `${serviceStats.retries.averageRetryDelayMs.toFixed(0)}ms`, circuitBreaker: serviceStats.retries.circuitBreakerState, }); } console.log('Configuration:', serviceStats.configuration); } ``` ## Retry Configuration ### Development Environment ```typescript const devClient = new GoveeClient({ apiKey: process.env.GOVEE_API_KEY!, enableRetries: true, retryPolicy: 'development', // More retries, shorter delays logger: pino({ level: 'debug' }), }); ``` ### Custom Retry Policy ```typescript import { GoveeClient, RetryPolicy } from '@felixgeelhaar/govee-api-client'; const customRetryPolicy = new RetryPolicy({ backoff: { type: 'exponential', initialDelayMs: 2000, // Start with 2 second delay maxDelayMs: 60000, // Max 1 minute delay multiplier: 2.0, // Double delay each retry }, jitter: { type: 'equal', // Add randomization factor: 0.1, // ±10% jitter }, condition: { maxAttempts: 5, // Maximum 5 attempts maxTotalTimeMs: 300000, // Give up after 5 minutes total retryableStatusCodes: [408, 429, 502, 503, 504], retryableErrorTypes: [RateLimitError, NetworkError], }, circuitBreaker: { enabled: true, failureThreshold: 10, // Open after 10 failures recoveryTimeoutMs: 60000, // Try again after 1 minute halfOpenSuccessThreshold: 3, // Need 3 successes to close }, enableMetrics: true, }); const client = new GoveeClient({ apiKey: process.env.GOVEE_API_KEY!, enableRetries: true, retryPolicy: customRetryPolicy, }); ``` ## Advanced Usage ### Command Pattern ```typescript import { CommandFactory, ColorRgb, Brightness } from '@felixgeelhaar/govee-api-client'; async function useCommandPattern(deviceId: string, model: string) { // Create commands manually const powerOn = CommandFactory.powerOn(); const setBrightness = CommandFactory.brightness(new Brightness(75)); const setColor = CommandFactory.color(new ColorRgb(255, 0, 0)); // Send commands await client.sendCommand(deviceId, model, powerOn); await client.sendCommand(deviceId, model, setBrightness); await client.sendCommand(deviceId, model, setColor); } ``` ### Batch Operations ```typescript async function batchOperations() { const devices = await client.getControllableDevices(); // Turn on all devices with same color const red = new ColorRgb(255, 0, 0); const promises = devices.map(device => client.turnOnWithColor(device.deviceId, device.model, red) ); try { await Promise.all(promises); console.log('All devices set to red'); } catch (error) { console.error('Some operations failed:', error.message); } } ``` ### Scene Management ```typescript import { ColorRgb, ColorTemperature, Brightness } from '@felixgeelhaar/govee-api-client'; interface Scene { name: string; devices: Array<{ deviceId: string; model: string; color?: ColorRgb; colorTemperature?: ColorTemperature; brightness: Brightness; }>; } async function applyScene(scene: Scene) { console.log(`Applying scene: ${scene.name}`); const promises = scene.devices.map(async device => { if (device.color) { await client.turnOnWithColor(device.deviceId, device.model, device.color, device.brightness); } else if (device.colorTemperature) { await client.turnOnWithColorTemperature( device.deviceId, device.model, device.colorTemperature, device.brightness ); } else { await client.turnOnWithBrightness(device.deviceId, device.model, device.brightness); } }); await Promise.all(promises); console.log(`Scene "${scene.name}" applied successfully`); } // Example scenes const movieScene: Scene = { name: 'Movie Night', devices: [ { deviceId: 'living-room-main', model: 'H6159', color: new ColorRgb(100, 0, 200), // Purple brightness: new Brightness(20), }, { deviceId: 'living-room-accent', model: 'H6003', colorTemperature: ColorTemperature.warmWhite(), brightness: new Brightness(10), }, ], }; ``` ### Environment-Specific Configuration ```typescript import { GoveeClient } from '@felixgeelhaar/govee-api-client'; import pino from 'pino'; function createClient() { const isDevelopment = process.env.NODE_ENV === 'development'; const isProduction = process.env.NODE_ENV === 'production'; return new GoveeClient({ apiKey: process.env.GOVEE_API_KEY!, timeout: isDevelopment ? 10000 : 30000, rateLimit: isDevelopment ? 50 : 95, logger: isProduction ? pino({ level: 'warn' }) : pino({ level: 'debug', transport: { target: 'pino-pretty' } }), enableRetries: isProduction, retryPolicy: isProduction ? 'production' : 'development', }); } ``` ## Complete Application Example ```typescript import { GoveeClient, ColorRgb, ColorTemperature, Brightness, GoveeApiError, RateLimitError, InvalidApiKeyError, } from '@felixgeelhaar/govee-api-client'; import pino from 'pino'; class GoveeLightController { private client: GoveeClient; private logger = pino({ level: 'info' }); constructor(apiKey: string) { this.client = new GoveeClient({ apiKey, logger: this.logger, enableRetries: true, retryPolicy: 'production', }); } async initialize() { try { const devices = await this.client.getDevices(); this.logger.info(`Connected successfully. Found ${devices.length} devices.`); return devices; } catch (error) { if (error instanceof InvalidApiKeyError) { this.logger.error('Invalid API key. Please check your configuration.'); throw error; } throw error; } } async setRoomColor(roomName: string, color: ColorRgb, brightness?: Brightness) { try { const device = await this.client.findDeviceByName(roomName); if (!device) { throw new Error(`Device not found: ${roomName}`); } if (!device.canControl()) { throw new Error(`Device not controllable: ${roomName}`); } await this.client.turnOnWithColor(device.deviceId, device.model, color, brightness); this.logger.info(`Set ${roomName} to ${color.toHex()}`); } catch (error) { if (error instanceof RateLimitError) { this.logger.warn(`Rate limited, retrying in ${error.getRetryAfterMs()}ms`); await new Promise(resolve => setTimeout(resolve, error.getRetryAfterMs())); return this.setRoomColor(roomName, color, brightness); } throw error; } } async getStats() { return this.client.getServiceStats(); } } // Usage async function main() { const controller = new GoveeLightController(process.env.GOVEE_API_KEY!); try { await controller.initialize(); // Set living room to warm red await controller.setRoomColor('Living Room', new ColorRgb(255, 100, 100), new Brightness(70)); // Monitor performance const stats = await controller.getStats(); console.log('Performance:', stats); } catch (error) { console.error('Application error:', error.message); } } if (require.main === module) { main().catch(console.error); } ``` ## Testing Examples ```typescript import { describe, it, expect, beforeEach } from 'vitest'; import { GoveeClient, ColorRgb, Brightness } from '@felixgeelhaar/govee-api-client'; describe('Govee Client Integration', () => { let client: GoveeClient; beforeEach(() => { client = new GoveeClient({ apiKey: process.env.GOVEE_TEST_API_KEY!, }); }); it('should list devices successfully', async () => { const devices = await client.getDevices(); expect(devices).toBeInstanceOf(Array); expect(devices.length).toBeGreaterThan(0); }); it('should control device color', async () => { const devices = await client.getControllableDevices(); const device = devices[0]; if (device && device.supportsCommand('color')) { const red = new ColorRgb(255, 0, 0); await client.setColor(device.deviceId, device.model, red); // Verify state change const state = await client.getDeviceState(device.deviceId, device.model); const currentColor = state.getColor(); expect(currentColor).toBeDefined(); expect(currentColor!.r).toBe(255); expect(currentColor!.g).toBe(0); expect(currentColor!.b).toBe(0); } }); }); ```