iobroker.sonoff 4.2.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -167,6 +167,11 @@ States:
167
167
  ### **WORK IN PROGRESS**
168
168
  -->
169
169
  ## Changelog
170
+ ### 4.2.1 (2026-09-14)
171
+ * (@patricknitsch) Fixed Device Manager showing no devices at all if a data point exists twice, e.g. `POWER` and `STATE.POWER` (#604)
172
+ * (@patricknitsch) Fixed writing to nested data points, e.g. `STATE.POWER1` with "Create object tree" enabled
173
+ * (@GermanBluefox) Fixed writing to devices whose MQTT client ID contains a dot
174
+
170
175
  ### 4.2.0 (2026-09-07)
171
176
  * (@patricknitsch) Added Device Manager for existing devices
172
177
 
@@ -214,11 +219,6 @@ States:
214
219
 
215
220
  * (bluefox) Sanitize the IDs of the clients
216
221
 
217
- ### 3.2.0 (2024-08-28)
218
- * (bluefox) Added information about connected clients in the server mode
219
-
220
- [Older changelogs can be found there](CHANGELOG_OLD.md)
221
-
222
222
  ## License
223
223
 
224
224
  The MIT License (MIT)
@@ -197,10 +197,9 @@ class SonoffDeviceManagement extends dm_utils_1.DeviceManagement {
197
197
  }
198
198
  onStateChange(id, state) {
199
199
  if (state) {
200
- if (!this.states[id] || this.states[id].val !== state.val) {
201
- // trigger DM update
202
- this.states[id] = state;
203
- }
200
+ // Also on an unchanged value: the timestamp decides which copy of a data point that exists
201
+ // more than once is the current one (see `buildControls` and `getPowerEntries`)
202
+ this.states[id] = state;
204
203
  }
205
204
  else if (this.states[id]) {
206
205
  // trigger DM update
@@ -376,64 +375,73 @@ class SonoffDeviceManagement extends dm_utils_1.DeviceManagement {
376
375
  if (!shortDeviceId || shortDeviceId.includes('.') || shortDeviceId === 'info') {
377
376
  continue;
378
377
  }
379
- const prefix = `${device._id}.`;
380
- const suffixes = this.getSuffixes(prefix);
381
- const alive = this.states[`${device._id}.alive`]?.val === true;
382
- const hostname = this.states[`${prefix}INFO.Hostname`]?.val;
383
- const ip = this.states[`${prefix}INFO.IPAddress`]?.val;
384
- const model = this.states[`${prefix}INFO.Module`]?.val || undefined;
385
- const rssiSuffix = this.findDataPointSuffix(prefix, 'RSSI');
386
- const rssi = rssiSuffix ? this.states[`${prefix}${rssiSuffix}`]?.val : undefined;
387
- const batterySuffix = this.findDataPointSuffix(prefix, 'BatteryPercentage');
388
- const battery = batterySuffix
389
- ? this.states[`${prefix}${batterySuffix}`]?.val
390
- : undefined;
391
- const clientId = device.native?.clientId;
392
- const group = this.getDeviceGroup(suffixes);
393
- const res = {
394
- id: device._id,
395
- identifier: hostname || ip || clientId || undefined,
396
- name: device.common.name,
397
- icon: DEVICE_ICON,
398
- color: !alive ? '#fff' : undefined,
399
- backgroundColor: !alive ? '#f44336' : undefined,
400
- group,
401
- model,
402
- status: {
403
- connection: alive ? 'connected' : 'disconnected',
404
- rssi,
405
- battery,
406
- },
407
- hasDetails: true,
408
- customInfo: this.buildCustomInfo(device._id, prefix),
409
- controls: this.buildControls(shortDeviceId, prefix),
410
- actions: [
411
- {
412
- id: 'rename',
413
- icon: 'edit',
414
- description: adapter_core_1.I18n.getTranslatedObject('Rename this device'),
415
- handler: async (deviceId, context) => await this.handleRenameDevice(deviceId, context),
416
- },
417
- {
418
- id: 'recreate',
419
- icon: 'refresh',
420
- description: adapter_core_1.I18n.getTranslatedObject('Delete and recreate all data points of this device'),
421
- confirmation: adapter_core_1.I18n.getTranslatedObject('This deletes all data points of this device (except its name). They will be recreated automatically the next time the device reports its state. Continue?'),
422
- handler: async (deviceId, context) => await this.handleRecreateDevice(deviceId, context),
378
+ try {
379
+ const prefix = `${device._id}.`;
380
+ const suffixes = this.getSuffixes(prefix);
381
+ const alive = this.states[`${device._id}.alive`]?.val === true;
382
+ const hostname = this.states[`${prefix}INFO.Hostname`]?.val;
383
+ const ip = this.states[`${prefix}INFO.IPAddress`]?.val;
384
+ const model = this.states[`${prefix}INFO.Module`]?.val || undefined;
385
+ const rssiSuffix = this.findDataPointSuffix(prefix, 'RSSI');
386
+ const rssi = rssiSuffix
387
+ ? this.states[`${prefix}${rssiSuffix}`]?.val
388
+ : undefined;
389
+ const batterySuffix = this.findDataPointSuffix(prefix, 'BatteryPercentage');
390
+ const battery = batterySuffix
391
+ ? this.states[`${prefix}${batterySuffix}`]?.val
392
+ : undefined;
393
+ const clientId = device.native?.clientId;
394
+ const group = this.getDeviceGroup(suffixes);
395
+ const res = {
396
+ id: device._id,
397
+ identifier: hostname || ip || clientId || undefined,
398
+ name: device.common?.name || shortDeviceId,
399
+ icon: DEVICE_ICON,
400
+ color: !alive ? '#fff' : undefined,
401
+ backgroundColor: !alive ? '#f44336' : undefined,
402
+ group,
403
+ model,
404
+ status: {
405
+ connection: alive ? 'connected' : 'disconnected',
406
+ rssi,
407
+ battery,
423
408
  },
424
- ...(hostname || ip
425
- ? [
426
- {
427
- id: 'web',
428
- icon: 'web',
429
- description: adapter_core_1.I18n.getTranslatedObject('Open device web interface'),
430
- url: `http://${hostname || ip}`,
431
- },
432
- ]
433
- : []),
434
- ],
435
- };
436
- context.addDevice(res);
409
+ hasDetails: true,
410
+ customInfo: this.buildCustomInfo(device._id, prefix),
411
+ controls: this.buildControls(shortDeviceId, prefix),
412
+ actions: [
413
+ {
414
+ id: 'rename',
415
+ icon: 'edit',
416
+ description: adapter_core_1.I18n.getTranslatedObject('Rename this device'),
417
+ handler: async (deviceId, context) => await this.handleRenameDevice(deviceId, context),
418
+ },
419
+ {
420
+ id: 'recreate',
421
+ icon: 'refresh',
422
+ description: adapter_core_1.I18n.getTranslatedObject('Delete and recreate all data points of this device'),
423
+ confirmation: adapter_core_1.I18n.getTranslatedObject('This deletes all data points of this device (except its name). They will be recreated automatically the next time the device reports its state. Continue?'),
424
+ handler: async (deviceId, context) => await this.handleRecreateDevice(deviceId, context),
425
+ },
426
+ ...(hostname || ip
427
+ ? [
428
+ {
429
+ id: 'web',
430
+ icon: 'web',
431
+ description: adapter_core_1.I18n.getTranslatedObject('Open device web interface'),
432
+ url: `http://${hostname || ip}`,
433
+ },
434
+ ]
435
+ : []),
436
+ ],
437
+ };
438
+ context.addDevice(res);
439
+ }
440
+ catch (error) {
441
+ // One malformed/leftover data point (e.g. a state object without "common", as can happen
442
+ // with data points from a much older adapter version) must not hide every other device
443
+ this.adapter.log.warn(`Cannot load device ${device._id} into the Device Manager: ${error}`);
444
+ }
437
445
  }
438
446
  }
439
447
  async getDeviceDetails(deviceId) {
@@ -661,11 +669,11 @@ class SonoffDeviceManagement extends dm_utils_1.DeviceManagement {
661
669
  buildControls(shortDeviceId, prefix) {
662
670
  const controls = [];
663
671
  const used = new Set();
664
- const ownStates = [];
672
+ const rawStates = [];
665
673
  for (const id in this.objects) {
666
674
  if (id.startsWith(prefix) && this.objects[id].type === 'state') {
667
675
  const suffix = id.substring(prefix.length);
668
- ownStates.push({
676
+ rawStates.push({
669
677
  id,
670
678
  suffix,
671
679
  name: flatName(suffix),
@@ -673,6 +681,19 @@ class SonoffDeviceManagement extends dm_utils_1.DeviceManagement {
673
681
  });
674
682
  }
675
683
  }
684
+ // Two state objects can flatten to the same name at once - e.g. a bare "POWER" next to a
685
+ // leftover "RESULT.POWER" from before "Create object tree" was toggled. Both would end up as
686
+ // a control with the same ID, which the Device Manager rejects as a duplicate - so only the
687
+ // most recently updated copy of each name is kept (a leftover stops being updated, so its
688
+ // timestamp falls behind, the same fallback rule as `getPowerEntries` uses).
689
+ const byName = new Map();
690
+ for (const state of rawStates) {
691
+ const existing = byName.get(state.name);
692
+ if (!existing || (this.states[state.id]?.ts ?? 0) > (this.states[existing.id]?.ts ?? 0)) {
693
+ byName.set(state.name, state);
694
+ }
695
+ }
696
+ const ownStates = [...byName.values()];
676
697
  const stateHandler = (fullId) => async (_deviceId, _actionId, state) => {
677
698
  await this.adapter.setForeignStateAsync(fullId, state);
678
699
  return { val: state, ts: Date.now(), ack: true };
@@ -766,7 +787,7 @@ class SonoffDeviceManagement extends dm_utils_1.DeviceManagement {
766
787
  // mode, ...) as buttons
767
788
  for (const pass of ['value', 'button']) {
768
789
  for (const state of ownStates) {
769
- if (used.has(state.id) || skipRe.test(state.name) || state.common?.write === false) {
790
+ if (used.has(state.id) || skipRe.test(state.name) || !state.common || state.common.write === false) {
770
791
  continue;
771
792
  }
772
793
  const type = state.common.type;
@@ -1 +1 @@
1
- {"version":3,"file":"deviceManager.js","sourceRoot":"","sources":["../../src/lib/deviceManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { I18n } from '@iobroker/adapter-core';\nimport {\n DeviceManagement,\n type ActionContext,\n type ConfigItemAny,\n type ControlState,\n type DeviceControl,\n type DeviceDetails,\n type DeviceInfo,\n type DeviceLoadContext,\n type DeviceRefresh,\n type InstanceDetails,\n} from '@iobroker/dm-utils';\n// Type only - the adapter gives access to the running MQTT server/bridge, see `handleRecreateDevice`\nimport type { SonoffAdapter } from '../main';\n\n/** Icon shown for every device, since the adapter does not ship per-model icons */\nconst DEVICE_ICON = 'adapter/sonoff/admin/sonoff.png';\n\n/** Group metadata: display name key (for i18n) */\nconst groupMeta: Record<string, { nameKey: string }> = {\n relay: { nameKey: 'Relays & Switches' },\n light: { nameKey: 'Lights & Dimmers' },\n cover: { nameKey: 'Shutters & Covers' },\n zigbee: { nameKey: 'Zigbee bridges' },\n meter: { nameKey: 'Energy Meters' },\n sensor: { nameKey: 'Sensors' },\n other: { nameKey: 'Other' },\n};\n\n/** Well-known sensor data points shown on the main tile */\nconst SENSOR_ITEMS: { stateId: string; label: string; unit: string; digits?: number }[] = [\n { stateId: 'Temperature', label: 'Temperature', unit: '°C', digits: 1 },\n { stateId: 'Humidity', label: 'Humidity', unit: '%', digits: 1 },\n { stateId: 'Pressure', label: 'Pressure', unit: 'hPa', digits: 1 },\n { stateId: 'Illuminance', label: 'Illuminance', unit: 'lx', digits: 0 },\n { stateId: 'CarbonDioxide', label: 'CO2', unit: 'ppm', digits: 0 },\n { stateId: 'TVOC', label: 'TVOC', unit: 'ppb', digits: 0 },\n { stateId: 'DewPoint', label: 'Dew point', unit: '°C', digits: 1 },\n];\n\n/**\n * Power-metering data points, keyed by their data point name (see `lib/datapoints.js`). Sonoff/Tasmota\n * devices have no fixed \"model\" that tells us which power values to expect, and unlike the built-in\n * `ENERGY.*` group, bridged external meters (SML smart-meter heads, PZEM sensors, ...) publish the same\n * data points nested under their own custom group name, e.g. `SML_Total_in`. So instead of only looking\n * at one fixed state ID, every state belonging to a device is matched against this table by its data\n * point name (the last path segment) - wherever it is found, the same way ioBroker.shelly reads its\n * power values from whichever states actually exist on a device (see shelly PR #1562).\n */\nconst POWER_METRIC_LABELS: Record<string, { label: string; unit: string; order: number }> = {\n Power: { label: 'Power', unit: 'W', order: 0 },\n Power_curr: { label: 'Power', unit: 'W', order: 0 },\n Leistung: { label: 'Power', unit: 'W', order: 0 },\n ApparentPower: { label: 'Apparent power', unit: 'VA', order: 1 },\n ReactivePower: { label: 'Reactive power', unit: 'var', order: 2 },\n Voltage: { label: 'Voltage', unit: 'V', order: 3 },\n Spannung: { label: 'Voltage', unit: 'V', order: 3 },\n Current: { label: 'Current', unit: 'A', order: 4 },\n Strom: { label: 'Current', unit: 'A', order: 4 },\n CurrentNeutral: { label: 'Neutral current', unit: 'A', order: 5 },\n Factor: { label: 'Power factor', unit: '', order: 6 },\n Frequency: { label: 'Frequency', unit: 'Hz', order: 7 },\n Frequenz: { label: 'Frequency', unit: 'Hz', order: 7 },\n Today: { label: 'Today', unit: 'kWh', order: 8 },\n heute: { label: 'Today', unit: 'kWh', order: 8 },\n TodaySumImport: { label: 'Today (import)', unit: 'kWh', order: 8 },\n TodaySumExport: { label: 'Today (export)', unit: 'kWh', order: 9 },\n Yesterday: { label: 'Yesterday', unit: 'kWh', order: 10 },\n gestern: { label: 'Yesterday', unit: 'kWh', order: 10 },\n Total: { label: 'Total', unit: 'kWh', order: 11 },\n Total_in: { label: 'Total (import)', unit: 'kWh', order: 11 },\n Total_out: { label: 'Total (export)', unit: 'kWh', order: 12 },\n ExportActive: { label: 'Returned energy', unit: 'kWh', order: 12 },\n PowerLow: { label: 'Power low threshold', unit: 'W', order: 14 },\n PowerHigh: { label: 'Power high threshold', unit: 'W', order: 15 },\n PowerDelta: { label: 'Power delta threshold', unit: 'W', order: 16 },\n};\n\n/** Fields taken from `INFO.*` (filled from Tasmota's `StatusNET`/`StatusFWR`) for the details panel */\nconst INFO_ITEMS: { stateId: string; label: string; copy?: boolean }[] = [\n { stateId: 'Module', label: 'Model' },\n { stateId: 'Hostname', label: 'Hostname' },\n { stateId: 'IPAddress', label: 'IP address', copy: true },\n { stateId: 'Mac', label: 'MAC address', copy: true },\n { stateId: 'Gateway', label: 'Gateway' },\n { stateId: 'Subnetmask', label: 'Subnet mask' },\n { stateId: 'Version', label: 'Firmware version' },\n { stateId: 'Hardware', label: 'Hardware' },\n { stateId: 'BuildDateTime', label: 'Build date' },\n { stateId: 'Core', label: 'Core' },\n { stateId: 'SDK', label: 'SDK' },\n];\n\n/**\n * Splits a state suffix into the group it belongs to and the data point name, e.g.\n * \"ENERGY.Voltage\" -> { channel: \"ENERGY\", key: \"Voltage\" }, \"SML_Total_in\" (a bridged external\n * meter nested under a custom \"SML\" group) -> { channel: \"SML\", key: \"Total_in\" }, and\n * \"SENSOR.ENERGY.Power\" (nested two levels deep with the \"Create object tree\" option enabled) ->\n * { channel: \"SENSOR.ENERGY\", key: \"Power\" }. A bare data point without any group, e.g. \"Voltage\",\n * resolves to `{ channel: \"\", key: \"Voltage\" }`. Returns `undefined` if the suffix's data point name\n * (last path segment, or underscore-joined tail) isn't one of `knownKeys`.\n *\n * @param suffix state ID without the device prefix\n * @param knownKeys data point names to match against, e.g. the keys of `POWER_METRIC_LABELS`\n */\nfunction splitDataPoint(suffix: string, knownKeys: readonly string[]): { channel: string; key: string } | undefined {\n const lastDotIdx = suffix.lastIndexOf('.');\n if (lastDotIdx > -1) {\n const key = suffix.substring(lastDotIdx + 1);\n return knownKeys.includes(key) ? { channel: suffix.substring(0, lastDotIdx), key } : undefined;\n }\n if (knownKeys.includes(suffix)) {\n return { channel: '', key: suffix };\n }\n for (const key of knownKeys) {\n if (suffix.endsWith(`_${key}`)) {\n return { channel: suffix.substring(0, suffix.length - key.length - 1), key };\n }\n }\n return undefined;\n}\n\n/**\n * A channel's leading \"SENSOR.\"/\"STATE.\"/\"RESULT.\"/\"WAKEUP.\" segment is only there because the\n * \"Create object tree\" (OBJ_TREE) option is/was enabled - it does not identify a different meter.\n * Stripping it gives the channel identity that is stable across that setting, so the same physical\n * meter (e.g. the built-in ENERGY group) is recognized as one meter even if its states exist twice,\n * once from before and once from after the option was toggled.\n */\nfunction canonicalizeChannel(channel: string): string {\n return channel.replace(/^(SENSOR|STATE|RESULT|WAKEUP)\\./, '');\n}\n\n/**\n * Whether a (raw, non-canonicalized) channel was nested under one of the topic-level groups created\n * by the \"Create object tree\" (OBJ_TREE) adapter option, e.g. \"SENSOR.ENERGY\" or \"STATE.Wifi\".\n */\nfunction isObjTreeStyleChannel(channel: string): boolean {\n return /^(SENSOR|STATE|RESULT|WAKEUP)\\./.test(channel);\n}\n\n/**\n * The suffix of a state as it looks with the \"Create object tree\" (OBJ_TREE) option turned off. That\n * option changes nothing about which data points a device has, it only moves them into a folder per\n * MQTT topic group and nests the payload path instead of joining it with \"_\":\n *\n * | OBJ_TREE on | OBJ_TREE off | flatName |\n * | ---------------------------- | ---------------------- | -------------------- |\n * | `RESULT.Dimmer` | `Dimmer` | `Dimmer` |\n * | `RESULT.Shutter1.Position` | `Shutter1_Position` | `Shutter1_Position` |\n * | `STATE.Wifi.RSSI` | `Wifi_RSSI` | `Wifi_RSSI` |\n * | `SENSOR.AM2301.Temperature` | `AM2301_Temperature` | `AM2301_Temperature` |\n *\n * Both settings therefore end up at the same name, so everything that recognizes a device by its data\n * points (see `getDeviceGroup` and `buildControls`) works the same with and without the option.\n */\nfunction flatName(suffix: string): string {\n return suffix.replace(/^(SENSOR|STATE|RESULT|WAKEUP)\\./, '').replace(/\\./g, '_');\n}\n\n/**\n * Data point names that make a device a sensor. Matched against the flattened suffix\n * (see `flatName`) either as the whole name or as its \"_\"-separated tail, because Tasmota puts the\n * name of the reporting sensor in front of the reading, e.g. \"AM2301_Temperature\".\n */\nconst SENSOR_KEYS = [\n 'Temperature',\n 'Humidity',\n 'Pressure',\n 'Illuminance',\n 'CarbonDioxide',\n 'TVOC',\n 'eCO2',\n 'DewPoint',\n 'AirQuality',\n 'PM2_5',\n 'PM10',\n 'UvIndex',\n 'Distance',\n 'Noise',\n];\n\n/**\n * DeviceManager Class\n */\nexport default class SonoffDeviceManagement extends DeviceManagement<SonoffAdapter> {\n private readonly ready: Promise<void>;\n private readonly states: { [id: string]: ioBroker.State } = {};\n private readonly objects: { [id: string]: ioBroker.ChannelObject | ioBroker.StateObject } = {};\n\n constructor(adapter: SonoffAdapter) {\n super(adapter);\n\n // Initialize i18n\n this.ready = I18n.init(__dirname, adapter)\n .catch(error => this.adapter.log.error(`Cannot initialize i18n: ${error}`))\n .then(() => this.init());\n }\n\n private async init(): Promise<void> {\n const channels = await this.adapter.getChannelsOfAsync();\n const stateObjects = await this.adapter.getStatesOfAsync();\n const states = await this.adapter.getStatesAsync('*');\n\n for (const channel of channels) {\n this.objects[channel._id] = channel;\n }\n for (const state of stateObjects) {\n this.objects[state._id] = state;\n }\n for (const id in states) {\n this.states[id] = states[id];\n }\n\n await this.adapter.subscribeStatesAsync('*');\n await this.adapter.subscribeObjectsAsync('*');\n }\n\n protected getInstanceInfo(): InstanceDetails {\n return {\n apiVersion: 'v3',\n smallCards: true,\n };\n }\n\n public onStateChange(id: string, state: ioBroker.State | null): void {\n if (state) {\n if (!this.states[id] || this.states[id].val !== state.val) {\n // trigger DM update\n this.states[id] = state;\n }\n } else if (this.states[id]) {\n // trigger DM update\n delete this.states[id];\n }\n }\n\n public onObjectChange(id: string, obj: ioBroker.ChannelObject | ioBroker.StateObject | null): void {\n if (obj) {\n this.objects[id] = obj;\n } else {\n delete this.objects[id];\n }\n }\n\n /**\n * Collects the short suffixes (state ID without the device prefix) of all currently known\n * objects that belong to one device, e.g. \"POWER1\", \"ENERGY.Voltage\" or \"Shutter1_Position\".\n *\n * @param prefix `<namespace>.<deviceId>.`\n */\n private getSuffixes(prefix: string): Set<string> {\n const suffixes = new Set<string>();\n for (const id in this.objects) {\n if (id.startsWith(prefix)) {\n suffixes.add(id.substring(prefix.length));\n }\n }\n return suffixes;\n }\n\n /**\n * Finds a state whose data point name (its last path segment) is `key`, regardless of which group\n * it is nested under - a data point can end up at very different state IDs depending on the\n * \"Create object tree\" (OBJ_TREE) adapter option and on which MQTT topic published it, e.g. RSSI is\n * \"Wifi_RSSI\" with OBJ_TREE off but \"STATE.Wifi.RSSI\" with it on. Returns the matching suffix (state\n * ID without the device prefix), or `undefined` if the device has no such data point.\n *\n * A key can exist under more than one group at once, e.g. Tasmota echoes \"Uptime\" as part of a\n * one-off command response (\"RESULT.Uptime\") in addition to reporting it with every regular\n * \"STATE\" telemetry message - only the latter keeps being updated, so when both exist, the periodic\n * telemetry groups (STATE, then SENSOR) are preferred over anything else.\n *\n * @param prefix `<namespace>.<deviceId>.`\n * @param key data point name, e.g. \"RSSI\" or \"Temperature\"\n */\n private findDataPointSuffix(prefix: string, key: string): string | undefined {\n const matches: string[] = [];\n for (const stateId of Object.keys(this.states)) {\n if (!stateId.startsWith(prefix)) {\n continue;\n }\n const suffix = stateId.substring(prefix.length);\n if (suffix === key || suffix.endsWith(`.${key}`) || suffix.endsWith(`_${key}`)) {\n matches.push(suffix);\n }\n }\n if (matches.length > 1) {\n for (const group of ['STATE', 'SENSOR']) {\n const preferred = matches.find(s => s.startsWith(`${group}.`));\n if (preferred) {\n return preferred;\n }\n }\n }\n return matches[0];\n }\n\n /**\n * Splits a state suffix into the group it belongs to and the power-metering data point name.\n * Thin wrapper around `splitDataPoint` fixed to the keys of `POWER_METRIC_LABELS`, see there.\n *\n * @param suffix state ID without the device prefix\n */\n private splitPowerSuffix(suffix: string): { channel: string; key: string } | undefined {\n return splitDataPoint(suffix, Object.keys(POWER_METRIC_LABELS));\n }\n\n /**\n * Human-readable label for a (canonicalized) power-metering channel, used when a device has more\n * than one meter and each one needs to be told apart. The built-in Tasmota group is translated\n * (\"ENERGY\" -> \"Power\"/\"Leistung\"/...); a bridged external meter has no fixed translation - its\n * group name comes directly from the MQTT payload (e.g. \"SML\", \"PZEM\"), so it is shown as-is.\n */\n private channelLabel(channel: string): ioBroker.StringOrTranslated {\n if (channel === 'ENERGY') {\n return I18n.getTranslatedObject('Power');\n }\n // Not a second meter, but the alarm thresholds Tasmota reports for the built-in one\n if (channel === 'MARGINS') {\n return I18n.getTranslatedObject('Thresholds');\n }\n return channel.replace(/[._]/g, ' ');\n }\n\n /**\n * Finds all power-metering data points of a device, wherever they are: in the built-in `ENERGY.*`\n * group, in a custom group of a bridged external meter, or as a bare top-level data point.\n *\n * The same reading can end up under more than one state ID for the same meter and metric (channel +\n * label): toggling \"Create object tree\" moves a data point without deleting the old one, and some\n * Tasmota firmware reports energy readings both nested inside its periodic \"SENSOR\" telemetry *and*\n * via its own dedicated \"ENERGY\" topic (both handled by `mqttBase.ts`, see there). Whenever a copy\n * nested under SENSOR/STATE/RESULT/WAKEUP (see `isObjTreeStyleChannel`) exists, it is the\n * authoritative one - a device that also sends its own dedicated topic still sends the regular\n * telemetry that ends up nested, so the bare, non-nested copy is always redundant when a nested one\n * exists. Only as a fallback, when no nested copy exists (or several do, e.g. two aliased data point\n * names), the entry whose state was updated most recently wins - stale leftovers stop being updated,\n * so their timestamp falls behind.\n *\n * @param prefix `<namespace>.<deviceId>.`\n */\n private getPowerEntries(\n prefix: string,\n ): { suffix: string; channel: string; key: string; label: string; unit: string; order: number }[] {\n const entries: { suffix: string; channel: string; key: string; label: string; unit: string; order: number }[] =\n [];\n for (const stateId of Object.keys(this.states)) {\n if (!stateId.startsWith(prefix)) {\n continue;\n }\n const suffix = stateId.substring(prefix.length);\n const common = (this.objects[stateId] as ioBroker.StateObject | undefined)?.common;\n if (!common || common.write === true) {\n continue;\n }\n const split = this.splitPowerSuffix(suffix);\n if (!split) {\n continue;\n }\n const meta = POWER_METRIC_LABELS[split.key];\n entries.push({\n suffix,\n channel: split.channel,\n key: split.key,\n label: meta.label,\n unit: meta.unit,\n order: meta.order,\n });\n }\n\n const byMeter = new Map<string, typeof entries>();\n for (const entry of entries) {\n const groupKey = `${canonicalizeChannel(entry.channel)} ${entry.label}`;\n const group = byMeter.get(groupKey);\n if (group) {\n group.push(entry);\n } else {\n byMeter.set(groupKey, [entry]);\n }\n }\n\n const deduped: typeof entries = [];\n for (const group of byMeter.values()) {\n const nested = group.filter(e => isObjTreeStyleChannel(e.channel));\n const candidates = nested.length ? nested : group;\n const preferred = candidates.reduce((newest, entry) => {\n const newestTs = this.states[`${prefix}${newest.suffix}`]?.ts ?? 0;\n const entryTs = this.states[`${prefix}${entry.suffix}`]?.ts ?? 0;\n return entryTs > newestTs ? entry : newest;\n });\n deduped.push({ ...preferred, channel: canonicalizeChannel(preferred.channel) });\n }\n\n return deduped.sort((a, b) => a.channel.localeCompare(b.channel) || a.order - b.order);\n }\n\n /**\n * Load all sonoff/Tasmota devices\n *\n * @param context context used to report the found devices to the backend\n */\n async loadDevices(context: DeviceLoadContext<string>): Promise<void> {\n // Wait that i18n is initialized\n await this.ready;\n\n const ns = this.adapter.namespace;\n\n for (const deviceId in this.objects) {\n const device = this.objects[deviceId];\n if (device.type !== 'channel') {\n continue;\n }\n const shortDeviceId = device._id.substring(ns.length + 1);\n // Only direct children of the namespace are devices, \"info\" is the adapter's own channel\n if (!shortDeviceId || shortDeviceId.includes('.') || shortDeviceId === 'info') {\n continue;\n }\n\n const prefix = `${device._id}.`;\n const suffixes = this.getSuffixes(prefix);\n\n const alive = this.states[`${device._id}.alive`]?.val === true;\n const hostname = this.states[`${prefix}INFO.Hostname`]?.val as string | undefined;\n const ip = this.states[`${prefix}INFO.IPAddress`]?.val as string | undefined;\n const model = (this.states[`${prefix}INFO.Module`]?.val as string) || undefined;\n const rssiSuffix = this.findDataPointSuffix(prefix, 'RSSI');\n const rssi = rssiSuffix ? (this.states[`${prefix}${rssiSuffix}`]?.val as number | undefined) : undefined;\n const batterySuffix = this.findDataPointSuffix(prefix, 'BatteryPercentage');\n const battery = batterySuffix\n ? (this.states[`${prefix}${batterySuffix}`]?.val as number | undefined)\n : undefined;\n const clientId = (device.native as { clientId?: string } | undefined)?.clientId;\n const group = this.getDeviceGroup(suffixes);\n\n const res: DeviceInfo<string> = {\n id: device._id,\n identifier: hostname || ip || clientId || undefined,\n name: device.common.name,\n icon: DEVICE_ICON,\n color: !alive ? '#fff' : undefined,\n backgroundColor: !alive ? '#f44336' : undefined,\n group,\n model,\n status: {\n connection: alive ? 'connected' : 'disconnected',\n rssi,\n battery,\n },\n hasDetails: true,\n customInfo: this.buildCustomInfo(device._id, prefix),\n controls: this.buildControls(shortDeviceId, prefix),\n actions: [\n {\n id: 'rename',\n icon: 'edit',\n description: I18n.getTranslatedObject('Rename this device'),\n handler: async (\n deviceId: string,\n context: ActionContext,\n ): Promise<{ refresh: DeviceRefresh }> => await this.handleRenameDevice(deviceId, context),\n },\n {\n id: 'recreate',\n icon: 'refresh',\n description: I18n.getTranslatedObject('Delete and recreate all data points of this device'),\n confirmation: I18n.getTranslatedObject(\n 'This deletes all data points of this device (except its name). They will be recreated automatically the next time the device reports its state. Continue?',\n ),\n handler: async (\n deviceId: string,\n context: ActionContext,\n ): Promise<{ refresh: DeviceRefresh }> => await this.handleRecreateDevice(deviceId, context),\n },\n ...(hostname || ip\n ? [\n {\n id: 'web',\n icon: 'web' as const,\n description: I18n.getTranslatedObject('Open device web interface'),\n url: `http://${hostname || ip}`,\n },\n ]\n : []),\n ],\n };\n\n context.addDevice(res);\n }\n }\n\n async getDeviceDetails(deviceId: string): Promise<DeviceDetails<string> | null> {\n // Wait that i18n is initialized\n await this.ready;\n\n const device = this.objects[deviceId];\n if (device?.type !== 'channel') {\n return null;\n }\n\n const ns = this.adapter.namespace;\n const shortDeviceId = device._id.substring(ns.length + 1);\n const prefix = `${deviceId}.`;\n const items: Record<string, ConfigItemAny> = {};\n\n const clientId = (device.native as { clientId?: string } | undefined)?.clientId;\n items._clientId = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject('Device ID'),\n data: clientId || shortDeviceId,\n addColon: true,\n copyToClipboard: true,\n };\n\n const hostname = this.states[`${prefix}INFO.Hostname`]?.val as string | undefined;\n const ip = this.states[`${prefix}INFO.IPAddress`]?.val as string | undefined;\n if (hostname || ip) {\n items._deviceLink = {\n // @ts-expect-error staticLink is OK\n type: 'staticLink',\n href: `http://${hostname || ip}`,\n label: I18n.getTranslatedObject('Open device web interface'),\n button: true,\n icon: 'web',\n newLine: true,\n };\n }\n\n for (const info of INFO_ITEMS) {\n const val = this.states[`${prefix}INFO.${info.stateId}`]?.val;\n if (val === undefined || val === null || val === '') {\n continue;\n }\n items[`info_${info.stateId}`] = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject(info.label),\n data: val,\n addColon: true,\n copyToClipboard: info.copy,\n };\n }\n\n const rssiSuffix = this.findDataPointSuffix(prefix, 'RSSI');\n const rssi = rssiSuffix ? (this.states[`${prefix}${rssiSuffix}`]?.val as number | undefined) : undefined;\n if (rssi !== undefined) {\n items._rssi = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject('RSSI'),\n data: rssi,\n unit: 'dBm',\n addColon: true,\n };\n }\n\n const uptimeSuffix = this.findDataPointSuffix(prefix, 'Uptime');\n const uptime = uptimeSuffix ? this.states[`${prefix}${uptimeSuffix}`]?.val : undefined;\n if (uptime !== undefined && uptime !== null && uptime !== '') {\n items._uptime = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject('Uptime'),\n data: uptime,\n addColon: true,\n };\n }\n\n // Power metering section – only shown when power data points exist. Bridged external meters\n // (SML, PZEM, ...) are shown as their own sub-section when a device has more than one meter.\n const powerEntries = this.getPowerEntries(prefix);\n if (powerEntries.length) {\n items._energyHeader = {\n type: 'header',\n text: I18n.getTranslatedObject('Power metering'),\n size: 6,\n newLine: true,\n };\n\n const channels = [...new Set(powerEntries.map(e => e.channel))];\n const multiChannel = channels.length > 1;\n\n for (const channel of channels) {\n if (multiChannel && channel) {\n items[`energy_ch_${channel.replace(/[.:]/g, '_')}`] = {\n type: 'staticInfo',\n label: this.channelLabel(channel),\n data: '',\n newLine: true,\n };\n }\n for (const entry of powerEntries.filter(e => e.channel === channel)) {\n const val = this.states[`${prefix}${entry.suffix}`]?.val;\n items[`energy_${entry.suffix.replace(/[.:]/g, '_')}`] = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject(entry.label),\n data: typeof val === 'number' ? Math.round(val * 100) / 100 : String(val ?? '—'),\n unit: entry.unit || undefined,\n addColon: true,\n };\n }\n }\n }\n\n // Digital inputs (physical switches/buttons wired to the device)\n const inputEntries: { suffix: string; label: string }[] = [];\n for (const stateId of Object.keys(this.states)) {\n if (!stateId.startsWith(prefix)) {\n continue;\n }\n const suffix = stateId.substring(prefix.length);\n // Only the last path segment is the actual data point name (may be nested, e.g. \"STATE.Switch1\")\n const lastSegment = suffix.substring(suffix.lastIndexOf('.') + 1);\n const match = lastSegment.match(/^(Switch|Button)(\\d+)$/);\n if (match) {\n inputEntries.push({ suffix, label: `${match[1]} ${match[2]}` });\n }\n }\n if (inputEntries.length) {\n items._inputsHeader = {\n type: 'header',\n text: I18n.getTranslatedObject('Digital inputs'),\n size: 6,\n newLine: true,\n };\n inputEntries.sort((a, b) => a.suffix.localeCompare(b.suffix));\n for (const entry of inputEntries) {\n const val = this.states[`${prefix}${entry.suffix}`]?.val;\n items[`input_${entry.suffix}`] = {\n type: 'staticInfo',\n label: entry.label,\n data: val ? '✓' : '✗',\n addColon: true,\n };\n }\n }\n\n return {\n id: deviceId,\n schema: {\n type: 'panel',\n items,\n },\n };\n }\n\n private getDeviceGroup(suffixes: Set<string>): { key: string; name: ioBroker.StringOrTranslated } {\n const list = [...suffixes].map(flatName);\n const test = (re: RegExp): boolean => list.some(s => re.test(s));\n\n let key = 'other';\n if (test(/^Shutter\\d+_Position$/)) {\n key = 'cover';\n } else if (test(/^(Color|CT|led_basecolor_rgb|led_basecolor_rgbcw|Dimmer|led_dimmer)$/)) {\n key = 'light';\n } else if (test(/^(POWER\\d*|led_enableAll)$/)) {\n key = 'relay';\n } else if (test(/^(ZbReceived_|ZbPower)/)) {\n key = 'zigbee';\n } else if (list.some(s => this.splitPowerSuffix(s) !== undefined)) {\n key = 'meter';\n } else if (list.some(s => SENSOR_KEYS.some(k => s === k || s.endsWith(`_${k}`)))) {\n key = 'sensor';\n }\n\n const meta = groupMeta[key] || groupMeta.other;\n return { key, name: I18n.getTranslatedObject(meta.nameKey) };\n }\n\n private buildCustomInfo(deviceId: string, prefix: string): DeviceDetails<string> | undefined {\n const shortDeviceId = deviceId.substring(this.adapter.namespace.length + 1);\n const items: Record<string, ConfigItemAny> = {};\n\n for (const sensor of SENSOR_ITEMS) {\n const suffix = this.findDataPointSuffix(prefix, sensor.stateId);\n if (suffix !== undefined) {\n items[sensor.stateId] = {\n type: 'state',\n oid: `${shortDeviceId}.${suffix}`,\n control: 'text',\n unit: sensor.unit,\n digits: sensor.digits,\n label: I18n.getTranslatedObject(sensor.label),\n addColon: true,\n style: { opacity: 0.7 },\n };\n }\n }\n\n // Power on the main tile: only the actual \"Power\" (W) readings, never voltage/current/energy\n const powerItems = this.getPowerEntries(prefix).filter(e => e.label === 'Power');\n const multiPower = powerItems.length > 1;\n for (const entry of powerItems) {\n const label = multiPower ? this.channelLabel(entry.channel) : I18n.getTranslatedObject('Power');\n items[`power_${entry.suffix.replace(/[.:]/g, '_')}`] = {\n type: 'state',\n oid: `${shortDeviceId}.${entry.suffix}`,\n control: 'text',\n unit: 'W',\n digits: 1,\n label,\n addColon: true,\n style: { opacity: 0.7 },\n };\n }\n if (powerItems.length) {\n items._powerSpacer = {\n type: 'divider',\n color: 'transparent',\n height: 2,\n };\n }\n\n if (Object.keys(items).length === 0) {\n return undefined;\n }\n\n return {\n id: deviceId,\n schema: {\n type: 'panel',\n items,\n },\n };\n }\n\n /**\n * Builds the controls of a device from its writable data points. Every data point is matched by its\n * flattened name (see `flatName`), so the same control shows up with and without the \"Create object\n * tree\" option. The current values come from the cached states instead of a request per control -\n * the cache holds all states of the adapter anyway (see `init`).\n */\n private buildControls(shortDeviceId: string, prefix: string): DeviceControl<string>[] {\n const controls: DeviceControl<string>[] = [];\n const used = new Set<string>();\n const ownStates: { id: string; suffix: string; name: string; common: ioBroker.StateCommon }[] = [];\n\n for (const id in this.objects) {\n if (id.startsWith(prefix) && this.objects[id].type === 'state') {\n const suffix = id.substring(prefix.length);\n ownStates.push({\n id,\n suffix,\n name: flatName(suffix),\n common: this.objects[id].common,\n });\n }\n }\n\n const stateHandler =\n (fullId: string) =>\n async (_deviceId: string, _actionId: string, state: ControlState): Promise<ioBroker.State> => {\n await this.adapter.setForeignStateAsync(fullId, state);\n return { val: state, ts: Date.now(), ack: true } as ioBroker.State;\n };\n\n const currentState = (fullId: string): ioBroker.State =>\n this.states[fullId] || ({ val: null, ts: Date.now(), ack: true } as ioBroker.State);\n\n // A control is named after the data point it belongs to, not after where that data point is\n // stored, so its ID stays the same when \"Create object tree\" is toggled\n const controlId = (name: string): string => name.replace(/[.:]/g, '_');\n\n // Primary switches: POWER, POWER1..29, Zigbee bridge relays, OpenBeken LED enable\n const switchRe = /^(POWER\\d*|ZbReceived_.+_Power|led_enableAll)$/;\n for (const state of ownStates) {\n if (state.common?.type !== 'boolean' || state.common.write === false || !switchRe.test(state.name)) {\n continue;\n }\n used.add(state.id);\n const label =\n state.name === 'POWER'\n ? 'Power'\n : state.name.startsWith('POWER')\n ? `Power ${state.name.substring(5)}`\n : state.name.replace(/_/g, ' ');\n controls.push({\n id: controlId(state.name),\n type: 'switch',\n stateId: `${shortDeviceId}.${state.suffix}`,\n label: I18n.getTranslatedObject(label),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n }\n\n // Sliders: dimmer, color temperature, shutter position/tilt\n const sliderDefs: {\n re: RegExp;\n label: (m: RegExpMatchArray) => string;\n min: number;\n max: number;\n unit?: string;\n }[] = [\n { re: /^Dimmer$/, label: () => 'Dimmer', min: 0, max: 100, unit: '%' },\n { re: /^led_dimmer$/, label: () => 'Dimmer', min: 0, max: 100, unit: '%' },\n { re: /^CT$/, label: () => 'Color temperature', min: 153, max: 500 },\n { re: /^led_temperature$/, label: () => 'Color temperature', min: 154, max: 500 },\n { re: /^Shutter(\\d+)_Position$/, label: m => `Shutter ${m[1]} position`, min: 0, max: 100, unit: '%' },\n { re: /^Shutter(\\d+)_Tilt$/, label: m => `Shutter ${m[1]} tilt`, min: 0, max: 100, unit: '%' },\n ];\n for (const state of ownStates) {\n if (used.has(state.id) || state.common?.type !== 'number' || state.common.write === false) {\n continue;\n }\n for (const def of sliderDefs) {\n const match = state.name.match(def.re);\n if (!match) {\n continue;\n }\n used.add(state.id);\n controls.push({\n id: controlId(state.name),\n type: 'slider',\n stateId: `${shortDeviceId}.${state.suffix}`,\n min: state.common.min ?? def.min,\n max: state.common.max ?? def.max,\n unit: state.common.unit || def.unit,\n label: I18n.getTranslatedObject(def.label(match)),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n break;\n }\n }\n\n // Color controls\n const colorRe = /^(Color|led_basecolor_rgb|led_basecolor_rgbcw)$/;\n for (const state of ownStates) {\n if (\n used.has(state.id) ||\n state.common?.type !== 'string' ||\n !state.common.write ||\n !colorRe.test(state.name)\n ) {\n continue;\n }\n used.add(state.id);\n controls.push({\n id: controlId(state.name),\n type: 'color',\n stateId: `${shortDeviceId}.${state.suffix}`,\n label: I18n.getTranslatedObject('Color'),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n }\n\n // Everything else: writable settings not shown elsewhere. INFO/ENERGY/MARGINS are read-only\n // anyway, \"Time\" is the timestamp of the telemetry message a group of data points came with\n const skipRe = /^(INFO_|ENERGY_|MARGINS_|Wifi_|Time$|alive$)/;\n let group: DeviceControl<string> | null = {\n id: 'group_settings',\n type: 'group',\n label: I18n.getTranslatedObject('Settings'),\n };\n\n // Writable numbers/strings first, then the leftover booleans (toggles like Fade, LED exor\n // mode, ...) as buttons\n for (const pass of ['value', 'button'] as const) {\n for (const state of ownStates) {\n if (used.has(state.id) || skipRe.test(state.name) || state.common?.write === false) {\n continue;\n }\n const type = state.common.type;\n if (pass === 'value' ? type !== 'number' && type !== 'string' : type !== 'boolean') {\n continue;\n }\n used.add(state.id);\n\n if (group) {\n controls.push(group);\n group = null;\n }\n\n if (pass === 'button') {\n controls.push({\n group: 'group_settings',\n id: controlId(state.name),\n type: 'button',\n stateId: `${shortDeviceId}.${state.suffix}`,\n label: state.name.replace(/_/g, ' '),\n variant: 'outlined',\n handler: stateHandler(state.id),\n });\n continue;\n }\n\n let options: { label: string; value: string }[] | undefined;\n const commonStates = state.common.states;\n if (commonStates) {\n options = Array.isArray(commonStates)\n ? commonStates.map((s: string) => ({ value: s, label: s }))\n : Object.keys(commonStates).map(k => ({\n value: k,\n label: String((commonStates as Record<string, string>)[k]),\n }));\n }\n\n controls.push({\n group: 'group_settings',\n id: controlId(state.name),\n type: options ? 'select' : type === 'number' ? 'number' : 'text',\n stateId: `${shortDeviceId}.${state.suffix}`,\n min: state.common.min,\n max: state.common.max,\n unit: state.common.unit,\n options,\n label: state.name.replace(/_/g, ' '),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n }\n }\n\n return controls;\n }\n\n /**\n * @param id ID to rename\n * @param context context sent from backend\n */\n async handleRenameDevice(id: string, context: ActionContext): Promise<{ refresh: DeviceRefresh }> {\n const result = await context.showForm(\n {\n type: 'panel',\n items: {\n newName: {\n type: 'text',\n trim: false,\n placeholder: '',\n },\n },\n },\n {\n data: {\n newName: '',\n },\n title: I18n.getTranslatedObject('Enter new name'),\n },\n );\n if (result?.newName === undefined || result?.newName === '') {\n return { refresh: 'none' };\n }\n const obj = {\n common: {\n name: result.newName,\n },\n };\n\n const res = await this.adapter.extendForeignObjectAsync(id, obj);\n if (res === null) {\n this.adapter.log.warn(`Can not rename device ${id}: ${JSON.stringify(res)}`);\n return { refresh: 'none' };\n }\n return { refresh: 'device' as DeviceRefresh };\n }\n\n /**\n * Deletes every data point of a device - its channel object (and therefore a custom name set via\n * `rename`) is kept, everything else gets recreated automatically the next time the device reports\n * its state. This is a manual, user-triggered fix for data points stuck in an outdated structure,\n * e.g. leftovers from before the \"Create object tree\" adapter option was changed.\n *\n * `alive` is kept as well: it is not reported by the device but written by the adapter when the\n * MQTT client connects (see `createClient` in `mqttBase.ts`), so a deleted `alive` would only come\n * back the next time the device reconnects - which can take days for a device that is simply up.\n *\n * @param id device (channel) ID to recreate\n * @param _context unused - the destructive confirmation is handled declaratively by the action itself\n */\n async handleRecreateDevice(id: string, _context: ActionContext): Promise<{ refresh: DeviceRefresh }> {\n const prefix = `${id}.`;\n let removed = 0;\n for (const stateId in this.objects) {\n if (!stateId.startsWith(prefix) || this.objects[stateId].type !== 'state') {\n continue;\n }\n if (stateId === `${prefix}alive`) {\n continue;\n }\n try {\n await this.adapter.delForeignStateAsync(stateId);\n await this.adapter.delForeignObjectAsync(stateId);\n delete this.objects[stateId];\n delete this.states[stateId];\n removed++;\n } catch (error) {\n this.adapter.log.warn(`Cannot remove data point ${stateId}: ${error}`);\n }\n }\n\n // The server/bridge remembers which objects it has already created and would not create them a\n // second time within this adapter run, so the deleted data points have to be forgotten there\n // too - otherwise they only come back after a restart of the adapter\n this.adapter.server?.forgetObjects(id);\n\n this.adapter.log.info(`Removed ${removed} data point(s) of ${id}, they will be recreated automatically`);\n return { refresh: 'device' as DeviceRefresh };\n }\n}\n"]}
1
+ {"version":3,"file":"deviceManager.js","sourceRoot":"","sources":["../../src/lib/deviceManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { I18n } from '@iobroker/adapter-core';\nimport {\n DeviceManagement,\n type ActionContext,\n type ConfigItemAny,\n type ControlState,\n type DeviceControl,\n type DeviceDetails,\n type DeviceInfo,\n type DeviceLoadContext,\n type DeviceRefresh,\n type InstanceDetails,\n} from '@iobroker/dm-utils';\n// Type only - the adapter gives access to the running MQTT server/bridge, see `handleRecreateDevice`\nimport type { SonoffAdapter } from '../main';\n\n/** Icon shown for every device, since the adapter does not ship per-model icons */\nconst DEVICE_ICON = 'adapter/sonoff/admin/sonoff.png';\n\n/** Group metadata: display name key (for i18n) */\nconst groupMeta: Record<string, { nameKey: string }> = {\n relay: { nameKey: 'Relays & Switches' },\n light: { nameKey: 'Lights & Dimmers' },\n cover: { nameKey: 'Shutters & Covers' },\n zigbee: { nameKey: 'Zigbee bridges' },\n meter: { nameKey: 'Energy Meters' },\n sensor: { nameKey: 'Sensors' },\n other: { nameKey: 'Other' },\n};\n\n/** Well-known sensor data points shown on the main tile */\nconst SENSOR_ITEMS: { stateId: string; label: string; unit: string; digits?: number }[] = [\n { stateId: 'Temperature', label: 'Temperature', unit: '°C', digits: 1 },\n { stateId: 'Humidity', label: 'Humidity', unit: '%', digits: 1 },\n { stateId: 'Pressure', label: 'Pressure', unit: 'hPa', digits: 1 },\n { stateId: 'Illuminance', label: 'Illuminance', unit: 'lx', digits: 0 },\n { stateId: 'CarbonDioxide', label: 'CO2', unit: 'ppm', digits: 0 },\n { stateId: 'TVOC', label: 'TVOC', unit: 'ppb', digits: 0 },\n { stateId: 'DewPoint', label: 'Dew point', unit: '°C', digits: 1 },\n];\n\n/**\n * Power-metering data points, keyed by their data point name (see `lib/datapoints.js`). Sonoff/Tasmota\n * devices have no fixed \"model\" that tells us which power values to expect, and unlike the built-in\n * `ENERGY.*` group, bridged external meters (SML smart-meter heads, PZEM sensors, ...) publish the same\n * data points nested under their own custom group name, e.g. `SML_Total_in`. So instead of only looking\n * at one fixed state ID, every state belonging to a device is matched against this table by its data\n * point name (the last path segment) - wherever it is found, the same way ioBroker.shelly reads its\n * power values from whichever states actually exist on a device (see shelly PR #1562).\n */\nconst POWER_METRIC_LABELS: Record<string, { label: string; unit: string; order: number }> = {\n Power: { label: 'Power', unit: 'W', order: 0 },\n Power_curr: { label: 'Power', unit: 'W', order: 0 },\n Leistung: { label: 'Power', unit: 'W', order: 0 },\n ApparentPower: { label: 'Apparent power', unit: 'VA', order: 1 },\n ReactivePower: { label: 'Reactive power', unit: 'var', order: 2 },\n Voltage: { label: 'Voltage', unit: 'V', order: 3 },\n Spannung: { label: 'Voltage', unit: 'V', order: 3 },\n Current: { label: 'Current', unit: 'A', order: 4 },\n Strom: { label: 'Current', unit: 'A', order: 4 },\n CurrentNeutral: { label: 'Neutral current', unit: 'A', order: 5 },\n Factor: { label: 'Power factor', unit: '', order: 6 },\n Frequency: { label: 'Frequency', unit: 'Hz', order: 7 },\n Frequenz: { label: 'Frequency', unit: 'Hz', order: 7 },\n Today: { label: 'Today', unit: 'kWh', order: 8 },\n heute: { label: 'Today', unit: 'kWh', order: 8 },\n TodaySumImport: { label: 'Today (import)', unit: 'kWh', order: 8 },\n TodaySumExport: { label: 'Today (export)', unit: 'kWh', order: 9 },\n Yesterday: { label: 'Yesterday', unit: 'kWh', order: 10 },\n gestern: { label: 'Yesterday', unit: 'kWh', order: 10 },\n Total: { label: 'Total', unit: 'kWh', order: 11 },\n Total_in: { label: 'Total (import)', unit: 'kWh', order: 11 },\n Total_out: { label: 'Total (export)', unit: 'kWh', order: 12 },\n ExportActive: { label: 'Returned energy', unit: 'kWh', order: 12 },\n PowerLow: { label: 'Power low threshold', unit: 'W', order: 14 },\n PowerHigh: { label: 'Power high threshold', unit: 'W', order: 15 },\n PowerDelta: { label: 'Power delta threshold', unit: 'W', order: 16 },\n};\n\n/** Fields taken from `INFO.*` (filled from Tasmota's `StatusNET`/`StatusFWR`) for the details panel */\nconst INFO_ITEMS: { stateId: string; label: string; copy?: boolean }[] = [\n { stateId: 'Module', label: 'Model' },\n { stateId: 'Hostname', label: 'Hostname' },\n { stateId: 'IPAddress', label: 'IP address', copy: true },\n { stateId: 'Mac', label: 'MAC address', copy: true },\n { stateId: 'Gateway', label: 'Gateway' },\n { stateId: 'Subnetmask', label: 'Subnet mask' },\n { stateId: 'Version', label: 'Firmware version' },\n { stateId: 'Hardware', label: 'Hardware' },\n { stateId: 'BuildDateTime', label: 'Build date' },\n { stateId: 'Core', label: 'Core' },\n { stateId: 'SDK', label: 'SDK' },\n];\n\n/**\n * Splits a state suffix into the group it belongs to and the data point name, e.g.\n * \"ENERGY.Voltage\" -> { channel: \"ENERGY\", key: \"Voltage\" }, \"SML_Total_in\" (a bridged external\n * meter nested under a custom \"SML\" group) -> { channel: \"SML\", key: \"Total_in\" }, and\n * \"SENSOR.ENERGY.Power\" (nested two levels deep with the \"Create object tree\" option enabled) ->\n * { channel: \"SENSOR.ENERGY\", key: \"Power\" }. A bare data point without any group, e.g. \"Voltage\",\n * resolves to `{ channel: \"\", key: \"Voltage\" }`. Returns `undefined` if the suffix's data point name\n * (last path segment, or underscore-joined tail) isn't one of `knownKeys`.\n *\n * @param suffix state ID without the device prefix\n * @param knownKeys data point names to match against, e.g. the keys of `POWER_METRIC_LABELS`\n */\nfunction splitDataPoint(suffix: string, knownKeys: readonly string[]): { channel: string; key: string } | undefined {\n const lastDotIdx = suffix.lastIndexOf('.');\n if (lastDotIdx > -1) {\n const key = suffix.substring(lastDotIdx + 1);\n return knownKeys.includes(key) ? { channel: suffix.substring(0, lastDotIdx), key } : undefined;\n }\n if (knownKeys.includes(suffix)) {\n return { channel: '', key: suffix };\n }\n for (const key of knownKeys) {\n if (suffix.endsWith(`_${key}`)) {\n return { channel: suffix.substring(0, suffix.length - key.length - 1), key };\n }\n }\n return undefined;\n}\n\n/**\n * A channel's leading \"SENSOR.\"/\"STATE.\"/\"RESULT.\"/\"WAKEUP.\" segment is only there because the\n * \"Create object tree\" (OBJ_TREE) option is/was enabled - it does not identify a different meter.\n * Stripping it gives the channel identity that is stable across that setting, so the same physical\n * meter (e.g. the built-in ENERGY group) is recognized as one meter even if its states exist twice,\n * once from before and once from after the option was toggled.\n */\nfunction canonicalizeChannel(channel: string): string {\n return channel.replace(/^(SENSOR|STATE|RESULT|WAKEUP)\\./, '');\n}\n\n/**\n * Whether a (raw, non-canonicalized) channel was nested under one of the topic-level groups created\n * by the \"Create object tree\" (OBJ_TREE) adapter option, e.g. \"SENSOR.ENERGY\" or \"STATE.Wifi\".\n */\nfunction isObjTreeStyleChannel(channel: string): boolean {\n return /^(SENSOR|STATE|RESULT|WAKEUP)\\./.test(channel);\n}\n\n/**\n * The suffix of a state as it looks with the \"Create object tree\" (OBJ_TREE) option turned off. That\n * option changes nothing about which data points a device has, it only moves them into a folder per\n * MQTT topic group and nests the payload path instead of joining it with \"_\":\n *\n * | OBJ_TREE on | OBJ_TREE off | flatName |\n * | ---------------------------- | ---------------------- | -------------------- |\n * | `RESULT.Dimmer` | `Dimmer` | `Dimmer` |\n * | `RESULT.Shutter1.Position` | `Shutter1_Position` | `Shutter1_Position` |\n * | `STATE.Wifi.RSSI` | `Wifi_RSSI` | `Wifi_RSSI` |\n * | `SENSOR.AM2301.Temperature` | `AM2301_Temperature` | `AM2301_Temperature` |\n *\n * Both settings therefore end up at the same name, so everything that recognizes a device by its data\n * points (see `getDeviceGroup` and `buildControls`) works the same with and without the option.\n */\nfunction flatName(suffix: string): string {\n return suffix.replace(/^(SENSOR|STATE|RESULT|WAKEUP)\\./, '').replace(/\\./g, '_');\n}\n\n/**\n * Data point names that make a device a sensor. Matched against the flattened suffix\n * (see `flatName`) either as the whole name or as its \"_\"-separated tail, because Tasmota puts the\n * name of the reporting sensor in front of the reading, e.g. \"AM2301_Temperature\".\n */\nconst SENSOR_KEYS = [\n 'Temperature',\n 'Humidity',\n 'Pressure',\n 'Illuminance',\n 'CarbonDioxide',\n 'TVOC',\n 'eCO2',\n 'DewPoint',\n 'AirQuality',\n 'PM2_5',\n 'PM10',\n 'UvIndex',\n 'Distance',\n 'Noise',\n];\n\n/**\n * DeviceManager Class\n */\nexport default class SonoffDeviceManagement extends DeviceManagement<SonoffAdapter> {\n private readonly ready: Promise<void>;\n private readonly states: { [id: string]: ioBroker.State } = {};\n private readonly objects: { [id: string]: ioBroker.ChannelObject | ioBroker.StateObject } = {};\n\n constructor(adapter: SonoffAdapter) {\n super(adapter);\n\n // Initialize i18n\n this.ready = I18n.init(__dirname, adapter)\n .catch(error => this.adapter.log.error(`Cannot initialize i18n: ${error}`))\n .then(() => this.init());\n }\n\n private async init(): Promise<void> {\n const channels = await this.adapter.getChannelsOfAsync();\n const stateObjects = await this.adapter.getStatesOfAsync();\n const states = await this.adapter.getStatesAsync('*');\n\n for (const channel of channels) {\n this.objects[channel._id] = channel;\n }\n for (const state of stateObjects) {\n this.objects[state._id] = state;\n }\n for (const id in states) {\n this.states[id] = states[id];\n }\n\n await this.adapter.subscribeStatesAsync('*');\n await this.adapter.subscribeObjectsAsync('*');\n }\n\n protected getInstanceInfo(): InstanceDetails {\n return {\n apiVersion: 'v3',\n smallCards: true,\n };\n }\n\n public onStateChange(id: string, state: ioBroker.State | null): void {\n if (state) {\n // Also on an unchanged value: the timestamp decides which copy of a data point that exists\n // more than once is the current one (see `buildControls` and `getPowerEntries`)\n this.states[id] = state;\n } else if (this.states[id]) {\n // trigger DM update\n delete this.states[id];\n }\n }\n\n public onObjectChange(id: string, obj: ioBroker.ChannelObject | ioBroker.StateObject | null): void {\n if (obj) {\n this.objects[id] = obj;\n } else {\n delete this.objects[id];\n }\n }\n\n /**\n * Collects the short suffixes (state ID without the device prefix) of all currently known\n * objects that belong to one device, e.g. \"POWER1\", \"ENERGY.Voltage\" or \"Shutter1_Position\".\n *\n * @param prefix `<namespace>.<deviceId>.`\n */\n private getSuffixes(prefix: string): Set<string> {\n const suffixes = new Set<string>();\n for (const id in this.objects) {\n if (id.startsWith(prefix)) {\n suffixes.add(id.substring(prefix.length));\n }\n }\n return suffixes;\n }\n\n /**\n * Finds a state whose data point name (its last path segment) is `key`, regardless of which group\n * it is nested under - a data point can end up at very different state IDs depending on the\n * \"Create object tree\" (OBJ_TREE) adapter option and on which MQTT topic published it, e.g. RSSI is\n * \"Wifi_RSSI\" with OBJ_TREE off but \"STATE.Wifi.RSSI\" with it on. Returns the matching suffix (state\n * ID without the device prefix), or `undefined` if the device has no such data point.\n *\n * A key can exist under more than one group at once, e.g. Tasmota echoes \"Uptime\" as part of a\n * one-off command response (\"RESULT.Uptime\") in addition to reporting it with every regular\n * \"STATE\" telemetry message - only the latter keeps being updated, so when both exist, the periodic\n * telemetry groups (STATE, then SENSOR) are preferred over anything else.\n *\n * @param prefix `<namespace>.<deviceId>.`\n * @param key data point name, e.g. \"RSSI\" or \"Temperature\"\n */\n private findDataPointSuffix(prefix: string, key: string): string | undefined {\n const matches: string[] = [];\n for (const stateId of Object.keys(this.states)) {\n if (!stateId.startsWith(prefix)) {\n continue;\n }\n const suffix = stateId.substring(prefix.length);\n if (suffix === key || suffix.endsWith(`.${key}`) || suffix.endsWith(`_${key}`)) {\n matches.push(suffix);\n }\n }\n if (matches.length > 1) {\n for (const group of ['STATE', 'SENSOR']) {\n const preferred = matches.find(s => s.startsWith(`${group}.`));\n if (preferred) {\n return preferred;\n }\n }\n }\n return matches[0];\n }\n\n /**\n * Splits a state suffix into the group it belongs to and the power-metering data point name.\n * Thin wrapper around `splitDataPoint` fixed to the keys of `POWER_METRIC_LABELS`, see there.\n *\n * @param suffix state ID without the device prefix\n */\n private splitPowerSuffix(suffix: string): { channel: string; key: string } | undefined {\n return splitDataPoint(suffix, Object.keys(POWER_METRIC_LABELS));\n }\n\n /**\n * Human-readable label for a (canonicalized) power-metering channel, used when a device has more\n * than one meter and each one needs to be told apart. The built-in Tasmota group is translated\n * (\"ENERGY\" -> \"Power\"/\"Leistung\"/...); a bridged external meter has no fixed translation - its\n * group name comes directly from the MQTT payload (e.g. \"SML\", \"PZEM\"), so it is shown as-is.\n */\n private channelLabel(channel: string): ioBroker.StringOrTranslated {\n if (channel === 'ENERGY') {\n return I18n.getTranslatedObject('Power');\n }\n // Not a second meter, but the alarm thresholds Tasmota reports for the built-in one\n if (channel === 'MARGINS') {\n return I18n.getTranslatedObject('Thresholds');\n }\n return channel.replace(/[._]/g, ' ');\n }\n\n /**\n * Finds all power-metering data points of a device, wherever they are: in the built-in `ENERGY.*`\n * group, in a custom group of a bridged external meter, or as a bare top-level data point.\n *\n * The same reading can end up under more than one state ID for the same meter and metric (channel +\n * label): toggling \"Create object tree\" moves a data point without deleting the old one, and some\n * Tasmota firmware reports energy readings both nested inside its periodic \"SENSOR\" telemetry *and*\n * via its own dedicated \"ENERGY\" topic (both handled by `mqttBase.ts`, see there). Whenever a copy\n * nested under SENSOR/STATE/RESULT/WAKEUP (see `isObjTreeStyleChannel`) exists, it is the\n * authoritative one - a device that also sends its own dedicated topic still sends the regular\n * telemetry that ends up nested, so the bare, non-nested copy is always redundant when a nested one\n * exists. Only as a fallback, when no nested copy exists (or several do, e.g. two aliased data point\n * names), the entry whose state was updated most recently wins - stale leftovers stop being updated,\n * so their timestamp falls behind.\n *\n * @param prefix `<namespace>.<deviceId>.`\n */\n private getPowerEntries(\n prefix: string,\n ): { suffix: string; channel: string; key: string; label: string; unit: string; order: number }[] {\n const entries: { suffix: string; channel: string; key: string; label: string; unit: string; order: number }[] =\n [];\n for (const stateId of Object.keys(this.states)) {\n if (!stateId.startsWith(prefix)) {\n continue;\n }\n const suffix = stateId.substring(prefix.length);\n const common = (this.objects[stateId] as ioBroker.StateObject | undefined)?.common;\n if (!common || common.write === true) {\n continue;\n }\n const split = this.splitPowerSuffix(suffix);\n if (!split) {\n continue;\n }\n const meta = POWER_METRIC_LABELS[split.key];\n entries.push({\n suffix,\n channel: split.channel,\n key: split.key,\n label: meta.label,\n unit: meta.unit,\n order: meta.order,\n });\n }\n\n const byMeter = new Map<string, typeof entries>();\n for (const entry of entries) {\n const groupKey = `${canonicalizeChannel(entry.channel)} ${entry.label}`;\n const group = byMeter.get(groupKey);\n if (group) {\n group.push(entry);\n } else {\n byMeter.set(groupKey, [entry]);\n }\n }\n\n const deduped: typeof entries = [];\n for (const group of byMeter.values()) {\n const nested = group.filter(e => isObjTreeStyleChannel(e.channel));\n const candidates = nested.length ? nested : group;\n const preferred = candidates.reduce((newest, entry) => {\n const newestTs = this.states[`${prefix}${newest.suffix}`]?.ts ?? 0;\n const entryTs = this.states[`${prefix}${entry.suffix}`]?.ts ?? 0;\n return entryTs > newestTs ? entry : newest;\n });\n deduped.push({ ...preferred, channel: canonicalizeChannel(preferred.channel) });\n }\n\n return deduped.sort((a, b) => a.channel.localeCompare(b.channel) || a.order - b.order);\n }\n\n /**\n * Load all sonoff/Tasmota devices\n *\n * @param context context used to report the found devices to the backend\n */\n async loadDevices(context: DeviceLoadContext<string>): Promise<void> {\n // Wait that i18n is initialized\n await this.ready;\n\n const ns = this.adapter.namespace;\n\n for (const deviceId in this.objects) {\n const device = this.objects[deviceId];\n if (device.type !== 'channel') {\n continue;\n }\n const shortDeviceId = device._id.substring(ns.length + 1);\n // Only direct children of the namespace are devices, \"info\" is the adapter's own channel\n if (!shortDeviceId || shortDeviceId.includes('.') || shortDeviceId === 'info') {\n continue;\n }\n\n try {\n const prefix = `${device._id}.`;\n const suffixes = this.getSuffixes(prefix);\n\n const alive = this.states[`${device._id}.alive`]?.val === true;\n const hostname = this.states[`${prefix}INFO.Hostname`]?.val as string | undefined;\n const ip = this.states[`${prefix}INFO.IPAddress`]?.val as string | undefined;\n const model = (this.states[`${prefix}INFO.Module`]?.val as string) || undefined;\n const rssiSuffix = this.findDataPointSuffix(prefix, 'RSSI');\n const rssi = rssiSuffix\n ? (this.states[`${prefix}${rssiSuffix}`]?.val as number | undefined)\n : undefined;\n const batterySuffix = this.findDataPointSuffix(prefix, 'BatteryPercentage');\n const battery = batterySuffix\n ? (this.states[`${prefix}${batterySuffix}`]?.val as number | undefined)\n : undefined;\n const clientId = (device.native as { clientId?: string } | undefined)?.clientId;\n const group = this.getDeviceGroup(suffixes);\n\n const res: DeviceInfo<string> = {\n id: device._id,\n identifier: hostname || ip || clientId || undefined,\n name: device.common?.name || shortDeviceId,\n icon: DEVICE_ICON,\n color: !alive ? '#fff' : undefined,\n backgroundColor: !alive ? '#f44336' : undefined,\n group,\n model,\n status: {\n connection: alive ? 'connected' : 'disconnected',\n rssi,\n battery,\n },\n hasDetails: true,\n customInfo: this.buildCustomInfo(device._id, prefix),\n controls: this.buildControls(shortDeviceId, prefix),\n actions: [\n {\n id: 'rename',\n icon: 'edit',\n description: I18n.getTranslatedObject('Rename this device'),\n handler: async (\n deviceId: string,\n context: ActionContext,\n ): Promise<{ refresh: DeviceRefresh }> => await this.handleRenameDevice(deviceId, context),\n },\n {\n id: 'recreate',\n icon: 'refresh',\n description: I18n.getTranslatedObject('Delete and recreate all data points of this device'),\n confirmation: I18n.getTranslatedObject(\n 'This deletes all data points of this device (except its name). They will be recreated automatically the next time the device reports its state. Continue?',\n ),\n handler: async (\n deviceId: string,\n context: ActionContext,\n ): Promise<{ refresh: DeviceRefresh }> =>\n await this.handleRecreateDevice(deviceId, context),\n },\n ...(hostname || ip\n ? [\n {\n id: 'web',\n icon: 'web' as const,\n description: I18n.getTranslatedObject('Open device web interface'),\n url: `http://${hostname || ip}`,\n },\n ]\n : []),\n ],\n };\n\n context.addDevice(res);\n } catch (error) {\n // One malformed/leftover data point (e.g. a state object without \"common\", as can happen\n // with data points from a much older adapter version) must not hide every other device\n this.adapter.log.warn(`Cannot load device ${device._id} into the Device Manager: ${error}`);\n }\n }\n }\n\n async getDeviceDetails(deviceId: string): Promise<DeviceDetails<string> | null> {\n // Wait that i18n is initialized\n await this.ready;\n\n const device = this.objects[deviceId];\n if (device?.type !== 'channel') {\n return null;\n }\n\n const ns = this.adapter.namespace;\n const shortDeviceId = device._id.substring(ns.length + 1);\n const prefix = `${deviceId}.`;\n const items: Record<string, ConfigItemAny> = {};\n\n const clientId = (device.native as { clientId?: string } | undefined)?.clientId;\n items._clientId = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject('Device ID'),\n data: clientId || shortDeviceId,\n addColon: true,\n copyToClipboard: true,\n };\n\n const hostname = this.states[`${prefix}INFO.Hostname`]?.val as string | undefined;\n const ip = this.states[`${prefix}INFO.IPAddress`]?.val as string | undefined;\n if (hostname || ip) {\n items._deviceLink = {\n // @ts-expect-error staticLink is OK\n type: 'staticLink',\n href: `http://${hostname || ip}`,\n label: I18n.getTranslatedObject('Open device web interface'),\n button: true,\n icon: 'web',\n newLine: true,\n };\n }\n\n for (const info of INFO_ITEMS) {\n const val = this.states[`${prefix}INFO.${info.stateId}`]?.val;\n if (val === undefined || val === null || val === '') {\n continue;\n }\n items[`info_${info.stateId}`] = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject(info.label),\n data: val,\n addColon: true,\n copyToClipboard: info.copy,\n };\n }\n\n const rssiSuffix = this.findDataPointSuffix(prefix, 'RSSI');\n const rssi = rssiSuffix ? (this.states[`${prefix}${rssiSuffix}`]?.val as number | undefined) : undefined;\n if (rssi !== undefined) {\n items._rssi = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject('RSSI'),\n data: rssi,\n unit: 'dBm',\n addColon: true,\n };\n }\n\n const uptimeSuffix = this.findDataPointSuffix(prefix, 'Uptime');\n const uptime = uptimeSuffix ? this.states[`${prefix}${uptimeSuffix}`]?.val : undefined;\n if (uptime !== undefined && uptime !== null && uptime !== '') {\n items._uptime = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject('Uptime'),\n data: uptime,\n addColon: true,\n };\n }\n\n // Power metering section – only shown when power data points exist. Bridged external meters\n // (SML, PZEM, ...) are shown as their own sub-section when a device has more than one meter.\n const powerEntries = this.getPowerEntries(prefix);\n if (powerEntries.length) {\n items._energyHeader = {\n type: 'header',\n text: I18n.getTranslatedObject('Power metering'),\n size: 6,\n newLine: true,\n };\n\n const channels = [...new Set(powerEntries.map(e => e.channel))];\n const multiChannel = channels.length > 1;\n\n for (const channel of channels) {\n if (multiChannel && channel) {\n items[`energy_ch_${channel.replace(/[.:]/g, '_')}`] = {\n type: 'staticInfo',\n label: this.channelLabel(channel),\n data: '',\n newLine: true,\n };\n }\n for (const entry of powerEntries.filter(e => e.channel === channel)) {\n const val = this.states[`${prefix}${entry.suffix}`]?.val;\n items[`energy_${entry.suffix.replace(/[.:]/g, '_')}`] = {\n type: 'staticInfo',\n label: I18n.getTranslatedObject(entry.label),\n data: typeof val === 'number' ? Math.round(val * 100) / 100 : String(val ?? '—'),\n unit: entry.unit || undefined,\n addColon: true,\n };\n }\n }\n }\n\n // Digital inputs (physical switches/buttons wired to the device)\n const inputEntries: { suffix: string; label: string }[] = [];\n for (const stateId of Object.keys(this.states)) {\n if (!stateId.startsWith(prefix)) {\n continue;\n }\n const suffix = stateId.substring(prefix.length);\n // Only the last path segment is the actual data point name (may be nested, e.g. \"STATE.Switch1\")\n const lastSegment = suffix.substring(suffix.lastIndexOf('.') + 1);\n const match = lastSegment.match(/^(Switch|Button)(\\d+)$/);\n if (match) {\n inputEntries.push({ suffix, label: `${match[1]} ${match[2]}` });\n }\n }\n if (inputEntries.length) {\n items._inputsHeader = {\n type: 'header',\n text: I18n.getTranslatedObject('Digital inputs'),\n size: 6,\n newLine: true,\n };\n inputEntries.sort((a, b) => a.suffix.localeCompare(b.suffix));\n for (const entry of inputEntries) {\n const val = this.states[`${prefix}${entry.suffix}`]?.val;\n items[`input_${entry.suffix}`] = {\n type: 'staticInfo',\n label: entry.label,\n data: val ? '✓' : '✗',\n addColon: true,\n };\n }\n }\n\n return {\n id: deviceId,\n schema: {\n type: 'panel',\n items,\n },\n };\n }\n\n private getDeviceGroup(suffixes: Set<string>): { key: string; name: ioBroker.StringOrTranslated } {\n const list = [...suffixes].map(flatName);\n const test = (re: RegExp): boolean => list.some(s => re.test(s));\n\n let key = 'other';\n if (test(/^Shutter\\d+_Position$/)) {\n key = 'cover';\n } else if (test(/^(Color|CT|led_basecolor_rgb|led_basecolor_rgbcw|Dimmer|led_dimmer)$/)) {\n key = 'light';\n } else if (test(/^(POWER\\d*|led_enableAll)$/)) {\n key = 'relay';\n } else if (test(/^(ZbReceived_|ZbPower)/)) {\n key = 'zigbee';\n } else if (list.some(s => this.splitPowerSuffix(s) !== undefined)) {\n key = 'meter';\n } else if (list.some(s => SENSOR_KEYS.some(k => s === k || s.endsWith(`_${k}`)))) {\n key = 'sensor';\n }\n\n const meta = groupMeta[key] || groupMeta.other;\n return { key, name: I18n.getTranslatedObject(meta.nameKey) };\n }\n\n private buildCustomInfo(deviceId: string, prefix: string): DeviceDetails<string> | undefined {\n const shortDeviceId = deviceId.substring(this.adapter.namespace.length + 1);\n const items: Record<string, ConfigItemAny> = {};\n\n for (const sensor of SENSOR_ITEMS) {\n const suffix = this.findDataPointSuffix(prefix, sensor.stateId);\n if (suffix !== undefined) {\n items[sensor.stateId] = {\n type: 'state',\n oid: `${shortDeviceId}.${suffix}`,\n control: 'text',\n unit: sensor.unit,\n digits: sensor.digits,\n label: I18n.getTranslatedObject(sensor.label),\n addColon: true,\n style: { opacity: 0.7 },\n };\n }\n }\n\n // Power on the main tile: only the actual \"Power\" (W) readings, never voltage/current/energy\n const powerItems = this.getPowerEntries(prefix).filter(e => e.label === 'Power');\n const multiPower = powerItems.length > 1;\n for (const entry of powerItems) {\n const label = multiPower ? this.channelLabel(entry.channel) : I18n.getTranslatedObject('Power');\n items[`power_${entry.suffix.replace(/[.:]/g, '_')}`] = {\n type: 'state',\n oid: `${shortDeviceId}.${entry.suffix}`,\n control: 'text',\n unit: 'W',\n digits: 1,\n label,\n addColon: true,\n style: { opacity: 0.7 },\n };\n }\n if (powerItems.length) {\n items._powerSpacer = {\n type: 'divider',\n color: 'transparent',\n height: 2,\n };\n }\n\n if (Object.keys(items).length === 0) {\n return undefined;\n }\n\n return {\n id: deviceId,\n schema: {\n type: 'panel',\n items,\n },\n };\n }\n\n /**\n * Builds the controls of a device from its writable data points. Every data point is matched by its\n * flattened name (see `flatName`), so the same control shows up with and without the \"Create object\n * tree\" option. The current values come from the cached states instead of a request per control -\n * the cache holds all states of the adapter anyway (see `init`).\n */\n private buildControls(shortDeviceId: string, prefix: string): DeviceControl<string>[] {\n const controls: DeviceControl<string>[] = [];\n const used = new Set<string>();\n const rawStates: { id: string; suffix: string; name: string; common: ioBroker.StateCommon }[] = [];\n\n for (const id in this.objects) {\n if (id.startsWith(prefix) && this.objects[id].type === 'state') {\n const suffix = id.substring(prefix.length);\n rawStates.push({\n id,\n suffix,\n name: flatName(suffix),\n common: this.objects[id].common,\n });\n }\n }\n\n // Two state objects can flatten to the same name at once - e.g. a bare \"POWER\" next to a\n // leftover \"RESULT.POWER\" from before \"Create object tree\" was toggled. Both would end up as\n // a control with the same ID, which the Device Manager rejects as a duplicate - so only the\n // most recently updated copy of each name is kept (a leftover stops being updated, so its\n // timestamp falls behind, the same fallback rule as `getPowerEntries` uses).\n const byName = new Map<string, (typeof rawStates)[number]>();\n for (const state of rawStates) {\n const existing = byName.get(state.name);\n if (!existing || (this.states[state.id]?.ts ?? 0) > (this.states[existing.id]?.ts ?? 0)) {\n byName.set(state.name, state);\n }\n }\n const ownStates = [...byName.values()];\n\n const stateHandler =\n (fullId: string) =>\n async (_deviceId: string, _actionId: string, state: ControlState): Promise<ioBroker.State> => {\n await this.adapter.setForeignStateAsync(fullId, state);\n return { val: state, ts: Date.now(), ack: true } as ioBroker.State;\n };\n\n const currentState = (fullId: string): ioBroker.State =>\n this.states[fullId] || ({ val: null, ts: Date.now(), ack: true } as ioBroker.State);\n\n // A control is named after the data point it belongs to, not after where that data point is\n // stored, so its ID stays the same when \"Create object tree\" is toggled\n const controlId = (name: string): string => name.replace(/[.:]/g, '_');\n\n // Primary switches: POWER, POWER1..29, Zigbee bridge relays, OpenBeken LED enable\n const switchRe = /^(POWER\\d*|ZbReceived_.+_Power|led_enableAll)$/;\n for (const state of ownStates) {\n if (state.common?.type !== 'boolean' || state.common.write === false || !switchRe.test(state.name)) {\n continue;\n }\n used.add(state.id);\n const label =\n state.name === 'POWER'\n ? 'Power'\n : state.name.startsWith('POWER')\n ? `Power ${state.name.substring(5)}`\n : state.name.replace(/_/g, ' ');\n controls.push({\n id: controlId(state.name),\n type: 'switch',\n stateId: `${shortDeviceId}.${state.suffix}`,\n label: I18n.getTranslatedObject(label),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n }\n\n // Sliders: dimmer, color temperature, shutter position/tilt\n const sliderDefs: {\n re: RegExp;\n label: (m: RegExpMatchArray) => string;\n min: number;\n max: number;\n unit?: string;\n }[] = [\n { re: /^Dimmer$/, label: () => 'Dimmer', min: 0, max: 100, unit: '%' },\n { re: /^led_dimmer$/, label: () => 'Dimmer', min: 0, max: 100, unit: '%' },\n { re: /^CT$/, label: () => 'Color temperature', min: 153, max: 500 },\n { re: /^led_temperature$/, label: () => 'Color temperature', min: 154, max: 500 },\n { re: /^Shutter(\\d+)_Position$/, label: m => `Shutter ${m[1]} position`, min: 0, max: 100, unit: '%' },\n { re: /^Shutter(\\d+)_Tilt$/, label: m => `Shutter ${m[1]} tilt`, min: 0, max: 100, unit: '%' },\n ];\n for (const state of ownStates) {\n if (used.has(state.id) || state.common?.type !== 'number' || state.common.write === false) {\n continue;\n }\n for (const def of sliderDefs) {\n const match = state.name.match(def.re);\n if (!match) {\n continue;\n }\n used.add(state.id);\n controls.push({\n id: controlId(state.name),\n type: 'slider',\n stateId: `${shortDeviceId}.${state.suffix}`,\n min: state.common.min ?? def.min,\n max: state.common.max ?? def.max,\n unit: state.common.unit || def.unit,\n label: I18n.getTranslatedObject(def.label(match)),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n break;\n }\n }\n\n // Color controls\n const colorRe = /^(Color|led_basecolor_rgb|led_basecolor_rgbcw)$/;\n for (const state of ownStates) {\n if (\n used.has(state.id) ||\n state.common?.type !== 'string' ||\n !state.common.write ||\n !colorRe.test(state.name)\n ) {\n continue;\n }\n used.add(state.id);\n controls.push({\n id: controlId(state.name),\n type: 'color',\n stateId: `${shortDeviceId}.${state.suffix}`,\n label: I18n.getTranslatedObject('Color'),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n }\n\n // Everything else: writable settings not shown elsewhere. INFO/ENERGY/MARGINS are read-only\n // anyway, \"Time\" is the timestamp of the telemetry message a group of data points came with\n const skipRe = /^(INFO_|ENERGY_|MARGINS_|Wifi_|Time$|alive$)/;\n let group: DeviceControl<string> | null = {\n id: 'group_settings',\n type: 'group',\n label: I18n.getTranslatedObject('Settings'),\n };\n\n // Writable numbers/strings first, then the leftover booleans (toggles like Fade, LED exor\n // mode, ...) as buttons\n for (const pass of ['value', 'button'] as const) {\n for (const state of ownStates) {\n if (used.has(state.id) || skipRe.test(state.name) || !state.common || state.common.write === false) {\n continue;\n }\n const type = state.common.type;\n if (pass === 'value' ? type !== 'number' && type !== 'string' : type !== 'boolean') {\n continue;\n }\n used.add(state.id);\n\n if (group) {\n controls.push(group);\n group = null;\n }\n\n if (pass === 'button') {\n controls.push({\n group: 'group_settings',\n id: controlId(state.name),\n type: 'button',\n stateId: `${shortDeviceId}.${state.suffix}`,\n label: state.name.replace(/_/g, ' '),\n variant: 'outlined',\n handler: stateHandler(state.id),\n });\n continue;\n }\n\n let options: { label: string; value: string }[] | undefined;\n const commonStates = state.common.states;\n if (commonStates) {\n options = Array.isArray(commonStates)\n ? commonStates.map((s: string) => ({ value: s, label: s }))\n : Object.keys(commonStates).map(k => ({\n value: k,\n label: String((commonStates as Record<string, string>)[k]),\n }));\n }\n\n controls.push({\n group: 'group_settings',\n id: controlId(state.name),\n type: options ? 'select' : type === 'number' ? 'number' : 'text',\n stateId: `${shortDeviceId}.${state.suffix}`,\n min: state.common.min,\n max: state.common.max,\n unit: state.common.unit,\n options,\n label: state.name.replace(/_/g, ' '),\n state: currentState(state.id),\n handler: stateHandler(state.id),\n });\n }\n }\n\n return controls;\n }\n\n /**\n * @param id ID to rename\n * @param context context sent from backend\n */\n async handleRenameDevice(id: string, context: ActionContext): Promise<{ refresh: DeviceRefresh }> {\n const result = await context.showForm(\n {\n type: 'panel',\n items: {\n newName: {\n type: 'text',\n trim: false,\n placeholder: '',\n },\n },\n },\n {\n data: {\n newName: '',\n },\n title: I18n.getTranslatedObject('Enter new name'),\n },\n );\n if (result?.newName === undefined || result?.newName === '') {\n return { refresh: 'none' };\n }\n const obj = {\n common: {\n name: result.newName,\n },\n };\n\n const res = await this.adapter.extendForeignObjectAsync(id, obj);\n if (res === null) {\n this.adapter.log.warn(`Can not rename device ${id}: ${JSON.stringify(res)}`);\n return { refresh: 'none' };\n }\n return { refresh: 'device' as DeviceRefresh };\n }\n\n /**\n * Deletes every data point of a device - its channel object (and therefore a custom name set via\n * `rename`) is kept, everything else gets recreated automatically the next time the device reports\n * its state. This is a manual, user-triggered fix for data points stuck in an outdated structure,\n * e.g. leftovers from before the \"Create object tree\" adapter option was changed.\n *\n * `alive` is kept as well: it is not reported by the device but written by the adapter when the\n * MQTT client connects (see `createClient` in `mqttBase.ts`), so a deleted `alive` would only come\n * back the next time the device reconnects - which can take days for a device that is simply up.\n *\n * @param id device (channel) ID to recreate\n * @param _context unused - the destructive confirmation is handled declaratively by the action itself\n */\n async handleRecreateDevice(id: string, _context: ActionContext): Promise<{ refresh: DeviceRefresh }> {\n const prefix = `${id}.`;\n let removed = 0;\n for (const stateId in this.objects) {\n if (!stateId.startsWith(prefix) || this.objects[stateId].type !== 'state') {\n continue;\n }\n if (stateId === `${prefix}alive`) {\n continue;\n }\n try {\n await this.adapter.delForeignStateAsync(stateId);\n await this.adapter.delForeignObjectAsync(stateId);\n delete this.objects[stateId];\n delete this.states[stateId];\n removed++;\n } catch (error) {\n this.adapter.log.warn(`Cannot remove data point ${stateId}: ${error}`);\n }\n }\n\n // The server/bridge remembers which objects it has already created and would not create them a\n // second time within this adapter run, so the deleted data points have to be forgotten there\n // too - otherwise they only come back after a restart of the adapter\n this.adapter.server?.forgetObjects(id);\n\n this.adapter.log.info(`Removed ${removed} data point(s) of ${id}, they will be recreated automatically`);\n return { refresh: 'device' as DeviceRefresh };\n }\n}\n"]}