iobroker.yamaha 2.3.0 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/xml/catalog.ts"],
4
- "sourcesContent": ["import type { ObjectDef } from \"../catalog/types\";\nimport type { I18nKey } from \"../i18n\";\nimport { escapeXmlText } from \"./entities\";\nimport type { BasicStatus } from \"./protocol\";\n\n/**\n * The single source for XML/YNC amplifier states: one entry per unified state\n * carries BOTH its ioBroker object (`common`) AND its Basic_Status read field +\n * PUT-XML builder \u2014 replacing the former `XML_AMP_STATES` (device-controller) /\n * `XML_STATE_MAPPINGS` (command-mapper) pair kept in sync by hand. The controller\n * reads `common`, the command-mapper reads `statusField`/`toInner`.\n */\nexport interface XmlAmpEntry {\n /** Unified state id, relative to the zone prefix. */\n state: string;\n /**\n * ioBroker common for the object, carrying its name as a translation KEY: this catalog is a\n * module-level constant, so the controller resolves the key when it creates the object.\n */\n common: Omit<ObjectDef[\"common\"], \"name\"> & { nameKey: I18nKey; descKey?: I18nKey };\n /** The Basic_Status field this state reads from; absent for a write-only command (e.g. scene recall). */\n statusField?: keyof BasicStatus;\n /** Build the inner PUT XML for a written value; absent means read-only. */\n toInner?: (value: unknown) => string;\n /** Only exists on the main zone (a system/main-wide feature like scenes, HDMI outputs, party). */\n mainOnly?: boolean;\n /** Override the write target element (e.g. `System` for HDMI outputs and party); default is the zone element. */\n writeZone?: string;\n}\n\n/** The unified XML amplifier catalog \u2014 object + read field + PUT builder in one list. */\nexport const XML_AMP_CATALOG: XmlAmpEntry[] = [\n {\n state: \"power\",\n common: { nameKey: \"power\", type: \"boolean\", role: \"switch.power\", read: true, write: true },\n statusField: \"power\",\n toInner: value => `<Power_Control><Power>${value ? \"On\" : \"Standby\"}</Power></Power_Control>`,\n },\n {\n state: \"volume\",\n common: {\n nameKey: \"volume\",\n type: \"number\",\n role: \"level.volume\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -80.5,\n max: 16.5,\n step: 0.5,\n },\n statusField: \"volume\",\n toInner: value =>\n `<Volume><Lvl><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume>`,\n },\n {\n state: \"mute\",\n common: { nameKey: \"mute\", type: \"boolean\", role: \"media.mute\", read: true, write: true },\n statusField: \"mute\",\n toInner: value => `<Volume><Mute>${value ? \"On\" : \"Off\"}</Mute></Volume>`,\n },\n {\n state: \"input\",\n common: { nameKey: \"input\", type: \"string\", role: \"media.input\", read: true, write: true },\n statusField: \"input\",\n toInner: value => `<Input><Input_Sel>${escapeXmlText(value)}</Input_Sel></Input>`,\n },\n {\n state: \"soundProgram\",\n common: { nameKey: \"soundProgram\", type: \"string\", role: \"state\", read: true, write: true },\n statusField: \"soundProgram\",\n toInner: value =>\n `<Surround><Program_Sel><Current><Sound_Program>${escapeXmlText(value)}</Sound_Program></Current></Program_Sel></Surround>`,\n },\n {\n state: \"sound.pureDirect\",\n common: { nameKey: \"pureDirect\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"pureDirect\",\n toInner: value => `<Sound_Video><Pure_Direct><Mode>${value ? \"On\" : \"Off\"}</Mode></Pure_Direct></Sound_Video>`,\n },\n {\n state: \"sound.straight\",\n common: { nameKey: \"straight\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"straight\",\n toInner: value =>\n `<Surround><Program_Sel><Current><Straight>${value ? \"On\" : \"Off\"}</Straight></Current></Program_Sel></Surround>`,\n },\n {\n state: \"sound.direct\",\n common: { nameKey: \"direct\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"direct\",\n toInner: value => `<Sound_Video><Direct><Mode>${value ? \"On\" : \"Off\"}</Mode></Direct></Sound_Video>`,\n },\n {\n state: \"sound.adaptiveDrc\",\n common: { nameKey: \"adaptiveDRC\", type: \"string\", role: \"state\", read: true, write: true },\n statusField: \"adaptiveDrc\",\n toInner: value => `<Sound_Video><Adaptive_DRC>${escapeXmlText(value)}</Adaptive_DRC></Sound_Video>`,\n },\n {\n // Read-only: openHAB reads the Dialogue_Lvl path, but the write value structure\n // (Val/Exp/Unit vs bare) is not confirmed by a reference, so no write is offered.\n state: \"sound.dialogueLevel\",\n common: { nameKey: \"dialogueLevel\", type: \"number\", role: \"value\", read: true, write: false },\n statusField: \"dialogueLevel\",\n },\n {\n state: \"sleep\",\n common: { nameKey: \"sleepTimer\", type: \"string\", role: \"state\", read: true, write: true },\n statusField: \"sleep\",\n toInner: value => `<Power_Control><Sleep>${escapeXmlText(value)}</Sleep></Power_Control>`,\n },\n // Tone, subwoofer trim and the Extra-Bass/YPAO toggles \u2014 exposed by the predecessor\n // adapter (yamaha-nodejs-soef) on real pre-2010 devices, and dropped in the rewrite.\n // Values verified against that library's PUT paths (audit findings F3/F4).\n // Tone/subwoofer values are real decibels on the state; the wire carries tenths with\n // Exp=1 (the same Val/Exp/Unit envelope as the volume above): *10 out, /10 on read.\n {\n state: \"sound.bass\",\n common: {\n nameKey: \"bass\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -6,\n max: 6,\n step: 0.5,\n },\n statusField: \"bass\",\n toInner: value =>\n `<Sound_Video><Tone><Bass><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Bass></Tone></Sound_Video>`,\n },\n {\n state: \"sound.treble\",\n common: {\n nameKey: \"treble\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -6,\n max: 6,\n step: 0.5,\n },\n statusField: \"treble\",\n toInner: value =>\n `<Sound_Video><Tone><Treble><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Treble></Tone></Sound_Video>`,\n },\n {\n state: \"sound.subwooferTrim\",\n common: {\n nameKey: \"subwooferTrim\",\n descKey: \"descSubwooferTrim\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -6,\n max: 6,\n step: 0.5,\n },\n statusField: \"subwooferTrim\",\n toInner: value =>\n `<Volume><Subwoofer_Trim><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Subwoofer_Trim></Volume>`,\n },\n {\n state: \"sound.extraBass\",\n common: { nameKey: \"extraBass\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"extraBass\",\n toInner: value => `<Sound_Video><Extra_Bass>${value ? \"Auto\" : \"Off\"}</Extra_Bass></Sound_Video>`,\n },\n {\n state: \"sound.ypaoVolume\",\n common: { nameKey: \"ypaoVolume\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"ypaoVolume\",\n toInner: value => `<Sound_Video><YPAO_Volume>${value ? \"Auto\" : \"Off\"}</YPAO_Volume></Sound_Video>`,\n },\n {\n state: \"sound.dialogueLift\",\n common: {\n nameKey: \"dialogueLift\",\n descKey: \"descDialogueLift\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 0,\n max: 5,\n step: 1,\n },\n statusField: \"dialogueLift\",\n toInner: value =>\n `<Sound_Video><Dialogue_Adjust><Dialogue_Lift>${Number(value)}</Dialogue_Lift></Dialogue_Adjust></Sound_Video>`,\n },\n // HDMI outputs and party \u2014 the predecessor's setHDMIOutput / partyMode.\n // Main-zone-only; both are written on the System element (writeZone).\n // Scenes are NOT in this static list: the device declares its scenes itself\n // (`<Scene_Sel_Item>`: which exist, their titles, the `Scene_Sel` write value \u2014\n // per zone), so the controller builds them from that declaration. The\n // predecessor's blind `Scene_Load` is gone; nothing ever proved it worked (#615).\n {\n state: \"hdmiOut1\",\n common: { nameKey: \"hdmiOUT1\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"hdmiOut1\",\n mainOnly: true,\n writeZone: \"System\",\n toInner: value => `<Sound_Video><HDMI><Output><OUT_1>${value ? \"On\" : \"Off\"}</OUT_1></Output></HDMI></Sound_Video>`,\n },\n {\n state: \"hdmiOut2\",\n common: { nameKey: \"hdmiOUT2\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"hdmiOut2\",\n mainOnly: true,\n writeZone: \"System\",\n toInner: value => `<Sound_Video><HDMI><Output><OUT_2>${value ? \"On\" : \"Off\"}</OUT_2></Output></HDMI></Sound_Video>`,\n },\n {\n state: \"multiroom.party\",\n common: { nameKey: \"partyModeAllZones\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"party\",\n mainOnly: true,\n writeZone: \"System\",\n toInner: value => `<Party_Mode><Mode>${value ? \"On\" : \"Off\"}</Mode></Party_Mode>`,\n },\n];\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ObjectDef } from \"../catalog/types\";\nimport type { I18nKey } from \"../i18n\";\nimport { escapeXmlText } from \"./entities\";\nimport type { BasicStatus } from \"./protocol\";\n\n/**\n * The single source for XML/YNC amplifier states: one entry per unified state\n * carries BOTH its ioBroker object (`common`) AND its Basic_Status read field +\n * PUT-XML builder \u2014 replacing the former `XML_AMP_STATES` (device-controller) /\n * `XML_STATE_MAPPINGS` (command-mapper) pair kept in sync by hand. The controller\n * reads `common`, the command-mapper reads `statusField`/`toInner`.\n */\nexport interface XmlAmpEntry {\n /** Unified state id, relative to the zone prefix. */\n state: string;\n /**\n * ioBroker common for the object, carrying its name as a translation KEY: this catalog is a\n * module-level constant, so the controller resolves the key when it creates the object.\n */\n common: Omit<ObjectDef[\"common\"], \"name\"> & { nameKey: I18nKey; descKey?: I18nKey };\n /** The Basic_Status field this state reads from; absent for a write-only command (e.g. scene recall). */\n statusField?: keyof BasicStatus;\n /** Build the inner PUT XML for a written value; absent means read-only. */\n toInner?: (value: unknown) => string;\n /** Only exists on the main zone (a system/main-wide feature like scenes, HDMI outputs, party). */\n mainOnly?: boolean;\n /** Override the write target element (e.g. `System` for HDMI outputs and party); default is the zone element. */\n writeZone?: string;\n}\n\n/** The unified XML amplifier catalog \u2014 object + read field + PUT builder in one list. */\nexport const XML_AMP_CATALOG: XmlAmpEntry[] = [\n {\n state: \"power\",\n common: { nameKey: \"power\", type: \"boolean\", role: \"switch.power\", read: true, write: true },\n statusField: \"power\",\n toInner: value => `<Power_Control><Power>${value ? \"On\" : \"Standby\"}</Power></Power_Control>`,\n },\n {\n state: \"volume\",\n common: {\n nameKey: \"volume\",\n type: \"number\",\n role: \"level.volume\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -80.5,\n max: 16.5,\n step: 0.5,\n },\n statusField: \"volume\",\n toInner: value =>\n `<Volume><Lvl><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume>`,\n },\n {\n state: \"mute\",\n common: { nameKey: \"mute\", type: \"boolean\", role: \"media.mute\", read: true, write: true },\n statusField: \"mute\",\n toInner: value => `<Volume><Mute>${value ? \"On\" : \"Off\"}</Mute></Volume>`,\n },\n {\n state: \"input\",\n common: { nameKey: \"input\", type: \"string\", role: \"media.input\", read: true, write: true },\n statusField: \"input\",\n toInner: value => `<Input><Input_Sel>${escapeXmlText(value)}</Input_Sel></Input>`,\n },\n {\n state: \"soundProgram\",\n common: {\n nameKey: \"soundProgram\",\n descKey: \"descSoundProgram\",\n type: \"string\",\n role: \"state\",\n read: true,\n write: true,\n },\n statusField: \"soundProgram\",\n toInner: value =>\n `<Surround><Program_Sel><Current><Sound_Program>${escapeXmlText(value)}</Sound_Program></Current></Program_Sel></Surround>`,\n },\n {\n state: \"sound.pureDirect\",\n common: {\n nameKey: \"pureDirect\",\n descKey: \"descPureDirect\",\n type: \"boolean\",\n role: \"switch\",\n read: true,\n write: true,\n },\n statusField: \"pureDirect\",\n toInner: value => `<Sound_Video><Pure_Direct><Mode>${value ? \"On\" : \"Off\"}</Mode></Pure_Direct></Sound_Video>`,\n },\n {\n state: \"sound.straight\",\n common: { nameKey: \"straight\", descKey: \"descStraight\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"straight\",\n toInner: value =>\n `<Surround><Program_Sel><Current><Straight>${value ? \"On\" : \"Off\"}</Straight></Current></Program_Sel></Surround>`,\n },\n {\n state: \"sound.direct\",\n common: { nameKey: \"direct\", descKey: \"descDirect\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"direct\",\n toInner: value => `<Sound_Video><Direct><Mode>${value ? \"On\" : \"Off\"}</Mode></Direct></Sound_Video>`,\n },\n {\n state: \"sound.adaptiveDrc\",\n common: {\n nameKey: \"adaptiveDRC\",\n descKey: \"descAdaptiveDRC\",\n type: \"string\",\n role: \"state\",\n read: true,\n write: true,\n },\n statusField: \"adaptiveDrc\",\n toInner: value => `<Sound_Video><Adaptive_DRC>${escapeXmlText(value)}</Adaptive_DRC></Sound_Video>`,\n },\n {\n // Read-only: openHAB reads the Dialogue_Lvl path, but the write value structure\n // (Val/Exp/Unit vs bare) is not confirmed by a reference, so no write is offered.\n state: \"sound.dialogueLevel\",\n common: {\n nameKey: \"dialogueLevel\",\n descKey: \"descDialogueLevel\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n },\n statusField: \"dialogueLevel\",\n },\n {\n state: \"sleep\",\n common: { nameKey: \"sleepTimer\", type: \"string\", role: \"state\", read: true, write: true },\n statusField: \"sleep\",\n toInner: value => `<Power_Control><Sleep>${escapeXmlText(value)}</Sleep></Power_Control>`,\n },\n // Tone, subwoofer trim and the Extra-Bass/YPAO toggles \u2014 exposed by the predecessor\n // adapter (yamaha-nodejs-soef) on real pre-2010 devices, and dropped in the rewrite.\n // Values verified against that library's PUT paths (audit findings F3/F4).\n // Tone/subwoofer values are real decibels on the state; the wire carries tenths with\n // Exp=1 (the same Val/Exp/Unit envelope as the volume above): *10 out, /10 on read.\n {\n state: \"sound.bass\",\n common: {\n nameKey: \"bass\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -6,\n max: 6,\n step: 0.5,\n },\n statusField: \"bass\",\n toInner: value =>\n `<Sound_Video><Tone><Bass><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Bass></Tone></Sound_Video>`,\n },\n {\n state: \"sound.treble\",\n common: {\n nameKey: \"treble\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -6,\n max: 6,\n step: 0.5,\n },\n statusField: \"treble\",\n toInner: value =>\n `<Sound_Video><Tone><Treble><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Treble></Tone></Sound_Video>`,\n },\n {\n state: \"sound.subwooferTrim\",\n common: {\n nameKey: \"subwooferTrim\",\n descKey: \"descSubwooferTrim\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n unit: \"dB\",\n min: -6,\n max: 6,\n step: 0.5,\n },\n statusField: \"subwooferTrim\",\n toInner: value =>\n `<Volume><Subwoofer_Trim><Val>${Math.round(Number(value) * 10)}</Val><Exp>1</Exp><Unit>dB</Unit></Subwoofer_Trim></Volume>`,\n },\n {\n state: \"sound.extraBass\",\n common: {\n nameKey: \"extraBass\",\n descKey: \"descExtraBass\",\n type: \"boolean\",\n role: \"switch\",\n read: true,\n write: true,\n },\n statusField: \"extraBass\",\n toInner: value => `<Sound_Video><Extra_Bass>${value ? \"Auto\" : \"Off\"}</Extra_Bass></Sound_Video>`,\n },\n {\n state: \"sound.ypaoVolume\",\n common: {\n nameKey: \"ypaoVolume\",\n descKey: \"descYpaoVolume\",\n type: \"boolean\",\n role: \"switch\",\n read: true,\n write: true,\n },\n statusField: \"ypaoVolume\",\n toInner: value => `<Sound_Video><YPAO_Volume>${value ? \"Auto\" : \"Off\"}</YPAO_Volume></Sound_Video>`,\n },\n {\n state: \"sound.dialogueLift\",\n common: {\n nameKey: \"dialogueLift\",\n descKey: \"descDialogueLift\",\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 0,\n max: 5,\n step: 1,\n },\n statusField: \"dialogueLift\",\n toInner: value =>\n `<Sound_Video><Dialogue_Adjust><Dialogue_Lift>${Number(value)}</Dialogue_Lift></Dialogue_Adjust></Sound_Video>`,\n },\n // HDMI outputs and party \u2014 the predecessor's setHDMIOutput / partyMode.\n // Main-zone-only; both are written on the System element (writeZone).\n // Scenes are NOT in this static list: the device declares its scenes itself\n // (`<Scene_Sel_Item>`: which exist, their titles, the `Scene_Sel` write value \u2014\n // per zone), so the controller builds them from that declaration. The\n // predecessor's blind `Scene_Load` is gone; nothing ever proved it worked (#615).\n {\n state: \"hdmiOut1\",\n common: { nameKey: \"hdmiOUT1\", descKey: \"descHdmiOUT1\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"hdmiOut1\",\n mainOnly: true,\n writeZone: \"System\",\n toInner: value => `<Sound_Video><HDMI><Output><OUT_1>${value ? \"On\" : \"Off\"}</OUT_1></Output></HDMI></Sound_Video>`,\n },\n {\n state: \"hdmiOut2\",\n common: { nameKey: \"hdmiOUT2\", descKey: \"descHdmiOUT2\", type: \"boolean\", role: \"switch\", read: true, write: true },\n statusField: \"hdmiOut2\",\n mainOnly: true,\n writeZone: \"System\",\n toInner: value => `<Sound_Video><HDMI><Output><OUT_2>${value ? \"On\" : \"Off\"}</OUT_2></Output></HDMI></Sound_Video>`,\n },\n {\n state: \"multiroom.party\",\n common: {\n nameKey: \"partyModeAllZones\",\n descKey: \"descPartyModeAllZones\",\n type: \"boolean\",\n role: \"switch\",\n read: true,\n write: true,\n },\n statusField: \"party\",\n mainOnly: true,\n writeZone: \"System\",\n toInner: value => `<Party_Mode><Mode>${value ? \"On\" : \"Off\"}</Mode></Party_Mode>`,\n },\n];\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -251,6 +251,7 @@ class XmlDeviceController {
251
251
  type: "state",
252
252
  common: {
253
253
  name: (0, import_i18n.tName)("recallScene"),
254
+ desc: (0, import_i18n.tName)("descRecallScene"),
254
255
  type: "number",
255
256
  role: "level",
256
257
  read: true,
@@ -300,6 +301,7 @@ class XmlDeviceController {
300
301
  };
301
302
  await state("preset", {
302
303
  name: (0, import_i18n.tName)("presetRecallByNumber"),
304
+ desc: (0, import_i18n.tName)("descPresetRecallByNumber"),
303
305
  type: "number",
304
306
  role: "level",
305
307
  read: true,
@@ -318,10 +320,25 @@ class XmlDeviceController {
318
320
  read: true,
319
321
  write: false
320
322
  });
321
- await state("rdsService", { name: (0, import_i18n.tName)("rdsStation"), type: "string", role: "text", read: true, write: false });
322
- await state("rdsText", { name: (0, import_i18n.tName)("rdsText"), type: "string", role: "text", read: true, write: false });
323
+ await state("rdsService", {
324
+ name: (0, import_i18n.tName)("rdsStation"),
325
+ desc: (0, import_i18n.tName)("descRdsStation"),
326
+ type: "string",
327
+ role: "text",
328
+ read: true,
329
+ write: false
330
+ });
331
+ await state("rdsText", {
332
+ name: (0, import_i18n.tName)("rdsText"),
333
+ desc: (0, import_i18n.tName)("descRdsText"),
334
+ type: "string",
335
+ role: "text",
336
+ read: true,
337
+ write: false
338
+ });
323
339
  await state("tuned", {
324
340
  name: (0, import_i18n.tName)("tunedToAStation"),
341
+ desc: (0, import_i18n.tName)("descTunedToAStation"),
325
342
  type: "boolean",
326
343
  role: "indicator",
327
344
  read: true,
@@ -329,6 +346,7 @@ class XmlDeviceController {
329
346
  });
330
347
  await state("stereo", {
331
348
  name: (0, import_i18n.tName)("stereoReception"),
349
+ desc: (0, import_i18n.tName)("descStereoReception"),
332
350
  type: "boolean",
333
351
  role: "indicator",
334
352
  read: true,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/xml/device-controller.ts"],
4
- "sourcesContent": ["import { CHANNEL_NAME_KEYS, type ObjectDef } from \"../catalog/types\";\nimport { tName } from \"../i18n\";\nimport {\n isPermanentXmlRefusal,\n parseInputList,\n parseReturnCode,\n parseSceneList,\n parseTunerInfo,\n type BasicStatus,\n type XmlScene,\n} from \"./protocol\";\nimport { parseXmlStatus, stateToXml, type XmlCommand } from \"./command-mapper\";\nimport { XML_AMP_CATALOG } from \"./catalog\";\nimport type { ConnectionHandle, ControllerLog } from \"../controller\";\nimport { errorMessage } from \"../util\";\nimport { PollDropDetector } from \"../lifecycle/poll-drop-detector\";\nimport type { ProbeMemory } from \"../lifecycle/probe-memory\";\nimport type { CommandGate } from \"../lifecycle/command-gate\";\nimport type { BrowseEngine } from \"../browse/browse-engine\";\nimport { createBrowseSurface } from \"../browse/surface\";\nimport { XML_BROWSE_SOURCES, XmlBrowseDriver } from \"../browse/xml-browse-driver\";\n\n/** XML/YNC has no push channel, so the state is polled at this interval by default. */\nconst DEFAULT_POLL_INTERVAL_MS = 60 * 1000;\n\ninterface XmlZone {\n /** Unified zone key (`main`, `zone2`, \u2026). */\n key: string;\n /** XML zone element (`Main_Zone`, `Zone_2`, \u2026). */\n element: string;\n /** State-id prefix for the zone. */\n prefix: string;\n}\n\nconst XML_ZONES: XmlZone[] = [\n { key: \"main\", element: \"Main_Zone\", prefix: \"\" },\n { key: \"zone2\", element: \"Zone_2\", prefix: \"multiroom.zone2.\" },\n { key: \"zone3\", element: \"Zone_3\", prefix: \"multiroom.zone3.\" },\n { key: \"zone4\", element: \"Zone_4\", prefix: \"multiroom.zone4.\" },\n];\n\n/** The subset of the XML client the controller uses (so tests can inject a fake). */\nexport interface XmlClientLike {\n /** Read a zone's Basic_Status. */\n getStatus(zone: string): Promise<BasicStatus>;\n /** Read the device's model name (System > Config). */\n getModelName(): Promise<string | undefined>;\n /** Send an inner command to a zone. */\n send(zone: string, inner: string): Promise<void>;\n /** Read an element's inner GET request and return the raw response body. */\n getXml(element: string, inner: string): Promise<string>;\n}\n\n/** The adapter callbacks the controller drives \u2014 narrow, so no adapter mock is needed in tests. */\nexport interface XmlControllerDeps {\n /** The XML client for this device. */\n client: XmlClientLike;\n /** Schedule the keepalive poll; returns a function that cancels it. */\n scheduleKeepalive(handler: () => void, ms: number): () => void;\n /** Create or update an object in the device tree. */\n upsertObject(id: string, def: ObjectDef): Promise<void>;\n /** Write a state value with ack (device-originated). */\n setStateAck(id: string, value: boolean | number | string): void;\n /** Adapter log. */\n log: ControllerLog;\n /**\n * The device's command gate: every request is paced through it, and its signal is the\n * connection's shutdown flag \u2014 a closed gate ends pending waits and stops state writes\n * from a poll that was already in flight. Absent in older tests \u2192 no browsing.\n */\n gate?: CommandGate;\n /** Per-device memory for answers that do not change while the device runs (see ProbeMemory). */\n probeMemory?: ProbeMemory;\n}\n\n/**\n * Drives one XML/YNC device: probe which zones answer, build the amp tree, seed\n * state, and route commands both ways. XML has no push, so state is refreshed by\n * a keepalive poll. Create-only.\n */\nexport class XmlDeviceController implements ConnectionHandle {\n private zones: XmlZone[] = [];\n private cancelKeepalive: (() => void) | undefined;\n private readonly dropDetector = new PollDropDetector();\n private browseEngine: BrowseEngine | undefined;\n /** The scenes each zone DECLARES (`Scene_Sel_Item`), for the recall write path. */\n private readonly scenesByZone = new Map<string, XmlScene[]>();\n /** Whether the device answers `<Tuner><Play_Info>` (the classic pre-2010 tuner). */\n private hasTuner = false;\n /** The amp state ids this controller actually created \u2014 the claim-with-proof gate for BOTH ways. */\n private readonly createdStates = new Set<string>();\n /** Per zone: the Basic_Status fields this device is known to deliver (persisted union). */\n private readonly zoneFields = new Map<string, Set<string>>();\n\n /**\n * @param deviceId the id-safe device id (object-tree path segment)\n * @param deps the client and adapter callbacks\n * @param pollIntervalMs how often to poll the device for state (default 60 s)\n */\n public constructor(\n private readonly deviceId: string,\n private readonly deps: XmlControllerDeps,\n private readonly pollIntervalMs: number = DEFAULT_POLL_INTERVAL_MS,\n ) {}\n\n /**\n * Probe each zone, create the tree for the ones that answer, seed state, and\n * start the keepalive poll.\n *\n * @returns true if the main zone answered and the tree was created\n */\n public async start(): Promise<boolean> {\n // Probe all zones in parallel and keep each answering zone's status, so a zone is\n // fetched once (for the probe) and seeded from that same response \u2014 not twice.\n const probes = await Promise.all(\n XML_ZONES.map(async zone => ({ zone, status: await this.tryGetStatus(zone.element) })),\n );\n const answered = probes.filter(probe => probe.status && Object.keys(probe.status).length > 0);\n if (!answered.some(probe => probe.zone.key === \"main\")) {\n this.deps.log.debug(`${this.deviceId}: no XML main zone \u2014 creating no objects`);\n return false;\n }\n this.zones = answered.map(probe => probe.zone);\n // Freshness guard for the (persisted) probe memory: the model name is the identity\n // this transport can read. A different device behind the address drops the\n // remembered XML declarations (scenes, inputs, tuner, browse sources); a device\n // that reports no model keeps them \u2014 the YNCA/YXC guards catch a swap there.\n let model: string | undefined;\n try {\n model = await this.deps.client.getModelName();\n if (model !== undefined && this.deps.probeMemory) {\n if (this.deps.probeMemory.remembered(\"xmlModel\") !== model) {\n // Every XML-owned memory key carries the xml prefix (xmlBrowseSources,\n // xmlScenes:*, xmlInputs:*, xmlTuner, xmlModel).\n this.deps.probeMemory.drop(key => key.startsWith(\"xml\"));\n this.deps.probeMemory.set(\"xmlModel\", model);\n }\n }\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: getModelName failed (${errorMessage(e)})`);\n }\n // The zone's own input list (`Input_Sel_Item`, per zone \u2014 Main and Zone 2 differ on\n // real hardware): the device says which inputs it accepts, so the input state gets a\n // dropdown instead of a free string. Constant per model \u2014 remembered per device.\n const inputsByZone = new Map<string, string[]>();\n for (const zone of this.zones) {\n const body = await this.probeXml(\n `xmlInputs:${zone.key}`,\n zone.element,\n \"<Input><Input_Sel_Item>GetParam</Input_Sel_Item></Input>\",\n );\n inputsByZone.set(zone.key, parseInputList(body));\n }\n // Claim with proof, XML edition (2.0.1): only the states whose Basic_Status field\n // this device DELIVERS are created \u2014 a blind full-catalog rollout left valueless\n // objects (hdmi.out2, sound.direct, \u2026) standing on devices without the feature.\n // The delivered field set is a model property, remembered per zone (union, so a\n // later standby start \u2014 which may report fewer fields \u2014 cannot shrink the tree).\n for (const { zone, status } of answered) {\n const key = `xmlStatusFields:${zone.key}`;\n const remembered = this.deps.probeMemory?.remembered<string[]>(key);\n const fields = new Set<string>(Array.isArray(remembered) ? remembered : []);\n for (const field of Object.keys(status ?? {})) {\n fields.add(field);\n }\n this.deps.probeMemory?.set(key, [...fields]);\n this.zoneFields.set(zone.key, fields);\n }\n // Every parent \u2014 the zone channels included \u2014 is created by the per-state loop below\n // and named from the shared CHANNEL_NAME_KEYS table (a zone that answered has at least\n // its power state, so its channel always comes into being this way).\n const createdChannels = new Set<string>();\n for (const zone of this.zones) {\n for (const entry of XML_AMP_CATALOG) {\n // Main/system-wide features (scenes, HDMI outputs, party) exist only on the main zone.\n if (entry.mainOnly && zone.key !== \"main\") {\n continue;\n }\n // Claim with proof: skip what this device's status never delivered.\n if (entry.statusField !== undefined && !this.zoneFields.get(zone.key)?.has(entry.statusField)) {\n continue;\n }\n const stateId = `${zone.prefix}${entry.state}`;\n // A dotted state (e.g. scene.recall) needs its parent channel created first.\n const segments = stateId.split(\".\");\n for (let i = 1; i < segments.length; i++) {\n const channelId = segments.slice(0, i).join(\".\");\n if (!createdChannels.has(channelId)) {\n createdChannels.add(channelId);\n await this.deps.upsertObject(`${this.deviceId}.${channelId}`, {\n id: channelId,\n type: \"channel\",\n common: {\n // Capitalised like the catalog path does it, so the same folder cannot end up\n // called \"sound\" here and \"Sound\" there depending on which transport owns it.\n name: CHANNEL_NAME_KEYS[segments[i - 1]]\n ? tName(CHANNEL_NAME_KEYS[segments[i - 1]])\n : segments[i - 1].charAt(0).toUpperCase() + segments[i - 1].slice(1),\n },\n });\n }\n }\n const { nameKey, descKey, ...rest } = entry.common;\n const common: ObjectDef[\"common\"] = {\n ...rest,\n name: tName(nameKey),\n // An absent key means the datapoint explains itself \u2014 the fleet standard wants the\n // field empty there rather than filled with invented prose.\n ...(descKey ? { desc: tName(descKey) } : {}),\n };\n // The device's own input list becomes the dropdown (XML-owned devices only \u2014\n // where YNCA is present its dropdown wins via the owner policy).\n const inputs = entry.state === \"input\" ? inputsByZone.get(zone.key) : undefined;\n if (inputs && inputs.length > 0) {\n common.states = Object.fromEntries(inputs.map(input => [input, input]));\n }\n await this.deps.upsertObject(`${this.deviceId}.${stateId}`, {\n id: stateId,\n type: \"state\",\n common,\n });\n this.createdStates.add(stateId);\n }\n }\n await this.setupScenes(createdChannels);\n await this.setupTuner(createdChannels);\n // Seed from the statuses already fetched during the probe \u2014 no second round-trip.\n for (const { zone, status } of answered) {\n if (status) {\n this.seedZone(zone, status);\n }\n }\n // The model name (already read by the freshness guard) for the device-manager card.\n // Best-effort \u2014 a device that does not report it still connects, the line stays empty.\n if (model) {\n // The info channel and info.model already exist \u2014 the adapter creates them for\n // every device up front, so the card renders even while the device is offline.\n this.emit(\"info.model\", model);\n }\n await this.setupBrowse();\n this.cancelKeepalive = this.deps.scheduleKeepalive(() => void this.keepalive(), this.pollIntervalMs);\n // The adapter logs one combined \"ready\" line across all transports; this stays at debug.\n this.deps.log.debug(`${this.deviceId}: Yamaha (XML) device ready (XML)`);\n return true;\n }\n\n /**\n * Read one element's list once per device: the answer is a property of the MODEL\n * (input lists, scene declarations), so reconnects reuse it via the probe memory.\n *\n * Only a DEFINITE answer is remembered: a body, or the model's own \"no such node\"\n * (bodyless HTTP 400 / return code 2, both captured on the RX-V6A) as \"declares none\".\n * A transient failure \u2014 timeout, connection error, HTTP 5xx, or a state-dependent\n * refusal (return code 3/4, \"not now\") \u2014 is NOT remembered: before this, one busy\n * moment during the first contact recorded \"no scenes\" for that device for good, until\n * the model changed. Now it is simply asked again on the next connect.\n *\n * @param key the probe-memory key\n * @param element the XML element to ask\n * @param inner the inner GET request\n * @returns the raw response body, or \"\" when the device (definitely or for now) has none\n */\n private async probeXml(key: string, element: string, inner: string): Promise<string> {\n const probe = async (): Promise<string> => {\n let body: string;\n try {\n body = await this.deps.client.getXml(element, inner);\n } catch (e) {\n if (isPermanentXmlRefusal(e)) {\n return \"\"; // the model has no such node \u2014 definite\n }\n throw e; // transient \u2014 not remembered\n }\n const rc = parseReturnCode(body);\n if (rc !== undefined && rc !== 0) {\n if (rc === 2) {\n return \"\"; // RC 2 = the node does not exist on this model \u2014 definite\n }\n throw new Error(`device refused ${element} probe (RC=${rc})`); // \"not now\" \u2014 not remembered\n }\n return body;\n };\n try {\n return this.deps.probeMemory ? await this.deps.probeMemory.once(key, probe) : await probe();\n } catch (e) {\n this.deps.log.debug(\n `${this.deviceId}: ${key} probe failed, asking again on the next connect (${errorMessage(e)})`,\n );\n return \"\";\n }\n }\n\n /**\n * Build the scene surface from the device's OWN declaration (#615): each zone's\n * `<Scene_Sel_Item>` names the scenes that exist, their titles and the write value\n * (`Scene N` via `<Scene_Sel>`). The predecessor blindly sent `Scene_Load` to a\n * fixed 1..12 main-zone state; the capture shows the device declaring `Scene_Sel`\n * instead \u2014 and declaring scenes for Zone 2 too.\n *\n * @param createdChannels the channel ids already created (extended here)\n */\n private async setupScenes(createdChannels: Set<string>): Promise<void> {\n for (const zone of this.zones) {\n const body = await this.probeXml(\n `xmlScenes:${zone.key}`,\n zone.element,\n \"<Scene><Scene_Sel_Item>GetParam</Scene_Sel_Item></Scene>\",\n );\n const scenes = parseSceneList(body);\n if (scenes.length === 0) {\n continue;\n }\n this.scenesByZone.set(zone.key, scenes);\n const channelId = `${zone.prefix}scene`;\n if (!createdChannels.has(channelId)) {\n createdChannels.add(channelId);\n await this.deps.upsertObject(`${this.deviceId}.${channelId}`, {\n id: channelId,\n type: \"channel\",\n common: { name: tName(CHANNEL_NAME_KEYS.scene ?? \"Scenes\") },\n });\n }\n const max = Math.max(...scenes.map(scene => scene.num));\n await this.deps.upsertObject(`${this.deviceId}.${channelId}.recall`, {\n id: `${channelId}.recall`,\n type: \"state\",\n common: {\n name: tName(\"recallScene\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 1,\n max,\n step: 1,\n // The declared titles as the dropdown, so the picker shows \"Movie Viewing\",\n // not a bare number.\n states: Object.fromEntries(scenes.map(scene => [scene.num, scene.title])),\n },\n });\n // ONE list state instead of a name datapoint per scene (v2.0.0): visualizations\n // read titles as VALUES (button captions \u2014 the #613 reporter's setup), and a\n // dropdown's labels are not readable, so the list carries them.\n await this.deps.upsertObject(`${this.deviceId}.${channelId}.list`, {\n id: `${channelId}.list`,\n type: \"state\",\n common: { name: tName(\"scenesNumberTitle\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n this.emit(`${channelId}.list`, JSON.stringify(scenes));\n }\n }\n\n /**\n * Build the classic tuner surface (pre-2010 devices, where XML is the ONLY\n * transport \u2014 the predecessor served their tuner, the rewrite had dropped it).\n * Existence is probed once per device; the preset write is the openHAB-verified\n * `<Play_Control><Preset><Preset_Sel>`; frequency/RDS/tuned are read-only from\n * Play_Info. On newer devices YNCA/YXC own these ids via the owner policy.\n *\n * @param createdChannels the channel ids already created (extended here)\n */\n private async setupTuner(createdChannels: Set<string>): Promise<void> {\n const probe = await this.probeXml(\"xmlTuner\", \"Tuner\", \"<Play_Info>GetParam</Play_Info>\");\n if (probe.length === 0) {\n return;\n }\n this.hasTuner = true;\n if (!createdChannels.has(\"tuner\")) {\n createdChannels.add(\"tuner\");\n await this.deps.upsertObject(`${this.deviceId}.tuner`, {\n id: \"tuner\",\n type: \"channel\",\n common: { name: tName(CHANNEL_NAME_KEYS.tuner ?? \"Tuner\") },\n });\n }\n const state = async (id: string, common: ObjectDef[\"common\"]): Promise<void> => {\n await this.deps.upsertObject(`${this.deviceId}.tuner.${id}`, { id: `tuner.${id}`, type: \"state\", common });\n };\n await state(\"preset\", {\n name: tName(\"presetRecallByNumber\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n // Slot 1 upwards \u2014 `handleTunerWrite` drops a 0, so offering it as the lower bound\n // invited a write that goes nowhere.\n min: 1,\n max: 40,\n step: 1,\n });\n await state(\"frequency\", {\n name: tName(\"frequency\"),\n type: \"number\",\n role: \"value\",\n unit: \"kHz\",\n read: true,\n write: false,\n });\n await state(\"rdsService\", { name: tName(\"rdsStation\"), type: \"string\", role: \"text\", read: true, write: false });\n await state(\"rdsText\", { name: tName(\"rdsText\"), type: \"string\", role: \"text\", read: true, write: false });\n await state(\"tuned\", {\n name: tName(\"tunedToAStation\"),\n type: \"boolean\",\n role: \"indicator\",\n read: true,\n write: false,\n });\n await state(\"stereo\", {\n name: tName(\"stereoReception\"),\n type: \"boolean\",\n role: \"indicator\",\n read: true,\n write: false,\n });\n // NOT `emitTunerInfo(probe)`: the probe body comes out of the PERSISTED memory on every\n // reconnect and restart, so seeding from it published a snapshot of an earlier session\n // \u2014 frequency, RDS station and text, \"tuned\" \u2014 as the CURRENT reading, until the first\n // poll up to a whole interval later. The existence verdict is a model property and stays\n // remembered; the values are read fresh. (Same class as the menu's resting shape, which\n // showed rows six days older than the connection until it was fixed.)\n await this.refreshTuner();\n }\n\n /**\n * Write the tuner states from a Play_Info response.\n *\n * @param xml the Play_Info response body\n */\n private emitTunerInfo(xml: string): void {\n const info = parseTunerInfo(xml);\n if (info.preset !== undefined) {\n this.emit(\"tuner.preset\", info.preset);\n }\n if (info.frequency !== undefined) {\n // Unified kHz (v2.0.0): the device reports FM in MHz, AM in kHz \u2014 normalize.\n this.emit(\"tuner.frequency\", Math.round(info.frequencyUnit === \"MHz\" ? info.frequency * 1000 : info.frequency));\n }\n if (info.rdsService !== undefined) {\n this.emit(\"tuner.rdsService\", info.rdsService);\n }\n if (info.rdsText !== undefined) {\n this.emit(\"tuner.rdsText\", info.rdsText);\n }\n if (info.tuned !== undefined) {\n this.emit(\"tuner.tuned\", info.tuned);\n }\n if (info.stereo !== undefined) {\n this.emit(\"tuner.stereo\", info.stereo);\n }\n }\n\n /**\n * Poll the tuner's Play_Info (keepalive) and write the states.\n */\n private async refreshTuner(): Promise<void> {\n try {\n this.emitTunerInfo(await this.deps.client.getXml(\"Tuner\", \"<Play_Info>GetParam</Play_Info>\"));\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: tuner Play_Info failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * A user write to `tuner.preset` \u2192 the openHAB-verified preset recall.\n *\n * @param stateId the state id relative to the device\n * @param value the written value\n * @returns true when the id was the tuner preset (handled here)\n */\n private handleTunerWrite(stateId: string, value: unknown): boolean {\n if (stateId !== \"tuner.preset\" || !this.hasTuner) {\n return stateId === \"tuner.preset\";\n }\n const num = Math.round(Number(value));\n if (!Number.isFinite(num) || num < 1) {\n return true;\n }\n void this.applyCommand({\n zone: \"Tuner\",\n inner: `<Play_Control><Preset><Preset_Sel>${num}</Preset_Sel></Preset></Play_Control>`,\n });\n return true;\n }\n\n /**\n * A user write to a zone's `scene.recall` \u2192 the DECLARED write element\n * (`<Scene><Scene_Sel>Scene N</Scene_Sel></Scene>`). Only zones that declared\n * scenes accept the write; a refusal lands in the log via applyCommand.\n *\n * @param stateId the state id relative to the device\n * @param value the written value\n * @returns true when the id was a scene recall (handled here)\n */\n private handleSceneWrite(stateId: string, value: unknown): boolean {\n const match = /^(?:multiroom\\.(zone[234])\\.)?scene\\.recall$/.exec(stateId);\n if (!match) {\n return false;\n }\n const zoneKey = match[1] ?? \"main\";\n const zone = this.zones.find(z => z.key === zoneKey);\n const scenes = this.scenesByZone.get(zoneKey);\n // A TITLE is as valid a write as a number (\"Movie Viewing\" \u2192 Scene 1).\n const byTitle =\n typeof value === \"string\" && !/^\\d+$/.test(value.trim())\n ? scenes?.find(scene => scene.title.toLowerCase() === value.trim().toLowerCase())?.num\n : undefined;\n const num = byTitle ?? Math.round(Number(value));\n if (!zone || !scenes || !scenes.some(scene => scene.num === num)) {\n return true;\n }\n void this.applyCommand({ zone: zone.element, inner: `<Scene><Scene_Sel>Scene ${num}</Scene_Sel></Scene>` });\n return true;\n }\n\n /**\n * Create the browsing surface (#613) when at least one source answers a List_Info\n * probe (NET_RADIO/SERVER/USB \u2014 the menus the predecessor adapter's users drove\n * via `Realtime.*.LINE1TXT` + `xmlCommand`). Skipped without a delay dep (older tests).\n */\n private async setupBrowse(): Promise<void> {\n const gate = this.deps.gate;\n if (!gate) {\n return;\n }\n const delay = (ms: number): Promise<void> => gate.delay(ms);\n // Which sources have a menu is a property of the MODEL, not of this connection \u2014 ask\n // once per device instead of costing three extra requests (up to five seconds on a\n // receiver that has no menus at all) on every single reconnect.\n const probe = async (): Promise<string[]> => {\n const probes = await Promise.all(\n XML_BROWSE_SOURCES.map(async source => {\n try {\n const body = await this.deps.client.getXml(source.element, \"<List_Info>GetParam</List_Info>\");\n return body.includes(\"<Menu_Status>\") ? source.key : undefined;\n } catch (e) {\n if (isPermanentXmlRefusal(e)) {\n return undefined; // this model has no menu for that source (bodyless HTTP 400)\n }\n throw e; // transient \u2014 \"no menus\" must not be remembered for good\n }\n }),\n );\n return probes.filter((key): key is string => key !== undefined);\n };\n let available: Set<string>;\n try {\n available = new Set(\n this.deps.probeMemory ? await this.deps.probeMemory.once(\"xmlBrowseSources\", probe) : await probe(),\n );\n } catch (e) {\n this.deps.log.debug(\n `${this.deviceId}: browse probe failed, asking again on the next connect (${errorMessage(e)})`,\n );\n return;\n }\n if (available.size === 0) {\n return;\n }\n const driver = new XmlBrowseDriver(this.deps.client, available, delay);\n this.browseEngine = await createBrowseSurface(driver, this.deviceId, {\n upsertObject: this.deps.upsertObject,\n emit: (id, value) => this.emit(id, value),\n log: this.deps.log,\n delay,\n });\n }\n\n /**\n * Write a device-originated value \u2014 but never after the connection was closed. A poll\n * that was already in flight when the adapter stopped would otherwise still write into\n * a tree that is being torn down.\n *\n * @param relativeId the state id relative to the device\n * @param value the value to write\n */\n private emit(relativeId: string, value: boolean | number | string): void {\n if (this.deps.gate?.closed) {\n return;\n }\n this.deps.setStateAck(`${this.deviceId}.${relativeId}`, value);\n }\n\n /**\n * Handle a state change: a user write (ack false) becomes an XML command; an\n * acked change (the device's own echo) is ignored.\n *\n * @param fullStateId the full state id (device id + \".\" + state)\n * @param ack whether the change is acked (device-originated)\n * @param value the new value\n */\n public handleStateChange(fullStateId: string, ack: boolean, value: unknown): void {\n if (ack) {\n return;\n }\n const prefix = `${this.deviceId}.`;\n if (!fullStateId.startsWith(prefix)) {\n return;\n }\n const stateId = fullStateId.slice(prefix.length);\n if (stateId.startsWith(\"player.browse.\")) {\n this.browseEngine?.handleWrite(stateId, value);\n return;\n }\n // Scenes and the classic tuner are device-declared (not in the static catalog).\n if (this.handleSceneWrite(stateId, value)) {\n return;\n }\n if (this.handleTunerWrite(stateId, value)) {\n return;\n }\n // Claim with proof, on the WRITE way too. Object creation has been proof-gated since\n // 2.0.1 (only fields this device's Basic_Status really delivers), but the write path was\n // not \u2014 the comment on `createdStates` claimed otherwise while it guarded the read side\n // alone. XML was the last transport without it: YNCA writes only through its per-device\n // write map, MusicCast only through device-declared endpoints. It bites on a datapoint an\n // adapter version before 2.0.1 created and that once carried a value, so no sweep removes\n // it: writing it put a blind command on the wire that the device answers with a refusal.\n if (!this.createdStates.has(stateId)) {\n this.deps.log.debug(`${this.deviceId}: ${stateId} was not reported by this device \u2014 write dropped`);\n return;\n }\n const command = stateToXml(stateId, value);\n if (command) {\n void this.applyCommand(command);\n }\n }\n\n /**\n * Register the supervisor's drop handler. XML has no push/socket-drop event, so a\n * drop is inferred from a run of failed polls (see keepalive).\n *\n * @param cb invoked once when the device is judged gone\n */\n public onDrop(cb: (reason?: Error) => void): void {\n this.dropDetector.onDrop(cb);\n }\n\n /** Cancel the keepalive poll. Synchronous \u2014 safe to call from onUnload. */\n public close(): void {\n this.browseEngine?.close();\n // Closing the gate empties its queue and aborts its signal: queued requests are\n // dropped and every pending wait ends, so nothing writes after the teardown.\n this.deps.gate?.close();\n this.cancelKeepalive?.();\n this.cancelKeepalive = undefined;\n }\n\n /**\n * Poll every live zone. If every zone fails for three consecutive failed polls in a\n * row, the device is judged gone and a drop is reported so the supervisor reconnects.\n */\n private async keepalive(): Promise<void> {\n // The keepalive is an async handler on an adapter timer: a rejection here is an\n // UNHANDLED rejection, and js-controller turns those into an adapter stop. Every step\n // below catches for itself today, so the guard is what makes that a guarantee instead\n // of something the next change has to remember.\n try {\n let anyOk = false;\n for (const zone of this.zones) {\n if (await this.refreshZone(zone)) {\n anyOk = true;\n }\n }\n if (this.hasTuner) {\n await this.refreshTuner();\n }\n this.dropDetector.record(anyOk);\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: keepalive poll failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * Fetch a zone's status and write its amp states with ack.\n *\n * @param zone the zone to refresh\n * @returns true if the status was fetched, false if the request failed\n */\n private async refreshZone(zone: XmlZone): Promise<boolean> {\n const status = await this.tryGetStatus(zone.element);\n if (!status) {\n return false;\n }\n this.seedZone(zone, status);\n return true;\n }\n\n /**\n * Write a zone's amp states from an already-fetched Basic_Status (used to seed\n * from the start-up probe without a second round-trip).\n *\n * @param zone the zone the status belongs to\n * @param status the parsed Basic_Status\n */\n private seedZone(zone: XmlZone, status: BasicStatus): void {\n // A field the device delivers for the FIRST time mid-run has no object yet\n // (claim-with-proof creates only proven fields at start): remember it \u2014 the next\n // start creates it \u2014 and skip the write, so no state lands without an object.\n const known = this.zoneFields.get(zone.key);\n if (known) {\n let grew = false;\n for (const field of Object.keys(status)) {\n if (!known.has(field)) {\n known.add(field);\n grew = true;\n }\n }\n if (grew) {\n this.deps.probeMemory?.set(`xmlStatusFields:${zone.key}`, [...known]);\n }\n }\n for (const update of parseXmlStatus(status, zone.key)) {\n if (this.createdStates.has(update.id)) {\n this.emit(update.id, update.value);\n }\n }\n }\n\n /**\n * Read a zone's status, swallowing errors (an absent zone or an offline device).\n *\n * @param element the XML zone element\n * @returns the parsed status, or undefined on failure\n */\n private async tryGetStatus(element: string): Promise<BasicStatus | undefined> {\n try {\n return await this.deps.client.getStatus(element);\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: getStatus(${element}) failed: ${errorMessage(e)}`);\n return undefined;\n }\n }\n\n /**\n * Send a mapped command to the device.\n *\n * @param command the XML command to apply\n */\n private async applyCommand(command: XmlCommand): Promise<void> {\n try {\n await this.deps.client.send(command.zone, command.inner);\n } catch (e) {\n this.deps.log.warn(`${this.deviceId}: XML command failed: ${errorMessage(e)}`);\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { CHANNEL_NAME_KEYS, type ObjectDef } from \"../catalog/types\";\nimport { tName } from \"../i18n\";\nimport {\n isPermanentXmlRefusal,\n parseInputList,\n parseReturnCode,\n parseSceneList,\n parseTunerInfo,\n type BasicStatus,\n type XmlScene,\n} from \"./protocol\";\nimport { parseXmlStatus, stateToXml, type XmlCommand } from \"./command-mapper\";\nimport { XML_AMP_CATALOG } from \"./catalog\";\nimport type { ConnectionHandle, ControllerLog } from \"../controller\";\nimport { errorMessage } from \"../util\";\nimport { PollDropDetector } from \"../lifecycle/poll-drop-detector\";\nimport type { ProbeMemory } from \"../lifecycle/probe-memory\";\nimport type { CommandGate } from \"../lifecycle/command-gate\";\nimport type { BrowseEngine } from \"../browse/browse-engine\";\nimport { createBrowseSurface } from \"../browse/surface\";\nimport { XML_BROWSE_SOURCES, XmlBrowseDriver } from \"../browse/xml-browse-driver\";\n\n/** XML/YNC has no push channel, so the state is polled at this interval by default. */\nconst DEFAULT_POLL_INTERVAL_MS = 60 * 1000;\n\ninterface XmlZone {\n /** Unified zone key (`main`, `zone2`, \u2026). */\n key: string;\n /** XML zone element (`Main_Zone`, `Zone_2`, \u2026). */\n element: string;\n /** State-id prefix for the zone. */\n prefix: string;\n}\n\nconst XML_ZONES: XmlZone[] = [\n { key: \"main\", element: \"Main_Zone\", prefix: \"\" },\n { key: \"zone2\", element: \"Zone_2\", prefix: \"multiroom.zone2.\" },\n { key: \"zone3\", element: \"Zone_3\", prefix: \"multiroom.zone3.\" },\n { key: \"zone4\", element: \"Zone_4\", prefix: \"multiroom.zone4.\" },\n];\n\n/** The subset of the XML client the controller uses (so tests can inject a fake). */\nexport interface XmlClientLike {\n /** Read a zone's Basic_Status. */\n getStatus(zone: string): Promise<BasicStatus>;\n /** Read the device's model name (System > Config). */\n getModelName(): Promise<string | undefined>;\n /** Send an inner command to a zone. */\n send(zone: string, inner: string): Promise<void>;\n /** Read an element's inner GET request and return the raw response body. */\n getXml(element: string, inner: string): Promise<string>;\n}\n\n/** The adapter callbacks the controller drives \u2014 narrow, so no adapter mock is needed in tests. */\nexport interface XmlControllerDeps {\n /** The XML client for this device. */\n client: XmlClientLike;\n /** Schedule the keepalive poll; returns a function that cancels it. */\n scheduleKeepalive(handler: () => void, ms: number): () => void;\n /** Create or update an object in the device tree. */\n upsertObject(id: string, def: ObjectDef): Promise<void>;\n /** Write a state value with ack (device-originated). */\n setStateAck(id: string, value: boolean | number | string): void;\n /** Adapter log. */\n log: ControllerLog;\n /**\n * The device's command gate: every request is paced through it, and its signal is the\n * connection's shutdown flag \u2014 a closed gate ends pending waits and stops state writes\n * from a poll that was already in flight. Absent in older tests \u2192 no browsing.\n */\n gate?: CommandGate;\n /** Per-device memory for answers that do not change while the device runs (see ProbeMemory). */\n probeMemory?: ProbeMemory;\n}\n\n/**\n * Drives one XML/YNC device: probe which zones answer, build the amp tree, seed\n * state, and route commands both ways. XML has no push, so state is refreshed by\n * a keepalive poll. Create-only.\n */\nexport class XmlDeviceController implements ConnectionHandle {\n private zones: XmlZone[] = [];\n private cancelKeepalive: (() => void) | undefined;\n private readonly dropDetector = new PollDropDetector();\n private browseEngine: BrowseEngine | undefined;\n /** The scenes each zone DECLARES (`Scene_Sel_Item`), for the recall write path. */\n private readonly scenesByZone = new Map<string, XmlScene[]>();\n /** Whether the device answers `<Tuner><Play_Info>` (the classic pre-2010 tuner). */\n private hasTuner = false;\n /** The amp state ids this controller actually created \u2014 the claim-with-proof gate for BOTH ways. */\n private readonly createdStates = new Set<string>();\n /** Per zone: the Basic_Status fields this device is known to deliver (persisted union). */\n private readonly zoneFields = new Map<string, Set<string>>();\n\n /**\n * @param deviceId the id-safe device id (object-tree path segment)\n * @param deps the client and adapter callbacks\n * @param pollIntervalMs how often to poll the device for state (default 60 s)\n */\n public constructor(\n private readonly deviceId: string,\n private readonly deps: XmlControllerDeps,\n private readonly pollIntervalMs: number = DEFAULT_POLL_INTERVAL_MS,\n ) {}\n\n /**\n * Probe each zone, create the tree for the ones that answer, seed state, and\n * start the keepalive poll.\n *\n * @returns true if the main zone answered and the tree was created\n */\n public async start(): Promise<boolean> {\n // Probe all zones in parallel and keep each answering zone's status, so a zone is\n // fetched once (for the probe) and seeded from that same response \u2014 not twice.\n const probes = await Promise.all(\n XML_ZONES.map(async zone => ({ zone, status: await this.tryGetStatus(zone.element) })),\n );\n const answered = probes.filter(probe => probe.status && Object.keys(probe.status).length > 0);\n if (!answered.some(probe => probe.zone.key === \"main\")) {\n this.deps.log.debug(`${this.deviceId}: no XML main zone \u2014 creating no objects`);\n return false;\n }\n this.zones = answered.map(probe => probe.zone);\n // Freshness guard for the (persisted) probe memory: the model name is the identity\n // this transport can read. A different device behind the address drops the\n // remembered XML declarations (scenes, inputs, tuner, browse sources); a device\n // that reports no model keeps them \u2014 the YNCA/YXC guards catch a swap there.\n let model: string | undefined;\n try {\n model = await this.deps.client.getModelName();\n if (model !== undefined && this.deps.probeMemory) {\n if (this.deps.probeMemory.remembered(\"xmlModel\") !== model) {\n // Every XML-owned memory key carries the xml prefix (xmlBrowseSources,\n // xmlScenes:*, xmlInputs:*, xmlTuner, xmlModel).\n this.deps.probeMemory.drop(key => key.startsWith(\"xml\"));\n this.deps.probeMemory.set(\"xmlModel\", model);\n }\n }\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: getModelName failed (${errorMessage(e)})`);\n }\n // The zone's own input list (`Input_Sel_Item`, per zone \u2014 Main and Zone 2 differ on\n // real hardware): the device says which inputs it accepts, so the input state gets a\n // dropdown instead of a free string. Constant per model \u2014 remembered per device.\n const inputsByZone = new Map<string, string[]>();\n for (const zone of this.zones) {\n const body = await this.probeXml(\n `xmlInputs:${zone.key}`,\n zone.element,\n \"<Input><Input_Sel_Item>GetParam</Input_Sel_Item></Input>\",\n );\n inputsByZone.set(zone.key, parseInputList(body));\n }\n // Claim with proof, XML edition (2.0.1): only the states whose Basic_Status field\n // this device DELIVERS are created \u2014 a blind full-catalog rollout left valueless\n // objects (hdmi.out2, sound.direct, \u2026) standing on devices without the feature.\n // The delivered field set is a model property, remembered per zone (union, so a\n // later standby start \u2014 which may report fewer fields \u2014 cannot shrink the tree).\n for (const { zone, status } of answered) {\n const key = `xmlStatusFields:${zone.key}`;\n const remembered = this.deps.probeMemory?.remembered<string[]>(key);\n const fields = new Set<string>(Array.isArray(remembered) ? remembered : []);\n for (const field of Object.keys(status ?? {})) {\n fields.add(field);\n }\n this.deps.probeMemory?.set(key, [...fields]);\n this.zoneFields.set(zone.key, fields);\n }\n // Every parent \u2014 the zone channels included \u2014 is created by the per-state loop below\n // and named from the shared CHANNEL_NAME_KEYS table (a zone that answered has at least\n // its power state, so its channel always comes into being this way).\n const createdChannels = new Set<string>();\n for (const zone of this.zones) {\n for (const entry of XML_AMP_CATALOG) {\n // Main/system-wide features (scenes, HDMI outputs, party) exist only on the main zone.\n if (entry.mainOnly && zone.key !== \"main\") {\n continue;\n }\n // Claim with proof: skip what this device's status never delivered.\n if (entry.statusField !== undefined && !this.zoneFields.get(zone.key)?.has(entry.statusField)) {\n continue;\n }\n const stateId = `${zone.prefix}${entry.state}`;\n // A dotted state (e.g. scene.recall) needs its parent channel created first.\n const segments = stateId.split(\".\");\n for (let i = 1; i < segments.length; i++) {\n const channelId = segments.slice(0, i).join(\".\");\n if (!createdChannels.has(channelId)) {\n createdChannels.add(channelId);\n await this.deps.upsertObject(`${this.deviceId}.${channelId}`, {\n id: channelId,\n type: \"channel\",\n common: {\n // Capitalised like the catalog path does it, so the same folder cannot end up\n // called \"sound\" here and \"Sound\" there depending on which transport owns it.\n name: CHANNEL_NAME_KEYS[segments[i - 1]]\n ? tName(CHANNEL_NAME_KEYS[segments[i - 1]])\n : segments[i - 1].charAt(0).toUpperCase() + segments[i - 1].slice(1),\n },\n });\n }\n }\n const { nameKey, descKey, ...rest } = entry.common;\n const common: ObjectDef[\"common\"] = {\n ...rest,\n name: tName(nameKey),\n // An absent key means the datapoint explains itself \u2014 the fleet standard wants the\n // field empty there rather than filled with invented prose.\n ...(descKey ? { desc: tName(descKey) } : {}),\n };\n // The device's own input list becomes the dropdown (XML-owned devices only \u2014\n // where YNCA is present its dropdown wins via the owner policy).\n const inputs = entry.state === \"input\" ? inputsByZone.get(zone.key) : undefined;\n if (inputs && inputs.length > 0) {\n common.states = Object.fromEntries(inputs.map(input => [input, input]));\n }\n await this.deps.upsertObject(`${this.deviceId}.${stateId}`, {\n id: stateId,\n type: \"state\",\n common,\n });\n this.createdStates.add(stateId);\n }\n }\n await this.setupScenes(createdChannels);\n await this.setupTuner(createdChannels);\n // Seed from the statuses already fetched during the probe \u2014 no second round-trip.\n for (const { zone, status } of answered) {\n if (status) {\n this.seedZone(zone, status);\n }\n }\n // The model name (already read by the freshness guard) for the device-manager card.\n // Best-effort \u2014 a device that does not report it still connects, the line stays empty.\n if (model) {\n // The info channel and info.model already exist \u2014 the adapter creates them for\n // every device up front, so the card renders even while the device is offline.\n this.emit(\"info.model\", model);\n }\n await this.setupBrowse();\n this.cancelKeepalive = this.deps.scheduleKeepalive(() => void this.keepalive(), this.pollIntervalMs);\n // The adapter logs one combined \"ready\" line across all transports; this stays at debug.\n this.deps.log.debug(`${this.deviceId}: Yamaha (XML) device ready (XML)`);\n return true;\n }\n\n /**\n * Read one element's list once per device: the answer is a property of the MODEL\n * (input lists, scene declarations), so reconnects reuse it via the probe memory.\n *\n * Only a DEFINITE answer is remembered: a body, or the model's own \"no such node\"\n * (bodyless HTTP 400 / return code 2, both captured on the RX-V6A) as \"declares none\".\n * A transient failure \u2014 timeout, connection error, HTTP 5xx, or a state-dependent\n * refusal (return code 3/4, \"not now\") \u2014 is NOT remembered: before this, one busy\n * moment during the first contact recorded \"no scenes\" for that device for good, until\n * the model changed. Now it is simply asked again on the next connect.\n *\n * @param key the probe-memory key\n * @param element the XML element to ask\n * @param inner the inner GET request\n * @returns the raw response body, or \"\" when the device (definitely or for now) has none\n */\n private async probeXml(key: string, element: string, inner: string): Promise<string> {\n const probe = async (): Promise<string> => {\n let body: string;\n try {\n body = await this.deps.client.getXml(element, inner);\n } catch (e) {\n if (isPermanentXmlRefusal(e)) {\n return \"\"; // the model has no such node \u2014 definite\n }\n throw e; // transient \u2014 not remembered\n }\n const rc = parseReturnCode(body);\n if (rc !== undefined && rc !== 0) {\n if (rc === 2) {\n return \"\"; // RC 2 = the node does not exist on this model \u2014 definite\n }\n throw new Error(`device refused ${element} probe (RC=${rc})`); // \"not now\" \u2014 not remembered\n }\n return body;\n };\n try {\n return this.deps.probeMemory ? await this.deps.probeMemory.once(key, probe) : await probe();\n } catch (e) {\n this.deps.log.debug(\n `${this.deviceId}: ${key} probe failed, asking again on the next connect (${errorMessage(e)})`,\n );\n return \"\";\n }\n }\n\n /**\n * Build the scene surface from the device's OWN declaration (#615): each zone's\n * `<Scene_Sel_Item>` names the scenes that exist, their titles and the write value\n * (`Scene N` via `<Scene_Sel>`). The predecessor blindly sent `Scene_Load` to a\n * fixed 1..12 main-zone state; the capture shows the device declaring `Scene_Sel`\n * instead \u2014 and declaring scenes for Zone 2 too.\n *\n * @param createdChannels the channel ids already created (extended here)\n */\n private async setupScenes(createdChannels: Set<string>): Promise<void> {\n for (const zone of this.zones) {\n const body = await this.probeXml(\n `xmlScenes:${zone.key}`,\n zone.element,\n \"<Scene><Scene_Sel_Item>GetParam</Scene_Sel_Item></Scene>\",\n );\n const scenes = parseSceneList(body);\n if (scenes.length === 0) {\n continue;\n }\n this.scenesByZone.set(zone.key, scenes);\n const channelId = `${zone.prefix}scene`;\n if (!createdChannels.has(channelId)) {\n createdChannels.add(channelId);\n await this.deps.upsertObject(`${this.deviceId}.${channelId}`, {\n id: channelId,\n type: \"channel\",\n common: { name: tName(CHANNEL_NAME_KEYS.scene ?? \"Scenes\") },\n });\n }\n const max = Math.max(...scenes.map(scene => scene.num));\n await this.deps.upsertObject(`${this.deviceId}.${channelId}.recall`, {\n id: `${channelId}.recall`,\n type: \"state\",\n common: {\n name: tName(\"recallScene\"),\n desc: tName(\"descRecallScene\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 1,\n max,\n step: 1,\n // The declared titles as the dropdown, so the picker shows \"Movie Viewing\",\n // not a bare number.\n states: Object.fromEntries(scenes.map(scene => [scene.num, scene.title])),\n },\n });\n // ONE list state instead of a name datapoint per scene (v2.0.0): visualizations\n // read titles as VALUES (button captions \u2014 the #613 reporter's setup), and a\n // dropdown's labels are not readable, so the list carries them.\n await this.deps.upsertObject(`${this.deviceId}.${channelId}.list`, {\n id: `${channelId}.list`,\n type: \"state\",\n common: { name: tName(\"scenesNumberTitle\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n this.emit(`${channelId}.list`, JSON.stringify(scenes));\n }\n }\n\n /**\n * Build the classic tuner surface (pre-2010 devices, where XML is the ONLY\n * transport \u2014 the predecessor served their tuner, the rewrite had dropped it).\n * Existence is probed once per device; the preset write is the openHAB-verified\n * `<Play_Control><Preset><Preset_Sel>`; frequency/RDS/tuned are read-only from\n * Play_Info. On newer devices YNCA/YXC own these ids via the owner policy.\n *\n * @param createdChannels the channel ids already created (extended here)\n */\n private async setupTuner(createdChannels: Set<string>): Promise<void> {\n const probe = await this.probeXml(\"xmlTuner\", \"Tuner\", \"<Play_Info>GetParam</Play_Info>\");\n if (probe.length === 0) {\n return;\n }\n this.hasTuner = true;\n if (!createdChannels.has(\"tuner\")) {\n createdChannels.add(\"tuner\");\n await this.deps.upsertObject(`${this.deviceId}.tuner`, {\n id: \"tuner\",\n type: \"channel\",\n common: { name: tName(CHANNEL_NAME_KEYS.tuner ?? \"Tuner\") },\n });\n }\n const state = async (id: string, common: ObjectDef[\"common\"]): Promise<void> => {\n await this.deps.upsertObject(`${this.deviceId}.tuner.${id}`, { id: `tuner.${id}`, type: \"state\", common });\n };\n await state(\"preset\", {\n name: tName(\"presetRecallByNumber\"),\n desc: tName(\"descPresetRecallByNumber\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n // Slot 1 upwards \u2014 `handleTunerWrite` drops a 0, so offering it as the lower bound\n // invited a write that goes nowhere.\n min: 1,\n max: 40,\n step: 1,\n });\n await state(\"frequency\", {\n name: tName(\"frequency\"),\n type: \"number\",\n role: \"value\",\n unit: \"kHz\",\n read: true,\n write: false,\n });\n await state(\"rdsService\", {\n name: tName(\"rdsStation\"),\n desc: tName(\"descRdsStation\"),\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n });\n await state(\"rdsText\", {\n name: tName(\"rdsText\"),\n desc: tName(\"descRdsText\"),\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n });\n await state(\"tuned\", {\n name: tName(\"tunedToAStation\"),\n desc: tName(\"descTunedToAStation\"),\n type: \"boolean\",\n role: \"indicator\",\n read: true,\n write: false,\n });\n await state(\"stereo\", {\n name: tName(\"stereoReception\"),\n desc: tName(\"descStereoReception\"),\n type: \"boolean\",\n role: \"indicator\",\n read: true,\n write: false,\n });\n // NOT `emitTunerInfo(probe)`: the probe body comes out of the PERSISTED memory on every\n // reconnect and restart, so seeding from it published a snapshot of an earlier session\n // \u2014 frequency, RDS station and text, \"tuned\" \u2014 as the CURRENT reading, until the first\n // poll up to a whole interval later. The existence verdict is a model property and stays\n // remembered; the values are read fresh. (Same class as the menu's resting shape, which\n // showed rows six days older than the connection until it was fixed.)\n await this.refreshTuner();\n }\n\n /**\n * Write the tuner states from a Play_Info response.\n *\n * @param xml the Play_Info response body\n */\n private emitTunerInfo(xml: string): void {\n const info = parseTunerInfo(xml);\n if (info.preset !== undefined) {\n this.emit(\"tuner.preset\", info.preset);\n }\n if (info.frequency !== undefined) {\n // Unified kHz (v2.0.0): the device reports FM in MHz, AM in kHz \u2014 normalize.\n this.emit(\"tuner.frequency\", Math.round(info.frequencyUnit === \"MHz\" ? info.frequency * 1000 : info.frequency));\n }\n if (info.rdsService !== undefined) {\n this.emit(\"tuner.rdsService\", info.rdsService);\n }\n if (info.rdsText !== undefined) {\n this.emit(\"tuner.rdsText\", info.rdsText);\n }\n if (info.tuned !== undefined) {\n this.emit(\"tuner.tuned\", info.tuned);\n }\n if (info.stereo !== undefined) {\n this.emit(\"tuner.stereo\", info.stereo);\n }\n }\n\n /**\n * Poll the tuner's Play_Info (keepalive) and write the states.\n */\n private async refreshTuner(): Promise<void> {\n try {\n this.emitTunerInfo(await this.deps.client.getXml(\"Tuner\", \"<Play_Info>GetParam</Play_Info>\"));\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: tuner Play_Info failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * A user write to `tuner.preset` \u2192 the openHAB-verified preset recall.\n *\n * @param stateId the state id relative to the device\n * @param value the written value\n * @returns true when the id was the tuner preset (handled here)\n */\n private handleTunerWrite(stateId: string, value: unknown): boolean {\n if (stateId !== \"tuner.preset\" || !this.hasTuner) {\n return stateId === \"tuner.preset\";\n }\n const num = Math.round(Number(value));\n if (!Number.isFinite(num) || num < 1) {\n return true;\n }\n void this.applyCommand({\n zone: \"Tuner\",\n inner: `<Play_Control><Preset><Preset_Sel>${num}</Preset_Sel></Preset></Play_Control>`,\n });\n return true;\n }\n\n /**\n * A user write to a zone's `scene.recall` \u2192 the DECLARED write element\n * (`<Scene><Scene_Sel>Scene N</Scene_Sel></Scene>`). Only zones that declared\n * scenes accept the write; a refusal lands in the log via applyCommand.\n *\n * @param stateId the state id relative to the device\n * @param value the written value\n * @returns true when the id was a scene recall (handled here)\n */\n private handleSceneWrite(stateId: string, value: unknown): boolean {\n const match = /^(?:multiroom\\.(zone[234])\\.)?scene\\.recall$/.exec(stateId);\n if (!match) {\n return false;\n }\n const zoneKey = match[1] ?? \"main\";\n const zone = this.zones.find(z => z.key === zoneKey);\n const scenes = this.scenesByZone.get(zoneKey);\n // A TITLE is as valid a write as a number (\"Movie Viewing\" \u2192 Scene 1).\n const byTitle =\n typeof value === \"string\" && !/^\\d+$/.test(value.trim())\n ? scenes?.find(scene => scene.title.toLowerCase() === value.trim().toLowerCase())?.num\n : undefined;\n const num = byTitle ?? Math.round(Number(value));\n if (!zone || !scenes || !scenes.some(scene => scene.num === num)) {\n return true;\n }\n void this.applyCommand({ zone: zone.element, inner: `<Scene><Scene_Sel>Scene ${num}</Scene_Sel></Scene>` });\n return true;\n }\n\n /**\n * Create the browsing surface (#613) when at least one source answers a List_Info\n * probe (NET_RADIO/SERVER/USB \u2014 the menus the predecessor adapter's users drove\n * via `Realtime.*.LINE1TXT` + `xmlCommand`). Skipped without a delay dep (older tests).\n */\n private async setupBrowse(): Promise<void> {\n const gate = this.deps.gate;\n if (!gate) {\n return;\n }\n const delay = (ms: number): Promise<void> => gate.delay(ms);\n // Which sources have a menu is a property of the MODEL, not of this connection \u2014 ask\n // once per device instead of costing three extra requests (up to five seconds on a\n // receiver that has no menus at all) on every single reconnect.\n const probe = async (): Promise<string[]> => {\n const probes = await Promise.all(\n XML_BROWSE_SOURCES.map(async source => {\n try {\n const body = await this.deps.client.getXml(source.element, \"<List_Info>GetParam</List_Info>\");\n return body.includes(\"<Menu_Status>\") ? source.key : undefined;\n } catch (e) {\n if (isPermanentXmlRefusal(e)) {\n return undefined; // this model has no menu for that source (bodyless HTTP 400)\n }\n throw e; // transient \u2014 \"no menus\" must not be remembered for good\n }\n }),\n );\n return probes.filter((key): key is string => key !== undefined);\n };\n let available: Set<string>;\n try {\n available = new Set(\n this.deps.probeMemory ? await this.deps.probeMemory.once(\"xmlBrowseSources\", probe) : await probe(),\n );\n } catch (e) {\n this.deps.log.debug(\n `${this.deviceId}: browse probe failed, asking again on the next connect (${errorMessage(e)})`,\n );\n return;\n }\n if (available.size === 0) {\n return;\n }\n const driver = new XmlBrowseDriver(this.deps.client, available, delay);\n this.browseEngine = await createBrowseSurface(driver, this.deviceId, {\n upsertObject: this.deps.upsertObject,\n emit: (id, value) => this.emit(id, value),\n log: this.deps.log,\n delay,\n });\n }\n\n /**\n * Write a device-originated value \u2014 but never after the connection was closed. A poll\n * that was already in flight when the adapter stopped would otherwise still write into\n * a tree that is being torn down.\n *\n * @param relativeId the state id relative to the device\n * @param value the value to write\n */\n private emit(relativeId: string, value: boolean | number | string): void {\n if (this.deps.gate?.closed) {\n return;\n }\n this.deps.setStateAck(`${this.deviceId}.${relativeId}`, value);\n }\n\n /**\n * Handle a state change: a user write (ack false) becomes an XML command; an\n * acked change (the device's own echo) is ignored.\n *\n * @param fullStateId the full state id (device id + \".\" + state)\n * @param ack whether the change is acked (device-originated)\n * @param value the new value\n */\n public handleStateChange(fullStateId: string, ack: boolean, value: unknown): void {\n if (ack) {\n return;\n }\n const prefix = `${this.deviceId}.`;\n if (!fullStateId.startsWith(prefix)) {\n return;\n }\n const stateId = fullStateId.slice(prefix.length);\n if (stateId.startsWith(\"player.browse.\")) {\n this.browseEngine?.handleWrite(stateId, value);\n return;\n }\n // Scenes and the classic tuner are device-declared (not in the static catalog).\n if (this.handleSceneWrite(stateId, value)) {\n return;\n }\n if (this.handleTunerWrite(stateId, value)) {\n return;\n }\n // Claim with proof, on the WRITE way too. Object creation has been proof-gated since\n // 2.0.1 (only fields this device's Basic_Status really delivers), but the write path was\n // not \u2014 the comment on `createdStates` claimed otherwise while it guarded the read side\n // alone. XML was the last transport without it: YNCA writes only through its per-device\n // write map, MusicCast only through device-declared endpoints. It bites on a datapoint an\n // adapter version before 2.0.1 created and that once carried a value, so no sweep removes\n // it: writing it put a blind command on the wire that the device answers with a refusal.\n if (!this.createdStates.has(stateId)) {\n this.deps.log.debug(`${this.deviceId}: ${stateId} was not reported by this device \u2014 write dropped`);\n return;\n }\n const command = stateToXml(stateId, value);\n if (command) {\n void this.applyCommand(command);\n }\n }\n\n /**\n * Register the supervisor's drop handler. XML has no push/socket-drop event, so a\n * drop is inferred from a run of failed polls (see keepalive).\n *\n * @param cb invoked once when the device is judged gone\n */\n public onDrop(cb: (reason?: Error) => void): void {\n this.dropDetector.onDrop(cb);\n }\n\n /** Cancel the keepalive poll. Synchronous \u2014 safe to call from onUnload. */\n public close(): void {\n this.browseEngine?.close();\n // Closing the gate empties its queue and aborts its signal: queued requests are\n // dropped and every pending wait ends, so nothing writes after the teardown.\n this.deps.gate?.close();\n this.cancelKeepalive?.();\n this.cancelKeepalive = undefined;\n }\n\n /**\n * Poll every live zone. If every zone fails for three consecutive failed polls in a\n * row, the device is judged gone and a drop is reported so the supervisor reconnects.\n */\n private async keepalive(): Promise<void> {\n // The keepalive is an async handler on an adapter timer: a rejection here is an\n // UNHANDLED rejection, and js-controller turns those into an adapter stop. Every step\n // below catches for itself today, so the guard is what makes that a guarantee instead\n // of something the next change has to remember.\n try {\n let anyOk = false;\n for (const zone of this.zones) {\n if (await this.refreshZone(zone)) {\n anyOk = true;\n }\n }\n if (this.hasTuner) {\n await this.refreshTuner();\n }\n this.dropDetector.record(anyOk);\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: keepalive poll failed: ${errorMessage(e)}`);\n }\n }\n\n /**\n * Fetch a zone's status and write its amp states with ack.\n *\n * @param zone the zone to refresh\n * @returns true if the status was fetched, false if the request failed\n */\n private async refreshZone(zone: XmlZone): Promise<boolean> {\n const status = await this.tryGetStatus(zone.element);\n if (!status) {\n return false;\n }\n this.seedZone(zone, status);\n return true;\n }\n\n /**\n * Write a zone's amp states from an already-fetched Basic_Status (used to seed\n * from the start-up probe without a second round-trip).\n *\n * @param zone the zone the status belongs to\n * @param status the parsed Basic_Status\n */\n private seedZone(zone: XmlZone, status: BasicStatus): void {\n // A field the device delivers for the FIRST time mid-run has no object yet\n // (claim-with-proof creates only proven fields at start): remember it \u2014 the next\n // start creates it \u2014 and skip the write, so no state lands without an object.\n const known = this.zoneFields.get(zone.key);\n if (known) {\n let grew = false;\n for (const field of Object.keys(status)) {\n if (!known.has(field)) {\n known.add(field);\n grew = true;\n }\n }\n if (grew) {\n this.deps.probeMemory?.set(`xmlStatusFields:${zone.key}`, [...known]);\n }\n }\n for (const update of parseXmlStatus(status, zone.key)) {\n if (this.createdStates.has(update.id)) {\n this.emit(update.id, update.value);\n }\n }\n }\n\n /**\n * Read a zone's status, swallowing errors (an absent zone or an offline device).\n *\n * @param element the XML zone element\n * @returns the parsed status, or undefined on failure\n */\n private async tryGetStatus(element: string): Promise<BasicStatus | undefined> {\n try {\n return await this.deps.client.getStatus(element);\n } catch (e) {\n this.deps.log.debug(`${this.deviceId}: getStatus(${element}) failed: ${errorMessage(e)}`);\n return undefined;\n }\n }\n\n /**\n * Send a mapped command to the device.\n *\n * @param command the XML command to apply\n */\n private async applyCommand(command: XmlCommand): Promise<void> {\n try {\n await this.deps.client.send(command.zone, command.inner);\n } catch (e) {\n this.deps.log.warn(`${this.deviceId}: XML command failed: ${errorMessage(e)}`);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }