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/yxc/command-mapper.ts"],
4
- "sourcesContent": ["import type { StateValue } from \"../types\";\nimport { YXC_ZONE_IDS, zonePrefix } from \"./zones\";\nimport type { I18nKey } from \"../i18n\";\nimport { isWritableValue } from \"../catalog/value-coerce\";\nimport { formatPlayTime } from \"../catalog/play-time\";\nimport { YXC_AMP_CATALOG } from \"./catalog\";\nimport type { YxcClientLike } from \"./client-contract\";\n\n/**\n * A mapped YXC write. Almost every command is a ready-to-run client call (`run`) built\n * from the catalog's `write.apply` or the transport/toggle tables \u2014 no method-name\n * string, no dispatch switch, no \"unknown command\" runtime path. The two commands that\n * need controller-cached state stay declarative: the equalizer (the device sets all\n * three bands in one call, the other two come from the cached status) and the tuner\n * frequency (setFreq needs the current band).\n */\nexport type YxcCommand =\n | { kind: \"run\"; run: (client: YxcClientLike) => Promise<unknown> }\n | { kind: \"equalizer\"; zone: string; band: \"low\" | \"mid\" | \"high\"; value: number }\n | { kind: \"tunerFreq\"; value: number }\n | { kind: \"tunerPreset\"; value: number }\n | { kind: \"tunerBand\"; band: string }\n | { kind: \"netusbPreset\"; value: number }\n | { kind: \"netusbRecent\"; value: number }\n | { kind: \"playerTransport\"; zone: string; action: PlayerTransport };\n\n/**\n * Read a catalog entry's raw getStatus value \u2014 a flat field or a nested path.\n *\n * @param status the getStatus response object\n * @param read the entry's read location\n * @returns the raw value, or undefined if the field is absent\n */\nfunction readStatusField(status: Record<string, unknown>, read: { field: string } | { path: string[] }): unknown {\n if (\"path\" in read) {\n let value: unknown = status;\n for (const key of read.path) {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n value = (value as Record<string, unknown>)[key];\n }\n return value;\n }\n return status[read.field];\n}\n\n/**\n * Button states \u2192 their client call. The CD transport routes through the one\n * `setCDPlayback(action)` method (not the per-action `pauseCD()` helpers, one of\n * which sends the wrong command in the library).\n */\nconst BUTTON_ACTIONS: Record<string, (client: YxcClientLike) => Promise<unknown>> = {\n \"player.cd.tray\": client => client.toggleTray(),\n};\n\n/** The unified player block's transport buttons (v2.0.0) \u2014 routed by the controller to the zone's playing source. */\nconst PLAYER_TRANSPORTS = [\"play\", \"pause\", \"stop\", \"next\", \"prev\", \"repeatToggle\", \"shuffleToggle\"] as const;\n\n/** A transport action of the unified player block. */\nexport type PlayerTransport = (typeof PLAYER_TRANSPORTS)[number];\n\n/** Equalizer band state (without zone prefix) \u2192 the band of the declarative equalizer command. */\nconst EQ_CHANNELS: Record<string, \"low\" | \"mid\" | \"high\"> = {\n \"sound.equalizer.low\": \"low\",\n \"sound.equalizer.mid\": \"mid\",\n \"sound.equalizer.high\": \"high\",\n};\n\n/**\n * Parse a YXC getStatus response into unified amp state updates for a zone. Only\n * fields the response actually carries are emitted (presence-checked, so a\n * `mute: false` is kept), each prefixed for its zone. States and their conversions\n * come from {@link YXC_AMP_CATALOG}.\n *\n * @param zoneStatus the getStatus response object\n * @param zone the zone the status belongs to (`main`, `zone2`, \u2026)\n * @returns the state updates, empty if malformed or no amp fields are present\n */\nexport function parseYxcStatus(zoneStatus: unknown, zone: string): StateValue[] {\n if (typeof zoneStatus !== \"object\" || zoneStatus === null) {\n return [];\n }\n const prefix = YXC_ZONE_IDS.includes(zone as (typeof YXC_ZONE_IDS)[number]) ? zonePrefix(zone) : undefined;\n if (prefix === undefined) {\n return [];\n }\n const status = zoneStatus as Record<string, unknown>;\n const updates: StateValue[] = [];\n for (const entry of YXC_AMP_CATALOG) {\n // Device-global entries (id under multiroom.) are emitted once, from the main status \u2014\n // a zone status carrying the same field must not produce a zone-prefixed copy.\n if (zone !== \"main\" && entry.state.startsWith(\"multiroom.\")) {\n continue;\n }\n const raw = readStatusField(status, entry.read);\n if (raw !== undefined) {\n updates.push({ id: `${prefix}${entry.state}`, value: entry.fromStatus(raw) });\n }\n }\n return updates;\n}\n\n/**\n * Map a unified state write to a YXC amplifier command.\n *\n * @param stateId the state id (e.g. `power`, `zone2.volume`)\n * @param value the value written to the state\n * @returns the YXC command, or undefined if the state or its zone is not mapped\n */\nexport function stateToYxc(stateId: string, value: unknown): YxcCommand | undefined {\n const button = BUTTON_ACTIONS[stateId];\n if (button) {\n return { kind: \"run\", run: button };\n }\n if (stateId === \"tuner.band\" && isWritableValue(value, false)) {\n // Its own kind, not a plain run: the controller has to remember the band, because a\n // frequency or preset write right afterwards needs it. Reading it back from the poll\n // is too late \u2014 a script that switches band and sets a frequency in one go would send\n // the frequency to the OLD band.\n return { kind: \"tunerBand\", band: String(value) };\n }\n if (stateId === \"tuner.frequency\" && isWritableValue(value, true)) {\n // The controller supplies the current band; the value carries only the frequency.\n return { kind: \"tunerFreq\", value: Number(value) };\n }\n if (stateId === \"player.netPlayer.preset\" && isWritableValue(value, true)) {\n const preset = Number(value);\n // Declarative, because the ZONE is not knowable here: recalling a favourite also\n // routes it to a zone, and the right one is whichever zone is listening to the network\n // player. Only the controller tracks that.\n return { kind: \"netusbPreset\", value: preset };\n }\n if (stateId === \"player.netPlayer.recallRecent\" && isWritableValue(value, true)) {\n const num = Number(value);\n return { kind: \"netusbRecent\", value: num };\n }\n if (stateId === \"tuner.preset\" && isWritableValue(value, true)) {\n // The controller supplies the band (or `common` on shared-list devices).\n return { kind: \"tunerPreset\", value: Number(value) };\n }\n if (stateId === \"tuner.presetUp\") {\n return { kind: \"run\", run: client => client.switchTunerPreset(\"next\") };\n }\n if (stateId === \"tuner.presetDown\") {\n return { kind: \"run\", run: client => client.switchTunerPreset(\"previous\") };\n }\n let zone = \"main\";\n let name = stateId;\n const zoneMatch = /^multiroom\\.(zone[234])\\.(.+)$/.exec(stateId);\n if (zoneMatch) {\n zone = zoneMatch[1];\n name = zoneMatch[2];\n }\n // Scene recall (#615) and the on-screen remote \u2014 zone-scoped, device-verified endpoints.\n if (name === \"scene.recall\" && isWritableValue(value, true)) {\n const num = Math.round(Number(value));\n const sceneZone = zone;\n return { kind: \"run\", run: client => client.recallScene(num, sceneZone) };\n }\n if (name === \"remote.cursor\" && isWritableValue(value, false)) {\n const cursorZone = zone;\n return { kind: \"run\", run: client => client.controlCursor(String(value), cursorZone) };\n }\n if (name === \"remote.menu\" && isWritableValue(value, false)) {\n const menuZone = zone;\n return { kind: \"run\", run: client => client.controlMenu(String(value), menuZone) };\n }\n // The unified player block's transport buttons (v2.0.0): declarative, because only\n // the controller knows which source (netusb or cd) the zone is playing.\n if (name.startsWith(\"player.\")) {\n const action = name.slice(\"player.\".length);\n if ((PLAYER_TRANSPORTS as readonly string[]).includes(action)) {\n return { kind: \"playerTransport\", zone, action: action as PlayerTransport };\n }\n }\n const eqBand = EQ_CHANNELS[name];\n if (eqBand && isWritableValue(value, true)) {\n // The controller supplies the other two bands; the value carries only this band.\n return { kind: \"equalizer\", zone, band: eqBand, value: Number(value) };\n }\n const entry = YXC_AMP_CATALOG.find(e => e.state === name);\n if (!entry?.write || !isWritableValue(value, entry.common.type === \"number\")) {\n return undefined;\n }\n const { apply } = entry.write;\n return { kind: \"run\", run: client => apply(client, value, zone) };\n}\n\n/**\n * Parse a getDistributionInfo response into the read-only multiroom (dist) states.\n *\n * @param info the getDistributionInfo response object\n * @returns the dist state updates, or an empty list if malformed\n */\nexport function parseYxcDistribution(info: unknown): StateValue[] {\n if (typeof info !== \"object\" || info === null) {\n return [];\n }\n const d = info as Record<string, unknown>;\n const updates: StateValue[] = [];\n if (typeof d.role === \"string\") {\n updates.push({ id: \"multiroom.group.role\", value: d.role });\n }\n if (typeof d.group_id === \"string\") {\n updates.push({ id: \"multiroom.group.id\", value: d.group_id });\n }\n if (typeof d.group_name === \"string\") {\n updates.push({ id: \"multiroom.group.name\", value: d.group_name });\n }\n if (typeof d.server_zone === \"string\") {\n updates.push({ id: \"multiroom.group.serverZone\", value: d.server_zone });\n }\n if (Array.isArray(d.client_list)) {\n updates.push({ id: \"multiroom.group.linkedDevices\", value: JSON.stringify(d.client_list) });\n }\n return updates;\n}\n\n/**\n * Parse a YXC getPlayInfo response into a player's read-only state updates\n * (playback status plus artist/album/track metadata). The same response shape is\n * used by every player source; the updates target the unified flat `player.*` block\n * (v2.0.0) \u2014 the CONTROLLER routes them to the zones listening to the source and\n * keeps the drive-own `player.cd.*` extras device-global.\n *\n * @param playInfo the getPlayInfo response object\n * @param block the source the info came from (`netusb` or `cd`)\n * @returns the player state updates, empty if malformed\n */\nexport function parseYxcPlayInfo(playInfo: unknown, block: \"netusb\" | \"cd\" = \"netusb\"): StateValue[] {\n if (typeof playInfo !== \"object\" || playInfo === null) {\n return [];\n }\n const info = playInfo as Record<string, unknown>;\n const updates: StateValue[] = [];\n // String metadata whose field name doubles as the state id (playback is coded separately).\n for (const field of [\"artist\", \"album\", \"track\"]) {\n const value = info[field];\n if (typeof value === \"string\") {\n updates.push({ id: `player.${field}`, value });\n }\n }\n // Repeat/shuffle carry the same typed form as the YNCA sources: repeat as the\n // media.mode.repeat code (wire off/one/all \u2014 captures-verified), shuffle as a boolean\n // (wire off/on). An unknown wire value is skipped, never coerced to a wrong state.\n const repeatCode: Record<string, number> = { off: 0, one: 1, all: 2 };\n if (typeof info.repeat === \"string\" && info.repeat in repeatCode) {\n updates.push({ id: \"player.repeat\", value: repeatCode[info.repeat] });\n }\n if (info.shuffle === \"on\" || info.shuffle === \"off\") {\n updates.push({ id: \"player.shuffle\", value: info.shuffle === \"on\" });\n }\n // Playback status \u2192 media.state code (the same 0/1/2 numbers as the YNCA player).\n const playbackCode: Record<string, number> = { play: 0, stop: 1, pause: 2 };\n if (typeof info.playback === \"string\" && info.playback in playbackCode) {\n updates.push({ id: \"player.playback\", value: playbackCode[info.playback] });\n }\n // Album art URL and the elapsed/total play time (renamed from the YXC field names).\n const albumArt = info.albumart_url;\n if (typeof albumArt === \"string\") {\n updates.push({ id: \"player.albumArt\", value: albumArt });\n }\n // Both forms of each time \u2014 see catalog/play-time.ts. MusicCast reports the seconds, so\n // the readable text is formatted from them here; the YNCA side parses its text into the\n // same seconds. One meaning per datapoint, on every device.\n const elapsed = info.play_time;\n if (typeof elapsed === \"number\") {\n updates.push({ id: \"player.elapsedTime\", value: elapsed });\n updates.push({ id: \"player.elapsedTimeText\", value: formatPlayTime(elapsed) });\n }\n const total = info.total_time;\n if (typeof total === \"number\") {\n updates.push({ id: \"player.totalTime\", value: total });\n updates.push({ id: \"player.totalTimeText\", value: formatPlayTime(total) });\n }\n // The playing source: netusb reports its active input (\"spotify\", \"net_radio\", \u2026);\n // the CD block IS its source.\n if (block === \"cd\") {\n updates.push({ id: \"player.source\", value: \"cd\" });\n } else if (typeof info.input === \"string\") {\n updates.push({ id: \"player.source\", value: info.input });\n }\n // CD drive-own extras (presence-checked, netusb responses carry none of these fields).\n if (typeof info.track_number === \"number\") {\n updates.push({ id: \"player.cd.trackNumber\", value: info.track_number });\n }\n if (typeof info.total_tracks === \"number\") {\n updates.push({ id: \"player.cd.totalTracks\", value: info.total_tracks });\n }\n if (typeof info.disc_time === \"number\") {\n updates.push({ id: \"player.cd.discTime\", value: info.disc_time });\n }\n if (typeof info.device_status === \"string\") {\n updates.push({ id: \"player.cd.deviceStatus\", value: info.device_status });\n }\n return updates;\n}\n\n/**\n * The values a zone's player block is reset to when the zone leaves its playing\n * source (v2.0.0 clear-on-switch): metadata empty, times zero, playback Stop.\n */\nexport const PLAYER_CLEAR: StateValue[] = [\n { id: \"player.source\", value: \"\" },\n { id: \"player.playback\", value: 1 },\n { id: \"player.artist\", value: \"\" },\n { id: \"player.album\", value: \"\" },\n { id: \"player.track\", value: \"\" },\n { id: \"player.albumArt\", value: \"\" },\n { id: \"player.elapsedTime\", value: 0 },\n { id: \"player.elapsedTimeText\", value: \"\" },\n { id: \"player.totalTime\", value: 0 },\n { id: \"player.totalTimeText\", value: \"\" },\n { id: \"player.repeat\", value: 0 },\n { id: \"player.shuffle\", value: false },\n];\n\n/**\n * The DAB block's fields \u2192 their unified state ids (aligned with the YNCA DAB ids so\n * both transports feed one node), with the object name each state is created under.\n * Single source for the object mapper (creation) and the parser below (read-back).\n */\nexport const DAB_FIELDS: Array<{\n field: string;\n id: string;\n type: \"string\" | \"number\" | \"boolean\";\n /** Object name as its translation KEY \u2014 this table is a module-level constant. */\n nameKey: I18nKey;\n /** Explanation key \u2014 absent means self-explanatory. */\n descKey?: I18nKey;\n}> = [\n { field: \"service_label\", id: \"tuner.dab.serviceLabel\", type: \"string\", nameKey: \"serviceLabel\" },\n { field: \"ensemble_label\", id: \"tuner.dab.ensembleLabel\", type: \"string\", nameKey: \"ensembleLabel\" },\n { field: \"ch_label\", id: \"tuner.dab.channelLabel\", type: \"string\", nameKey: \"channelLabel\" },\n { field: \"dls\", id: \"tuner.dab.dls\", type: \"string\", nameKey: \"dlsText\" },\n { field: \"program_type\", id: \"tuner.dab.programType\", type: \"string\", nameKey: \"programmeType\" },\n // preset and audio_mode are NOT listed here: the active-band parse feeds the\n // unified flat tuner.preset / tuner.audioMode states (v2.0.0).\n { field: \"status\", id: \"tuner.dab.status\", type: \"string\", nameKey: \"dabStatus\" },\n { field: \"bit_rate\", id: \"tuner.dab.bitRate\", type: \"number\", nameKey: \"bitRate\" },\n { field: \"quality\", id: \"tuner.dab.quality\", type: \"number\", nameKey: \"signalQuality\" },\n { field: \"off_air\", id: \"tuner.dab.offAir\", type: \"boolean\", nameKey: \"offAir\" },\n { field: \"dab_plus\", id: \"tuner.dab.dabPlus\", type: \"boolean\", nameKey: \"dabPlus\" },\n { field: \"category\", id: \"tuner.dab.category\", type: \"string\", nameKey: \"serviceCategory\" },\n { field: \"total_station_num\", id: \"tuner.dab.totalStations\", type: \"number\", nameKey: \"totalStations\" },\n { field: \"initial_scan_progress\", id: \"tuner.dab.scanProgress\", type: \"number\", nameKey: \"initialScanProgress\" },\n { field: \"tune_aid\", id: \"tuner.dab.tuneAid\", type: \"number\", nameKey: \"tuneAidLevel\" },\n];\n\n/**\n * Parse a YXC `/tuner/getPlayInfo` response into the tuner's read-only states: the\n * current band with its frequency/preset/tuned/audio mode, the RDS block, and the\n * DAB details (on the YNCA-shared `tuner.dab.*` ids). Absent fields are skipped.\n *\n * @param tunerInfo the getPlayInfo(\"tuner\") response object\n * @returns the tuner state updates, empty if malformed\n */\nexport function parseYxcTunerInfo(tunerInfo: unknown): StateValue[] {\n if (typeof tunerInfo !== \"object\" || tunerInfo === null) {\n return [];\n }\n const info = tunerInfo as Record<string, unknown>;\n const updates: StateValue[] = [];\n const band = info.band;\n if (typeof band === \"string\") {\n updates.push({ id: \"tuner.band\", value: band });\n const bandInfo = info[band];\n if (typeof bandInfo === \"object\" && bandInfo !== null) {\n const current = bandInfo as Record<string, unknown>;\n if (typeof current.freq === \"number\") {\n updates.push({ id: \"tuner.frequency\", value: current.freq });\n }\n // The active band's preset slot, tuned flag and audio mode \u2014 consolidated onto\n // the flat tuner states (the band state says which band they describe).\n if (typeof current.preset === \"number\") {\n updates.push({ id: \"tuner.preset\", value: current.preset });\n }\n if (typeof current.tuned === \"boolean\") {\n updates.push({ id: \"tuner.tuned\", value: current.tuned });\n }\n if (typeof current.audio_mode === \"string\") {\n updates.push({ id: \"tuner.audioMode\", value: current.audio_mode });\n }\n }\n }\n const rds = info.rds;\n if (typeof rds === \"object\" && rds !== null) {\n const r = rds as Record<string, unknown>;\n if (typeof r.radio_text_a === \"string\") {\n updates.push({ id: \"tuner.rdsText\", value: r.radio_text_a });\n }\n if (typeof r.radio_text_b === \"string\") {\n updates.push({ id: \"tuner.rdsTextB\", value: r.radio_text_b });\n }\n if (typeof r.program_service === \"string\") {\n updates.push({ id: \"tuner.rdsService\", value: r.program_service });\n }\n if (typeof r.program_type === \"string\") {\n updates.push({ id: \"tuner.rdsProgramType\", value: r.program_type });\n }\n }\n // The DAB block is reported alongside the band blocks (capture-verified) and feeds\n // the same tuner.dab.* ids as the YNCA DAB subunit, so both transports share one node.\n const dab = info.dab;\n if (typeof dab === \"object\" && dab !== null) {\n const d = dab as Record<string, unknown>;\n for (const { field, id, type } of DAB_FIELDS) {\n const value = d[field];\n if (typeof value === type) {\n updates.push({ id, value: value as string | number | boolean });\n }\n }\n }\n return updates;\n}\n\n/**\n * Parse a `/netusb/getPresetInfo` response into the favourites JSON state: the stored\n * slots with their names, empty slots (input `unknown` or no text) skipped.\n *\n * @param info the getPresetInfo response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcPresetList(info: unknown): StateValue | undefined {\n const list = (info as { preset_info?: unknown } | null)?.preset_info;\n if (!Array.isArray(list)) {\n return undefined;\n }\n const slots: Array<{ num: number; input: string; name: string }> = [];\n list.forEach((entry, index) => {\n if (typeof entry !== \"object\" || entry === null) {\n return;\n }\n const { input, text } = entry as { input?: unknown; text?: unknown };\n if (typeof input === \"string\" && input !== \"unknown\" && typeof text === \"string\" && text.length > 0) {\n slots.push({ num: index + 1, input, name: text });\n }\n });\n return { id: \"player.netPlayer.presets\", value: JSON.stringify(slots) };\n}\n\n/**\n * Parse a `/netusb/getRecentInfo` response into the recently-played JSON state.\n *\n * @param info the getRecentInfo response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcRecentList(info: unknown): StateValue | undefined {\n const list = (info as { recent_info?: unknown } | null)?.recent_info;\n if (!Array.isArray(list)) {\n return undefined;\n }\n const items: Array<{ num: number; input: string; name: string; albumArt?: string; playCount?: number }> = [];\n list.forEach((entry, index) => {\n if (typeof entry !== \"object\" || entry === null) {\n return;\n }\n const e = entry as { input?: unknown; text?: unknown; albumart_url?: unknown; play_count?: unknown };\n if (typeof e.input !== \"string\" || typeof e.text !== \"string\" || e.text.length === 0) {\n return;\n }\n const item: { num: number; input: string; name: string; albumArt?: string; playCount?: number } = {\n num: index + 1,\n input: e.input,\n name: e.text,\n };\n if (typeof e.albumart_url === \"string\" && e.albumart_url.length > 0) {\n item.albumArt = e.albumart_url;\n }\n if (typeof e.play_count === \"number\") {\n item.playCount = e.play_count;\n }\n items.push(item);\n });\n return { id: \"player.netPlayer.recent\", value: JSON.stringify(items) };\n}\n\n/**\n * Parse `/tuner/getPresetInfo` responses (one per fetched band) into the tuner\n * presets JSON state: an object keyed by band, each STORED slot kept in its raw response\n * form plus its 1-based slot number (the shape varies per band and firmware).\n *\n * The device answers with its full slot count whether or not anything is stored \u2014 on a\n * receiver with no tuner presets that is 40 FM plus 40 DAB entries of\n * `{band:\"unknown\", number:0, text:\"\"}`, a JSON datapoint nobody can use. Empty slots are\n * dropped the same way the favourites list drops them; a slot is only empty when it says so\n * on every count, so an unfamiliar firmware shape is kept rather than silently swallowed.\n *\n * @param byBand each fetched band's getPresetInfo response object\n * @returns the state update, or undefined when no band delivered a list\n */\nexport function parseYxcTunerPresetLists(byBand: Record<string, unknown>): StateValue | undefined {\n const result: Record<string, unknown[]> = {};\n for (const [band, info] of Object.entries(byBand)) {\n const list = (info as { preset_info?: unknown } | null)?.preset_info;\n if (!Array.isArray(list)) {\n continue;\n }\n const slots: unknown[] = [];\n list.forEach((entry, index) => {\n if (typeof entry === \"object\" && entry !== null && !isEmptyTunerPreset(entry as Record<string, unknown>)) {\n slots.push({ num: index + 1, ...(entry as Record<string, unknown>) });\n }\n });\n result[band] = slots;\n }\n if (Object.keys(result).length === 0) {\n return undefined;\n }\n return { id: \"tuner.presets\", value: JSON.stringify(result) };\n}\n\n/**\n * Whether a tuner preset slot holds nothing. The device marks an unused slot with the band\n * `unknown`, frequency/service number 0 and empty text \u2014 a slot counts as empty only when\n * none of the three carries anything, so a firmware that fills only some of them is kept.\n *\n * @param slot one raw preset_info entry\n * @returns true when the slot is unused\n */\nfunction isEmptyTunerPreset(slot: Record<string, unknown>): boolean {\n const band = slot.band;\n const hasBand = typeof band === \"string\" && band.length > 0 && band !== \"unknown\";\n const hasNumber = typeof slot.number === \"number\" && slot.number !== 0;\n const hasText = typeof slot.text === \"string\" && slot.text.trim().length > 0;\n return !hasBand && !hasNumber && !hasText;\n}\n\n/**\n * Parse a `/<zone>/getSignalInfo` response into the read-only audio-signal states\n * (capture-verified shape: `audio` with format/fs/bit/bitrate).\n *\n * @param info the getSignalInfo response object\n * @param zone the zone the info belongs to\n * @returns the signal state updates, empty if malformed\n */\nexport function parseYxcSignalInfo(info: unknown, zone: string): StateValue[] {\n const audio = (info as { audio?: unknown } | null)?.audio;\n if (typeof audio !== \"object\" || audio === null) {\n return [];\n }\n const prefix = YXC_ZONE_IDS.includes(zone as (typeof YXC_ZONE_IDS)[number]) ? zonePrefix(zone) : undefined;\n if (prefix === undefined) {\n return [];\n }\n const a = audio as Record<string, unknown>;\n const updates: StateValue[] = [];\n // With no signal on the input the device fills these with its own dash placeholder (\"---\").\n // Passing that through makes a text datapoint that reads like content; empty says \"nothing\n // here\" in the same way the player block's resting seeds do.\n const text = (value: unknown): string => {\n const trimmed = typeof value === \"string\" ? value.trim() : \"\";\n return /^-+$/.test(trimmed) ? \"\" : trimmed;\n };\n if (typeof a.format === \"string\") {\n updates.push({ id: `${prefix}sound.signal.format`, value: text(a.format) });\n }\n if (typeof a.fs === \"string\") {\n updates.push({ id: `${prefix}sound.signal.sampling`, value: text(a.fs) });\n }\n if (typeof a.bit === \"string\") {\n updates.push({ id: `${prefix}sound.signal.bits`, value: text(a.bit) });\n }\n if (typeof a.bitrate === \"number\") {\n updates.push({ id: `${prefix}sound.signal.bitrate`, value: a.bitrate });\n }\n return updates;\n}\n\n/**\n * Parse a `/netusb/getMcPlaylistName` response into the playlists JSON state\n * (capture-verified shape: `name_list` of strings).\n *\n * @param info the getMcPlaylistName response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcPlaylistNames(info: unknown): StateValue | undefined {\n const names = (info as { name_list?: unknown } | null)?.name_list;\n if (!Array.isArray(names)) {\n return undefined;\n }\n const list = names\n .map((name, index) => ({ num: index + 1, name }))\n .filter((entry): entry is { num: number; name: string } => typeof entry.name === \"string\");\n return { id: \"player.netPlayer.playlists\", value: JSON.stringify(list) };\n}\n\n/**\n * Parse a `/netusb/getPlayQueue` response into the play-queue JSON state\n * (capture-verified shape: `track_info` plus `playing_index`).\n *\n * @param info the getPlayQueue response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcPlayQueue(info: unknown): StateValue | undefined {\n if (typeof info !== \"object\" || info === null) {\n return undefined;\n }\n const q = info as { track_info?: unknown; playing_index?: unknown; max_line?: unknown };\n if (!Array.isArray(q.track_info)) {\n return undefined;\n }\n const value = {\n playingIndex: typeof q.playing_index === \"number\" ? q.playing_index : -1,\n totalTracks: typeof q.max_line === \"number\" ? q.max_line : q.track_info.length,\n tracks: q.track_info,\n };\n return { id: \"player.netPlayer.queue\", value: JSON.stringify(value) };\n}\n\n/**\n * Format a YXC alarm time (\"0800\") as a readable \"08:00\"; other shapes pass through.\n *\n * @param time the raw time value\n * @returns the formatted time\n */\nfunction formatAlarmTime(time: string): string {\n return /^\\d{4}$/.test(time) ? `${time.slice(0, 2)}:${time.slice(2)}` : time;\n}\n\n/**\n * Parse one alarm-detail block (oneday and each weekly day share the shape).\n *\n * @param prefix the state-id prefix the fields land under (e.g. `clock.alarm.oneday`)\n * @param detail the raw detail block\n * @returns the state updates for that block\n */\nfunction parseAlarmDetail(prefix: string, detail: Record<string, unknown>): StateValue[] {\n const updates: StateValue[] = [];\n if (typeof detail.enable === \"boolean\") {\n updates.push({ id: `${prefix}.enable`, value: detail.enable });\n }\n if (typeof detail.time === \"string\") {\n updates.push({ id: `${prefix}.time`, value: formatAlarmTime(detail.time) });\n }\n if (typeof detail.beep === \"boolean\") {\n updates.push({ id: `${prefix}.beep`, value: detail.beep });\n }\n if (typeof detail.playback_type === \"string\") {\n updates.push({ id: `${prefix}.playbackType`, value: detail.playback_type });\n }\n const resume = detail.resume;\n if (typeof resume === \"object\" && resume !== null && typeof (resume as { input?: unknown }).input === \"string\") {\n updates.push({ id: `${prefix}.resumeInput`, value: (resume as { input: string }).input });\n }\n const preset = detail.preset;\n if (typeof preset === \"object\" && preset !== null) {\n const p = preset as Record<string, unknown>;\n if (typeof p.type === \"string\") {\n updates.push({ id: `${prefix}.presetType`, value: p.type });\n }\n if (typeof p.num === \"number\") {\n updates.push({ id: `${prefix}.presetNumber`, value: p.num });\n }\n if (typeof p.netusb_input === \"string\") {\n updates.push({ id: `${prefix}.presetInput`, value: p.netusb_input });\n }\n }\n return updates;\n}\n\n/** The weekly alarm day keys, as the YXC clock block names them. */\nexport const ALARM_DAYS = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n\n/**\n * Parse a `/clock/getSettings` response into the read-only clock/alarm states\n * (capture-verified shape: auto_sync/format plus the nested alarm block).\n *\n * @param settings the getSettings response object\n * @returns the clock state updates, empty if malformed\n */\nexport function parseYxcClock(settings: unknown): StateValue[] {\n if (typeof settings !== \"object\" || settings === null) {\n return [];\n }\n const s = settings as Record<string, unknown>;\n const updates: StateValue[] = [];\n if (typeof s.auto_sync === \"boolean\") {\n updates.push({ id: \"clock.autoSync\", value: s.auto_sync });\n }\n if (typeof s.format === \"string\") {\n updates.push({ id: \"clock.format\", value: s.format });\n }\n const alarm = s.alarm;\n if (typeof alarm === \"object\" && alarm !== null) {\n const a = alarm as Record<string, unknown>;\n if (typeof a.alarm_on === \"boolean\") {\n updates.push({ id: \"clock.alarm.on\", value: a.alarm_on });\n }\n if (typeof a.volume === \"number\") {\n updates.push({ id: \"clock.alarm.volume\", value: a.volume });\n }\n if (typeof a.fade_interval === \"number\") {\n updates.push({ id: \"clock.alarm.fadeInterval\", value: a.fade_interval });\n }\n if (typeof a.fade_type === \"number\") {\n updates.push({ id: \"clock.alarm.fadeType\", value: a.fade_type });\n }\n if (typeof a.mode === \"string\") {\n updates.push({ id: \"clock.alarm.mode\", value: a.mode });\n }\n if (typeof a.repeat === \"boolean\") {\n updates.push({ id: \"clock.alarm.repeat\", value: a.repeat });\n }\n const oneday = a.oneday;\n if (typeof oneday === \"object\" && oneday !== null) {\n updates.push(...parseAlarmDetail(\"clock.alarm.oneday\", oneday as Record<string, unknown>));\n }\n for (const day of ALARM_DAYS) {\n const detail = a[day];\n if (typeof detail === \"object\" && detail !== null) {\n updates.push(...parseAlarmDetail(`clock.alarm.${day}`, detail as Record<string, unknown>));\n }\n }\n }\n return updates;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { StateValue } from \"../types\";\nimport { YXC_ZONE_IDS, zonePrefix } from \"./zones\";\nimport type { I18nKey } from \"../i18n\";\nimport { isWritableValue } from \"../catalog/value-coerce\";\nimport { formatPlayTime } from \"../catalog/play-time\";\nimport { YXC_AMP_CATALOG } from \"./catalog\";\nimport type { YxcClientLike } from \"./client-contract\";\n\n/**\n * A mapped YXC write. Almost every command is a ready-to-run client call (`run`) built\n * from the catalog's `write.apply` or the transport/toggle tables \u2014 no method-name\n * string, no dispatch switch, no \"unknown command\" runtime path. The two commands that\n * need controller-cached state stay declarative: the equalizer (the device sets all\n * three bands in one call, the other two come from the cached status) and the tuner\n * frequency (setFreq needs the current band).\n */\nexport type YxcCommand =\n | { kind: \"run\"; run: (client: YxcClientLike) => Promise<unknown> }\n | { kind: \"equalizer\"; zone: string; band: \"low\" | \"mid\" | \"high\"; value: number }\n | { kind: \"tunerFreq\"; value: number }\n | { kind: \"tunerPreset\"; value: number }\n | { kind: \"tunerBand\"; band: string }\n | { kind: \"netusbPreset\"; value: number }\n | { kind: \"netusbRecent\"; value: number }\n | { kind: \"playerTransport\"; zone: string; action: PlayerTransport };\n\n/**\n * Read a catalog entry's raw getStatus value \u2014 a flat field or a nested path.\n *\n * @param status the getStatus response object\n * @param read the entry's read location\n * @returns the raw value, or undefined if the field is absent\n */\nfunction readStatusField(status: Record<string, unknown>, read: { field: string } | { path: string[] }): unknown {\n if (\"path\" in read) {\n let value: unknown = status;\n for (const key of read.path) {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n value = (value as Record<string, unknown>)[key];\n }\n return value;\n }\n return status[read.field];\n}\n\n/**\n * Button states \u2192 their client call. The CD transport routes through the one\n * `setCDPlayback(action)` method (not the per-action `pauseCD()` helpers, one of\n * which sends the wrong command in the library).\n */\nconst BUTTON_ACTIONS: Record<string, (client: YxcClientLike) => Promise<unknown>> = {\n \"player.cd.tray\": client => client.toggleTray(),\n};\n\n/** The unified player block's transport buttons (v2.0.0) \u2014 routed by the controller to the zone's playing source. */\nconst PLAYER_TRANSPORTS = [\"play\", \"pause\", \"stop\", \"next\", \"prev\", \"repeatToggle\", \"shuffleToggle\"] as const;\n\n/** A transport action of the unified player block. */\nexport type PlayerTransport = (typeof PLAYER_TRANSPORTS)[number];\n\n/** Equalizer band state (without zone prefix) \u2192 the band of the declarative equalizer command. */\nconst EQ_CHANNELS: Record<string, \"low\" | \"mid\" | \"high\"> = {\n \"sound.equalizer.low\": \"low\",\n \"sound.equalizer.mid\": \"mid\",\n \"sound.equalizer.high\": \"high\",\n};\n\n/**\n * Parse a YXC getStatus response into unified amp state updates for a zone. Only\n * fields the response actually carries are emitted (presence-checked, so a\n * `mute: false` is kept), each prefixed for its zone. States and their conversions\n * come from {@link YXC_AMP_CATALOG}.\n *\n * @param zoneStatus the getStatus response object\n * @param zone the zone the status belongs to (`main`, `zone2`, \u2026)\n * @returns the state updates, empty if malformed or no amp fields are present\n */\nexport function parseYxcStatus(zoneStatus: unknown, zone: string): StateValue[] {\n if (typeof zoneStatus !== \"object\" || zoneStatus === null) {\n return [];\n }\n const prefix = YXC_ZONE_IDS.includes(zone as (typeof YXC_ZONE_IDS)[number]) ? zonePrefix(zone) : undefined;\n if (prefix === undefined) {\n return [];\n }\n const status = zoneStatus as Record<string, unknown>;\n const updates: StateValue[] = [];\n for (const entry of YXC_AMP_CATALOG) {\n // Device-global entries (id under multiroom.) are emitted once, from the main status \u2014\n // a zone status carrying the same field must not produce a zone-prefixed copy.\n if (zone !== \"main\" && entry.state.startsWith(\"multiroom.\")) {\n continue;\n }\n const raw = readStatusField(status, entry.read);\n if (raw !== undefined) {\n updates.push({ id: `${prefix}${entry.state}`, value: entry.fromStatus(raw) });\n }\n }\n return updates;\n}\n\n/**\n * Map a unified state write to a YXC amplifier command.\n *\n * @param stateId the state id (e.g. `power`, `zone2.volume`)\n * @param value the value written to the state\n * @returns the YXC command, or undefined if the state or its zone is not mapped\n */\nexport function stateToYxc(stateId: string, value: unknown): YxcCommand | undefined {\n const button = BUTTON_ACTIONS[stateId];\n if (button) {\n return { kind: \"run\", run: button };\n }\n if (stateId === \"tuner.band\" && isWritableValue(value, false)) {\n // Its own kind, not a plain run: the controller has to remember the band, because a\n // frequency or preset write right afterwards needs it. Reading it back from the poll\n // is too late \u2014 a script that switches band and sets a frequency in one go would send\n // the frequency to the OLD band.\n return { kind: \"tunerBand\", band: String(value) };\n }\n if (stateId === \"tuner.frequency\" && isWritableValue(value, true)) {\n // The controller supplies the current band; the value carries only the frequency.\n return { kind: \"tunerFreq\", value: Number(value) };\n }\n if (stateId === \"player.netPlayer.preset\" && isWritableValue(value, true)) {\n const preset = Number(value);\n // Declarative, because the ZONE is not knowable here: recalling a favourite also\n // routes it to a zone, and the right one is whichever zone is listening to the network\n // player. Only the controller tracks that.\n return { kind: \"netusbPreset\", value: preset };\n }\n if (stateId === \"player.netPlayer.recallRecent\" && isWritableValue(value, true)) {\n const num = Number(value);\n return { kind: \"netusbRecent\", value: num };\n }\n if (stateId === \"tuner.preset\" && isWritableValue(value, true)) {\n // The controller supplies the band (or `common` on shared-list devices).\n return { kind: \"tunerPreset\", value: Number(value) };\n }\n if (stateId === \"tuner.presetUp\") {\n return { kind: \"run\", run: client => client.switchTunerPreset(\"next\") };\n }\n if (stateId === \"tuner.presetDown\") {\n return { kind: \"run\", run: client => client.switchTunerPreset(\"previous\") };\n }\n let zone = \"main\";\n let name = stateId;\n const zoneMatch = /^multiroom\\.(zone[234])\\.(.+)$/.exec(stateId);\n if (zoneMatch) {\n zone = zoneMatch[1];\n name = zoneMatch[2];\n }\n // Scene recall (#615) and the on-screen remote \u2014 zone-scoped, device-verified endpoints.\n if (name === \"scene.recall\" && isWritableValue(value, true)) {\n const num = Math.round(Number(value));\n const sceneZone = zone;\n return { kind: \"run\", run: client => client.recallScene(num, sceneZone) };\n }\n if (name === \"remote.cursor\" && isWritableValue(value, false)) {\n const cursorZone = zone;\n return { kind: \"run\", run: client => client.controlCursor(String(value), cursorZone) };\n }\n if (name === \"remote.menu\" && isWritableValue(value, false)) {\n const menuZone = zone;\n return { kind: \"run\", run: client => client.controlMenu(String(value), menuZone) };\n }\n // The unified player block's transport buttons (v2.0.0): declarative, because only\n // the controller knows which source (netusb or cd) the zone is playing.\n if (name.startsWith(\"player.\")) {\n const action = name.slice(\"player.\".length);\n if ((PLAYER_TRANSPORTS as readonly string[]).includes(action)) {\n return { kind: \"playerTransport\", zone, action: action as PlayerTransport };\n }\n }\n const eqBand = EQ_CHANNELS[name];\n if (eqBand && isWritableValue(value, true)) {\n // The controller supplies the other two bands; the value carries only this band.\n return { kind: \"equalizer\", zone, band: eqBand, value: Number(value) };\n }\n const entry = YXC_AMP_CATALOG.find(e => e.state === name);\n if (!entry?.write || !isWritableValue(value, entry.common.type === \"number\")) {\n return undefined;\n }\n const { apply } = entry.write;\n return { kind: \"run\", run: client => apply(client, value, zone) };\n}\n\n/**\n * Parse a getDistributionInfo response into the read-only multiroom (dist) states.\n *\n * @param info the getDistributionInfo response object\n * @returns the dist state updates, or an empty list if malformed\n */\nexport function parseYxcDistribution(info: unknown): StateValue[] {\n if (typeof info !== \"object\" || info === null) {\n return [];\n }\n const d = info as Record<string, unknown>;\n const updates: StateValue[] = [];\n if (typeof d.role === \"string\") {\n updates.push({ id: \"multiroom.group.role\", value: d.role });\n }\n if (typeof d.group_id === \"string\") {\n updates.push({ id: \"multiroom.group.id\", value: d.group_id });\n }\n if (typeof d.group_name === \"string\") {\n updates.push({ id: \"multiroom.group.name\", value: d.group_name });\n }\n if (typeof d.server_zone === \"string\") {\n updates.push({ id: \"multiroom.group.serverZone\", value: d.server_zone });\n }\n if (Array.isArray(d.client_list)) {\n updates.push({ id: \"multiroom.group.linkedDevices\", value: JSON.stringify(d.client_list) });\n }\n return updates;\n}\n\n/**\n * Parse a YXC getPlayInfo response into a player's read-only state updates\n * (playback status plus artist/album/track metadata). The same response shape is\n * used by every player source; the updates target the unified flat `player.*` block\n * (v2.0.0) \u2014 the CONTROLLER routes them to the zones listening to the source and\n * keeps the drive-own `player.cd.*` extras device-global.\n *\n * @param playInfo the getPlayInfo response object\n * @param block the source the info came from (`netusb` or `cd`)\n * @returns the player state updates, empty if malformed\n */\nexport function parseYxcPlayInfo(playInfo: unknown, block: \"netusb\" | \"cd\" = \"netusb\"): StateValue[] {\n if (typeof playInfo !== \"object\" || playInfo === null) {\n return [];\n }\n const info = playInfo as Record<string, unknown>;\n const updates: StateValue[] = [];\n // String metadata whose field name doubles as the state id (playback is coded separately).\n for (const field of [\"artist\", \"album\", \"track\"]) {\n const value = info[field];\n if (typeof value === \"string\") {\n updates.push({ id: `player.${field}`, value });\n }\n }\n // Repeat/shuffle carry the same typed form as the YNCA sources: repeat as the\n // media.mode.repeat code (wire off/one/all \u2014 captures-verified), shuffle as a boolean\n // (wire off/on). An unknown wire value is skipped, never coerced to a wrong state.\n const repeatCode: Record<string, number> = { off: 0, one: 1, all: 2 };\n if (typeof info.repeat === \"string\" && info.repeat in repeatCode) {\n updates.push({ id: \"player.repeat\", value: repeatCode[info.repeat] });\n }\n if (info.shuffle === \"on\" || info.shuffle === \"off\") {\n updates.push({ id: \"player.shuffle\", value: info.shuffle === \"on\" });\n }\n // Playback status \u2192 media.state code (the same 0/1/2 numbers as the YNCA player).\n const playbackCode: Record<string, number> = { play: 0, stop: 1, pause: 2 };\n if (typeof info.playback === \"string\" && info.playback in playbackCode) {\n updates.push({ id: \"player.playback\", value: playbackCode[info.playback] });\n }\n // Album art URL and the elapsed/total play time (renamed from the YXC field names).\n const albumArt = info.albumart_url;\n if (typeof albumArt === \"string\") {\n updates.push({ id: \"player.albumArt\", value: albumArt });\n }\n // Both forms of each time \u2014 see catalog/play-time.ts. MusicCast reports the seconds, so\n // the readable text is formatted from them here; the YNCA side parses its text into the\n // same seconds. One meaning per datapoint, on every device.\n const elapsed = info.play_time;\n if (typeof elapsed === \"number\") {\n updates.push({ id: \"player.elapsedTime\", value: elapsed });\n updates.push({ id: \"player.elapsedTimeText\", value: formatPlayTime(elapsed) });\n }\n const total = info.total_time;\n if (typeof total === \"number\") {\n updates.push({ id: \"player.totalTime\", value: total });\n updates.push({ id: \"player.totalTimeText\", value: formatPlayTime(total) });\n }\n // The playing source: netusb reports its active input (\"spotify\", \"net_radio\", \u2026);\n // the CD block IS its source.\n if (block === \"cd\") {\n updates.push({ id: \"player.source\", value: \"cd\" });\n } else if (typeof info.input === \"string\") {\n updates.push({ id: \"player.source\", value: info.input });\n }\n // CD drive-own extras (presence-checked, netusb responses carry none of these fields).\n if (typeof info.track_number === \"number\") {\n updates.push({ id: \"player.cd.trackNumber\", value: info.track_number });\n }\n if (typeof info.total_tracks === \"number\") {\n updates.push({ id: \"player.cd.totalTracks\", value: info.total_tracks });\n }\n if (typeof info.disc_time === \"number\") {\n updates.push({ id: \"player.cd.discTime\", value: info.disc_time });\n }\n if (typeof info.device_status === \"string\") {\n updates.push({ id: \"player.cd.deviceStatus\", value: info.device_status });\n }\n return updates;\n}\n\n/**\n * The values a zone's player block is reset to when the zone leaves its playing\n * source (v2.0.0 clear-on-switch): metadata empty, times zero, playback Stop.\n */\nexport const PLAYER_CLEAR: StateValue[] = [\n { id: \"player.source\", value: \"\" },\n { id: \"player.playback\", value: 1 },\n { id: \"player.artist\", value: \"\" },\n { id: \"player.album\", value: \"\" },\n { id: \"player.track\", value: \"\" },\n { id: \"player.albumArt\", value: \"\" },\n { id: \"player.elapsedTime\", value: 0 },\n { id: \"player.elapsedTimeText\", value: \"\" },\n { id: \"player.totalTime\", value: 0 },\n { id: \"player.totalTimeText\", value: \"\" },\n { id: \"player.repeat\", value: 0 },\n { id: \"player.shuffle\", value: false },\n];\n\n/**\n * The DAB block's fields \u2192 their unified state ids (aligned with the YNCA DAB ids so\n * both transports feed one node), with the object name each state is created under.\n * Single source for the object mapper (creation) and the parser below (read-back).\n */\nexport const DAB_FIELDS: Array<{\n field: string;\n id: string;\n type: \"string\" | \"number\" | \"boolean\";\n /** Object name as its translation KEY \u2014 this table is a module-level constant. */\n nameKey: I18nKey;\n /** Explanation key \u2014 absent means self-explanatory. */\n descKey?: I18nKey;\n}> = [\n {\n field: \"service_label\",\n id: \"tuner.dab.serviceLabel\",\n type: \"string\",\n nameKey: \"serviceLabel\",\n descKey: \"descDabService\",\n },\n {\n field: \"ensemble_label\",\n id: \"tuner.dab.ensembleLabel\",\n type: \"string\",\n nameKey: \"ensembleLabel\",\n descKey: \"descDabEnsemble\",\n },\n {\n field: \"ch_label\",\n id: \"tuner.dab.channelLabel\",\n type: \"string\",\n nameKey: \"channelLabel\",\n descKey: \"descDabChannel\",\n },\n { field: \"dls\", id: \"tuner.dab.dls\", type: \"string\", nameKey: \"dlsText\", descKey: \"descDabDLSText\" },\n {\n field: \"program_type\",\n id: \"tuner.dab.programType\",\n type: \"string\",\n nameKey: \"programmeType\",\n descKey: \"descDabProgramType\",\n },\n // preset and audio_mode are NOT listed here: the active-band parse feeds the\n // unified flat tuner.preset / tuner.audioMode states (v2.0.0).\n { field: \"status\", id: \"tuner.dab.status\", type: \"string\", nameKey: \"dabStatus\", descKey: \"descDabStatus\" },\n { field: \"bit_rate\", id: \"tuner.dab.bitRate\", type: \"number\", nameKey: \"bitRate\", descKey: \"descBitRate\" },\n { field: \"quality\", id: \"tuner.dab.quality\", type: \"number\", nameKey: \"signalQuality\", descKey: \"descSignalQuality\" },\n { field: \"off_air\", id: \"tuner.dab.offAir\", type: \"boolean\", nameKey: \"offAir\", descKey: \"descOffAir\" },\n { field: \"dab_plus\", id: \"tuner.dab.dabPlus\", type: \"boolean\", nameKey: \"dabPlus\", descKey: \"descDabPlus\" },\n {\n field: \"category\",\n id: \"tuner.dab.category\",\n type: \"string\",\n nameKey: \"serviceCategory\",\n descKey: \"descServiceCategory\",\n },\n {\n field: \"total_station_num\",\n id: \"tuner.dab.totalStations\",\n type: \"number\",\n nameKey: \"totalStations\",\n descKey: \"descTotalStations\",\n },\n {\n field: \"initial_scan_progress\",\n id: \"tuner.dab.scanProgress\",\n type: \"number\",\n nameKey: \"initialScanProgress\",\n descKey: \"descInitialScanProgress\",\n },\n { field: \"tune_aid\", id: \"tuner.dab.tuneAid\", type: \"number\", nameKey: \"tuneAidLevel\", descKey: \"descTuneAidLevel\" },\n];\n\n/**\n * Parse a YXC `/tuner/getPlayInfo` response into the tuner's read-only states: the\n * current band with its frequency/preset/tuned/audio mode, the RDS block, and the\n * DAB details (on the YNCA-shared `tuner.dab.*` ids). Absent fields are skipped.\n *\n * @param tunerInfo the getPlayInfo(\"tuner\") response object\n * @returns the tuner state updates, empty if malformed\n */\nexport function parseYxcTunerInfo(tunerInfo: unknown): StateValue[] {\n if (typeof tunerInfo !== \"object\" || tunerInfo === null) {\n return [];\n }\n const info = tunerInfo as Record<string, unknown>;\n const updates: StateValue[] = [];\n const band = info.band;\n if (typeof band === \"string\") {\n updates.push({ id: \"tuner.band\", value: band });\n const bandInfo = info[band];\n if (typeof bandInfo === \"object\" && bandInfo !== null) {\n const current = bandInfo as Record<string, unknown>;\n if (typeof current.freq === \"number\") {\n updates.push({ id: \"tuner.frequency\", value: current.freq });\n }\n // The active band's preset slot, tuned flag and audio mode \u2014 consolidated onto\n // the flat tuner states (the band state says which band they describe).\n if (typeof current.preset === \"number\") {\n updates.push({ id: \"tuner.preset\", value: current.preset });\n }\n if (typeof current.tuned === \"boolean\") {\n updates.push({ id: \"tuner.tuned\", value: current.tuned });\n }\n if (typeof current.audio_mode === \"string\") {\n updates.push({ id: \"tuner.audioMode\", value: current.audio_mode });\n }\n }\n }\n const rds = info.rds;\n if (typeof rds === \"object\" && rds !== null) {\n const r = rds as Record<string, unknown>;\n if (typeof r.radio_text_a === \"string\") {\n updates.push({ id: \"tuner.rdsText\", value: r.radio_text_a });\n }\n if (typeof r.radio_text_b === \"string\") {\n updates.push({ id: \"tuner.rdsTextB\", value: r.radio_text_b });\n }\n if (typeof r.program_service === \"string\") {\n updates.push({ id: \"tuner.rdsService\", value: r.program_service });\n }\n if (typeof r.program_type === \"string\") {\n updates.push({ id: \"tuner.rdsProgramType\", value: r.program_type });\n }\n }\n // The DAB block is reported alongside the band blocks (capture-verified) and feeds\n // the same tuner.dab.* ids as the YNCA DAB subunit, so both transports share one node.\n const dab = info.dab;\n if (typeof dab === \"object\" && dab !== null) {\n const d = dab as Record<string, unknown>;\n for (const { field, id, type } of DAB_FIELDS) {\n const value = d[field];\n if (typeof value === type) {\n updates.push({ id, value: value as string | number | boolean });\n }\n }\n }\n return updates;\n}\n\n/**\n * Parse a `/netusb/getPresetInfo` response into the favourites JSON state: the stored\n * slots with their names, empty slots (input `unknown` or no text) skipped.\n *\n * @param info the getPresetInfo response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcPresetList(info: unknown): StateValue | undefined {\n const list = (info as { preset_info?: unknown } | null)?.preset_info;\n if (!Array.isArray(list)) {\n return undefined;\n }\n const slots: Array<{ num: number; input: string; name: string }> = [];\n list.forEach((entry, index) => {\n if (typeof entry !== \"object\" || entry === null) {\n return;\n }\n const { input, text } = entry as { input?: unknown; text?: unknown };\n if (typeof input === \"string\" && input !== \"unknown\" && typeof text === \"string\" && text.length > 0) {\n slots.push({ num: index + 1, input, name: text });\n }\n });\n return { id: \"player.netPlayer.presets\", value: JSON.stringify(slots) };\n}\n\n/**\n * Parse a `/netusb/getRecentInfo` response into the recently-played JSON state.\n *\n * @param info the getRecentInfo response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcRecentList(info: unknown): StateValue | undefined {\n const list = (info as { recent_info?: unknown } | null)?.recent_info;\n if (!Array.isArray(list)) {\n return undefined;\n }\n const items: Array<{ num: number; input: string; name: string; albumArt?: string; playCount?: number }> = [];\n list.forEach((entry, index) => {\n if (typeof entry !== \"object\" || entry === null) {\n return;\n }\n const e = entry as { input?: unknown; text?: unknown; albumart_url?: unknown; play_count?: unknown };\n if (typeof e.input !== \"string\" || typeof e.text !== \"string\" || e.text.length === 0) {\n return;\n }\n const item: { num: number; input: string; name: string; albumArt?: string; playCount?: number } = {\n num: index + 1,\n input: e.input,\n name: e.text,\n };\n if (typeof e.albumart_url === \"string\" && e.albumart_url.length > 0) {\n item.albumArt = e.albumart_url;\n }\n if (typeof e.play_count === \"number\") {\n item.playCount = e.play_count;\n }\n items.push(item);\n });\n return { id: \"player.netPlayer.recent\", value: JSON.stringify(items) };\n}\n\n/**\n * Parse `/tuner/getPresetInfo` responses (one per fetched band) into the tuner\n * presets JSON state: an object keyed by band, each STORED slot kept in its raw response\n * form plus its 1-based slot number (the shape varies per band and firmware).\n *\n * The device answers with its full slot count whether or not anything is stored \u2014 on a\n * receiver with no tuner presets that is 40 FM plus 40 DAB entries of\n * `{band:\"unknown\", number:0, text:\"\"}`, a JSON datapoint nobody can use. Empty slots are\n * dropped the same way the favourites list drops them; a slot is only empty when it says so\n * on every count, so an unfamiliar firmware shape is kept rather than silently swallowed.\n *\n * @param byBand each fetched band's getPresetInfo response object\n * @returns the state update, or undefined when no band delivered a list\n */\nexport function parseYxcTunerPresetLists(byBand: Record<string, unknown>): StateValue | undefined {\n const result: Record<string, unknown[]> = {};\n for (const [band, info] of Object.entries(byBand)) {\n const list = (info as { preset_info?: unknown } | null)?.preset_info;\n if (!Array.isArray(list)) {\n continue;\n }\n const slots: unknown[] = [];\n list.forEach((entry, index) => {\n if (typeof entry === \"object\" && entry !== null && !isEmptyTunerPreset(entry as Record<string, unknown>)) {\n slots.push({ num: index + 1, ...(entry as Record<string, unknown>) });\n }\n });\n result[band] = slots;\n }\n if (Object.keys(result).length === 0) {\n return undefined;\n }\n return { id: \"tuner.presets\", value: JSON.stringify(result) };\n}\n\n/**\n * Whether a tuner preset slot holds nothing. The device marks an unused slot with the band\n * `unknown`, frequency/service number 0 and empty text \u2014 a slot counts as empty only when\n * none of the three carries anything, so a firmware that fills only some of them is kept.\n *\n * @param slot one raw preset_info entry\n * @returns true when the slot is unused\n */\nfunction isEmptyTunerPreset(slot: Record<string, unknown>): boolean {\n const band = slot.band;\n const hasBand = typeof band === \"string\" && band.length > 0 && band !== \"unknown\";\n const hasNumber = typeof slot.number === \"number\" && slot.number !== 0;\n const hasText = typeof slot.text === \"string\" && slot.text.trim().length > 0;\n return !hasBand && !hasNumber && !hasText;\n}\n\n/**\n * Parse a `/<zone>/getSignalInfo` response into the read-only audio-signal states\n * (capture-verified shape: `audio` with format/fs/bit/bitrate).\n *\n * @param info the getSignalInfo response object\n * @param zone the zone the info belongs to\n * @returns the signal state updates, empty if malformed\n */\nexport function parseYxcSignalInfo(info: unknown, zone: string): StateValue[] {\n const audio = (info as { audio?: unknown } | null)?.audio;\n if (typeof audio !== \"object\" || audio === null) {\n return [];\n }\n const prefix = YXC_ZONE_IDS.includes(zone as (typeof YXC_ZONE_IDS)[number]) ? zonePrefix(zone) : undefined;\n if (prefix === undefined) {\n return [];\n }\n const a = audio as Record<string, unknown>;\n const updates: StateValue[] = [];\n // With no signal on the input the device fills these with its own dash placeholder (\"---\").\n // Passing that through makes a text datapoint that reads like content; empty says \"nothing\n // here\" in the same way the player block's resting seeds do.\n const text = (value: unknown): string => {\n const trimmed = typeof value === \"string\" ? value.trim() : \"\";\n return /^-+$/.test(trimmed) ? \"\" : trimmed;\n };\n if (typeof a.format === \"string\") {\n updates.push({ id: `${prefix}sound.signal.format`, value: text(a.format) });\n }\n if (typeof a.fs === \"string\") {\n updates.push({ id: `${prefix}sound.signal.sampling`, value: text(a.fs) });\n }\n if (typeof a.bit === \"string\") {\n updates.push({ id: `${prefix}sound.signal.bits`, value: text(a.bit) });\n }\n if (typeof a.bitrate === \"number\") {\n updates.push({ id: `${prefix}sound.signal.bitrate`, value: a.bitrate });\n }\n return updates;\n}\n\n/**\n * Parse a `/netusb/getMcPlaylistName` response into the playlists JSON state\n * (capture-verified shape: `name_list` of strings).\n *\n * @param info the getMcPlaylistName response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcPlaylistNames(info: unknown): StateValue | undefined {\n const names = (info as { name_list?: unknown } | null)?.name_list;\n if (!Array.isArray(names)) {\n return undefined;\n }\n const list = names\n .map((name, index) => ({ num: index + 1, name }))\n .filter((entry): entry is { num: number; name: string } => typeof entry.name === \"string\");\n return { id: \"player.netPlayer.playlists\", value: JSON.stringify(list) };\n}\n\n/**\n * Parse a `/netusb/getPlayQueue` response into the play-queue JSON state\n * (capture-verified shape: `track_info` plus `playing_index`).\n *\n * @param info the getPlayQueue response object\n * @returns the state update, or undefined if the response is malformed\n */\nexport function parseYxcPlayQueue(info: unknown): StateValue | undefined {\n if (typeof info !== \"object\" || info === null) {\n return undefined;\n }\n const q = info as { track_info?: unknown; playing_index?: unknown; max_line?: unknown };\n if (!Array.isArray(q.track_info)) {\n return undefined;\n }\n const value = {\n playingIndex: typeof q.playing_index === \"number\" ? q.playing_index : -1,\n totalTracks: typeof q.max_line === \"number\" ? q.max_line : q.track_info.length,\n tracks: q.track_info,\n };\n return { id: \"player.netPlayer.queue\", value: JSON.stringify(value) };\n}\n\n/**\n * Format a YXC alarm time (\"0800\") as a readable \"08:00\"; other shapes pass through.\n *\n * @param time the raw time value\n * @returns the formatted time\n */\nfunction formatAlarmTime(time: string): string {\n return /^\\d{4}$/.test(time) ? `${time.slice(0, 2)}:${time.slice(2)}` : time;\n}\n\n/**\n * Parse one alarm-detail block (oneday and each weekly day share the shape).\n *\n * @param prefix the state-id prefix the fields land under (e.g. `clock.alarm.oneday`)\n * @param detail the raw detail block\n * @returns the state updates for that block\n */\nfunction parseAlarmDetail(prefix: string, detail: Record<string, unknown>): StateValue[] {\n const updates: StateValue[] = [];\n if (typeof detail.enable === \"boolean\") {\n updates.push({ id: `${prefix}.enable`, value: detail.enable });\n }\n if (typeof detail.time === \"string\") {\n updates.push({ id: `${prefix}.time`, value: formatAlarmTime(detail.time) });\n }\n if (typeof detail.beep === \"boolean\") {\n updates.push({ id: `${prefix}.beep`, value: detail.beep });\n }\n if (typeof detail.playback_type === \"string\") {\n updates.push({ id: `${prefix}.playbackType`, value: detail.playback_type });\n }\n const resume = detail.resume;\n if (typeof resume === \"object\" && resume !== null && typeof (resume as { input?: unknown }).input === \"string\") {\n updates.push({ id: `${prefix}.resumeInput`, value: (resume as { input: string }).input });\n }\n const preset = detail.preset;\n if (typeof preset === \"object\" && preset !== null) {\n const p = preset as Record<string, unknown>;\n if (typeof p.type === \"string\") {\n updates.push({ id: `${prefix}.presetType`, value: p.type });\n }\n if (typeof p.num === \"number\") {\n updates.push({ id: `${prefix}.presetNumber`, value: p.num });\n }\n if (typeof p.netusb_input === \"string\") {\n updates.push({ id: `${prefix}.presetInput`, value: p.netusb_input });\n }\n }\n return updates;\n}\n\n/** The weekly alarm day keys, as the YXC clock block names them. */\nexport const ALARM_DAYS = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n\n/**\n * Parse a `/clock/getSettings` response into the read-only clock/alarm states\n * (capture-verified shape: auto_sync/format plus the nested alarm block).\n *\n * @param settings the getSettings response object\n * @returns the clock state updates, empty if malformed\n */\nexport function parseYxcClock(settings: unknown): StateValue[] {\n if (typeof settings !== \"object\" || settings === null) {\n return [];\n }\n const s = settings as Record<string, unknown>;\n const updates: StateValue[] = [];\n if (typeof s.auto_sync === \"boolean\") {\n updates.push({ id: \"clock.autoSync\", value: s.auto_sync });\n }\n if (typeof s.format === \"string\") {\n updates.push({ id: \"clock.format\", value: s.format });\n }\n const alarm = s.alarm;\n if (typeof alarm === \"object\" && alarm !== null) {\n const a = alarm as Record<string, unknown>;\n if (typeof a.alarm_on === \"boolean\") {\n updates.push({ id: \"clock.alarm.on\", value: a.alarm_on });\n }\n if (typeof a.volume === \"number\") {\n updates.push({ id: \"clock.alarm.volume\", value: a.volume });\n }\n if (typeof a.fade_interval === \"number\") {\n updates.push({ id: \"clock.alarm.fadeInterval\", value: a.fade_interval });\n }\n if (typeof a.fade_type === \"number\") {\n updates.push({ id: \"clock.alarm.fadeType\", value: a.fade_type });\n }\n if (typeof a.mode === \"string\") {\n updates.push({ id: \"clock.alarm.mode\", value: a.mode });\n }\n if (typeof a.repeat === \"boolean\") {\n updates.push({ id: \"clock.alarm.repeat\", value: a.repeat });\n }\n const oneday = a.oneday;\n if (typeof oneday === \"object\" && oneday !== null) {\n updates.push(...parseAlarmDetail(\"clock.alarm.oneday\", oneday as Record<string, unknown>));\n }\n for (const day of ALARM_DAYS) {\n const detail = a[day];\n if (typeof detail === \"object\" && detail !== null) {\n updates.push(...parseAlarmDetail(`clock.alarm.${day}`, detail as Record<string, unknown>));\n }\n }\n }\n return updates;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -77,7 +77,15 @@ const PLAYER_STATES = [
77
77
  // way round — so the datapoints mean the same thing on every device.
78
78
  {
79
79
  state: "elapsedTime",
80
- common: { nameKey: "elapsedTime", type: "number", unit: "s", role: "media.elapsed", read: true, write: false }
80
+ common: {
81
+ nameKey: "elapsedTime",
82
+ descKey: "descElapsedTime",
83
+ type: "number",
84
+ unit: "s",
85
+ role: "media.elapsed",
86
+ read: true,
87
+ write: false
88
+ }
81
89
  },
82
90
  {
83
91
  state: "elapsedTimeText",
@@ -92,11 +100,26 @@ const PLAYER_STATES = [
92
100
  },
93
101
  {
94
102
  state: "totalTime",
95
- common: { nameKey: "totalTime", type: "number", unit: "s", role: "media.duration", read: true, write: false }
103
+ common: {
104
+ nameKey: "totalTime",
105
+ descKey: "descTotalTime",
106
+ type: "number",
107
+ unit: "s",
108
+ role: "media.duration",
109
+ read: true,
110
+ write: false
111
+ }
96
112
  },
97
113
  {
98
114
  state: "totalTimeText",
99
- common: { nameKey: "totalTimeReadable", type: "string", role: "media.duration.text", read: true, write: false }
115
+ common: {
116
+ nameKey: "totalTimeReadable",
117
+ descKey: "descTotalTimeReadable",
118
+ type: "string",
119
+ role: "media.duration.text",
120
+ read: true,
121
+ write: false
122
+ }
100
123
  },
101
124
  {
102
125
  state: "albumArt",
@@ -208,6 +231,7 @@ function mapYxcToObjects(capabilities) {
208
231
  type: "state",
209
232
  common: {
210
233
  name: (0, import_i18n.tName)("recallScene"),
234
+ desc: (0, import_i18n.tName)("descRecallScene"),
211
235
  type: "number",
212
236
  role: "level",
213
237
  read: true,
@@ -278,7 +302,15 @@ function mapYxcToObjects(capabilities) {
278
302
  objects.push({
279
303
  id: "player.netPlayer.preset",
280
304
  type: "state",
281
- common: { name: (0, import_i18n.tName)("recallPreset"), type: "number", role: "level", read: true, write: true, min: 1 }
305
+ common: {
306
+ name: (0, import_i18n.tName)("recallPreset"),
307
+ desc: (0, import_i18n.tName)("descRecallPreset"),
308
+ type: "number",
309
+ role: "level",
310
+ read: true,
311
+ write: true,
312
+ min: 1
313
+ }
282
314
  });
283
315
  objects.push({
284
316
  id: "player.netPlayer.presets",
@@ -367,17 +399,38 @@ function mapYxcToObjects(capabilities) {
367
399
  objects.push({
368
400
  id: "tuner.rdsText",
369
401
  type: "state",
370
- common: { name: (0, import_i18n.tName)("rdsText"), type: "string", role: "text", read: true, write: false }
402
+ common: {
403
+ name: (0, import_i18n.tName)("rdsText"),
404
+ desc: (0, import_i18n.tName)("descRdsText"),
405
+ type: "string",
406
+ role: "text",
407
+ read: true,
408
+ write: false
409
+ }
371
410
  });
372
411
  objects.push({
373
412
  id: "tuner.rdsTextB",
374
413
  type: "state",
375
- common: { name: (0, import_i18n.tName)("rdsTextB"), type: "string", role: "text", read: true, write: false }
414
+ common: {
415
+ name: (0, import_i18n.tName)("rdsTextB"),
416
+ desc: (0, import_i18n.tName)("descRdsTextB"),
417
+ type: "string",
418
+ role: "text",
419
+ read: true,
420
+ write: false
421
+ }
376
422
  });
377
423
  objects.push({
378
424
  id: "tuner.rdsService",
379
425
  type: "state",
380
- common: { name: (0, import_i18n.tName)("rdsStation"), type: "string", role: "text", read: true, write: false }
426
+ common: {
427
+ name: (0, import_i18n.tName)("rdsStation"),
428
+ desc: (0, import_i18n.tName)("descRdsStation"),
429
+ type: "string",
430
+ role: "text",
431
+ read: true,
432
+ write: false
433
+ }
381
434
  });
382
435
  objects.push({
383
436
  id: "tuner.rdsProgramType",
@@ -386,6 +439,7 @@ function mapYxcToObjects(capabilities) {
386
439
  });
387
440
  const presetCommon = {
388
441
  name: (0, import_i18n.tName)("presetRecallByNumber"),
442
+ desc: (0, import_i18n.tName)("descPresetRecallByNumber"),
389
443
  type: "number",
390
444
  role: "level",
391
445
  read: true,
@@ -419,7 +473,14 @@ function mapYxcToObjects(capabilities) {
419
473
  objects.push({
420
474
  id: "tuner.audioMode",
421
475
  type: "state",
422
- common: { name: (0, import_i18n.tName)("audioMode"), type: "string", role: "state", read: true, write: false }
476
+ common: {
477
+ name: (0, import_i18n.tName)("audioMode"),
478
+ desc: (0, import_i18n.tName)("descAudioMode"),
479
+ type: "string",
480
+ role: "state",
481
+ read: true,
482
+ write: false
483
+ }
423
484
  });
424
485
  if (bands.includes("dab")) {
425
486
  objects.push({ id: "tuner.dab", type: "channel", common: { name: (0, import_i18n.tName)("dab") } });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/yxc/object-mapper.ts"],
4
- "sourcesContent": ["import { CHANNEL_NAME_KEYS, type ObjectDef } from \"../catalog/types\";\nimport { tName, type I18nKey } from \"../i18n\";\nimport { YXC_ZONE_IDS, zonePrefix } from \"./zones\";\nimport type { YxcCapabilities } from \"./capability\";\nimport { YXC_AMP_CATALOG } from \"./catalog\";\nimport { ALARM_DAYS, DAB_FIELDS } from \"./command-mapper\";\n\n/**\n * Build a value \u2192 label dropdown map from a device-reported value list.\n *\n * @param values the allowed values\n * @returns the states map\n */\nfunction selfMap(values: readonly string[]): Record<string, string> {\n return Object.fromEntries(values.map(value => [value, value]));\n}\n\n/** The zones the adapter maps: main flat, zone2-4 each under multiroom. */\nconst ZONES: Array<{ id: string; prefix: string }> = YXC_ZONE_IDS.map(id => ({ id, prefix: zonePrefix(id) }));\n\n/** The \"now playing\" block's states (v2.0.0, one per zone): read metadata + transport buttons. */\nconst PLAYER_STATES: Array<{\n state: string;\n common: Omit<ObjectDef[\"common\"], \"name\"> & { nameKey: I18nKey; descKey?: I18nKey };\n}> = [\n {\n // What the zone is playing (the netusb source name, or `cd`) \u2014 read-only display;\n // switching happens over the zone's `input` state.\n state: \"source\",\n common: { nameKey: \"playingSource\", type: \"string\", role: \"text\", read: true, write: false },\n },\n {\n state: \"playback\",\n common: {\n // media.state is a number in the type-detector; the same 0/1/2 coding as the YNCA player.\n nameKey: \"playback\",\n type: \"number\",\n role: \"media.state\",\n read: true,\n write: false,\n states: { 0: \"Play\", 1: \"Stop\", 2: \"Pause\" },\n },\n },\n { state: \"artist\", common: { nameKey: \"artist\", type: \"string\", role: \"media.artist\", read: true, write: false } },\n { state: \"album\", common: { nameKey: \"album\", type: \"string\", role: \"media.album\", read: true, write: false } },\n { state: \"track\", common: { nameKey: \"track\", type: \"string\", role: \"media.title\", read: true, write: false } },\n // Read-only playback metadata, typed exactly like the YNCA sources so both players\n // present the same shape on one device: repeat as the media.mode.repeat number code\n // (wire off/one/all, captures-verified), shuffle as a media.mode.shuffle boolean\n // (wire knows only off/on). Writing stays with the toggle buttons \u2014 YXC has no setter.\n {\n state: \"repeat\",\n common: {\n nameKey: \"repeat\",\n type: \"number\",\n role: \"media.mode.repeat\",\n read: true,\n write: false,\n states: { 0: \"Off\", 1: \"Single\", 2: \"All\" },\n },\n },\n {\n state: \"shuffle\",\n common: { nameKey: \"shuffle\", type: \"boolean\", role: \"media.mode.shuffle\", read: true, write: false },\n },\n // Both forms of each time, from the one value the device reports: the seconds fill the\n // type detector's media-player slot (it takes nothing else), the text is what a\n // visualisation shows. The YNCA side publishes exactly the same pair, converted the other\n // way round \u2014 so the datapoints mean the same thing on every device.\n {\n state: \"elapsedTime\",\n common: { nameKey: \"elapsedTime\", type: \"number\", unit: \"s\", role: \"media.elapsed\", read: true, write: false },\n },\n {\n state: \"elapsedTimeText\",\n common: {\n nameKey: \"elapsedTimeReadable\",\n descKey: \"descElapsedTimeReadable\",\n type: \"string\",\n role: \"media.elapsed.text\",\n read: true,\n write: false,\n },\n },\n {\n state: \"totalTime\",\n common: { nameKey: \"totalTime\", type: \"number\", unit: \"s\", role: \"media.duration\", read: true, write: false },\n },\n {\n state: \"totalTimeText\",\n common: { nameKey: \"totalTimeReadable\", type: \"string\", role: \"media.duration.text\", read: true, write: false },\n },\n {\n state: \"albumArt\",\n common: { nameKey: \"albumArt\", type: \"string\", role: \"media.cover\", read: true, write: false },\n },\n // Transport buttons carry the type-detector media-player roles so a MusicCast player's\n // controls are recognised as play/pause/stop/next/prev, not generic buttons.\n { state: \"play\", common: { nameKey: \"play\", type: \"boolean\", role: \"button.play\", read: false, write: true } },\n { state: \"pause\", common: { nameKey: \"pause\", type: \"boolean\", role: \"button.pause\", read: false, write: true } },\n { state: \"stop\", common: { nameKey: \"stop\", type: \"boolean\", role: \"button.stop\", read: false, write: true } },\n { state: \"next\", common: { nameKey: \"next\", type: \"boolean\", role: \"button.next\", read: false, write: true } },\n { state: \"prev\", common: { nameKey: \"previous\", type: \"boolean\", role: \"button.prev\", read: false, write: true } },\n {\n state: \"repeatToggle\",\n common: { nameKey: \"toggleRepeat\", type: \"boolean\", role: \"button\", read: false, write: true },\n },\n {\n state: \"shuffleToggle\",\n common: { nameKey: \"toggleShuffle\", type: \"boolean\", role: \"button\", read: false, write: true },\n },\n];\n\n/**\n * Append a media-player block (channel + the shared player states) under a\n * dotted prefix. Used for every player source the device reports.\n *\n * @param objects the object list to append to\n * @param prefix the channel/state prefix (e.g. `netPlayer`, `cd`)\n * @param channelName the human-readable channel name\n */\nfunction pushPlayerBlock(objects: ObjectDef[], prefix: string, channelName: ioBroker.StringOrTranslated): void {\n objects.push({ id: prefix, type: \"channel\", common: { name: channelName } });\n for (const player of PLAYER_STATES) {\n const { nameKey: playerNameKey, descKey: playerDescKey, ...playerCommon } = player.common;\n objects.push({\n id: `${prefix}.${player.state}`,\n type: \"state\",\n common: {\n ...playerCommon,\n name: tName(playerNameKey),\n ...(playerDescKey ? { desc: tName(playerDescKey) } : {}),\n },\n });\n }\n}\n\n/**\n * Turn YXC capabilities into the unified object tree: main's functions as\n * top-level states, each additional zone as a channel with its own states. An\n * input state is added when the zone offers inputs. Player sources (netusb, cd)\n * and the tuner get their own channel. Only reported functions are created,\n * parents before children. States and their common come from {@link YXC_AMP_CATALOG}.\n *\n * @param capabilities the parsed YXC capabilities\n * @returns the object definitions to create\n */\nexport function mapYxcToObjects(capabilities: YxcCapabilities): ObjectDef[] {\n const objects: ObjectDef[] = [];\n const channels = new Set<string>();\n for (const zoneDef of ZONES) {\n const zone = capabilities.zones.find(z => z.id === zoneDef.id);\n if (!zone) {\n continue;\n }\n const hasInput = zone.inputs.length > 0;\n const entries = YXC_AMP_CATALOG.filter(entry => {\n // Device-global entries (id under multiroom.) exist once \u2014 never as a per-zone copy.\n if (zoneDef.id !== \"main\" && entry.state.startsWith(\"multiroom.\")) {\n return false;\n }\n if (entry.create.kind === \"always\") {\n return true;\n }\n if (entry.create.kind === \"input\") {\n return hasInput;\n }\n return zone.funcs.includes(entry.create.func);\n });\n // A zone needs an advertised function or an input to exist \u2014 the \"always\" status\n // fields alone (which every entry set contains) do not create a zone.\n if (!entries.some(entry => entry.create.kind !== \"always\")) {\n continue;\n }\n // Every parent \u2014 the zone channel included \u2014 is created by the per-state loop and\n // named from the shared CHANNEL_NAMES table (a zone exists only with an entry).\n for (const entry of entries) {\n const fullId = `${zoneDef.prefix}${entry.state}`;\n const segments = fullId.split(\".\");\n for (let i = 1; i < segments.length; i++) {\n const channelId = segments.slice(0, i).join(\".\");\n if (!channels.has(channelId)) {\n channels.add(channelId);\n const segment = segments[i - 1];\n objects.push({\n id: channelId,\n type: \"channel\",\n common: {\n name: CHANNEL_NAME_KEYS[segment]\n ? tName(CHANNEL_NAME_KEYS[segment])\n : segment.charAt(0).toUpperCase() + segment.slice(1),\n },\n });\n }\n }\n const { nameKey: entryNameKey, descKey: entryDescKey, ...entryRest } = entry.common;\n const common: ObjectDef[\"common\"] = {\n ...entryRest,\n name: tName(entryNameKey),\n ...(entryDescKey ? { desc: tName(entryDescKey) } : {}),\n };\n if (entry.state === \"volume\" && zone.volumeRange) {\n common.min = zone.volumeRange.min;\n common.max = zone.volumeRange.max;\n common.step = zone.volumeRange.step;\n }\n // The device's own allowed-value lists (getFeatures) become dropdowns: the zone's\n // inputs on the input state, sound_program_list & co on their states. On a device\n // that also speaks YNCA the YNCA-owned dropdown wins via the owner policy \u2014 these\n // matter on MusicCast-only devices, where the raw string was the old poverty.\n if (entry.state === \"input\" && zone.inputs.length > 0) {\n common.states = selfMap(zone.inputs);\n }\n const valueList = zone.valueLists?.[entry.state];\n if (valueList) {\n common.states = selfMap(valueList);\n }\n objects.push({ id: fullId, type: \"state\", common });\n }\n const zoneChannelHelper = (id: string, name: ioBroker.StringOrTranslated): void => {\n if (!channels.has(id)) {\n channels.add(id);\n objects.push({ id, type: \"channel\", common: { name } });\n }\n };\n // Scene recall (#615): the zone declares `scene` + scene_num \u2014 per zone, so a\n // Zone-2 scene is first-class (the RX-V6A declares 8 for main AND zone2).\n if (zone.funcs.includes(\"scene\") && zone.sceneNum && zone.sceneNum > 0) {\n zoneChannelHelper(`${zoneDef.prefix}scene`, tName(CHANNEL_NAME_KEYS.scene ?? \"Scenes\"));\n objects.push({\n id: `${zoneDef.prefix}scene.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: zone.sceneNum,\n step: 1,\n },\n });\n }\n // The on-screen remote (cursor pad + menu keys) \u2014 declared as zone functions\n // `cursor`/`menu`; the endpoints and their vocabulary are device-verified.\n if (zone.funcs.includes(\"cursor\") || zone.funcs.includes(\"menu\")) {\n zoneChannelHelper(`${zoneDef.prefix}remote`, tName(CHANNEL_NAME_KEYS.remote ?? \"Remote control\"));\n if (zone.funcs.includes(\"cursor\")) {\n objects.push({\n id: `${zoneDef.prefix}remote.cursor`,\n type: \"state\",\n common: {\n name: tName(\"cursorPad\"),\n type: \"string\",\n role: \"state\",\n read: false,\n write: true,\n states: selfMap([\"up\", \"down\", \"left\", \"right\", \"select\", \"return\"]),\n },\n });\n }\n if (zone.funcs.includes(\"menu\")) {\n objects.push({\n id: `${zoneDef.prefix}remote.menu`,\n type: \"state\",\n common: {\n name: tName(\"menuKey\"),\n type: \"string\",\n role: \"state\",\n read: false,\n write: true,\n states: selfMap([\"on_screen\", \"top_menu\", \"menu\", \"option\", \"display\", \"home\"]),\n },\n });\n }\n }\n // The audio-signal info (own endpoint, declared as `signal_info`): what the zone\n // currently decodes \u2014 format, sampling rate, bit depth, bitrate.\n if (zone.funcs.includes(\"signal_info\")) {\n zoneChannelHelper(`${zoneDef.prefix}sound`, tName(CHANNEL_NAME_KEYS.sound ?? \"Sound\"));\n zoneChannelHelper(`${zoneDef.prefix}sound.signal`, tName(CHANNEL_NAME_KEYS.signal ?? \"Audio signal\"));\n const signal = (\n id: string,\n name: ioBroker.StringOrTranslated,\n type: \"string\" | \"number\",\n role: \"text\" | \"value\",\n ): void => {\n objects.push({\n id: `${zoneDef.prefix}sound.signal.${id}`,\n type: \"state\",\n common: { name, type, role, read: true, write: false },\n });\n };\n signal(\"format\", tName(\"audioSignalFormat\"), \"string\", \"text\");\n signal(\"sampling\", tName(\"audioSamplingRate\"), \"string\", \"text\");\n signal(\"bits\", tName(\"audioBitDepth\"), \"string\", \"text\");\n signal(\"bitrate\", tName(\"audioBitrate\"), \"number\", \"value\");\n }\n }\n if (capabilities.media.includes(\"netusb\") || capabilities.media.includes(\"cd\")) {\n // ONE \"now playing\" block per zone (v2.0.0): the controller feeds it from\n // whichever source the zone is listening to (netusb or cd) and clears it on a\n // source switch. The source folders below keep only their genuinely own states.\n pushPlayerBlock(objects, \"player\", tName(\"mediaPlayer\"));\n for (const zone of capabilities.zones) {\n if (zone.id !== \"main\") {\n pushPlayerBlock(objects, `${zonePrefix(zone.id)}player`, tName(\"mediaPlayer\"));\n }\n }\n }\n if (capabilities.media.includes(\"netusb\")) {\n objects.push({ id: \"player.netPlayer\", type: \"channel\", common: { name: tName(\"networkPlayer\") } });\n objects.push({\n id: \"player.netPlayer.preset\",\n type: \"state\",\n common: { name: tName(\"recallPreset\"), type: \"number\", role: \"level\", read: true, write: true, min: 1 },\n });\n // The favourites and recently-played lists (names included) plus the recall-by-number\n // for recents \u2014 the musiccast adapter's selection surface, on our tree.\n objects.push({\n id: \"player.netPlayer.presets\",\n type: \"state\",\n common: { name: tName(\"favouritesStoredPresets\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n objects.push({\n id: \"player.netPlayer.recent\",\n type: \"state\",\n common: { name: tName(\"recentlyPlayed\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n objects.push({\n id: \"player.netPlayer.recallRecent\",\n type: \"state\",\n common: {\n name: tName(\"recallRecentlyPlayedNumber\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 1,\n },\n });\n // MusicCast playlists and the play queue \u2014 declared in the netusb func_list.\n // Read-only surfaces: no write path for them is documented anywhere, and blind\n // writes are exactly what this adapter no longer does.\n if (capabilities.netusbFuncs?.includes(\"mc_playlist\")) {\n objects.push({\n id: \"player.netPlayer.playlists\",\n type: \"state\",\n common: { name: tName(\"musiccastPlaylists\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n }\n if (capabilities.netusbFuncs?.includes(\"play_queue\")) {\n objects.push({\n id: \"player.netPlayer.queue\",\n type: \"state\",\n common: { name: tName(\"playQueue\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n }\n }\n if (capabilities.media.includes(\"cd\")) {\n // Drive-own states only \u2014 what the disc is PLAYING shows in the flat block above.\n objects.push({ id: \"player.cd\", type: \"channel\", common: { name: tName(\"cd\") } });\n objects.push({\n id: \"player.cd.tray\",\n type: \"state\",\n common: { name: tName(\"toggleTray\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"player.cd.trackNumber\",\n type: \"state\",\n common: { name: tName(\"trackNumber\"), type: \"number\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"player.cd.totalTracks\",\n type: \"state\",\n common: { name: tName(\"totalTracks\"), type: \"number\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"player.cd.discTime\",\n type: \"state\",\n common: { name: tName(\"discTime\"), type: \"number\", unit: \"s\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"player.cd.deviceStatus\",\n type: \"state\",\n common: { name: tName(\"driveStatus\"), type: \"string\", role: \"state\", read: true, write: false },\n });\n }\n if (capabilities.media.includes(\"tuner\")) {\n objects.push({ id: \"tuner\", type: \"channel\", common: { name: tName(\"tuner\") } });\n const bandCommon: ObjectDef[\"common\"] = {\n name: tName(\"band\"),\n type: \"string\",\n role: \"state\",\n read: true,\n write: true,\n };\n const bands = capabilities.tuner?.bands ?? [];\n if (bands.length > 0) {\n bandCommon.states = selfMap(bands);\n }\n objects.push({ id: \"tuner.band\", type: \"state\", common: bandCommon });\n // Frequency in kHz \u2014 FM/AM/DAB all report kHz in getPlayInfo (FM 100900 =\n // 100.9 MHz, AM 1080, DAB 180064), verified against real device captures.\n objects.push({\n id: \"tuner.frequency\",\n type: \"state\",\n common: { name: tName(\"frequency\"), type: \"number\", unit: \"kHz\", role: \"level\", read: true, write: true },\n });\n objects.push({\n id: \"tuner.rdsText\",\n type: \"state\",\n common: { name: tName(\"rdsText\"), type: \"string\", role: \"text\", read: true, write: false },\n });\n objects.push({\n id: \"tuner.rdsTextB\",\n type: \"state\",\n common: { name: tName(\"rdsTextB\"), type: \"string\", role: \"text\", read: true, write: false },\n });\n objects.push({\n id: \"tuner.rdsService\",\n type: \"state\",\n common: { name: tName(\"rdsStation\"), type: \"string\", role: \"text\", read: true, write: false },\n });\n objects.push({\n id: \"tuner.rdsProgramType\",\n type: \"state\",\n common: { name: tName(\"rdsProgrammeType\"), type: \"string\", role: \"text\", read: true, write: false },\n });\n // The stored-station surface: recall by number (writable), the active slot read back\n // from play info, up/down stepping, and the stored lists (with what the device knows\n // about each slot) as JSON \u2014 the selection surface the musiccast adapter offered.\n const presetCommon: ObjectDef[\"common\"] = {\n name: tName(\"presetRecallByNumber\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 0,\n };\n if (capabilities.tuner?.presetNum) {\n presetCommon.max = capabilities.tuner.presetNum;\n }\n objects.push({ id: \"tuner.preset\", type: \"state\", common: presetCommon });\n objects.push({\n id: \"tuner.presetUp\",\n type: \"state\",\n common: { name: tName(\"nextPreset\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"tuner.presetDown\",\n type: \"state\",\n common: { name: tName(\"previousPreset\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"tuner.presets\",\n type: \"state\",\n common: { name: tName(\"storedPresets\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n objects.push({\n id: \"tuner.tuned\",\n type: \"state\",\n common: { name: tName(\"tuned\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n objects.push({\n id: \"tuner.audioMode\",\n type: \"state\",\n common: { name: tName(\"audioMode\"), type: \"string\", role: \"state\", read: true, write: false },\n });\n if (bands.includes(\"dab\")) {\n objects.push({ id: \"tuner.dab\", type: \"channel\", common: { name: tName(\"dab\") } });\n for (const field of DAB_FIELDS) {\n objects.push({\n id: field.id,\n type: \"state\",\n common: {\n name: tName(field.nameKey),\n ...(field.descKey ? { desc: tName(field.descKey) } : {}),\n type: field.type,\n role: field.type === \"boolean\" ? \"indicator\" : field.type === \"number\" ? \"value\" : \"text\",\n read: true,\n write: false,\n },\n });\n }\n }\n }\n if (capabilities.clock) {\n // The clock/alarm block, as the musiccast adapter showed it \u2014 read-only display\n // (the predecessor's clock datapoints had no working write path either); the\n // devices that report it are the desk-audio/clock models.\n objects.push({ id: \"clock\", type: \"channel\", common: { name: tName(\"clockAlarm\") } });\n objects.push({\n id: \"clock.autoSync\",\n type: \"state\",\n common: { name: tName(\"automaticTimeSync\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n objects.push({\n id: \"clock.format\",\n type: \"state\",\n common: { name: tName(\"clockFormat\"), type: \"string\", role: \"state\", read: true, write: false },\n });\n objects.push({ id: \"clock.alarm\", type: \"channel\", common: { name: tName(\"alarm\") } });\n objects.push({\n id: \"clock.alarm.on\",\n type: \"state\",\n common: { name: tName(\"alarmArmed\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n const volumeCommon: ObjectDef[\"common\"] = {\n name: tName(\"alarmVolume\"),\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n };\n if (capabilities.clock.alarmVolumeRange) {\n volumeCommon.min = capabilities.clock.alarmVolumeRange.min;\n volumeCommon.max = capabilities.clock.alarmVolumeRange.max;\n }\n objects.push({ id: \"clock.alarm.volume\", type: \"state\", common: volumeCommon });\n objects.push({\n id: \"clock.alarm.fadeInterval\",\n type: \"state\",\n common: { name: tName(\"fadeInTime\"), type: \"number\", unit: \"s\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"clock.alarm.fadeType\",\n type: \"state\",\n common: { name: tName(\"fadeType\"), type: \"number\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"clock.alarm.mode\",\n type: \"state\",\n common: { name: tName(\"alarmMode\"), type: \"string\", role: \"state\", read: true, write: false },\n });\n objects.push({\n id: \"clock.alarm.repeat\",\n type: \"state\",\n common: { name: tName(\"repeatSnooze\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n const detailChannels = [\"oneday\", ...(capabilities.clock.alarmModes.includes(\"weekly\") ? ALARM_DAYS : [])];\n for (const channel of detailChannels) {\n // The weekday channels are named by the device; only the fixed one-day channel translates.\n const label: ioBroker.StringOrTranslated =\n channel === \"oneday\" ? tName(\"oneDayAlarm\") : channel.charAt(0).toUpperCase() + channel.slice(1);\n objects.push({ id: `clock.alarm.${channel}`, type: \"channel\", common: { name: label } });\n const detail = (\n id: string,\n name: ioBroker.StringOrTranslated,\n type: \"boolean\" | \"number\" | \"string\",\n role: string,\n ): void => {\n objects.push({\n id: `clock.alarm.${channel}.${id}`,\n type: \"state\",\n common: { name, type, role, read: true, write: false },\n });\n };\n detail(\"enable\", tName(\"enabled\"), \"boolean\", \"indicator\");\n detail(\"time\", tName(\"alarmTime\"), \"string\", \"text\");\n detail(\"beep\", tName(\"beep\"), \"boolean\", \"indicator\");\n detail(\"playbackType\", tName(\"playbackType\"), \"string\", \"state\");\n detail(\"resumeInput\", tName(\"resumeInput\"), \"string\", \"state\");\n detail(\"presetType\", tName(\"presetType\"), \"string\", \"state\");\n detail(\"presetNumber\", tName(\"presetNumber\"), \"number\", \"value\");\n detail(\"presetInput\", tName(\"presetSource\"), \"string\", \"state\");\n }\n }\n if (capabilities.hasDistribution) {\n // The MusicCast-Link states live in their own folder so the tree itself tells the\n // scope: directly under multiroom = all zones of this device, group = linked devices.\n // Both channels already exist: the main zone's always-created\n // multiroom.group.streamingEnabled state brought them in through the parent loop.\n const distState = (id: string, name: ioBroker.StringOrTranslated, role: string): void => {\n objects.push({\n id: `multiroom.group.${id}`,\n type: \"state\",\n common: { name, type: \"string\", role, read: true, write: false },\n });\n };\n distState(\"role\", tName(\"roleServerClient\"), \"state\");\n distState(\"id\", tName(\"groupID\"), \"text\");\n distState(\"name\", tName(\"groupName\"), \"text\");\n distState(\"serverZone\", tName(\"serverZoneFeedsTheGroup\"), \"text\");\n distState(\"linkedDevices\", tName(\"linkedDevices\"), \"json\");\n objects.push({\n id: \"multiroom.group.leave\",\n type: \"state\",\n common: { name: tName(\"leaveGroup\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"multiroom.group.linkDevice\",\n type: \"state\",\n common: { name: tName(\"linkADeviceItsIP\"), type: \"string\", role: \"text\", read: false, write: true },\n });\n }\n return objects;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { CHANNEL_NAME_KEYS, type ObjectDef } from \"../catalog/types\";\nimport { tName, type I18nKey } from \"../i18n\";\nimport { YXC_ZONE_IDS, zonePrefix } from \"./zones\";\nimport type { YxcCapabilities } from \"./capability\";\nimport { YXC_AMP_CATALOG } from \"./catalog\";\nimport { ALARM_DAYS, DAB_FIELDS } from \"./command-mapper\";\n\n/**\n * Build a value \u2192 label dropdown map from a device-reported value list.\n *\n * @param values the allowed values\n * @returns the states map\n */\nfunction selfMap(values: readonly string[]): Record<string, string> {\n return Object.fromEntries(values.map(value => [value, value]));\n}\n\n/** The zones the adapter maps: main flat, zone2-4 each under multiroom. */\nconst ZONES: Array<{ id: string; prefix: string }> = YXC_ZONE_IDS.map(id => ({ id, prefix: zonePrefix(id) }));\n\n/** The \"now playing\" block's states (v2.0.0, one per zone): read metadata + transport buttons. */\nconst PLAYER_STATES: Array<{\n state: string;\n common: Omit<ObjectDef[\"common\"], \"name\"> & { nameKey: I18nKey; descKey?: I18nKey };\n}> = [\n {\n // What the zone is playing (the netusb source name, or `cd`) \u2014 read-only display;\n // switching happens over the zone's `input` state.\n state: \"source\",\n common: { nameKey: \"playingSource\", type: \"string\", role: \"text\", read: true, write: false },\n },\n {\n state: \"playback\",\n common: {\n // media.state is a number in the type-detector; the same 0/1/2 coding as the YNCA player.\n nameKey: \"playback\",\n type: \"number\",\n role: \"media.state\",\n read: true,\n write: false,\n states: { 0: \"Play\", 1: \"Stop\", 2: \"Pause\" },\n },\n },\n { state: \"artist\", common: { nameKey: \"artist\", type: \"string\", role: \"media.artist\", read: true, write: false } },\n { state: \"album\", common: { nameKey: \"album\", type: \"string\", role: \"media.album\", read: true, write: false } },\n { state: \"track\", common: { nameKey: \"track\", type: \"string\", role: \"media.title\", read: true, write: false } },\n // Read-only playback metadata, typed exactly like the YNCA sources so both players\n // present the same shape on one device: repeat as the media.mode.repeat number code\n // (wire off/one/all, captures-verified), shuffle as a media.mode.shuffle boolean\n // (wire knows only off/on). Writing stays with the toggle buttons \u2014 YXC has no setter.\n {\n state: \"repeat\",\n common: {\n nameKey: \"repeat\",\n type: \"number\",\n role: \"media.mode.repeat\",\n read: true,\n write: false,\n states: { 0: \"Off\", 1: \"Single\", 2: \"All\" },\n },\n },\n {\n state: \"shuffle\",\n common: { nameKey: \"shuffle\", type: \"boolean\", role: \"media.mode.shuffle\", read: true, write: false },\n },\n // Both forms of each time, from the one value the device reports: the seconds fill the\n // type detector's media-player slot (it takes nothing else), the text is what a\n // visualisation shows. The YNCA side publishes exactly the same pair, converted the other\n // way round \u2014 so the datapoints mean the same thing on every device.\n {\n state: \"elapsedTime\",\n common: {\n nameKey: \"elapsedTime\",\n descKey: \"descElapsedTime\",\n type: \"number\",\n unit: \"s\",\n role: \"media.elapsed\",\n read: true,\n write: false,\n },\n },\n {\n state: \"elapsedTimeText\",\n common: {\n nameKey: \"elapsedTimeReadable\",\n descKey: \"descElapsedTimeReadable\",\n type: \"string\",\n role: \"media.elapsed.text\",\n read: true,\n write: false,\n },\n },\n {\n state: \"totalTime\",\n common: {\n nameKey: \"totalTime\",\n descKey: \"descTotalTime\",\n type: \"number\",\n unit: \"s\",\n role: \"media.duration\",\n read: true,\n write: false,\n },\n },\n {\n state: \"totalTimeText\",\n common: {\n nameKey: \"totalTimeReadable\",\n descKey: \"descTotalTimeReadable\",\n type: \"string\",\n role: \"media.duration.text\",\n read: true,\n write: false,\n },\n },\n {\n state: \"albumArt\",\n common: { nameKey: \"albumArt\", type: \"string\", role: \"media.cover\", read: true, write: false },\n },\n // Transport buttons carry the type-detector media-player roles so a MusicCast player's\n // controls are recognised as play/pause/stop/next/prev, not generic buttons.\n { state: \"play\", common: { nameKey: \"play\", type: \"boolean\", role: \"button.play\", read: false, write: true } },\n { state: \"pause\", common: { nameKey: \"pause\", type: \"boolean\", role: \"button.pause\", read: false, write: true } },\n { state: \"stop\", common: { nameKey: \"stop\", type: \"boolean\", role: \"button.stop\", read: false, write: true } },\n { state: \"next\", common: { nameKey: \"next\", type: \"boolean\", role: \"button.next\", read: false, write: true } },\n { state: \"prev\", common: { nameKey: \"previous\", type: \"boolean\", role: \"button.prev\", read: false, write: true } },\n {\n state: \"repeatToggle\",\n common: { nameKey: \"toggleRepeat\", type: \"boolean\", role: \"button\", read: false, write: true },\n },\n {\n state: \"shuffleToggle\",\n common: { nameKey: \"toggleShuffle\", type: \"boolean\", role: \"button\", read: false, write: true },\n },\n];\n\n/**\n * Append a media-player block (channel + the shared player states) under a\n * dotted prefix. Used for every player source the device reports.\n *\n * @param objects the object list to append to\n * @param prefix the channel/state prefix (e.g. `netPlayer`, `cd`)\n * @param channelName the human-readable channel name\n */\nfunction pushPlayerBlock(objects: ObjectDef[], prefix: string, channelName: ioBroker.StringOrTranslated): void {\n objects.push({ id: prefix, type: \"channel\", common: { name: channelName } });\n for (const player of PLAYER_STATES) {\n const { nameKey: playerNameKey, descKey: playerDescKey, ...playerCommon } = player.common;\n objects.push({\n id: `${prefix}.${player.state}`,\n type: \"state\",\n common: {\n ...playerCommon,\n name: tName(playerNameKey),\n ...(playerDescKey ? { desc: tName(playerDescKey) } : {}),\n },\n });\n }\n}\n\n/**\n * Turn YXC capabilities into the unified object tree: main's functions as\n * top-level states, each additional zone as a channel with its own states. An\n * input state is added when the zone offers inputs. Player sources (netusb, cd)\n * and the tuner get their own channel. Only reported functions are created,\n * parents before children. States and their common come from {@link YXC_AMP_CATALOG}.\n *\n * @param capabilities the parsed YXC capabilities\n * @returns the object definitions to create\n */\nexport function mapYxcToObjects(capabilities: YxcCapabilities): ObjectDef[] {\n const objects: ObjectDef[] = [];\n const channels = new Set<string>();\n for (const zoneDef of ZONES) {\n const zone = capabilities.zones.find(z => z.id === zoneDef.id);\n if (!zone) {\n continue;\n }\n const hasInput = zone.inputs.length > 0;\n const entries = YXC_AMP_CATALOG.filter(entry => {\n // Device-global entries (id under multiroom.) exist once \u2014 never as a per-zone copy.\n if (zoneDef.id !== \"main\" && entry.state.startsWith(\"multiroom.\")) {\n return false;\n }\n if (entry.create.kind === \"always\") {\n return true;\n }\n if (entry.create.kind === \"input\") {\n return hasInput;\n }\n return zone.funcs.includes(entry.create.func);\n });\n // A zone needs an advertised function or an input to exist \u2014 the \"always\" status\n // fields alone (which every entry set contains) do not create a zone.\n if (!entries.some(entry => entry.create.kind !== \"always\")) {\n continue;\n }\n // Every parent \u2014 the zone channel included \u2014 is created by the per-state loop and\n // named from the shared CHANNEL_NAMES table (a zone exists only with an entry).\n for (const entry of entries) {\n const fullId = `${zoneDef.prefix}${entry.state}`;\n const segments = fullId.split(\".\");\n for (let i = 1; i < segments.length; i++) {\n const channelId = segments.slice(0, i).join(\".\");\n if (!channels.has(channelId)) {\n channels.add(channelId);\n const segment = segments[i - 1];\n objects.push({\n id: channelId,\n type: \"channel\",\n common: {\n name: CHANNEL_NAME_KEYS[segment]\n ? tName(CHANNEL_NAME_KEYS[segment])\n : segment.charAt(0).toUpperCase() + segment.slice(1),\n },\n });\n }\n }\n const { nameKey: entryNameKey, descKey: entryDescKey, ...entryRest } = entry.common;\n const common: ObjectDef[\"common\"] = {\n ...entryRest,\n name: tName(entryNameKey),\n ...(entryDescKey ? { desc: tName(entryDescKey) } : {}),\n };\n if (entry.state === \"volume\" && zone.volumeRange) {\n common.min = zone.volumeRange.min;\n common.max = zone.volumeRange.max;\n common.step = zone.volumeRange.step;\n }\n // The device's own allowed-value lists (getFeatures) become dropdowns: the zone's\n // inputs on the input state, sound_program_list & co on their states. On a device\n // that also speaks YNCA the YNCA-owned dropdown wins via the owner policy \u2014 these\n // matter on MusicCast-only devices, where the raw string was the old poverty.\n if (entry.state === \"input\" && zone.inputs.length > 0) {\n common.states = selfMap(zone.inputs);\n }\n const valueList = zone.valueLists?.[entry.state];\n if (valueList) {\n common.states = selfMap(valueList);\n }\n objects.push({ id: fullId, type: \"state\", common });\n }\n const zoneChannelHelper = (id: string, name: ioBroker.StringOrTranslated): void => {\n if (!channels.has(id)) {\n channels.add(id);\n objects.push({ id, type: \"channel\", common: { name } });\n }\n };\n // Scene recall (#615): the zone declares `scene` + scene_num \u2014 per zone, so a\n // Zone-2 scene is first-class (the RX-V6A declares 8 for main AND zone2).\n if (zone.funcs.includes(\"scene\") && zone.sceneNum && zone.sceneNum > 0) {\n zoneChannelHelper(`${zoneDef.prefix}scene`, tName(CHANNEL_NAME_KEYS.scene ?? \"Scenes\"));\n objects.push({\n id: `${zoneDef.prefix}scene.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: zone.sceneNum,\n step: 1,\n },\n });\n }\n // The on-screen remote (cursor pad + menu keys) \u2014 declared as zone functions\n // `cursor`/`menu`; the endpoints and their vocabulary are device-verified.\n if (zone.funcs.includes(\"cursor\") || zone.funcs.includes(\"menu\")) {\n zoneChannelHelper(`${zoneDef.prefix}remote`, tName(CHANNEL_NAME_KEYS.remote ?? \"Remote control\"));\n if (zone.funcs.includes(\"cursor\")) {\n objects.push({\n id: `${zoneDef.prefix}remote.cursor`,\n type: \"state\",\n common: {\n name: tName(\"cursorPad\"),\n type: \"string\",\n role: \"state\",\n read: false,\n write: true,\n states: selfMap([\"up\", \"down\", \"left\", \"right\", \"select\", \"return\"]),\n },\n });\n }\n if (zone.funcs.includes(\"menu\")) {\n objects.push({\n id: `${zoneDef.prefix}remote.menu`,\n type: \"state\",\n common: {\n name: tName(\"menuKey\"),\n type: \"string\",\n role: \"state\",\n read: false,\n write: true,\n states: selfMap([\"on_screen\", \"top_menu\", \"menu\", \"option\", \"display\", \"home\"]),\n },\n });\n }\n }\n // The audio-signal info (own endpoint, declared as `signal_info`): what the zone\n // currently decodes \u2014 format, sampling rate, bit depth, bitrate.\n if (zone.funcs.includes(\"signal_info\")) {\n zoneChannelHelper(`${zoneDef.prefix}sound`, tName(CHANNEL_NAME_KEYS.sound ?? \"Sound\"));\n zoneChannelHelper(`${zoneDef.prefix}sound.signal`, tName(CHANNEL_NAME_KEYS.signal ?? \"Audio signal\"));\n const signal = (\n id: string,\n name: ioBroker.StringOrTranslated,\n type: \"string\" | \"number\",\n role: \"text\" | \"value\",\n ): void => {\n objects.push({\n id: `${zoneDef.prefix}sound.signal.${id}`,\n type: \"state\",\n common: { name, type, role, read: true, write: false },\n });\n };\n signal(\"format\", tName(\"audioSignalFormat\"), \"string\", \"text\");\n signal(\"sampling\", tName(\"audioSamplingRate\"), \"string\", \"text\");\n signal(\"bits\", tName(\"audioBitDepth\"), \"string\", \"text\");\n signal(\"bitrate\", tName(\"audioBitrate\"), \"number\", \"value\");\n }\n }\n if (capabilities.media.includes(\"netusb\") || capabilities.media.includes(\"cd\")) {\n // ONE \"now playing\" block per zone (v2.0.0): the controller feeds it from\n // whichever source the zone is listening to (netusb or cd) and clears it on a\n // source switch. The source folders below keep only their genuinely own states.\n pushPlayerBlock(objects, \"player\", tName(\"mediaPlayer\"));\n for (const zone of capabilities.zones) {\n if (zone.id !== \"main\") {\n pushPlayerBlock(objects, `${zonePrefix(zone.id)}player`, tName(\"mediaPlayer\"));\n }\n }\n }\n if (capabilities.media.includes(\"netusb\")) {\n objects.push({ id: \"player.netPlayer\", type: \"channel\", common: { name: tName(\"networkPlayer\") } });\n objects.push({\n id: \"player.netPlayer.preset\",\n type: \"state\",\n common: {\n name: tName(\"recallPreset\"),\n desc: tName(\"descRecallPreset\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 1,\n },\n });\n // The favourites and recently-played lists (names included) plus the recall-by-number\n // for recents \u2014 the musiccast adapter's selection surface, on our tree.\n objects.push({\n id: \"player.netPlayer.presets\",\n type: \"state\",\n common: { name: tName(\"favouritesStoredPresets\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n objects.push({\n id: \"player.netPlayer.recent\",\n type: \"state\",\n common: { name: tName(\"recentlyPlayed\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n objects.push({\n id: \"player.netPlayer.recallRecent\",\n type: \"state\",\n common: {\n name: tName(\"recallRecentlyPlayedNumber\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 1,\n },\n });\n // MusicCast playlists and the play queue \u2014 declared in the netusb func_list.\n // Read-only surfaces: no write path for them is documented anywhere, and blind\n // writes are exactly what this adapter no longer does.\n if (capabilities.netusbFuncs?.includes(\"mc_playlist\")) {\n objects.push({\n id: \"player.netPlayer.playlists\",\n type: \"state\",\n common: { name: tName(\"musiccastPlaylists\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n }\n if (capabilities.netusbFuncs?.includes(\"play_queue\")) {\n objects.push({\n id: \"player.netPlayer.queue\",\n type: \"state\",\n common: { name: tName(\"playQueue\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n }\n }\n if (capabilities.media.includes(\"cd\")) {\n // Drive-own states only \u2014 what the disc is PLAYING shows in the flat block above.\n objects.push({ id: \"player.cd\", type: \"channel\", common: { name: tName(\"cd\") } });\n objects.push({\n id: \"player.cd.tray\",\n type: \"state\",\n common: { name: tName(\"toggleTray\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"player.cd.trackNumber\",\n type: \"state\",\n common: { name: tName(\"trackNumber\"), type: \"number\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"player.cd.totalTracks\",\n type: \"state\",\n common: { name: tName(\"totalTracks\"), type: \"number\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"player.cd.discTime\",\n type: \"state\",\n common: { name: tName(\"discTime\"), type: \"number\", unit: \"s\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"player.cd.deviceStatus\",\n type: \"state\",\n common: { name: tName(\"driveStatus\"), type: \"string\", role: \"state\", read: true, write: false },\n });\n }\n if (capabilities.media.includes(\"tuner\")) {\n objects.push({ id: \"tuner\", type: \"channel\", common: { name: tName(\"tuner\") } });\n const bandCommon: ObjectDef[\"common\"] = {\n name: tName(\"band\"),\n type: \"string\",\n role: \"state\",\n read: true,\n write: true,\n };\n const bands = capabilities.tuner?.bands ?? [];\n if (bands.length > 0) {\n bandCommon.states = selfMap(bands);\n }\n objects.push({ id: \"tuner.band\", type: \"state\", common: bandCommon });\n // Frequency in kHz \u2014 FM/AM/DAB all report kHz in getPlayInfo (FM 100900 =\n // 100.9 MHz, AM 1080, DAB 180064), verified against real device captures.\n objects.push({\n id: \"tuner.frequency\",\n type: \"state\",\n common: { name: tName(\"frequency\"), type: \"number\", unit: \"kHz\", role: \"level\", read: true, write: true },\n });\n objects.push({\n id: \"tuner.rdsText\",\n type: \"state\",\n common: {\n name: tName(\"rdsText\"),\n desc: tName(\"descRdsText\"),\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n },\n });\n objects.push({\n id: \"tuner.rdsTextB\",\n type: \"state\",\n common: {\n name: tName(\"rdsTextB\"),\n desc: tName(\"descRdsTextB\"),\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n },\n });\n objects.push({\n id: \"tuner.rdsService\",\n type: \"state\",\n common: {\n name: tName(\"rdsStation\"),\n desc: tName(\"descRdsStation\"),\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n },\n });\n objects.push({\n id: \"tuner.rdsProgramType\",\n type: \"state\",\n common: { name: tName(\"rdsProgrammeType\"), type: \"string\", role: \"text\", read: true, write: false },\n });\n // The stored-station surface: recall by number (writable), the active slot read back\n // from play info, up/down stepping, and the stored lists (with what the device knows\n // about each slot) as JSON \u2014 the selection surface the musiccast adapter offered.\n const presetCommon: ObjectDef[\"common\"] = {\n name: tName(\"presetRecallByNumber\"),\n desc: tName(\"descPresetRecallByNumber\"),\n type: \"number\",\n role: \"level\",\n read: true,\n write: true,\n min: 0,\n };\n if (capabilities.tuner?.presetNum) {\n presetCommon.max = capabilities.tuner.presetNum;\n }\n objects.push({ id: \"tuner.preset\", type: \"state\", common: presetCommon });\n objects.push({\n id: \"tuner.presetUp\",\n type: \"state\",\n common: { name: tName(\"nextPreset\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"tuner.presetDown\",\n type: \"state\",\n common: { name: tName(\"previousPreset\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"tuner.presets\",\n type: \"state\",\n common: { name: tName(\"storedPresets\"), type: \"string\", role: \"json\", read: true, write: false },\n });\n objects.push({\n id: \"tuner.tuned\",\n type: \"state\",\n common: { name: tName(\"tuned\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n objects.push({\n id: \"tuner.audioMode\",\n type: \"state\",\n common: {\n name: tName(\"audioMode\"),\n desc: tName(\"descAudioMode\"),\n type: \"string\",\n role: \"state\",\n read: true,\n write: false,\n },\n });\n if (bands.includes(\"dab\")) {\n objects.push({ id: \"tuner.dab\", type: \"channel\", common: { name: tName(\"dab\") } });\n for (const field of DAB_FIELDS) {\n objects.push({\n id: field.id,\n type: \"state\",\n common: {\n name: tName(field.nameKey),\n ...(field.descKey ? { desc: tName(field.descKey) } : {}),\n type: field.type,\n role: field.type === \"boolean\" ? \"indicator\" : field.type === \"number\" ? \"value\" : \"text\",\n read: true,\n write: false,\n },\n });\n }\n }\n }\n if (capabilities.clock) {\n // The clock/alarm block, as the musiccast adapter showed it \u2014 read-only display\n // (the predecessor's clock datapoints had no working write path either); the\n // devices that report it are the desk-audio/clock models.\n objects.push({ id: \"clock\", type: \"channel\", common: { name: tName(\"clockAlarm\") } });\n objects.push({\n id: \"clock.autoSync\",\n type: \"state\",\n common: { name: tName(\"automaticTimeSync\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n objects.push({\n id: \"clock.format\",\n type: \"state\",\n common: { name: tName(\"clockFormat\"), type: \"string\", role: \"state\", read: true, write: false },\n });\n objects.push({ id: \"clock.alarm\", type: \"channel\", common: { name: tName(\"alarm\") } });\n objects.push({\n id: \"clock.alarm.on\",\n type: \"state\",\n common: { name: tName(\"alarmArmed\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n const volumeCommon: ObjectDef[\"common\"] = {\n name: tName(\"alarmVolume\"),\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n };\n if (capabilities.clock.alarmVolumeRange) {\n volumeCommon.min = capabilities.clock.alarmVolumeRange.min;\n volumeCommon.max = capabilities.clock.alarmVolumeRange.max;\n }\n objects.push({ id: \"clock.alarm.volume\", type: \"state\", common: volumeCommon });\n objects.push({\n id: \"clock.alarm.fadeInterval\",\n type: \"state\",\n common: { name: tName(\"fadeInTime\"), type: \"number\", unit: \"s\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"clock.alarm.fadeType\",\n type: \"state\",\n common: { name: tName(\"fadeType\"), type: \"number\", role: \"value\", read: true, write: false },\n });\n objects.push({\n id: \"clock.alarm.mode\",\n type: \"state\",\n common: { name: tName(\"alarmMode\"), type: \"string\", role: \"state\", read: true, write: false },\n });\n objects.push({\n id: \"clock.alarm.repeat\",\n type: \"state\",\n common: { name: tName(\"repeatSnooze\"), type: \"boolean\", role: \"indicator\", read: true, write: false },\n });\n const detailChannels = [\"oneday\", ...(capabilities.clock.alarmModes.includes(\"weekly\") ? ALARM_DAYS : [])];\n for (const channel of detailChannels) {\n // The weekday channels are named by the device; only the fixed one-day channel translates.\n const label: ioBroker.StringOrTranslated =\n channel === \"oneday\" ? tName(\"oneDayAlarm\") : channel.charAt(0).toUpperCase() + channel.slice(1);\n objects.push({ id: `clock.alarm.${channel}`, type: \"channel\", common: { name: label } });\n const detail = (\n id: string,\n name: ioBroker.StringOrTranslated,\n type: \"boolean\" | \"number\" | \"string\",\n role: string,\n ): void => {\n objects.push({\n id: `clock.alarm.${channel}.${id}`,\n type: \"state\",\n common: { name, type, role, read: true, write: false },\n });\n };\n detail(\"enable\", tName(\"enabled\"), \"boolean\", \"indicator\");\n detail(\"time\", tName(\"alarmTime\"), \"string\", \"text\");\n detail(\"beep\", tName(\"beep\"), \"boolean\", \"indicator\");\n detail(\"playbackType\", tName(\"playbackType\"), \"string\", \"state\");\n detail(\"resumeInput\", tName(\"resumeInput\"), \"string\", \"state\");\n detail(\"presetType\", tName(\"presetType\"), \"string\", \"state\");\n detail(\"presetNumber\", tName(\"presetNumber\"), \"number\", \"value\");\n detail(\"presetInput\", tName(\"presetSource\"), \"string\", \"state\");\n }\n }\n if (capabilities.hasDistribution) {\n // The MusicCast-Link states live in their own folder so the tree itself tells the\n // scope: directly under multiroom = all zones of this device, group = linked devices.\n // Both channels already exist: the main zone's always-created\n // multiroom.group.streamingEnabled state brought them in through the parent loop.\n const distState = (id: string, name: ioBroker.StringOrTranslated, role: string): void => {\n objects.push({\n id: `multiroom.group.${id}`,\n type: \"state\",\n common: { name, type: \"string\", role, read: true, write: false },\n });\n };\n distState(\"role\", tName(\"roleServerClient\"), \"state\");\n distState(\"id\", tName(\"groupID\"), \"text\");\n distState(\"name\", tName(\"groupName\"), \"text\");\n distState(\"serverZone\", tName(\"serverZoneFeedsTheGroup\"), \"text\");\n distState(\"linkedDevices\", tName(\"linkedDevices\"), \"json\");\n objects.push({\n id: \"multiroom.group.leave\",\n type: \"state\",\n common: { name: tName(\"leaveGroup\"), type: \"boolean\", role: \"button\", read: false, write: true },\n });\n objects.push({\n id: \"multiroom.group.linkDevice\",\n type: \"state\",\n common: { name: tName(\"linkADeviceItsIP\"), type: \"string\", role: \"text\", read: false, write: true },\n });\n }\n return objects;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }