iobroker.yamaha 2.1.0 → 2.1.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
@@ -103,6 +103,10 @@ On the very first contact the adapter asks the receiver which functions it suppo
103
103
  Placeholder for the next version (at the beginning of the line):
104
104
  ### **WORK IN PROGRESS**
105
105
  -->
106
+ ### 2.1.1 (2026-09-02)
107
+
108
+ - (krobipd) Fixed: on an updated instance the info datapoints now get the translated names too, instead of keeping the ones an older version had written
109
+
106
110
  ### 2.1.0 (2026-09-02)
107
111
 
108
112
  - (krobipd) New: every datapoint name is now shown in your ioBroker language, in eleven languages; a name you changed yourself stays untouched
@@ -132,13 +136,6 @@ On the very first contact the adapter asks the receiver which functions it suppo
132
136
 
133
137
  - (krobipd) Fixed: a restart while the receiver stands by no longer forgets abilities the device proved while awake — the remembered capability map only ever grows for the same device and firmware
134
138
 
135
- ### 2.0.1 (2026-09-01)
136
-
137
- - (krobipd) Fixed: devices on the oldest protocol no longer get datapoints their status never delivers — states are only created for what the device really answers, like on the other two protocols
138
- - (krobipd) Fixed: leftover datapoints that never carried a value — zone sound settings or a second HDMI output the receiver does not have — are cleaned away once when updating the adapter
139
- - (krobipd) Fixed: the player block of a zone that is not playing a media source starts with cleared values (empty texts, playback on stop) instead of staying without any value until the first playback
140
- - (krobipd) Fixed: the two DAB station-scan datapoints (stations found, scan progress) start at zero instead of staying without a value until the first station scan
141
-
142
139
  [Older changelogs can be found there](CHANGELOG_OLD.md)
143
140
 
144
141
  ## History
package/build/main.js CHANGED
@@ -143,6 +143,7 @@ class Yamaha extends utils.Adapter {
143
143
  }
144
144
  await this.snapshotExistingDatapoints();
145
145
  await this.cleanupStaleObjects(new Set(devices.map((device) => device.id)));
146
+ await this.ensureInstanceInfoObjects();
146
147
  await this.subscribeToStates();
147
148
  const pushReceiver = new import_push_receiver.YxcPushReceiver({
148
149
  log: { debug: (message) => this.log.debug(message), warn: (message) => this.log.warn(message) },
@@ -628,9 +629,72 @@ class Yamaha extends utils.Adapter {
628
629
  }, DATAPOINT_BALANCE_SETTLE_MS);
629
630
  }
630
631
  /**
631
- * Create a device's header objects (the device node, its info channel and a
632
+ * Refresh the adapter's OWN `info.*` objects.
633
+ *
634
+ * js-controller creates them from `io-package.json` `instanceObjects` when the instance is
635
+ * added, and leaves an existing object's `common` alone on every later upgrade — so an
636
+ * instance that predates a change keeps whatever the old version wrote. Measured after the
637
+ * name translation went live: five of them still carried a plain-string name while the whole
638
+ * rest of the tree was translated. Writing them here every start closes that half; extendObject
639
+ * merges, so a recording setting or anything else a user attached survives.
640
+ */
641
+ async ensureInstanceInfoObjects() {
642
+ const objects = [
643
+ ["info", { id: "info", type: "channel", common: { name: (0, import_i18n.tName)("Information") } }],
644
+ [
645
+ "info.connection",
646
+ {
647
+ id: "info.connection",
648
+ type: "state",
649
+ common: {
650
+ name: (0, import_i18n.tName)("Device or service connected"),
651
+ type: "boolean",
652
+ role: "indicator.connected",
653
+ read: true,
654
+ write: false
655
+ }
656
+ }
657
+ ],
658
+ [
659
+ "info.devicesTotal",
660
+ {
661
+ id: "info.devicesTotal",
662
+ type: "state",
663
+ common: { name: (0, import_i18n.tName)("Devices total"), type: "number", role: "value", read: true, write: false }
664
+ }
665
+ ],
666
+ [
667
+ "info.devicesOnline",
668
+ {
669
+ id: "info.devicesOnline",
670
+ type: "state",
671
+ common: { name: (0, import_i18n.tName)("Devices online"), type: "number", role: "value", read: true, write: false }
672
+ }
673
+ ],
674
+ [
675
+ "info.devicesAllOnline",
676
+ {
677
+ id: "info.devicesAllOnline",
678
+ type: "state",
679
+ common: { name: (0, import_i18n.tName)("All devices online"), type: "boolean", role: "indicator", read: true, write: false }
680
+ }
681
+ ]
682
+ ];
683
+ for (const [id, def] of objects) {
684
+ await this.extendObject(id, { type: def.type, common: def.common, native: {} });
685
+ }
686
+ }
687
+ /**
688
+ * Create AND refresh a device's header objects (the device node, its info channel and a
632
689
  * per-device connection indicator) so its state is visible even while offline.
633
690
  *
691
+ * Written with `extendObject` on every start, not created once: an object that already exists
692
+ * is otherwise never touched again, so an instance upgraded from an older version keeps
693
+ * whatever that version wrote — measured live after the name translation, where these were the
694
+ * only device datapoints left with a plain-string name (`info.model`/`info.firmware` came out
695
+ * right only because a catalog entry upserts them on top). extendObject merges, so a recording
696
+ * setting a user attached survives.
697
+ *
634
698
  * @param deviceId the id-safe device id
635
699
  * @param ip the device's current address (from config or discovery)
636
700
  */
@@ -656,12 +720,12 @@ class Yamaha extends utils.Adapter {
656
720
  },
657
721
  { preserve: { common: ["name"] } }
658
722
  );
