iobroker.govee-smart 2.18.1 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +9 -9
  2. package/admin/i18n/de.json +0 -1
  3. package/admin/i18n/en.json +0 -1
  4. package/admin/i18n/es.json +0 -1
  5. package/admin/i18n/fr.json +0 -1
  6. package/admin/i18n/it.json +0 -1
  7. package/admin/i18n/nl.json +0 -1
  8. package/admin/i18n/pl.json +0 -1
  9. package/admin/i18n/pt.json +0 -1
  10. package/admin/i18n/ru.json +0 -1
  11. package/admin/i18n/uk.json +0 -1
  12. package/admin/i18n/zh-cn.json +0 -1
  13. package/build/lib/capability-mapper.js +13 -9
  14. package/build/lib/capability-mapper.js.map +2 -2
  15. package/build/lib/command-router.js +13 -13
  16. package/build/lib/command-router.js.map +2 -2
  17. package/build/lib/device-manager/cache.js +2 -1
  18. package/build/lib/device-manager/cache.js.map +2 -2
  19. package/build/lib/device-manager.js +12 -8
  20. package/build/lib/device-manager.js.map +2 -2
  21. package/build/lib/group-fanout.js +1 -1
  22. package/build/lib/group-fanout.js.map +2 -2
  23. package/build/lib/handlers/cloud-creds-handler.js +45 -8
  24. package/build/lib/handlers/cloud-creds-handler.js.map +2 -2
  25. package/build/lib/segment-wizard.js +3 -2
  26. package/build/lib/segment-wizard.js.map +2 -2
  27. package/build/lib/sku-cache.js +5 -4
  28. package/build/lib/sku-cache.js.map +2 -2
  29. package/build/lib/snapshot-handler.js +6 -5
  30. package/build/lib/snapshot-handler.js.map +2 -2
  31. package/build/lib/state-manager.js +15 -14
  32. package/build/lib/state-manager.js.map +2 -2
  33. package/build/lib/types.js +6 -0
  34. package/build/lib/types.js.map +2 -2
  35. package/build/main.js +8 -5
  36. package/build/main.js.map +2 -2
  37. package/io-package.json +27 -49
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -94,6 +94,15 @@ This adapter's MQTT authentication and BLE-over-LAN (ptReal) protocol implementa
94
94
  Placeholder for the next version (at the beginning of the line):
95
95
  ### **WORK IN PROGRESS**
96
96
  -->
97
+ ### 2.19.0 (2026-07-04)
98
+
99
+ - Device log lines now consistently name devices as "name (model)" — cache maintenance, command errors and wizard messages included, no more bare model/address labels
100
+ - The credentials entry disappears from the object tree — login tokens stay saved in encrypted form, just no longer visible as an object
101
+
102
+ ### 2.18.2 (2026-07-03)
103
+
104
+ - Internal refactoring. No user-facing changes.
105
+
97
106
  ### 2.18.1 (2026-07-03)
98
107
 
99
108
  - Added several new Govee devices to the catalog — 4 more lamps and 3 sensors (including a CO2 monitor). They start as experimental; enable them in the adapter settings to try them.
@@ -143,15 +152,6 @@ This adapter's MQTT authentication and BLE-over-LAN (ptReal) protocol implementa
143
152
  - Fixed: temperature-only sensors no longer keep a phantom humidity datapoint stuck at 0 — a device with no humidity sensor drops it, while a real thermo-hygrometer keeps its humidity.
144
153
  - Fixed: a rejected Govee API key is now always reported as "API key rejected — check Govee API key" and stops the retry loop, instead of a generic error and retrying a bad key forever.
145
154
 
146
- ### 2.16.2 (2026-06-16) — stable
147
- - On hosts with multiple network interfaces, LAN device discovery now uses the selected interface for outgoing traffic, so it no longer misses devices by scanning on the wrong one.
148
-
149
- ### 2.16.1 (2026-06-11)
150
-
151
- - Running in compact mode no longer intercepts errors from other adapters in the same process — error reporting stays correctly attributed per adapter.
152
- - Cloud commands issued during a long rate-limit window no longer pile up without limit; stale queued calls are dropped with a single warning.
153
- - Device info entries (name, model, IP address) no longer rewrite their timestamps on every refresh — state history stays clean.
154
-
155
155
  [Older changelogs can be found there](CHANGELOG_OLD.md)
156
156
 
157
157
  ## Support
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud-API verbunden",
70
70
  "infoConnection": "Adapter verbunden",
71
71
  "infoMqttConnected": "MQTT verbunden",
72
- "infoMqttCredentials": "Gespeicherte MQTT-Anmeldedaten",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT verbunden",
74
73
  "infoWizardStatus": "Segment-Wizard Status",
75
74
  "information": "Information",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud API connected",
70
70
  "infoConnection": "Adapter connected",
71
71
  "infoMqttConnected": "MQTT connected",
72
- "infoMqttCredentials": "Persisted MQTT credentials",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT connected",
74
73
  "infoWizardStatus": "Segment-Wizard status",
75
74
  "information": "Information",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud API conectado",
70
70
  "infoConnection": "Adaptador conectado",
71
71
  "infoMqttConnected": "MQTT conectado",
72
- "infoMqttCredentials": "Credenciales MQTT guardadas",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT conectado",
74
73
  "infoWizardStatus": "Estado del asistente de segmentos",
75
74
  "information": "Información",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "API Cloud connectée",
70
70
  "infoConnection": "Adaptateur connecté",
71
71
  "infoMqttConnected": "MQTT connecté",
72
- "infoMqttCredentials": "Identifiants MQTT enregistrés",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT connecté",
74
73
  "infoWizardStatus": "État de l'assistant de segments",
75
74
  "information": "Information",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud API connesso",
70
70
  "infoConnection": "Adattatore connesso",
71
71
  "infoMqttConnected": "MQTT connesso",
72
- "infoMqttCredentials": "Credenziali MQTT salvate",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT connesso",
74
73
  "infoWizardStatus": "Stato del wizard segmenti",
75
74
  "information": "Informazioni",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud-API verbonden",
70
70
  "infoConnection": "Adapter verbonden",
71
71
  "infoMqttConnected": "MQTT verbonden",
72
- "infoMqttCredentials": "Opgeslagen MQTT-referenties",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT verbonden",
74
73
  "infoWizardStatus": "Segment-wizard status",
75
74
  "information": "Informatie",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud API połączony",
70
70
  "infoConnection": "Adapter połączony",
71
71
  "infoMqttConnected": "MQTT połączony",
72
- "infoMqttCredentials": "Zapisane dane uwierzytelniające MQTT",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT połączony",
74
73
  "infoWizardStatus": "Status kreatora segmentów",
75
74
  "information": "Informacja",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud API conectada",
70
70
  "infoConnection": "Adaptador conectado",
71
71
  "infoMqttConnected": "MQTT conectado",
72
- "infoMqttCredentials": "Credenciais MQTT persistidas",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT conectado",
74
73
  "infoWizardStatus": "Estado do assistente de segmentos",
75
74
  "information": "Informação",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud API подключён",
70
70
  "infoConnection": "Адаптер подключён",
71
71
  "infoMqttConnected": "MQTT подключён",
72
- "infoMqttCredentials": "Сохранённые учётные данные MQTT",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT подключён",
74
73
  "infoWizardStatus": "Статус мастера сегментов",
75
74
  "information": "Информация",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "Cloud API підключено",
70
70
  "infoConnection": "Адаптер підключено",
71
71
  "infoMqttConnected": "MQTT підключено",
72
- "infoMqttCredentials": "Збережені облікові дані MQTT",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT підключено",
74
73
  "infoWizardStatus": "Статус майстра сегментів",
75
74
  "information": "Інформація",
@@ -69,7 +69,6 @@
69
69
  "infoCloudConnected": "云 API 已连接",
70
70
  "infoConnection": "适配器已连接",
71
71
  "infoMqttConnected": "MQTT 已连接",
72
- "infoMqttCredentials": "已保存的 MQTT 凭据",
73
72
  "infoOpenapiMqttConnected": "Govee OpenAPI MQTT 已连接",
74
73
  "infoWizardStatus": "分段向导状态",
75
74
  "information": "信息",
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var capability_mapper_exports = {};
20
20
  __export(capability_mapper_exports, {
21
21
  LAN_STATE_IDS: () => LAN_STATE_IDS,
22
+ SENSOR_ROLE_UNIT: () => SENSOR_ROLE_UNIT,
22
23
  applyQuirksToStates: () => applyQuirksToStates,
23
24
  buildCloudStateDefs: () => buildCloudStateDefs,
24
25
  buildLanStateDefs: () => buildLanStateDefs,
@@ -312,23 +313,25 @@ function mapMode(cap) {
312
313
  }
313
314
  ];
314
315
  }
316
+ const SENSOR_ROLE_UNIT = {
317
+ temperature: { role: "value.temperature", unit: "\xB0C" },
318
+ humidity: { role: "value.humidity", unit: "%" },
319
+ battery: { role: "value.battery", unit: "%" },
320
+ co2: { role: "value.co2", unit: "ppm" }
321
+ };
315
322
  function mapProperty(cap) {
316
323
  var _a, _b;
317
324
  const instance = cap.instance.toLowerCase();
318
325
  let role = "value";
319
326
  let unit;
320
327
  if (instance.includes("temperature")) {
321
- role = "value.temperature";
322
- unit = "\xB0C";
328
+ ({ role, unit } = SENSOR_ROLE_UNIT.temperature);
323
329
  } else if (instance.includes("humidity")) {
324
- role = "value.humidity";
325
- unit = "%";
330
+ ({ role, unit } = SENSOR_ROLE_UNIT.humidity);
326
331
  } else if (instance.includes("battery")) {
327
- role = "value.battery";
328
- unit = "%";
332
+ ({ role, unit } = SENSOR_ROLE_UNIT.battery);
329
333
  } else if (instance.includes("co2") || instance.includes("carbondioxide")) {
330
- role = "value.co2";
331
- unit = "ppm";
334
+ ({ role, unit } = SENSOR_ROLE_UNIT.co2);
332
335
  }
333
336
  return [
334
337
  {
@@ -849,7 +852,7 @@ function buildCloudStateDefs(device, log, localSnapshots, memberDevices) {
849
852
  }
850
853
  }
851
854
  } catch (e) {
852
- log.debug(`${device.sku}: speed-config parse failed for scene "${entry.name}": ${(0, import_types.errMessage)(e)}`);
855
+ log.debug(`${(0, import_types.deviceLabel)(device)}: speed-config parse failed for scene "${entry.name}": ${(0, import_types.errMessage)(e)}`);
853
856
  }
854
857
  }
855
858
  return max;
