iobroker.govee-smart 2.6.1 → 2.6.2
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 +11 -6
- package/build/lib/command-router.js +10 -11
- package/build/lib/command-router.js.map +2 -2
- package/build/lib/device-manager.js +11 -6
- package/build/lib/device-manager.js.map +2 -2
- package/build/lib/govee-lan-client.js +2 -3
- package/build/lib/govee-lan-client.js.map +2 -2
- package/build/lib/govee-mqtt-client.js +6 -7
- package/build/lib/govee-mqtt-client.js.map +2 -2
- package/build/lib/govee-openapi-mqtt-client.js +3 -4
- package/build/lib/govee-openapi-mqtt-client.js.map +2 -2
- package/build/lib/local-snapshots.js +2 -3
- package/build/lib/local-snapshots.js.map +2 -2
- package/build/lib/sku-cache.js +3 -4
- package/build/lib/sku-cache.js.map +2 -2
- package/build/main.js +15 -17
- package/build/main.js.map +2 -2
- package/io-package.json +14 -14
- package/package.json +1 -1
- package/build/lib/i18n-logs.js +0 -617
- package/build/lib/i18n-logs.js.map +0 -7
package/README.md
CHANGED
|
@@ -124,13 +124,21 @@ This adapter's MQTT authentication and BLE-over-LAN (ptReal) protocol implementa
|
|
|
124
124
|
---
|
|
125
125
|
|
|
126
126
|
## Changelog
|
|
127
|
+
<!--
|
|
128
|
+
Placeholder for the next version (at the beginning of the line):
|
|
129
|
+
### **WORK IN PROGRESS**
|
|
130
|
+
-->
|
|
131
|
+
### 2.6.2 (2026-05-09)
|
|
132
|
+
|
|
133
|
+
- Adapter log messages are now English only, in line with the ioBroker community standard. Localized state names, descriptions and dropdown labels (11 languages) are unchanged. The user-visible segment-detection wizard text in the admin UI also remains localized.
|
|
134
|
+
|
|
127
135
|
### 2.6.1 (2026-05-06)
|
|
128
136
|
|
|
129
137
|
- Documentation: changelog entries (v2.5.x–v2.6.0) restored to English — a few bullets had been left in German. No code changes.
|
|
130
138
|
|
|
131
139
|
### 2.6.0 (2026-05-06)
|
|
132
140
|
|
|
133
|
-
- Multi-language:
|
|
141
|
+
- Multi-language: state names, descriptions and dropdown labels are now in your ioBroker system language (11 languages).
|
|
134
142
|
|
|
135
143
|
### 2.5.4 (2026-05-04)
|
|
136
144
|
|
|
@@ -141,12 +149,9 @@ This adapter's MQTT authentication and BLE-over-LAN (ptReal) protocol implementa
|
|
|
141
149
|
- Segment-detection wizard no longer spams "has no existing object" WARN for indices above the real strip length — echo packets are filtered against `segmentCount` (Issue #8).
|
|
142
150
|
- Command before cloud-init (e.g. cloud-only device right after restart) is silent now instead of warning "No channel available".
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
- Stops the `groups.*.info.membersUnreachable` WARN spam every 2 min — the group state stays present with an empty value when all members are reachable instead of being deleted/recreated.
|
|
147
|
-
- Verified H61A8 Outdoor Neon LED Strip 10m (reported by tukey42 in Issue #11).
|
|
152
|
+
Older entries are in [CHANGELOG_OLD.md](CHANGELOG_OLD.md).
|
|
148
153
|
|
|
149
|
-
|
|
154
|
+
## Support
|
|
150
155
|
|
|
151
156
|
- [Wiki](https://github.com/krobipd/ioBroker.govee-smart/wiki) — user documentation (EN / DE)
|
|
152
157
|
- [GitHub Issues](https://github.com/krobipd/ioBroker.govee-smart/issues) — bug reports, feature requests
|
|
@@ -23,7 +23,6 @@ __export(command_router_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(command_router_exports);
|
|
24
24
|
var import_types = require("./types");
|
|
25
25
|
var import_govee_lan_client = require("./govee-lan-client");
|
|
26
|
-
var import_i18n_logs = require("./i18n-logs");
|
|
27
26
|
const FORCE_COLOR_MODE_SETTLE_MS = 150;
|
|
28
27
|
class CommandRouter {
|
|
29
28
|
log;
|
|
@@ -188,7 +187,7 @@ class CommandRouter {
|
|
|
188
187
|
this.log.debug(`Command for ${device.name} dropped: Cloud client not ready yet`);
|
|
189
188
|
return;
|
|
190
189
|
}
|
|
191
|
-
this.log.warn(
|
|
190
|
+
this.log.warn(`No channel available for ${device.name} (${device.sku})`);
|
|
192
191
|
}
|
|
193
192
|
/**
|
|
194
193
|
* Send a generic capability command via Cloud API.
|
|
@@ -233,7 +232,7 @@ class CommandRouter {
|
|
|
233
232
|
return;
|
|
234
233
|
}
|
|
235
234
|
if (!parsed) {
|
|
236
|
-
this.log.warn(
|
|
235
|
+
this.log.warn(`Invalid segment command "${commandStr}" for ${device.name}`);
|
|
237
236
|
return;
|
|
238
237
|
}
|
|
239
238
|
const cap = this.findCapabilityForCommand(device, "segmentColor:0");
|
|
@@ -381,7 +380,7 @@ class CommandRouter {
|
|
|
381
380
|
case "lightScene": {
|
|
382
381
|
const idx = parseInt(String(value), 10);
|
|
383
382
|
if (isNaN(idx) || idx < 1 || idx > device.scenes.length) {
|
|
384
|
-
this.log.warn(
|
|
383
|
+
this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);
|
|
385
384
|
return value;
|
|
386
385
|
}
|
|
387
386
|
return device.scenes[idx - 1].value;
|
|
@@ -389,7 +388,7 @@ class CommandRouter {
|
|
|
389
388
|
case "diyScene": {
|
|
390
389
|
const idx = parseInt(String(value), 10);
|
|
391
390
|
if (isNaN(idx) || idx < 1 || idx > device.diyScenes.length) {
|
|
392
|
-
this.log.warn(
|
|
391
|
+
this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);
|
|
393
392
|
return value;
|
|
394
393
|
}
|
|
395
394
|
return device.diyScenes[idx - 1].value;
|
|
@@ -397,7 +396,7 @@ class CommandRouter {
|
|
|
397
396
|
case "snapshot": {
|
|
398
397
|
const idx = parseInt(String(value), 10);
|
|
399
398
|
if (isNaN(idx) || idx < 1 || idx > device.snapshots.length) {
|
|
400
|
-
this.log.warn(
|
|
399
|
+
this.log.warn(`${device.sku}: invalid snapshot index ${String(value)}`);
|
|
401
400
|
return value;
|
|
402
401
|
}
|
|
403
402
|
return device.snapshots[idx - 1].value;
|
|
@@ -406,7 +405,7 @@ class CommandRouter {
|
|
|
406
405
|
if (command.startsWith("segmentColor:")) {
|
|
407
406
|
const segIdx = parseInt(command.split(":")[1], 10);
|
|
408
407
|
if (isNaN(segIdx) || segIdx < 0) {
|
|
409
|
-
this.log.warn(
|
|
408
|
+
this.log.warn(`${device.sku}: invalid segment index in ${command}`);
|
|
410
409
|
return value;
|
|
411
410
|
}
|
|
412
411
|
const { r, g, b } = (0, import_types.hexToRgb)(value);
|
|
@@ -415,7 +414,7 @@ class CommandRouter {
|
|
|
415
414
|
if (command.startsWith("segmentBrightness:")) {
|
|
416
415
|
const segIdx = parseInt(command.split(":")[1], 10);
|
|
417
416
|
if (isNaN(segIdx) || segIdx < 0) {
|
|
418
|
-
this.log.warn(
|
|
417
|
+
this.log.warn(`${device.sku}: invalid segment index in ${command}`);
|
|
419
418
|
return value;
|
|
420
419
|
}
|
|
421
420
|
return { segment: [segIdx], brightness: value };
|
|
@@ -502,7 +501,7 @@ class CommandRouter {
|
|
|
502
501
|
case "diyScene": {
|
|
503
502
|
const diyIdx = parseInt(String(value), 10);
|
|
504
503
|
if (isNaN(diyIdx) || diyIdx < 1 || diyIdx > device.diyScenes.length) {
|
|
505
|
-
this.log.warn(
|
|
504
|
+
this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);
|
|
506
505
|
return;
|
|
507
506
|
}
|
|
508
507
|
const diyScene = device.diyScenes[diyIdx - 1];
|
|
@@ -527,7 +526,7 @@ class CommandRouter {
|
|
|
527
526
|
case "lightScene": {
|
|
528
527
|
const idx = parseInt(String(value), 10);
|
|
529
528
|
if (isNaN(idx) || idx < 1 || idx > device.scenes.length) {
|
|
530
|
-
this.log.warn(
|
|
529
|
+
this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);
|
|
531
530
|
return;
|
|
532
531
|
}
|
|
533
532
|
const scene = device.scenes[idx - 1];
|
|
@@ -573,7 +572,7 @@ class CommandRouter {
|
|
|
573
572
|
case "snapshot": {
|
|
574
573
|
const idx = parseInt(String(value), 10);
|
|
575
574
|
if (isNaN(idx) || idx < 1 || idx > device.snapshots.length) {
|
|
576
|
-
this.log.warn(
|
|
575
|
+
this.log.warn(`${device.sku}: invalid snapshot index ${String(value)}`);
|
|
577
576
|
return;
|
|
578
577
|
}
|
|
579
578
|
const cmdGroups = (_e = device.snapshotBleCmds) == null ? void 0 : _e[idx - 1];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/command-router.ts"],
|
|
4
|
-
"sourcesContent": ["import { hexToRgb, logDedup, type ErrorCategory, type GoveeDevice, type TimerAdapter } from \"./types\";\nimport type { GoveeCloudClient } from \"./govee-cloud-client\";\nimport type { GoveeLanClient } from \"./govee-lan-client\";\nimport { applySceneSpeed } from \"./govee-lan-client\";\nimport { tLog } from \"./i18n-logs\";\nimport type { RateLimiter } from \"./rate-limiter\";\n\n/**\n * Delay between switching the device into static-color mode and sending the\n * follow-up segment commands. Empirically the firmware needs ~150 ms for the\n * mode flip; shorter delays leave the device still in scene/music mode and the\n * subsequent segment writes are silently dropped.\n */\nconst FORCE_COLOR_MODE_SETTLE_MS = 150;\n\n/**\n * Command router \u2014 routes device commands through the fastest available\n * channel: LAN \u2192 Cloud.\n */\nexport class CommandRouter {\n private readonly log: ioBroker.Logger;\n private readonly timers: TimerAdapter;\n private lanClient: GoveeLanClient | null = null;\n private cloudClient: GoveeCloudClient | null = null;\n private rateLimiter: RateLimiter | null = null;\n /**\n * Letzte Fehler-Kategorie pro Cloud-Fallback-Fail \u2014 verhindert\n * log-Spam wenn das gleiche Symptom mehrfach kommt.\n */\n private lastCloudFallbackError: ErrorCategory | null = null;\n /** Dedup-Tracker f\u00FCr \u201Ekein Cloud-channel\"-Warns (M20). */\n private lastNoChannelCategory: ErrorCategory | null = null;\n\n /** Callback for batch segment state sync */\n onSegmentBatchUpdate?: (\n device: GoveeDevice,\n batch: { segments: number[]; color?: number; brightness?: number },\n ) => void;\n\n /**\n * @param log ioBroker logger\n * @param timers Adapter timer wrapper \u2014 routed through `this.setTimeout` so\n * pending color-mode delays get cleared on onUnload.\n */\n constructor(log: ioBroker.Logger, timers: TimerAdapter) {\n this.log = log;\n this.timers = timers;\n }\n\n /**\n * Register the LAN client\n *\n * @param client LAN UDP client instance\n */\n setLanClient(client: GoveeLanClient): void {\n this.lanClient = client;\n }\n\n /**\n * Register the Cloud client\n *\n * @param client Cloud API client instance\n */\n setCloudClient(client: GoveeCloudClient): void {\n this.cloudClient = client;\n }\n\n /**\n * Register the rate limiter for cloud calls\n *\n * @param limiter Rate limiter instance\n */\n setRateLimiter(limiter: RateLimiter): void {\n this.rateLimiter = limiter;\n }\n\n /**\n * Execute a function through the rate limiter if available, or directly.\n *\n * @param fn Async function to execute\n * @param priority Queue priority (0 = highest)\n */\n async executeRateLimited(fn: () => Promise<void>, priority = 0): Promise<void> {\n if (this.rateLimiter) {\n await this.rateLimiter.tryExecute(fn, priority);\n } else {\n await fn();\n }\n }\n\n /**\n * Force the device into static-color mode before sending segment_color_setting\n * ptReal packets. Without this, the device silently ignores segment-level\n * overrides while it's in Scene/Gradient/Music mode \u2014 the classic \"I set\n * segment 5 red and nothing happened\" symptom. Sends a `colorwc` command with\n * the device's last-known colorRgb (so the strip doesn't visibly change if it\n * was already in color mode), then waits 150 ms so the firmware can switch.\n *\n * As a bonus: once the device is in color mode, subsequent segment commands\n * trigger AA A5 MQTT pushes \u2014 so the adapter learns the real segmentCount\n * automatically the first time the user touches segment controls.\n *\n * @param device Target device\n */\n private async forceColorMode(device: GoveeDevice): Promise<void> {\n if (!device.lanIp || !this.lanClient) {\n return;\n }\n const current = typeof device.state.colorRgb === \"string\" ? device.state.colorRgb : null;\n const { r, g, b } = current ? hexToRgb(current) : { r: 255, g: 255, b: 255 };\n this.lanClient.setColor(device.lanIp, r, g, b);\n // Delay routed through the adapter's timer wrapper so it gets cancelled\n // if the adapter unloads mid-delay. Native setTimeout would leave a\n // pending handle that fires into a half-torn-down adapter.\n await new Promise<void>(resolve => this.timers.setTimeout(() => resolve(), FORCE_COLOR_MODE_SETTLE_MS));\n }\n\n /**\n * Send a command to a device \u2014 routes through LAN \u2192 Cloud.\n * MQTT is status-push only and never used for commands.\n *\n * @param device Target device\n * @param command Command type\n * @param value Command value\n */\n async sendCommand(device: GoveeDevice, command: string, value: unknown): Promise<void> {\n // Segment color: LAN ptReal first, Cloud fallback\n if (command.startsWith(\"segmentColor:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n return;\n }\n if (device.lanIp && this.lanClient) {\n await this.forceColorMode(device);\n const { r, g, b } = hexToRgb(value as string);\n this.lanClient.setSegmentColor(device.lanIp, r, g, b, [segIdx]);\n return;\n }\n if (device.channels.cloud && this.cloudClient) {\n await this.sendCloudCommand(device, command, value);\n return;\n }\n return;\n }\n\n // Segment batch: LAN ptReal first (multi-segment bitmask), Cloud fallback.\n // Accepts either a string in the user-batch syntax (\"0-5:#ff0000:20\") or a\n // pre-parsed object { segments, color?, brightness? } from internal callers\n // like the detection wizard (avoids a string\u2192parse round-trip).\n if (command === \"segmentBatch\") {\n const parsed = typeof value === \"string\" ? this.parseSegmentBatch(device, value) : this.coerceParsedBatch(value);\n if (parsed) {\n this.onSegmentBatchUpdate?.(device, parsed);\n }\n if (device.lanIp && this.lanClient && parsed) {\n await this.forceColorMode(device);\n if (parsed.color !== undefined) {\n const r = (parsed.color >> 16) & 0xff;\n const g = (parsed.color >> 8) & 0xff;\n const b = parsed.color & 0xff;\n this.lanClient.setSegmentColor(device.lanIp, r, g, b, parsed.segments);\n }\n if (parsed.brightness !== undefined) {\n this.lanClient.setSegmentBrightness(device.lanIp, parsed.brightness, parsed.segments);\n }\n return;\n }\n if (device.channels.cloud && this.cloudClient && parsed) {\n await this.sendSegmentBatchParsed(device, typeof value === \"string\" ? value : \"\", parsed);\n return;\n }\n return;\n }\n\n // Segment brightness: LAN ptReal first, Cloud fallback\n if (command.startsWith(\"segmentBrightness:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n return;\n }\n if (device.lanIp && this.lanClient) {\n await this.forceColorMode(device);\n this.lanClient.setSegmentBrightness(device.lanIp, value as number, [segIdx]);\n return;\n }\n if (device.channels.cloud && this.cloudClient) {\n await this.sendCloudCommand(device, command, value);\n return;\n }\n return;\n }\n\n // Priority 1: LAN\n if (device.lanIp && this.lanClient) {\n this.sendLanCommand(device, command, value);\n return;\n }\n\n // Priority 2: Cloud (rate-limited)\n if (device.channels.cloud && this.cloudClient) {\n await this.sendCloudCommand(device, command, value);\n return;\n }\n\n // Cloud-f\u00E4hig aber Client noch nicht ready (Init-Phase nach Restart):\n // typischer Race-Fall, wenn der User sofort einen Befehl schickt w\u00E4hrend\n // der Adapter Cloud-Login + IoT-Key noch erledigt. Kein WARN, nur debug \u2014\n // der Befehl ist verloren, aber der User sieht eh wenige Sekunden sp\u00E4ter\n // dass alles l\u00E4uft. WARN war hier ein false alarm.\n if (device.channels.cloud && !this.cloudClient) {\n this.log.debug(`Command for ${device.name} dropped: Cloud client not ready yet`);\n return;\n }\n\n this.log.warn(tLog(\"noChannelAvailable\", { name: device.name, sku: device.sku }));\n }\n\n /**\n * Send a generic capability command via Cloud API.\n * Used for capability types not explicitly handled (toggle, dynamic_scene, etc.)\n *\n * @param device Target device\n * @param capabilityType Full capability type (e.g. \"devices.capabilities.toggle\")\n * @param capabilityInstance Capability instance name (e.g. \"gradientToggle\")\n * @param value Command value\n */\n async sendCapabilityCommand(\n device: GoveeDevice,\n capabilityType: string,\n capabilityInstance: string,\n value: unknown,\n ): Promise<void> {\n if (!this.cloudClient || !device.channels.cloud) {\n this.log.debug(`Cloud not available for generic command on ${device.name}`);\n return;\n }\n\n const shortType = capabilityType.replace(\"devices.capabilities.\", \"\");\n let cloudValue: unknown = value;\n\n if (shortType === \"toggle\") {\n cloudValue = value ? 1 : 0;\n }\n\n const execute = async (): Promise<void> => {\n await this.cloudClient!.controlDevice(\n device.sku,\n device.deviceId,\n capabilityType,\n capabilityInstance,\n cloudValue,\n );\n };\n\n await this.executeRateLimited(execute);\n }\n\n /**\n * Send a batch segment command with pre-parsed data.\n *\n * @param device Target device\n * @param commandStr Original command string (for error messages)\n * @param parsed Pre-parsed batch data (null = invalid command)\n */\n private async sendSegmentBatchParsed(\n device: GoveeDevice,\n commandStr: string,\n parsed: { segments: number[]; color?: number; brightness?: number } | null,\n ): Promise<void> {\n if (!this.cloudClient) {\n return;\n }\n\n if (!parsed) {\n this.log.warn(tLog(\"invalidSegmentCommand\", { command: commandStr, name: device.name }));\n return;\n }\n\n const cap = this.findCapabilityForCommand(device, \"segmentColor:0\");\n if (!cap) {\n this.log.debug(`No segment capability for ${device.name}`);\n return;\n }\n\n if (parsed.color !== undefined) {\n const execute = async (): Promise<void> => {\n await this.cloudClient!.controlDevice(device.sku, device.deviceId, cap.type, cap.instance, {\n segment: parsed.segments,\n rgb: parsed.color,\n });\n };\n await this.executeRateLimited(execute);\n }\n\n if (parsed.brightness !== undefined) {\n const caps = Array.isArray(device.capabilities) ? device.capabilities : [];\n const brightCap = caps.find(\n c =>\n c &&\n typeof c.type === \"string\" &&\n typeof c.instance === \"string\" &&\n c.type.includes(\"segment_color_setting\") &&\n c.instance.toLowerCase().includes(\"brightness\"),\n );\n const execute = async (): Promise<void> => {\n await this.cloudClient!.controlDevice(\n device.sku,\n device.deviceId,\n (brightCap ?? cap).type,\n (brightCap ?? cap).instance,\n { segment: parsed.segments, brightness: parsed.brightness },\n );\n };\n await this.executeRateLimited(execute);\n }\n\n // Update individual segment states to stay in sync\n this.onSegmentBatchUpdate?.(device, parsed);\n }\n\n /**\n * Parse batch segment command string.\n *\n * @param device Target device (for segment count)\n * @param cmd Command string (e.g. \"1-5:#ff0000:20\")\n */\n parseSegmentBatch(\n device: GoveeDevice,\n cmd: string,\n ): {\n segments: number[];\n color?: number;\n brightness?: number;\n } | null {\n // Defensive guard \u2014 non-string input (e.g. from internal caller passing\n // an already-parsed object) would crash cmd.split(). Treat as no-op.\n if (typeof cmd !== \"string\") {\n return null;\n }\n const parts = cmd.split(\":\");\n if (parts.length < 1 || !parts[0]) {\n return null;\n }\n\n // Effective physical segments: honor manual override for cut strips\n const validIndices =\n device.manualMode && Array.isArray(device.manualSegments) && device.manualSegments.length > 0\n ? new Set(device.manualSegments)\n : null;\n const segCount = device.segmentCount ?? 0;\n const isValid = (i: number): boolean => (validIndices ? validIndices.has(i) : i >= 0 && i < segCount);\n\n // Parse segment indices\n const segStr = parts[0].trim();\n let segments: number[];\n\n if (segStr === \"all\") {\n // \"all\" expands to valid physical segments only (skip cut ones)\n segments = validIndices\n ? Array.from(validIndices).sort((a, b) => a - b)\n : Array.from({ length: segCount }, (_, i) => i);\n } else {\n segments = [];\n for (const part of segStr.split(\",\")) {\n const rangeMatch = /^(\\d+)-(\\d+)$/.exec(part.trim());\n if (rangeMatch) {\n const start = parseInt(rangeMatch[1], 10);\n const end = parseInt(rangeMatch[2], 10);\n for (let i = start; i <= end; i++) {\n if (isValid(i)) {\n segments.push(i);\n }\n }\n } else {\n const idx = parseInt(part.trim(), 10);\n if (!isNaN(idx) && isValid(idx)) {\n segments.push(idx);\n }\n }\n }\n }\n\n if (segments.length === 0) {\n return null;\n }\n\n // Parse color (#RRGGBB \u2192 packed int)\n let color: number | undefined;\n if (parts.length >= 2 && parts[1]) {\n const colorStr = parts[1].trim();\n if (/^#?[0-9a-fA-F]{6}$/.test(colorStr)) {\n color = parseInt(colorStr.replace(\"#\", \"\"), 16);\n }\n }\n\n // Parse brightness (0-100)\n let brightness: number | undefined;\n if (parts.length >= 3 && parts[2]) {\n const bri = parseInt(parts[2].trim(), 10);\n if (!isNaN(bri) && bri >= 0 && bri <= 100) {\n brightness = bri;\n }\n }\n\n if (color === undefined && brightness === undefined) {\n return null;\n }\n\n return { segments, color, brightness };\n }\n\n /**\n * Coerce a pre-parsed batch object (from internal callers) to the canonical\n * shape. Returns null if the input is not a valid {segments, ...} object.\n *\n * @param value Candidate object\n */\n private coerceParsedBatch(value: unknown): {\n segments: number[];\n color?: number;\n brightness?: number;\n } | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n const v = value as Record<string, unknown>;\n if (!Array.isArray(v.segments) || v.segments.length === 0) {\n return null;\n }\n const segments = v.segments.filter(n => typeof n === \"number\" && Number.isFinite(n) && n >= 0) as number[];\n if (segments.length === 0) {\n return null;\n }\n const color = typeof v.color === \"number\" && Number.isFinite(v.color) ? v.color & 0xffffff : undefined;\n const brightness =\n typeof v.brightness === \"number\" && Number.isFinite(v.brightness)\n ? Math.max(0, Math.min(100, Math.round(v.brightness)))\n : undefined;\n if (color === undefined && brightness === undefined) {\n return null;\n }\n return { segments, color, brightness };\n }\n\n /**\n * Convert adapter value to Cloud API value\n *\n * @param device Target device (for scene/snapshot lookup)\n * @param command Command type\n * @param value Adapter-side value to convert\n */\n toCloudValue(device: GoveeDevice, command: string, value: unknown): unknown {\n switch (command) {\n case \"power\":\n return value ? 1 : 0;\n case \"brightness\":\n return value;\n case \"colorRgb\": {\n const { r, g, b } = hexToRgb(value as string);\n return (r << 16) | (g << 8) | b;\n }\n case \"colorTemperature\":\n return value;\n case \"scene\":\n return value;\n case \"lightScene\": {\n // Value is the dropdown index (string) \u2014 resolve to scene activation payload\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.scenes.length) {\n this.log.warn(tLog(\"invalidSceneIndex\", { sku: device.sku, value: String(value) }));\n return value;\n }\n return device.scenes[idx - 1].value;\n }\n case \"diyScene\": {\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.diyScenes.length) {\n this.log.warn(tLog(\"invalidSceneIndex\", { sku: device.sku, value: String(value) }));\n return value;\n }\n return device.diyScenes[idx - 1].value;\n }\n case \"snapshot\": {\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.snapshots.length) {\n this.log.warn(tLog(\"invalidSnapshotIndex\", { sku: device.sku, value: String(value) }));\n return value;\n }\n return device.snapshots[idx - 1].value;\n }\n default:\n if (command.startsWith(\"segmentColor:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n this.log.warn(tLog(\"invalidSegmentIndex\", { sku: device.sku, command }));\n return value;\n }\n const { r, g, b } = hexToRgb(value as string);\n return { segment: [segIdx], rgb: (r << 16) | (g << 8) | b };\n }\n if (command.startsWith(\"segmentBrightness:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n this.log.warn(tLog(\"invalidSegmentIndex\", { sku: device.sku, command }));\n return value;\n }\n return { segment: [segIdx], brightness: value };\n }\n return value;\n }\n }\n\n /**\n * Find capability matching a command name\n *\n * @param device Target device\n * @param command Command type to find capability for\n */\n findCapabilityForCommand(device: GoveeDevice, command: string): { type: string; instance: string } | undefined {\n const caps = Array.isArray(device.capabilities) ? device.capabilities : [];\n for (const cap of caps) {\n if (!cap || typeof cap.type !== \"string\" || typeof cap.instance !== \"string\") {\n continue;\n }\n const shortType = cap.type.replace(\"devices.capabilities.\", \"\");\n if (command === \"power\" && shortType === \"on_off\") {\n return cap;\n }\n if (command === \"brightness\" && shortType === \"range\" && cap.instance.toLowerCase().includes(\"brightness\")) {\n return cap;\n }\n if (command === \"colorRgb\" && shortType === \"color_setting\" && cap.instance === \"colorRgb\") {\n return cap;\n }\n if (command === \"colorTemperature\" && shortType === \"color_setting\" && cap.instance.includes(\"colorTem\")) {\n return cap;\n }\n if (command === \"scene\" && shortType === \"mode\" && cap.instance === \"presetScene\") {\n return cap;\n }\n if (command === \"lightScene\" && shortType === \"dynamic_scene\" && cap.instance === \"lightScene\") {\n return cap;\n }\n if (command === \"diyScene\" && shortType === \"dynamic_scene\" && cap.instance === \"diyScene\") {\n return cap;\n }\n if (command === \"snapshot\" && shortType === \"dynamic_scene\" && cap.instance === \"snapshot\") {\n return cap;\n }\n if (\n command.startsWith(\"segmentColor:\") &&\n shortType === \"segment_color_setting\" &&\n !cap.instance.toLowerCase().includes(\"brightness\")\n ) {\n return cap;\n }\n if (\n command.startsWith(\"segmentBrightness:\") &&\n shortType === \"segment_color_setting\" &&\n cap.instance.toLowerCase().includes(\"brightness\")\n ) {\n return cap;\n }\n }\n return undefined;\n }\n\n /**\n * Send command via LAN UDP\n *\n * @param device Target device\n * @param command Command type\n * @param value Command value\n */\n private sendLanCommand(device: GoveeDevice, command: string, value: unknown): void {\n if (!device.lanIp || !this.lanClient) {\n return;\n }\n\n switch (command) {\n case \"power\":\n this.lanClient.setPower(device.lanIp, value as boolean);\n break;\n case \"brightness\":\n this.lanClient.setBrightness(device.lanIp, value as number);\n break;\n case \"colorRgb\": {\n const { r, g, b } = hexToRgb(value as string);\n this.lanClient.setColor(device.lanIp, r, g, b);\n break;\n }\n case \"colorTemperature\":\n this.lanClient.setColorTemperature(device.lanIp, value as number);\n break;\n case \"gradientToggle\":\n this.lanClient.setGradient(device.lanIp, value as boolean);\n break;\n case \"diyScene\": {\n // Try ptReal BLE-over-LAN if DIY scene is in library\n const diyIdx = parseInt(String(value), 10);\n if (isNaN(diyIdx) || diyIdx < 1 || diyIdx > device.diyScenes.length) {\n this.log.warn(tLog(\"invalidSceneIndex\", { sku: device.sku, value: String(value) }));\n return;\n }\n const diyScene = device.diyScenes[diyIdx - 1];\n if (diyScene) {\n const diyLib = device.diyLibrary.find(d => d.name === diyScene.name);\n if (diyLib) {\n this.log.debug(`ptReal DIY: ${diyScene.name} \u2192 code=${diyLib.diyCode}`);\n this.lanClient.setDiyScene(device.lanIp, diyLib.scenceParam ?? \"\");\n return;\n }\n }\n // No library match \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n break;\n }\n case \"lightScene\": {\n // Try ptReal BLE-over-LAN if scene is in scene library\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.scenes.length) {\n this.log.warn(tLog(\"invalidSceneIndex\", { sku: device.sku, value: String(value) }));\n return;\n }\n const scene = device.scenes[idx - 1];\n if (scene) {\n // Match by exact name first, then by base name (strip -A/-B suffix)\n const baseName = scene.name.replace(/-[A-Z]$/, \"\");\n const libEntry =\n device.sceneLibrary.find(s => s.name === scene.name) ?? device.sceneLibrary.find(s => s.name === baseName);\n if (libEntry) {\n const baseParam = libEntry.scenceParam ?? \"\";\n // Devices without segment hardware (e.g. H70B3 Curtain Lights)\n // can't parse the A3-framed multi-packet ptReal protocol that\n // `scenceParam` carries \u2014 those packets describe per-segment\n // animation data. On such devices the packets are silently\n // dropped: the scene activation never happens locally. Fall\n // straight to Cloud activation for them so the scene still\n // works. Simple scenes without scenceParam (older presets like\n // \"Valentine's Day\") still take the ptReal path \u2014 the single\n // activation packet is understood by every Govee light.\n const hasSegments = typeof device.segmentCount === \"number\" && device.segmentCount > 0;\n if (!hasSegments && baseParam.length > 0) {\n this.log.debug(\n `ptReal scene ${scene.name} skipped \u2014 ${device.sku} has no segments, falling through to Cloud`,\n );\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n return;\n }\n let param = baseParam;\n if (\n device.sceneSpeed !== undefined &&\n device.sceneSpeed > 0 &&\n libEntry.speedInfo?.supSpeed &&\n libEntry.speedInfo.config\n ) {\n param = applySceneSpeed(param, device.sceneSpeed, libEntry.speedInfo.config);\n }\n this.log.debug(`ptReal: ${scene.name} \u2192 code=${libEntry.sceneCode}`);\n this.lanClient.setScene(device.lanIp, libEntry.sceneCode, param);\n return;\n }\n }\n // Scene not in library \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n break;\n }\n case \"snapshot\": {\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.snapshots.length) {\n this.log.warn(tLog(\"invalidSnapshotIndex\", { sku: device.sku, value: String(value) }));\n return;\n }\n const cmdGroups = device.snapshotBleCmds?.[idx - 1];\n if (cmdGroups && cmdGroups.length > 0) {\n const allPackets = cmdGroups.flat();\n if (allPackets.length > 0) {\n this.log.debug(`ptReal Snapshot: ${device.snapshots[idx - 1].name} \u2192 ${allPackets.length} packets`);\n this.lanClient.sendPtReal(device.lanIp, allPackets);\n return;\n }\n }\n // No BLE data \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n break;\n }\n default:\n // LAN doesn't support this command \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n }\n }\n\n /**\n * Send command via Cloud API (rate-limited)\n *\n * @param device Target device\n * @param command Command type\n * @param value Command value\n */\n private async sendCloudCommand(device: GoveeDevice, command: string, value: unknown): Promise<void> {\n // M19 \u2014 Closure capture: lokale Variable nach Guard. Verhindert Race\n // wenn `setCloudClient(null)` zwischen Guard-Check und executeRateLimited\n // l\u00E4uft (z.B. Adapter-Stop mid-await).\n const cloudClient = this.cloudClient;\n if (!cloudClient) {\n return;\n }\n\n // Find the matching capability\n const cap = this.findCapabilityForCommand(device, command);\n if (!cap) {\n // M20 \u2014 dedup-warn statt nur debug. User klickt einen State, kein\n // Channel-Match \u2192 Fehlersuche braucht das Erstauftreten als warn.\n this.lastNoChannelCategory = logDedup(\n this.log,\n this.lastNoChannelCategory,\n `No channel for ${device.name}/${command}`,\n new Error(\"no matching capability\"),\n );\n return;\n }\n\n const cloudValue = this.toCloudValue(device, command, value);\n\n const execute = async (): Promise<void> => {\n await cloudClient.controlDevice(device.sku, device.deviceId, cap.type, cap.instance, cloudValue);\n };\n\n await this.executeRateLimited(execute);\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { hexToRgb, logDedup, type ErrorCategory, type GoveeDevice, type TimerAdapter } from \"./types\";\nimport type { GoveeCloudClient } from \"./govee-cloud-client\";\nimport type { GoveeLanClient } from \"./govee-lan-client\";\nimport { applySceneSpeed } from \"./govee-lan-client\";\nimport type { RateLimiter } from \"./rate-limiter\";\n\n/**\n * Delay between switching the device into static-color mode and sending the\n * follow-up segment commands. Empirically the firmware needs ~150 ms for the\n * mode flip; shorter delays leave the device still in scene/music mode and the\n * subsequent segment writes are silently dropped.\n */\nconst FORCE_COLOR_MODE_SETTLE_MS = 150;\n\n/**\n * Command router \u2014 routes device commands through the fastest available\n * channel: LAN \u2192 Cloud.\n */\nexport class CommandRouter {\n private readonly log: ioBroker.Logger;\n private readonly timers: TimerAdapter;\n private lanClient: GoveeLanClient | null = null;\n private cloudClient: GoveeCloudClient | null = null;\n private rateLimiter: RateLimiter | null = null;\n /**\n * Letzte Fehler-Kategorie pro Cloud-Fallback-Fail \u2014 verhindert\n * log-Spam wenn das gleiche Symptom mehrfach kommt.\n */\n private lastCloudFallbackError: ErrorCategory | null = null;\n /** Dedup-Tracker f\u00FCr \u201Ekein Cloud-channel\"-Warns (M20). */\n private lastNoChannelCategory: ErrorCategory | null = null;\n\n /** Callback for batch segment state sync */\n onSegmentBatchUpdate?: (\n device: GoveeDevice,\n batch: { segments: number[]; color?: number; brightness?: number },\n ) => void;\n\n /**\n * @param log ioBroker logger\n * @param timers Adapter timer wrapper \u2014 routed through `this.setTimeout` so\n * pending color-mode delays get cleared on onUnload.\n */\n constructor(log: ioBroker.Logger, timers: TimerAdapter) {\n this.log = log;\n this.timers = timers;\n }\n\n /**\n * Register the LAN client\n *\n * @param client LAN UDP client instance\n */\n setLanClient(client: GoveeLanClient): void {\n this.lanClient = client;\n }\n\n /**\n * Register the Cloud client\n *\n * @param client Cloud API client instance\n */\n setCloudClient(client: GoveeCloudClient): void {\n this.cloudClient = client;\n }\n\n /**\n * Register the rate limiter for cloud calls\n *\n * @param limiter Rate limiter instance\n */\n setRateLimiter(limiter: RateLimiter): void {\n this.rateLimiter = limiter;\n }\n\n /**\n * Execute a function through the rate limiter if available, or directly.\n *\n * @param fn Async function to execute\n * @param priority Queue priority (0 = highest)\n */\n async executeRateLimited(fn: () => Promise<void>, priority = 0): Promise<void> {\n if (this.rateLimiter) {\n await this.rateLimiter.tryExecute(fn, priority);\n } else {\n await fn();\n }\n }\n\n /**\n * Force the device into static-color mode before sending segment_color_setting\n * ptReal packets. Without this, the device silently ignores segment-level\n * overrides while it's in Scene/Gradient/Music mode \u2014 the classic \"I set\n * segment 5 red and nothing happened\" symptom. Sends a `colorwc` command with\n * the device's last-known colorRgb (so the strip doesn't visibly change if it\n * was already in color mode), then waits 150 ms so the firmware can switch.\n *\n * As a bonus: once the device is in color mode, subsequent segment commands\n * trigger AA A5 MQTT pushes \u2014 so the adapter learns the real segmentCount\n * automatically the first time the user touches segment controls.\n *\n * @param device Target device\n */\n private async forceColorMode(device: GoveeDevice): Promise<void> {\n if (!device.lanIp || !this.lanClient) {\n return;\n }\n const current = typeof device.state.colorRgb === \"string\" ? device.state.colorRgb : null;\n const { r, g, b } = current ? hexToRgb(current) : { r: 255, g: 255, b: 255 };\n this.lanClient.setColor(device.lanIp, r, g, b);\n // Delay routed through the adapter's timer wrapper so it gets cancelled\n // if the adapter unloads mid-delay. Native setTimeout would leave a\n // pending handle that fires into a half-torn-down adapter.\n await new Promise<void>(resolve => this.timers.setTimeout(() => resolve(), FORCE_COLOR_MODE_SETTLE_MS));\n }\n\n /**\n * Send a command to a device \u2014 routes through LAN \u2192 Cloud.\n * MQTT is status-push only and never used for commands.\n *\n * @param device Target device\n * @param command Command type\n * @param value Command value\n */\n async sendCommand(device: GoveeDevice, command: string, value: unknown): Promise<void> {\n // Segment color: LAN ptReal first, Cloud fallback\n if (command.startsWith(\"segmentColor:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n return;\n }\n if (device.lanIp && this.lanClient) {\n await this.forceColorMode(device);\n const { r, g, b } = hexToRgb(value as string);\n this.lanClient.setSegmentColor(device.lanIp, r, g, b, [segIdx]);\n return;\n }\n if (device.channels.cloud && this.cloudClient) {\n await this.sendCloudCommand(device, command, value);\n return;\n }\n return;\n }\n\n // Segment batch: LAN ptReal first (multi-segment bitmask), Cloud fallback.\n // Accepts either a string in the user-batch syntax (\"0-5:#ff0000:20\") or a\n // pre-parsed object { segments, color?, brightness? } from internal callers\n // like the detection wizard (avoids a string\u2192parse round-trip).\n if (command === \"segmentBatch\") {\n const parsed = typeof value === \"string\" ? this.parseSegmentBatch(device, value) : this.coerceParsedBatch(value);\n if (parsed) {\n this.onSegmentBatchUpdate?.(device, parsed);\n }\n if (device.lanIp && this.lanClient && parsed) {\n await this.forceColorMode(device);\n if (parsed.color !== undefined) {\n const r = (parsed.color >> 16) & 0xff;\n const g = (parsed.color >> 8) & 0xff;\n const b = parsed.color & 0xff;\n this.lanClient.setSegmentColor(device.lanIp, r, g, b, parsed.segments);\n }\n if (parsed.brightness !== undefined) {\n this.lanClient.setSegmentBrightness(device.lanIp, parsed.brightness, parsed.segments);\n }\n return;\n }\n if (device.channels.cloud && this.cloudClient && parsed) {\n await this.sendSegmentBatchParsed(device, typeof value === \"string\" ? value : \"\", parsed);\n return;\n }\n return;\n }\n\n // Segment brightness: LAN ptReal first, Cloud fallback\n if (command.startsWith(\"segmentBrightness:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n return;\n }\n if (device.lanIp && this.lanClient) {\n await this.forceColorMode(device);\n this.lanClient.setSegmentBrightness(device.lanIp, value as number, [segIdx]);\n return;\n }\n if (device.channels.cloud && this.cloudClient) {\n await this.sendCloudCommand(device, command, value);\n return;\n }\n return;\n }\n\n // Priority 1: LAN\n if (device.lanIp && this.lanClient) {\n this.sendLanCommand(device, command, value);\n return;\n }\n\n // Priority 2: Cloud (rate-limited)\n if (device.channels.cloud && this.cloudClient) {\n await this.sendCloudCommand(device, command, value);\n return;\n }\n\n // Cloud-f\u00E4hig aber Client noch nicht ready (Init-Phase nach Restart):\n // typischer Race-Fall, wenn der User sofort einen Befehl schickt w\u00E4hrend\n // der Adapter Cloud-Login + IoT-Key noch erledigt. Kein WARN, nur debug \u2014\n // der Befehl ist verloren, aber der User sieht eh wenige Sekunden sp\u00E4ter\n // dass alles l\u00E4uft. WARN war hier ein false alarm.\n if (device.channels.cloud && !this.cloudClient) {\n this.log.debug(`Command for ${device.name} dropped: Cloud client not ready yet`);\n return;\n }\n\n this.log.warn(`No channel available for ${device.name} (${device.sku})`);\n }\n\n /**\n * Send a generic capability command via Cloud API.\n * Used for capability types not explicitly handled (toggle, dynamic_scene, etc.)\n *\n * @param device Target device\n * @param capabilityType Full capability type (e.g. \"devices.capabilities.toggle\")\n * @param capabilityInstance Capability instance name (e.g. \"gradientToggle\")\n * @param value Command value\n */\n async sendCapabilityCommand(\n device: GoveeDevice,\n capabilityType: string,\n capabilityInstance: string,\n value: unknown,\n ): Promise<void> {\n if (!this.cloudClient || !device.channels.cloud) {\n this.log.debug(`Cloud not available for generic command on ${device.name}`);\n return;\n }\n\n const shortType = capabilityType.replace(\"devices.capabilities.\", \"\");\n let cloudValue: unknown = value;\n\n if (shortType === \"toggle\") {\n cloudValue = value ? 1 : 0;\n }\n\n const execute = async (): Promise<void> => {\n await this.cloudClient!.controlDevice(\n device.sku,\n device.deviceId,\n capabilityType,\n capabilityInstance,\n cloudValue,\n );\n };\n\n await this.executeRateLimited(execute);\n }\n\n /**\n * Send a batch segment command with pre-parsed data.\n *\n * @param device Target device\n * @param commandStr Original command string (for error messages)\n * @param parsed Pre-parsed batch data (null = invalid command)\n */\n private async sendSegmentBatchParsed(\n device: GoveeDevice,\n commandStr: string,\n parsed: { segments: number[]; color?: number; brightness?: number } | null,\n ): Promise<void> {\n if (!this.cloudClient) {\n return;\n }\n\n if (!parsed) {\n this.log.warn(`Invalid segment command \"${commandStr}\" for ${device.name}`);\n return;\n }\n\n const cap = this.findCapabilityForCommand(device, \"segmentColor:0\");\n if (!cap) {\n this.log.debug(`No segment capability for ${device.name}`);\n return;\n }\n\n if (parsed.color !== undefined) {\n const execute = async (): Promise<void> => {\n await this.cloudClient!.controlDevice(device.sku, device.deviceId, cap.type, cap.instance, {\n segment: parsed.segments,\n rgb: parsed.color,\n });\n };\n await this.executeRateLimited(execute);\n }\n\n if (parsed.brightness !== undefined) {\n const caps = Array.isArray(device.capabilities) ? device.capabilities : [];\n const brightCap = caps.find(\n c =>\n c &&\n typeof c.type === \"string\" &&\n typeof c.instance === \"string\" &&\n c.type.includes(\"segment_color_setting\") &&\n c.instance.toLowerCase().includes(\"brightness\"),\n );\n const execute = async (): Promise<void> => {\n await this.cloudClient!.controlDevice(\n device.sku,\n device.deviceId,\n (brightCap ?? cap).type,\n (brightCap ?? cap).instance,\n { segment: parsed.segments, brightness: parsed.brightness },\n );\n };\n await this.executeRateLimited(execute);\n }\n\n // Update individual segment states to stay in sync\n this.onSegmentBatchUpdate?.(device, parsed);\n }\n\n /**\n * Parse batch segment command string.\n *\n * @param device Target device (for segment count)\n * @param cmd Command string (e.g. \"1-5:#ff0000:20\")\n */\n parseSegmentBatch(\n device: GoveeDevice,\n cmd: string,\n ): {\n segments: number[];\n color?: number;\n brightness?: number;\n } | null {\n // Defensive guard \u2014 non-string input (e.g. from internal caller passing\n // an already-parsed object) would crash cmd.split(). Treat as no-op.\n if (typeof cmd !== \"string\") {\n return null;\n }\n const parts = cmd.split(\":\");\n if (parts.length < 1 || !parts[0]) {\n return null;\n }\n\n // Effective physical segments: honor manual override for cut strips\n const validIndices =\n device.manualMode && Array.isArray(device.manualSegments) && device.manualSegments.length > 0\n ? new Set(device.manualSegments)\n : null;\n const segCount = device.segmentCount ?? 0;\n const isValid = (i: number): boolean => (validIndices ? validIndices.has(i) : i >= 0 && i < segCount);\n\n // Parse segment indices\n const segStr = parts[0].trim();\n let segments: number[];\n\n if (segStr === \"all\") {\n // \"all\" expands to valid physical segments only (skip cut ones)\n segments = validIndices\n ? Array.from(validIndices).sort((a, b) => a - b)\n : Array.from({ length: segCount }, (_, i) => i);\n } else {\n segments = [];\n for (const part of segStr.split(\",\")) {\n const rangeMatch = /^(\\d+)-(\\d+)$/.exec(part.trim());\n if (rangeMatch) {\n const start = parseInt(rangeMatch[1], 10);\n const end = parseInt(rangeMatch[2], 10);\n for (let i = start; i <= end; i++) {\n if (isValid(i)) {\n segments.push(i);\n }\n }\n } else {\n const idx = parseInt(part.trim(), 10);\n if (!isNaN(idx) && isValid(idx)) {\n segments.push(idx);\n }\n }\n }\n }\n\n if (segments.length === 0) {\n return null;\n }\n\n // Parse color (#RRGGBB \u2192 packed int)\n let color: number | undefined;\n if (parts.length >= 2 && parts[1]) {\n const colorStr = parts[1].trim();\n if (/^#?[0-9a-fA-F]{6}$/.test(colorStr)) {\n color = parseInt(colorStr.replace(\"#\", \"\"), 16);\n }\n }\n\n // Parse brightness (0-100)\n let brightness: number | undefined;\n if (parts.length >= 3 && parts[2]) {\n const bri = parseInt(parts[2].trim(), 10);\n if (!isNaN(bri) && bri >= 0 && bri <= 100) {\n brightness = bri;\n }\n }\n\n if (color === undefined && brightness === undefined) {\n return null;\n }\n\n return { segments, color, brightness };\n }\n\n /**\n * Coerce a pre-parsed batch object (from internal callers) to the canonical\n * shape. Returns null if the input is not a valid {segments, ...} object.\n *\n * @param value Candidate object\n */\n private coerceParsedBatch(value: unknown): {\n segments: number[];\n color?: number;\n brightness?: number;\n } | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n const v = value as Record<string, unknown>;\n if (!Array.isArray(v.segments) || v.segments.length === 0) {\n return null;\n }\n const segments = v.segments.filter(n => typeof n === \"number\" && Number.isFinite(n) && n >= 0) as number[];\n if (segments.length === 0) {\n return null;\n }\n const color = typeof v.color === \"number\" && Number.isFinite(v.color) ? v.color & 0xffffff : undefined;\n const brightness =\n typeof v.brightness === \"number\" && Number.isFinite(v.brightness)\n ? Math.max(0, Math.min(100, Math.round(v.brightness)))\n : undefined;\n if (color === undefined && brightness === undefined) {\n return null;\n }\n return { segments, color, brightness };\n }\n\n /**\n * Convert adapter value to Cloud API value\n *\n * @param device Target device (for scene/snapshot lookup)\n * @param command Command type\n * @param value Adapter-side value to convert\n */\n toCloudValue(device: GoveeDevice, command: string, value: unknown): unknown {\n switch (command) {\n case \"power\":\n return value ? 1 : 0;\n case \"brightness\":\n return value;\n case \"colorRgb\": {\n const { r, g, b } = hexToRgb(value as string);\n return (r << 16) | (g << 8) | b;\n }\n case \"colorTemperature\":\n return value;\n case \"scene\":\n return value;\n case \"lightScene\": {\n // Value is the dropdown index (string) \u2014 resolve to scene activation payload\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.scenes.length) {\n this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);\n return value;\n }\n return device.scenes[idx - 1].value;\n }\n case \"diyScene\": {\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.diyScenes.length) {\n this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);\n return value;\n }\n return device.diyScenes[idx - 1].value;\n }\n case \"snapshot\": {\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.snapshots.length) {\n this.log.warn(`${device.sku}: invalid snapshot index ${String(value)}`);\n return value;\n }\n return device.snapshots[idx - 1].value;\n }\n default:\n if (command.startsWith(\"segmentColor:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n this.log.warn(`${device.sku}: invalid segment index in ${command}`);\n return value;\n }\n const { r, g, b } = hexToRgb(value as string);\n return { segment: [segIdx], rgb: (r << 16) | (g << 8) | b };\n }\n if (command.startsWith(\"segmentBrightness:\")) {\n const segIdx = parseInt(command.split(\":\")[1], 10);\n if (isNaN(segIdx) || segIdx < 0) {\n this.log.warn(`${device.sku}: invalid segment index in ${command}`);\n return value;\n }\n return { segment: [segIdx], brightness: value };\n }\n return value;\n }\n }\n\n /**\n * Find capability matching a command name\n *\n * @param device Target device\n * @param command Command type to find capability for\n */\n findCapabilityForCommand(device: GoveeDevice, command: string): { type: string; instance: string } | undefined {\n const caps = Array.isArray(device.capabilities) ? device.capabilities : [];\n for (const cap of caps) {\n if (!cap || typeof cap.type !== \"string\" || typeof cap.instance !== \"string\") {\n continue;\n }\n const shortType = cap.type.replace(\"devices.capabilities.\", \"\");\n if (command === \"power\" && shortType === \"on_off\") {\n return cap;\n }\n if (command === \"brightness\" && shortType === \"range\" && cap.instance.toLowerCase().includes(\"brightness\")) {\n return cap;\n }\n if (command === \"colorRgb\" && shortType === \"color_setting\" && cap.instance === \"colorRgb\") {\n return cap;\n }\n if (command === \"colorTemperature\" && shortType === \"color_setting\" && cap.instance.includes(\"colorTem\")) {\n return cap;\n }\n if (command === \"scene\" && shortType === \"mode\" && cap.instance === \"presetScene\") {\n return cap;\n }\n if (command === \"lightScene\" && shortType === \"dynamic_scene\" && cap.instance === \"lightScene\") {\n return cap;\n }\n if (command === \"diyScene\" && shortType === \"dynamic_scene\" && cap.instance === \"diyScene\") {\n return cap;\n }\n if (command === \"snapshot\" && shortType === \"dynamic_scene\" && cap.instance === \"snapshot\") {\n return cap;\n }\n if (\n command.startsWith(\"segmentColor:\") &&\n shortType === \"segment_color_setting\" &&\n !cap.instance.toLowerCase().includes(\"brightness\")\n ) {\n return cap;\n }\n if (\n command.startsWith(\"segmentBrightness:\") &&\n shortType === \"segment_color_setting\" &&\n cap.instance.toLowerCase().includes(\"brightness\")\n ) {\n return cap;\n }\n }\n return undefined;\n }\n\n /**\n * Send command via LAN UDP\n *\n * @param device Target device\n * @param command Command type\n * @param value Command value\n */\n private sendLanCommand(device: GoveeDevice, command: string, value: unknown): void {\n if (!device.lanIp || !this.lanClient) {\n return;\n }\n\n switch (command) {\n case \"power\":\n this.lanClient.setPower(device.lanIp, value as boolean);\n break;\n case \"brightness\":\n this.lanClient.setBrightness(device.lanIp, value as number);\n break;\n case \"colorRgb\": {\n const { r, g, b } = hexToRgb(value as string);\n this.lanClient.setColor(device.lanIp, r, g, b);\n break;\n }\n case \"colorTemperature\":\n this.lanClient.setColorTemperature(device.lanIp, value as number);\n break;\n case \"gradientToggle\":\n this.lanClient.setGradient(device.lanIp, value as boolean);\n break;\n case \"diyScene\": {\n // Try ptReal BLE-over-LAN if DIY scene is in library\n const diyIdx = parseInt(String(value), 10);\n if (isNaN(diyIdx) || diyIdx < 1 || diyIdx > device.diyScenes.length) {\n this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);\n return;\n }\n const diyScene = device.diyScenes[diyIdx - 1];\n if (diyScene) {\n const diyLib = device.diyLibrary.find(d => d.name === diyScene.name);\n if (diyLib) {\n this.log.debug(`ptReal DIY: ${diyScene.name} \u2192 code=${diyLib.diyCode}`);\n this.lanClient.setDiyScene(device.lanIp, diyLib.scenceParam ?? \"\");\n return;\n }\n }\n // No library match \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n break;\n }\n case \"lightScene\": {\n // Try ptReal BLE-over-LAN if scene is in scene library\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.scenes.length) {\n this.log.warn(`${device.sku}: invalid scene index ${String(value)}`);\n return;\n }\n const scene = device.scenes[idx - 1];\n if (scene) {\n // Match by exact name first, then by base name (strip -A/-B suffix)\n const baseName = scene.name.replace(/-[A-Z]$/, \"\");\n const libEntry =\n device.sceneLibrary.find(s => s.name === scene.name) ?? device.sceneLibrary.find(s => s.name === baseName);\n if (libEntry) {\n const baseParam = libEntry.scenceParam ?? \"\";\n // Devices without segment hardware (e.g. H70B3 Curtain Lights)\n // can't parse the A3-framed multi-packet ptReal protocol that\n // `scenceParam` carries \u2014 those packets describe per-segment\n // animation data. On such devices the packets are silently\n // dropped: the scene activation never happens locally. Fall\n // straight to Cloud activation for them so the scene still\n // works. Simple scenes without scenceParam (older presets like\n // \"Valentine's Day\") still take the ptReal path \u2014 the single\n // activation packet is understood by every Govee light.\n const hasSegments = typeof device.segmentCount === \"number\" && device.segmentCount > 0;\n if (!hasSegments && baseParam.length > 0) {\n this.log.debug(\n `ptReal scene ${scene.name} skipped \u2014 ${device.sku} has no segments, falling through to Cloud`,\n );\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n return;\n }\n let param = baseParam;\n if (\n device.sceneSpeed !== undefined &&\n device.sceneSpeed > 0 &&\n libEntry.speedInfo?.supSpeed &&\n libEntry.speedInfo.config\n ) {\n param = applySceneSpeed(param, device.sceneSpeed, libEntry.speedInfo.config);\n }\n this.log.debug(`ptReal: ${scene.name} \u2192 code=${libEntry.sceneCode}`);\n this.lanClient.setScene(device.lanIp, libEntry.sceneCode, param);\n return;\n }\n }\n // Scene not in library \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n break;\n }\n case \"snapshot\": {\n const idx = parseInt(String(value), 10);\n if (isNaN(idx) || idx < 1 || idx > device.snapshots.length) {\n this.log.warn(`${device.sku}: invalid snapshot index ${String(value)}`);\n return;\n }\n const cmdGroups = device.snapshotBleCmds?.[idx - 1];\n if (cmdGroups && cmdGroups.length > 0) {\n const allPackets = cmdGroups.flat();\n if (allPackets.length > 0) {\n this.log.debug(`ptReal Snapshot: ${device.snapshots[idx - 1].name} \u2192 ${allPackets.length} packets`);\n this.lanClient.sendPtReal(device.lanIp, allPackets);\n return;\n }\n }\n // No BLE data \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n break;\n }\n default:\n // LAN doesn't support this command \u2014 fall through to Cloud\n this.sendCloudCommand(device, command, value).catch(e => {\n this.lastCloudFallbackError = logDedup(\n this.log,\n this.lastCloudFallbackError,\n `Cloud fallback for ${device.name}/${command}`,\n e,\n );\n });\n }\n }\n\n /**\n * Send command via Cloud API (rate-limited)\n *\n * @param device Target device\n * @param command Command type\n * @param value Command value\n */\n private async sendCloudCommand(device: GoveeDevice, command: string, value: unknown): Promise<void> {\n // M19 \u2014 Closure capture: lokale Variable nach Guard. Verhindert Race\n // wenn `setCloudClient(null)` zwischen Guard-Check und executeRateLimited\n // l\u00E4uft (z.B. Adapter-Stop mid-await).\n const cloudClient = this.cloudClient;\n if (!cloudClient) {\n return;\n }\n\n // Find the matching capability\n const cap = this.findCapabilityForCommand(device, command);\n if (!cap) {\n // M20 \u2014 dedup-warn statt nur debug. User klickt einen State, kein\n // Channel-Match \u2192 Fehlersuche braucht das Erstauftreten als warn.\n this.lastNoChannelCategory = logDedup(\n this.log,\n this.lastNoChannelCategory,\n `No channel for ${device.name}/${command}`,\n new Error(\"no matching capability\"),\n );\n return;\n }\n\n const cloudValue = this.toCloudValue(device, command, value);\n\n const execute = async (): Promise<void> => {\n await cloudClient.controlDevice(device.sku, device.deviceId, cap.type, cap.instance, cloudValue);\n };\n\n await this.executeRateLimited(execute);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -30,7 +30,6 @@ var import_capability_mapper = require("./capability-mapper");
|
|
|
30
30
|
var import_command_router = require("./command-router");
|
|
31
31
|
var import_device_registry = require("./device-registry");
|
|
32
32
|
var import_diagnostics = require("./diagnostics");
|
|
33
|
-
var import_i18n_logs = require("./i18n-logs");
|
|
34
33
|
var import_types = require("./types");
|
|
35
34
|
var import_http_client = require("./http-client");
|
|
36
35
|
function parseMqttSegmentData(commands) {
|
|
@@ -298,7 +297,7 @@ class DeviceManager {
|
|
|
298
297
|
}
|
|
299
298
|
}
|
|
300
299
|
if (changed) {
|
|
301
|
-
this.log.info(
|
|
300
|
+
this.log.info(`Loaded ${cached.length} device(s) from cache`);
|
|
302
301
|
}
|
|
303
302
|
const hasLight = Array.from(this.devices.values()).some((d) => d.type === "devices.types.light");
|
|
304
303
|
if (hasLight) {
|
|
@@ -813,13 +812,17 @@ class DeviceManager {
|
|
|
813
812
|
return;
|
|
814
813
|
case "seed":
|
|
815
814
|
if ((0, import_device_registry.isSeedAndDormant)(upper)) {
|
|
816
|
-
this.log.warn(
|
|
815
|
+
this.log.warn(
|
|
816
|
+
`Device ${label} is in beta and needs the "Enable experimental device support" toggle in adapter settings to apply known per-SKU corrections.`
|
|
817
|
+
);
|
|
817
818
|
} else {
|
|
818
|
-
this.log.info(
|
|
819
|
+
this.log.info(`Device ${label} is in beta \u2014 experimental quirks are active.`);
|
|
819
820
|
}
|
|
820
821
|
return;
|
|
821
822
|
case "unknown":
|
|
822
|
-
this.log.warn(
|
|
823
|
+
this.log.warn(
|
|
824
|
+
`Device ${label} is not in the supported device list. Please trigger diag.export and post the resulting JSON in a GitHub issue so the SKU can be added.`
|
|
825
|
+
);
|
|
823
826
|
return;
|
|
824
827
|
}
|
|
825
828
|
}
|
|
@@ -872,7 +875,9 @@ class DeviceManager {
|
|
|
872
875
|
return;
|
|
873
876
|
}
|
|
874
877
|
if (maxSeen > current) {
|
|
875
|
-
this.log.info(
|
|
878
|
+
this.log.info(
|
|
879
|
+
`${device.name}: detected ${maxSeen} segments via MQTT (was ${current}) \u2014 rebuilding state tree`
|
|
880
|
+
);
|
|
876
881
|
device.segmentCount = maxSeen;
|
|
877
882
|
if (this.skuCache) {
|
|
878
883
|
this.skuCache.save(this.goveeDeviceToCached(device));
|