659
- await this.setObjectNotExistsAsync(`${deviceId}.info`, {
723
+ await this.extendObject(`${deviceId}.info`, {
660
724
  type: "channel",
661
725
  common: { name: (0, import_i18n.tName)("Info") },
662
726
  native: {}
663
727
  });
664
- await this.setObjectNotExistsAsync(`${deviceId}.info.connection`, {
728
+ await this.extendObject(`${deviceId}.info.connection`, {
665
729
  type: "state",
666
730
  common: {
667
731
  name: (0, import_i18n.tName)("Connected"),
@@ -673,24 +737,24 @@ class Yamaha extends utils.Adapter {
673
737
  },
674
738
  native: {}
675
739
  });
676
- await this.setObjectNotExistsAsync(`${deviceId}.info.model`, {
740
+ await this.extendObject(`${deviceId}.info.model`, {
677
741
  type: "state",
678
742
  common: { name: (0, import_i18n.tName)("Model"), type: "string", role: "text", read: true, write: false, def: "" },
679
743
  native: {}
680
744
  });
681
- await this.setObjectNotExistsAsync(`${deviceId}.info.ip`, {
745
+ await this.extendObject(`${deviceId}.info.ip`, {
682
746
  type: "state",
683
747
  common: { name: (0, import_i18n.tName)("IP address"), type: "string", role: "info.ip", read: true, write: false, def: "" },
684
748
  native: {}
685
749
  });
686
750
  await this.setState(`${deviceId}.info.ip`, { val: ip, ack: true });
687
- await this.setObjectNotExistsAsync(`${deviceId}.info.transports`, {
751
+ await this.extendObject(`${deviceId}.info.transports`, {
688
752
  type: "channel",
689
753
  common: { name: (0, import_i18n.tName)("Transports") },
690
754
  native: {}
691
755
  });
692
756
  for (const proto of TRANSPORT_IDS) {
693
- await this.setObjectNotExistsAsync(`${deviceId}.info.transports.${proto}`, {
757
+ await this.extendObject(`${deviceId}.info.transports.${proto}`, {
694
758
  type: "state",
695
759
  common: {
696
760
  name: (0, import_i18n.tName)("%s connected", proto.toUpperCase()),
package/build/main.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts"],
4
- "sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { createSocket } from \"node:dgram\";\nimport { get as httpGet } from \"node:http\";\nimport { networkInterfaces } from \"node:os\";\nimport { attemptDevice } from \"./lib/attempt-device\";\nimport { searchInterfaces } from \"./lib/network-interfaces\";\nimport { isGroupEnabled } from \"./lib/catalog/groups\";\nimport { iconForModel } from \"./lib/device-type\";\nimport {\n childlessChannelIds,\n LABEL_RANK,\n type LabelRank,\n legacyDeviceRow,\n mergeDiscovered,\n neverWrittenStateIds,\n nextDeviceLabel,\n parseDevices,\n renamedObjectIds,\n staleObjects,\n stripNamespace,\n} from \"./lib/pure-helpers\";\nimport { errorMessage, MAX_HTTP_BODY_BYTES } from \"./lib/util\";\nimport { tName } from \"./lib/i18n\";\nimport { discoverYamaha } from \"./lib/discovery\";\nimport { readDiscovered, readIgnored, writeDiscovered } from \"./lib/discovered-store\";\nimport { discoveredStoreDeps, ignoredStoreDeps } from \"./lib/discovered-store-deps\";\nimport { YxcPushReceiver } from \"./lib/yxc/push-receiver\";\nimport { YamahaDeviceManagement } from \"./device-management\";\nimport type { DeviceRecord } from \"./lib/types\";\nimport { DeviceSupervisor, type ConnectionHandle } from \"./lib/lifecycle/device-supervisor\";\nimport { ReconnectStrategy } from \"./lib/lifecycle/reconnect-strategy\";\nimport { ReachabilityDedup } from \"./lib/lifecycle/reachability-dedup\";\nimport { createSubunitCache, isAvailSnapshot, type YncaSubunitCache } from \"./lib/ynca/subunit-cache\";\nimport { ProbeMemory } from \"./lib/lifecycle/probe-memory\";\n\n/** Supervisor reconnect backoff bounds (exponential: 1s, 2s \u2026 capped at 60s). */\nconst RECONNECT_BASE_MS = 1000;\nconst RECONNECT_MAX_MS = 60000;\n\n/** Abort a discovery description fetch after this long, so a dead device cannot hang it. */\nconst FETCH_TIMEOUT_MS = 4000;\n\n/** How often the discovery M-SEARCH is repeated \u2014 multicast is lossy, one dropped packet must not hide a receiver. */\nconst SSDP_SEARCH_BURST = 3;\n/** Spacing between the repeated M-SEARCH sends, inside the collect window. */\nconst SSDP_SEARCH_INTERVAL_MS = 1000;\n\n/** The three transports in attempt order \u2014 also the per-transport `info.transports.*` state ids. */\nconst TRANSPORT_IDS = [\"ynca\", \"yxc\", \"xml\"] as const;\n\n/**\n * How long the datapoint balance waits for quiet before it logs. Devices connect\n * asynchronously and in parallel, so the line has to outlast the slowest of them.\n */\nconst DATAPOINT_BALANCE_SETTLE_MS = 5000;\n\n/**\n * Shortest gap between two network searches triggered by an offline auto-found device. A\n * receiver that moved to another address answers nowhere else, so the search is the only way\n * back to it \u2014 but a device that is simply switched off must not turn that into a scan loop.\n */\nconst REDISCOVER_MIN_INTERVAL_MS = 300000;\n\n/**\n * ioBroker.yamaha \u2014 controls Yamaha AV receivers and MusicCast devices.\n *\n * Each configured device is driven by a supervisor that keeps a multi-transport\n * handle online: every protocol the device answers \u2014 YNCA (amp control over a held\n * TCP connection, event-pushed), YXC (MusicCast, push + poll), XML/YNC (pre-2010,\n * polled over HTTP) \u2014 connects in parallel on one object tree, each datapoint owned\n * by the best-fitting transport. All YXC devices share one UDP push receiver, keyed\n * by source IP.\n */\nexport class Yamaha extends utils.Adapter {\n private readonly supervisors: DeviceSupervisor[] = [];\n /** deviceId \u2192 its supervisor, so a state change goes to ONE device, not to all of them. */\n private readonly supervisorById = new Map<string, DeviceSupervisor>();\n private readonly deviceConnected = new Map<string, boolean>();\n /** deviceId \u2192 the record it is currently running with, so an address change is visible. */\n private readonly deviceRecords = new Map<string, DeviceRecord>();\n /** The addresses of all supervised devices \u2014 a multiroom group resolves its clients through it. */\n private readonly knownDeviceIps = new Set<string>();\n /** True while the instance runs whatever the network search finds (empty device table). */\n private autoMode = false;\n /** Armed while an auto-found device is offline: the search that can bring it back. */\n private rediscoverTimer: ioBroker.Timeout | undefined;\n /** When the last background search ran, so the retry cannot become a scan loop. */\n private lastRediscovery = 0;\n private pushReceiver: YxcPushReceiver | undefined;\n /**\n * Set the moment teardown begins: a connect attempt still in flight then resolves into\n * a closing adapter and must not arm keepalives/timers any more \u2014 the framework would\n * refuse them anyway, but with a warn line per attempt (\"setInterval called, but\n * adapter is shutting down\", seen live on the 1.7.0 upgrade restart).\n */\n private unloading = false;\n /** Device-manager backend: the receivers as cards with add/edit/delete. */\n private readonly deviceManagement: YamahaDeviceManagement;\n /**\n * Every datapoint that existed when this run started, filled ONCE before the cleanup and\n * before any device connects. Without it the balance below would report the whole tree as\n * new on every restart: `upsertObject` runs `extendObject` on every state it touches (the\n * role/unit retrofit), so \"did the create path run?\" is not the same question as \"is this\n * datapoint new?\".\n */\n private readonly knownDatapoints = new Set<string>();\n /** State ids (namespace-relative) some transport upserted in THIS run \u2014 live claims. */\n private readonly touchedThisRun = new Set<string>();\n /** Devices that reported connected at least once in this run (gates the orphan purge). */\n private readonly readyDevices = new Set<string>();\n private createdDatapoints = 0;\n private removedDatapoints = 0;\n /** Debounce for the balance line, so one config change produces ONE line, not one per device. */\n private balanceTimer: ioBroker.Timeout | undefined;\n /** Set when the start-up snapshot failed \u2014 a balance without it would be wrong, so none is written. */\n private balanceDisabled = false;\n /**\n * Latched after the first failed database write, so an outage warns once and the\n * repeats stay at debug until a write goes through again (nut2 `failedUps` pattern).\n */\n private stateWritesFailing = false;\n\n /**\n * @param options adapter options passed through by js-controller\n */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"yamaha\",\n });\n\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"stateChange\", this.onStateChange.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n this.deviceManagement = new YamahaDeviceManagement(this);\n }\n\n /** Start a supervisor for each configured device, then subscribe to state changes. */\n private async onReady(): Promise<void> {\n try {\n this.log.info('starting \u2014 a \"ready\" message will follow for each device');\n await this.setState(\"info.connection\", { val: false, ack: true });\n await this.migrateLegacyDevice();\n await this.migrateGroupZones();\n // The device list is the switch: filled \u2192 use exactly those (manual); empty\n // \u2192 discover on the network and run what is found (auto). XML/pre-2010 devices\n // never answer SSDP, so they are always added manually.\n const configured = parseDevices(this.config.devices, (dropped, takenId) =>\n this.log.warn(`device \"${dropped}\" skipped \u2014 its object id \"${takenId}\" is already used by another device`),\n );\n const devices = configured.length > 0 ? configured : await this.autoDiscover();\n if (this.unloading) {\n // Stopped during the initial network search: it resolves on its own clock, and\n // everything below \u2014 push socket, subscriptions, device sockets and timers \u2014\n // would come up on an instance that is already gone, with nothing left to close it.\n return;\n }\n this.autoMode = configured.length === 0;\n // The start-up search (blocking first setup, or the background one below) is the first\n // search of this run \u2014 an offline device must not fire another one right behind it.\n this.lastRediscovery = Date.now();\n for (const device of devices) {\n this.knownDeviceIps.add(device.ip);\n }\n // Before the cleanup and before any device connects \u2014 see knownDatapoints.\n await this.snapshotExistingDatapoints();\n await this.cleanupStaleObjects(new Set(devices.map(device => device.id)));\n await this.subscribeToStates();\n const pushReceiver = new YxcPushReceiver({\n log: { debug: message => this.log.debug(message), warn: message => this.log.warn(message) },\n schedule: (cb, ms) => this.setTimeout(cb, ms),\n cancel: handle => this.clearTimeout(handle),\n });\n pushReceiver.start();\n this.pushReceiver = pushReceiver;\n if (configured.length > 0) {\n this.log.info(`setting up ${devices.length} configured device(s)...`);\n }\n for (const device of devices) {\n await this.startDevice(device, pushReceiver);\n }\n this.writeDeviceOverview();\n // Auto mode with remembered devices: they started WITHOUT waiting for the network\n // search \u2014 it runs behind them, adds newcomers and moves a device that changed address.\n if (this.autoMode && devices.length > 0) {\n void this.discoverAdditionalDevices(pushReceiver);\n }\n } catch (e) {\n this.log.error(`onReady failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * Bring one device under supervision: header objects, disconnected stamp, the\n * per-device caches, and the supervisor that keeps it connected. Factored out of\n * onReady so the background discovery can start a late-found device the same way.\n *\n * @param device the device record\n * @param pushReceiver the shared YXC push receiver\n */\n private async startDevice(device: DeviceRecord, pushReceiver: YxcPushReceiver): Promise<void> {\n // Both callers check `unloading` after their network search resolves (onReady and\n // discoverAdditionalDevices) \u2014 a device handed over after onUnload never gets here.\n this.deviceConnected.set(device.id, false);\n this.deviceRecords.set(device.id, { ...device });\n this.knownDeviceIps.add(device.ip);\n await this.ensureDeviceHeader(device.id, device.ip);\n // Stamp it disconnected BEFORE the first attempt: ioBroker keeps a state's last value\n // forever, so a crash or a power cut would otherwise leave the device green until it\n // reports again \u2014 and a device that never answers would stay green for good.\n await this.setState(`${device.id}.info.connection`, { val: false, ack: true });\n // The three protocol flags follow the same rule: left at their last value, a crash\n // would show \"YNCA connected\" on the card next to a red connection dot \u2014 for good, if\n // the device never answers again.\n this.setTransports(device.id, []);\n const reachability = new ReachabilityDedup();\n const subunitCache = await this.loadYncaSubunitCache(device.id);\n // Held here, not in the controllers: those are rebuilt on every connection attempt;\n // persisted at the device object, so a restart starts from the remembered answers.\n const probeMemory = await this.loadProbeMemory(device.id);\n const supervisor = new DeviceSupervisor({\n attempt: () =>\n this.attemptDevice(device, pushReceiver, this.knownDeviceIps, reachability, subunitCache, probeMemory),\n schedule: (cb, ms) => this.setTimeout(cb, ms),\n cancel: handle => this.clearTimeout(handle as ioBroker.Timeout | undefined),\n onConnectionChange: connected => this.reportConnection(device.id, connected),\n backoff: new ReconnectStrategy(RECONNECT_BASE_MS, RECONNECT_MAX_MS),\n log: {\n debug: message => this.log.debug(message),\n info: message => this.log.info(message),\n warn: message => this.log.warn(message),\n },\n });\n this.supervisors.push(supervisor);\n this.supervisorById.set(device.id, supervisor);\n supervisor.start();\n }\n\n /**\n * The background half of auto-discovery: search the network, bring devices online that are\n * not supervised yet, and move a device that answers at a NEW address. The remembered devices\n * did not wait for this \u2014 the search window (seconds) used to gate every restart although the\n * devices were already known.\n *\n * The address half matters because the object id \u2014 and with it the whole tree, its history and\n * every visualisation binding \u2014 is fixed to the device, not to where it sits. A receiver that\n * moved by DHCP is the same device at a new address, so its supervisor is rebuilt there instead\n * of retrying an address nobody answers at any more.\n *\n * @param pushReceiver the shared YXC push receiver\n */\n private async discoverAdditionalDevices(pushReceiver: YxcPushReceiver): Promise<void> {\n try {\n const merged = await this.runDiscovery();\n if (this.unloading) {\n return; // the search outlived the adapter \u2014 nothing may start now\n }\n let changed = false;\n for (const device of merged) {\n const running = this.deviceRecords.get(device.id);\n if (!running) {\n this.log.info(`discovery found ${device.id} \u2014 setting up`);\n await this.startDevice(device, pushReceiver);\n changed = true;\n } else if (running.ip !== device.ip) {\n this.log.info(`${device.id}: address changed from ${running.ip} to ${device.ip} \u2014 reconnecting it there`);\n this.knownDeviceIps.delete(running.ip);\n this.stopDevice(device.id);\n await this.startDevice(device, pushReceiver);\n changed = true;\n }\n }\n if (changed) {\n this.writeDeviceOverview();\n }\n } catch (e) {\n this.log.warn(`background discovery failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * Arm a background search because an auto-found device is offline \u2014 the only way back to a\n * receiver that moved to another address, since it answers at the remembered one no more.\n * Throttled: a device that is merely switched off must not turn this into a scan loop, and\n * one timer covers however many devices are down.\n */\n private scheduleRediscovery(): void {\n if (!this.autoMode || this.unloading || this.rediscoverTimer !== undefined) {\n return;\n }\n const receiver = this.pushReceiver;\n if (!receiver) {\n return;\n }\n const due = Math.max(0, REDISCOVER_MIN_INTERVAL_MS - (Date.now() - this.lastRediscovery));\n this.rediscoverTimer = this.setTimeout(() => {\n this.rediscoverTimer = undefined;\n this.lastRediscovery = Date.now();\n if (!this.unloading) {\n void this.discoverAdditionalDevices(receiver);\n }\n }, due);\n }\n\n /**\n * Stop supervising one device and release its supervisor. The object tree is untouched \u2014\n * a readdress puts the same device straight back on it.\n *\n * @param deviceId the id-safe device id\n */\n private stopDevice(deviceId: string): void {\n const supervisor = this.supervisorById.get(deviceId);\n if (!supervisor) {\n return;\n }\n supervisor.close();\n this.supervisorById.delete(deviceId);\n const index = this.supervisors.indexOf(supervisor);\n if (index >= 0) {\n this.supervisors.splice(index, 1);\n }\n }\n\n /**\n * Remove one device for good: stop talking to it and delete its object tree.\n *\n * Driven by the device manager's delete action. Deleting a discovered device used to only\n * empty the remembered list \u2014 the supervisor kept the connection, the tree stayed, and the card\n * came back on the next start. Now the delete is what it says; the id is additionally kept in\n * the ignored list (device manager) so a later search does not put the device back.\n *\n * @param deviceId the id-safe device id\n */\n public async removeDevice(deviceId: string): Promise<void> {\n this.stopDevice(deviceId);\n const record = this.deviceRecords.get(deviceId);\n if (record) {\n this.knownDeviceIps.delete(record.ip);\n }\n this.deviceRecords.delete(deviceId);\n this.deviceConnected.delete(deviceId);\n this.readyDevices.delete(deviceId);\n try {\n await this.delObjectAsync(deviceId, { recursive: true });\n } catch (e) {\n this.log.warn(`could not remove the object tree of \"${deviceId}\" (${errorMessage(e)})`);\n }\n this.writeState(\"info.connection\", [...this.deviceConnected.values()].some(Boolean));\n this.writeDeviceOverview();\n }\n\n /**\n * Subscribe to the adapter's own states \u2014 OBSERVED, like every other database call.\n *\n * `subscribeStates` without a callback returns a promise, and its wildcard branch reads the\n * matching objects first: any failure there ends in `maybeCallbackWithError`, which rejects\n * for everything except the plain \"database closed\" case (js-controller-common-db source).\n * Left unawaited that is an unhandled rejection \u2014 and js-controller turns those into an\n * adapter stop, the same trap the nine bare state writes carried.\n *\n * A failure is loud but not fatal: without the subscription the tree still fills from the\n * devices, only user writes stop being applied. Saying so beats a silent half-working\n * instance, and beats losing the whole start over it.\n */\n private async subscribeToStates(): Promise<void> {\n try {\n await this.subscribeStatesAsync(\"*\");\n } catch (e) {\n this.log.error(\n `could not subscribe to state changes (${errorMessage(e)}) \u2014 the tree still updates, ` +\n `but writes to datapoints will not reach the device until the instance is restarted`,\n );\n }\n }\n\n /**\n * Aggregate one device's connection state into the adapter's `info.connection`\n * (true while at least one device is connected).\n *\n * @param deviceId the device reporting\n * @param connected whether that device is currently connected\n */\n private reportConnection(deviceId: string, connected: boolean): void {\n if (connected && !this.readyDevices.has(deviceId)) {\n this.readyDevices.add(deviceId);\n // Arm the settle pass even when the connect created nothing new \u2014 the once-per-\n // version orphan purge rides the same settled moment as the balance line.\n this.scheduleDatapointBalance();\n }\n this.deviceConnected.set(deviceId, connected);\n this.writeState(`${deviceId}.info.connection`, connected);\n // A drop clears the per-transport flags; a (re)connect sets them again via onTransports.\n if (!connected) {\n this.setTransports(deviceId, []);\n // In auto mode the device may simply have moved \u2014 only a search can find it again.\n this.scheduleRediscovery();\n }\n const anyConnected = [...this.deviceConnected.values()].some(Boolean);\n this.writeState(\"info.connection\", anyConnected);\n this.writeDeviceOverview();\n }\n\n /**\n * The three overview datapoints: how many devices this instance runs, how many are\n * connected right now, and whether that is all of them. Derived from the SAME map that\n * feeds the per-device markers and written in the same round \u2014 computed separately they\n * would drift away from what the single devices say.\n *\n * `devicesAllOnline` needs at least one device: zero of zero is not \"everything is fine\".\n */\n private writeDeviceOverview(): void {\n const total = this.deviceConnected.size;\n const online = [...this.deviceConnected.values()].filter(Boolean).length;\n this.writeState(\"info.devicesTotal\", total);\n this.writeState(\"info.devicesOnline\", online);\n this.writeState(\"info.devicesAllOnline\", total > 0 && online === total);\n }\n\n /**\n * Reflect the live transport set into a device's `info.transports.*` flags so the\n * device-manager card shows which protocols (YNCA/YXC/XML) are connected right now.\n *\n * @param deviceId the id-safe device id\n * @param names the transports live now (empty on a drop)\n */\n private setTransports(deviceId: string, names: string[]): void {\n const live = new Set(names);\n for (const proto of TRANSPORT_IDS) {\n this.writeState(`${deviceId}.info.transports.${proto}`, live.has(proto));\n }\n }\n\n /**\n * Write a state with ack \u2014 and OBSERVE the promise. js-controller turns an unhandled\n * promise rejection into an adapter stop (`_exceptionHandler` \u2192 exit code\n * UNCAUGHT_EXCEPTION, read in the controller source), and `setState` rejects whenever\n * the states database is not reachable for a moment (`ERROR_DB_CLOSED`, or a pending\n * command cancelled by a reconnecting Redis). Nine fire-and-forget writes used to run\n * bare: one hiccup while a device pushed a value would have restarted the whole\n * instance. The failure lands in the log instead \u2014 once per outage at warn, then at\n * debug until a write succeeds again; during teardown it is expected and stays silent.\n *\n * @param id the state id (namespace-relative)\n * @param value the value to write\n */\n private writeState(id: string, value: ioBroker.StateValue): void {\n this.setState(id, { val: value, ack: true }).then(\n () => {\n this.stateWritesFailing = false;\n },\n (e: unknown) => this.noteWriteFailure(`state ${id}`, e),\n );\n }\n\n /**\n * Persist a device object's `native` part \u2014 observed like {@link writeState}. The two\n * per-device caches (YNCA subunit probe, probe memory) persist through it.\n *\n * @param deviceId the device object id\n * @param native the native fields to merge into the object\n */\n private persistDeviceNative(deviceId: string, native: Record<string, unknown>): void {\n this.extendObject(deviceId, { native }).then(\n () => {\n this.stateWritesFailing = false;\n },\n (e: unknown) => this.noteWriteFailure(`device object ${deviceId}`, e),\n );\n }\n\n /**\n * Log a failed database write: warn on the first failure of an outage, debug for the\n * repeats, silence while unloading (the database is going down with us).\n *\n * @param what which write failed, for the log line\n * @param e the rejection reason\n */\n private noteWriteFailure(what: string, e: unknown): void {\n if (this.unloading) {\n return;\n }\n const message = `could not write ${what} (${errorMessage(e)})`;\n if (this.stateWritesFailing) {\n this.log.debug(message);\n return;\n }\n this.stateWritesFailing = true;\n this.log.warn(`${message} \u2014 repeats stay at debug until a write succeeds`);\n }\n\n /**\n * One-shot startup cleanup: delete every object that does not belong to a\n * configured device (the previous adapter's whole tree, and any device dropped\n * from the config). Runs before the devices connect; a configured device's\n * subtree is kept whether or not it has connected yet.\n *\n * @param deviceIds the ids of the currently configured devices\n */\n private async cleanupStaleObjects(deviceIds: Set<string>): Promise<void> {\n const allObjects = await this.getAdapterObjectsAsync();\n const existing = Object.keys(allObjects);\n const stale = staleObjects(existing, deviceIds, this.namespace);\n // Old states this version renamed/moved (e.g. system.model -> info.model): delete the\n // old object so it does not linger orphaned beside the new one under a kept device.\n const renamed = renamedObjectIds(existing, deviceIds, this.namespace);\n // Objects whose datapoint group the user switched off \u2014 remove them so turning a group from\n // on to off cleans up its whole subtree (a toggle change restarts the instance, so this runs).\n const config = this.config as unknown as Record<string, unknown>;\n const disabled = existing.filter(full => {\n for (const deviceId of deviceIds) {\n const base = `${this.namespace}.${deviceId}.`;\n if (full.startsWith(base) && !isGroupEnabled(full.slice(base.length), config)) {\n return true;\n }\n }\n return false;\n });\n for (const fullId of [...stale, ...renamed, ...disabled]) {\n try {\n await this.delObjectAsync(stripNamespace(fullId, this.namespace));\n } catch {\n // already removed together with its parent\n }\n }\n // The reasons stay available for diagnosis; the user sees ONE balance line instead of\n // three counts they have to add up themselves.\n if (stale.length > 0) {\n this.log.debug(`removed ${stale.length} object(s) from a previous configuration`);\n }\n if (renamed.length > 0) {\n this.log.debug(`removed ${renamed.length} renamed object(s) from an earlier version`);\n }\n if (disabled.length > 0) {\n this.log.debug(`removed ${disabled.length} object(s) from switched-off datapoint groups`);\n }\n // Channels and device nodes go with them, but only datapoints are counted \u2014 that is what\n // the user switched on or off, and what they look for in the object tree.\n this.noteDatapointsRemoved(\n [...stale, ...renamed, ...disabled].filter(fullId => allObjects[fullId]?.type === \"state\"),\n );\n }\n\n /**\n * Once per adapter version and device (marker `native.purgeVersion` on the DEVICE\n * object): remove read-capable states under a CONNECTED device that never carried a\n * value and were not (re)created by this run's transports \u2014 over-declarations of an\n * earlier adapter version that today's claim-with-proof creation no longer makes.\n * Deleting them is lossless (no value, no history). Runs after the tree settled, so\n * a device that has not connected in this run keeps its tree untouched \u2014 its sweep\n * happens on the first start that reaches it.\n */\n private async purgeNeverFilled(): Promise<void> {\n const candidates: string[] = [];\n for (const deviceId of this.readyDevices) {\n const device = await this.getObjectAsync(deviceId);\n if ((device?.native as Record<string, unknown> | undefined)?.purgeVersion !== this.version) {\n candidates.push(deviceId);\n }\n }\n if (candidates.length === 0) {\n return;\n }\n const allObjects = await this.getAdapterObjectsAsync();\n const states = await this.getStatesAsync(\"*\");\n const purged = neverWrittenStateIds(allObjects, states, new Set(candidates), this.namespace).filter(\n fullId => !this.touchedThisRun.has(stripNamespace(fullId, this.namespace)),\n );\n for (const fullId of purged) {\n try {\n await this.delObjectAsync(stripNamespace(fullId, this.namespace));\n } catch {\n // already gone\n }\n }\n for (const deviceId of candidates) {\n await this.extendObject(deviceId, { native: { purgeVersion: this.version } });\n }\n if (purged.length > 0) {\n this.log.debug(`removed ${purged.length} never-filled object(s) from an earlier version`);\n this.noteDatapointsRemoved(purged);\n }\n }\n\n /**\n * Remove folders that hold no datapoint any more, under the devices that connected this run.\n *\n * The two sweeps above only ever delete datapoints, so a folder emptied by a tree rework stays\n * behind and promises content it can never get \u2014 `player.server` is the live case: the v2.0.0\n * migration deletes the SERVER source's playback copies, and the new tree gives that source no\n * datapoint of its own. Runs on every start, not once per version: an empty folder is wrong\n * whenever it is found, and re-reading the objects after the orphan purge catches the ones that\n * purge just emptied. Not counted in the datapoint balance \u2014 a folder is not a datapoint.\n */\n private async purgeChildlessChannels(): Promise<void> {\n if (this.readyDevices.size === 0) {\n return;\n }\n const empty = childlessChannelIds(await this.getAdapterObjectsAsync(), this.readyDevices, this.namespace);\n for (const fullId of empty) {\n try {\n await this.delObjectAsync(stripNamespace(fullId, this.namespace));\n } catch {\n // already removed together with its parent\n }\n }\n if (empty.length > 0) {\n this.log.debug(`removed ${empty.length} empty folder(s) left over from an earlier object tree`);\n }\n }\n\n /**\n * Remember every datapoint that already exists, ONCE per adapter run.\n *\n * @see knownDatapoints for why the create path alone cannot answer \"is this new?\"\n */\n private async snapshotExistingDatapoints(): Promise<void> {\n try {\n for (const [fullId, object] of Object.entries(await this.getAdapterObjectsAsync())) {\n if (object?.type === \"state\") {\n this.knownDatapoints.add(stripNamespace(fullId, this.namespace));\n }\n }\n } catch (e) {\n // Without the snapshot the balance would call every datapoint new; better to stay\n // silent about it than to log a wrong number.\n this.log.debug(`could not read the existing datapoints (${errorMessage(e)}); balance line disabled`);\n this.balanceDisabled = true;\n }\n }\n\n /**\n * Count a datapoint the device tree just created \u2014 new ones only.\n *\n * @param id the state id relative to the namespace\n */\n private noteDatapointCreated(id: string): void {\n if (this.knownDatapoints.has(id)) {\n return;\n }\n this.knownDatapoints.add(id);\n this.createdDatapoints++;\n this.scheduleDatapointBalance();\n }\n\n /**\n * Count removed datapoints, and let them count again should they ever come back.\n *\n * @param fullIds the removed ids, namespace included\n */\n private noteDatapointsRemoved(fullIds: readonly string[]): void {\n for (const fullId of fullIds) {\n this.knownDatapoints.delete(stripNamespace(fullId, this.namespace));\n this.removedDatapoints++;\n }\n if (fullIds.length > 0) {\n this.scheduleDatapointBalance();\n }\n }\n\n /**\n * Log the balance once the tree has settled. A device connects asynchronously and several\n * devices connect at once, so the line waits for quiet instead of firing per device \u2014 the\n * user made ONE change and reads ONE result.\n */\n private scheduleDatapointBalance(): void {\n if (this.balanceDisabled) {\n return;\n }\n this.clearTimeout(this.balanceTimer);\n this.balanceTimer = this.setTimeout(() => {\n this.balanceTimer = undefined;\n void (async () => {\n // The tree has settled: sweep the never-filled orphans FIRST, so their\n // removals land in the same balance line the user is about to read.\n try {\n await this.purgeNeverFilled();\n } catch (e) {\n this.log.debug(`orphan purge failed (${errorMessage(e)}); skipped for this run`);\n }\n // Then the folders those removals (or an earlier version's tree rework) left empty.\n try {\n await this.purgeChildlessChannels();\n } catch (e) {\n this.log.debug(`empty-folder purge failed (${errorMessage(e)}); skipped for this run`);\n }\n const parts: string[] = [];\n if (this.createdDatapoints > 0) {\n parts.push(`created ${this.createdDatapoints} datapoint(s)`);\n }\n if (this.removedDatapoints > 0) {\n parts.push(`removed ${this.removedDatapoints} datapoint(s)`);\n }\n this.createdDatapoints = 0;\n this.removedDatapoints = 0;\n // Silent when nothing changed: a plain restart must not write a line.\n if (parts.length > 0) {\n this.log.info(`Object tree updated: ${parts.join(\", \")}`);\n }\n })();\n }, DATAPOINT_BALANCE_SETTLE_MS);\n }\n\n /**\n * Create a device's header objects (the device node, its info channel and a\n * per-device connection indicator) so its state is visible even while offline.\n *\n * @param deviceId the id-safe device id\n * @param ip the device's current address (from config or discovery)\n */\n private async ensureDeviceHeader(deviceId: string, ip: string): Promise<void> {\n // statusStates.onlineId lets the admin paint a green/red reachability symbol on the\n // device object itself (as govee does), fed by the per-device connection state.\n // extendObject with preserve:name so an upgrade adds the symbol without overwriting\n // a name the user changed.\n // A device that has not reported its model yet would sit in the tree without any\n // symbol \u2014 an upgraded instance shows that on every start before the first report,\n // and a device that never answers shows it for good. Seed the default silhouette,\n // but only when there is none: overwriting would flip a soundbar back to the\n // receiver default for the seconds until its model arrives.\n let icon: string | undefined;\n try {\n const existing = await this.getObjectAsync(deviceId);\n icon = existing?.common?.icon ? undefined : iconForModel(undefined);\n } catch {\n icon = undefined;\n }\n await this.extendObject(\n deviceId,\n {\n type: \"device\",\n common: {\n name: deviceId,\n ...(icon ? { icon } : {}),\n statusStates: { onlineId: `${this.namespace}.${deviceId}.info.connection` },\n },\n native: {},\n },\n { preserve: { common: [\"name\"] } },\n );\n await this.setObjectNotExistsAsync(`${deviceId}.info`, {\n type: \"channel\",\n common: { name: tName(\"Info\") },\n native: {},\n });\n await this.setObjectNotExistsAsync(`${deviceId}.info.connection`, {\n type: \"state\",\n common: {\n name: tName(\"Connected\"),\n type: \"boolean\",\n role: \"indicator.reachable\",\n read: true,\n write: false,\n def: false,\n },\n native: {},\n });\n // Model name shown on the device-manager card. Filled by whichever transport reports it\n // (YNCA MODELNAME, YXC/XML model); created here so the card's model line binds even for an\n // offline device or a transport that does not report a model.\n await this.setObjectNotExistsAsync(`${deviceId}.info.model`, {\n type: \"state\",\n common: { name: tName(\"Model\"), type: \"string\", role: \"text\", read: true, write: false, def: \"\" },\n native: {},\n });\n // The device's address \u2014 for a discovered device it lived only in the adapter's\n // internals, so no diagnosis (log capture, browser access to the device's own pages)\n // could name it without a network search. Refreshed every start: DHCP may move it.\n await this.setObjectNotExistsAsync(`${deviceId}.info.ip`, {\n type: \"state\",\n common: { name: tName(\"IP address\"), type: \"string\", role: \"info.ip\", read: true, write: false, def: \"\" },\n native: {},\n });\n await this.setState(`${deviceId}.info.ip`, { val: ip, ack: true });\n // Per-transport connection flags, fed by the live set from connectTransports and read live\n // by the device-manager card indicators. Created here so an offline device's card still\n // renders all three (false) instead of nothing.\n await this.setObjectNotExistsAsync(`${deviceId}.info.transports`, {\n type: \"channel\",\n common: { name: tName(\"Transports\") },\n native: {},\n });\n for (const proto of TRANSPORT_IDS) {\n await this.setObjectNotExistsAsync(`${deviceId}.info.transports.${proto}`, {\n type: \"state\",\n common: {\n name: tName(\"%s connected\", proto.toUpperCase()),\n type: \"boolean\",\n role: \"indicator.reachable\",\n read: true,\n write: false,\n def: false,\n },\n native: {},\n });\n }\n }\n\n /** The icon last written per device, so repeated model reports do not re-write the object. */\n private readonly deviceIcons = new Map<string, string>();\n\n /** The label this adapter wrote per device, with the rank of the source behind it. */\n private readonly deviceLabels = new Map<string, { name: string; rank: LabelRank }>();\n\n /**\n * Give the device node a name a user recognises, once the device reports one.\n *\n * An instance upgraded from the previous adapter carries the receiver's ip as its\n * device name \u2014 that adapter knew nothing but an ip, so the migration had nothing\n * else to call it. The object id stays that ip for good (history and visualisation\n * bindings hang off it), but the displayed name does not have to.\n *\n * A name the user typed is never touched, and the model never replaces a name the\n * device reported for itself \u2014 see {@link nextDeviceLabel}.\n *\n * @param deviceId the id-safe device id\n * @param candidate the reported name (a MusicCast zone name, or the model)\n * @param rank how trustworthy the candidate is\n */\n private async updateDeviceLabel(deviceId: string, candidate: string, rank: LabelRank): Promise<void> {\n const own = this.deviceLabels.get(deviceId);\n try {\n const current = (await this.getObjectAsync(deviceId))?.common?.name;\n const label = nextDeviceLabel(\n typeof current === \"string\" ? current : undefined,\n deviceId,\n candidate,\n rank,\n own?.name,\n own?.rank,\n );\n if (label === undefined) {\n return;\n }\n // Deliberately without `preserve: { common: [\"name\"] }`: nextDeviceLabel has just\n // established that the present name is the adapter's own placeholder, not a user's.\n await this.extendObject(deviceId, { common: { name: label } });\n this.deviceLabels.set(deviceId, { name: label, rank });\n this.log.debug(`${deviceId}: device name set to \"${label}\"`);\n } catch (e) {\n this.log.debug(`${deviceId}: setting the device name failed (${errorMessage(e)})`);\n }\n }\n\n /**\n * Paint the device-class silhouette on the device node once the model is known \u2014\n * detected from the reported model name, written only when it actually changes.\n *\n * @param deviceId the id-safe device id\n * @param model the reported model name\n */\n private async updateDeviceIcon(deviceId: string, model: string): Promise<void> {\n const icon = iconForModel(model);\n if (this.deviceIcons.get(deviceId) === icon) {\n return;\n }\n this.deviceIcons.set(deviceId, icon);\n try {\n await this.extendObject(deviceId, { common: { icon } });\n } catch (e) {\n this.log.debug(`${deviceId}: setting device icon failed (${errorMessage(e)})`);\n }\n }\n\n /**\n * Carry over the previous adapter's single-device config into the device table.\n * The old yamaha stored one receiver as `config.ip` (older installs: `config.IP`);\n * the new adapter uses a `devices` table, so an upgraded instance would otherwise\n * start with an empty table and lose its receiver. Persists the row so the admin\n * table shows it, and fills `this.config` in memory so this run already drives it.\n */\n private async migrateLegacyDevice(): Promise<void> {\n const config = this.config as unknown as Record<string, unknown>;\n const row = legacyDeviceRow(config);\n if (!row) {\n return;\n }\n // Fill the in-memory config first, so this run already drives the device even\n // if persisting the table below fails \u2014 persistence is a convenience for the\n // admin view, not a precondition for running.\n config.devices = [row];\n try {\n await this.extendForeignObjectAsync(`system.adapter.${this.namespace}`, { native: { devices: [row] } });\n this.log.info(`carried the previous single-device config (${row.ip}) over into the device table`);\n } catch (e) {\n this.log.warn(\n `could not persist the migrated device table (${errorMessage(e)}); ` + `running with the in-memory value`,\n );\n }\n }\n\n /**\n * Fold the removed `group_zones` toggle into `group_multiroom` \u2014 zone 2/3/4 now\n * belong to the multiroom group. Existing installs that had zones on but multiroom\n * off would otherwise lose their zone datapoints after the update.\n */\n private async migrateGroupZones(): Promise<void> {\n const config = this.config as unknown as Record<string, unknown>;\n if (!(\"group_zones\" in config)) {\n return;\n }\n if (config.group_zones) {\n config.group_multiroom = true;\n }\n delete config.group_zones;\n try {\n const obj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);\n if (obj?.native) {\n if (obj.native.group_zones) {\n obj.native.group_multiroom = true;\n }\n delete obj.native.group_zones;\n await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, obj);\n this.log.info(\"migrated group_zones setting into group_multiroom\");\n }\n } catch (e) {\n this.log.warn(`could not persist group_zones migration (${errorMessage(e)})`);\n }\n }\n\n /**\n * Bring one device online across ALL its transports: every protocol that answers\n * \u2014 YNCA (amp control over a held TCP connection), YXC (MusicCast), XML/YNC\n * (pre-2010) \u2014 connects in parallel on one object tree. Returns a connection handle\n * the supervisor keeps, or null when no transport answers this attempt. Each\n * datapoint is owned by exactly one transport (owner-policy), so the mappers never\n * collide on a shared id.\n *\n * @param device the configured device record\n * @param pushReceiver the shared YXC push receiver\n * @param knownDeviceIps IPs of all configured devices, for resolving a multiroom client\n * @param reachability dedup for the \"no reachable transport\" warning (one instance per device,\n * held by the caller across retries \u2014 see {@link ReachabilityDedup})\n * @param yncaSubunitCache per-device cache of the YNCA AVAIL probe (skips the probe on reconnects)\n * @param probeMemory per-device memory for constant device answers (skips re-asking on reconnects)\n * @returns a connection handle, or null when no transport connected\n */\n private attemptDevice(\n device: DeviceRecord,\n pushReceiver: YxcPushReceiver,\n knownDeviceIps: Set<string>,\n reachability: ReachabilityDedup,\n yncaSubunitCache: YncaSubunitCache,\n probeMemory: ProbeMemory,\n ): Promise<ConnectionHandle | null> {\n return attemptDevice(device, {\n reachability,\n yncaSubunitCache,\n probeMemory,\n // Group gate for the YNCA sweep: a disabled group's functions are never even fetched.\n isEntryEnabled: id => isGroupEnabled(id, this.config as unknown as Record<string, unknown>),\n log: {\n debug: message => this.log.debug(message),\n info: message => this.log.info(message),\n warn: message => this.log.warn(message),\n },\n upsertObject: async (id, def) => {\n // Gate on the datapoint group: a switched-off group's objects are not created. The id is\n // \"<deviceId>.<relativeId>\"; groupOf reads the relative part.\n if (!isGroupEnabled(id.slice(id.indexOf(\".\") + 1), this.config as unknown as Record<string, unknown>)) {\n return;\n }\n await this.extendObject(id, { type: def.type, common: def.common, native: {} });\n if (def.type === \"state\") {\n this.noteDatapointCreated(id);\n this.touchedThisRun.add(id);\n }\n },\n setStateAck: (id, value) => {\n // Same group gate as upsertObject, so a switched-off group seeds no orphan value either.\n if (!isGroupEnabled(id.slice(id.indexOf(\".\") + 1), this.config as unknown as Record<string, unknown>)) {\n return;\n }\n this.writeState(id, value);\n // A model report also decides the device-class icon on the device node \u2014 and, for a\n // device still carrying the ip it was migrated with, its readable name.\n if (id.endsWith(\".info.model\") && typeof value === \"string\" && value.length > 0) {\n const reporting = id.slice(0, id.indexOf(\".\"));\n void this.updateDeviceIcon(reporting, value);\n void this.updateDeviceLabel(reporting, value, LABEL_RANK.model);\n }\n },\n onDeviceName: name => void this.updateDeviceLabel(device.id, name, LABEL_RANK.deviceName),\n timers: {\n schedule: (handler, ms) => (this.unloading ? undefined : this.setTimeout(handler, ms)),\n cancel: handle => this.clearTimeout(handle),\n },\n registerPush: (ip, onPush) => pushReceiver.register(ip, onPush),\n pushActive: () => pushReceiver.isListening(),\n scheduleKeepalive: (handler, ms) => {\n if (this.unloading) {\n return () => {};\n }\n const timer = this.setInterval(handler, ms);\n return () => {\n if (timer) {\n this.clearInterval(timer);\n }\n };\n },\n xmlPollIntervalMs: this.xmlPollIntervalMs(),\n onTransports: names => this.setTransports(device.id, names),\n knownDeviceIps,\n });\n }\n\n /**\n * Route a state change to every device's supervisor (each forwards to its\n * active controller, which ignores ids outside its subtree and its acked echoes).\n *\n * @param id the full state id\n * @param state the new state (null when deleted)\n */\n private onStateChange(id: string, state: ioBroker.State | null | undefined): void {\n if (!state) {\n return;\n }\n const relative = stripNamespace(id, this.namespace);\n // The adapter subscribes to its whole namespace, so every one of its own acked writes\n // comes back here too \u2014 during a sweep that is hundreds of events. Route by the id's\n // first segment instead of offering each one to every device in turn.\n const deviceId = relative.slice(0, relative.indexOf(\".\"));\n this.supervisorById.get(deviceId)?.handleStateChange(relative, state.ack, state.val);\n }\n\n /**\n * Synchronous teardown \u2014 no await, call the callback immediately (SIGKILL otherwise).\n *\n * @param callback function to invoke once teardown is complete\n */\n private onUnload(callback: () => void): void {\n try {\n this.unloading = true;\n this.clearTimeout(this.balanceTimer);\n this.clearTimeout(this.rediscoverTimer);\n this.pushReceiver?.close();\n for (const supervisor of this.supervisors) {\n supervisor.close();\n }\n // A stopped adapter talks to nothing, so no device may keep claiming to be connected \u2014\n // that state paints the symbol on the device object (statusStates.onlineId), and the\n // instance-wide info.connection alone would leave every device green. The protocol\n // flags and the overview go with them; devicesTotal stays, how many devices there are\n // did not change.\n //\n // The callback goes LAST, after the writes: reporting \"done\" straight away loses them,\n // the host tears the process down as soon as it is told.\n const writes: Promise<unknown>[] = [this.setState(\"info.connection\", { val: false, ack: true })];\n for (const deviceId of this.deviceConnected.keys()) {\n this.deviceConnected.set(deviceId, false);\n writes.push(this.setState(`${deviceId}.info.connection`, { val: false, ack: true }));\n // The protocol flags on the card go down with the connection \u2014 a stopped adapter\n // is connected over no protocol.\n for (const proto of TRANSPORT_IDS) {\n writes.push(this.setState(`${deviceId}.info.transports.${proto}`, { val: false, ack: true }));\n }\n }\n writes.push(this.setState(\"info.devicesOnline\", { val: 0, ack: true }));\n writes.push(this.setState(\"info.devicesAllOnline\", { val: false, ack: true }));\n void Promise.all(writes)\n .catch(() => {\n /* states DB already going down \u2014 nothing left to report to */\n })\n .finally(callback);\n return;\n } catch {\n // fall through\n }\n callback();\n }\n\n /**\n * Auto-discovery for an empty device table: scan the network, merge the finds with\n * the devices remembered from earlier runs (standby protection), persist the merged\n * set and return it. XML/pre-2010 receivers do not answer SSDP and never appear here.\n *\n * @returns the device records to run this session\n */\n private async autoDiscover(): Promise<DeviceRecord[]> {\n const store = discoveredStoreDeps(this);\n const known = await readDiscovered(store);\n if (known.length > 0) {\n // Remembered devices start NOW \u2014 the network search used to gate every restart\n // by its collect window although the devices were already known. It still runs,\n // in the background, to pick up newcomers (see discoverAdditionalDevices).\n this.log.info(`setting up ${known.length} remembered device(s); the network search runs in the background`);\n return known;\n }\n this.log.info(\"auto-discovery via SSDP (older XML-only devices must be added manually)\");\n const merged = await this.runDiscovery();\n this.log.info(`setting up ${merged.length} discovered device(s)...`);\n return merged;\n }\n\n /**\n * Search the network, merge with the remembered devices, and persist the result.\n * Shared by the blocking first-setup path and the background search.\n *\n * @returns the merged device records\n */\n private async runDiscovery(): Promise<DeviceRecord[]> {\n // Every search counts against the throttle, whoever asked for it \u2014 otherwise the first\n // offline device would fire another one right behind the start-up search.\n this.lastRediscovery = Date.now();\n const store = discoveredStoreDeps(this);\n const known = await readDiscovered(store);\n let found: Array<{ ip: string; name: string }> = [];\n try {\n found = await discoverYamaha({\n search: (target, ms) => this.ssdpSearch(target, ms),\n fetch: url => this.fetchUrl(url),\n log: { debug: message => this.log.debug(message), warn: message => this.log.warn(message) },\n });\n } catch (e) {\n this.log.warn(`auto-discovery scan failed, using the remembered devices: ${errorMessage(e)}`);\n }\n const merged = mergeDiscovered(known, found, (dropped, takenId) =>\n this.log.warn(`discovered device \"${dropped}\" skipped \u2014 its object id \"${takenId}\" is already taken`),\n );\n // Devices the user deleted from the card list stay out \u2014 otherwise the next search simply\n // undoes the delete.\n const ignored = new Set(await readIgnored(ignoredStoreDeps(this)));\n const kept = ignored.size > 0 ? merged.filter(device => !ignored.has(device.id)) : merged;\n await writeDiscovered(store, kept);\n return kept;\n }\n\n /**\n * Load a device's persisted YNCA subunit-cache (the AVAIL probe result) from its\n * device object's native part, wrapped so updates persist back there. The device\n * object is the right home: writing an instance object's native restarts the\n * adapter, a device object's does not.\n *\n * @param deviceId the id-safe device id\n * @returns the per-device cache\n */\n private async loadYncaSubunitCache(deviceId: string): Promise<YncaSubunitCache> {\n let stored: unknown;\n try {\n stored = (await this.getObjectAsync(deviceId))?.native?.yncaAvail;\n } catch {\n stored = undefined;\n }\n return createSubunitCache(isAvailSnapshot(stored) ? stored : undefined, snapshot => {\n this.persistDeviceNative(deviceId, { yncaAvail: snapshot ?? null });\n });\n }\n\n /**\n * Load a device's persisted probe memory (constant device answers: capabilities,\n * declared scenes/inputs, names) from its device object's native part, wrapped so\n * every change persists back there \u2014 the same home as the subunit cache. This is\n * what makes a restart fast: the object tree is rebuilt from the remembered\n * answers while only the live proofs and the value refresh still go to the device.\n *\n * @param deviceId the id-safe device id\n * @returns the per-device memory\n */\n private async loadProbeMemory(deviceId: string): Promise<ProbeMemory> {\n // Stored as a JSON STRING deliberately: extendObject MERGES nested objects, so a\n // dropped key would rise from the dead on the next persist \u2014 a string replaces.\n let initial: Record<string, unknown> | undefined;\n try {\n const stored = (await this.getObjectAsync(deviceId))?.native?.probeCache;\n if (typeof stored === \"string\") {\n const parsed: unknown = JSON.parse(stored);\n if (typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed)) {\n initial = parsed as Record<string, unknown>;\n }\n }\n } catch {\n initial = undefined;\n }\n return new ProbeMemory(initial, entries => {\n this.persistDeviceNative(deviceId, { probeCache: JSON.stringify(entries) });\n });\n }\n\n /**\n * The XML/YNC poll interval in milliseconds, from `config.xmlPollInterval`\n * (seconds, default 60).\n *\n * @returns the interval in ms\n */\n private xmlPollIntervalMs(): number {\n const seconds = Number((this.config as unknown as Record<string, unknown>).xmlPollInterval);\n return (Number.isFinite(seconds) && seconds > 0 ? seconds : 60) * 1000;\n }\n\n /**\n * Run an SSDP M-SEARCH and collect the responders' description URL and address.\n *\n * With a configured network interface the search leaves exactly that one; left empty it\n * leaves EVERY non-internal IPv4 interface at once (one socket each), because multicast\n * egress otherwise follows only the host's default route \u2014 on a multi-homed host whose\n * default route is not the AV network that means the receiver is never reached and nothing\n * is found. Responders from all interfaces are merged into one list; the caller\n * de-duplicates by address.\n *\n * @param target the search target (device type)\n * @param timeoutMs how long to collect responses\n * @returns the responders\n */\n private ssdpSearch(target: string, timeoutMs: number): Promise<Array<{ location: string; address: string }>> {\n return new Promise(resolve => {\n const bindAddrs = searchInterfaces(this.config.networkInterface, networkInterfaces());\n const responders: Array<{ location: string; address: string }> = [];\n const sockets: ReturnType<typeof createSocket>[] = [];\n let settled = false;\n const finish = (): void => {\n if (settled) {\n return;\n }\n settled = true;\n for (const socket of sockets) {\n try {\n socket.close();\n } catch {\n // already closed\n }\n }\n resolve(responders);\n };\n // Open one search socket bound to a single interface (or the default route when bindAddr\n // is undefined). Every socket shares the responders list and the one settle timeout.\n const searchFrom = (bindAddr: string | undefined): void => {\n const socket = createSocket(\"udp4\");\n sockets.push(socket);\n socket.on(\"message\", (msg, rinfo) => {\n const location = /LOCATION:\\s*(\\S+)/i.exec(msg.toString());\n if (location) {\n responders.push({ location: location[1], address: rinfo.address });\n }\n });\n socket.on(\"error\", err => {\n // One interface failing (typically a stale selected IP after a DHCP change) must not\n // kill the search on the others \u2014 warn and drop just this socket; the timeout still\n // resolves whatever the rest found.\n this.log.warn(\n `discovery socket failed${bindAddr ? ` on interface ${bindAddr}` : \"\"}: ${errorMessage(err)}${\n bindAddr ? \" \u2014 check the Network Interface setting\" : \"\"\n }`,\n );\n try {\n socket.close();\n } catch {\n // already closed\n }\n });\n const sendSearch = (): void => {\n if (settled) {\n return;\n }\n const msearch = `M-SEARCH * HTTP/1.1\\r\\nHOST: 239.255.255.250:1900\\r\\nMAN: \"ssdp:discover\"\\r\\nMX: 3\\r\\nST: ${target}\\r\\n\\r\\n`;\n try {\n socket.send(msearch, 1900, \"239.255.255.250\");\n } catch {\n // socket already closed by an error above\n }\n };\n socket.bind(0, bindAddr, () => {\n // Pin OUTGOING multicast to this interface. bind() only sets the source address; the\n // egress interface is IP_MULTICAST_IF \u2014 without it the OS uses its default route, so\n // the search can leave the wrong NIC on a multi-homed host (Node dgram docs).\n if (bindAddr) {\n try {\n socket.setMulticastInterface(bindAddr);\n } catch {\n this.log.info(`discovery: could not pin multicast egress to ${bindAddr} \u2014 using the default interface`);\n }\n }\n // Multicast is lossy and a single request can be dropped \u2014 repeat the M-SEARCH a few\n // times inside the collect window so one lost packet does not hide a receiver.\n for (let i = 0; i < SSDP_SEARCH_BURST; i++) {\n this.setTimeout(sendSearch, i * SSDP_SEARCH_INTERVAL_MS);\n }\n });\n };\n // Configured \u2192 that one interface; empty \u2192 every non-internal IPv4; none usable \u2192 default route.\n if (bindAddrs.length === 0) {\n searchFrom(undefined);\n } else {\n for (const bindAddr of bindAddrs) {\n searchFrom(bindAddr);\n }\n }\n this.setTimeout(finish, timeoutMs);\n });\n }\n\n /**\n * Fetch a URL over HTTP and resolve its body.\n *\n * @param url the URL to fetch\n * @returns the response body\n */\n private fetchUrl(url: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const req = httpGet(url, res => {\n let data = \"\";\n let bytes = 0;\n res.on(\"data\", chunk => {\n bytes += (chunk as Buffer).length;\n if (bytes > MAX_HTTP_BODY_BYTES) {\n // A description document is a few KB \u2014 whatever streams past the cap is not one.\n res.destroy(new Error(`description too large: ${url}`));\n return;\n }\n data += String(chunk);\n });\n // A connection dropped mid-body emits on the RESPONSE stream, not the request \u2014\n // without this handler that is an unhandled error event instead of a rejection.\n res.on(\"error\", reject);\n res.on(\"end\", () => resolve(data));\n });\n req.on(\"error\", reject);\n req.setTimeout(FETCH_TIMEOUT_MS, () => req.destroy(new Error(`fetch timed out: ${url}`)));\n });\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new Yamaha(options);\n} else {\n // Start the instance directly\n (() => new Yamaha())();\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { createSocket } from \"node:dgram\";\nimport { get as httpGet } from \"node:http\";\nimport { networkInterfaces } from \"node:os\";\nimport { attemptDevice } from \"./lib/attempt-device\";\nimport { searchInterfaces } from \"./lib/network-interfaces\";\nimport { isGroupEnabled } from \"./lib/catalog/groups\";\nimport { iconForModel } from \"./lib/device-type\";\nimport {\n childlessChannelIds,\n LABEL_RANK,\n type LabelRank,\n legacyDeviceRow,\n mergeDiscovered,\n neverWrittenStateIds,\n nextDeviceLabel,\n parseDevices,\n renamedObjectIds,\n staleObjects,\n stripNamespace,\n} from \"./lib/pure-helpers\";\nimport { errorMessage, MAX_HTTP_BODY_BYTES } from \"./lib/util\";\nimport { tName } from \"./lib/i18n\";\nimport { discoverYamaha } from \"./lib/discovery\";\nimport { readDiscovered, readIgnored, writeDiscovered } from \"./lib/discovered-store\";\nimport { discoveredStoreDeps, ignoredStoreDeps } from \"./lib/discovered-store-deps\";\nimport { YxcPushReceiver } from \"./lib/yxc/push-receiver\";\nimport { YamahaDeviceManagement } from \"./device-management\";\nimport type { DeviceRecord } from \"./lib/types\";\nimport type { ObjectDef } from \"./lib/catalog/types\";\nimport { DeviceSupervisor, type ConnectionHandle } from \"./lib/lifecycle/device-supervisor\";\nimport { ReconnectStrategy } from \"./lib/lifecycle/reconnect-strategy\";\nimport { ReachabilityDedup } from \"./lib/lifecycle/reachability-dedup\";\nimport { createSubunitCache, isAvailSnapshot, type YncaSubunitCache } from \"./lib/ynca/subunit-cache\";\nimport { ProbeMemory } from \"./lib/lifecycle/probe-memory\";\n\n/** Supervisor reconnect backoff bounds (exponential: 1s, 2s \u2026 capped at 60s). */\nconst RECONNECT_BASE_MS = 1000;\nconst RECONNECT_MAX_MS = 60000;\n\n/** Abort a discovery description fetch after this long, so a dead device cannot hang it. */\nconst FETCH_TIMEOUT_MS = 4000;\n\n/** How often the discovery M-SEARCH is repeated \u2014 multicast is lossy, one dropped packet must not hide a receiver. */\nconst SSDP_SEARCH_BURST = 3;\n/** Spacing between the repeated M-SEARCH sends, inside the collect window. */\nconst SSDP_SEARCH_INTERVAL_MS = 1000;\n\n/** The three transports in attempt order \u2014 also the per-transport `info.transports.*` state ids. */\nconst TRANSPORT_IDS = [\"ynca\", \"yxc\", \"xml\"] as const;\n\n/**\n * How long the datapoint balance waits for quiet before it logs. Devices connect\n * asynchronously and in parallel, so the line has to outlast the slowest of them.\n */\nconst DATAPOINT_BALANCE_SETTLE_MS = 5000;\n\n/**\n * Shortest gap between two network searches triggered by an offline auto-found device. A\n * receiver that moved to another address answers nowhere else, so the search is the only way\n * back to it \u2014 but a device that is simply switched off must not turn that into a scan loop.\n */\nconst REDISCOVER_MIN_INTERVAL_MS = 300000;\n\n/**\n * ioBroker.yamaha \u2014 controls Yamaha AV receivers and MusicCast devices.\n *\n * Each configured device is driven by a supervisor that keeps a multi-transport\n * handle online: every protocol the device answers \u2014 YNCA (amp control over a held\n * TCP connection, event-pushed), YXC (MusicCast, push + poll), XML/YNC (pre-2010,\n * polled over HTTP) \u2014 connects in parallel on one object tree, each datapoint owned\n * by the best-fitting transport. All YXC devices share one UDP push receiver, keyed\n * by source IP.\n */\nexport class Yamaha extends utils.Adapter {\n private readonly supervisors: DeviceSupervisor[] = [];\n /** deviceId \u2192 its supervisor, so a state change goes to ONE device, not to all of them. */\n private readonly supervisorById = new Map<string, DeviceSupervisor>();\n private readonly deviceConnected = new Map<string, boolean>();\n /** deviceId \u2192 the record it is currently running with, so an address change is visible. */\n private readonly deviceRecords = new Map<string, DeviceRecord>();\n /** The addresses of all supervised devices \u2014 a multiroom group resolves its clients through it. */\n private readonly knownDeviceIps = new Set<string>();\n /** True while the instance runs whatever the network search finds (empty device table). */\n private autoMode = false;\n /** Armed while an auto-found device is offline: the search that can bring it back. */\n private rediscoverTimer: ioBroker.Timeout | undefined;\n /** When the last background search ran, so the retry cannot become a scan loop. */\n private lastRediscovery = 0;\n private pushReceiver: YxcPushReceiver | undefined;\n /**\n * Set the moment teardown begins: a connect attempt still in flight then resolves into\n * a closing adapter and must not arm keepalives/timers any more \u2014 the framework would\n * refuse them anyway, but with a warn line per attempt (\"setInterval called, but\n * adapter is shutting down\", seen live on the 1.7.0 upgrade restart).\n */\n private unloading = false;\n /** Device-manager backend: the receivers as cards with add/edit/delete. */\n private readonly deviceManagement: YamahaDeviceManagement;\n /**\n * Every datapoint that existed when this run started, filled ONCE before the cleanup and\n * before any device connects. Without it the balance below would report the whole tree as\n * new on every restart: `upsertObject` runs `extendObject` on every state it touches (the\n * role/unit retrofit), so \"did the create path run?\" is not the same question as \"is this\n * datapoint new?\".\n */\n private readonly knownDatapoints = new Set<string>();\n /** State ids (namespace-relative) some transport upserted in THIS run \u2014 live claims. */\n private readonly touchedThisRun = new Set<string>();\n /** Devices that reported connected at least once in this run (gates the orphan purge). */\n private readonly readyDevices = new Set<string>();\n private createdDatapoints = 0;\n private removedDatapoints = 0;\n /** Debounce for the balance line, so one config change produces ONE line, not one per device. */\n private balanceTimer: ioBroker.Timeout | undefined;\n /** Set when the start-up snapshot failed \u2014 a balance without it would be wrong, so none is written. */\n private balanceDisabled = false;\n /**\n * Latched after the first failed database write, so an outage warns once and the\n * repeats stay at debug until a write goes through again (nut2 `failedUps` pattern).\n */\n private stateWritesFailing = false;\n\n /**\n * @param options adapter options passed through by js-controller\n */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"yamaha\",\n });\n\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"stateChange\", this.onStateChange.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n this.deviceManagement = new YamahaDeviceManagement(this);\n }\n\n /** Start a supervisor for each configured device, then subscribe to state changes. */\n private async onReady(): Promise<void> {\n try {\n this.log.info('starting \u2014 a \"ready\" message will follow for each device');\n await this.setState(\"info.connection\", { val: false, ack: true });\n await this.migrateLegacyDevice();\n await this.migrateGroupZones();\n // The device list is the switch: filled \u2192 use exactly those (manual); empty\n // \u2192 discover on the network and run what is found (auto). XML/pre-2010 devices\n // never answer SSDP, so they are always added manually.\n const configured = parseDevices(this.config.devices, (dropped, takenId) =>\n this.log.warn(`device \"${dropped}\" skipped \u2014 its object id \"${takenId}\" is already used by another device`),\n );\n const devices = configured.length > 0 ? configured : await this.autoDiscover();\n if (this.unloading) {\n // Stopped during the initial network search: it resolves on its own clock, and\n // everything below \u2014 push socket, subscriptions, device sockets and timers \u2014\n // would come up on an instance that is already gone, with nothing left to close it.\n return;\n }\n this.autoMode = configured.length === 0;\n // The start-up search (blocking first setup, or the background one below) is the first\n // search of this run \u2014 an offline device must not fire another one right behind it.\n this.lastRediscovery = Date.now();\n for (const device of devices) {\n this.knownDeviceIps.add(device.ip);\n }\n // Before the cleanup and before any device connects \u2014 see knownDatapoints.\n await this.snapshotExistingDatapoints();\n await this.cleanupStaleObjects(new Set(devices.map(device => device.id)));\n await this.ensureInstanceInfoObjects();\n await this.subscribeToStates();\n const pushReceiver = new YxcPushReceiver({\n log: { debug: message => this.log.debug(message), warn: message => this.log.warn(message) },\n schedule: (cb, ms) => this.setTimeout(cb, ms),\n cancel: handle => this.clearTimeout(handle),\n });\n pushReceiver.start();\n this.pushReceiver = pushReceiver;\n if (configured.length > 0) {\n this.log.info(`setting up ${devices.length} configured device(s)...`);\n }\n for (const device of devices) {\n await this.startDevice(device, pushReceiver);\n }\n this.writeDeviceOverview();\n // Auto mode with remembered devices: they started WITHOUT waiting for the network\n // search \u2014 it runs behind them, adds newcomers and moves a device that changed address.\n if (this.autoMode && devices.length > 0) {\n void this.discoverAdditionalDevices(pushReceiver);\n }\n } catch (e) {\n this.log.error(`onReady failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * Bring one device under supervision: header objects, disconnected stamp, the\n * per-device caches, and the supervisor that keeps it connected. Factored out of\n * onReady so the background discovery can start a late-found device the same way.\n *\n * @param device the device record\n * @param pushReceiver the shared YXC push receiver\n */\n private async startDevice(device: DeviceRecord, pushReceiver: YxcPushReceiver): Promise<void> {\n // Both callers check `unloading` after their network search resolves (onReady and\n // discoverAdditionalDevices) \u2014 a device handed over after onUnload never gets here.\n this.deviceConnected.set(device.id, false);\n this.deviceRecords.set(device.id, { ...device });\n this.knownDeviceIps.add(device.ip);\n await this.ensureDeviceHeader(device.id, device.ip);\n // Stamp it disconnected BEFORE the first attempt: ioBroker keeps a state's last value\n // forever, so a crash or a power cut would otherwise leave the device green until it\n // reports again \u2014 and a device that never answers would stay green for good.\n await this.setState(`${device.id}.info.connection`, { val: false, ack: true });\n // The three protocol flags follow the same rule: left at their last value, a crash\n // would show \"YNCA connected\" on the card next to a red connection dot \u2014 for good, if\n // the device never answers again.\n this.setTransports(device.id, []);\n const reachability = new ReachabilityDedup();\n const subunitCache = await this.loadYncaSubunitCache(device.id);\n // Held here, not in the controllers: those are rebuilt on every connection attempt;\n // persisted at the device object, so a restart starts from the remembered answers.\n const probeMemory = await this.loadProbeMemory(device.id);\n const supervisor = new DeviceSupervisor({\n attempt: () =>\n this.attemptDevice(device, pushReceiver, this.knownDeviceIps, reachability, subunitCache, probeMemory),\n schedule: (cb, ms) => this.setTimeout(cb, ms),\n cancel: handle => this.clearTimeout(handle as ioBroker.Timeout | undefined),\n onConnectionChange: connected => this.reportConnection(device.id, connected),\n backoff: new ReconnectStrategy(RECONNECT_BASE_MS, RECONNECT_MAX_MS),\n log: {\n debug: message => this.log.debug(message),\n info: message => this.log.info(message),\n warn: message => this.log.warn(message),\n },\n });\n this.supervisors.push(supervisor);\n this.supervisorById.set(device.id, supervisor);\n supervisor.start();\n }\n\n /**\n * The background half of auto-discovery: search the network, bring devices online that are\n * not supervised yet, and move a device that answers at a NEW address. The remembered devices\n * did not wait for this \u2014 the search window (seconds) used to gate every restart although the\n * devices were already known.\n *\n * The address half matters because the object id \u2014 and with it the whole tree, its history and\n * every visualisation binding \u2014 is fixed to the device, not to where it sits. A receiver that\n * moved by DHCP is the same device at a new address, so its supervisor is rebuilt there instead\n * of retrying an address nobody answers at any more.\n *\n * @param pushReceiver the shared YXC push receiver\n */\n private async discoverAdditionalDevices(pushReceiver: YxcPushReceiver): Promise<void> {\n try {\n const merged = await this.runDiscovery();\n if (this.unloading) {\n return; // the search outlived the adapter \u2014 nothing may start now\n }\n let changed = false;\n for (const device of merged) {\n const running = this.deviceRecords.get(device.id);\n if (!running) {\n this.log.info(`discovery found ${device.id} \u2014 setting up`);\n await this.startDevice(device, pushReceiver);\n changed = true;\n } else if (running.ip !== device.ip) {\n this.log.info(`${device.id}: address changed from ${running.ip} to ${device.ip} \u2014 reconnecting it there`);\n this.knownDeviceIps.delete(running.ip);\n this.stopDevice(device.id);\n await this.startDevice(device, pushReceiver);\n changed = true;\n }\n }\n if (changed) {\n this.writeDeviceOverview();\n }\n } catch (e) {\n this.log.warn(`background discovery failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * Arm a background search because an auto-found device is offline \u2014 the only way back to a\n * receiver that moved to another address, since it answers at the remembered one no more.\n * Throttled: a device that is merely switched off must not turn this into a scan loop, and\n * one timer covers however many devices are down.\n */\n private scheduleRediscovery(): void {\n if (!this.autoMode || this.unloading || this.rediscoverTimer !== undefined) {\n return;\n }\n const receiver = this.pushReceiver;\n if (!receiver) {\n return;\n }\n const due = Math.max(0, REDISCOVER_MIN_INTERVAL_MS - (Date.now() - this.lastRediscovery));\n this.rediscoverTimer = this.setTimeout(() => {\n this.rediscoverTimer = undefined;\n this.lastRediscovery = Date.now();\n if (!this.unloading) {\n void this.discoverAdditionalDevices(receiver);\n }\n }, due);\n }\n\n /**\n * Stop supervising one device and release its supervisor. The object tree is untouched \u2014\n * a readdress puts the same device straight back on it.\n *\n * @param deviceId the id-safe device id\n */\n private stopDevice(deviceId: string): void {\n const supervisor = this.supervisorById.get(deviceId);\n if (!supervisor) {\n return;\n }\n supervisor.close();\n this.supervisorById.delete(deviceId);\n const index = this.supervisors.indexOf(supervisor);\n if (index >= 0) {\n this.supervisors.splice(index, 1);\n }\n }\n\n /**\n * Remove one device for good: stop talking to it and delete its object tree.\n *\n * Driven by the device manager's delete action. Deleting a discovered device used to only\n * empty the remembered list \u2014 the supervisor kept the connection, the tree stayed, and the card\n * came back on the next start. Now the delete is what it says; the id is additionally kept in\n * the ignored list (device manager) so a later search does not put the device back.\n *\n * @param deviceId the id-safe device id\n */\n public async removeDevice(deviceId: string): Promise<void> {\n this.stopDevice(deviceId);\n const record = this.deviceRecords.get(deviceId);\n if (record) {\n this.knownDeviceIps.delete(record.ip);\n }\n this.deviceRecords.delete(deviceId);\n this.deviceConnected.delete(deviceId);\n this.readyDevices.delete(deviceId);\n try {\n await this.delObjectAsync(deviceId, { recursive: true });\n } catch (e) {\n this.log.warn(`could not remove the object tree of \"${deviceId}\" (${errorMessage(e)})`);\n }\n this.writeState(\"info.connection\", [...this.deviceConnected.values()].some(Boolean));\n this.writeDeviceOverview();\n }\n\n /**\n * Subscribe to the adapter's own states \u2014 OBSERVED, like every other database call.\n *\n * `subscribeStates` without a callback returns a promise, and its wildcard branch reads the\n * matching objects first: any failure there ends in `maybeCallbackWithError`, which rejects\n * for everything except the plain \"database closed\" case (js-controller-common-db source).\n * Left unawaited that is an unhandled rejection \u2014 and js-controller turns those into an\n * adapter stop, the same trap the nine bare state writes carried.\n *\n * A failure is loud but not fatal: without the subscription the tree still fills from the\n * devices, only user writes stop being applied. Saying so beats a silent half-working\n * instance, and beats losing the whole start over it.\n */\n private async subscribeToStates(): Promise<void> {\n try {\n await this.subscribeStatesAsync(\"*\");\n } catch (e) {\n this.log.error(\n `could not subscribe to state changes (${errorMessage(e)}) \u2014 the tree still updates, ` +\n `but writes to datapoints will not reach the device until the instance is restarted`,\n );\n }\n }\n\n /**\n * Aggregate one device's connection state into the adapter's `info.connection`\n * (true while at least one device is connected).\n *\n * @param deviceId the device reporting\n * @param connected whether that device is currently connected\n */\n private reportConnection(deviceId: string, connected: boolean): void {\n if (connected && !this.readyDevices.has(deviceId)) {\n this.readyDevices.add(deviceId);\n // Arm the settle pass even when the connect created nothing new \u2014 the once-per-\n // version orphan purge rides the same settled moment as the balance line.\n this.scheduleDatapointBalance();\n }\n this.deviceConnected.set(deviceId, connected);\n this.writeState(`${deviceId}.info.connection`, connected);\n // A drop clears the per-transport flags; a (re)connect sets them again via onTransports.\n if (!connected) {\n this.setTransports(deviceId, []);\n // In auto mode the device may simply have moved \u2014 only a search can find it again.\n this.scheduleRediscovery();\n }\n const anyConnected = [...this.deviceConnected.values()].some(Boolean);\n this.writeState(\"info.connection\", anyConnected);\n this.writeDeviceOverview();\n }\n\n /**\n * The three overview datapoints: how many devices this instance runs, how many are\n * connected right now, and whether that is all of them. Derived from the SAME map that\n * feeds the per-device markers and written in the same round \u2014 computed separately they\n * would drift away from what the single devices say.\n *\n * `devicesAllOnline` needs at least one device: zero of zero is not \"everything is fine\".\n */\n private writeDeviceOverview(): void {\n const total = this.deviceConnected.size;\n const online = [...this.deviceConnected.values()].filter(Boolean).length;\n this.writeState(\"info.devicesTotal\", total);\n this.writeState(\"info.devicesOnline\", online);\n this.writeState(\"info.devicesAllOnline\", total > 0 && online === total);\n }\n\n /**\n * Reflect the live transport set into a device's `info.transports.*` flags so the\n * device-manager card shows which protocols (YNCA/YXC/XML) are connected right now.\n *\n * @param deviceId the id-safe device id\n * @param names the transports live now (empty on a drop)\n */\n private setTransports(deviceId: string, names: string[]): void {\n const live = new Set(names);\n for (const proto of TRANSPORT_IDS) {\n this.writeState(`${deviceId}.info.transports.${proto}`, live.has(proto));\n }\n }\n\n /**\n * Write a state with ack \u2014 and OBSERVE the promise. js-controller turns an unhandled\n * promise rejection into an adapter stop (`_exceptionHandler` \u2192 exit code\n * UNCAUGHT_EXCEPTION, read in the controller source), and `setState` rejects whenever\n * the states database is not reachable for a moment (`ERROR_DB_CLOSED`, or a pending\n * command cancelled by a reconnecting Redis). Nine fire-and-forget writes used to run\n * bare: one hiccup while a device pushed a value would have restarted the whole\n * instance. The failure lands in the log instead \u2014 once per outage at warn, then at\n * debug until a write succeeds again; during teardown it is expected and stays silent.\n *\n * @param id the state id (namespace-relative)\n * @param value the value to write\n */\n private writeState(id: string, value: ioBroker.StateValue): void {\n this.setState(id, { val: value, ack: true }).then(\n () => {\n this.stateWritesFailing = false;\n },\n (e: unknown) => this.noteWriteFailure(`state ${id}`, e),\n );\n }\n\n /**\n * Persist a device object's `native` part \u2014 observed like {@link writeState}. The two\n * per-device caches (YNCA subunit probe, probe memory) persist through it.\n *\n * @param deviceId the device object id\n * @param native the native fields to merge into the object\n */\n private persistDeviceNative(deviceId: string, native: Record<string, unknown>): void {\n this.extendObject(deviceId, { native }).then(\n () => {\n this.stateWritesFailing = false;\n },\n (e: unknown) => this.noteWriteFailure(`device object ${deviceId}`, e),\n );\n }\n\n /**\n * Log a failed database write: warn on the first failure of an outage, debug for the\n * repeats, silence while unloading (the database is going down with us).\n *\n * @param what which write failed, for the log line\n * @param e the rejection reason\n */\n private noteWriteFailure(what: string, e: unknown): void {\n if (this.unloading) {\n return;\n }\n const message = `could not write ${what} (${errorMessage(e)})`;\n if (this.stateWritesFailing) {\n this.log.debug(message);\n return;\n }\n this.stateWritesFailing = true;\n this.log.warn(`${message} \u2014 repeats stay at debug until a write succeeds`);\n }\n\n /**\n * One-shot startup cleanup: delete every object that does not belong to a\n * configured device (the previous adapter's whole tree, and any device dropped\n * from the config). Runs before the devices connect; a configured device's\n * subtree is kept whether or not it has connected yet.\n *\n * @param deviceIds the ids of the currently configured devices\n */\n private async cleanupStaleObjects(deviceIds: Set<string>): Promise<void> {\n const allObjects = await this.getAdapterObjectsAsync();\n const existing = Object.keys(allObjects);\n const stale = staleObjects(existing, deviceIds, this.namespace);\n // Old states this version renamed/moved (e.g. system.model -> info.model): delete the\n // old object so it does not linger orphaned beside the new one under a kept device.\n const renamed = renamedObjectIds(existing, deviceIds, this.namespace);\n // Objects whose datapoint group the user switched off \u2014 remove them so turning a group from\n // on to off cleans up its whole subtree (a toggle change restarts the instance, so this runs).\n const config = this.config as unknown as Record<string, unknown>;\n const disabled = existing.filter(full => {\n for (const deviceId of deviceIds) {\n const base = `${this.namespace}.${deviceId}.`;\n if (full.startsWith(base) && !isGroupEnabled(full.slice(base.length), config)) {\n return true;\n }\n }\n return false;\n });\n for (const fullId of [...stale, ...renamed, ...disabled]) {\n try {\n await this.delObjectAsync(stripNamespace(fullId, this.namespace));\n } catch {\n // already removed together with its parent\n }\n }\n // The reasons stay available for diagnosis; the user sees ONE balance line instead of\n // three counts they have to add up themselves.\n if (stale.length > 0) {\n this.log.debug(`removed ${stale.length} object(s) from a previous configuration`);\n }\n if (renamed.length > 0) {\n this.log.debug(`removed ${renamed.length} renamed object(s) from an earlier version`);\n }\n if (disabled.length > 0) {\n this.log.debug(`removed ${disabled.length} object(s) from switched-off datapoint groups`);\n }\n // Channels and device nodes go with them, but only datapoints are counted \u2014 that is what\n // the user switched on or off, and what they look for in the object tree.\n this.noteDatapointsRemoved(\n [...stale, ...renamed, ...disabled].filter(fullId => allObjects[fullId]?.type === \"state\"),\n );\n }\n\n /**\n * Once per adapter version and device (marker `native.purgeVersion` on the DEVICE\n * object): remove read-capable states under a CONNECTED device that never carried a\n * value and were not (re)created by this run's transports \u2014 over-declarations of an\n * earlier adapter version that today's claim-with-proof creation no longer makes.\n * Deleting them is lossless (no value, no history). Runs after the tree settled, so\n * a device that has not connected in this run keeps its tree untouched \u2014 its sweep\n * happens on the first start that reaches it.\n */\n private async purgeNeverFilled(): Promise<void> {\n const candidates: string[] = [];\n for (const deviceId of this.readyDevices) {\n const device = await this.getObjectAsync(deviceId);\n if ((device?.native as Record<string, unknown> | undefined)?.purgeVersion !== this.version) {\n candidates.push(deviceId);\n }\n }\n if (candidates.length === 0) {\n return;\n }\n const allObjects = await this.getAdapterObjectsAsync();\n const states = await this.getStatesAsync(\"*\");\n const purged = neverWrittenStateIds(allObjects, states, new Set(candidates), this.namespace).filter(\n fullId => !this.touchedThisRun.has(stripNamespace(fullId, this.namespace)),\n );\n for (const fullId of purged) {\n try {\n await this.delObjectAsync(stripNamespace(fullId, this.namespace));\n } catch {\n // already gone\n }\n }\n for (const deviceId of candidates) {\n await this.extendObject(deviceId, { native: { purgeVersion: this.version } });\n }\n if (purged.length > 0) {\n this.log.debug(`removed ${purged.length} never-filled object(s) from an earlier version`);\n this.noteDatapointsRemoved(purged);\n }\n }\n\n /**\n * Remove folders that hold no datapoint any more, under the devices that connected this run.\n *\n * The two sweeps above only ever delete datapoints, so a folder emptied by a tree rework stays\n * behind and promises content it can never get \u2014 `player.server` is the live case: the v2.0.0\n * migration deletes the SERVER source's playback copies, and the new tree gives that source no\n * datapoint of its own. Runs on every start, not once per version: an empty folder is wrong\n * whenever it is found, and re-reading the objects after the orphan purge catches the ones that\n * purge just emptied. Not counted in the datapoint balance \u2014 a folder is not a datapoint.\n */\n private async purgeChildlessChannels(): Promise<void> {\n if (this.readyDevices.size === 0) {\n return;\n }\n const empty = childlessChannelIds(await this.getAdapterObjectsAsync(), this.readyDevices, this.namespace);\n for (const fullId of empty) {\n try {\n await this.delObjectAsync(stripNamespace(fullId, this.namespace));\n } catch {\n // already removed together with its parent\n }\n }\n if (empty.length > 0) {\n this.log.debug(`removed ${empty.length} empty folder(s) left over from an earlier object tree`);\n }\n }\n\n /**\n * Remember every datapoint that already exists, ONCE per adapter run.\n *\n * @see knownDatapoints for why the create path alone cannot answer \"is this new?\"\n */\n private async snapshotExistingDatapoints(): Promise<void> {\n try {\n for (const [fullId, object] of Object.entries(await this.getAdapterObjectsAsync())) {\n if (object?.type === \"state\") {\n this.knownDatapoints.add(stripNamespace(fullId, this.namespace));\n }\n }\n } catch (e) {\n // Without the snapshot the balance would call every datapoint new; better to stay\n // silent about it than to log a wrong number.\n this.log.debug(`could not read the existing datapoints (${errorMessage(e)}); balance line disabled`);\n this.balanceDisabled = true;\n }\n }\n\n /**\n * Count a datapoint the device tree just created \u2014 new ones only.\n *\n * @param id the state id relative to the namespace\n */\n private noteDatapointCreated(id: string): void {\n if (this.knownDatapoints.has(id)) {\n return;\n }\n this.knownDatapoints.add(id);\n this.createdDatapoints++;\n this.scheduleDatapointBalance();\n }\n\n /**\n * Count removed datapoints, and let them count again should they ever come back.\n *\n * @param fullIds the removed ids, namespace included\n */\n private noteDatapointsRemoved(fullIds: readonly string[]): void {\n for (const fullId of fullIds) {\n this.knownDatapoints.delete(stripNamespace(fullId, this.namespace));\n this.removedDatapoints++;\n }\n if (fullIds.length > 0) {\n this.scheduleDatapointBalance();\n }\n }\n\n /**\n * Log the balance once the tree has settled. A device connects asynchronously and several\n * devices connect at once, so the line waits for quiet instead of firing per device \u2014 the\n * user made ONE change and reads ONE result.\n */\n private scheduleDatapointBalance(): void {\n if (this.balanceDisabled) {\n return;\n }\n this.clearTimeout(this.balanceTimer);\n this.balanceTimer = this.setTimeout(() => {\n this.balanceTimer = undefined;\n void (async () => {\n // The tree has settled: sweep the never-filled orphans FIRST, so their\n // removals land in the same balance line the user is about to read.\n try {\n await this.purgeNeverFilled();\n } catch (e) {\n this.log.debug(`orphan purge failed (${errorMessage(e)}); skipped for this run`);\n }\n // Then the folders those removals (or an earlier version's tree rework) left empty.\n try {\n await this.purgeChildlessChannels();\n } catch (e) {\n this.log.debug(`empty-folder purge failed (${errorMessage(e)}); skipped for this run`);\n }\n const parts: string[] = [];\n if (this.createdDatapoints > 0) {\n parts.push(`created ${this.createdDatapoints} datapoint(s)`);\n }\n if (this.removedDatapoints > 0) {\n parts.push(`removed ${this.removedDatapoints} datapoint(s)`);\n }\n this.createdDatapoints = 0;\n this.removedDatapoints = 0;\n // Silent when nothing changed: a plain restart must not write a line.\n if (parts.length > 0) {\n this.log.info(`Object tree updated: ${parts.join(\", \")}`);\n }\n })();\n }, DATAPOINT_BALANCE_SETTLE_MS);\n }\n\n /**\n * Refresh the adapter's OWN `info.*` objects.\n *\n * js-controller creates them from `io-package.json` `instanceObjects` when the instance is\n * added, and leaves an existing object's `common` alone on every later upgrade \u2014 so an\n * instance that predates a change keeps whatever the old version wrote. Measured after the\n * name translation went live: five of them still carried a plain-string name while the whole\n * rest of the tree was translated. Writing them here every start closes that half; extendObject\n * merges, so a recording setting or anything else a user attached survives.\n */\n private async ensureInstanceInfoObjects(): Promise<void> {\n const objects: Array<[string, ObjectDef]> = [\n [\"info\", { id: \"info\", type: \"channel\", common: { name: tName(\"Information\") } }],\n [\n \"info.connection\",\n {\n id: \"info.connection\",\n type: \"state\",\n common: {\n name: tName(\"Device or service connected\"),\n type: \"boolean\",\n role: \"indicator.connected\",\n read: true,\n write: false,\n },\n },\n ],\n [\n \"info.devicesTotal\",\n {\n id: \"info.devicesTotal\",\n type: \"state\",\n common: { name: tName(\"Devices total\"), type: \"number\", role: \"value\", read: true, write: false },\n },\n ],\n [\n \"info.devicesOnline\",\n {\n id: \"info.devicesOnline\",\n type: \"state\",\n common: { name: tName(\"Devices online\"), type: \"number\", role: \"value\", read: true, write: false },\n },\n ],\n [\n \"info.devicesAllOnline\",\n {\n id: \"info.devicesAllOnline\",\n type: \"state\",\n common: { name: tName(\"All devices online\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n },\n ],\n ];\n for (const [id, def] of objects) {\n await this.extendObject(id, { type: def.type, common: def.common, native: {} });\n }\n }\n\n /**\n * Create AND refresh a device's header objects (the device node, its info channel and a\n * per-device connection indicator) so its state is visible even while offline.\n *\n * Written with `extendObject` on every start, not created once: an object that already exists\n * is otherwise never touched again, so an instance upgraded from an older version keeps\n * whatever that version wrote \u2014 measured live after the name translation, where these were the\n * only device datapoints left with a plain-string name (`info.model`/`info.firmware` came out\n * right only because a catalog entry upserts them on top). extendObject merges, so a recording\n * setting a user attached survives.\n *\n * @param deviceId the id-safe device id\n * @param ip the device's current address (from config or discovery)\n */\n private async ensureDeviceHeader(deviceId: string, ip: string): Promise<void> {\n // statusStates.onlineId lets the admin paint a green/red reachability symbol on the\n // device object itself (as govee does), fed by the per-device connection state.\n // extendObject with preserve:name so an upgrade adds the symbol without overwriting\n // a name the user changed.\n // A device that has not reported its model yet would sit in the tree without any\n // symbol \u2014 an upgraded instance shows that on every start before the first report,\n // and a device that never answers shows it for good. Seed the default silhouette,\n // but only when there is none: overwriting would flip a soundbar back to the\n // receiver default for the seconds until its model arrives.\n let icon: string | undefined;\n try {\n const existing = await this.getObjectAsync(deviceId);\n icon = existing?.common?.icon ? undefined : iconForModel(undefined);\n } catch {\n icon = undefined;\n }\n await this.extendObject(\n deviceId,\n {\n type: \"device\",\n common: {\n name: deviceId,\n ...(icon ? { icon } : {}),\n statusStates: { onlineId: `${this.namespace}.${deviceId}.info.connection` },\n },\n native: {},\n },\n { preserve: { common: [\"name\"] } },\n );\n await this.extendObject(`${deviceId}.info`, {\n type: \"channel\",\n common: { name: tName(\"Info\") },\n native: {},\n });\n await this.extendObject(`${deviceId}.info.connection`, {\n type: \"state\",\n common: {\n name: tName(\"Connected\"),\n type: \"boolean\",\n role: \"indicator.reachable\",\n read: true,\n write: false,\n def: false,\n },\n native: {},\n });\n // Model name shown on the device-manager card. Filled by whichever transport reports it\n // (YNCA MODELNAME, YXC/XML model); created here so the card's model line binds even for an\n // offline device or a transport that does not report a model.\n await this.extendObject(`${deviceId}.info.model`, {\n type: \"state\",\n common: { name: tName(\"Model\"), type: \"string\", role: \"text\", read: true, write: false, def: \"\" },\n native: {},\n });\n // The device's address \u2014 for a discovered device it lived only in the adapter's\n // internals, so no diagnosis (log capture, browser access to the device's own pages)\n // could name it without a network search. Refreshed every start: DHCP may move it.\n await this.extendObject(`${deviceId}.info.ip`, {\n type: \"state\",\n common: { name: tName(\"IP address\"), type: \"string\", role: \"info.ip\", read: true, write: false, def: \"\" },\n native: {},\n });\n await this.setState(`${deviceId}.info.ip`, { val: ip, ack: true });\n // Per-transport connection flags, fed by the live set from connectTransports and read live\n // by the device-manager card indicators. Created here so an offline device's card still\n // renders all three (false) instead of nothing.\n await this.extendObject(`${deviceId}.info.transports`, {\n type: \"channel\",\n common: { name: tName(\"Transports\") },\n native: {},\n });\n for (const proto of TRANSPORT_IDS) {\n await this.extendObject(`${deviceId}.info.transports.${proto}`, {\n type: \"state\",\n common: {\n name: tName(\"%s connected\", proto.toUpperCase()),\n type: \"boolean\",\n role: \"indicator.reachable\",\n read: true,\n write: false,\n def: false,\n },\n native: {},\n });\n }\n }\n\n /** The icon last written per device, so repeated model reports do not re-write the object. */\n private readonly deviceIcons = new Map<string, string>();\n\n /** The label this adapter wrote per device, with the rank of the source behind it. */\n private readonly deviceLabels = new Map<string, { name: string; rank: LabelRank }>();\n\n /**\n * Give the device node a name a user recognises, once the device reports one.\n *\n * An instance upgraded from the previous adapter carries the receiver's ip as its\n * device name \u2014 that adapter knew nothing but an ip, so the migration had nothing\n * else to call it. The object id stays that ip for good (history and visualisation\n * bindings hang off it), but the displayed name does not have to.\n *\n * A name the user typed is never touched, and the model never replaces a name the\n * device reported for itself \u2014 see {@link nextDeviceLabel}.\n *\n * @param deviceId the id-safe device id\n * @param candidate the reported name (a MusicCast zone name, or the model)\n * @param rank how trustworthy the candidate is\n */\n private async updateDeviceLabel(deviceId: string, candidate: string, rank: LabelRank): Promise<void> {\n const own = this.deviceLabels.get(deviceId);\n try {\n const current = (await this.getObjectAsync(deviceId))?.common?.name;\n const label = nextDeviceLabel(\n typeof current === \"string\" ? current : undefined,\n deviceId,\n candidate,\n rank,\n own?.name,\n own?.rank,\n );\n if (label === undefined) {\n return;\n }\n // Deliberately without `preserve: { common: [\"name\"] }`: nextDeviceLabel has just\n // established that the present name is the adapter's own placeholder, not a user's.\n await this.extendObject(deviceId, { common: { name: label } });\n this.deviceLabels.set(deviceId, { name: label, rank });\n this.log.debug(`${deviceId}: device name set to \"${label}\"`);\n } catch (e) {\n this.log.debug(`${deviceId}: setting the device name failed (${errorMessage(e)})`);\n }\n }\n\n /**\n * Paint the device-class silhouette on the device node once the model is known \u2014\n * detected from the reported model name, written only when it actually changes.\n *\n * @param deviceId the id-safe device id\n * @param model the reported model name\n */\n private async updateDeviceIcon(deviceId: string, model: string): Promise<void> {\n const icon = iconForModel(model);\n if (this.deviceIcons.get(deviceId) === icon) {\n return;\n }\n this.deviceIcons.set(deviceId, icon);\n try {\n await this.extendObject(deviceId, { common: { icon } });\n } catch (e) {\n this.log.debug(`${deviceId}: setting device icon failed (${errorMessage(e)})`);\n }\n }\n\n /**\n * Carry over the previous adapter's single-device config into the device table.\n * The old yamaha stored one receiver as `config.ip` (older installs: `config.IP`);\n * the new adapter uses a `devices` table, so an upgraded instance would otherwise\n * start with an empty table and lose its receiver. Persists the row so the admin\n * table shows it, and fills `this.config` in memory so this run already drives it.\n */\n private async migrateLegacyDevice(): Promise<void> {\n const config = this.config as unknown as Record<string, unknown>;\n const row = legacyDeviceRow(config);\n if (!row) {\n return;\n }\n // Fill the in-memory config first, so this run already drives the device even\n // if persisting the table below fails \u2014 persistence is a convenience for the\n // admin view, not a precondition for running.\n config.devices = [row];\n try {\n await this.extendForeignObjectAsync(`system.adapter.${this.namespace}`, { native: { devices: [row] } });\n this.log.info(`carried the previous single-device config (${row.ip}) over into the device table`);\n } catch (e) {\n this.log.warn(\n `could not persist the migrated device table (${errorMessage(e)}); ` + `running with the in-memory value`,\n );\n }\n }\n\n /**\n * Fold the removed `group_zones` toggle into `group_multiroom` \u2014 zone 2/3/4 now\n * belong to the multiroom group. Existing installs that had zones on but multiroom\n * off would otherwise lose their zone datapoints after the update.\n */\n private async migrateGroupZones(): Promise<void> {\n const config = this.config as unknown as Record<string, unknown>;\n if (!(\"group_zones\" in config)) {\n return;\n }\n if (config.group_zones) {\n config.group_multiroom = true;\n }\n delete config.group_zones;\n try {\n const obj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);\n if (obj?.native) {\n if (obj.native.group_zones) {\n obj.native.group_multiroom = true;\n }\n delete obj.native.group_zones;\n await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, obj);\n this.log.info(\"migrated group_zones setting into group_multiroom\");\n }\n } catch (e) {\n this.log.warn(`could not persist group_zones migration (${errorMessage(e)})`);\n }\n }\n\n /**\n * Bring one device online across ALL its transports: every protocol that answers\n * \u2014 YNCA (amp control over a held TCP connection), YXC (MusicCast), XML/YNC\n * (pre-2010) \u2014 connects in parallel on one object tree. Returns a connection handle\n * the supervisor keeps, or null when no transport answers this attempt. Each\n * datapoint is owned by exactly one transport (owner-policy), so the mappers never\n * collide on a shared id.\n *\n * @param device the configured device record\n * @param pushReceiver the shared YXC push receiver\n * @param knownDeviceIps IPs of all configured devices, for resolving a multiroom client\n * @param reachability dedup for the \"no reachable transport\" warning (one instance per device,\n * held by the caller across retries \u2014 see {@link ReachabilityDedup})\n * @param yncaSubunitCache per-device cache of the YNCA AVAIL probe (skips the probe on reconnects)\n * @param probeMemory per-device memory for constant device answers (skips re-asking on reconnects)\n * @returns a connection handle, or null when no transport connected\n */\n private attemptDevice(\n device: DeviceRecord,\n pushReceiver: YxcPushReceiver,\n knownDeviceIps: Set<string>,\n reachability: ReachabilityDedup,\n yncaSubunitCache: YncaSubunitCache,\n probeMemory: ProbeMemory,\n ): Promise<ConnectionHandle | null> {\n return attemptDevice(device, {\n reachability,\n yncaSubunitCache,\n probeMemory,\n // Group gate for the YNCA sweep: a disabled group's functions are never even fetched.\n isEntryEnabled: id => isGroupEnabled(id, this.config as unknown as Record<string, unknown>),\n log: {\n debug: message => this.log.debug(message),\n info: message => this.log.info(message),\n warn: message => this.log.warn(message),\n },\n upsertObject: async (id, def) => {\n // Gate on the datapoint group: a switched-off group's objects are not created. The id is\n // \"<deviceId>.<relativeId>\"; groupOf reads the relative part.\n if (!isGroupEnabled(id.slice(id.indexOf(\".\") + 1), this.config as unknown as Record<string, unknown>)) {\n return;\n }\n await this.extendObject(id, { type: def.type, common: def.common, native: {} });\n if (def.type === \"state\") {\n this.noteDatapointCreated(id);\n this.touchedThisRun.add(id);\n }\n },\n setStateAck: (id, value) => {\n // Same group gate as upsertObject, so a switched-off group seeds no orphan value either.\n if (!isGroupEnabled(id.slice(id.indexOf(\".\") + 1), this.config as unknown as Record<string, unknown>)) {\n return;\n }\n this.writeState(id, value);\n // A model report also decides the device-class icon on the device node \u2014 and, for a\n // device still carrying the ip it was migrated with, its readable name.\n if (id.endsWith(\".info.model\") && typeof value === \"string\" && value.length > 0) {\n const reporting = id.slice(0, id.indexOf(\".\"));\n void this.updateDeviceIcon(reporting, value);\n void this.updateDeviceLabel(reporting, value, LABEL_RANK.model);\n }\n },\n onDeviceName: name => void this.updateDeviceLabel(device.id, name, LABEL_RANK.deviceName),\n timers: {\n schedule: (handler, ms) => (this.unloading ? undefined : this.setTimeout(handler, ms)),\n cancel: handle => this.clearTimeout(handle),\n },\n registerPush: (ip, onPush) => pushReceiver.register(ip, onPush),\n pushActive: () => pushReceiver.isListening(),\n scheduleKeepalive: (handler, ms) => {\n if (this.unloading) {\n return () => {};\n }\n const timer = this.setInterval(handler, ms);\n return () => {\n if (timer) {\n this.clearInterval(timer);\n }\n };\n },\n xmlPollIntervalMs: this.xmlPollIntervalMs(),\n onTransports: names => this.setTransports(device.id, names),\n knownDeviceIps,\n });\n }\n\n /**\n * Route a state change to every device's supervisor (each forwards to its\n * active controller, which ignores ids outside its subtree and its acked echoes).\n *\n * @param id the full state id\n * @param state the new state (null when deleted)\n */\n private onStateChange(id: string, state: ioBroker.State | null | undefined): void {\n if (!state) {\n return;\n }\n const relative = stripNamespace(id, this.namespace);\n // The adapter subscribes to its whole namespace, so every one of its own acked writes\n // comes back here too \u2014 during a sweep that is hundreds of events. Route by the id's\n // first segment instead of offering each one to every device in turn.\n const deviceId = relative.slice(0, relative.indexOf(\".\"));\n this.supervisorById.get(deviceId)?.handleStateChange(relative, state.ack, state.val);\n }\n\n /**\n * Synchronous teardown \u2014 no await, call the callback immediately (SIGKILL otherwise).\n *\n * @param callback function to invoke once teardown is complete\n */\n private onUnload(callback: () => void): void {\n try {\n this.unloading = true;\n this.clearTimeout(this.balanceTimer);\n this.clearTimeout(this.rediscoverTimer);\n this.pushReceiver?.close();\n for (const supervisor of this.supervisors) {\n supervisor.close();\n }\n // A stopped adapter talks to nothing, so no device may keep claiming to be connected \u2014\n // that state paints the symbol on the device object (statusStates.onlineId), and the\n // instance-wide info.connection alone would leave every device green. The protocol\n // flags and the overview go with them; devicesTotal stays, how many devices there are\n // did not change.\n //\n // The callback goes LAST, after the writes: reporting \"done\" straight away loses them,\n // the host tears the process down as soon as it is told.\n const writes: Promise<unknown>[] = [this.setState(\"info.connection\", { val: false, ack: true })];\n for (const deviceId of this.deviceConnected.keys()) {\n this.deviceConnected.set(deviceId, false);\n writes.push(this.setState(`${deviceId}.info.connection`, { val: false, ack: true }));\n // The protocol flags on the card go down with the connection \u2014 a stopped adapter\n // is connected over no protocol.\n for (const proto of TRANSPORT_IDS) {\n writes.push(this.setState(`${deviceId}.info.transports.${proto}`, { val: false, ack: true }));\n }\n }\n writes.push(this.setState(\"info.devicesOnline\", { val: 0, ack: true }));\n writes.push(this.setState(\"info.devicesAllOnline\", { val: false, ack: true }));\n void Promise.all(writes)\n .catch(() => {\n /* states DB already going down \u2014 nothing left to report to */\n })\n .finally(callback);\n return;\n } catch {\n // fall through\n }\n callback();\n }\n\n /**\n * Auto-discovery for an empty device table: scan the network, merge the finds with\n * the devices remembered from earlier runs (standby protection), persist the merged\n * set and return it. XML/pre-2010 receivers do not answer SSDP and never appear here.\n *\n * @returns the device records to run this session\n */\n private async autoDiscover(): Promise<DeviceRecord[]> {\n const store = discoveredStoreDeps(this);\n const known = await readDiscovered(store);\n if (known.length > 0) {\n // Remembered devices start NOW \u2014 the network search used to gate every restart\n // by its collect window although the devices were already known. It still runs,\n // in the background, to pick up newcomers (see discoverAdditionalDevices).\n this.log.info(`setting up ${known.length} remembered device(s); the network search runs in the background`);\n return known;\n }\n this.log.info(\"auto-discovery via SSDP (older XML-only devices must be added manually)\");\n const merged = await this.runDiscovery();\n this.log.info(`setting up ${merged.length} discovered device(s)...`);\n return merged;\n }\n\n /**\n * Search the network, merge with the remembered devices, and persist the result.\n * Shared by the blocking first-setup path and the background search.\n *\n * @returns the merged device records\n */\n private async runDiscovery(): Promise<DeviceRecord[]> {\n // Every search counts against the throttle, whoever asked for it \u2014 otherwise the first\n // offline device would fire another one right behind the start-up search.\n this.lastRediscovery = Date.now();\n const store = discoveredStoreDeps(this);\n const known = await readDiscovered(store);\n let found: Array<{ ip: string; name: string }> = [];\n try {\n found = await discoverYamaha({\n search: (target, ms) => this.ssdpSearch(target, ms),\n fetch: url => this.fetchUrl(url),\n log: { debug: message => this.log.debug(message), warn: message => this.log.warn(message) },\n });\n } catch (e) {\n this.log.warn(`auto-discovery scan failed, using the remembered devices: ${errorMessage(e)}`);\n }\n const merged = mergeDiscovered(known, found, (dropped, takenId) =>\n this.log.warn(`discovered device \"${dropped}\" skipped \u2014 its object id \"${takenId}\" is already taken`),\n );\n // Devices the user deleted from the card list stay out \u2014 otherwise the next search simply\n // undoes the delete.\n const ignored = new Set(await readIgnored(ignoredStoreDeps(this)));\n const kept = ignored.size > 0 ? merged.filter(device => !ignored.has(device.id)) : merged;\n await writeDiscovered(store, kept);\n return kept;\n }\n\n /**\n * Load a device's persisted YNCA subunit-cache (the AVAIL probe result) from its\n * device object's native part, wrapped so updates persist back there. The device\n * object is the right home: writing an instance object's native restarts the\n * adapter, a device object's does not.\n *\n * @param deviceId the id-safe device id\n * @returns the per-device cache\n */\n private async loadYncaSubunitCache(deviceId: string): Promise<YncaSubunitCache> {\n let stored: unknown;\n try {\n stored = (await this.getObjectAsync(deviceId))?.native?.yncaAvail;\n } catch {\n stored = undefined;\n }\n return createSubunitCache(isAvailSnapshot(stored) ? stored : undefined, snapshot => {\n this.persistDeviceNative(deviceId, { yncaAvail: snapshot ?? null });\n });\n }\n\n /**\n * Load a device's persisted probe memory (constant device answers: capabilities,\n * declared scenes/inputs, names) from its device object's native part, wrapped so\n * every change persists back there \u2014 the same home as the subunit cache. This is\n * what makes a restart fast: the object tree is rebuilt from the remembered\n * answers while only the live proofs and the value refresh still go to the device.\n *\n * @param deviceId the id-safe device id\n * @returns the per-device memory\n */\n private async loadProbeMemory(deviceId: string): Promise<ProbeMemory> {\n // Stored as a JSON STRING deliberately: extendObject MERGES nested objects, so a\n // dropped key would rise from the dead on the next persist \u2014 a string replaces.\n let initial: Record<string, unknown> | undefined;\n try {\n const stored = (await this.getObjectAsync(deviceId))?.native?.probeCache;\n if (typeof stored === \"string\") {\n const parsed: unknown = JSON.parse(stored);\n if (typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed)) {\n initial = parsed as Record<string, unknown>;\n }\n }\n } catch {\n initial = undefined;\n }\n return new ProbeMemory(initial, entries => {\n this.persistDeviceNative(deviceId, { probeCache: JSON.stringify(entries) });\n });\n }\n\n /**\n * The XML/YNC poll interval in milliseconds, from `config.xmlPollInterval`\n * (seconds, default 60).\n *\n * @returns the interval in ms\n */\n private xmlPollIntervalMs(): number {\n const seconds = Number((this.config as unknown as Record<string, unknown>).xmlPollInterval);\n return (Number.isFinite(seconds) && seconds > 0 ? seconds : 60) * 1000;\n }\n\n /**\n * Run an SSDP M-SEARCH and collect the responders' description URL and address.\n *\n * With a configured network interface the search leaves exactly that one; left empty it\n * leaves EVERY non-internal IPv4 interface at once (one socket each), because multicast\n * egress otherwise follows only the host's default route \u2014 on a multi-homed host whose\n * default route is not the AV network that means the receiver is never reached and nothing\n * is found. Responders from all interfaces are merged into one list; the caller\n * de-duplicates by address.\n *\n * @param target the search target (device type)\n * @param timeoutMs how long to collect responses\n * @returns the responders\n */\n private ssdpSearch(target: string, timeoutMs: number): Promise<Array<{ location: string; address: string }>> {\n return new Promise(resolve => {\n const bindAddrs = searchInterfaces(this.config.networkInterface, networkInterfaces());\n const responders: Array<{ location: string; address: string }> = [];\n const sockets: ReturnType<typeof createSocket>[] = [];\n let settled = false;\n const finish = (): void => {\n if (settled) {\n return;\n }\n settled = true;\n for (const socket of sockets) {\n try {\n socket.close();\n } catch {\n // already closed\n }\n }\n resolve(responders);\n };\n // Open one search socket bound to a single interface (or the default route when bindAddr\n // is undefined). Every socket shares the responders list and the one settle timeout.\n const searchFrom = (bindAddr: string | undefined): void => {\n const socket = createSocket(\"udp4\");\n sockets.push(socket);\n socket.on(\"message\", (msg, rinfo) => {\n const location = /LOCATION:\\s*(\\S+)/i.exec(msg.toString());\n if (location) {\n responders.push({ location: location[1], address: rinfo.address });\n }\n });\n socket.on(\"error\", err => {\n // One interface failing (typically a stale selected IP after a DHCP change) must not\n // kill the search on the others \u2014 warn and drop just this socket; the timeout still\n // resolves whatever the rest found.\n this.log.warn(\n `discovery socket failed${bindAddr ? ` on interface ${bindAddr}` : \"\"}: ${errorMessage(err)}${\n bindAddr ? \" \u2014 check the Network Interface setting\" : \"\"\n }`,\n );\n try {\n socket.close();\n } catch {\n // already closed\n }\n });\n const sendSearch = (): void => {\n if (settled) {\n return;\n }\n const msearch = `M-SEARCH * HTTP/1.1\\r\\nHOST: 239.255.255.250:1900\\r\\nMAN: \"ssdp:discover\"\\r\\nMX: 3\\r\\nST: ${target}\\r\\n\\r\\n`;\n try {\n socket.send(msearch, 1900, \"239.255.255.250\");\n } catch {\n // socket already closed by an error above\n }\n };\n socket.bind(0, bindAddr, () => {\n // Pin OUTGOING multicast to this interface. bind() only sets the source address; the\n // egress interface is IP_MULTICAST_IF \u2014 without it the OS uses its default route, so\n // the search can leave the wrong NIC on a multi-homed host (Node dgram docs).\n if (bindAddr) {\n try {\n socket.setMulticastInterface(bindAddr);\n } catch {\n this.log.info(`discovery: could not pin multicast egress to ${bindAddr} \u2014 using the default interface`);\n }\n }\n // Multicast is lossy and a single request can be dropped \u2014 repeat the M-SEARCH a few\n // times inside the collect window so one lost packet does not hide a receiver.\n for (let i = 0; i < SSDP_SEARCH_BURST; i++) {\n this.setTimeout(sendSearch, i * SSDP_SEARCH_INTERVAL_MS);\n }\n });\n };\n // Configured \u2192 that one interface; empty \u2192 every non-internal IPv4; none usable \u2192 default route.\n if (bindAddrs.length === 0) {\n searchFrom(undefined);\n } else {\n for (const bindAddr of bindAddrs) {\n searchFrom(bindAddr);\n }\n }\n this.setTimeout(finish, timeoutMs);\n });\n }\n\n /**\n * Fetch a URL over HTTP and resolve its body.\n *\n * @param url the URL to fetch\n * @returns the response body\n */\n private fetchUrl(url: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const req = httpGet(url, res => {\n let data = \"\";\n let bytes = 0;\n res.on(\"data\", chunk => {\n bytes += (chunk as Buffer).length;\n if (bytes > MAX_HTTP_BODY_BYTES) {\n // A description document is a few KB \u2014 whatever streams past the cap is not one.\n res.destroy(new Error(`description too large: ${url}`));\n return;\n }\n data += String(chunk);\n });\n // A connection dropped mid-body emits on the RESPONSE stream, not the request \u2014\n // without this handler that is an unhandled error event instead of a rejection.\n res.on(\"error\", reject);\n res.on(\"end\", () => resolve(data));\n });\n req.on(\"error\", reject);\n req.setTimeout(FETCH_TIMEOUT_MS, () => req.destroy(new Error(`fetch timed out: ${url}`)));\n });\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new Yamaha(options);\n} else {\n // Start the instance directly\n (() => new Yamaha())();\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAAuB;AACvB,wBAA6B;AAC7B,uBAA+B;AAC/B,qBAAkC;AAClC,4BAA8B;AAC9B,gCAAiC;AACjC,oBAA+B;AAC/B,yBAA6B;AAC7B,0BAYO;AACP,kBAAkD;AAClD,kBAAsB;AACtB,uBAA+B;AAC/B,8BAA6D;AAC7D,mCAAsD;AACtD,2BAAgC;AAChC,+BAAuC;AAGvC,+BAAwD;AACxD,gCAAkC;AAClC,gCAAkC;AAClC,2BAA2E;AAC3E,0BAA4B;AAG5B,MAAM,oBAAoB;AAC1B,MAAM,mBAAmB;AAGzB,MAAM,mBAAmB;AAGzB,MAAM,oBAAoB;AAE1B,MAAM,0BAA0B;AAGhC,MAAM,gBAAgB,CAAC,QAAQ,OAAO,KAAK;AAM3C,MAAM,8BAA8B;AAOpC,MAAM,6BAA6B;AAY5B,MAAM,eAAe,MAAM,QAAQ;AAAA,EACvB,cAAkC,CAAC;AAAA;AAAA,EAEnC,iBAAiB,oBAAI,IAA8B;AAAA,EACnD,kBAAkB,oBAAI,IAAqB;AAAA;AAAA,EAE3C,gBAAgB,oBAAI,IAA0B;AAAA;AAAA,EAE9C,iBAAiB,oBAAI,IAAY;AAAA;AAAA,EAE1C,WAAW;AAAA;AAAA,EAEX;AAAA;AAAA,EAEA,kBAAkB;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY;AAAA;AAAA,EAEH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,kBAAkB,oBAAI,IAAY;AAAA;AAAA,EAElC,iBAAiB,oBAAI,IAAY;AAAA;AAAA,EAEjC,eAAe,oBAAI,IAAY;AAAA,EACxC,oBAAoB;AAAA,EACpB,oBAAoB;AAAA;AAAA,EAEpB;AAAA;AAAA,EAEA,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKlB,qBAAqB;AAAA;AAAA;AAAA;AAAA,EAKtB,YAAY,UAAyC,CAAC,GAAG;AAC9D,UAAM;AAAA,MACJ,GAAG;AAAA,MACH,MAAM;AAAA,IACR,CAAC;AAED,SAAK,GAAG,SAAS,KAAK,QAAQ,KAAK,IAAI,CAAC;AACxC,SAAK,GAAG,eAAe,KAAK,cAAc,KAAK,IAAI,CAAC;AACpD,SAAK,GAAG,UAAU,KAAK,SAAS,KAAK,IAAI,CAAC;AAC1C,SAAK,mBAAmB,IAAI,gDAAuB,IAAI;AAAA,EACzD;AAAA;AAAA,EAGA,MAAc,UAAyB;AACrC,QAAI;AACF,WAAK,IAAI,KAAK,+DAA0D;AACxE,YAAM,KAAK,SAAS,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAChE,YAAM,KAAK,oBAAoB;AAC/B,YAAM,KAAK,kBAAkB;AAI7B,YAAM,iBAAa;AAAA,QAAa,KAAK,OAAO;AAAA,QAAS,CAAC,SAAS,YAC7D,KAAK,IAAI,KAAK,WAAW,OAAO,mCAA8B,OAAO,qCAAqC;AAAA,MAC5G;AACA,YAAM,UAAU,WAAW,SAAS,IAAI,aAAa,MAAM,KAAK,aAAa;AAC7E,UAAI,KAAK,WAAW;AAIlB;AAAA,MACF;AACA,WAAK,WAAW,WAAW,WAAW;AAGtC,WAAK,kBAAkB,KAAK,IAAI;AAChC,iBAAW,UAAU,SAAS;AAC5B,aAAK,eAAe,IAAI,OAAO,EAAE;AAAA,MACnC;AAEA,YAAM,KAAK,2BAA2B;AACtC,YAAM,KAAK,oBAAoB,IAAI,IAAI,QAAQ,IAAI,YAAU,OAAO,EAAE,CAAC,CAAC;AACxE,YAAM,KAAK,0BAA0B;AACrC,YAAM,KAAK,kBAAkB;AAC7B,YAAM,eAAe,IAAI,qCAAgB;AAAA,QACvC,KAAK,EAAE,OAAO,aAAW,KAAK,IAAI,MAAM,OAAO,GAAG,MAAM,aAAW,KAAK,IAAI,KAAK,OAAO,EAAE;AAAA,QAC1F,UAAU,CAAC,IAAI,OAAO,KAAK,WAAW,IAAI,EAAE;AAAA,QAC5C,QAAQ,YAAU,KAAK,aAAa,MAAM;AAAA,MAC5C,CAAC;AACD,mBAAa,MAAM;AACnB,WAAK,eAAe;AACpB,UAAI,WAAW,SAAS,GAAG;AACzB,aAAK,IAAI,KAAK,cAAc,QAAQ,MAAM,0BAA0B;AAAA,MACtE;AACA,iBAAW,UAAU,SAAS;AAC5B,cAAM,KAAK,YAAY,QAAQ,YAAY;AAAA,MAC7C;AACA,WAAK,oBAAoB;AAGzB,UAAI,KAAK,YAAY,QAAQ,SAAS,GAAG;AACvC,aAAK,KAAK,0BAA0B,YAAY;AAAA,MAClD;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,uBAAmB,0BAAa,CAAC,CAAC,EAAE;AAAA,IACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,YAAY,QAAsB,cAA8C;AAG5F,SAAK,gBAAgB,IAAI,OAAO,IAAI,KAAK;AACzC,SAAK,cAAc,IAAI,OAAO,IAAI,EAAE,GAAG,OAAO,CAAC;AAC/C,SAAK,eAAe,IAAI,OAAO,EAAE;AACjC,UAAM,KAAK,mBAAmB,OAAO,IAAI,OAAO,EAAE;AAIlD,UAAM,KAAK,SAAS,GAAG,OAAO,EAAE,oBAAoB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAI7E,SAAK,cAAc,OAAO,IAAI,CAAC,CAAC;AAChC,UAAM,eAAe,IAAI,4CAAkB;AAC3C,UAAM,eAAe,MAAM,KAAK,qBAAqB,OAAO,EAAE;AAG9D,UAAM,cAAc,MAAM,KAAK,gBAAgB,OAAO,EAAE;AACxD,UAAM,aAAa,IAAI,0CAAiB;AAAA,MACtC,SAAS,MACP,KAAK,cAAc,QAAQ,cAAc,KAAK,gBAAgB,cAAc,cAAc,WAAW;AAAA,MACvG,UAAU,CAAC,IAAI,OAAO,KAAK,WAAW,IAAI,EAAE;AAAA,MAC5C,QAAQ,YAAU,KAAK,aAAa,MAAsC;AAAA,MAC1E,oBAAoB,eAAa,KAAK,iBAAiB,OAAO,IAAI,SAAS;AAAA,MAC3E,SAAS,IAAI,4CAAkB,mBAAmB,gBAAgB;AAAA,MAClE,KAAK;AAAA,QACH,OAAO,aAAW,KAAK,IAAI,MAAM,OAAO;AAAA,QACxC,MAAM,aAAW,KAAK,IAAI,KAAK,OAAO;AAAA,QACtC,MAAM,aAAW,KAAK,IAAI,KAAK,OAAO;AAAA,MACxC;AAAA,IACF,CAAC;AACD,SAAK,YAAY,KAAK,UAAU;AAChC,SAAK,eAAe,IAAI,OAAO,IAAI,UAAU;AAC7C,eAAW,MAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAc,0BAA0B,cAA8C;AACpF,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,aAAa;AACvC,UAAI,KAAK,WAAW;AAClB;AAAA,MACF;AACA,UAAI,UAAU;AACd,iBAAW,UAAU,QAAQ;AAC3B,cAAM,UAAU,KAAK,cAAc,IAAI,OAAO,EAAE;AAChD,YAAI,CAAC,SAAS;AACZ,eAAK,IAAI,KAAK,mBAAmB,OAAO,EAAE,oBAAe;AACzD,gBAAM,KAAK,YAAY,QAAQ,YAAY;AAC3C,oBAAU;AAAA,QACZ,WAAW,QAAQ,OAAO,OAAO,IAAI;AACnC,eAAK,IAAI,KAAK,GAAG,OAAO,EAAE,0BAA0B,QAAQ,EAAE,OAAO,OAAO,EAAE,+BAA0B;AACxG,eAAK,eAAe,OAAO,QAAQ,EAAE;AACrC,eAAK,WAAW,OAAO,EAAE;AACzB,gBAAM,KAAK,YAAY,QAAQ,YAAY;AAC3C,oBAAU;AAAA,QACZ;AAAA,MACF;AACA,UAAI,SAAS;AACX,aAAK,oBAAoB;AAAA,MAC3B;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,KAAK,oCAAgC,0BAAa,CAAC,CAAC,EAAE;AAAA,IACjE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,sBAA4B;AAClC,QAAI,CAAC,KAAK,YAAY,KAAK,aAAa,KAAK,oBAAoB,QAAW;AAC1E;AAAA,IACF;AACA,UAAM,WAAW,KAAK;AACtB,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AACA,UAAM,MAAM,KAAK,IAAI,GAAG,8BAA8B,KAAK,IAAI,IAAI,KAAK,gBAAgB;AACxF,SAAK,kBAAkB,KAAK,WAAW,MAAM;AAC3C,WAAK,kBAAkB;AACvB,WAAK,kBAAkB,KAAK,IAAI;AAChC,UAAI,CAAC,KAAK,WAAW;AACnB,aAAK,KAAK,0BAA0B,QAAQ;AAAA,MAC9C;AAAA,IACF,GAAG,GAAG;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAW,UAAwB;AACzC,UAAM,aAAa,KAAK,eAAe,IAAI,QAAQ;AACnD,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AACA,eAAW,MAAM;AACjB,SAAK,eAAe,OAAO,QAAQ;AACnC,UAAM,QAAQ,KAAK,YAAY,QAAQ,UAAU;AACjD,QAAI,SAAS,GAAG;AACd,WAAK,YAAY,OAAO,OAAO,CAAC;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAa,aAAa,UAAiC;AACzD,SAAK,WAAW,QAAQ;AACxB,UAAM,SAAS,KAAK,cAAc,IAAI,QAAQ;AAC9C,QAAI,QAAQ;AACV,WAAK,eAAe,OAAO,OAAO,EAAE;AAAA,IACtC;AACA,SAAK,cAAc,OAAO,QAAQ;AAClC,SAAK,gBAAgB,OAAO,QAAQ;AACpC,SAAK,aAAa,OAAO,QAAQ;AACjC,QAAI;AACF,YAAM,KAAK,eAAe,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,IACzD,SAAS,GAAG;AACV,WAAK,IAAI,KAAK,wCAAwC,QAAQ,UAAM,0BAAa,CAAC,CAAC,GAAG;AAAA,IACxF;AACA,SAAK,WAAW,mBAAmB,CAAC,GAAG,KAAK,gBAAgB,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC;AACnF,SAAK,oBAAoB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAc,oBAAmC;AAC/C,QAAI;AACF,YAAM,KAAK,qBAAqB,GAAG;AAAA,IACrC,SAAS,GAAG;AACV,WAAK,IAAI;AAAA,QACP,6CAAyC,0BAAa,CAAC,CAAC;AAAA,MAE1D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,iBAAiB,UAAkB,WAA0B;AACnE,QAAI,aAAa,CAAC,KAAK,aAAa,IAAI,QAAQ,GAAG;AACjD,WAAK,aAAa,IAAI,QAAQ;AAG9B,WAAK,yBAAyB;AAAA,IAChC;AACA,SAAK,gBAAgB,IAAI,UAAU,SAAS;AAC5C,SAAK,WAAW,GAAG,QAAQ,oBAAoB,SAAS;AAExD,QAAI,CAAC,WAAW;AACd,WAAK,cAAc,UAAU,CAAC,CAAC;AAE/B,WAAK,oBAAoB;AAAA,IAC3B;AACA,UAAM,eAAe,CAAC,GAAG,KAAK,gBAAgB,OAAO,CAAC,EAAE,KAAK,OAAO;AACpE,SAAK,WAAW,mBAAmB,YAAY;AAC/C,SAAK,oBAAoB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,sBAA4B;AAClC,UAAM,QAAQ,KAAK,gBAAgB;AACnC,UAAM,SAAS,CAAC,GAAG,KAAK,gBAAgB,OAAO,CAAC,EAAE,OAAO,OAAO,EAAE;AAClE,SAAK,WAAW,qBAAqB,KAAK;AAC1C,SAAK,WAAW,sBAAsB,MAAM;AAC5C,SAAK,WAAW,yBAAyB,QAAQ,KAAK,WAAW,KAAK;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,cAAc,UAAkB,OAAuB;AAC7D,UAAM,OAAO,IAAI,IAAI,KAAK;AAC1B,eAAW,SAAS,eAAe;AACjC,WAAK,WAAW,GAAG,QAAQ,oBAAoB,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,WAAW,IAAY,OAAkC;AAC/D,SAAK,SAAS,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,EAAE;AAAA,MAC3C,MAAM;AACJ,aAAK,qBAAqB;AAAA,MAC5B;AAAA,MACA,CAAC,MAAe,KAAK,iBAAiB,SAAS,EAAE,IAAI,CAAC;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,oBAAoB,UAAkB,QAAuC;AACnF,SAAK,aAAa,UAAU,EAAE,OAAO,CAAC,EAAE;AAAA,MACtC,MAAM;AACJ,aAAK,qBAAqB;AAAA,MAC5B;AAAA,MACA,CAAC,MAAe,KAAK,iBAAiB,iBAAiB,QAAQ,IAAI,CAAC;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,iBAAiB,MAAc,GAAkB;AACvD,QAAI,KAAK,WAAW;AAClB;AAAA,IACF;AACA,UAAM,UAAU,mBAAmB,IAAI,SAAK,0BAAa,CAAC,CAAC;AAC3D,QAAI,KAAK,oBAAoB;AAC3B,WAAK,IAAI,MAAM,OAAO;AACtB;AAAA,IACF;AACA,SAAK,qBAAqB;AAC1B,SAAK,IAAI,KAAK,GAAG,OAAO,sDAAiD;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,oBAAoB,WAAuC;AACvE,UAAM,aAAa,MAAM,KAAK,uBAAuB;AACrD,UAAM,WAAW,OAAO,KAAK,UAAU;AACvC,UAAM,YAAQ,kCAAa,UAAU,WAAW,KAAK,SAAS;AAG9D,UAAM,cAAU,sCAAiB,UAAU,WAAW,KAAK,SAAS;AAGpE,UAAM,SAAS,KAAK;AACpB,UAAM,WAAW,SAAS,OAAO,UAAQ;AACvC,iBAAW,YAAY,WAAW;AAChC,cAAM,OAAO,GAAG,KAAK,SAAS,IAAI,QAAQ;AAC1C,YAAI,KAAK,WAAW,IAAI,KAAK,KAAC,8BAAe,KAAK,MAAM,KAAK,MAAM,GAAG,MAAM,GAAG;AAC7E,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT,CAAC;AACD,eAAW,UAAU,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG;AACxD,UAAI;AACF,cAAM,KAAK,mBAAe,oCAAe,QAAQ,KAAK,SAAS,CAAC;AAAA,MAClE,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,QAAI,MAAM,SAAS,GAAG;AACpB,WAAK,IAAI,MAAM,WAAW,MAAM,MAAM,0CAA0C;AAAA,IAClF;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,IAAI,MAAM,WAAW,QAAQ,MAAM,4CAA4C;AAAA,IACtF;AACA,QAAI,SAAS,SAAS,GAAG;AACvB,WAAK,IAAI,MAAM,WAAW,SAAS,MAAM,+CAA+C;AAAA,IAC1F;AAGA,SAAK;AAAA,MACH,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,EAAE,OAAO,YAAO;AA5hBxD;AA4hB2D,iCAAW,MAAM,MAAjB,mBAAoB,UAAS;AAAA,OAAO;AAAA,IAC3F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,mBAAkC;AAziBlD;AA0iBI,UAAM,aAAuB,CAAC;AAC9B,eAAW,YAAY,KAAK,cAAc;AACxC,YAAM,SAAS,MAAM,KAAK,eAAe,QAAQ;AACjD,YAAK,sCAAQ,WAAR,mBAAwD,kBAAiB,KAAK,SAAS;AAC1F,mBAAW,KAAK,QAAQ;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,WAAW,WAAW,GAAG;AAC3B;AAAA,IACF;AACA,UAAM,aAAa,MAAM,KAAK,uBAAuB;AACrD,UAAM,SAAS,MAAM,KAAK,eAAe,GAAG;AAC5C,UAAM,aAAS,0CAAqB,YAAY,QAAQ,IAAI,IAAI,UAAU,GAAG,KAAK,SAAS,EAAE;AAAA,MAC3F,YAAU,CAAC,KAAK,eAAe,QAAI,oCAAe,QAAQ,KAAK,SAAS,CAAC;AAAA,IAC3E;AACA,eAAW,UAAU,QAAQ;AAC3B,UAAI;AACF,cAAM,KAAK,mBAAe,oCAAe,QAAQ,KAAK,SAAS,CAAC;AAAA,MAClE,QAAQ;AAAA,MAER;AAAA,IACF;AACA,eAAW,YAAY,YAAY;AACjC,YAAM,KAAK,aAAa,UAAU,EAAE,QAAQ,EAAE,cAAc,KAAK,QAAQ,EAAE,CAAC;AAAA,IAC9E;AACA,QAAI,OAAO,SAAS,GAAG;AACrB,WAAK,IAAI,MAAM,WAAW,OAAO,MAAM,iDAAiD;AACxF,WAAK,sBAAsB,MAAM;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,yBAAwC;AACpD,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC;AAAA,IACF;AACA,UAAM,YAAQ,yCAAoB,MAAM,KAAK,uBAAuB,GAAG,KAAK,cAAc,KAAK,SAAS;AACxG,eAAW,UAAU,OAAO;AAC1B,UAAI;AACF,cAAM,KAAK,mBAAe,oCAAe,QAAQ,KAAK,SAAS,CAAC;AAAA,MAClE,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,MAAM,SAAS,GAAG;AACpB,WAAK,IAAI,MAAM,WAAW,MAAM,MAAM,wDAAwD;AAAA,IAChG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,6BAA4C;AACxD,QAAI;AACF,iBAAW,CAAC,QAAQ,MAAM,KAAK,OAAO,QAAQ,MAAM,KAAK,uBAAuB,CAAC,GAAG;AAClF,aAAI,iCAAQ,UAAS,SAAS;AAC5B,eAAK,gBAAgB,QAAI,oCAAe,QAAQ,KAAK,SAAS,CAAC;AAAA,QACjE;AAAA,MACF;AAAA,IACF,SAAS,GAAG;AAGV,WAAK,IAAI,MAAM,+CAA2C,0BAAa,CAAC,CAAC,0BAA0B;AACnG,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,qBAAqB,IAAkB;AAC7C,QAAI,KAAK,gBAAgB,IAAI,EAAE,GAAG;AAChC;AAAA,IACF;AACA,SAAK,gBAAgB,IAAI,EAAE;AAC3B,SAAK;AACL,SAAK,yBAAyB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,sBAAsB,SAAkC;AAC9D,eAAW,UAAU,SAAS;AAC5B,WAAK,gBAAgB,WAAO,oCAAe,QAAQ,KAAK,SAAS,CAAC;AAClE,WAAK;AAAA,IACP;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,yBAAyB;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,2BAAiC;AACvC,QAAI,KAAK,iBAAiB;AACxB;AAAA,IACF;AACA,SAAK,aAAa,KAAK,YAAY;AACnC,SAAK,eAAe,KAAK,WAAW,MAAM;AACxC,WAAK,eAAe;AACpB,YAAM,YAAY;AAGhB,YAAI;AACF,gBAAM,KAAK,iBAAiB;AAAA,QAC9B,SAAS,GAAG;AACV,eAAK,IAAI,MAAM,4BAAwB,0BAAa,CAAC,CAAC,yBAAyB;AAAA,QACjF;AAEA,YAAI;AACF,gBAAM,KAAK,uBAAuB;AAAA,QACpC,SAAS,GAAG;AACV,eAAK,IAAI,MAAM,kCAA8B,0BAAa,CAAC,CAAC,yBAAyB;AAAA,QACvF;AACA,cAAM,QAAkB,CAAC;AACzB,YAAI,KAAK,oBAAoB,GAAG;AAC9B,gBAAM,KAAK,WAAW,KAAK,iBAAiB,eAAe;AAAA,QAC7D;AACA,YAAI,KAAK,oBAAoB,GAAG;AAC9B,gBAAM,KAAK,WAAW,KAAK,iBAAiB,eAAe;AAAA,QAC7D;AACA,aAAK,oBAAoB;AACzB,aAAK,oBAAoB;AAEzB,YAAI,MAAM,SAAS,GAAG;AACpB,eAAK,IAAI,KAAK,wBAAwB,MAAM,KAAK,IAAI,CAAC,EAAE;AAAA,QAC1D;AAAA,MACF,GAAG;AAAA,IACL,GAAG,2BAA2B;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,4BAA2C;AACvD,UAAM,UAAsC;AAAA,MAC1C,CAAC,QAAQ,EAAE,IAAI,QAAQ,MAAM,WAAW,QAAQ,EAAE,UAAM,mBAAM,aAAa,EAAE,EAAE,CAAC;AAAA,MAChF;AAAA,QACE;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,QAAQ;AAAA,YACN,UAAM,mBAAM,6BAA6B;AAAA,YACzC,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,QAAQ,EAAE,UAAM,mBAAM,eAAe,GAAG,MAAM,UAAU,MAAM,SAAS,MAAM,MAAM,OAAO,MAAM;AAAA,QAClG;AAAA,MACF;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,QAAQ,EAAE,UAAM,mBAAM,gBAAgB,GAAG,MAAM,UAAU,MAAM,SAAS,MAAM,MAAM,OAAO,MAAM;AAAA,QACnG;AAAA,MACF;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,QAAQ,EAAE,UAAM,mBAAM,oBAAoB,GAAG,MAAM,WAAW,MAAM,aAAa,MAAM,MAAM,OAAO,MAAM;AAAA,QAC5G;AAAA,MACF;AAAA,IACF;AACA,eAAW,CAAC,IAAI,GAAG,KAAK,SAAS;AAC/B,YAAM,KAAK,aAAa,IAAI,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAQ,QAAQ,CAAC,EAAE,CAAC;AAAA,IAChF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAc,mBAAmB,UAAkB,IAA2B;AAvwBhF;AAixBI,QAAI;AACJ,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,eAAe,QAAQ;AACnD,eAAO,0CAAU,WAAV,mBAAkB,QAAO,aAAY,iCAAa,MAAS;AAAA,IACpE,QAAQ;AACN,aAAO;AAAA,IACT;AACA,UAAM,KAAK;AAAA,MACT;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,UACvB,cAAc,EAAE,UAAU,GAAG,KAAK,SAAS,IAAI,QAAQ,mBAAmB;AAAA,QAC5E;AAAA,QACA,QAAQ,CAAC;AAAA,MACX;AAAA,MACA,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE;AAAA,IACnC;AACA,UAAM,KAAK,aAAa,GAAG,QAAQ,SAAS;AAAA,MAC1C,MAAM;AAAA,MACN,QAAQ,EAAE,UAAM,mBAAM,MAAM,EAAE;AAAA,MAC9B,QAAQ,CAAC;AAAA,IACX,CAAC;AACD,UAAM,KAAK,aAAa,GAAG,QAAQ,oBAAoB;AAAA,MACrD,MAAM;AAAA,MACN,QAAQ;AAAA,QACN,UAAM,mBAAM,WAAW;AAAA,QACvB,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,KAAK;AAAA,MACP;AAAA,MACA,QAAQ,CAAC;AAAA,IACX,CAAC;AAID,UAAM,KAAK,aAAa,GAAG,QAAQ,eAAe;AAAA,MAChD,MAAM;AAAA,MACN,QAAQ,EAAE,UAAM,mBAAM,OAAO,GAAG,MAAM,UAAU,MAAM,QAAQ,MAAM,MAAM,OAAO,OAAO,KAAK,GAAG;AAAA,MAChG,QAAQ,CAAC;AAAA,IACX,CAAC;AAID,UAAM,KAAK,aAAa,GAAG,QAAQ,YAAY;AAAA,MAC7C,MAAM;AAAA,MACN,QAAQ,EAAE,UAAM,mBAAM,YAAY,GAAG,MAAM,UAAU,MAAM,WAAW,MAAM,MAAM,OAAO,OAAO,KAAK,GAAG;AAAA,MACxG,QAAQ,CAAC;AAAA,IACX,CAAC;AACD,UAAM,KAAK,SAAS,GAAG,QAAQ,YAAY,EAAE,KAAK,IAAI,KAAK,KAAK,CAAC;AAIjE,UAAM,KAAK,aAAa,GAAG,QAAQ,oBAAoB;AAAA,MACrD,MAAM;AAAA,MACN,QAAQ,EAAE,UAAM,mBAAM,YAAY,EAAE;AAAA,MACpC,QAAQ,CAAC;AAAA,IACX,CAAC;AACD,eAAW,SAAS,eAAe;AACjC,YAAM,KAAK,aAAa,GAAG,QAAQ,oBAAoB,KAAK,IAAI;AAAA,QAC9D,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,UAAM,mBAAM,gBAAgB,MAAM,YAAY,CAAC;AAAA,UAC/C,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,UACP,KAAK;AAAA,QACP;AAAA,QACA,QAAQ,CAAC;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA,EAGiB,cAAc,oBAAI,IAAoB;AAAA;AAAA,EAGtC,eAAe,oBAAI,IAA+C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBnF,MAAc,kBAAkB,UAAkB,WAAmB,MAAgC;AAp3BvG;AAq3BI,UAAM,MAAM,KAAK,aAAa,IAAI,QAAQ;AAC1C,QAAI;AACF,YAAM,WAAW,iBAAM,KAAK,eAAe,QAAQ,MAAlC,mBAAsC,WAAtC,mBAA8C;AAC/D,YAAM,YAAQ;AAAA,QACZ,OAAO,YAAY,WAAW,UAAU;AAAA,QACxC;AAAA,QACA;AAAA,QACA;AAAA,QACA,2BAAK;AAAA,QACL,2BAAK;AAAA,MACP;AACA,UAAI,UAAU,QAAW;AACvB;AAAA,MACF;AAGA,YAAM,KAAK,aAAa,UAAU,EAAE,QAAQ,EAAE,MAAM,MAAM,EAAE,CAAC;AAC7D,WAAK,aAAa,IAAI,UAAU,EAAE,MAAM,OAAO,KAAK,CAAC;AACrD,WAAK,IAAI,MAAM,GAAG,QAAQ,yBAAyB,KAAK,GAAG;AAAA,IAC7D,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,GAAG,QAAQ,yCAAqC,0BAAa,CAAC,CAAC,GAAG;AAAA,IACnF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,iBAAiB,UAAkB,OAA8B;AAC7E,UAAM,WAAO,iCAAa,KAAK;AAC/B,QAAI,KAAK,YAAY,IAAI,QAAQ,MAAM,MAAM;AAC3C;AAAA,IACF;AACA,SAAK,YAAY,IAAI,UAAU,IAAI;AACnC,QAAI;AACF,YAAM,KAAK,aAAa,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAAA,IACxD,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,GAAG,QAAQ,qCAAiC,0BAAa,CAAC,CAAC,GAAG;AAAA,IAC/E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,sBAAqC;AACjD,UAAM,SAAS,KAAK;AACpB,UAAM,UAAM,qCAAgB,MAAM;AAClC,QAAI,CAAC,KAAK;AACR;AAAA,IACF;AAIA,WAAO,UAAU,CAAC,GAAG;AACrB,QAAI;AACF,YAAM,KAAK,yBAAyB,kBAAkB,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,GAAG,EAAE,EAAE,CAAC;AACtG,WAAK,IAAI,KAAK,8CAA8C,IAAI,EAAE,8BAA8B;AAAA,IAClG,SAAS,GAAG;AACV,WAAK,IAAI;AAAA,QACP,oDAAgD,0BAAa,CAAC,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,oBAAmC;AAC/C,UAAM,SAAS,KAAK;AACpB,QAAI,EAAE,iBAAiB,SAAS;AAC9B;AAAA,IACF;AACA,QAAI,OAAO,aAAa;AACtB,aAAO,kBAAkB;AAAA,IAC3B;AACA,WAAO,OAAO;AACd,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,sBAAsB,kBAAkB,KAAK,SAAS,EAAE;AAC/E,UAAI,2BAAK,QAAQ;AACf,YAAI,IAAI,OAAO,aAAa;AAC1B,cAAI,OAAO,kBAAkB;AAAA,QAC/B;AACA,eAAO,IAAI,OAAO;AAClB,cAAM,KAAK,sBAAsB,kBAAkB,KAAK,SAAS,IAAI,GAAG;AACxE,aAAK,IAAI,KAAK,mDAAmD;AAAA,MACnE;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,KAAK,gDAA4C,0BAAa,CAAC,CAAC,GAAG;AAAA,IAC9E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBQ,cACN,QACA,cACA,gBACA,cACA,kBACA,aACkC;AAClC,eAAO,qCAAc,QAAQ;AAAA,MAC3B;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA,gBAAgB,YAAM,8BAAe,IAAI,KAAK,MAA4C;AAAA,MAC1F,KAAK;AAAA,QACH,OAAO,aAAW,KAAK,IAAI,MAAM,OAAO;AAAA,QACxC,MAAM,aAAW,KAAK,IAAI,KAAK,OAAO;AAAA,QACtC,MAAM,aAAW,KAAK,IAAI,KAAK,OAAO;AAAA,MACxC;AAAA,MACA,cAAc,OAAO,IAAI,QAAQ;AAG/B,YAAI,KAAC,8BAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,KAAK,MAA4C,GAAG;AACrG;AAAA,QACF;AACA,cAAM,KAAK,aAAa,IAAI,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAQ,QAAQ,CAAC,EAAE,CAAC;AAC9E,YAAI,IAAI,SAAS,SAAS;AACxB,eAAK,qBAAqB,EAAE;AAC5B,eAAK,eAAe,IAAI,EAAE;AAAA,QAC5B;AAAA,MACF;AAAA,MACA,aAAa,CAAC,IAAI,UAAU;AAE1B,YAAI,KAAC,8BAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,KAAK,MAA4C,GAAG;AACrG;AAAA,QACF;AACA,aAAK,WAAW,IAAI,KAAK;AAGzB,YAAI,GAAG,SAAS,aAAa,KAAK,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AAC/E,gBAAM,YAAY,GAAG,MAAM,GAAG,GAAG,QAAQ,GAAG,CAAC;AAC7C,eAAK,KAAK,iBAAiB,WAAW,KAAK;AAC3C,eAAK,KAAK,kBAAkB,WAAW,OAAO,+BAAW,KAAK;AAAA,QAChE;AAAA,MACF;AAAA,MACA,cAAc,UAAQ,KAAK,KAAK,kBAAkB,OAAO,IAAI,MAAM,+BAAW,UAAU;AAAA,MACxF,QAAQ;AAAA,QACN,UAAU,CAAC,SAAS,OAAQ,KAAK,YAAY,SAAY,KAAK,WAAW,SAAS,EAAE;AAAA,QACpF,QAAQ,YAAU,KAAK,aAAa,MAAM;AAAA,MAC5C;AAAA,MACA,cAAc,CAAC,IAAI,WAAW,aAAa,SAAS,IAAI,MAAM;AAAA,MAC9D,YAAY,MAAM,aAAa,YAAY;AAAA,MAC3C,mBAAmB,CAAC,SAAS,OAAO;AAClC,YAAI,KAAK,WAAW;AAClB,iBAAO,MAAM;AAAA,UAAC;AAAA,QAChB;AACA,cAAM,QAAQ,KAAK,YAAY,SAAS,EAAE;AAC1C,eAAO,MAAM;AACX,cAAI,OAAO;AACT,iBAAK,cAAc,KAAK;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAAA,MACA,mBAAmB,KAAK,kBAAkB;AAAA,MAC1C,cAAc,WAAS,KAAK,cAAc,OAAO,IAAI,KAAK;AAAA,MAC1D;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,cAAc,IAAY,OAAgD;AAtjCpF;AAujCI,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AACA,UAAM,eAAW,oCAAe,IAAI,KAAK,SAAS;AAIlD,UAAM,WAAW,SAAS,MAAM,GAAG,SAAS,QAAQ,GAAG,CAAC;AACxD,eAAK,eAAe,IAAI,QAAQ,MAAhC,mBAAmC,kBAAkB,UAAU,MAAM,KAAK,MAAM;AAAA,EAClF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,SAAS,UAA4B;AAvkC/C;AAwkCI,QAAI;AACF,WAAK,YAAY;AACjB,WAAK,aAAa,KAAK,YAAY;AACnC,WAAK,aAAa,KAAK,eAAe;AACtC,iBAAK,iBAAL,mBAAmB;AACnB,iBAAW,cAAc,KAAK,aAAa;AACzC,mBAAW,MAAM;AAAA,MACnB;AASA,YAAM,SAA6B,CAAC,KAAK,SAAS,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC;AAC/F,iBAAW,YAAY,KAAK,gBAAgB,KAAK,GAAG;AAClD,aAAK,gBAAgB,IAAI,UAAU,KAAK;AACxC,eAAO,KAAK,KAAK,SAAS,GAAG,QAAQ,oBAAoB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC;AAGnF,mBAAW,SAAS,eAAe;AACjC,iBAAO,KAAK,KAAK,SAAS,GAAG,QAAQ,oBAAoB,KAAK,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC;AAAA,QAC9F;AAAA,MACF;AACA,aAAO,KAAK,KAAK,SAAS,sBAAsB,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC;AACtE,aAAO,KAAK,KAAK,SAAS,yBAAyB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC;AAC7E,WAAK,QAAQ,IAAI,MAAM,EACpB,MAAM,MAAM;AAAA,MAEb,CAAC,EACA,QAAQ,QAAQ;AACnB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,aAAS;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,eAAwC;AACpD,UAAM,YAAQ,kDAAoB,IAAI;AACtC,UAAM,QAAQ,UAAM,wCAAe,KAAK;AACxC,QAAI,MAAM,SAAS,GAAG;AAIpB,WAAK,IAAI,KAAK,cAAc,MAAM,MAAM,kEAAkE;AAC1G,aAAO;AAAA,IACT;AACA,SAAK,IAAI,KAAK,yEAAyE;AACvF,UAAM,SAAS,MAAM,KAAK,aAAa;AACvC,SAAK,IAAI,KAAK,cAAc,OAAO,MAAM,0BAA0B;AACnE,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,eAAwC;AAGpD,SAAK,kBAAkB,KAAK,IAAI;AAChC,UAAM,YAAQ,kDAAoB,IAAI;AACtC,UAAM,QAAQ,UAAM,wCAAe,KAAK;AACxC,QAAI,QAA6C,CAAC;AAClD,QAAI;AACF,cAAQ,UAAM,iCAAe;AAAA,QAC3B,QAAQ,CAAC,QAAQ,OAAO,KAAK,WAAW,QAAQ,EAAE;AAAA,QAClD,OAAO,SAAO,KAAK,SAAS,GAAG;AAAA,QAC/B,KAAK,EAAE,OAAO,aAAW,KAAK,IAAI,MAAM,OAAO,GAAG,MAAM,aAAW,KAAK,IAAI,KAAK,OAAO,EAAE;AAAA,MAC5F,CAAC;AAAA,IACH,SAAS,GAAG;AACV,WAAK,IAAI,KAAK,iEAA6D,0BAAa,CAAC,CAAC,EAAE;AAAA,IAC9F;AACA,UAAM,aAAS;AAAA,MAAgB;AAAA,MAAO;AAAA,MAAO,CAAC,SAAS,YACrD,KAAK,IAAI,KAAK,sBAAsB,OAAO,mCAA8B,OAAO,oBAAoB;AAAA,IACtG;AAGA,UAAM,UAAU,IAAI,IAAI,UAAM,yCAAY,+CAAiB,IAAI,CAAC,CAAC;AACjE,UAAM,OAAO,QAAQ,OAAO,IAAI,OAAO,OAAO,YAAU,CAAC,QAAQ,IAAI,OAAO,EAAE,CAAC,IAAI;AACnF,cAAM,yCAAgB,OAAO,IAAI;AACjC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,qBAAqB,UAA6C;AAjrClF;AAkrCI,QAAI;AACJ,QAAI;AACF,gBAAU,iBAAM,KAAK,eAAe,QAAQ,MAAlC,mBAAsC,WAAtC,mBAA8C;AAAA,IAC1D,QAAQ;AACN,eAAS;AAAA,IACX;AACA,eAAO,6CAAmB,sCAAgB,MAAM,IAAI,SAAS,QAAW,cAAY;AAClF,WAAK,oBAAoB,UAAU,EAAE,WAAW,8BAAY,KAAK,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,gBAAgB,UAAwC;AAvsCxE;AA0sCI,QAAI;AACJ,QAAI;AACF,YAAM,UAAU,iBAAM,KAAK,eAAe,QAAQ,MAAlC,mBAAsC,WAAtC,mBAA8C;AAC9D,UAAI,OAAO,WAAW,UAAU;AAC9B,cAAM,SAAkB,KAAK,MAAM,MAAM;AACzC,YAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC3E,oBAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF,QAAQ;AACN,gBAAU;AAAA,IACZ;AACA,WAAO,IAAI,gCAAY,SAAS,aAAW;AACzC,WAAK,oBAAoB,UAAU,EAAE,YAAY,KAAK,UAAU,OAAO,EAAE,CAAC;AAAA,IAC5E,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBAA4B;AAClC,UAAM,UAAU,OAAQ,KAAK,OAA8C,eAAe;AAC1F,YAAQ,OAAO,SAAS,OAAO,KAAK,UAAU,IAAI,UAAU,MAAM;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,WAAW,QAAgB,WAA0E;AAC3G,WAAO,IAAI,QAAQ,aAAW;AAC5B,YAAM,gBAAY,4CAAiB,KAAK,OAAO,sBAAkB,kCAAkB,CAAC;AACpF,YAAM,aAA2D,CAAC;AAClE,YAAM,UAA6C,CAAC;AACpD,UAAI,UAAU;AACd,YAAM,SAAS,MAAY;AACzB,YAAI,SAAS;AACX;AAAA,QACF;AACA,kBAAU;AACV,mBAAW,UAAU,SAAS;AAC5B,cAAI;AACF,mBAAO,MAAM;AAAA,UACf,QAAQ;AAAA,UAER;AAAA,QACF;AACA,gBAAQ,UAAU;AAAA,MACpB;AAGA,YAAM,aAAa,CAAC,aAAuC;AACzD,cAAM,aAAS,gCAAa,MAAM;AAClC,gBAAQ,KAAK,MAAM;AACnB,eAAO,GAAG,WAAW,CAAC,KAAK,UAAU;AACnC,gBAAM,WAAW,qBAAqB,KAAK,IAAI,SAAS,CAAC;AACzD,cAAI,UAAU;AACZ,uBAAW,KAAK,EAAE,UAAU,SAAS,CAAC,GAAG,SAAS,MAAM,QAAQ,CAAC;AAAA,UACnE;AAAA,QACF,CAAC;AACD,eAAO,GAAG,SAAS,SAAO;AAIxB,eAAK,IAAI;AAAA,YACP,0BAA0B,WAAW,iBAAiB,QAAQ,KAAK,EAAE,SAAK,0BAAa,GAAG,CAAC,GACzF,WAAW,gDAA2C,EACxD;AAAA,UACF;AACA,cAAI;AACF,mBAAO,MAAM;AAAA,UACf,QAAQ;AAAA,UAER;AAAA,QACF,CAAC;AACD,cAAM,aAAa,MAAY;AAC7B,cAAI,SAAS;AACX;AAAA,UACF;AACA,gBAAM,UAAU;AAAA;AAAA;AAAA;AAAA,MAA6F,MAAM;AAAA;AAAA;AACnH,cAAI;AACF,mBAAO,KAAK,SAAS,MAAM,iBAAiB;AAAA,UAC9C,QAAQ;AAAA,UAER;AAAA,QACF;AACA,eAAO,KAAK,GAAG,UAAU,MAAM;AAI7B,cAAI,UAAU;AACZ,gBAAI;AACF,qBAAO,sBAAsB,QAAQ;AAAA,YACvC,QAAQ;AACN,mBAAK,IAAI,KAAK,gDAAgD,QAAQ,qCAAgC;AAAA,YACxG;AAAA,UACF;AAGA,mBAAS,IAAI,GAAG,IAAI,mBAAmB,KAAK;AAC1C,iBAAK,WAAW,YAAY,IAAI,uBAAuB;AAAA,UACzD;AAAA,QACF,CAAC;AAAA,MACH;AAEA,UAAI,UAAU,WAAW,GAAG;AAC1B,mBAAW,MAAS;AAAA,MACtB,OAAO;AACL,mBAAW,YAAY,WAAW;AAChC,qBAAW,QAAQ;AAAA,QACrB;AAAA,MACF;AACA,WAAK,WAAW,QAAQ,SAAS;AAAA,IACnC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,SAAS,KAA8B;AAC7C,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAM,iBAAAA,KAAQ,KAAK,SAAO;AAC9B,YAAI,OAAO;AACX,YAAI,QAAQ;AACZ,YAAI,GAAG,QAAQ,WAAS;AACtB,mBAAU,MAAiB;AAC3B,cAAI,QAAQ,iCAAqB;AAE/B,gBAAI,QAAQ,IAAI,MAAM,0BAA0B,GAAG,EAAE,CAAC;AACtD;AAAA,UACF;AACA,kBAAQ,OAAO,KAAK;AAAA,QACtB,CAAC;AAGD,YAAI,GAAG,SAAS,MAAM;AACtB,YAAI,GAAG,OAAO,MAAM,QAAQ,IAAI,CAAC;AAAA,MACnC,CAAC;AACD,UAAI,GAAG,SAAS,MAAM;AACtB,UAAI,WAAW,kBAAkB,MAAM,IAAI,QAAQ,IAAI,MAAM,oBAAoB,GAAG,EAAE,CAAC,CAAC;AAAA,IAC1F,CAAC;AAAA,EACH;AACF;AAEA,IAAI,QAAQ,SAAS,QAAQ;AAE3B,SAAO,UAAU,CAAC,YAAuD,IAAI,OAAO,OAAO;AAC7F,OAAO;AAEL,GAAC,MAAM,IAAI,OAAO,GAAG;AACvB;",
6
6
  "names": ["httpGet"]
7
7
  }
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "yamaha",
4
- "version": "2.1.0",
4
+ "version": "2.1.1",
5
5
  "news": {
6
+ "2.1.1": {
7
+ "en": "On an instance updated from an earlier version, the info datapoints now get the translated names too instead of keeping the old ones.",
8
+ "de": "Auf einer aktualisierten Instanz bekommen auch die info-Datenpunkte die übersetzten Namen, statt die alten zu behalten.",
9
+ "ru": "На обновлённом экземпляре точки данных info тоже получают переведённые названия вместо старых.",
10
+ "pt": "Numa instância atualizada, os pontos de dados info passam a receber os nomes traduzidos em vez de manter os antigos.",
11
+ "nl": "Op een bijgewerkte instantie krijgen ook de info-datapunten de vertaalde namen in plaats van de oude te behouden.",
12
+ "fr": "Sur une instance mise à jour, les points de données info reçoivent aussi les noms traduits au lieu de garder les anciens.",
13
+ "it": "Su un'istanza aggiornata anche i datapoint info ricevono i nomi tradotti invece di mantenere i vecchi.",
14
+ "es": "En una instancia actualizada, los puntos de datos info también reciben los nombres traducidos en lugar de conservar los antiguos.",
15
+ "pl": "Na zaktualizowanej instancji punkty danych info również otrzymują przetłumaczone nazwy zamiast zachowywać stare.",
16
+ "uk": "На оновленому екземплярі точки даних info також отримують перекладені назви замість старих.",
17
+ "zh-cn": "在已更新的实例上,info 数据点现在也会获得翻译后的名称,而不再保留旧名称。"
18
+ },
6
19
  "2.1.0": {
7
20
  "en": "Datapoint names now appear in your ioBroker language. The empty Media server folder is gone, the menu view starts clean, and a receiver that changed its address is found again.",
8
21
  "de": "Datenpunkt-Namen erscheinen jetzt in deiner ioBroker-Sprache. Der leere Ordner Media server ist weg, die Menü-Ansicht startet leer, und ein Receiver mit neuer Adresse wird wiedergefunden.",
@@ -80,19 +93,6 @@
80
93
  "pl": "Przebudowane drzewo obiektów: jeden blok odtwarzacza na strefę pokazuje, co gra, menu scen niesie tytuły z urządzenia plus listę scen, a jeden tuner obsługuje wszystkie pasma",
81
94
  "uk": "Перероблене дерево об'єктів: один блок плеєра на зону показує, що грає, меню сцен несе назви з пристрою плюс список сцен, один тюнер обслуговує всі діапазони",
82
95
  "zh-cn": "重构的对象树:每个区域一个播放器块显示当前播放内容,场景菜单带设备自带的标题外加场景列表,所有波段共用一个调谐器"
83
- },
84
- "1.7.0": {
85
- "en": "Scene recall works on every generation and shows titles, refused commands are reported, restarts finish in seconds, plus on-screen remote and many new datapoints.",
86
- "de": "Szenen-Aufruf klappt auf jeder Generation und zeigt Titel, abgelehnte Befehle werden gemeldet, Neustarts dauern Sekunden, dazu Bildschirm-Fernbedienung und viele neue Datenpunkte.",
87
- "ru": "Вызов сцен работает на всех поколениях и показывает названия, отклонённые команды отображаются, перезапуск занимает секунды, плюс экранный пульт и много новых точек данных.",
88
- "pt": "A chamada de cenas funciona em todas as gerações e mostra títulos, comandos recusados são reportados, reinícios demoram segundos, além de controlo no ecrã e muitos pontos novos.",
89
- "nl": "Scène-oproep werkt op elke generatie en toont titels, geweigerde commando's worden gemeld, herstarts duren seconden, plus schermafstandsbediening en veel nieuwe datapunten.",
90
- "fr": "Le rappel de scène fonctionne sur toutes les générations et affiche les titres, les commandes refusées sont signalées, les redémarrages prennent quelques secondes, plus de nouveaux points de données.",
91
- "it": "Il richiamo scene funziona su ogni generazione e mostra i titoli, i comandi rifiutati vengono segnalati, i riavvii durano secondi, più telecomando su schermo e molti nuovi datapoint.",
92
- "es": "La llamada de escenas funciona en todas las generaciones y muestra títulos, los comandos rechazados se notifican, los reinicios tardan segundos, más mando en pantalla y muchos puntos nuevos.",
93
- "pl": "Wywoływanie scen działa na każdej generacji i pokazuje tytuły, odrzucone polecenia są zgłaszane, restarty trwają sekundy, do tego pilot ekranowy i wiele nowych punktów danych.",
94
- "uk": "Виклик сцен працює на всіх поколіннях і показує назви, відхилені команди повідомляються, перезапуск триває секунди, плюс екранний пульт і багато нових точок даних.",
95
- "zh-cn": "场景调用适用于所有代次并显示名称,被拒绝的命令会被报告,重启只需数秒,另有屏幕遥控和众多新数据点。"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.yamaha",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "ioBroker adapter for Yamaha AV receivers and MusicCast devices",
5
5
  "author": {
6
6
  "name": "krobi",