@@ -995,6 +998,7 @@ function buildGroupStateDefs(members) {
995
998
  // Annotate the CommonJS export names for ESM import in node:
996
999
  0 && (module.exports = {
997
1000
  LAN_STATE_IDS,
1001
+ SENSOR_ROLE_UNIT,
998
1002
  applyQuirksToStates,
999
1003
  buildCloudStateDefs,
1000
1004
  buildLanStateDefs,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/capability-mapper.ts"],
4
- "sourcesContent": ["import {\n buildUniqueLabelMap,\n capMatchesControl,\n errMessage,\n rgbToHex,\n type CapabilityOption,\n type CloudCapability,\n type CloudStateCapability,\n type ControlKind,\n type GoveeDevice,\n} from \"./types\";\nimport { applyColorTempQuirk, getDeviceQuirks } from \"./device-registry\";\nimport { GOVEE_CAP_TYPE, GOVEE_DEVICE_TYPE } from \"./govee-constants\";\nimport { resolveLabel, tDesc, tName } from \"./i18n\";\n\n/** ioBroker state definition derived from a Govee capability */\nexport interface StateDefinition {\n /** State ID suffix (e.g. \"power\", \"brightness\", \"color_rgb\") */\n id: string;\n /**\n * Display name. Plain string for capability-derived names (e.g. from\n * `humanize(cap.instance)` of an unknown Govee capability \u2014 those aren't\n * predictable). For known states, a translation object `{en, de, ru, ...}`\n * built via `tName()` \u2014 Admin/vis/Object-Browser pick the user's language\n * automatically.\n */\n name: string | Record<string, string>;\n /**\n * Human-readable description shown in the object browser \u2014 used to clarify\n * ambiguous state names (e.g. cloud vs local snapshots) where the id alone\n * isn't enough for a user to know what the state does.\n */\n desc?: string | Record<string, string>;\n /** ioBroker value type */\n type: ioBroker.CommonType;\n /** ioBroker role */\n role: string;\n /** Whether state is writable */\n write: boolean;\n /** Unit string */\n unit?: string;\n /** Min value for numbers */\n min?: number;\n /** Max value for numbers */\n max?: number;\n /**\n * Predefined values for a select (value \u2192 label).\n *\n * **Labels MUST be plain-string** \u2014 Admin renders states-values as React\n * children and a `{en, de, \u2026}` translation object triggers React Error #31\n * \u2192 fatal \"Error in GUI\" on dropdown open (verified 2026-05-12). For\n * localized labels, resolve via {@link resolveLabel} with the adapter's\n * `system.config.language` value once.\n */\n states?: Record<string, string>;\n /** Default value for new states */\n def?: ioBroker.StateValue;\n /** Original capability type */\n capabilityType: string;\n /** Original capability instance */\n capabilityInstance: string;\n /** Target channel (control, scenes, music, snapshots). Defaults to \"control\". */\n channel?: string;\n}\n\n/**\n * Coerce arbitrary value to boolean. Accepts true/1/\"1\"/\"true\" as truthy.\n *\n * @param v Raw value from API\n */\nfunction coerceBool(v: unknown): boolean {\n return v === true || v === 1 || v === \"1\" || v === \"true\";\n}\n\n/**\n * Stringify an unknown raw API value for an ioBroker state. Objects /\n * functions go through JSON.stringify (so we don't get `[object Object]`);\n * everything else takes the primitive `String()` path. Centralised to keep\n * the no-base-to-string lint rule happy at the call sites without\n * sprinkling type assertions all over.\n *\n * @param v Raw value from API\n */\nfunction safeStringify(v: unknown): string {\n switch (typeof v) {\n case \"string\":\n return v;\n case \"number\":\n case \"bigint\":\n case \"boolean\":\n case \"symbol\":\n return v.toString();\n case \"undefined\":\n return \"undefined\";\n default:\n // object, function, null\n return JSON.stringify(v);\n }\n}\n\n/**\n * Coerce arbitrary value to finite number, or null if not parseable.\n *\n * @param v Raw value from API\n */\nfunction coerceNum(v: unknown): number | null {\n if (typeof v === \"number\" && Number.isFinite(v)) {\n return v;\n }\n if (typeof v === \"string\" && v.trim() !== \"\") {\n const n = Number(v);\n if (Number.isFinite(n)) {\n return n;\n }\n }\n return null;\n}\n\n/**\n * Maps Govee Cloud API capabilities to ioBroker state definitions.\n * Pure function \u2014 no side effects, easily testable.\n *\n * @param capabilities Device capabilities from Cloud API\n * @param log Adapter logger \u2014 per-cap skip-decisions land on debug.\n */\nexport function mapCapabilities(capabilities: CloudCapability[], log: ioBroker.Logger): StateDefinition[] {\n const states: StateDefinition[] = [];\n\n if (!Array.isArray(capabilities)) {\n return states;\n }\n\n let mapped = 0;\n let skipped = 0;\n for (const cap of capabilities) {\n const result = mapSingleCapability(cap);\n if (result) {\n states.push(...result);\n mapped++;\n } else {\n skipped++;\n // Unknown / unhandled cap shape \u2014 log so bug reports can see what\n // Govee sent that we didn't know what to do with. Includes type+\n // instance because that's enough to grep against device-registry\n // entries when adding support for a new SKU.\n log.debug(\n `Cap skipped: type=${cap?.type ?? \"?\"} instance=${cap?.instance ?? \"?\"} \u2014 no mapping (capability not handled or malformed)`,\n );\n }\n }\n log.debug(`mapCapabilities: ${mapped} mapped, ${skipped} skipped, ${states.length} state def(s) produced`);\n\n return states;\n}\n\n/**\n * Probe `capabilities` for a `devices.capabilities.dynamic_scene` entry of\n * the given instance (lightScene / diyScene / snapshot). Used to gate the\n * scene/snapshot dropdowns capability-driven instead of data-driven \u2014 a\n * device that exposes the cap should always show the dropdown, even if\n * the scene list hasn't been fetched yet.\n *\n * @param capabilities Device capabilities from Cloud API\n * @param instance The dynamic_scene instance to look up\n */\nexport function hasDynamicSceneCapability(\n capabilities: CloudCapability[],\n instance: \"lightScene\" | \"diyScene\" | \"snapshot\",\n): boolean {\n if (!Array.isArray(capabilities)) {\n return false;\n }\n return capabilities.some(\n cap =>\n typeof cap?.type === \"string\" &&\n typeof cap?.instance === \"string\" &&\n (cap.type === GOVEE_CAP_TYPE.DYNAMIC_SCENE || cap.type === \"dynamic_scene\") &&\n cap.instance === instance,\n );\n}\n\n/**\n * Single source of truth for \"this state-id belongs to LAN territory\". Used by:\n * - cleanupCloudOwnedStates \u2192 skip these ids when wiping cloud-owned states in the control channel\n * - buildCloudStateDefs \u2192 dedup capability-derived defs against LAN ownership (prevents double-create)\n * - cloud-state-loader \u2192 filter out LAN-state-ids when applying cloud values\n *\n * Adding a new LAN-default state means: extend this set AND add the entry in getDefaultLanStates.\n * The capability-tag-invariant test enforces both stay in lock-step.\n */\nexport const LAN_STATE_IDS: ReadonlySet<string> = new Set([\"power\", \"brightness\", \"color_rgb\", \"color_temperature\"]);\n\n/**\n * Default state definitions for LAN-only devices (no Cloud capabilities).\n * All LAN-capable Govee lights support: power, brightness, color, color temperature.\n *\n * State IDs MUST match LAN_STATE_IDS above. Invariant test in capability-mapper.test.ts\n * fails if these drift apart.\n */\nexport function getDefaultLanStates(): StateDefinition[] {\n return [\n {\n id: \"power\",\n name: tName(\"power\"),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: false,\n capabilityType: \"lan\",\n capabilityInstance: \"powerSwitch\",\n },\n {\n id: \"brightness\",\n name: tName(\"brightness\"),\n type: \"number\",\n role: \"level.brightness\",\n write: true,\n min: 0,\n max: 100,\n unit: \"%\",\n def: 0,\n capabilityType: \"lan\",\n capabilityInstance: \"brightness\",\n },\n {\n id: \"color_rgb\",\n name: tName(\"colorRgb\"),\n type: \"string\",\n role: \"level.color.rgb\",\n write: true,\n def: \"#000000\",\n capabilityType: \"lan\",\n capabilityInstance: \"colorRgb\",\n },\n {\n id: \"color_temperature\",\n name: tName(\"colorTemperature\"),\n type: \"number\",\n role: \"level.color.temperature\",\n write: true,\n min: 2000,\n max: 9000,\n unit: \"K\",\n def: 2000,\n capabilityType: \"lan\",\n capabilityInstance: \"colorTemperatureK\",\n },\n ];\n}\n\n/**\n * Map a single capability to state definition(s)\n *\n * @param cap Cloud capability to map\n */\nfunction mapSingleCapability(cap: CloudCapability): StateDefinition[] | null {\n if (!cap || typeof cap.type !== \"string\" || typeof cap.instance !== \"string\") {\n return null;\n }\n const shortType = cap.type.replace(\"devices.capabilities.\", \"\");\n\n switch (shortType) {\n case \"on_off\":\n return [\n {\n id: \"power\",\n name: tName(\"power\"),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: false,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"range\":\n return mapRange(cap);\n\n case \"color_setting\":\n return mapColorSetting(cap);\n\n case \"toggle\":\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: false,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"mode\":\n return mapMode(cap);\n\n case \"property\":\n return mapProperty(cap);\n\n case \"online\":\n // Handled separately \u2014 not a regular state\n return null;\n\n case \"segment_color_setting\":\n // Segments are handled specially by state-manager\n return [\n {\n id: `_segment_${sanitizeId(cap.instance)}`,\n name: humanize(cap.instance),\n type: \"string\",\n role: \"json\",\n write: true,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"dynamic_scene\":\n // lightScene / diyScene / snapshot get real dropdowns built later in\n // buildCloudStateDefs from the scenes/snapshots arrays \u2014 skip the\n // generic stub here so we don't create and immediately delete it.\n if (cap.instance === \"lightScene\" || cap.instance === \"diyScene\" || cap.instance === \"snapshot\") {\n return null;\n }\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"string\",\n role: \"json\",\n write: true,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"work_mode\":\n return mapWorkMode(cap);\n\n case \"temperature_setting\":\n return mapTemperatureSetting(cap);\n\n case \"event\":\n return mapEvent(cap);\n\n case \"music_setting\":\n return mapMusicSetting(cap);\n\n default:\n return null;\n }\n}\n\n/**\n * Map range capability (brightness, humidity, etc.)\n *\n * @param cap Cloud range capability\n */\nfunction mapRange(cap: CloudCapability): StateDefinition[] {\n const range = cap.parameters?.range;\n const isBrightness = cap.instance.toLowerCase().includes(\"brightness\");\n\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"number\",\n role: isBrightness ? \"level.brightness\" : \"level\",\n write: true,\n min: range?.min ?? 0,\n max: range?.max ?? 100,\n unit: normalizeUnit(cap.parameters?.unit),\n def: range?.min ?? 0,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n}\n\n/**\n * Map color_setting capability (RGB or color temperature)\n *\n * @param cap Cloud color setting capability\n */\nfunction mapColorSetting(cap: CloudCapability): StateDefinition[] {\n if (cap.instance === \"colorRgb\") {\n return [\n {\n id: \"color_rgb\",\n name: tName(\"colorRgb\"),\n type: \"string\",\n role: \"level.color.rgb\",\n write: true,\n def: \"#000000\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n if (cap.instance.includes(\"colorTem\")) {\n const range = cap.parameters?.range;\n return [\n {\n id: \"color_temperature\",\n name: tName(\"colorTemperature\"),\n type: \"number\",\n role: \"level.color.temperature\",\n write: true,\n min: range?.min ?? 2000,\n max: range?.max ?? 9000,\n unit: \"K\",\n def: range?.min ?? 2000,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n return [];\n}\n\n/**\n * Map mode capability (scenes with ENUM options)\n *\n * @param cap Cloud mode capability\n */\nfunction mapMode(cap: CloudCapability): StateDefinition[] {\n if (cap.instance !== \"presetScene\" || !Array.isArray(cap.parameters?.options)) {\n return [];\n }\n\n const states: Record<string, string> = {};\n for (const opt of cap.parameters.options) {\n if (!opt || typeof opt.name !== \"string\") {\n continue;\n }\n const val = safeStringify(opt.value);\n states[val] = opt.name;\n }\n\n return [\n {\n id: \"scene\",\n name: tName(\"scene\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n}\n\n/**\n * Map property capability (read-only sensors). Routes to the `sensor`\n * channel so a Heater's temperature reading sits cleanly next to other\n * sensor-style states instead of in `control`.\n *\n * @param cap Cloud property capability\n */\nfunction mapProperty(cap: CloudCapability): StateDefinition[] {\n const instance = cap.instance.toLowerCase();\n let role = \"value\";\n let unit: string | undefined;\n\n if (instance.includes(\"temperature\")) {\n role = \"value.temperature\";\n unit = \"\u00B0C\";\n } else if (instance.includes(\"humidity\")) {\n role = \"value.humidity\";\n unit = \"%\";\n } else if (instance.includes(\"battery\")) {\n role = \"value.battery\";\n unit = \"%\";\n } else if (instance.includes(\"co2\") || instance.includes(\"carbondioxide\")) {\n role = \"value.co2\";\n unit = \"ppm\";\n }\n\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"number\",\n role,\n write: false,\n unit: normalizeUnit(cap.parameters?.unit) ?? unit,\n def: 0, // avoid null in vis until the first sensor reading (consistency \u2014 B11)\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n channel: \"sensor\",\n },\n ];\n}\n\n/**\n * Map work_mode capability (STRUCT \u2014 Govee Heater/Humidifier/Fan/...).\n *\n * Two states max:\n * - `work_mode` \u2014 main mode dropdown (mixed type so users can write\n * either the numeric mode value or the label name)\n * - `mode_value` \u2014 secondary parameter (e.g. fan-speed level for the\n * \"manual\" mode); only created if the API actually exposes one\n *\n * @param cap Cloud work_mode capability\n */\nfunction mapWorkMode(cap: CloudCapability): StateDefinition[] {\n const fields = cap.parameters?.fields;\n if (!fields || fields.length === 0) {\n return [\n {\n id: \"work_mode\",\n name: tName(\"workMode\"),\n type: \"mixed\",\n role: \"level.mode.work\",\n write: true,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n const states: StateDefinition[] = [];\n const modeField = fields.find(f => f && f.fieldName === \"workMode\");\n if (modeField?.options && modeField.options.length > 0) {\n const modeStates: Record<string, string> = {};\n for (const opt of modeField.options) {\n if (opt && typeof opt.name === \"string\") {\n modeStates[safeStringify(opt.value)] = opt.name;\n }\n }\n states.push({\n id: \"work_mode\",\n name: tName(\"workMode\"),\n type: \"mixed\",\n role: \"level.mode.work\",\n write: true,\n states: modeStates,\n def: modeField.options[0] ? safeStringify(modeField.options[0].value) : \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n const valueField = fields.find(f => f && f.fieldName === \"modeValue\");\n if (valueField) {\n if (valueField.options && valueField.options.length > 0) {\n const valStates: Record<string, string> = {};\n for (const opt of valueField.options) {\n if (opt && typeof opt.name === \"string\") {\n valStates[safeStringify(opt.value)] = opt.name;\n }\n }\n states.push({\n id: \"mode_value\",\n name: tName(\"modeValue\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: valStates,\n def: valueField.options[0] ? safeStringify(valueField.options[0].value) : \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n } else if (valueField.range) {\n states.push({\n id: \"mode_value\",\n name: tName(\"modeValue\"),\n type: \"number\",\n role: \"level\",\n write: true,\n min: valueField.range.min,\n max: valueField.range.max,\n def: valueField.range.min,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n }\n\n return states;\n}\n\n/**\n * Map temperature_setting capability \u2014 Heater target-temp slider.\n * Honours the unit reported by the API (\u00B0F or \u00B0C); falls back to \u00B0F\n * because that's the more common Govee Heater default.\n *\n * @param cap Cloud temperature_setting capability\n */\nfunction mapTemperatureSetting(cap: CloudCapability): StateDefinition[] {\n const fields = cap.parameters?.fields;\n if (Array.isArray(fields) && fields.length > 0) {\n const tempField = fields.find(f => {\n if (!f || typeof f.fieldName !== \"string\") {\n return false;\n }\n if (f.fieldName === \"targetTemperature\") {\n return true;\n }\n return f.fieldName.toLowerCase().includes(\"temperature\");\n });\n if (tempField?.range) {\n const unit = normalizeUnit(cap.parameters?.unit) ?? \"\u00B0F\";\n return [\n {\n id: \"target_temperature\",\n name: tName(\"targetTemperature\"),\n type: \"number\",\n role: \"level.temperature\",\n write: true,\n min: tempField.range.min,\n max: tempField.range.max,\n unit,\n def: tempField.range.min,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n }\n\n const range = cap.parameters?.range;\n if (range) {\n const unit = normalizeUnit(cap.parameters?.unit) ?? \"\u00B0F\";\n return [\n {\n id: \"target_temperature\",\n name: tName(\"targetTemperature\"),\n type: \"number\",\n role: \"level.temperature\",\n write: true,\n min: range.min,\n max: range.max,\n unit,\n def: range.min,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n // No usable schema \u2014 expose the raw payload so the user at least sees\n // the attempt and can report it. Stays JSON to avoid pretending we\n // understand the structure.\n return [\n {\n id: \"target_temperature\",\n name: tName(\"targetTemperature\"),\n type: \"string\",\n role: \"json\",\n write: true,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n}\n\n/**\n * Map event capability (asynchronous OpenAPI-MQTT alarms \u2014 read-only).\n * Each event becomes a boolean indicator in the events/ channel\n * (lackWater, iceFull, bodyAppeared, dirtDetected, \u2026).\n *\n * @param cap Cloud event capability\n */\nfunction mapEvent(cap: CloudCapability): StateDefinition[] {\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"boolean\",\n role: \"indicator.alarm\",\n write: false,\n def: false,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n channel: \"events\",\n },\n ];\n}\n\n/**\n * Map music_setting capability to user-friendly states.\n * Parses STRUCT fields into: mode dropdown, sensitivity slider, auto-color toggle.\n *\n * @param cap Cloud music_setting capability\n */\n/**\n * Whether a music mode carries a custom RGB colour in the ptReal LAN packet\n * (`33 05 01 <mode> R G B`). Spectrum + Rolling are colour-reactive; Energic,\n * Rhythm and any other mode use the device's own auto-colours.\n *\n * Keyed on the mode NAME, not its numeric value: Govee's music-mode values are\n * SKU-specific (the fleet has 0-based and 1-based SKUs \u2014 see A1), so a\n * value-based gate would append/withhold RGB on the wrong mode for a SKU whose\n * Spectrum/Rolling isn't at value 1/2. An unknown mode name (e.g. \"Sprouting\")\n * returns false \u2014 the safe default is to send no RGB rather than push bytes a\n * mode may not expect.\n *\n * @param name Music mode name from the capability option\n */\nexport function musicModeNameUsesRgb(name: string | undefined): boolean {\n if (typeof name !== \"string\") {\n return false;\n }\n const n = name.trim().toLowerCase();\n return n === \"spectrum\" || n === \"rolling\";\n}\n\n/**\n * Ordered list of valid music-mode options from a music_setting capability.\n * Both the dropdown builder ({@link mapMusicSetting}) and the send path\n * (`sendMusicCommand` in state-change-router) resolve music modes through THIS\n * function, so the index\u2192value mapping can never drift between what the user\n * selects and what is sent to the device.\n *\n * @param cap A music_setting capability\n * @returns The `musicMode` field's options (string name only), in API order\n */\nexport function getMusicModeOptions(cap: CloudCapability): CapabilityOption[] {\n const fields = cap.parameters?.fields;\n if (!Array.isArray(fields)) {\n return [];\n }\n const modeField = fields.find(f => f && typeof f.fieldName === \"string\" && f.fieldName === \"musicMode\");\n if (!modeField?.options || !Array.isArray(modeField.options)) {\n return [];\n }\n return modeField.options.filter(o => !!o && typeof o.name === \"string\");\n}\n\nfunction mapMusicSetting(cap: CloudCapability): StateDefinition[] {\n const fields = cap.parameters?.fields;\n if (!Array.isArray(fields) || fields.length === 0) {\n // No field details from API \u2014 can't create usable states\n return [];\n }\n\n const states: StateDefinition[] = [];\n\n // Mode dropdown \u2014 only if API provides actual mode options. Index-based\n // (0 = \"---\" sentinel), consistent with every other dropdown; the device\n // value for index N is modeOptions[N-1].value, resolved at send time through\n // the SAME getMusicModeOptions() list so build and send never drift. The old\n // value-keyed scheme collided with the sentinel on 0-based SKUs, making the\n // first music mode unreachable (A1).\n const modeOptions = getMusicModeOptions(cap);\n if (modeOptions.length > 0) {\n states.push({\n id: \"music_mode\",\n name: tName(\"musicMode\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(modeOptions),\n def: \"0\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n // Sensitivity slider\n const sensField = fields.find(f => f && typeof f.fieldName === \"string\" && f.fieldName === \"sensitivity\");\n if (sensField?.range) {\n states.push({\n id: \"music_sensitivity\",\n name: tName(\"musicSensitivity\"),\n type: \"number\",\n role: \"level\",\n write: true,\n min: sensField.range.min,\n max: sensField.range.max,\n unit: \"%\",\n def: sensField.range.max,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n // Auto color toggle\n const autoColorField = fields.find(f => f && typeof f.fieldName === \"string\" && f.fieldName === \"autoColor\");\n if (autoColorField) {\n states.push({\n id: \"music_auto_color\",\n name: tName(\"musicAutoColor\"),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: true,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n // All music states belong to the music channel\n for (const s of states) {\n s.channel = \"music\";\n }\n return states;\n}\n\n/**\n * Apply device quirks to mapped state definitions.\n * Corrects wrong API data (e.g. color temperature range) for specific SKUs.\n *\n * @param sku Device model (e.g. \"H60A1\")\n * @param states State definitions to adjust\n * @param log Adapter logger \u2014 quirk-applied events land on debug.\n */\nexport function applyQuirksToStates(sku: string, states: StateDefinition[], log: ioBroker.Logger): StateDefinition[] {\n for (const state of states) {\n if (state.id === \"color_temperature\" && state.min != null && state.max != null) {\n const corrected = applyColorTempQuirk(sku, state.min, state.max);\n if (corrected.min !== state.min || corrected.max !== state.max) {\n log.debug(\n `Quirk applied for ${sku}: color_temperature range ${state.min}-${state.max}K \u2192 ${corrected.min}-${corrected.max}K`,\n );\n }\n state.min = corrected.min;\n state.max = corrected.max;\n state.def = corrected.min;\n }\n }\n return states;\n}\n\n/** Known Govee API unit strings \u2192 ioBroker units */\nconst UNIT_MAP: Record<string, string> = {\n \"unit.percent\": \"%\",\n \"unit.kelvin\": \"K\",\n \"unit.celsius\": \"\u00B0C\",\n \"unit.fahrenheit\": \"\u00B0F\",\n};\n\n/**\n * Normalize Govee API unit string to ioBroker standard\n *\n * @param unit Raw unit string from API\n */\nfunction normalizeUnit(unit?: string): string | undefined {\n if (!unit) {\n return undefined;\n }\n return UNIT_MAP[unit] ?? unit;\n}\n\n/**\n * Sanitize a string for use as ioBroker state ID\n *\n * @param str Input string to sanitize\n */\nfunction sanitizeId(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1_$2\")\n .replace(/[^a-zA-Z0-9_]/g, \"_\")\n .toLowerCase();\n}\n\n/**\n * Convert camelCase to human-readable name\n *\n * @param str camelCase input string\n */\nfunction humanize(str: string): string {\n // Order: first underscore \u2192 space, then camelCase split, then trim +\n // uppercase the first character. Previously leading-underscore IDs\n // (e.g. `_segment_color`) became ` segment color` with a leading space\n // and no capitalization (^\\w matches a space, not a word char).\n return str\n .replace(/_/g, \" \")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .trim()\n .replace(/^./, c => c.toUpperCase());\n}\n\n/** Mapped Cloud state value: state ID + converted value */\nexport interface CloudStateValue {\n /** State ID in control/ channel (e.g. \"power\", \"brightness\", \"gradient_toggle\") */\n stateId: string;\n /** Converted value ready for ioBroker setState */\n value: ioBroker.StateValue;\n}\n\n/**\n * Map a Cloud device state capability to a state ID + converted value.\n * Uses the same ID logic as mapCapabilities so IDs always match.\n *\n * @param cap Cloud state capability with current value\n */\nexport function mapCloudStateValue(cap: CloudStateCapability): CloudStateValue | null {\n if (!cap || typeof cap.type !== \"string\" || typeof cap.instance !== \"string\") {\n return null;\n }\n const shortType = cap.type.replace(\"devices.capabilities.\", \"\");\n const raw = cap.state?.value;\n if (raw === undefined || raw === null) {\n return null;\n }\n\n switch (shortType) {\n case \"on_off\":\n return { stateId: \"power\", value: coerceBool(raw) };\n\n case \"range\": {\n const n = coerceNum(raw);\n if (n === null) {\n return null;\n }\n return { stateId: sanitizeId(cap.instance), value: n };\n }\n\n case \"color_setting\":\n if (cap.instance === \"colorRgb\") {\n const num = coerceNum(raw) ?? 0;\n return {\n stateId: \"color_rgb\",\n value: rgbToHex((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff),\n };\n }\n if (cap.instance.includes(\"colorTem\")) {\n const n = coerceNum(raw);\n if (n === null) {\n return null;\n }\n return { stateId: \"color_temperature\", value: n };\n }\n return null;\n\n case \"toggle\":\n return { stateId: sanitizeId(cap.instance), value: coerceBool(raw) };\n\n case \"mode\":\n if (cap.instance === \"presetScene\") {\n return {\n stateId: \"scene\",\n value: safeStringify(raw),\n };\n }\n return null;\n\n case \"dynamic_scene\":\n // snapshot is an action-only dropdown (activate a saved snapshot) \u2014 there\n // is no persistent \"current snapshot\" status, and its real state id is\n // snapshot_cloud (not \"snapshot\"), so a cloud-state write here only\n // produced a \"has no existing object\" warning (B12). Skip it. lightScene /\n // diyScene DO have a meaningful active value and sanitizeId maps them to\n // light_scene / diy_scene, which match their real states.\n if (cap.instance === \"snapshot\") {\n return null;\n }\n return {\n stateId: sanitizeId(cap.instance),\n value: safeStringify(raw),\n };\n\n case \"work_mode\": {\n // STRUCT: { workMode: <number>, modeValue?: <number> }. Cloud\n // /device/state only returns the primary mode here \u2014 mode_value\n // (sub-parameter) follows via MQTT status push when the device\n // reports it, so we don't lose it just because it isn't in the\n // initial state response.\n if (typeof raw === \"object\" && raw !== null) {\n const struct = raw as Record<string, unknown>;\n const n = coerceNum(struct.workMode);\n if (n !== null) {\n return { stateId: \"work_mode\", value: n };\n }\n }\n const direct = coerceNum(raw);\n if (direct !== null) {\n return { stateId: \"work_mode\", value: direct };\n }\n return null;\n }\n\n case \"temperature_setting\": {\n // STRUCT: { targetTemperature: <number>, temperatureUnit?: ... }\n // \u2014 fall back to direct number for adapters that simplify.\n const direct = coerceNum(raw);\n if (direct !== null) {\n return { stateId: \"target_temperature\", value: direct };\n }\n if (typeof raw === \"object\" && raw !== null) {\n const struct = raw as Record<string, unknown>;\n const temp = struct.targetTemperature ?? struct.temperature ?? struct.temp;\n const n = coerceNum(temp);\n if (n !== null) {\n return { stateId: \"target_temperature\", value: n };\n }\n }\n return null;\n }\n\n case \"event\":\n return {\n stateId: sanitizeId(cap.instance),\n value: coerceBool(raw),\n };\n\n case \"music_setting\":\n // Extract mode value from STRUCT state\n if (typeof raw === \"object\" && raw !== null) {\n const struct = raw as Record<string, unknown>;\n const mode = coerceNum(struct.musicMode);\n return {\n stateId: \"music_mode\",\n value: mode !== null ? String(mode) : \"0\",\n };\n }\n return null;\n\n case \"property\": {\n const n = coerceNum(raw);\n if (n === null) {\n return null;\n }\n return { stateId: sanitizeId(cap.instance), value: n };\n }\n\n default:\n return null;\n }\n}\n\n/**\n * Plan the per-state writes for a list of synthesised Cloud-state\n * capabilities. Used by the App-API poll and the OpenAPI-MQTT event\n * handler (both call into `applyCloudCapabilities` on the adapter side).\n *\n * Returns the resolved `(stateId, value)` pairs for capabilities that:\n * - decode via `mapCloudStateValue` to a non-null result, AND\n * - aren't shadowed by the LAN-state set when the device is LAN-capable\n * (lights with `lanIp` shouldn't have their LAN sub-second updates\n * overwritten by a Cloud-source value).\n *\n * Pure function \u2014 no adapter state, no I/O \u2014 so the LAN-shadow logic is\n * unit-testable independent of the live state-write pipeline.\n *\n * @param caps Capabilities to consider\n * @param hasLanIp Whether the target device has a known LAN IP\n * @param lanStateIds Default-LAN state IDs that LAN delivers authoritatively\n */\nexport function planCloudCapabilityWrites(\n caps: CloudStateCapability[],\n hasLanIp: boolean,\n lanStateIds: ReadonlySet<string>,\n): CloudStateValue[] {\n const writes: CloudStateValue[] = [];\n if (!Array.isArray(caps)) {\n return writes;\n }\n for (const cap of caps) {\n const mapped = mapCloudStateValue(cap);\n if (!mapped) {\n continue;\n }\n if (hasLanIp && lanStateIds.has(mapped.stateId)) {\n continue;\n }\n writes.push(mapped);\n }\n return writes;\n}\n\n/**\n * Scene-dropdown rules \u2014 three states with identical shape (dropdown over a\n * device-content array, capabilityType `dynamic_scene`). Adding a new\n * scene-style dropdown means: append one entry here. The 8 other Cloud-derived\n * states (scene_speed, refresh_cloud, snapshot_local/save/delete, diag.*)\n * have genuinely different shapes and stay inline \u2014 not the same anti-pattern.\n */\nconst SCENE_DROPDOWN_RULES: ReadonlyArray<{\n id: string;\n cap: \"lightScene\" | \"diyScene\" | \"snapshot\";\n nameKey: Parameters<typeof tName>[0];\n descKey?: Parameters<typeof tDesc>[0];\n channel: \"scenes\" | \"snapshots\";\n source: (d: GoveeDevice) => { name: string }[];\n}> = [\n { id: \"light_scene\", cap: \"lightScene\", nameKey: \"lightScene\", channel: \"scenes\", source: d => d.scenes },\n { id: \"diy_scene\", cap: \"diyScene\", nameKey: \"diyScene\", channel: \"scenes\", source: d => d.diyScenes },\n {\n id: \"snapshot_cloud\",\n cap: \"snapshot\",\n nameKey: \"cloudSnapshot\",\n descKey: \"cloudSnapshotDesc\",\n channel: \"snapshots\",\n source: d => d.snapshots,\n },\n];\n\n/**\n * Build LAN-owned state definitions for a device. Returns the four\n * lan-default states (power/brightness/colorRgb/colorTemperature) with quirks\n * applied, or [] for devices without a LAN address (sensors, appliances,\n * groups).\n *\n * Phase architecture: belongs to the LAN phase. Called when a device becomes\n * visible via LAN discovery or is loaded with a lanIp from the cache.\n *\n * @param device Govee device\n * @param log Adapter logger \u2014 forwarded to applyQuirksToStates.\n */\nexport function buildLanStateDefs(device: GoveeDevice, log: ioBroker.Logger): StateDefinition[] {\n if (!device.lanIp) {\n return [];\n }\n const stateDefs = getDefaultLanStates();\n applyQuirksToStates(device.sku, stateDefs, log);\n return stateDefs;\n}\n\n/**\n * The three diagnostics states (export button, JSON result, trust tier).\n * Shared by buildCloudStateDefs (tier default \"unknown\") and\n * buildGroupStateDefs (tier default \"verified\") \u2014 only the tier default differs.\n *\n * @param tierDef Initial value for the diag.tier state\n */\nfunction buildDiagStateDefs(tierDef: string | null): StateDefinition[] {\n const defs: StateDefinition[] = [\n {\n id: \"export\",\n name: tName(\"exportDiagnostics\"),\n type: \"boolean\",\n role: \"button\",\n write: true,\n def: false,\n capabilityType: \"local\",\n capabilityInstance: \"diagnosticsExport\",\n channel: \"diag\",\n },\n {\n id: \"result\",\n name: tName(\"diagnosticsJson\"),\n type: \"string\",\n role: \"json\",\n write: false,\n def: \"\",\n capabilityType: \"local\",\n capabilityInstance: \"diagnosticsResult\",\n channel: \"diag\",\n },\n ];\n // The trust tier is a per-SKU catalog attribute \u2014 meaningless for a BaseGroup\n // (not a real device), which used to show a hard-coded \"verified\". Groups pass\n // null and get no tier state at all (B4).\n if (tierDef !== null) {\n defs.push({\n id: \"tier\",\n name: tName(\"deviceTier\"),\n type: \"string\",\n role: \"text\",\n write: false,\n def: tierDef,\n states: {\n verified: resolveLabel(\"deviceTierVerified\"),\n reported: resolveLabel(\"deviceTierReported\"),\n seed: resolveLabel(\"deviceTierSeed\"),\n unknown: resolveLabel(\"deviceTierUnknown\"),\n },\n capabilityType: \"local\",\n capabilityInstance: \"diagnosticsTier\",\n channel: \"diag\",\n });\n }\n return defs;\n}\n\n/**\n * Build Cloud-owned state definitions for a device \u2014 everything that needs\n * Cloud capabilities or local synthetic decoration. Excludes LAN-default IDs\n * (the LAN phase owns those). Returns intersection state for BaseGroup\n * devices.\n *\n * Phase architecture: belongs to the Cloud phase. Called when capabilities for\n * a device become available from the cache or a fresh cloud load.\n *\n * @param device Govee device\n * @param log Adapter logger \u2014 forwarded to mapCapabilities / applyQuirksToStates.\n * @param localSnapshots Optional local snapshot names\n * @param memberDevices Resolved member devices (only for BaseGroup)\n */\nexport function buildCloudStateDefs(\n device: GoveeDevice,\n log: ioBroker.Logger,\n localSnapshots?: { name: string }[],\n memberDevices?: GoveeDevice[],\n): StateDefinition[] {\n if (device.sku === \"BaseGroup\") {\n return buildGroupStateDefs(memberDevices || []);\n }\n\n // Per-SKU quirk: brokenPlatformApi \u2192 don't trust the platform-cap tree.\n // Skip capability-derived defs AND the scene/snapshot dropdowns (they're\n // gated by hasDynamicSceneCapability which reads device.capabilities, i.e.\n // the same untrusted source). LAN-phase still creates power / brightness /\n // colorRgb / colorTemperature defaults so the device stays controllable.\n const quirks = getDeviceQuirks(device.sku);\n const skipCapabilities = quirks?.brokenPlatformApi === true;\n\n // Capability-derived states. A LAN-owning light (lanIp set) lets the LAN\n // phase own the four control ids (power/brightness/colorRgb/colorTemperature)\n // \u2014 they are filtered out here so they are not double-created in the same\n // channel. Without a lanIp there is NO LAN phase to provide them and nothing\n // to double-create, so keep the cap-derived control states: a cloud-only\n // light (owner never enabled the local API) stays controllable via the Cloud,\n // and appliances (which never have a LAN phase) keep their on/off too. The\n // command-router routes these via Cloud (`no-lan` / `light-no-lan-fallback`).\n // Local-first stays, local-only does not. Sensors carry no such caps; groups\n // returned earlier. LAN lights are unchanged.\n const noLanPhase = !device.lanIp;\n const stateDefs: StateDefinition[] = skipCapabilities\n ? []\n : mapCapabilities(device.capabilities, log).filter(d => noLanPhase || !LAN_STATE_IDS.has(d.id));\n\n if (skipCapabilities) {\n log.debug(`${device.sku}: brokenPlatformApi quirk active \u2014 skipping capability-derived states + dropdowns`);\n }\n\n applyQuirksToStates(device.sku, stateDefs, log);\n\n // Light-only synthetic state defs \u2014 scenes / snapshots / music / scene_speed\n // only make sense for lights. Sensors and appliances would otherwise see\n // empty snapshot dropdowns and a useless save/delete button pair.\n const isLight = device.type === GOVEE_DEVICE_TYPE.LIGHT;\n\n // Three structurally-identical Cloud dropdowns \u2014 collapsed into one loop.\n for (const r of SCENE_DROPDOWN_RULES) {\n if (skipCapabilities || !isLight || !hasDynamicSceneCapability(device.capabilities, r.cap)) {\n continue;\n }\n stateDefs.push({\n id: r.id,\n name: tName(r.nameKey),\n desc: r.descKey ? tDesc(r.descKey) : undefined,\n // mixed lets users write the index (\"1\"), the index as number (1),\n // or the entry name (\"Aurora\") \u2014 the onStateChange handler resolves\n // all three forms via the common.states map.\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(r.source(device)),\n def: \"0\",\n capabilityType: GOVEE_CAP_TYPE.DYNAMIC_SCENE,\n capabilityInstance: r.cap,\n channel: r.channel,\n });\n }\n\n // Scene speed slider \u2014 only if any scene supports speed adjustment.\n // Stays inline: depends on a computed maxSpeedLevel that doesn't fit the\n // dropdown-rule shape.\n const maxSpeedLevel = device.sceneLibrary.reduce((max, entry) => {\n if (entry.speedInfo?.supSpeed && entry.speedInfo.config) {\n try {\n const parsed = JSON.parse(entry.speedInfo.config) as unknown;\n // Config can drift \u2014 if not an array, skip this entry silently\n if (!Array.isArray(parsed)) {\n return max;\n }\n for (const cfg of parsed as Array<{ moveIn?: number[] }>) {\n if (cfg && Array.isArray(cfg.moveIn) && cfg.moveIn.length - 1 > max) {\n max = cfg.moveIn.length - 1;\n }\n }\n } catch (e) {\n log.debug(`${device.sku}: speed-config parse failed for scene \"${entry.name}\": ${errMessage(e)}`);\n }\n }\n return max;\n }, -1);\n if (isLight && maxSpeedLevel > 0) {\n stateDefs.push({\n id: \"scene_speed\",\n name: tName(\"sceneSpeed\"),\n type: \"number\",\n role: \"level\",\n write: true,\n min: 0,\n max: maxSpeedLevel,\n def: 0,\n capabilityType: \"local\",\n capabilityInstance: \"sceneSpeed\",\n channel: \"scenes\",\n });\n }\n\n // Per-device refresh button \u2014 gated on ANY dynamic-scene capability.\n // OR-gate over three caps doesn't fit a rules-table.\n // brokenPlatformApi gate skips this too: the refresh action would post a\n // user-facing button for a Cloud endpoint that wasn't trustable to begin\n // with.\n if (\n !skipCapabilities &&\n isLight &&\n (hasDynamicSceneCapability(device.capabilities, \"lightScene\") ||\n hasDynamicSceneCapability(device.capabilities, \"diyScene\") ||\n hasDynamicSceneCapability(device.capabilities, \"snapshot\"))\n ) {\n stateDefs.push({\n id: \"refresh_cloud\",\n name: tName(\"refreshCloud\"),\n desc: tDesc(\"refreshCloudDesc\"),\n type: \"boolean\",\n role: \"button\",\n write: true,\n def: false,\n capabilityType: \"local\",\n capabilityInstance: \"refreshCloud\",\n channel: \"snapshots\",\n });\n }\n\n // Local snapshots \u2014 three states with different shapes (mixed dropdown vs\n // plain string-write fields). Inline because a rules-table would need a\n // discriminator field with no payoff.\n if (isLight) {\n stateDefs.push({\n id: \"snapshot_local\",\n name: tName(\"localSnapshot\"),\n desc: tDesc(\"localSnapshotDesc\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(localSnapshots ?? []),\n def: \"0\",\n capabilityType: \"local\",\n capabilityInstance: \"snapshotLocal\",\n channel: \"snapshots\",\n });\n stateDefs.push({\n id: \"snapshot_save\",\n name: tName(\"saveLocalSnapshot\"),\n desc: tDesc(\"saveLocalSnapshotDesc\"),\n type: \"string\",\n role: \"text\",\n write: true,\n def: \"\",\n capabilityType: \"local\",\n capabilityInstance: \"snapshotSave\",\n channel: \"snapshots\",\n });\n stateDefs.push({\n id: \"snapshot_delete\",\n name: tName(\"deleteLocalSnapshot\"),\n desc: tDesc(\"deleteLocalSnapshotDesc\"),\n type: \"string\",\n role: \"text\",\n write: true,\n def: \"\",\n capabilityType: \"local\",\n capabilityInstance: \"snapshotDelete\",\n channel: \"snapshots\",\n });\n }\n\n // Diagnostics \u2014 export button, JSON result, trust tier. Default tier\n // \"unknown\" for a real device (its actual tier comes from the registry).\n stateDefs.push(...buildDiagStateDefs(\"unknown\"));\n\n return stateDefs;\n}\n\n/** Maps the snake_case LAN control state-id (getDefaultLanStates ids) to its cloud-instance kind. */\nconst CONTROL_STATE_ID_TO_KIND: Record<string, ControlKind> = {\n power: \"power\",\n brightness: \"brightness\",\n color_rgb: \"colorRgb\",\n color_temperature: \"colorTemperature\",\n};\n\n/**\n * Check if a member device supports a given control state.\n * LAN-capable devices support all basic controls.\n *\n * @param member Group member device\n * @param stateId Control state ID (e.g. \"power\", \"brightness\")\n */\nfunction memberHasControlState(member: GoveeDevice, stateId: string): boolean {\n if (member.lanIp) {\n return true;\n }\n const kind = CONTROL_STATE_ID_TO_KIND[stateId];\n if (!kind) {\n return false;\n }\n const caps = Array.isArray(member.capabilities) ? member.capabilities : [];\n return caps.some(c => capMatchesControl(c, kind));\n}\n\n/**\n * Build state definitions for a BaseGroup device.\n * Capabilities = intersection of controllable member devices.\n * No snapshots, no segments; diag-states (export/result/tier) included since v2.9.1.\n *\n * @param members Resolved member devices\n */\nfunction buildGroupStateDefs(members: GoveeDevice[]): StateDefinition[] {\n const controllable = members.filter(m => m.lanIp || m.channels.cloud);\n if (controllable.length === 0) {\n return [];\n }\n\n const stateDefs: StateDefinition[] = [];\n\n // Control states: intersection of member capabilities\n for (const ld of getDefaultLanStates()) {\n if (controllable.every(m => memberHasControlState(m, ld.id))) {\n stateDefs.push(ld);\n }\n }\n\n // Scenes: intersection of member scene names\n if (controllable.every(m => m.scenes.length > 0)) {\n const firstNames = controllable[0].scenes.map(s => s.name);\n const commonNames = firstNames.filter(name => controllable.every(m => m.scenes.some(s => s.name === name)));\n if (commonNames.length > 0) {\n stateDefs.push({\n id: \"light_scene\",\n name: tName(\"lightScene\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(commonNames.map(name => ({ name }))),\n def: \"0\",\n capabilityType: GOVEE_CAP_TYPE.DYNAMIC_SCENE,\n capabilityInstance: \"lightScene\",\n channel: \"scenes\",\n });\n }\n }\n\n // Music: intersection of member music libraries\n if (controllable.every(m => m.musicLibrary.length > 0)) {\n const firstNames = controllable[0].musicLibrary.map(m => m.name);\n const commonNames = firstNames.filter(name => controllable.every(m => m.musicLibrary.some(ml => ml.name === name)));\n if (commonNames.length > 0) {\n stateDefs.push({\n id: \"music_mode\",\n name: tName(\"musicMode\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(commonNames.map(name => ({ name }))),\n def: \"0\",\n capabilityType: GOVEE_CAP_TYPE.MUSIC_SETTING,\n capabilityInstance: \"musicMode\",\n channel: \"music\",\n });\n }\n }\n\n // v2.9.1 \u2014 BaseGroups get the same three diag states. Tier defaults to\n // \"verified\" because BaseGroup isn't a real SKU and has no quirks entry \u2014\n // the diag-button just renders consistently.\n stateDefs.push(...buildDiagStateDefs(null));\n\n return stateDefs;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n buildUniqueLabelMap,\n capMatchesControl,\n deviceLabel,\n errMessage,\n rgbToHex,\n type CapabilityOption,\n type CloudCapability,\n type CloudStateCapability,\n type ControlKind,\n type GoveeDevice,\n} from \"./types\";\nimport { applyColorTempQuirk, getDeviceQuirks } from \"./device-registry\";\nimport { GOVEE_CAP_TYPE, GOVEE_DEVICE_TYPE } from \"./govee-constants\";\nimport { resolveLabel, tDesc, tName } from \"./i18n\";\n\n/** ioBroker state definition derived from a Govee capability */\nexport interface StateDefinition {\n /** State ID suffix (e.g. \"power\", \"brightness\", \"color_rgb\") */\n id: string;\n /**\n * Display name. Plain string for capability-derived names (e.g. from\n * `humanize(cap.instance)` of an unknown Govee capability \u2014 those aren't\n * predictable). For known states, a translation object `{en, de, ru, ...}`\n * built via `tName()` \u2014 Admin/vis/Object-Browser pick the user's language\n * automatically.\n */\n name: string | Record<string, string>;\n /**\n * Human-readable description shown in the object browser \u2014 used to clarify\n * ambiguous state names (e.g. cloud vs local snapshots) where the id alone\n * isn't enough for a user to know what the state does.\n */\n desc?: string | Record<string, string>;\n /** ioBroker value type */\n type: ioBroker.CommonType;\n /** ioBroker role */\n role: string;\n /** Whether state is writable */\n write: boolean;\n /** Unit string */\n unit?: string;\n /** Min value for numbers */\n min?: number;\n /** Max value for numbers */\n max?: number;\n /**\n * Predefined values for a select (value \u2192 label).\n *\n * **Labels MUST be plain-string** \u2014 Admin renders states-values as React\n * children and a `{en, de, \u2026}` translation object triggers React Error #31\n * \u2192 fatal \"Error in GUI\" on dropdown open (verified 2026-05-12). For\n * localized labels, resolve via {@link resolveLabel} with the adapter's\n * `system.config.language` value once.\n */\n states?: Record<string, string>;\n /** Default value for new states */\n def?: ioBroker.StateValue;\n /** Original capability type */\n capabilityType: string;\n /** Original capability instance */\n capabilityInstance: string;\n /** Target channel (control, scenes, music, snapshots). Defaults to \"control\". */\n channel?: string;\n}\n\n/**\n * Coerce arbitrary value to boolean. Accepts true/1/\"1\"/\"true\" as truthy.\n *\n * @param v Raw value from API\n */\nfunction coerceBool(v: unknown): boolean {\n return v === true || v === 1 || v === \"1\" || v === \"true\";\n}\n\n/**\n * Stringify an unknown raw API value for an ioBroker state. Objects /\n * functions go through JSON.stringify (so we don't get `[object Object]`);\n * everything else takes the primitive `String()` path. Centralised to keep\n * the no-base-to-string lint rule happy at the call sites without\n * sprinkling type assertions all over.\n *\n * @param v Raw value from API\n */\nfunction safeStringify(v: unknown): string {\n switch (typeof v) {\n case \"string\":\n return v;\n case \"number\":\n case \"bigint\":\n case \"boolean\":\n case \"symbol\":\n return v.toString();\n case \"undefined\":\n return \"undefined\";\n default:\n // object, function, null\n return JSON.stringify(v);\n }\n}\n\n/**\n * Coerce arbitrary value to finite number, or null if not parseable.\n *\n * @param v Raw value from API\n */\nfunction coerceNum(v: unknown): number | null {\n if (typeof v === \"number\" && Number.isFinite(v)) {\n return v;\n }\n if (typeof v === \"string\" && v.trim() !== \"\") {\n const n = Number(v);\n if (Number.isFinite(n)) {\n return n;\n }\n }\n return null;\n}\n\n/**\n * Maps Govee Cloud API capabilities to ioBroker state definitions.\n * Pure function \u2014 no side effects, easily testable.\n *\n * @param capabilities Device capabilities from Cloud API\n * @param log Adapter logger \u2014 per-cap skip-decisions land on debug.\n */\nexport function mapCapabilities(capabilities: CloudCapability[], log: ioBroker.Logger): StateDefinition[] {\n const states: StateDefinition[] = [];\n\n if (!Array.isArray(capabilities)) {\n return states;\n }\n\n let mapped = 0;\n let skipped = 0;\n for (const cap of capabilities) {\n const result = mapSingleCapability(cap);\n if (result) {\n states.push(...result);\n mapped++;\n } else {\n skipped++;\n // Unknown / unhandled cap shape \u2014 log so bug reports can see what\n // Govee sent that we didn't know what to do with. Includes type+\n // instance because that's enough to grep against device-registry\n // entries when adding support for a new SKU.\n log.debug(\n `Cap skipped: type=${cap?.type ?? \"?\"} instance=${cap?.instance ?? \"?\"} \u2014 no mapping (capability not handled or malformed)`,\n );\n }\n }\n log.debug(`mapCapabilities: ${mapped} mapped, ${skipped} skipped, ${states.length} state def(s) produced`);\n\n return states;\n}\n\n/**\n * Probe `capabilities` for a `devices.capabilities.dynamic_scene` entry of\n * the given instance (lightScene / diyScene / snapshot). Used to gate the\n * scene/snapshot dropdowns capability-driven instead of data-driven \u2014 a\n * device that exposes the cap should always show the dropdown, even if\n * the scene list hasn't been fetched yet.\n *\n * @param capabilities Device capabilities from Cloud API\n * @param instance The dynamic_scene instance to look up\n */\nexport function hasDynamicSceneCapability(\n capabilities: CloudCapability[],\n instance: \"lightScene\" | \"diyScene\" | \"snapshot\",\n): boolean {\n if (!Array.isArray(capabilities)) {\n return false;\n }\n return capabilities.some(\n cap =>\n typeof cap?.type === \"string\" &&\n typeof cap?.instance === \"string\" &&\n (cap.type === GOVEE_CAP_TYPE.DYNAMIC_SCENE || cap.type === \"dynamic_scene\") &&\n cap.instance === instance,\n );\n}\n\n/**\n * Single source of truth for \"this state-id belongs to LAN territory\". Used by:\n * - cleanupCloudOwnedStates \u2192 skip these ids when wiping cloud-owned states in the control channel\n * - buildCloudStateDefs \u2192 dedup capability-derived defs against LAN ownership (prevents double-create)\n * - cloud-state-loader \u2192 filter out LAN-state-ids when applying cloud values\n *\n * Adding a new LAN-default state means: extend this set AND add the entry in getDefaultLanStates.\n * The capability-tag-invariant test enforces both stay in lock-step.\n */\nexport const LAN_STATE_IDS: ReadonlySet<string> = new Set([\"power\", \"brightness\", \"color_rgb\", \"color_temperature\"]);\n\n/**\n * Default state definitions for LAN-only devices (no Cloud capabilities).\n * All LAN-capable Govee lights support: power, brightness, color, color temperature.\n *\n * State IDs MUST match LAN_STATE_IDS above. Invariant test in capability-mapper.test.ts\n * fails if these drift apart.\n */\nexport function getDefaultLanStates(): StateDefinition[] {\n return [\n {\n id: \"power\",\n name: tName(\"power\"),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: false,\n capabilityType: \"lan\",\n capabilityInstance: \"powerSwitch\",\n },\n {\n id: \"brightness\",\n name: tName(\"brightness\"),\n type: \"number\",\n role: \"level.brightness\",\n write: true,\n min: 0,\n max: 100,\n unit: \"%\",\n def: 0,\n capabilityType: \"lan\",\n capabilityInstance: \"brightness\",\n },\n {\n id: \"color_rgb\",\n name: tName(\"colorRgb\"),\n type: \"string\",\n role: \"level.color.rgb\",\n write: true,\n def: \"#000000\",\n capabilityType: \"lan\",\n capabilityInstance: \"colorRgb\",\n },\n {\n id: \"color_temperature\",\n name: tName(\"colorTemperature\"),\n type: \"number\",\n role: \"level.color.temperature\",\n write: true,\n min: 2000,\n max: 9000,\n unit: \"K\",\n def: 2000,\n capabilityType: \"lan\",\n capabilityInstance: \"colorTemperatureK\",\n },\n ];\n}\n\n/**\n * Map a single capability to state definition(s)\n *\n * @param cap Cloud capability to map\n */\nfunction mapSingleCapability(cap: CloudCapability): StateDefinition[] | null {\n if (!cap || typeof cap.type !== \"string\" || typeof cap.instance !== \"string\") {\n return null;\n }\n const shortType = cap.type.replace(\"devices.capabilities.\", \"\");\n\n switch (shortType) {\n case \"on_off\":\n return [\n {\n id: \"power\",\n name: tName(\"power\"),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: false,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"range\":\n return mapRange(cap);\n\n case \"color_setting\":\n return mapColorSetting(cap);\n\n case \"toggle\":\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: false,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"mode\":\n return mapMode(cap);\n\n case \"property\":\n return mapProperty(cap);\n\n case \"online\":\n // Handled separately \u2014 not a regular state\n return null;\n\n case \"segment_color_setting\":\n // Segments are handled specially by state-manager\n return [\n {\n id: `_segment_${sanitizeId(cap.instance)}`,\n name: humanize(cap.instance),\n type: \"string\",\n role: \"json\",\n write: true,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"dynamic_scene\":\n // lightScene / diyScene / snapshot get real dropdowns built later in\n // buildCloudStateDefs from the scenes/snapshots arrays \u2014 skip the\n // generic stub here so we don't create and immediately delete it.\n if (cap.instance === \"lightScene\" || cap.instance === \"diyScene\" || cap.instance === \"snapshot\") {\n return null;\n }\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"string\",\n role: \"json\",\n write: true,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n\n case \"work_mode\":\n return mapWorkMode(cap);\n\n case \"temperature_setting\":\n return mapTemperatureSetting(cap);\n\n case \"event\":\n return mapEvent(cap);\n\n case \"music_setting\":\n return mapMusicSetting(cap);\n\n default:\n return null;\n }\n}\n\n/**\n * Map range capability (brightness, humidity, etc.)\n *\n * @param cap Cloud range capability\n */\nfunction mapRange(cap: CloudCapability): StateDefinition[] {\n const range = cap.parameters?.range;\n const isBrightness = cap.instance.toLowerCase().includes(\"brightness\");\n\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"number\",\n role: isBrightness ? \"level.brightness\" : \"level\",\n write: true,\n min: range?.min ?? 0,\n max: range?.max ?? 100,\n unit: normalizeUnit(cap.parameters?.unit),\n def: range?.min ?? 0,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n}\n\n/**\n * Map color_setting capability (RGB or color temperature)\n *\n * @param cap Cloud color setting capability\n */\nfunction mapColorSetting(cap: CloudCapability): StateDefinition[] {\n if (cap.instance === \"colorRgb\") {\n return [\n {\n id: \"color_rgb\",\n name: tName(\"colorRgb\"),\n type: \"string\",\n role: \"level.color.rgb\",\n write: true,\n def: \"#000000\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n if (cap.instance.includes(\"colorTem\")) {\n const range = cap.parameters?.range;\n return [\n {\n id: \"color_temperature\",\n name: tName(\"colorTemperature\"),\n type: \"number\",\n role: \"level.color.temperature\",\n write: true,\n min: range?.min ?? 2000,\n max: range?.max ?? 9000,\n unit: \"K\",\n def: range?.min ?? 2000,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n return [];\n}\n\n/**\n * Map mode capability (scenes with ENUM options)\n *\n * @param cap Cloud mode capability\n */\nfunction mapMode(cap: CloudCapability): StateDefinition[] {\n if (cap.instance !== \"presetScene\" || !Array.isArray(cap.parameters?.options)) {\n return [];\n }\n\n const states: Record<string, string> = {};\n for (const opt of cap.parameters.options) {\n if (!opt || typeof opt.name !== \"string\") {\n continue;\n }\n const val = safeStringify(opt.value);\n states[val] = opt.name;\n }\n\n return [\n {\n id: \"scene\",\n name: tName(\"scene\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n}\n\n/**\n * Canonical (role, unit) for the numeric Govee sensor kinds \u2014 the single\n * source of truth shared between {@link mapProperty} (Cloud property caps) and\n * state-manager's synthetic-state table, so a reading gets the same role + unit\n * whether it arrives via the Cloud-capability path or the App-API/MQTT path.\n */\nexport const SENSOR_ROLE_UNIT = {\n temperature: { role: \"value.temperature\", unit: \"\u00B0C\" },\n humidity: { role: \"value.humidity\", unit: \"%\" },\n battery: { role: \"value.battery\", unit: \"%\" },\n co2: { role: \"value.co2\", unit: \"ppm\" },\n} as const;\n\n/**\n * Map property capability (read-only sensors). Routes to the `sensor`\n * channel so a Heater's temperature reading sits cleanly next to other\n * sensor-style states instead of in `control`.\n *\n * @param cap Cloud property capability\n */\nfunction mapProperty(cap: CloudCapability): StateDefinition[] {\n const instance = cap.instance.toLowerCase();\n let role = \"value\";\n let unit: string | undefined;\n\n if (instance.includes(\"temperature\")) {\n ({ role, unit } = SENSOR_ROLE_UNIT.temperature);\n } else if (instance.includes(\"humidity\")) {\n ({ role, unit } = SENSOR_ROLE_UNIT.humidity);\n } else if (instance.includes(\"battery\")) {\n ({ role, unit } = SENSOR_ROLE_UNIT.battery);\n } else if (instance.includes(\"co2\") || instance.includes(\"carbondioxide\")) {\n ({ role, unit } = SENSOR_ROLE_UNIT.co2);\n }\n\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"number\",\n role,\n write: false,\n unit: normalizeUnit(cap.parameters?.unit) ?? unit,\n def: 0, // avoid null in vis until the first sensor reading (consistency \u2014 B11)\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n channel: \"sensor\",\n },\n ];\n}\n\n/**\n * Map work_mode capability (STRUCT \u2014 Govee Heater/Humidifier/Fan/...).\n *\n * Two states max:\n * - `work_mode` \u2014 main mode dropdown (mixed type so users can write\n * either the numeric mode value or the label name)\n * - `mode_value` \u2014 secondary parameter (e.g. fan-speed level for the\n * \"manual\" mode); only created if the API actually exposes one\n *\n * @param cap Cloud work_mode capability\n */\nfunction mapWorkMode(cap: CloudCapability): StateDefinition[] {\n const fields = cap.parameters?.fields;\n if (!fields || fields.length === 0) {\n return [\n {\n id: \"work_mode\",\n name: tName(\"workMode\"),\n type: \"mixed\",\n role: \"level.mode.work\",\n write: true,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n const states: StateDefinition[] = [];\n const modeField = fields.find(f => f && f.fieldName === \"workMode\");\n if (modeField?.options && modeField.options.length > 0) {\n const modeStates: Record<string, string> = {};\n for (const opt of modeField.options) {\n if (opt && typeof opt.name === \"string\") {\n modeStates[safeStringify(opt.value)] = opt.name;\n }\n }\n states.push({\n id: \"work_mode\",\n name: tName(\"workMode\"),\n type: \"mixed\",\n role: \"level.mode.work\",\n write: true,\n states: modeStates,\n def: modeField.options[0] ? safeStringify(modeField.options[0].value) : \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n const valueField = fields.find(f => f && f.fieldName === \"modeValue\");\n if (valueField) {\n if (valueField.options && valueField.options.length > 0) {\n const valStates: Record<string, string> = {};\n for (const opt of valueField.options) {\n if (opt && typeof opt.name === \"string\") {\n valStates[safeStringify(opt.value)] = opt.name;\n }\n }\n states.push({\n id: \"mode_value\",\n name: tName(\"modeValue\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: valStates,\n def: valueField.options[0] ? safeStringify(valueField.options[0].value) : \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n } else if (valueField.range) {\n states.push({\n id: \"mode_value\",\n name: tName(\"modeValue\"),\n type: \"number\",\n role: \"level\",\n write: true,\n min: valueField.range.min,\n max: valueField.range.max,\n def: valueField.range.min,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n }\n\n return states;\n}\n\n/**\n * Map temperature_setting capability \u2014 Heater target-temp slider.\n * Honours the unit reported by the API (\u00B0F or \u00B0C); falls back to \u00B0F\n * because that's the more common Govee Heater default.\n *\n * @param cap Cloud temperature_setting capability\n */\nfunction mapTemperatureSetting(cap: CloudCapability): StateDefinition[] {\n const fields = cap.parameters?.fields;\n if (Array.isArray(fields) && fields.length > 0) {\n const tempField = fields.find(f => {\n if (!f || typeof f.fieldName !== \"string\") {\n return false;\n }\n if (f.fieldName === \"targetTemperature\") {\n return true;\n }\n return f.fieldName.toLowerCase().includes(\"temperature\");\n });\n if (tempField?.range) {\n const unit = normalizeUnit(cap.parameters?.unit) ?? \"\u00B0F\";\n return [\n {\n id: \"target_temperature\",\n name: tName(\"targetTemperature\"),\n type: \"number\",\n role: \"level.temperature\",\n write: true,\n min: tempField.range.min,\n max: tempField.range.max,\n unit,\n def: tempField.range.min,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n }\n\n const range = cap.parameters?.range;\n if (range) {\n const unit = normalizeUnit(cap.parameters?.unit) ?? \"\u00B0F\";\n return [\n {\n id: \"target_temperature\",\n name: tName(\"targetTemperature\"),\n type: \"number\",\n role: \"level.temperature\",\n write: true,\n min: range.min,\n max: range.max,\n unit,\n def: range.min,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n }\n\n // No usable schema \u2014 expose the raw payload so the user at least sees\n // the attempt and can report it. Stays JSON to avoid pretending we\n // understand the structure.\n return [\n {\n id: \"target_temperature\",\n name: tName(\"targetTemperature\"),\n type: \"string\",\n role: \"json\",\n write: true,\n def: \"\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n },\n ];\n}\n\n/**\n * Map event capability (asynchronous OpenAPI-MQTT alarms \u2014 read-only).\n * Each event becomes a boolean indicator in the events/ channel\n * (lackWater, iceFull, bodyAppeared, dirtDetected, \u2026).\n *\n * @param cap Cloud event capability\n */\nfunction mapEvent(cap: CloudCapability): StateDefinition[] {\n return [\n {\n id: sanitizeId(cap.instance),\n name: humanize(cap.instance),\n type: \"boolean\",\n role: \"indicator.alarm\",\n write: false,\n def: false,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n channel: \"events\",\n },\n ];\n}\n\n/**\n * Map music_setting capability to user-friendly states.\n * Parses STRUCT fields into: mode dropdown, sensitivity slider, auto-color toggle.\n *\n * @param cap Cloud music_setting capability\n */\n/**\n * Whether a music mode carries a custom RGB colour in the ptReal LAN packet\n * (`33 05 01 <mode> R G B`). Spectrum + Rolling are colour-reactive; Energic,\n * Rhythm and any other mode use the device's own auto-colours.\n *\n * Keyed on the mode NAME, not its numeric value: Govee's music-mode values are\n * SKU-specific (the fleet has 0-based and 1-based SKUs \u2014 see A1), so a\n * value-based gate would append/withhold RGB on the wrong mode for a SKU whose\n * Spectrum/Rolling isn't at value 1/2. An unknown mode name (e.g. \"Sprouting\")\n * returns false \u2014 the safe default is to send no RGB rather than push bytes a\n * mode may not expect.\n *\n * @param name Music mode name from the capability option\n */\nexport function musicModeNameUsesRgb(name: string | undefined): boolean {\n if (typeof name !== \"string\") {\n return false;\n }\n const n = name.trim().toLowerCase();\n return n === \"spectrum\" || n === \"rolling\";\n}\n\n/**\n * Ordered list of valid music-mode options from a music_setting capability.\n * Both the dropdown builder ({@link mapMusicSetting}) and the send path\n * (`sendMusicCommand` in state-change-router) resolve music modes through THIS\n * function, so the index\u2192value mapping can never drift between what the user\n * selects and what is sent to the device.\n *\n * @param cap A music_setting capability\n * @returns The `musicMode` field's options (string name only), in API order\n */\nexport function getMusicModeOptions(cap: CloudCapability): CapabilityOption[] {\n const fields = cap.parameters?.fields;\n if (!Array.isArray(fields)) {\n return [];\n }\n const modeField = fields.find(f => f && typeof f.fieldName === \"string\" && f.fieldName === \"musicMode\");\n if (!modeField?.options || !Array.isArray(modeField.options)) {\n return [];\n }\n return modeField.options.filter(o => !!o && typeof o.name === \"string\");\n}\n\nfunction mapMusicSetting(cap: CloudCapability): StateDefinition[] {\n const fields = cap.parameters?.fields;\n if (!Array.isArray(fields) || fields.length === 0) {\n // No field details from API \u2014 can't create usable states\n return [];\n }\n\n const states: StateDefinition[] = [];\n\n // Mode dropdown \u2014 only if API provides actual mode options. Index-based\n // (0 = \"---\" sentinel), consistent with every other dropdown; the device\n // value for index N is modeOptions[N-1].value, resolved at send time through\n // the SAME getMusicModeOptions() list so build and send never drift. The old\n // value-keyed scheme collided with the sentinel on 0-based SKUs, making the\n // first music mode unreachable (A1).\n const modeOptions = getMusicModeOptions(cap);\n if (modeOptions.length > 0) {\n states.push({\n id: \"music_mode\",\n name: tName(\"musicMode\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(modeOptions),\n def: \"0\",\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n // Sensitivity slider\n const sensField = fields.find(f => f && typeof f.fieldName === \"string\" && f.fieldName === \"sensitivity\");\n if (sensField?.range) {\n states.push({\n id: \"music_sensitivity\",\n name: tName(\"musicSensitivity\"),\n type: \"number\",\n role: \"level\",\n write: true,\n min: sensField.range.min,\n max: sensField.range.max,\n unit: \"%\",\n def: sensField.range.max,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n // Auto color toggle\n const autoColorField = fields.find(f => f && typeof f.fieldName === \"string\" && f.fieldName === \"autoColor\");\n if (autoColorField) {\n states.push({\n id: \"music_auto_color\",\n name: tName(\"musicAutoColor\"),\n type: \"boolean\",\n role: \"switch\",\n write: true,\n def: true,\n capabilityType: cap.type,\n capabilityInstance: cap.instance,\n });\n }\n\n // All music states belong to the music channel\n for (const s of states) {\n s.channel = \"music\";\n }\n return states;\n}\n\n/**\n * Apply device quirks to mapped state definitions.\n * Corrects wrong API data (e.g. color temperature range) for specific SKUs.\n *\n * @param sku Device model (e.g. \"H60A1\")\n * @param states State definitions to adjust\n * @param log Adapter logger \u2014 quirk-applied events land on debug.\n */\nexport function applyQuirksToStates(sku: string, states: StateDefinition[], log: ioBroker.Logger): StateDefinition[] {\n for (const state of states) {\n if (state.id === \"color_temperature\" && state.min != null && state.max != null) {\n const corrected = applyColorTempQuirk(sku, state.min, state.max);\n if (corrected.min !== state.min || corrected.max !== state.max) {\n log.debug(\n `Quirk applied for ${sku}: color_temperature range ${state.min}-${state.max}K \u2192 ${corrected.min}-${corrected.max}K`,\n );\n }\n state.min = corrected.min;\n state.max = corrected.max;\n state.def = corrected.min;\n }\n }\n return states;\n}\n\n/** Known Govee API unit strings \u2192 ioBroker units */\nconst UNIT_MAP: Record<string, string> = {\n \"unit.percent\": \"%\",\n \"unit.kelvin\": \"K\",\n \"unit.celsius\": \"\u00B0C\",\n \"unit.fahrenheit\": \"\u00B0F\",\n};\n\n/**\n * Normalize Govee API unit string to ioBroker standard\n *\n * @param unit Raw unit string from API\n */\nfunction normalizeUnit(unit?: string): string | undefined {\n if (!unit) {\n return undefined;\n }\n return UNIT_MAP[unit] ?? unit;\n}\n\n/**\n * Sanitize a string for use as ioBroker state ID\n *\n * @param str Input string to sanitize\n */\nfunction sanitizeId(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1_$2\")\n .replace(/[^a-zA-Z0-9_]/g, \"_\")\n .toLowerCase();\n}\n\n/**\n * Convert camelCase to human-readable name\n *\n * @param str camelCase input string\n */\nfunction humanize(str: string): string {\n // Order: first underscore \u2192 space, then camelCase split, then trim +\n // uppercase the first character. Previously leading-underscore IDs\n // (e.g. `_segment_color`) became ` segment color` with a leading space\n // and no capitalization (^\\w matches a space, not a word char).\n return str\n .replace(/_/g, \" \")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .trim()\n .replace(/^./, c => c.toUpperCase());\n}\n\n/** Mapped Cloud state value: state ID + converted value */\nexport interface CloudStateValue {\n /** State ID in control/ channel (e.g. \"power\", \"brightness\", \"gradient_toggle\") */\n stateId: string;\n /** Converted value ready for ioBroker setState */\n value: ioBroker.StateValue;\n}\n\n/**\n * Map a Cloud device state capability to a state ID + converted value.\n * Uses the same ID logic as mapCapabilities so IDs always match.\n *\n * @param cap Cloud state capability with current value\n */\nexport function mapCloudStateValue(cap: CloudStateCapability): CloudStateValue | null {\n if (!cap || typeof cap.type !== \"string\" || typeof cap.instance !== \"string\") {\n return null;\n }\n const shortType = cap.type.replace(\"devices.capabilities.\", \"\");\n const raw = cap.state?.value;\n if (raw === undefined || raw === null) {\n return null;\n }\n\n switch (shortType) {\n case \"on_off\":\n return { stateId: \"power\", value: coerceBool(raw) };\n\n case \"range\": {\n const n = coerceNum(raw);\n if (n === null) {\n return null;\n }\n return { stateId: sanitizeId(cap.instance), value: n };\n }\n\n case \"color_setting\":\n if (cap.instance === \"colorRgb\") {\n const num = coerceNum(raw) ?? 0;\n return {\n stateId: \"color_rgb\",\n value: rgbToHex((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff),\n };\n }\n if (cap.instance.includes(\"colorTem\")) {\n const n = coerceNum(raw);\n if (n === null) {\n return null;\n }\n return { stateId: \"color_temperature\", value: n };\n }\n return null;\n\n case \"toggle\":\n return { stateId: sanitizeId(cap.instance), value: coerceBool(raw) };\n\n case \"mode\":\n if (cap.instance === \"presetScene\") {\n return {\n stateId: \"scene\",\n value: safeStringify(raw),\n };\n }\n return null;\n\n case \"dynamic_scene\":\n // snapshot is an action-only dropdown (activate a saved snapshot) \u2014 there\n // is no persistent \"current snapshot\" status, and its real state id is\n // snapshot_cloud (not \"snapshot\"), so a cloud-state write here only\n // produced a \"has no existing object\" warning (B12). Skip it. lightScene /\n // diyScene DO have a meaningful active value and sanitizeId maps them to\n // light_scene / diy_scene, which match their real states.\n if (cap.instance === \"snapshot\") {\n return null;\n }\n return {\n stateId: sanitizeId(cap.instance),\n value: safeStringify(raw),\n };\n\n case \"work_mode\": {\n // STRUCT: { workMode: <number>, modeValue?: <number> }. Cloud\n // /device/state only returns the primary mode here \u2014 mode_value\n // (sub-parameter) follows via MQTT status push when the device\n // reports it, so we don't lose it just because it isn't in the\n // initial state response.\n if (typeof raw === \"object\" && raw !== null) {\n const struct = raw as Record<string, unknown>;\n const n = coerceNum(struct.workMode);\n if (n !== null) {\n return { stateId: \"work_mode\", value: n };\n }\n }\n const direct = coerceNum(raw);\n if (direct !== null) {\n return { stateId: \"work_mode\", value: direct };\n }\n return null;\n }\n\n case \"temperature_setting\": {\n // STRUCT: { targetTemperature: <number>, temperatureUnit?: ... }\n // \u2014 fall back to direct number for adapters that simplify.\n const direct = coerceNum(raw);\n if (direct !== null) {\n return { stateId: \"target_temperature\", value: direct };\n }\n if (typeof raw === \"object\" && raw !== null) {\n const struct = raw as Record<string, unknown>;\n const temp = struct.targetTemperature ?? struct.temperature ?? struct.temp;\n const n = coerceNum(temp);\n if (n !== null) {\n return { stateId: \"target_temperature\", value: n };\n }\n }\n return null;\n }\n\n case \"event\":\n return {\n stateId: sanitizeId(cap.instance),\n value: coerceBool(raw),\n };\n\n case \"music_setting\":\n // Extract mode value from STRUCT state\n if (typeof raw === \"object\" && raw !== null) {\n const struct = raw as Record<string, unknown>;\n const mode = coerceNum(struct.musicMode);\n return {\n stateId: \"music_mode\",\n value: mode !== null ? String(mode) : \"0\",\n };\n }\n return null;\n\n case \"property\": {\n const n = coerceNum(raw);\n if (n === null) {\n return null;\n }\n return { stateId: sanitizeId(cap.instance), value: n };\n }\n\n default:\n return null;\n }\n}\n\n/**\n * Plan the per-state writes for a list of synthesised Cloud-state\n * capabilities. Used by the App-API poll and the OpenAPI-MQTT event\n * handler (both call into `applyCloudCapabilities` on the adapter side).\n *\n * Returns the resolved `(stateId, value)` pairs for capabilities that:\n * - decode via `mapCloudStateValue` to a non-null result, AND\n * - aren't shadowed by the LAN-state set when the device is LAN-capable\n * (lights with `lanIp` shouldn't have their LAN sub-second updates\n * overwritten by a Cloud-source value).\n *\n * Pure function \u2014 no adapter state, no I/O \u2014 so the LAN-shadow logic is\n * unit-testable independent of the live state-write pipeline.\n *\n * @param caps Capabilities to consider\n * @param hasLanIp Whether the target device has a known LAN IP\n * @param lanStateIds Default-LAN state IDs that LAN delivers authoritatively\n */\nexport function planCloudCapabilityWrites(\n caps: CloudStateCapability[],\n hasLanIp: boolean,\n lanStateIds: ReadonlySet<string>,\n): CloudStateValue[] {\n const writes: CloudStateValue[] = [];\n if (!Array.isArray(caps)) {\n return writes;\n }\n for (const cap of caps) {\n const mapped = mapCloudStateValue(cap);\n if (!mapped) {\n continue;\n }\n if (hasLanIp && lanStateIds.has(mapped.stateId)) {\n continue;\n }\n writes.push(mapped);\n }\n return writes;\n}\n\n/**\n * Scene-dropdown rules \u2014 three states with identical shape (dropdown over a\n * device-content array, capabilityType `dynamic_scene`). Adding a new\n * scene-style dropdown means: append one entry here. The 8 other Cloud-derived\n * states (scene_speed, refresh_cloud, snapshot_local/save/delete, diag.*)\n * have genuinely different shapes and stay inline \u2014 not the same anti-pattern.\n */\nconst SCENE_DROPDOWN_RULES: ReadonlyArray<{\n id: string;\n cap: \"lightScene\" | \"diyScene\" | \"snapshot\";\n nameKey: Parameters<typeof tName>[0];\n descKey?: Parameters<typeof tDesc>[0];\n channel: \"scenes\" | \"snapshots\";\n source: (d: GoveeDevice) => { name: string }[];\n}> = [\n { id: \"light_scene\", cap: \"lightScene\", nameKey: \"lightScene\", channel: \"scenes\", source: d => d.scenes },\n { id: \"diy_scene\", cap: \"diyScene\", nameKey: \"diyScene\", channel: \"scenes\", source: d => d.diyScenes },\n {\n id: \"snapshot_cloud\",\n cap: \"snapshot\",\n nameKey: \"cloudSnapshot\",\n descKey: \"cloudSnapshotDesc\",\n channel: \"snapshots\",\n source: d => d.snapshots,\n },\n];\n\n/**\n * Build LAN-owned state definitions for a device. Returns the four\n * lan-default states (power/brightness/colorRgb/colorTemperature) with quirks\n * applied, or [] for devices without a LAN address (sensors, appliances,\n * groups).\n *\n * Phase architecture: belongs to the LAN phase. Called when a device becomes\n * visible via LAN discovery or is loaded with a lanIp from the cache.\n *\n * @param device Govee device\n * @param log Adapter logger \u2014 forwarded to applyQuirksToStates.\n */\nexport function buildLanStateDefs(device: GoveeDevice, log: ioBroker.Logger): StateDefinition[] {\n if (!device.lanIp) {\n return [];\n }\n const stateDefs = getDefaultLanStates();\n applyQuirksToStates(device.sku, stateDefs, log);\n return stateDefs;\n}\n\n/**\n * The three diagnostics states (export button, JSON result, trust tier).\n * Shared by buildCloudStateDefs (tier default \"unknown\") and\n * buildGroupStateDefs (tier default \"verified\") \u2014 only the tier default differs.\n *\n * @param tierDef Initial value for the diag.tier state\n */\nfunction buildDiagStateDefs(tierDef: string | null): StateDefinition[] {\n const defs: StateDefinition[] = [\n {\n id: \"export\",\n name: tName(\"exportDiagnostics\"),\n type: \"boolean\",\n role: \"button\",\n write: true,\n def: false,\n capabilityType: \"local\",\n capabilityInstance: \"diagnosticsExport\",\n channel: \"diag\",\n },\n {\n id: \"result\",\n name: tName(\"diagnosticsJson\"),\n type: \"string\",\n role: \"json\",\n write: false,\n def: \"\",\n capabilityType: \"local\",\n capabilityInstance: \"diagnosticsResult\",\n channel: \"diag\",\n },\n ];\n // The trust tier is a per-SKU catalog attribute \u2014 meaningless for a BaseGroup\n // (not a real device), which used to show a hard-coded \"verified\". Groups pass\n // null and get no tier state at all (B4).\n if (tierDef !== null) {\n defs.push({\n id: \"tier\",\n name: tName(\"deviceTier\"),\n type: \"string\",\n role: \"text\",\n write: false,\n def: tierDef,\n states: {\n verified: resolveLabel(\"deviceTierVerified\"),\n reported: resolveLabel(\"deviceTierReported\"),\n seed: resolveLabel(\"deviceTierSeed\"),\n unknown: resolveLabel(\"deviceTierUnknown\"),\n },\n capabilityType: \"local\",\n capabilityInstance: \"diagnosticsTier\",\n channel: \"diag\",\n });\n }\n return defs;\n}\n\n/**\n * Build Cloud-owned state definitions for a device \u2014 everything that needs\n * Cloud capabilities or local synthetic decoration. Excludes LAN-default IDs\n * (the LAN phase owns those). Returns intersection state for BaseGroup\n * devices.\n *\n * Phase architecture: belongs to the Cloud phase. Called when capabilities for\n * a device become available from the cache or a fresh cloud load.\n *\n * @param device Govee device\n * @param log Adapter logger \u2014 forwarded to mapCapabilities / applyQuirksToStates.\n * @param localSnapshots Optional local snapshot names\n * @param memberDevices Resolved member devices (only for BaseGroup)\n */\nexport function buildCloudStateDefs(\n device: GoveeDevice,\n log: ioBroker.Logger,\n localSnapshots?: { name: string }[],\n memberDevices?: GoveeDevice[],\n): StateDefinition[] {\n if (device.sku === \"BaseGroup\") {\n return buildGroupStateDefs(memberDevices || []);\n }\n\n // Per-SKU quirk: brokenPlatformApi \u2192 don't trust the platform-cap tree.\n // Skip capability-derived defs AND the scene/snapshot dropdowns (they're\n // gated by hasDynamicSceneCapability which reads device.capabilities, i.e.\n // the same untrusted source). LAN-phase still creates power / brightness /\n // colorRgb / colorTemperature defaults so the device stays controllable.\n const quirks = getDeviceQuirks(device.sku);\n const skipCapabilities = quirks?.brokenPlatformApi === true;\n\n // Capability-derived states. A LAN-owning light (lanIp set) lets the LAN\n // phase own the four control ids (power/brightness/colorRgb/colorTemperature)\n // \u2014 they are filtered out here so they are not double-created in the same\n // channel. Without a lanIp there is NO LAN phase to provide them and nothing\n // to double-create, so keep the cap-derived control states: a cloud-only\n // light (owner never enabled the local API) stays controllable via the Cloud,\n // and appliances (which never have a LAN phase) keep their on/off too. The\n // command-router routes these via Cloud (`no-lan` / `light-no-lan-fallback`).\n // Local-first stays, local-only does not. Sensors carry no such caps; groups\n // returned earlier. LAN lights are unchanged.\n const noLanPhase = !device.lanIp;\n const stateDefs: StateDefinition[] = skipCapabilities\n ? []\n : mapCapabilities(device.capabilities, log).filter(d => noLanPhase || !LAN_STATE_IDS.has(d.id));\n\n if (skipCapabilities) {\n log.debug(`${device.sku}: brokenPlatformApi quirk active \u2014 skipping capability-derived states + dropdowns`);\n }\n\n applyQuirksToStates(device.sku, stateDefs, log);\n\n // Light-only synthetic state defs \u2014 scenes / snapshots / music / scene_speed\n // only make sense for lights. Sensors and appliances would otherwise see\n // empty snapshot dropdowns and a useless save/delete button pair.\n const isLight = device.type === GOVEE_DEVICE_TYPE.LIGHT;\n\n // Three structurally-identical Cloud dropdowns \u2014 collapsed into one loop.\n for (const r of SCENE_DROPDOWN_RULES) {\n if (skipCapabilities || !isLight || !hasDynamicSceneCapability(device.capabilities, r.cap)) {\n continue;\n }\n stateDefs.push({\n id: r.id,\n name: tName(r.nameKey),\n desc: r.descKey ? tDesc(r.descKey) : undefined,\n // mixed lets users write the index (\"1\"), the index as number (1),\n // or the entry name (\"Aurora\") \u2014 the onStateChange handler resolves\n // all three forms via the common.states map.\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(r.source(device)),\n def: \"0\",\n capabilityType: GOVEE_CAP_TYPE.DYNAMIC_SCENE,\n capabilityInstance: r.cap,\n channel: r.channel,\n });\n }\n\n // Scene speed slider \u2014 only if any scene supports speed adjustment.\n // Stays inline: depends on a computed maxSpeedLevel that doesn't fit the\n // dropdown-rule shape.\n const maxSpeedLevel = device.sceneLibrary.reduce((max, entry) => {\n if (entry.speedInfo?.supSpeed && entry.speedInfo.config) {\n try {\n const parsed = JSON.parse(entry.speedInfo.config) as unknown;\n // Config can drift \u2014 if not an array, skip this entry silently\n if (!Array.isArray(parsed)) {\n return max;\n }\n for (const cfg of parsed as Array<{ moveIn?: number[] }>) {\n if (cfg && Array.isArray(cfg.moveIn) && cfg.moveIn.length - 1 > max) {\n max = cfg.moveIn.length - 1;\n }\n }\n } catch (e) {\n log.debug(`${deviceLabel(device)}: speed-config parse failed for scene \"${entry.name}\": ${errMessage(e)}`);\n }\n }\n return max;\n }, -1);\n if (isLight && maxSpeedLevel > 0) {\n stateDefs.push({\n id: \"scene_speed\",\n name: tName(\"sceneSpeed\"),\n type: \"number\",\n role: \"level\",\n write: true,\n min: 0,\n max: maxSpeedLevel,\n def: 0,\n capabilityType: \"local\",\n capabilityInstance: \"sceneSpeed\",\n channel: \"scenes\",\n });\n }\n\n // Per-device refresh button \u2014 gated on ANY dynamic-scene capability.\n // OR-gate over three caps doesn't fit a rules-table.\n // brokenPlatformApi gate skips this too: the refresh action would post a\n // user-facing button for a Cloud endpoint that wasn't trustable to begin\n // with.\n if (\n !skipCapabilities &&\n isLight &&\n (hasDynamicSceneCapability(device.capabilities, \"lightScene\") ||\n hasDynamicSceneCapability(device.capabilities, \"diyScene\") ||\n hasDynamicSceneCapability(device.capabilities, \"snapshot\"))\n ) {\n stateDefs.push({\n id: \"refresh_cloud\",\n name: tName(\"refreshCloud\"),\n desc: tDesc(\"refreshCloudDesc\"),\n type: \"boolean\",\n role: \"button\",\n write: true,\n def: false,\n capabilityType: \"local\",\n capabilityInstance: \"refreshCloud\",\n channel: \"snapshots\",\n });\n }\n\n // Local snapshots \u2014 three states with different shapes (mixed dropdown vs\n // plain string-write fields). Inline because a rules-table would need a\n // discriminator field with no payoff.\n if (isLight) {\n stateDefs.push({\n id: \"snapshot_local\",\n name: tName(\"localSnapshot\"),\n desc: tDesc(\"localSnapshotDesc\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(localSnapshots ?? []),\n def: \"0\",\n capabilityType: \"local\",\n capabilityInstance: \"snapshotLocal\",\n channel: \"snapshots\",\n });\n stateDefs.push({\n id: \"snapshot_save\",\n name: tName(\"saveLocalSnapshot\"),\n desc: tDesc(\"saveLocalSnapshotDesc\"),\n type: \"string\",\n role: \"text\",\n write: true,\n def: \"\",\n capabilityType: \"local\",\n capabilityInstance: \"snapshotSave\",\n channel: \"snapshots\",\n });\n stateDefs.push({\n id: \"snapshot_delete\",\n name: tName(\"deleteLocalSnapshot\"),\n desc: tDesc(\"deleteLocalSnapshotDesc\"),\n type: \"string\",\n role: \"text\",\n write: true,\n def: \"\",\n capabilityType: \"local\",\n capabilityInstance: \"snapshotDelete\",\n channel: \"snapshots\",\n });\n }\n\n // Diagnostics \u2014 export button, JSON result, trust tier. Default tier\n // \"unknown\" for a real device (its actual tier comes from the registry).\n stateDefs.push(...buildDiagStateDefs(\"unknown\"));\n\n return stateDefs;\n}\n\n/** Maps the snake_case LAN control state-id (getDefaultLanStates ids) to its cloud-instance kind. */\nconst CONTROL_STATE_ID_TO_KIND: Record<string, ControlKind> = {\n power: \"power\",\n brightness: \"brightness\",\n color_rgb: \"colorRgb\",\n color_temperature: \"colorTemperature\",\n};\n\n/**\n * Check if a member device supports a given control state.\n * LAN-capable devices support all basic controls.\n *\n * @param member Group member device\n * @param stateId Control state ID (e.g. \"power\", \"brightness\")\n */\nfunction memberHasControlState(member: GoveeDevice, stateId: string): boolean {\n if (member.lanIp) {\n return true;\n }\n const kind = CONTROL_STATE_ID_TO_KIND[stateId];\n if (!kind) {\n return false;\n }\n const caps = Array.isArray(member.capabilities) ? member.capabilities : [];\n return caps.some(c => capMatchesControl(c, kind));\n}\n\n/**\n * Build state definitions for a BaseGroup device.\n * Capabilities = intersection of controllable member devices.\n * No snapshots, no segments; diag-states (export/result/tier) included since v2.9.1.\n *\n * @param members Resolved member devices\n */\nfunction buildGroupStateDefs(members: GoveeDevice[]): StateDefinition[] {\n const controllable = members.filter(m => m.lanIp || m.channels.cloud);\n if (controllable.length === 0) {\n return [];\n }\n\n const stateDefs: StateDefinition[] = [];\n\n // Control states: intersection of member capabilities\n for (const ld of getDefaultLanStates()) {\n if (controllable.every(m => memberHasControlState(m, ld.id))) {\n stateDefs.push(ld);\n }\n }\n\n // Scenes: intersection of member scene names\n if (controllable.every(m => m.scenes.length > 0)) {\n const firstNames = controllable[0].scenes.map(s => s.name);\n const commonNames = firstNames.filter(name => controllable.every(m => m.scenes.some(s => s.name === name)));\n if (commonNames.length > 0) {\n stateDefs.push({\n id: \"light_scene\",\n name: tName(\"lightScene\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(commonNames.map(name => ({ name }))),\n def: \"0\",\n capabilityType: GOVEE_CAP_TYPE.DYNAMIC_SCENE,\n capabilityInstance: \"lightScene\",\n channel: \"scenes\",\n });\n }\n }\n\n // Music: intersection of member music libraries\n if (controllable.every(m => m.musicLibrary.length > 0)) {\n const firstNames = controllable[0].musicLibrary.map(m => m.name);\n const commonNames = firstNames.filter(name => controllable.every(m => m.musicLibrary.some(ml => ml.name === name)));\n if (commonNames.length > 0) {\n stateDefs.push({\n id: \"music_mode\",\n name: tName(\"musicMode\"),\n type: \"mixed\",\n role: \"state\",\n write: true,\n states: buildUniqueLabelMap(commonNames.map(name => ({ name }))),\n def: \"0\",\n capabilityType: GOVEE_CAP_TYPE.MUSIC_SETTING,\n capabilityInstance: \"musicMode\",\n channel: \"music\",\n });\n }\n }\n\n // v2.9.1 \u2014 BaseGroups get the same three diag states. Tier defaults to\n // \"verified\" because BaseGroup isn't a real SKU and has no quirks entry \u2014\n // the diag-button just renders consistently.\n stateDefs.push(...buildDiagStateDefs(null));\n\n return stateDefs;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }