iobroker.androidtv 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/main.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts"],
4
- "sourcesContent": ["/*\n * ioBroker.androidtv - control Android TV / Google TV devices (e.g. NVIDIA SHIELD) via the Android TV Remote\n * protocol v2 and optionally ADB (media information, app start by package, Fire TV).\n */\nimport * as utils from '@iobroker/adapter-core';\nimport { generateAdbKey, type AdbKey } from './lib/adb/auth';\nimport { DEFAULT_ADB_PORT } from './lib/adb/client';\nimport { AdbDevice } from './lib/adb/device';\nimport type { MediaInfo, PollResult } from './lib/adb/parsers';\nimport {\n generateClientCertificate,\n isUsableCertificate,\n parseTvIdentity,\n type ClientCertificate,\n} from './lib/certificate';\nimport { PairingError, PairingSession } from './lib/pairing';\nimport { RemoteConnection, resolveKeyCode } from './lib/remote';\nimport { ADB_STATES, DEVICE_STATES, KEY_BUTTONS, type StateDefinition } from './lib/states';\nimport type { Timers } from './lib/timers';\nimport { sendWakeOnLan, wolTargets } from './lib/wol';\n\ntype DeviceConfig = ioBroker.AdapterConfig['devices'][number];\ntype AppConfig = ioBroker.AdapterConfig['apps'][number];\ntype ConnectionMode = 'remote' | 'remote_adb' | 'adb';\n\n/** Runtime data of one configured TV. */\ninterface DeviceRuntime {\n id: string;\n config: DeviceConfig;\n mode: ConnectionMode;\n label: string;\n remote?: RemoteConnection;\n adb?: AdbDevice;\n remoteConnected: boolean;\n adbConnected: boolean;\n adbAuthPending: boolean;\n pairingRequired: boolean;\n muted: boolean | undefined;\n /** Pending ADB power command: contradicting poll results are ignored until it is reached or expires */\n powerTarget?: { value: boolean; until: number };\n /** Started when the primary connection is lost; resets power/media if the device stays away */\n offlineTimer?: ioBroker.Timeout;\n /** Wake-on-LAN was sent: switch on as soon as ADB is back (until this time) */\n wakePendingUntil?: number;\n}\n\n/** Answer for the admin `textSendTo` status line. */\ninterface StatusText {\n text: string;\n icon: string;\n style: { color: string };\n}\n\n/** Open pairing attempt (between \"start\" and \"finish\"). */\ninterface PendingPairing {\n session: PairingSession;\n timer: ioBroker.Timeout | undefined;\n}\n\n/** Pairing sessions are dropped after this time; the TV itself cancels after about a minute. */\nconst PAIRING_SESSION_TTL_MS = 120_000;\nconst DEFAULT_RECONNECT_MAX_S = 60;\nconst DEFAULT_ADB_POLL_S = 5;\nconst CERT_FILE = 'client-certificate.json';\nconst ADB_KEY_FILE = 'adb-key.json';\n/** A device that stays unreachable this long is considered off (power=false, media idle). */\nconst OFFLINE_GRACE_MS = 30_000;\n\n/** Texts returned to the admin UI (sendTo); German or English depending on system language. */\nconst MESSAGES = {\n pairingStarted: {\n en: 'The TV now shows a 6-character code. Enter it and click \"Confirm code\".',\n de: 'Der TV zeigt jetzt einen 6-stelligen Code. Code eingeben und \u201ECode best\u00E4tigen\u201C klicken.',\n },\n pairingDone: {\n en: 'Pairing successful. The connection is being established.',\n de: 'Kopplung erfolgreich. Die Verbindung wird aufgebaut.',\n },\n pairingDoneSave: {\n en: 'Pairing successful. Save the settings to connect to this TV.',\n de: 'Kopplung erfolgreich. Einstellungen speichern, damit die Verbindung aufgebaut wird.',\n },\n statusConnected: { en: 'Connected', de: 'Verbunden' },\n statusPairing: {\n en: 'Pairing required \u2013 switch the device on and follow step 1',\n de: 'Kopplung erforderlich \u2013 Ger\u00E4t einschalten und Schritt 1 ausf\u00FChren',\n },\n statusOffline: {\n en: 'Not connected \u2013 device switched off, unplugged or unreachable',\n de: 'Nicht verbunden \u2013 Ger\u00E4t aus, ohne Strom oder nicht erreichbar',\n },\n statusAdbPending: {\n en: 'Please confirm \"Allow debugging\" on the TV (tick \"Always allow\")',\n de: 'Bitte am Fernseher \u201EDebugging zulassen\u201C best\u00E4tigen (\u201EImmer zulassen\u201C anhaken)',\n },\n statusAdbOffline: {\n en: 'Not connected \u2013 device switched off or debugging not enabled',\n de: 'Nicht verbunden \u2013 Ger\u00E4t aus oder Debugging am Ger\u00E4t nicht eingeschaltet',\n },\n statusAdbOk: {\n en: 'playback info active',\n de: 'Wiedergabe-Infos aktiv',\n },\n statusInfoOffline: {\n en: 'playback info not available (debugging switched on?)',\n de: 'Wiedergabe-Infos nicht verf\u00FCgbar (Debugging eingeschaltet?)',\n },\n statusDisabled: { en: 'Disabled', de: 'Deaktiviert' },\n statusUnsaved: {\n en: 'Not saved yet \u2013 pairing already works, the connection starts after saving',\n de: 'Noch nicht gespeichert \u2013 Koppeln geht schon, die Verbindung startet nach dem Speichern',\n },\n statusNoHost: { en: 'Enter the IP address first', de: 'Zuerst die IP-Adresse eintragen' },\n noHost: { en: 'Please enter the IP address of the TV.', de: 'Bitte die IP-Adresse des TV eingeben.' },\n noSession: {\n en: 'No pairing in progress for this IP - click \"Start pairing\" first.',\n de: 'F\u00FCr diese IP l\u00E4uft keine Kopplung - zuerst \u201EKopplung starten\u201C klicken.',\n },\n noCertificate: {\n en: 'Client certificate not ready yet - wait a few seconds and try again.',\n de: 'Client-Zertifikat noch nicht bereit - ein paar Sekunden warten und erneut versuchen.',\n },\n connect: {\n en: 'The device cannot be reached for pairing. Check the IP address and that the device is switched on. For a Fire TV choose the device type \"Amazon Fire TV\" \u2013 it needs no pairing.',\n de: 'Das Ger\u00E4t ist zum Koppeln nicht erreichbar. IP-Adresse pr\u00FCfen und ob das Ger\u00E4t eingeschaltet ist. F\u00FCr einen Fire TV den Ger\u00E4tetyp \u201EAmazon Fire TV\u201C w\u00E4hlen \u2013 dort ist keine Kopplung n\u00F6tig.',\n },\n timeout: { en: 'The TV did not answer in time.', de: 'Der TV hat nicht rechtzeitig geantwortet.' },\n closed: {\n en: 'The TV closed the pairing (cancelled or timed out). Start again.',\n de: 'Der TV hat die Kopplung beendet (abgebrochen oder Zeit abgelaufen). Erneut starten.',\n },\n rejected: { en: 'The TV rejected the pairing.', de: 'Der TV hat die Kopplung abgelehnt.' },\n invalidPin: {\n en: 'The code must have 6 characters (0-9, A-F).',\n de: 'Der Code muss 6 Zeichen haben (0-9, A-F).',\n },\n wrongPin: {\n en: 'The code does not match. Please check it and try again.',\n de: 'Der Code passt nicht. Bitte pr\u00FCfen und erneut eingeben.',\n },\n} as const;\n\ntype MessageKey = keyof typeof MESSAGES;\n\n/**\n * Converts a free text into a valid ioBroker object id part.\n *\n * @param text - e.g. \"Shield Wohnzimmer\"\n * @param forbidden - Adapter FORBIDDEN_CHARS regex\n */\nfunction toId(text: string, forbidden: RegExp): string {\n // @KI_HINWEIS: Strenger als FORBIDDEN_CHARS: nur A-Z, a-z, 0-9, _ und -, damit IDs wie \"Disney+\" in Skripten,\n // MQTT-Topics und Vis ohne Escaping nutzbar sind.\n const umlauts: Record<string, string> = { \u00E4: 'ae', \u00F6: 'oe', \u00FC: 'ue', \u00C4: 'Ae', \u00D6: 'Oe', \u00DC: 'Ue', \u00DF: 'ss' };\n return text\n .trim()\n .replace(/[\u00E4\u00F6\u00FC\u00C4\u00D6\u00DC\u00DF]/g, c => umlauts[c] ?? c)\n .replace(forbidden, '_')\n .replace(/[^A-Za-z0-9_-]+/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n}\n\n/**\n * Normalizes the configured connection mode (configs of 0.0.1 have none).\n *\n * @param config - Device configuration\n */\nfunction modeOf(config: DeviceConfig): ConnectionMode {\n return config.connection === 'adb' || config.connection === 'remote_adb' ? config.connection : 'remote';\n}\n\n/**\n * Whether a launch target is a URL/deep link (has a scheme) rather than a package name.\n *\n * @param target - Link or package name\n */\nfunction isLink(target: string): boolean {\n return /^[a-z][a-z0-9+.-]*:/i.test(target.trim());\n}\n\nclass Androidtv extends utils.Adapter {\n /** Adapter timers for the protocol classes; cleared automatically on unload. */\n private readonly adapterTimers: Timers = {\n setTimeout: (callback, ms) => this.setTimeout(callback, ms),\n clearTimeout: handle => this.clearTimeout(handle as ioBroker.Timeout | undefined),\n };\n private readonly devices = new Map<string, DeviceRuntime>();\n private readonly appsById = new Map<string, AppConfig>();\n private readonly pairings = new Map<string, PendingPairing>();\n private certificate: ClientCertificate | undefined;\n private adbKey: AdbKey | undefined;\n private uiLanguage: 'en' | 'de' = 'en';\n\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: 'androidtv',\n });\n this.on('ready', this.onReady.bind(this));\n this.on('stateChange', this.onStateChange.bind(this));\n this.on('message', this.onMessage.bind(this));\n this.on('unload', this.onUnload.bind(this));\n }\n\n private async onReady(): Promise<void> {\n await this.setState('info.connection', false, true);\n\n const sysConfig = await this.getForeignObjectAsync('system.config');\n this.uiLanguage = sysConfig?.common?.language === 'de' ? 'de' : 'en';\n\n await this.ensureCertificate();\n\n const devices = (Array.isArray(this.config.devices) ? this.config.devices : []).filter(\n d => d && typeof d.host === 'string' && d.host.trim() !== '',\n );\n const apps = (Array.isArray(this.config.apps) ? this.config.apps : []).filter(\n a =>\n a &&\n typeof a.name === 'string' &&\n a.name.trim() !== '' &&\n typeof a.link === 'string' &&\n a.link.trim() !== '',\n );\n for (const app of apps) {\n const id = toId(app.name, this.FORBIDDEN_CHARS);\n if (id && !this.appsById.has(id)) {\n this.appsById.set(id, app);\n }\n }\n if (devices.some(d => d.enabled !== false && modeOf(d) !== 'remote')) {\n await this.ensureAdbKey();\n }\n\n const usedIds = new Set<string>();\n for (const config of devices) {\n const base =\n toId(config.name || config.host, this.FORBIDDEN_CHARS) || toId(config.host, this.FORBIDDEN_CHARS);\n let id = base;\n for (let n = 2; usedIds.has(id); n++) {\n id = `${base}_${n}`;\n }\n usedIds.add(id);\n await this.createDeviceObjects(id, config);\n if (config.enabled === false) {\n continue;\n }\n const runtime = this.createRuntime(id, config);\n this.devices.set(id, runtime);\n await this.refreshStatus(runtime);\n }\n await this.removeStaleObjects(usedIds, devices);\n\n if (!devices.length) {\n this.log.warn('No devices configured - add your TV in the instance settings.');\n }\n\n this.subscribeStates('*');\n for (const device of this.devices.values()) {\n device.remote?.start();\n device.adb?.start();\n this.scheduleOffline(device); // device unreachable at start-up \u2192 stale power/media are reset\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Keys and certificates (adapter file storage, private keys encrypted with the instance secret)\n // ------------------------------------------------------------------------------------------------------------\n\n /**\n * Loads the client certificate or creates and stores a new one.\n *\n * Storage: adapter file storage `<namespace>/client-certificate.json`, private key encrypted with the\n * instance secret. Older versions kept it in `native` \u2013 it is migrated once.\n */\n private async ensureCertificate(): Promise<void> {\n // @KI_HINWEIS: Nicht (mehr) in native: Eine vor dem ersten Start ge\u00F6ffnete Konfigurationsseite schreibt beim\n // Speichern leere Zertifikatsfelder zur\u00FCck \u2192 neues Zertifikat \u2192 Kopplung am TV verloren (im Feldtest\n // beobachtet). Der Dateispeicher wird vom Admin-Speichern nicht ber\u00FChrt; au\u00DFerdem entf\u00E4llt der Neustart.\n const stored = await this.readSecretFile(CERT_FILE);\n if (typeof stored?.cert === 'string' && typeof stored.key === 'string') {\n const candidate = { cert: stored.cert, key: this.decrypt(stored.key) };\n if (isUsableCertificate(candidate)) {\n this.certificate = candidate;\n return;\n }\n this.log.warn('Stored client certificate is invalid or expired - creating a new one (pairing required)');\n }\n const fromNative = { cert: this.config.clientCert, key: this.config.clientKey };\n if (isUsableCertificate(fromNative)) {\n this.log.info('Moving client certificate from instance configuration to file storage');\n this.certificate = fromNative;\n } else {\n this.log.info('Creating client certificate for pairing (one-time)');\n this.certificate = generateClientCertificate(this.config.clientName?.trim() || 'ioBroker');\n }\n await this.writeSecretFile(CERT_FILE, { cert: this.certificate.cert, key: this.encrypt(this.certificate.key) });\n }\n\n /** Loads or creates the RSA key used for ADB authentication. */\n private async ensureAdbKey(): Promise<void> {\n const stored = await this.readSecretFile(ADB_KEY_FILE);\n if (typeof stored?.key === 'string') {\n try {\n const privateKey = this.decrypt(stored.key);\n if (privateKey.includes('PRIVATE KEY')) {\n this.adbKey = { privateKey };\n return;\n }\n } catch (e) {\n this.log.warn(`Cannot read stored ADB key: ${(e as Error).message}`);\n }\n }\n this.log.info('Creating ADB key (one-time) - confirm \"Allow debugging\" on the TV at the first connection');\n this.adbKey = generateAdbKey();\n await this.writeSecretFile(ADB_KEY_FILE, { key: this.encrypt(this.adbKey.privateKey) });\n }\n\n private async readSecretFile(name: string): Promise<Record<string, unknown> | undefined> {\n try {\n if (!(await this.fileExistsAsync(this.namespace, name))) {\n return undefined;\n }\n const file = await this.readFileAsync(this.namespace, name);\n const raw = typeof file.file === 'string' ? file.file : file.file.toString('utf8');\n const parsed: unknown = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : undefined;\n } catch (e) {\n this.log.warn(`Cannot read ${name}: ${(e as Error).message}`);\n return undefined;\n }\n }\n\n private async writeSecretFile(name: string, content: Record<string, string>): Promise<void> {\n await this.setForeignObjectNotExistsAsync(this.namespace, {\n type: 'meta',\n common: { name: 'Android TV internal files', type: 'meta.user' },\n native: {},\n });\n await this.writeFileAsync(this.namespace, name, JSON.stringify(content));\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Device runtime\n // ------------------------------------------------------------------------------------------------------------\n\n private createRuntime(id: string, config: DeviceConfig): DeviceRuntime {\n const mode = modeOf(config);\n const reconnectMax =\n Number(this.config.reconnectMax) > 0 ? Number(this.config.reconnectMax) : DEFAULT_RECONNECT_MAX_S;\n const runtime: DeviceRuntime = {\n id,\n config,\n mode,\n label: `${config.name || id} (${config.host})`,\n remoteConnected: false,\n adbConnected: false,\n adbAuthPending: false,\n pairingRequired: false,\n muted: undefined,\n };\n if (mode !== 'adb') {\n if (!this.certificate) {\n throw new Error('Certificate missing');\n }\n runtime.remote = new RemoteConnection({\n timers: this.adapterTimers,\n host: config.host.trim(),\n certificate: this.certificate,\n enableIme: config.enableIme !== false,\n reconnectMaxMs: reconnectMax * 1000,\n });\n this.attachRemote(runtime, runtime.remote);\n }\n if (mode !== 'remote') {\n if (!this.adbKey) {\n throw new Error('ADB key missing');\n }\n const pollS =\n Number(this.config.adbPollInterval) >= 2 ? Number(this.config.adbPollInterval) : DEFAULT_ADB_POLL_S;\n const port = Number(config.adbPort) > 0 ? Number(config.adbPort) : DEFAULT_ADB_PORT;\n runtime.adb = new AdbDevice({\n timers: this.adapterTimers,\n host: config.host.trim(),\n port,\n key: this.adbKey,\n keyName: `ioBroker@${this.host ?? 'iobroker'}`,\n pollIntervalMs: pollS * 1000,\n reconnectMaxMs: reconnectMax * 1000,\n externalPower: () => (runtime.remoteConnected ? runtime.remote?.isOn : undefined),\n });\n this.attachAdb(runtime, runtime.adb);\n }\n return runtime;\n }\n\n private attachRemote(runtime: DeviceRuntime, remote: RemoteConnection): void {\n const { id, label } = runtime;\n remote.on('ready', (info, peerCert) => {\n runtime.remoteConnected = true;\n this.cancelOffline(runtime);\n runtime.pairingRequired = false;\n this.log.info(`${label}: connected`);\n void this.setDeviceStates(id, {\n 'info.connected': true,\n 'info.pairingRequired': false,\n 'info.model': info.model ?? null,\n 'info.manufacturer': info.vendor ?? null,\n 'info.remoteServiceVersion': info.appVersion ?? null,\n });\n if (peerCert) {\n try {\n const { mac } = parseTvIdentity(peerCert);\n // @KI_HINWEIS: Nur zur Info. Feldtest SHIELD: Die MAC im TV-Zertifikat ist die WLAN-MAC, auch\n // wenn das Ger\u00E4t per LAN h\u00E4ngt. F\u00FCr Wake-on-LAN z\u00E4hlt nur die MAC aus der Konfiguration.\n if (mac) {\n void this.setStateChangedAsync(`${id}.info.mac`, mac, true);\n }\n } catch (e) {\n this.log.debug(`${label}: cannot parse TV certificate: ${(e as Error).message}`);\n }\n }\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n remote.on('disconnected', reason => {\n runtime.remoteConnected = false;\n this.scheduleOffline(runtime);\n this.log.debug(`${label}: disconnected (${reason}), reconnecting`);\n void this.setStateChangedAsync(`${id}.info.connected`, false, true);\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n remote.on('unpaired', () => {\n runtime.remoteConnected = false;\n runtime.pairingRequired = true;\n this.log.warn(\n `${label}: the TV does not accept this ioBroker instance. Please pair it in the instance settings (tab \"Devices\").`,\n );\n void this.setDeviceStates(id, { 'info.connected': false, 'info.pairingRequired': true });\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n remote.on('power', on => {\n void this.setStateChangedAsync(`${id}.power`, on, true);\n if (on) {\n runtime.adb?.pollSoon();\n } else if (runtime.adb) {\n void this.setMediaStates(id, null);\n }\n });\n remote.on('currentApp', app => {\n void this.setStateChangedAsync(`${id}.currentApp`, app, true);\n runtime.adb?.pollSoon();\n });\n remote.on('volume', volume => {\n runtime.muted = volume.muted;\n // @KI_HINWEIS: max = 0 meldet z. B. die SHIELD, wenn die Lautst\u00E4rke per HDMI-CEC am TV/AVR geregelt wird\n // (Feldtest). Dann gibt es keinen eigenen Pegel \u2192 null statt einer irref\u00FChrenden 0.\n const known = volume.max > 0;\n void this.setDeviceStates(id, {\n volume: known ? volume.level : null,\n volumeMax: known ? volume.max : null,\n muted: volume.muted,\n });\n });\n remote.on('log', (level, message) => this.log[level](message));\n }\n\n private attachAdb(runtime: DeviceRuntime, adb: AdbDevice): void {\n const { id, label } = runtime;\n const adbOnly = runtime.mode === 'adb';\n adb.on('ready', info => {\n runtime.adbConnected = true;\n if (adbOnly) {\n this.cancelOffline(runtime);\n }\n runtime.adbAuthPending = false;\n this.log.info(`${label}: ADB connected${info.model ? ` (${info.model})` : ''}`);\n const values: Record<string, ioBroker.StateValue> = {\n 'info.adbConnected': true,\n 'info.adbAuthorizationPending': false,\n };\n if (adbOnly) {\n values['info.connected'] = true;\n values['info.model'] = info.model ?? null;\n adb.getProp('ro.product.manufacturer')\n .then(m => this.setStateChangedAsync(`${id}.info.manufacturer`, m ?? null, true))\n .catch(e => this.log.debug(`${label}: cannot read manufacturer: ${(e as Error).message}`));\n }\n void this.setDeviceStates(id, values);\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n // @KI_HINWEIS: Diagnose: Der Schl\u00FCssel der Wake-on-LAN-Einstellung ist herstellerabh\u00E4ngig (Fire-TV-\n // Fernseher verschiedener Hersteller). Erst die Schl\u00FCssel im Feld sammeln, bevor der Adapter sie setzt.\n adb.findWakeSettings()\n .then(found =>\n this.log.debug(`${label}: wake settings: ${found.length ? found.join(' | ') : 'none found'}`),\n )\n .catch(() => undefined);\n if (runtime.wakePendingUntil && Date.now() < runtime.wakePendingUntil) {\n runtime.wakePendingUntil = undefined;\n runtime.powerTarget = { value: true, until: Date.now() + 5_000 };\n adb.setPower(true).catch(e =>\n this.log.debug(`${label}: switching on after Wake-on-LAN failed: ${(e as Error).message}`),\n );\n }\n });\n adb.on('authPending', () => {\n runtime.adbAuthPending = true;\n this.log.warn(\n `${label}: confirm \"Allow debugging\" on the TV and tick \"Always allow from this computer\" (key ioBroker@${this.host ?? 'iobroker'})`,\n );\n void this.setStateChangedAsync(`${id}.info.adbAuthorizationPending`, true, true);\n void this.refreshStatus(runtime);\n });\n adb.on('disconnected', () => {\n runtime.adbConnected = false;\n if (adbOnly) {\n this.scheduleOffline(runtime);\n }\n this.log.debug(`${label}: ADB disconnected, reconnecting`);\n const values: Record<string, ioBroker.StateValue> = { 'info.adbConnected': false };\n if (adbOnly) {\n values['info.connected'] = false;\n }\n void this.setDeviceStates(id, values);\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n adb.on('poll', result => void this.applyPoll(runtime, result));\n adb.on('log', (level, message) => this.log[level](message));\n }\n\n /**\n * Writes the result of an ADB poll to the states.\n *\n * @param runtime - Device\n * @param result - Parsed poll output\n */\n private async applyPoll(runtime: DeviceRuntime, result: PollResult): Promise<void> {\n const { id } = runtime;\n if (runtime.mode === 'adb') {\n const target = runtime.powerTarget;\n // @KI_HINWEIS: Feldtest Fire TV: Eine kurz vor dem Befehl gestartete Abfrage meldete noch den alten\n // Zustand und best\u00E4tigte ihn (Flackern true\u2192false). Bis zum Ziel bzw. 5 s danach widersprechende\n // Werte ignorieren.\n const contradicts = target !== undefined && Date.now() < target.until && result.awake !== target.value;\n if (target && (!contradicts || Date.now() >= target.until)) {\n runtime.powerTarget = undefined;\n }\n if (result.awake !== undefined && !contradicts) {\n await this.setStateChangedAsync(`${id}.power`, result.awake, true);\n }\n if (result.currentApp) {\n await this.setStateChangedAsync(`${id}.currentApp`, result.currentApp, true);\n }\n }\n if (result.awake === false) {\n await this.setMediaStates(id, null);\n } else if (result.media !== undefined) {\n await this.setMediaStates(id, result.media);\n }\n }\n\n /**\n * Starts the grace timer after the primary connection was lost. Short interruptions (e.g. the TV closes the\n * connection after a rejected command and is back after 2 s) do not touch power/media.\n *\n * @param runtime - Device\n */\n private scheduleOffline(runtime: DeviceRuntime): void {\n if (runtime.offlineTimer) {\n return;\n }\n runtime.offlineTimer = this.setTimeout(() => {\n runtime.offlineTimer = undefined;\n this.log.debug(`${runtime.label}: unreachable for ${OFFLINE_GRACE_MS / 1000} s - treating as off`);\n void this.setStateChangedAsync(`${runtime.id}.power`, false, true);\n if (runtime.adb) {\n void this.setMediaStates(runtime.id, null);\n }\n }, OFFLINE_GRACE_MS);\n }\n\n private cancelOffline(runtime: DeviceRuntime): void {\n if (runtime.offlineTimer) {\n this.clearTimeout(runtime.offlineTimer);\n runtime.offlineTimer = undefined;\n }\n }\n\n private async setMediaStates(id: string, media: MediaInfo | null): Promise<void> {\n await this.setDeviceStates(id, {\n 'media.state': media?.state ?? 'idle',\n 'media.title': media?.title ?? null,\n 'media.subtitle': media?.subtitle ?? null,\n 'media.app': media?.app ?? null,\n 'media.position': media?.position ?? null,\n });\n }\n\n private async setDeviceStates(id: string, values: Record<string, ioBroker.StateValue>): Promise<void> {\n for (const [key, val] of Object.entries(values)) {\n await this.setStateChangedAsync(`${id}.${key}`, val, true);\n }\n }\n\n private async updateConnectionState(): Promise<void> {\n const anyConnected = [...this.devices.values()].some(d =>\n d.mode === 'adb' ? d.adbConnected : d.remoteConnected,\n );\n await this.setStateChangedAsync('info.connection', anyConnected, true);\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Objects\n // ------------------------------------------------------------------------------------------------------------\n\n /**\n * State definitions that apply to a device in its connection mode.\n *\n * @param config - Device configuration\n */\n private stateDefinitionsFor(config: DeviceConfig): StateDefinition[] {\n const mode = modeOf(config);\n const base = DEVICE_STATES.filter(d => mode !== 'adb' || !d.remoteOnly);\n return mode === 'remote' ? base : [...base, ...ADB_STATES];\n }\n\n private async createDeviceObjects(id: string, config: DeviceConfig): Promise<void> {\n const mode = modeOf(config);\n await this.extendObjectAsync(id, {\n type: 'device',\n common: {\n name: config.name || config.host,\n statusStates: { onlineId: `${this.namespace}.${id}.info.connected` },\n },\n native: { host: config.host, connection: mode },\n });\n const channels: [string, ioBroker.StringOrTranslated][] = [\n ['info', { en: 'Information', de: 'Informationen' }],\n ['keys', { en: 'Remote keys', de: 'Fernbedienungstasten' }],\n ['apps', { en: 'App favorites', de: 'App-Favoriten' }],\n ];\n if (mode !== 'remote') {\n channels.push(['media', { en: 'Media (via ADB)', de: 'Medien (\u00FCber ADB)' }]);\n }\n for (const [channel, name] of channels) {\n await this.extendObjectAsync(`${id}.${channel}`, { type: 'channel', common: { name }, native: {} });\n }\n for (const def of this.stateDefinitionsFor(config)) {\n await this.extendObjectAsync(`${id}.${def.id}`, { type: 'state', common: def.common, native: {} });\n }\n for (const btn of KEY_BUTTONS) {\n await this.extendObjectAsync(`${id}.keys.${btn.id}`, {\n type: 'state',\n common: { name: btn.name, type: 'boolean', role: btn.role, read: false, write: true },\n native: { key: btn.key },\n });\n }\n for (const [appId, app] of this.appsById) {\n await this.extendObjectAsync(`${id}.apps.${appId}`, {\n type: 'state',\n common: { name: app.name, type: 'boolean', role: 'button', read: false, write: true },\n native: { link: app.link },\n });\n }\n }\n\n /**\n * Deletes devices, states and channels that no longer apply (device removed, app removed, mode changed).\n *\n * @param deviceIds - Ids of all configured devices\n * @param configs - Device configurations (same order as ids were assigned)\n */\n private async removeStaleObjects(deviceIds: Set<string>, configs: DeviceConfig[]): Promise<void> {\n const prefix = `${this.namespace}.`;\n const range = (start: string): { startkey: string; endkey: string } => ({\n startkey: start,\n endkey: `${start}\u9999`,\n });\n const devices = await this.getObjectViewAsync('system', 'device', range(prefix));\n for (const row of devices.rows) {\n const id = row.id.slice(prefix.length);\n if (!deviceIds.has(id)) {\n this.log.info(`Removing objects of device \"${id}\" (no longer configured)`);\n await this.delObjectAsync(id, { recursive: true });\n }\n }\n const ids = [...deviceIds];\n for (let i = 0; i < ids.length; i++) {\n const id = ids[i];\n const config = configs[i];\n const expected = new Set<string>([\n ...this.stateDefinitionsFor(config).map(d => d.id),\n ...KEY_BUTTONS.map(b => `keys.${b.id}`),\n ...[...this.appsById.keys()].map(a => `apps.${a}`),\n ]);\n const devPrefix = `${prefix}${id}.`;\n const states = await this.getObjectViewAsync('system', 'state', range(devPrefix));\n for (const row of states.rows) {\n const rel = row.id.slice(devPrefix.length);\n if (!expected.has(rel)) {\n await this.delObjectAsync(row.id.slice(prefix.length));\n }\n }\n if (modeOf(config) === 'remote') {\n const media = await this.getObjectAsync(`${id}.media`);\n if (media) {\n await this.delObjectAsync(`${id}.media`);\n }\n }\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Commands\n // ------------------------------------------------------------------------------------------------------------\n\n private onStateChange(fullId: string, state: ioBroker.State | null | undefined): void {\n if (!state || state.ack) {\n return;\n }\n const parts = fullId.slice(this.namespace.length + 1).split('.');\n const device = this.devices.get(parts[0]);\n if (!device) {\n return;\n }\n const command = parts.slice(1).join('.');\n this.handleCommand(device, command, state).catch((e: Error) =>\n this.log.warn(`${device.config.name || device.id}: command \"${command}\" failed: ${e.message}`),\n );\n }\n\n /**\n * Executes a user command (ack=false) on a device, using Remote v2 where possible and ADB otherwise.\n *\n * @param device - Target device\n * @param command - State id relative to the device\n * @param state - New state\n */\n private async handleCommand(device: DeviceRuntime, command: string, state: ioBroker.State): Promise<void> {\n const ack = (): void => void this.setState(`${device.id}.${command}`, { val: state.val, ack: true });\n\n if (command.startsWith('keys.')) {\n const btn = KEY_BUTTONS.find(b => b.id === command.slice(5));\n if (btn) {\n await this.sendKey(device, btn.key);\n ack();\n }\n return;\n }\n if (command.startsWith('apps.')) {\n const app = this.appsById.get(command.slice(5));\n if (app) {\n await this.launch(device, app.link);\n ack();\n }\n return;\n }\n switch (command) {\n case 'power':\n await this.setPower(device, !!state.val, ack);\n return;\n case 'muted':\n if (device.muted === !!state.val) {\n ack();\n } else {\n await this.sendKey(device, 'VOLUME_MUTE');\n if (!device.remote?.isReady) {\n ack(); // ADB has no mute feedback\n }\n }\n return;\n case 'launchApp':\n if (typeof state.val === 'string' && state.val.trim()) {\n await this.launch(device, state.val);\n ack();\n }\n return;\n case 'sendKey': {\n const key = state.val === null || state.val === undefined ? '' : String(state.val);\n if (resolveKeyCode(key) === undefined) {\n throw new Error(`unknown key \"${key}\"`);\n }\n await this.sendKey(device, key);\n ack();\n return;\n }\n case 'sendText':\n if (typeof state.val === 'string' && state.val !== '') {\n if (device.remote?.isReady && device.config.enableIme !== false) {\n device.remote.sendText(state.val);\n } else if (device.adb?.isReady) {\n await device.adb.sendText(state.val);\n } else {\n throw new Error('not connected');\n }\n ack();\n }\n return;\n default:\n this.log.debug(`Ignoring write to read-only or unknown state ${device.id}.${command}`);\n }\n }\n\n private async sendKey(device: DeviceRuntime, key: string): Promise<void> {\n if (device.remote?.isReady) {\n device.remote.sendKey(key);\n device.adb?.pollSoon(); // playback info reflects play/pause about 1 s later instead of up to one interval\n } else if (device.adb?.isReady) {\n await device.adb.sendKey(key);\n } else {\n throw new Error('not connected');\n }\n }\n\n /**\n * Starts an app: https deep links preferably via Remote v2, package names preferably via ADB.\n *\n * @param device - Target device\n * @param target - Deep link or package name\n */\n private async launch(device: DeviceRuntime, target: string): Promise<void> {\n // @KI_HINWEIS: Feldtest SHIELD: Remote v2 akzeptiert nur http(s)-Links, Paketnamen nur per ADB (monkey).\n const preferAdb = !isLink(target);\n if (preferAdb && device.adb?.isReady) {\n await device.adb.launch(target);\n } else if (device.remote?.isReady) {\n device.remote.launchApp(target);\n device.adb?.pollSoon();\n } else if (device.adb?.isReady) {\n await device.adb.launch(target);\n } else {\n throw new Error('not connected');\n }\n }\n\n /**\n * Switches the device on/off. Remote v2 only knows the POWER toggle, ADB has idempotent WAKEUP/SLEEP.\n * The ack is set by the state feedback of the device (remote_start or ADB poll).\n *\n * @param device - Target device\n * @param wanted - Desired state\n * @param ack - Acknowledges the command immediately\n */\n private async setPower(device: DeviceRuntime, wanted: boolean, ack: () => void): Promise<void> {\n if (device.remote?.isReady) {\n // @KI_HINWEIS: Nur senden, wenn der vom TV gemeldete Zustand abweicht (Toggle-Taste).\n if (device.remote.isOn === wanted) {\n ack();\n } else {\n device.remote.sendKey('POWER');\n }\n } else if (device.adb?.isReady) {\n device.powerTarget = { value: wanted, until: Date.now() + 5_000 };\n await device.adb.setPower(wanted);\n } else if (wanted) {\n await this.wakeOnLan(device);\n } else {\n throw new Error('not connected');\n }\n }\n\n /**\n * Wakes an unreachable device (deep standby without network) with a magic packet and reconnects right away.\n *\n * @param device - Device\n */\n private async wakeOnLan(device: DeviceRuntime): Promise<void> {\n // @KI_HINWEIS: Feldtest Fire TV: Im tiefen Energiesparmodus schaltet das Ger\u00E4t das Netzwerk ab, ADB und\n // Remote sind nicht erreichbar. Viele Ger\u00E4te reagieren dann noch auf Wake-on-LAN (am Ger\u00E4t einschalten).\n // @KI_HINWEIS: Die MAC l\u00E4sst sich \u00FCber ADB nicht zuverl\u00E4ssig lesen (Fire TV/SHIELD: /sys gesperrt, ip link\n // leer) und das TV-Zertifikat enth\u00E4lt ggf. die WLAN-MAC. Deshalb nur die MAC aus der Konfiguration.\n const mac = device.config.wolMac?.trim();\n if (!mac) {\n throw new Error(\n 'not connected. To switch it on from deep standby, enter its MAC address in the instance settings (Wake-on-LAN)',\n );\n }\n const targets = wolTargets(device.config.host.trim(), device.config.wolBroadcast);\n const sent = await sendWakeOnLan(mac, targets);\n if (!sent) {\n throw new Error(`Wake-on-LAN could not be sent (${targets.join(', ')})`);\n }\n this.log.info(`${device.label}: not reachable, sent Wake-on-LAN to ${mac}`);\n device.wakePendingUntil = Date.now() + 90_000;\n device.remote?.reconnectNow();\n device.adb?.reconnectNow();\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Admin messages (pairing, status)\n // ------------------------------------------------------------------------------------------------------------\n\n private text(key: MessageKey): string {\n return MESSAGES[key][this.uiLanguage];\n }\n\n private reply(obj: ioBroker.Message, payload: { result: string } | { error: string } | StatusText): void {\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, payload, obj.callback);\n }\n }\n\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n if (!obj || typeof obj !== 'object') {\n return;\n }\n const msg = (obj.message ?? {}) as { host?: unknown; pin?: unknown; enabled?: unknown };\n const host = typeof msg.host === 'string' ? msg.host.trim() : '';\n try {\n switch (obj.command) {\n case 'startPairing':\n await this.startPairing(host);\n this.reply(obj, { result: this.text('pairingStarted') });\n break;\n case 'finishPairing': {\n const configured = await this.finishPairing(host, typeof msg.pin === 'string' ? msg.pin : '');\n this.reply(obj, { result: this.text(configured ? 'pairingDone' : 'pairingDoneSave') });\n break;\n }\n case 'deviceStatus':\n this.reply(obj, this.deviceStatus(host, msg.enabled !== false));\n return;\n default:\n this.log.debug(`Unknown message command \"${obj.command}\"`);\n }\n } catch (e) {\n const key: MessageKey =\n e instanceof PairingError\n ? e.code\n : e instanceof Error && e.message in MESSAGES\n ? (e.message as MessageKey)\n : 'closed';\n this.log.warn(`Pairing with ${host || '?'} failed: ${(e as Error).message}`);\n this.reply(obj, { error: this.text(key) });\n }\n }\n\n /**\n * Status line for one device in the admin UI (`textSendTo`).\n *\n * @param host - Host as currently entered in the form (may be unsaved)\n * @param enabled - Checkbox value in the form\n */\n private deviceStatus(host: string, enabled: boolean): StatusText {\n const grey = { color: '#888888' };\n if (!host) {\n return { text: this.text('statusNoHost'), icon: 'connection', style: grey };\n }\n if (!enabled) {\n return { text: this.text('statusDisabled'), icon: 'no-connection', style: grey };\n }\n const device = [...this.devices.values()].find(d => d.config.host.trim() === host);\n if (!device) {\n return { text: this.text('statusUnsaved'), icon: 'pair', style: grey };\n }\n return this.statusOf(device);\n }\n\n /**\n * Current status of a running device (admin status line and state `info.status`).\n *\n * @param device - Device runtime\n */\n private statusOf(device: DeviceRuntime): StatusText {\n const green = { color: '#2e7d32' };\n const orange = { color: '#ed6c02' };\n const red = { color: '#d32f2f' };\n const adbPart = (): { key: MessageKey; style: { color: string } } =>\n device.adbConnected\n ? { key: 'statusAdbOk', style: green }\n : device.adbAuthPending\n ? { key: 'statusAdbPending', style: orange }\n : { key: device.mode === 'adb' ? 'statusAdbOffline' : 'statusInfoOffline', style: red };\n if (device.mode === 'adb') {\n const a = adbPart();\n return {\n text: this.text(a.key === 'statusAdbOk' ? 'statusConnected' : a.key),\n icon: device.adbConnected ? 'connection' : 'no-connection',\n style: a.style,\n };\n }\n let main: StatusText;\n if (device.remoteConnected) {\n main = { text: this.text('statusConnected'), icon: 'connection', style: green };\n } else if (device.pairingRequired) {\n main = { text: this.text('statusPairing'), icon: 'pair', style: orange };\n } else {\n main = { text: this.text('statusOffline'), icon: 'no-connection', style: red };\n }\n if (device.mode === 'remote_adb') {\n const a = adbPart();\n main.text = `${main.text} \u00B7 ${this.text(a.key)}`;\n if (main.style === green && a.style !== green) {\n main.style = a.style;\n }\n }\n return main;\n }\n\n /**\n * Writes the human readable status to `<device>.info.status` (shown live in the admin UI).\n *\n * @param device - Device runtime\n */\n private async refreshStatus(device: DeviceRuntime): Promise<void> {\n await this.setStateChangedAsync(`${device.id}.info.status`, this.statusOf(device).text, true);\n }\n\n private async startPairing(host: string): Promise<void> {\n if (!host) {\n throw new Error('noHost');\n }\n if (!this.certificate) {\n throw new Error('noCertificate');\n }\n this.dropPairing(host);\n const session = new PairingSession({\n timers: this.adapterTimers,\n host,\n clientName: this.config.clientName?.trim() || 'ioBroker',\n certificate: this.certificate,\n });\n const pending: PendingPairing = { session, timer: undefined };\n this.pairings.set(host, pending);\n try {\n await session.start();\n } catch (e) {\n this.dropPairing(host);\n throw e;\n }\n pending.timer = this.setTimeout(() => this.dropPairing(host), PAIRING_SESSION_TTL_MS);\n }\n\n /**\n * Completes pairing and reconnects matching devices.\n *\n * @param host - TV address\n * @param pin - Code shown on the TV\n * @returns true if a running device uses this host\n */\n private async finishPairing(host: string, pin: string): Promise<boolean> {\n if (!host) {\n throw new Error('noHost');\n }\n const pending = this.pairings.get(host);\n if (!pending?.session.isOpen) {\n this.dropPairing(host);\n throw new Error('noSession');\n }\n try {\n await pending.session.finish(pin);\n } catch (e) {\n // Wrong/mistyped code: the TV keeps the session open, the user may retry.\n if (!(e instanceof PairingError && (e.code === 'wrongPin' || e.code === 'invalidPin'))) {\n this.dropPairing(host);\n }\n throw e;\n }\n this.dropPairing(host);\n this.log.info(`Pairing with ${host} successful`);\n let configured = false;\n for (const device of this.devices.values()) {\n if (device.config.host.trim() === host && device.remote) {\n configured = true;\n device.pairingRequired = false;\n await this.setStateChangedAsync(`${device.id}.info.pairingRequired`, false, true);\n device.remote.restart();\n }\n }\n return configured;\n }\n\n private dropPairing(host: string): void {\n const pending = this.pairings.get(host);\n if (pending) {\n this.clearTimeout(pending.timer);\n pending.session.close();\n this.pairings.delete(host);\n }\n }\n\n private onUnload(callback: () => void): void {\n try {\n for (const host of [...this.pairings.keys()]) {\n this.dropPairing(host);\n }\n for (const device of this.devices.values()) {\n device.remote?.stop();\n device.remote?.removeAllListeners();\n device.adb?.stop();\n device.adb?.removeAllListeners();\n }\n this.devices.clear();\n callback();\n } catch (error) {\n this.log.error(`Error during unloading: ${(error as Error).message}`);\n callback();\n }\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new Androidtv(options);\n} else {\n // otherwise start the instance directly\n (() => new Androidtv())();\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/*\n * ioBroker.androidtv - control Android TV / Google TV devices (e.g. NVIDIA SHIELD) via the Android TV Remote\n * protocol v2 and optionally ADB (media information, app start by package, Fire TV).\n */\nimport * as utils from '@iobroker/adapter-core';\nimport { generateAdbKey, type AdbKey } from './lib/adb/auth';\nimport { DEFAULT_ADB_PORT } from './lib/adb/client';\nimport { AdbDevice } from './lib/adb/device';\nimport type { MediaInfo, PollResult } from './lib/adb/parsers';\nimport {\n generateClientCertificate,\n isUsableCertificate,\n parseTvIdentity,\n type ClientCertificate,\n} from './lib/certificate';\nimport { PairingError, PairingSession } from './lib/pairing';\nimport { RemoteConnection, resolveKeyCode } from './lib/remote';\nimport { ADB_STATES, DEVICE_STATES, KEY_BUTTONS, type StateDefinition } from './lib/states';\nimport type { Timers } from './lib/timers';\nimport { sendWakeOnLan, wolTargets } from './lib/wol';\n\ntype DeviceConfig = ioBroker.AdapterConfig['devices'][number];\ntype AppConfig = ioBroker.AdapterConfig['apps'][number];\ntype ConnectionMode = 'remote' | 'remote_adb' | 'adb';\n\n/** Runtime data of one configured TV. */\ninterface DeviceRuntime {\n id: string;\n config: DeviceConfig;\n mode: ConnectionMode;\n label: string;\n remote?: RemoteConnection;\n adb?: AdbDevice;\n remoteConnected: boolean;\n adbConnected: boolean;\n adbAuthPending: boolean;\n pairingRequired: boolean;\n muted: boolean | undefined;\n /** Pending ADB power command: contradicting poll results are ignored until it is reached or expires */\n powerTarget?: { value: boolean; until: number };\n /** Started when the primary connection is lost; resets power/media if the device stays away */\n offlineTimer?: ioBroker.Timeout;\n /** Wake-on-LAN was sent: switch on as soon as ADB is back (until this time) */\n wakePendingUntil?: number;\n}\n\n/** Answer for the admin `textSendTo` status line. */\ninterface StatusText {\n text: string;\n icon: string;\n style: { color: string };\n}\n\n/** Open pairing attempt (between \"start\" and \"finish\"). */\ninterface PendingPairing {\n session: PairingSession;\n timer: ioBroker.Timeout | undefined;\n}\n\n/** Pairing sessions are dropped after this time; the TV itself cancels after about a minute. */\nconst PAIRING_SESSION_TTL_MS = 120_000;\nconst DEFAULT_RECONNECT_MAX_S = 60;\nconst DEFAULT_ADB_POLL_S = 5;\nconst CERT_FILE = 'client-certificate.json';\nconst ADB_KEY_FILE = 'adb-key.json';\n/** A device that stays unreachable this long is considered off (power=false, media idle). */\nconst OFFLINE_GRACE_MS = 30_000;\n\n/**\n * Texts returned to the admin UI (sendTo) and written to `<device>.info.status`, in all admin languages.\n * The system language picks the text; English is the fallback.\n */\nconst MESSAGES = {\n pairingStarted: {\n en: 'The TV now shows a 6-character code. Enter it and click \"Confirm code\".',\n de: 'Der TV zeigt jetzt einen 6-stelligen Code. Code eingeben und \u201ECode best\u00E4tigen\u201C klicken.',\n ru: '\u0422\u0435\u043B\u0435\u0432\u0438\u0437\u043E\u0440 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 6-\u0437\u043D\u0430\u0447\u043D\u044B\u0439 \u043A\u043E\u0434. \u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0435\u0433\u043E \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u00AB\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u043A\u043E\u0434\u00BB.',\n pt: 'A TV mostra agora um c\u00F3digo de 6 caracteres. Introduza-o e clique em \"Confirmar c\u00F3digo\".',\n nl: 'De tv toont nu een code van 6 tekens. Voer deze in en klik op \"Code bevestigen\".',\n fr: 'Le t\u00E9l\u00E9viseur affiche maintenant un code \u00E0 6 caract\u00E8res. Saisissez-le et cliquez sur \u00AB Confirmer le code \u00BB.',\n it: 'La TV mostra ora un codice di 6 caratteri. Inseriscilo e fai clic su \"Conferma codice\".',\n es: 'La TV muestra ahora un c\u00F3digo de 6 caracteres. Introd\u00FAzcalo y haga clic en \"Confirmar c\u00F3digo\".',\n pl: 'Telewizor wy\u015Bwietla teraz 6-znakowy kod. Wpisz go i kliknij \u201EPotwierd\u017A kod\u201D.',\n uk: '\u0422\u0435\u043B\u0435\u0432\u0456\u0437\u043E\u0440 \u043F\u043E\u043A\u0430\u0437\u0443\u0454 6-\u0437\u043D\u0430\u0447\u043D\u0438\u0439 \u043A\u043E\u0434. \u0412\u0432\u0435\u0434\u0456\u0442\u044C \u0439\u043E\u0433\u043E \u0442\u0430 \u043D\u0430\u0442\u0438\u0441\u043D\u0456\u0442\u044C \u00AB\u041F\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0438 \u043A\u043E\u0434\u00BB.',\n 'zh-cn': '\u7535\u89C6\u73B0\u5728\u663E\u793A\u4E00\u4E2A 6 \u4F4D\u4EE3\u7801\u3002\u8BF7\u8F93\u5165\u4EE3\u7801\u5E76\u70B9\u51FB\u201C\u786E\u8BA4\u4EE3\u7801\u201D\u3002',\n },\n pairingDone: {\n en: 'Pairing successful. The connection is being established.',\n de: 'Kopplung erfolgreich. Die Verbindung wird aufgebaut.',\n ru: '\u0421\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u043E. \u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435.',\n pt: 'Emparelhamento conclu\u00EDdo. A liga\u00E7\u00E3o est\u00E1 a ser estabelecida.',\n nl: 'Koppelen gelukt. De verbinding wordt opgebouwd.',\n fr: 'Appairage r\u00E9ussi. La connexion est en cours d\u2019\u00E9tablissement.',\n it: 'Associazione riuscita. La connessione \u00E8 in corso.',\n es: 'Emparejamiento correcto. Se est\u00E1 estableciendo la conexi\u00F3n.',\n pl: 'Parowanie zako\u0144czone. Trwa nawi\u0105zywanie po\u0142\u0105czenia.',\n uk: '\u0421\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F \u0432\u0438\u043A\u043E\u043D\u0430\u043D\u043E. \u0412\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u044E\u0454\u0442\u044C\u0441\u044F \u0437\u2019\u0454\u0434\u043D\u0430\u043D\u043D\u044F.',\n 'zh-cn': '\u914D\u5BF9\u6210\u529F\uFF0C\u6B63\u5728\u5EFA\u7ACB\u8FDE\u63A5\u3002',\n },\n pairingDoneSave: {\n en: 'Pairing successful. Save the settings to connect to this TV.',\n de: 'Kopplung erfolgreich. Einstellungen speichern, damit die Verbindung aufgebaut wird.',\n ru: '\u0421\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u043E. \u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043A \u0442\u0435\u043B\u0435\u0432\u0438\u0437\u043E\u0440\u0443.',\n pt: 'Emparelhamento conclu\u00EDdo. Guarde as defini\u00E7\u00F5es para ligar a esta TV.',\n nl: 'Koppelen gelukt. Sla de instellingen op om met deze tv te verbinden.',\n fr: 'Appairage r\u00E9ussi. Enregistrez les param\u00E8tres pour vous connecter \u00E0 ce t\u00E9l\u00E9viseur.',\n it: 'Associazione riuscita. Salva le impostazioni per connetterti a questa TV.',\n es: 'Emparejamiento correcto. Guarde los ajustes para conectarse a esta TV.',\n pl: 'Parowanie zako\u0144czone. Zapisz ustawienia, aby po\u0142\u0105czy\u0107 si\u0119 z telewizorem.',\n uk: '\u0421\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F \u0432\u0438\u043A\u043E\u043D\u0430\u043D\u043E. \u0417\u0431\u0435\u0440\u0435\u0436\u0456\u0442\u044C \u043D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u043D\u044F, \u0449\u043E\u0431 \u043F\u0456\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u0438\u0441\u044F \u0434\u043E \u0442\u0435\u043B\u0435\u0432\u0456\u0437\u043E\u0440\u0430.',\n 'zh-cn': '\u914D\u5BF9\u6210\u529F\u3002\u8BF7\u4FDD\u5B58\u8BBE\u7F6E\u4EE5\u8FDE\u63A5\u5230\u6B64\u7535\u89C6\u3002',\n },\n statusConnected: {\n en: 'Connected',\n de: 'Verbunden',\n ru: '\u041F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043E',\n pt: 'Conectado',\n nl: 'Verbonden',\n fr: 'Connect\u00E9',\n it: 'Connesso',\n es: 'Conectado',\n pl: 'Po\u0142\u0105czono',\n uk: '\u041F\u0456\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043E',\n 'zh-cn': '\u5DF2\u8FDE\u63A5',\n },\n statusPairing: {\n en: 'Pairing required \u2013 switch the device on and follow step 1',\n de: 'Kopplung erforderlich \u2013 Ger\u00E4t einschalten und Schritt 1 ausf\u00FChren',\n ru: '\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435 \u2013 \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E \u0438 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u0448\u0430\u0433 1',\n pt: 'Emparelhamento necess\u00E1rio \u2013 ligue o dispositivo e siga o passo 1',\n nl: 'Koppelen vereist \u2013 zet het apparaat aan en volg stap 1',\n fr: 'Appairage requis \u2013 allumez l\u2019appareil et suivez l\u2019\u00E9tape 1',\n it: 'Associazione necessaria \u2013 accendi il dispositivo e segui il passo 1',\n es: 'Emparejamiento necesario \u2013 encienda el dispositivo y siga el paso 1',\n pl: 'Wymagane parowanie \u2013 w\u0142\u0105cz urz\u0105dzenie i wykonaj krok 1',\n uk: '\u041F\u043E\u0442\u0440\u0456\u0431\u043D\u0435 \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F \u2013 \u0443\u0432\u0456\u043C\u043A\u043D\u0456\u0442\u044C \u043F\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0456 \u0432\u0438\u043A\u043E\u043D\u0430\u0439\u0442\u0435 \u043A\u0440\u043E\u043A 1',\n 'zh-cn': '\u9700\u8981\u914D\u5BF9 \u2013 \u8BF7\u6253\u5F00\u8BBE\u5907\u5E76\u6309\u7167\u7B2C 1 \u6B65\u64CD\u4F5C',\n },\n statusOffline: {\n en: 'Not connected \u2013 device switched off, unplugged or unreachable',\n de: 'Nicht verbunden \u2013 Ger\u00E4t aus, ohne Strom oder nicht erreichbar',\n ru: '\u041D\u0435\u0442 \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F \u2013 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u043E\u0431\u0435\u0441\u0442\u043E\u0447\u0435\u043D\u043E \u0438\u043B\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E',\n pt: 'Sem liga\u00E7\u00E3o \u2013 dispositivo desligado, sem corrente ou inacess\u00EDvel',\n nl: 'Niet verbonden \u2013 apparaat uit, zonder stroom of onbereikbaar',\n fr: 'Non connect\u00E9 \u2013 appareil \u00E9teint, d\u00E9branch\u00E9 ou injoignable',\n it: 'Non connesso \u2013 dispositivo spento, scollegato o non raggiungibile',\n es: 'Sin conexi\u00F3n \u2013 dispositivo apagado, desenchufado o inaccesible',\n pl: 'Brak po\u0142\u0105czenia \u2013 urz\u0105dzenie wy\u0142\u0105czone, bez zasilania lub nieosi\u0105galne',\n uk: '\u041D\u0435\u043C\u0430\u0454 \u0437\u2019\u0454\u0434\u043D\u0430\u043D\u043D\u044F \u2013 \u043F\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0438\u043C\u043A\u043D\u0435\u043D\u043E, \u0437\u043D\u0435\u0441\u0442\u0440\u0443\u043C\u043B\u0435\u043D\u043E \u0430\u0431\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0438\u0439',\n 'zh-cn': '\u672A\u8FDE\u63A5 \u2013 \u8BBE\u5907\u5DF2\u5173\u95ED\u3001\u65AD\u7535\u6216\u65E0\u6CD5\u8BBF\u95EE',\n },\n statusAdbPending: {\n en: 'Please confirm \"Allow debugging\" on the TV (tick \"Always allow\")',\n de: 'Bitte am Fernseher \u201EDebugging zulassen\u201C best\u00E4tigen (\u201EImmer zulassen\u201C anhaken)',\n ru: '\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u00AB\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043E\u0442\u043B\u0430\u0434\u043A\u0443\u00BB \u043D\u0430 \u0442\u0435\u043B\u0435\u0432\u0438\u0437\u043E\u0440\u0435 (\u043E\u0442\u043C\u0435\u0442\u044C\u0442\u0435 \u00AB\u0412\u0441\u0435\u0433\u0434\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0442\u044C\u00BB)',\n pt: 'Confirme \"Permitir depura\u00E7\u00E3o\" na TV (assinale \"Permitir sempre\")',\n nl: 'Bevestig \"Foutopsporing toestaan\" op de tv (vink \"Altijd toestaan\" aan)',\n fr: 'Confirmez \u00AB Autoriser le d\u00E9bogage \u00BB sur le t\u00E9l\u00E9viseur (cochez \u00AB Toujours autoriser \u00BB)',\n it: 'Conferma \"Consenti debug\" sulla TV (spunta \"Consenti sempre\")',\n es: 'Confirme \"Permitir depuraci\u00F3n\" en la TV (marque \"Permitir siempre\")',\n pl: 'Potwierd\u017A \u201EZezw\u00F3l na debugowanie\u201D na telewizorze (zaznacz \u201EZawsze zezwalaj\u201D)',\n uk: '\u041F\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u044C\u0442\u0435 \u00AB\u0414\u043E\u0437\u0432\u043E\u043B\u0438\u0442\u0438 \u043D\u0430\u043B\u0430\u0433\u043E\u0434\u0436\u0435\u043D\u043D\u044F\u00BB \u043D\u0430 \u0442\u0435\u043B\u0435\u0432\u0456\u0437\u043E\u0440\u0456 (\u043F\u043E\u0437\u043D\u0430\u0447\u0442\u0435 \u00AB\u0417\u0430\u0432\u0436\u0434\u0438 \u0434\u043E\u0437\u0432\u043E\u043B\u044F\u0442\u0438\u00BB)',\n 'zh-cn': '\u8BF7\u5728\u7535\u89C6\u4E0A\u786E\u8BA4\u201C\u5141\u8BB8\u8C03\u8BD5\u201D\uFF08\u52FE\u9009\u201C\u59CB\u7EC8\u5141\u8BB8\u201D\uFF09',\n },\n statusAdbOffline: {\n en: 'Not connected \u2013 device switched off or debugging not enabled',\n de: 'Nicht verbunden \u2013 Ger\u00E4t aus oder Debugging am Ger\u00E4t nicht eingeschaltet',\n ru: '\u041D\u0435\u0442 \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F \u2013 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0438\u043B\u0438 \u043E\u0442\u043B\u0430\u0434\u043A\u0430 \u043D\u0435 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0430',\n pt: 'Sem liga\u00E7\u00E3o \u2013 dispositivo desligado ou depura\u00E7\u00E3o n\u00E3o ativada',\n nl: 'Niet verbonden \u2013 apparaat uit of foutopsporing niet ingeschakeld',\n fr: 'Non connect\u00E9 \u2013 appareil \u00E9teint ou d\u00E9bogage non activ\u00E9',\n it: 'Non connesso \u2013 dispositivo spento o debug non attivato',\n es: 'Sin conexi\u00F3n \u2013 dispositivo apagado o depuraci\u00F3n no activada',\n pl: 'Brak po\u0142\u0105czenia \u2013 urz\u0105dzenie wy\u0142\u0105czone lub debugowanie nie jest w\u0142\u0105czone',\n uk: '\u041D\u0435\u043C\u0430\u0454 \u0437\u2019\u0454\u0434\u043D\u0430\u043D\u043D\u044F \u2013 \u043F\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0438\u043C\u043A\u043D\u0435\u043D\u043E \u0430\u0431\u043E \u043D\u0430\u043B\u0430\u0433\u043E\u0434\u0436\u0435\u043D\u043D\u044F \u043D\u0435 \u0432\u0432\u0456\u043C\u043A\u043D\u0435\u043D\u043E',\n 'zh-cn': '\u672A\u8FDE\u63A5 \u2013 \u8BBE\u5907\u5DF2\u5173\u95ED\u6216\u672A\u542F\u7528\u8C03\u8BD5',\n },\n statusAdbOk: {\n en: 'playback info active',\n de: 'Wiedergabe-Infos aktiv',\n ru: '\u0441\u0432\u0435\u0434\u0435\u043D\u0438\u044F \u043E \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0434\u0435\u043D\u0438\u0438 \u0430\u043A\u0442\u0438\u0432\u043D\u044B',\n pt: 'informa\u00E7\u00F5es de reprodu\u00E7\u00E3o ativas',\n nl: 'afspeelinfo actief',\n fr: 'infos de lecture actives',\n it: 'info di riproduzione attive',\n es: 'informaci\u00F3n de reproducci\u00F3n activa',\n pl: 'informacje o odtwarzaniu aktywne',\n uk: '\u0432\u0456\u0434\u043E\u043C\u043E\u0441\u0442\u0456 \u043F\u0440\u043E \u0432\u0456\u0434\u0442\u0432\u043E\u0440\u0435\u043D\u043D\u044F \u0430\u043A\u0442\u0438\u0432\u043D\u0456',\n 'zh-cn': '\u64AD\u653E\u4FE1\u606F\u5DF2\u542F\u7528',\n },\n statusInfoOffline: {\n en: 'playback info not available (debugging switched on?)',\n de: 'Wiedergabe-Infos nicht verf\u00FCgbar (Debugging eingeschaltet?)',\n ru: '\u0441\u0432\u0435\u0434\u0435\u043D\u0438\u044F \u043E \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0434\u0435\u043D\u0438\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B (\u043E\u0442\u043B\u0430\u0434\u043A\u0430 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0430?)',\n pt: 'informa\u00E7\u00F5es de reprodu\u00E7\u00E3o indispon\u00EDveis (depura\u00E7\u00E3o ativada?)',\n nl: 'afspeelinfo niet beschikbaar (foutopsporing ingeschakeld?)',\n fr: 'infos de lecture indisponibles (d\u00E9bogage activ\u00E9 ?)',\n it: 'info di riproduzione non disponibili (debug attivato?)',\n es: 'informaci\u00F3n de reproducci\u00F3n no disponible (\u00BFdepuraci\u00F3n activada?)',\n pl: 'informacje o odtwarzaniu niedost\u0119pne (debugowanie w\u0142\u0105czone?)',\n uk: '\u0432\u0456\u0434\u043E\u043C\u043E\u0441\u0442\u0456 \u043F\u0440\u043E \u0432\u0456\u0434\u0442\u0432\u043E\u0440\u0435\u043D\u043D\u044F \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0456 (\u043D\u0430\u043B\u0430\u0433\u043E\u0434\u0436\u0435\u043D\u043D\u044F \u0432\u0432\u0456\u043C\u043A\u043D\u0435\u043D\u043E?)',\n 'zh-cn': '\u64AD\u653E\u4FE1\u606F\u4E0D\u53EF\u7528\uFF08\u662F\u5426\u5DF2\u542F\u7528\u8C03\u8BD5\uFF1F\uFF09',\n },\n statusDisabled: {\n en: 'Disabled',\n de: 'Deaktiviert',\n ru: '\u041E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u043E',\n pt: 'Desativado',\n nl: 'Uitgeschakeld',\n fr: 'D\u00E9sactiv\u00E9',\n it: 'Disattivato',\n es: 'Desactivado',\n pl: 'Wy\u0142\u0105czone',\n uk: '\u0412\u0438\u043C\u043A\u043D\u0435\u043D\u043E',\n 'zh-cn': '\u5DF2\u7981\u7528',\n },\n statusUnsaved: {\n en: 'Not saved yet \u2013 pairing already works, the connection starts after saving',\n de: 'Noch nicht gespeichert \u2013 Koppeln geht schon, die Verbindung startet nach dem Speichern',\n ru: '\u0415\u0449\u0451 \u043D\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043E \u2013 \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435 \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442, \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435 \u043D\u0430\u0447\u043D\u0451\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u044F',\n pt: 'Ainda n\u00E3o guardado \u2013 o emparelhamento j\u00E1 funciona, a liga\u00E7\u00E3o come\u00E7a depois de guardar',\n nl: 'Nog niet opgeslagen \u2013 koppelen werkt al, de verbinding start na het opslaan',\n fr: 'Pas encore enregistr\u00E9 \u2013 l\u2019appairage fonctionne d\u00E9j\u00E0, la connexion d\u00E9marre apr\u00E8s l\u2019enregistrement',\n it: 'Non ancora salvato \u2013 l\u2019associazione funziona gi\u00E0, la connessione parte dopo il salvataggio',\n es: 'A\u00FAn no guardado \u2013 el emparejamiento ya funciona, la conexi\u00F3n empieza al guardar',\n pl: 'Jeszcze nie zapisano \u2013 parowanie ju\u017C dzia\u0142a, po\u0142\u0105czenie rozpocznie si\u0119 po zapisaniu',\n uk: '\u0429\u0435 \u043D\u0435 \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u043E \u2013 \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F \u0432\u0436\u0435 \u043F\u0440\u0430\u0446\u044E\u0454, \u0437\u2019\u0454\u0434\u043D\u0430\u043D\u043D\u044F \u043F\u043E\u0447\u043D\u0435\u0442\u044C\u0441\u044F \u043F\u0456\u0441\u043B\u044F \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u043D\u044F',\n 'zh-cn': '\u5C1A\u672A\u4FDD\u5B58 \u2013 \u5DF2\u53EF\u914D\u5BF9\uFF0C\u4FDD\u5B58\u540E\u5F00\u59CB\u8FDE\u63A5',\n },\n statusNoHost: {\n en: 'Enter the IP address first',\n de: 'Zuerst die IP-Adresse eintragen',\n ru: '\u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441',\n pt: 'Introduza primeiro o endere\u00E7o IP',\n nl: 'Voer eerst het IP-adres in',\n fr: 'Saisissez d\u2019abord l\u2019adresse IP',\n it: 'Inserisci prima l\u2019indirizzo IP',\n es: 'Introduzca primero la direcci\u00F3n IP',\n pl: 'Najpierw wpisz adres IP',\n uk: '\u0421\u043F\u043E\u0447\u0430\u0442\u043A\u0443 \u0432\u0432\u0435\u0434\u0456\u0442\u044C IP-\u0430\u0434\u0440\u0435\u0441\u0443',\n 'zh-cn': '\u8BF7\u5148\u8F93\u5165 IP \u5730\u5740',\n },\n noHost: {\n en: 'Please enter the IP address of the TV.',\n de: 'Bitte die IP-Adresse des TV eingeben.',\n ru: '\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0442\u0435\u043B\u0435\u0432\u0438\u0437\u043E\u0440\u0430.',\n pt: 'Introduza o endere\u00E7o IP da TV.',\n nl: 'Voer het IP-adres van de tv in.',\n fr: 'Saisissez l\u2019adresse IP du t\u00E9l\u00E9viseur.',\n it: 'Inserisci l\u2019indirizzo IP della TV.',\n es: 'Introduzca la direcci\u00F3n IP de la TV.',\n pl: 'Wpisz adres IP telewizora.',\n uk: '\u0412\u0432\u0435\u0434\u0456\u0442\u044C IP-\u0430\u0434\u0440\u0435\u0441\u0443 \u0442\u0435\u043B\u0435\u0432\u0456\u0437\u043E\u0440\u0430.',\n 'zh-cn': '\u8BF7\u8F93\u5165\u7535\u89C6\u7684 IP \u5730\u5740\u3002',\n },\n noSession: {\n en: 'No pairing in progress for this IP - click \"Start pairing\" first.',\n de: 'F\u00FCr diese IP l\u00E4uft keine Kopplung - zuerst \u201EKopplung starten\u201C klicken.',\n ru: '\u0414\u043B\u044F \u044D\u0442\u043E\u0433\u043E IP \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435 \u043D\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E \u2013 \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u00AB\u041D\u0430\u0447\u0430\u0442\u044C \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435\u00BB.',\n pt: 'N\u00E3o h\u00E1 emparelhamento em curso para este IP - clique primeiro em \"Iniciar emparelhamento\".',\n nl: 'Geen koppeling bezig voor dit IP - klik eerst op \"Koppelen starten\".',\n fr: 'Aucun appairage en cours pour cette IP - cliquez d\u2019abord sur \u00AB D\u00E9marrer l\u2019appairage \u00BB.',\n it: 'Nessuna associazione in corso per questo IP - fai prima clic su \"Avvia associazione\".',\n es: 'No hay emparejamiento en curso para esta IP - haga clic primero en \"Iniciar emparejamiento\".',\n pl: 'Dla tego IP nie trwa parowanie \u2013 najpierw kliknij \u201ERozpocznij parowanie\u201D.',\n uk: '\u0414\u043B\u044F \u0446\u0456\u0454\u0457 IP \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F \u043D\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E \u2013 \u0441\u043F\u043E\u0447\u0430\u0442\u043A\u0443 \u043D\u0430\u0442\u0438\u0441\u043D\u0456\u0442\u044C \u00AB\u041F\u043E\u0447\u0430\u0442\u0438 \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F\u00BB.',\n 'zh-cn': '\u6B64 IP \u6CA1\u6709\u6B63\u5728\u8FDB\u884C\u7684\u914D\u5BF9 - \u8BF7\u5148\u70B9\u51FB\u201C\u5F00\u59CB\u914D\u5BF9\u201D\u3002',\n },\n noCertificate: {\n en: 'Client certificate not ready yet - wait a few seconds and try again.',\n de: 'Client-Zertifikat noch nicht bereit - ein paar Sekunden warten und erneut versuchen.',\n ru: '\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0435\u0449\u0451 \u043D\u0435 \u0433\u043E\u0442\u043E\u0432 \u2013 \u043F\u043E\u0434\u043E\u0436\u0434\u0438\u0442\u0435 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0435\u043A\u0443\u043D\u0434 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435.',\n pt: 'O certificado do cliente ainda n\u00E3o est\u00E1 pronto - aguarde alguns segundos e tente novamente.',\n nl: 'Clientcertificaat nog niet klaar - wacht enkele seconden en probeer het opnieuw.',\n fr: 'Le certificat client n\u2019est pas encore pr\u00EAt - attendez quelques secondes et r\u00E9essayez.',\n it: 'Il certificato client non \u00E8 ancora pronto - attendi qualche secondo e riprova.',\n es: 'El certificado de cliente a\u00FAn no est\u00E1 listo - espere unos segundos y vuelva a intentarlo.',\n pl: 'Certyfikat klienta nie jest jeszcze gotowy \u2013 odczekaj kilka sekund i spr\u00F3buj ponownie.',\n uk: '\u0421\u0435\u0440\u0442\u0438\u0444\u0456\u043A\u0430\u0442 \u043A\u043B\u0456\u0454\u043D\u0442\u0430 \u0449\u0435 \u043D\u0435 \u0433\u043E\u0442\u043E\u0432\u0438\u0439 \u2013 \u0437\u0430\u0447\u0435\u043A\u0430\u0439\u0442\u0435 \u043A\u0456\u043B\u044C\u043A\u0430 \u0441\u0435\u043A\u0443\u043D\u0434 \u0456 \u043F\u043E\u0432\u0442\u043E\u0440\u0456\u0442\u044C.',\n 'zh-cn': '\u5BA2\u6237\u7AEF\u8BC1\u4E66\u5C1A\u672A\u5C31\u7EEA - \u8BF7\u7B49\u5F85\u51E0\u79D2\u540E\u91CD\u8BD5\u3002',\n },\n connect: {\n en: 'The device cannot be reached for pairing. Check the IP address and that the device is switched on. For a Fire TV choose the device type \"Amazon Fire TV\" \u2013 it needs no pairing.',\n de: 'Das Ger\u00E4t ist zum Koppeln nicht erreichbar. IP-Adresse pr\u00FCfen und ob das Ger\u00E4t eingeschaltet ist. F\u00FCr einen Fire TV den Ger\u00E4tetyp \u201EAmazon Fire TV\u201C w\u00E4hlen \u2013 dort ist keine Kopplung n\u00F6tig.',\n ru: '\u0423\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u0434\u043B\u044F \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u044F. \u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u043B\u0438 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E. \u0414\u043B\u044F Fire TV \u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043F \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430 \u00ABAmazon Fire TV\u00BB \u2013 \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F.',\n pt: 'O dispositivo n\u00E3o est\u00E1 acess\u00EDvel para emparelhamento. Verifique o endere\u00E7o IP e se o dispositivo est\u00E1 ligado. Para um Fire TV escolha o tipo de dispositivo \"Amazon Fire TV\" \u2013 n\u00E3o precisa de emparelhamento.',\n nl: 'Het apparaat is niet bereikbaar om te koppelen. Controleer het IP-adres en of het apparaat aan staat. Kies voor een Fire TV het apparaattype \"Amazon Fire TV\" \u2013 koppelen is dan niet nodig.',\n fr: 'L\u2019appareil est injoignable pour l\u2019appairage. V\u00E9rifiez l\u2019adresse IP et que l\u2019appareil est allum\u00E9. Pour un Fire TV, choisissez le type d\u2019appareil \u00AB Amazon Fire TV \u00BB \u2013 aucun appairage n\u2019est n\u00E9cessaire.',\n it: 'Il dispositivo non \u00E8 raggiungibile per l\u2019associazione. Controlla l\u2019indirizzo IP e che il dispositivo sia acceso. Per una Fire TV scegli il tipo di dispositivo \"Amazon Fire TV\" \u2013 non serve l\u2019associazione.',\n es: 'No se puede acceder al dispositivo para emparejarlo. Compruebe la direcci\u00F3n IP y que el dispositivo est\u00E9 encendido. Para un Fire TV elija el tipo de dispositivo \"Amazon Fire TV\" \u2013 no necesita emparejamiento.',\n pl: 'Urz\u0105dzenie jest nieosi\u0105galne do parowania. Sprawd\u017A adres IP i czy urz\u0105dzenie jest w\u0142\u0105czone. Dla Fire TV wybierz typ urz\u0105dzenia \u201EAmazon Fire TV\u201D \u2013 parowanie nie jest potrzebne.',\n uk: '\u041F\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0438\u0439 \u0434\u043B\u044F \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F. \u041F\u0435\u0440\u0435\u0432\u0456\u0440\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441\u0443 \u0442\u0430 \u0447\u0438 \u0432\u0432\u0456\u043C\u043A\u043D\u0435\u043D\u043E \u043F\u0440\u0438\u0441\u0442\u0440\u0456\u0439. \u0414\u043B\u044F Fire TV \u0432\u0438\u0431\u0435\u0440\u0456\u0442\u044C \u0442\u0438\u043F \u043F\u0440\u0438\u0441\u0442\u0440\u043E\u044E \u00ABAmazon Fire TV\u00BB \u2013 \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F \u043D\u0435 \u043F\u043E\u0442\u0440\u0456\u0431\u043D\u0435.',\n 'zh-cn':\n '\u65E0\u6CD5\u8FDE\u63A5\u8BBE\u5907\u8FDB\u884C\u914D\u5BF9\u3002\u8BF7\u68C0\u67E5 IP \u5730\u5740\u4EE5\u53CA\u8BBE\u5907\u662F\u5426\u5DF2\u5F00\u542F\u3002Fire TV \u8BF7\u9009\u62E9\u8BBE\u5907\u7C7B\u578B\u201CAmazon Fire TV\u201D\u2013 \u65E0\u9700\u914D\u5BF9\u3002',\n },\n timeout: {\n en: 'The TV did not answer in time.',\n de: 'Der TV hat nicht rechtzeitig geantwortet.',\n ru: '\u0422\u0435\u043B\u0435\u0432\u0438\u0437\u043E\u0440 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0432\u043E\u0432\u0440\u0435\u043C\u044F.',\n pt: 'A TV n\u00E3o respondeu a tempo.',\n nl: 'De tv heeft niet op tijd geantwoord.',\n fr: 'Le t\u00E9l\u00E9viseur n\u2019a pas r\u00E9pondu \u00E0 temps.',\n it: 'La TV non ha risposto in tempo.',\n es: 'La TV no respondi\u00F3 a tiempo.',\n pl: 'Telewizor nie odpowiedzia\u0142 na czas.',\n uk: '\u0422\u0435\u043B\u0435\u0432\u0456\u0437\u043E\u0440 \u043D\u0435 \u0432\u0456\u0434\u043F\u043E\u0432\u0456\u0432 \u0432\u0447\u0430\u0441\u043D\u043E.',\n 'zh-cn': '\u7535\u89C6\u672A\u53CA\u65F6\u54CD\u5E94\u3002',\n },\n closed: {\n en: 'The TV closed the pairing (cancelled or timed out). Start again.',\n de: 'Der TV hat die Kopplung beendet (abgebrochen oder Zeit abgelaufen). Erneut starten.',\n ru: '\u0422\u0435\u043B\u0435\u0432\u0438\u0437\u043E\u0440 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435 (\u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E \u0438\u043B\u0438 \u0438\u0441\u0442\u0435\u043A\u043B\u043E \u0432\u0440\u0435\u043C\u044F). \u041D\u0430\u0447\u043D\u0438\u0442\u0435 \u0437\u0430\u043D\u043E\u0432\u043E.',\n pt: 'A TV terminou o emparelhamento (cancelado ou tempo esgotado). Comece novamente.',\n nl: 'De tv heeft het koppelen be\u00EBindigd (geannuleerd of verlopen). Begin opnieuw.',\n fr: 'Le t\u00E9l\u00E9viseur a mis fin \u00E0 l\u2019appairage (annul\u00E9 ou d\u00E9lai d\u00E9pass\u00E9). Recommencez.',\n it: 'La TV ha terminato l\u2019associazione (annullata o scaduta). Ricomincia.',\n es: 'La TV termin\u00F3 el emparejamiento (cancelado o tiempo agotado). Vuelva a empezar.',\n pl: 'Telewizor zako\u0144czy\u0142 parowanie (anulowano lub up\u0142yn\u0105\u0142 czas). Zacznij od nowa.',\n uk: '\u0422\u0435\u043B\u0435\u0432\u0456\u0437\u043E\u0440 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0432 \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F (\u0441\u043A\u0430\u0441\u043E\u0432\u0430\u043D\u043E \u0430\u0431\u043E \u043C\u0438\u043D\u0443\u0432 \u0447\u0430\u0441). \u041F\u043E\u0447\u043D\u0456\u0442\u044C \u0437\u043D\u043E\u0432\u0443.',\n 'zh-cn': '\u7535\u89C6\u5DF2\u7ED3\u675F\u914D\u5BF9\uFF08\u5DF2\u53D6\u6D88\u6216\u8D85\u65F6\uFF09\u3002\u8BF7\u91CD\u65B0\u5F00\u59CB\u3002',\n },\n rejected: {\n en: 'The TV rejected the pairing.',\n de: 'Der TV hat die Kopplung abgelehnt.',\n ru: '\u0422\u0435\u043B\u0435\u0432\u0438\u0437\u043E\u0440 \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u043B \u0441\u043E\u043F\u0440\u044F\u0436\u0435\u043D\u0438\u0435.',\n pt: 'A TV recusou o emparelhamento.',\n nl: 'De tv heeft het koppelen geweigerd.',\n fr: 'Le t\u00E9l\u00E9viseur a refus\u00E9 l\u2019appairage.',\n it: 'La TV ha rifiutato l\u2019associazione.',\n es: 'La TV rechaz\u00F3 el emparejamiento.',\n pl: 'Telewizor odrzuci\u0142 parowanie.',\n uk: '\u0422\u0435\u043B\u0435\u0432\u0456\u0437\u043E\u0440 \u0432\u0456\u0434\u0445\u0438\u043B\u0438\u0432 \u0441\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044F.',\n 'zh-cn': '\u7535\u89C6\u62D2\u7EDD\u4E86\u914D\u5BF9\u3002',\n },\n invalidPin: {\n en: 'The code must have 6 characters (0-9, A-F).',\n de: 'Der Code muss 6 Zeichen haben (0-9, A-F).',\n ru: '\u041A\u043E\u0434 \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C 6 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 (0-9, A-F).',\n pt: 'O c\u00F3digo deve ter 6 caracteres (0-9, A-F).',\n nl: 'De code moet 6 tekens hebben (0-9, A-F).',\n fr: 'Le code doit comporter 6 caract\u00E8res (0-9, A-F).',\n it: 'Il codice deve avere 6 caratteri (0-9, A-F).',\n es: 'El c\u00F3digo debe tener 6 caracteres (0-9, A-F).',\n pl: 'Kod musi mie\u0107 6 znak\u00F3w (0-9, A-F).',\n uk: '\u041A\u043E\u0434 \u043C\u0430\u0454 \u043C\u0456\u0441\u0442\u0438\u0442\u0438 6 \u0441\u0438\u043C\u0432\u043E\u043B\u0456\u0432 (0-9, A-F).',\n 'zh-cn': '\u4EE3\u7801\u5FC5\u987B\u4E3A 6 \u4E2A\u5B57\u7B26\uFF080-9\u3001A-F\uFF09\u3002',\n },\n wrongPin: {\n en: 'The code does not match. Please check it and try again.',\n de: 'Der Code passt nicht. Bitte pr\u00FCfen und erneut eingeben.',\n ru: '\u041A\u043E\u0434 \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442. \u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u0435\u0433\u043E \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435.',\n pt: 'O c\u00F3digo n\u00E3o corresponde. Verifique-o e tente novamente.',\n nl: 'De code klopt niet. Controleer hem en probeer het opnieuw.',\n fr: 'Le code ne correspond pas. V\u00E9rifiez-le et r\u00E9essayez.',\n it: 'Il codice non corrisponde. Controllalo e riprova.',\n es: 'El c\u00F3digo no coincide. Compru\u00E9belo y vuelva a intentarlo.',\n pl: 'Kod si\u0119 nie zgadza. Sprawd\u017A go i spr\u00F3buj ponownie.',\n uk: '\u041A\u043E\u0434 \u043D\u0435 \u0437\u0431\u0456\u0433\u0430\u0454\u0442\u044C\u0441\u044F. \u041F\u0435\u0440\u0435\u0432\u0456\u0440\u0442\u0435 \u0439\u043E\u0433\u043E \u0442\u0430 \u043F\u043E\u0432\u0442\u043E\u0440\u0456\u0442\u044C.',\n 'zh-cn': '\u4EE3\u7801\u4E0D\u5339\u914D\u3002\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002',\n },\n} as const satisfies Record<string, Record<ioBroker.Languages, string>>;\n\ntype MessageKey = keyof typeof MESSAGES;\n\n/**\n * Converts a free text into a valid ioBroker object id part.\n *\n * @param text - e.g. \"Shield Wohnzimmer\"\n * @param forbidden - Adapter FORBIDDEN_CHARS regex\n */\nfunction toId(text: string, forbidden: RegExp): string {\n // @KI_HINWEIS: Strenger als FORBIDDEN_CHARS: nur A-Z, a-z, 0-9, _ und -, damit IDs wie \"Disney+\" in Skripten,\n // MQTT-Topics und Vis ohne Escaping nutzbar sind.\n const umlauts: Record<string, string> = { \u00E4: 'ae', \u00F6: 'oe', \u00FC: 'ue', \u00C4: 'Ae', \u00D6: 'Oe', \u00DC: 'Ue', \u00DF: 'ss' };\n return text\n .trim()\n .replace(/[\u00E4\u00F6\u00FC\u00C4\u00D6\u00DC\u00DF]/g, c => umlauts[c] ?? c)\n .replace(forbidden, '_')\n .replace(/[^A-Za-z0-9_-]+/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n}\n\n/**\n * Validates a numeric setting in code (the limits in the settings page alone are not enough).\n *\n * @param value - Configured value\n * @param min - Smallest allowed value\n * @param max - Largest allowed value\n * @param fallback - Used when the value is missing or outside the range\n */\nfunction numberInRange(value: unknown, min: number, max: number, fallback: number): number {\n const n = Number(value);\n return Number.isFinite(n) && n >= min && n <= max ? Math.round(n) : fallback;\n}\n\n/**\n * Normalizes the configured connection mode (configs of 0.0.1 have none).\n *\n * @param config - Device configuration\n */\nfunction modeOf(config: DeviceConfig): ConnectionMode {\n return config.connection === 'adb' || config.connection === 'remote_adb' ? config.connection : 'remote';\n}\n\n/**\n * Whether a launch target is a URL/deep link (has a scheme) rather than a package name.\n *\n * @param target - Link or package name\n */\nfunction isLink(target: string): boolean {\n return /^[a-z][a-z0-9+.-]*:/i.test(target.trim());\n}\n\nclass Androidtv extends utils.Adapter {\n /** Adapter timers for the protocol classes; cleared automatically on unload. */\n private readonly adapterTimers: Timers = {\n setTimeout: (callback, ms) => this.setTimeout(callback, ms),\n clearTimeout: handle => this.clearTimeout(handle as ioBroker.Timeout | undefined),\n };\n private readonly devices = new Map<string, DeviceRuntime>();\n private readonly appsById = new Map<string, AppConfig>();\n private readonly pairings = new Map<string, PendingPairing>();\n private certificate: ClientCertificate | undefined;\n private adbKey: AdbKey | undefined;\n private uiLanguage: ioBroker.Languages = 'en';\n\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: 'androidtv',\n });\n this.on('ready', this.onReady.bind(this));\n this.on('stateChange', this.onStateChange.bind(this));\n this.on('message', this.onMessage.bind(this));\n this.on('unload', this.onUnload.bind(this));\n }\n\n private async onReady(): Promise<void> {\n await this.setState('info.connection', false, true);\n\n const sysConfig = await this.getForeignObjectAsync('system.config');\n const language = sysConfig?.common?.language;\n this.uiLanguage = language && language in MESSAGES.statusConnected ? language : 'en';\n\n await this.ensureCertificate();\n\n const devices = (Array.isArray(this.config.devices) ? this.config.devices : []).filter(\n d => d && typeof d.host === 'string' && d.host.trim() !== '',\n );\n const apps = (Array.isArray(this.config.apps) ? this.config.apps : []).filter(\n a =>\n a &&\n typeof a.name === 'string' &&\n a.name.trim() !== '' &&\n typeof a.link === 'string' &&\n a.link.trim() !== '',\n );\n const appIdsLower = new Set<string>();\n for (const app of apps) {\n const id = toId(app.name, this.FORBIDDEN_CHARS);\n // IDs must be unique case-insensitively (\"Netflix\" and \"netflix\" would collide)\n if (id && !appIdsLower.has(id.toLowerCase())) {\n appIdsLower.add(id.toLowerCase());\n this.appsById.set(id, app);\n }\n }\n if (devices.some(d => d.enabled !== false && modeOf(d) !== 'remote')) {\n await this.ensureAdbKey();\n }\n\n const usedIds = new Set<string>();\n const usedIdsLower = new Set<string>();\n for (const config of devices) {\n const base =\n toId(config.name || config.host, this.FORBIDDEN_CHARS) || toId(config.host, this.FORBIDDEN_CHARS);\n let id = base;\n // IDs must be unique case-insensitively (\"TV\" and \"tv\" would collide)\n for (let n = 2; usedIdsLower.has(id.toLowerCase()); n++) {\n id = `${base}_${n}`;\n }\n usedIds.add(id);\n usedIdsLower.add(id.toLowerCase());\n await this.createDeviceObjects(id, config);\n if (config.enabled === false) {\n continue;\n }\n const runtime = this.createRuntime(id, config);\n this.devices.set(id, runtime);\n await this.refreshStatus(runtime);\n }\n await this.removeStaleObjects(usedIds, devices);\n\n if (!devices.length) {\n this.log.warn('No devices configured - add your TV in the instance settings.');\n }\n\n this.subscribeStates('*');\n for (const device of this.devices.values()) {\n device.remote?.start();\n device.adb?.start();\n this.scheduleOffline(device); // device unreachable at start-up \u2192 stale power/media are reset\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Keys and certificates (adapter file storage, private keys encrypted with the instance secret)\n // ------------------------------------------------------------------------------------------------------------\n\n /**\n * Loads the client certificate or creates and stores a new one.\n *\n * Storage: adapter file storage `<namespace>/client-certificate.json`, private key encrypted with the\n * instance secret. Older versions kept it in `native` \u2013 it is migrated once.\n */\n private async ensureCertificate(): Promise<void> {\n // @KI_HINWEIS: Nicht (mehr) in native: Eine vor dem ersten Start ge\u00F6ffnete Konfigurationsseite schreibt beim\n // Speichern leere Zertifikatsfelder zur\u00FCck \u2192 neues Zertifikat \u2192 Kopplung am TV verloren (im Feldtest\n // beobachtet). Der Dateispeicher wird vom Admin-Speichern nicht ber\u00FChrt; au\u00DFerdem entf\u00E4llt der Neustart.\n const stored = await this.readSecretFile(CERT_FILE);\n if (typeof stored?.cert === 'string' && typeof stored.key === 'string') {\n const candidate = { cert: stored.cert, key: this.decrypt(stored.key) };\n if (isUsableCertificate(candidate)) {\n this.certificate = candidate;\n return;\n }\n this.log.warn('Stored client certificate is invalid or expired - creating a new one (pairing required)');\n }\n const fromNative = { cert: this.config.clientCert, key: this.config.clientKey };\n if (isUsableCertificate(fromNative)) {\n this.log.info('Moving client certificate from instance configuration to file storage');\n this.certificate = fromNative;\n } else {\n this.log.info('Creating client certificate for pairing (one-time)');\n this.certificate = generateClientCertificate(this.config.clientName?.trim() || 'ioBroker');\n }\n await this.writeSecretFile(CERT_FILE, { cert: this.certificate.cert, key: this.encrypt(this.certificate.key) });\n }\n\n /** Loads or creates the RSA key used for ADB authentication. */\n private async ensureAdbKey(): Promise<void> {\n const stored = await this.readSecretFile(ADB_KEY_FILE);\n if (typeof stored?.key === 'string') {\n try {\n const privateKey = this.decrypt(stored.key);\n if (privateKey.includes('PRIVATE KEY')) {\n this.adbKey = { privateKey };\n return;\n }\n } catch (e) {\n this.log.warn(`Cannot read stored ADB key: ${(e as Error).message}`);\n }\n }\n this.log.info('Creating ADB key (one-time) - confirm \"Allow debugging\" on the TV at the first connection');\n this.adbKey = generateAdbKey();\n await this.writeSecretFile(ADB_KEY_FILE, { key: this.encrypt(this.adbKey.privateKey) });\n }\n\n private async readSecretFile(name: string): Promise<Record<string, unknown> | undefined> {\n try {\n if (!(await this.fileExistsAsync(this.namespace, name))) {\n return undefined;\n }\n const file = await this.readFileAsync(this.namespace, name);\n const raw = typeof file.file === 'string' ? file.file : file.file.toString('utf8');\n const parsed: unknown = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : undefined;\n } catch (e) {\n this.log.warn(`Cannot read ${name}: ${(e as Error).message}`);\n return undefined;\n }\n }\n\n private async writeSecretFile(name: string, content: Record<string, string>): Promise<void> {\n // @KI_HINWEIS: Das meta-Objekt f\u00FCr den Dateispeicher kommt aus instanceObjects (io-package.json).\n await this.writeFileAsync(this.namespace, name, JSON.stringify(content));\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Device runtime\n // ------------------------------------------------------------------------------------------------------------\n\n private createRuntime(id: string, config: DeviceConfig): DeviceRuntime {\n const mode = modeOf(config);\n const reconnectMax = numberInRange(this.config.reconnectMax, 5, 3600, DEFAULT_RECONNECT_MAX_S);\n const runtime: DeviceRuntime = {\n id,\n config,\n mode,\n label: `${config.name || id} (${config.host})`,\n remoteConnected: false,\n adbConnected: false,\n adbAuthPending: false,\n pairingRequired: false,\n muted: undefined,\n };\n if (mode !== 'adb') {\n if (!this.certificate) {\n throw new Error('Certificate missing');\n }\n runtime.remote = new RemoteConnection({\n timers: this.adapterTimers,\n host: config.host.trim(),\n certificate: this.certificate,\n enableIme: config.enableIme !== false,\n reconnectMaxMs: reconnectMax * 1000,\n });\n this.attachRemote(runtime, runtime.remote);\n }\n if (mode !== 'remote') {\n if (!this.adbKey) {\n throw new Error('ADB key missing');\n }\n const pollS = numberInRange(this.config.adbPollInterval, 2, 300, DEFAULT_ADB_POLL_S);\n const port = numberInRange(config.adbPort, 1, 65535, DEFAULT_ADB_PORT);\n runtime.adb = new AdbDevice({\n timers: this.adapterTimers,\n host: config.host.trim(),\n port,\n key: this.adbKey,\n keyName: `ioBroker@${this.host ?? 'iobroker'}`,\n pollIntervalMs: pollS * 1000,\n reconnectMaxMs: reconnectMax * 1000,\n externalPower: () => (runtime.remoteConnected ? runtime.remote?.isOn : undefined),\n });\n this.attachAdb(runtime, runtime.adb);\n }\n return runtime;\n }\n\n private attachRemote(runtime: DeviceRuntime, remote: RemoteConnection): void {\n const { id, label } = runtime;\n remote.on('ready', (info, peerCert) => {\n runtime.remoteConnected = true;\n this.cancelOffline(runtime);\n runtime.pairingRequired = false;\n this.log.info(`${label}: connected`);\n void this.setDeviceStates(id, {\n 'info.connected': true,\n 'info.pairingRequired': false,\n 'info.model': info.model ?? null,\n 'info.manufacturer': info.vendor ?? null,\n 'info.remoteServiceVersion': info.appVersion ?? null,\n });\n if (peerCert) {\n try {\n const { mac } = parseTvIdentity(peerCert);\n // @KI_HINWEIS: Nur zur Info. Feldtest SHIELD: Die MAC im TV-Zertifikat ist die WLAN-MAC, auch\n // wenn das Ger\u00E4t per LAN h\u00E4ngt. F\u00FCr Wake-on-LAN z\u00E4hlt nur die MAC aus der Konfiguration.\n if (mac) {\n void this.setStateChangedAsync(`${id}.info.mac`, mac, true);\n }\n } catch (e) {\n this.log.debug(`${label}: cannot parse TV certificate: ${(e as Error).message}`);\n }\n }\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n remote.on('disconnected', reason => {\n runtime.remoteConnected = false;\n this.scheduleOffline(runtime);\n this.log.debug(`${label}: disconnected (${reason}), reconnecting`);\n void this.setStateChangedAsync(`${id}.info.connected`, false, true);\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n remote.on('unpaired', () => {\n runtime.remoteConnected = false;\n runtime.pairingRequired = true;\n this.log.warn(\n `${label}: the TV does not accept this ioBroker instance. Please pair it in the instance settings (tab \"Devices\").`,\n );\n void this.setDeviceStates(id, { 'info.connected': false, 'info.pairingRequired': true });\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n remote.on('power', on => {\n void this.setStateChangedAsync(`${id}.power`, on, true);\n if (on) {\n runtime.adb?.pollSoon();\n } else if (runtime.adb) {\n void this.setMediaStates(id, null);\n }\n });\n remote.on('currentApp', app => {\n void this.setStateChangedAsync(`${id}.currentApp`, app, true);\n runtime.adb?.pollSoon();\n });\n remote.on('volume', volume => {\n runtime.muted = volume.muted;\n // @KI_HINWEIS: max = 0 meldet z. B. die SHIELD, wenn die Lautst\u00E4rke per HDMI-CEC am TV/AVR geregelt wird\n // (Feldtest). Dann gibt es keinen eigenen Pegel \u2192 null statt einer irref\u00FChrenden 0.\n const known = volume.max > 0;\n void this.setDeviceStates(id, {\n volume: known ? volume.level : null,\n volumeMax: known ? volume.max : null,\n muted: volume.muted,\n });\n });\n remote.on('log', (level, message) => this.log[level](message));\n }\n\n private attachAdb(runtime: DeviceRuntime, adb: AdbDevice): void {\n const { id, label } = runtime;\n const adbOnly = runtime.mode === 'adb';\n adb.on('ready', info => {\n runtime.adbConnected = true;\n if (adbOnly) {\n this.cancelOffline(runtime);\n }\n runtime.adbAuthPending = false;\n this.log.info(`${label}: ADB connected${info.model ? ` (${info.model})` : ''}`);\n const values: Record<string, ioBroker.StateValue> = {\n 'info.adbConnected': true,\n 'info.adbAuthorizationPending': false,\n };\n if (adbOnly) {\n values['info.connected'] = true;\n values['info.model'] = info.model ?? null;\n adb.getProp('ro.product.manufacturer')\n .then(m => this.setStateChangedAsync(`${id}.info.manufacturer`, m ?? null, true))\n .catch(e => this.log.debug(`${label}: cannot read manufacturer: ${(e as Error).message}`));\n }\n void this.setDeviceStates(id, values);\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n // @KI_HINWEIS: Diagnose: Der Schl\u00FCssel der Wake-on-LAN-Einstellung ist herstellerabh\u00E4ngig (Fire-TV-\n // Fernseher verschiedener Hersteller). Erst die Schl\u00FCssel im Feld sammeln, bevor der Adapter sie setzt.\n adb.findWakeSettings()\n .then(found =>\n this.log.debug(`${label}: wake settings: ${found.length ? found.join(' | ') : 'none found'}`),\n )\n .catch(() => undefined);\n if (runtime.wakePendingUntil && Date.now() < runtime.wakePendingUntil) {\n runtime.wakePendingUntil = undefined;\n runtime.powerTarget = { value: true, until: Date.now() + 5_000 };\n adb.setPower(true).catch(e =>\n this.log.debug(`${label}: switching on after Wake-on-LAN failed: ${(e as Error).message}`),\n );\n }\n });\n adb.on('authPending', () => {\n runtime.adbAuthPending = true;\n this.log.warn(\n `${label}: confirm \"Allow debugging\" on the TV and tick \"Always allow from this computer\" (key ioBroker@${this.host ?? 'iobroker'})`,\n );\n void this.setStateChangedAsync(`${id}.info.adbAuthorizationPending`, true, true);\n void this.refreshStatus(runtime);\n });\n adb.on('disconnected', () => {\n runtime.adbConnected = false;\n if (adbOnly) {\n this.scheduleOffline(runtime);\n }\n this.log.debug(`${label}: ADB disconnected, reconnecting`);\n const values: Record<string, ioBroker.StateValue> = { 'info.adbConnected': false };\n if (adbOnly) {\n values['info.connected'] = false;\n }\n void this.setDeviceStates(id, values);\n void this.updateConnectionState();\n void this.refreshStatus(runtime);\n });\n adb.on('poll', result => void this.applyPoll(runtime, result));\n adb.on('log', (level, message) => this.log[level](message));\n }\n\n /**\n * Writes the result of an ADB poll to the states.\n *\n * @param runtime - Device\n * @param result - Parsed poll output\n */\n private async applyPoll(runtime: DeviceRuntime, result: PollResult): Promise<void> {\n const { id } = runtime;\n if (runtime.mode === 'adb') {\n const target = runtime.powerTarget;\n // @KI_HINWEIS: Feldtest Fire TV: Eine kurz vor dem Befehl gestartete Abfrage meldete noch den alten\n // Zustand und best\u00E4tigte ihn (Flackern true\u2192false). Bis zum Ziel bzw. 5 s danach widersprechende\n // Werte ignorieren.\n const contradicts = target !== undefined && Date.now() < target.until && result.awake !== target.value;\n if (target && (!contradicts || Date.now() >= target.until)) {\n runtime.powerTarget = undefined;\n }\n if (result.awake !== undefined && !contradicts) {\n await this.setStateChangedAsync(`${id}.power`, result.awake, true);\n }\n if (result.currentApp) {\n await this.setStateChangedAsync(`${id}.currentApp`, result.currentApp, true);\n }\n }\n if (result.awake === false) {\n await this.setMediaStates(id, null);\n } else if (result.media !== undefined) {\n await this.setMediaStates(id, result.media);\n }\n }\n\n /**\n * Starts the grace timer after the primary connection was lost. Short interruptions (e.g. the TV closes the\n * connection after a rejected command and is back after 2 s) do not touch power/media.\n *\n * @param runtime - Device\n */\n private scheduleOffline(runtime: DeviceRuntime): void {\n if (runtime.offlineTimer) {\n return;\n }\n runtime.offlineTimer = this.setTimeout(() => {\n runtime.offlineTimer = undefined;\n this.log.debug(`${runtime.label}: unreachable for ${OFFLINE_GRACE_MS / 1000} s - treating as off`);\n void this.setStateChangedAsync(`${runtime.id}.power`, false, true);\n if (runtime.adb) {\n void this.setMediaStates(runtime.id, null);\n }\n }, OFFLINE_GRACE_MS);\n }\n\n private cancelOffline(runtime: DeviceRuntime): void {\n if (runtime.offlineTimer) {\n this.clearTimeout(runtime.offlineTimer);\n runtime.offlineTimer = undefined;\n }\n }\n\n private async setMediaStates(id: string, media: MediaInfo | null): Promise<void> {\n await this.setDeviceStates(id, {\n 'media.state': media?.state ?? 'idle',\n 'media.title': media?.title ?? null,\n 'media.subtitle': media?.subtitle ?? null,\n 'media.app': media?.app ?? null,\n 'media.position': media?.position ?? null,\n });\n }\n\n private async setDeviceStates(id: string, values: Record<string, ioBroker.StateValue>): Promise<void> {\n for (const [key, val] of Object.entries(values)) {\n await this.setStateChangedAsync(`${id}.${key}`, val, true);\n }\n }\n\n private async updateConnectionState(): Promise<void> {\n const anyConnected = [...this.devices.values()].some(d =>\n d.mode === 'adb' ? d.adbConnected : d.remoteConnected,\n );\n await this.setStateChangedAsync('info.connection', anyConnected, true);\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Objects\n // ------------------------------------------------------------------------------------------------------------\n\n /**\n * State definitions that apply to a device in its connection mode.\n *\n * @param config - Device configuration\n */\n private stateDefinitionsFor(config: DeviceConfig): StateDefinition[] {\n const mode = modeOf(config);\n const base = DEVICE_STATES.filter(d => mode !== 'adb' || !d.remoteOnly);\n return mode === 'remote' ? base : [...base, ...ADB_STATES];\n }\n\n private async createDeviceObjects(id: string, config: DeviceConfig): Promise<void> {\n const mode = modeOf(config);\n await this.extendObjectAsync(id, {\n type: 'device',\n common: {\n name: config.name || config.host,\n statusStates: { onlineId: `${this.namespace}.${id}.info.connected` },\n },\n native: { host: config.host, connection: mode },\n });\n const channels: [string, ioBroker.StringOrTranslated][] = [\n [\n 'info',\n {\n en: 'Information',\n de: 'Informationen',\n ru: '\u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F',\n pt: 'Informa\u00E7\u00E3o',\n nl: 'Informatie',\n fr: 'Informations',\n it: 'Informazioni',\n es: 'Informaci\u00F3n',\n pl: 'Informacje',\n uk: '\u0406\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0456\u044F',\n 'zh-cn': '\u4FE1\u606F',\n },\n ],\n [\n 'keys',\n {\n en: 'Remote keys',\n de: 'Fernbedienungstasten',\n ru: '\u041A\u043D\u043E\u043F\u043A\u0438 \u043F\u0443\u043B\u044C\u0442\u0430',\n pt: 'Teclas do comando',\n nl: 'Afstandsbedieningstoetsen',\n fr: 'Touches de la t\u00E9l\u00E9commande',\n it: 'Tasti del telecomando',\n es: 'Teclas del mando',\n pl: 'Przyciski pilota',\n uk: '\u041A\u043D\u043E\u043F\u043A\u0438 \u043F\u0443\u043B\u044C\u0442\u0430',\n 'zh-cn': '\u9065\u63A7\u6309\u952E',\n },\n ],\n [\n 'apps',\n {\n en: 'App favorites',\n de: 'App-Favoriten',\n ru: '\u0418\u0437\u0431\u0440\u0430\u043D\u043D\u044B\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F',\n pt: 'Aplicativos favoritos',\n nl: 'App-favorieten',\n fr: 'Applications favorites',\n it: 'App preferite',\n es: 'Aplicaciones favoritas',\n pl: 'Ulubione aplikacje',\n uk: '\u0423\u043B\u044E\u0431\u043B\u0435\u043D\u0456 \u0437\u0430\u0441\u0442\u043E\u0441\u0443\u043D\u043A\u0438',\n 'zh-cn': '\u6536\u85CF\u7684\u5E94\u7528',\n },\n ],\n ];\n if (mode !== 'remote') {\n channels.push([\n 'media',\n {\n en: 'Media (via ADB)',\n de: 'Medien (\u00FCber ADB)',\n ru: '\u041C\u0435\u0434\u0438\u0430 (\u0447\u0435\u0440\u0435\u0437 ADB)',\n pt: 'Multim\u00E9dia (via ADB)',\n nl: 'Media (via ADB)',\n fr: 'M\u00E9dias (via ADB)',\n it: 'Media (tramite ADB)',\n es: 'Multimedia (v\u00EDa ADB)',\n pl: 'Multimedia (przez ADB)',\n uk: '\u041C\u0435\u0434\u0456\u0430 (\u0447\u0435\u0440\u0435\u0437 ADB)',\n 'zh-cn': '\u5A92\u4F53\uFF08\u901A\u8FC7 ADB\uFF09',\n },\n ]);\n }\n for (const [channel, name] of channels) {\n await this.extendObjectAsync(`${id}.${channel}`, { type: 'channel', common: { name }, native: {} });\n }\n for (const def of this.stateDefinitionsFor(config)) {\n await this.extendObjectAsync(`${id}.${def.id}`, { type: 'state', common: def.common, native: {} });\n }\n for (const btn of KEY_BUTTONS) {\n await this.extendObjectAsync(`${id}.keys.${btn.id}`, {\n type: 'state',\n common: { name: btn.name, type: 'boolean', role: btn.role, read: false, write: true },\n native: { key: btn.key },\n });\n }\n for (const [appId, app] of this.appsById) {\n await this.extendObjectAsync(`${id}.apps.${appId}`, {\n type: 'state',\n common: { name: app.name, type: 'boolean', role: 'button', read: false, write: true },\n native: { link: app.link },\n });\n }\n }\n\n /**\n * Deletes devices, states and channels that no longer apply (device removed, app removed, mode changed).\n *\n * @param deviceIds - Ids of all configured devices\n * @param configs - Device configurations (same order as ids were assigned)\n */\n private async removeStaleObjects(deviceIds: Set<string>, configs: DeviceConfig[]): Promise<void> {\n const prefix = `${this.namespace}.`;\n const range = (start: string): { startkey: string; endkey: string } => ({\n startkey: start,\n endkey: `${start}\u9999`,\n });\n const devices = await this.getObjectViewAsync('system', 'device', range(prefix));\n for (const row of devices.rows) {\n const id = row.id.slice(prefix.length);\n if (!deviceIds.has(id)) {\n this.log.info(`Removing objects of device \"${id}\" (no longer configured)`);\n await this.delObjectAsync(id, { recursive: true });\n }\n }\n const ids = [...deviceIds];\n for (let i = 0; i < ids.length; i++) {\n const id = ids[i];\n const config = configs[i];\n const expected = new Set<string>([\n ...this.stateDefinitionsFor(config).map(d => d.id),\n ...KEY_BUTTONS.map(b => `keys.${b.id}`),\n ...[...this.appsById.keys()].map(a => `apps.${a}`),\n ]);\n const devPrefix = `${prefix}${id}.`;\n const states = await this.getObjectViewAsync('system', 'state', range(devPrefix));\n for (const row of states.rows) {\n const rel = row.id.slice(devPrefix.length);\n if (!expected.has(rel)) {\n await this.delObjectAsync(row.id.slice(prefix.length));\n }\n }\n if (modeOf(config) === 'remote') {\n const media = await this.getObjectAsync(`${id}.media`);\n if (media) {\n await this.delObjectAsync(`${id}.media`);\n }\n }\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Commands\n // ------------------------------------------------------------------------------------------------------------\n\n private onStateChange(fullId: string, state: ioBroker.State | null | undefined): void {\n if (!state || state.ack) {\n return;\n }\n const parts = fullId.slice(this.namespace.length + 1).split('.');\n const device = this.devices.get(parts[0]);\n if (!device) {\n return;\n }\n const command = parts.slice(1).join('.');\n this.handleCommand(device, command, state).catch((e: Error) =>\n this.log.warn(`${device.config.name || device.id}: command \"${command}\" failed: ${e.message}`),\n );\n }\n\n /**\n * Executes a user command (ack=false) on a device, using Remote v2 where possible and ADB otherwise.\n *\n * @param device - Target device\n * @param command - State id relative to the device\n * @param state - New state\n */\n private async handleCommand(device: DeviceRuntime, command: string, state: ioBroker.State): Promise<void> {\n const ack = (): void => void this.setState(`${device.id}.${command}`, { val: state.val, ack: true });\n\n if (command.startsWith('keys.')) {\n const btn = KEY_BUTTONS.find(b => b.id === command.slice(5));\n if (btn) {\n await this.sendKey(device, btn.key);\n ack();\n }\n return;\n }\n if (command.startsWith('apps.')) {\n const app = this.appsById.get(command.slice(5));\n if (app) {\n await this.launch(device, app.link);\n ack();\n }\n return;\n }\n switch (command) {\n case 'power':\n await this.setPower(device, !!state.val, ack);\n return;\n case 'muted':\n if (device.muted === !!state.val) {\n ack();\n } else {\n await this.sendKey(device, 'VOLUME_MUTE');\n if (!device.remote?.isReady) {\n ack(); // ADB has no mute feedback\n }\n }\n return;\n case 'launchApp':\n if (typeof state.val === 'string' && state.val.trim()) {\n await this.launch(device, state.val);\n ack();\n }\n return;\n case 'sendKey': {\n const key = state.val === null || state.val === undefined ? '' : String(state.val);\n if (resolveKeyCode(key) === undefined) {\n throw new Error(`unknown key \"${key}\"`);\n }\n await this.sendKey(device, key);\n ack();\n return;\n }\n case 'sendText':\n if (typeof state.val === 'string' && state.val !== '') {\n if (device.remote?.isReady && device.config.enableIme !== false) {\n device.remote.sendText(state.val);\n } else if (device.adb?.isReady) {\n await device.adb.sendText(state.val);\n } else {\n throw new Error('not connected');\n }\n ack();\n }\n return;\n default:\n this.log.debug(`Ignoring write to read-only or unknown state ${device.id}.${command}`);\n }\n }\n\n private async sendKey(device: DeviceRuntime, key: string): Promise<void> {\n if (device.remote?.isReady) {\n device.remote.sendKey(key);\n device.adb?.pollSoon(); // playback info reflects play/pause about 1 s later instead of up to one interval\n } else if (device.adb?.isReady) {\n await device.adb.sendKey(key);\n } else {\n throw new Error('not connected');\n }\n }\n\n /**\n * Starts an app: https deep links preferably via Remote v2, package names preferably via ADB.\n *\n * @param device - Target device\n * @param target - Deep link or package name\n */\n private async launch(device: DeviceRuntime, target: string): Promise<void> {\n // @KI_HINWEIS: Feldtest SHIELD: Remote v2 akzeptiert nur http(s)-Links, Paketnamen nur per ADB (monkey).\n const preferAdb = !isLink(target);\n if (preferAdb && device.adb?.isReady) {\n await device.adb.launch(target);\n } else if (device.remote?.isReady) {\n device.remote.launchApp(target);\n device.adb?.pollSoon();\n } else if (device.adb?.isReady) {\n await device.adb.launch(target);\n } else {\n throw new Error('not connected');\n }\n }\n\n /**\n * Switches the device on/off. Remote v2 only knows the POWER toggle, ADB has idempotent WAKEUP/SLEEP.\n * The ack is set by the state feedback of the device (remote_start or ADB poll).\n *\n * @param device - Target device\n * @param wanted - Desired state\n * @param ack - Acknowledges the command immediately\n */\n private async setPower(device: DeviceRuntime, wanted: boolean, ack: () => void): Promise<void> {\n if (device.remote?.isReady) {\n // @KI_HINWEIS: Nur senden, wenn der vom TV gemeldete Zustand abweicht (Toggle-Taste).\n if (device.remote.isOn === wanted) {\n ack();\n } else {\n device.remote.sendKey('POWER');\n }\n } else if (device.adb?.isReady) {\n device.powerTarget = { value: wanted, until: Date.now() + 5_000 };\n await device.adb.setPower(wanted);\n } else if (wanted) {\n await this.wakeOnLan(device);\n } else {\n throw new Error('not connected');\n }\n }\n\n /**\n * Wakes an unreachable device (deep standby without network) with a magic packet and reconnects right away.\n *\n * @param device - Device\n */\n private async wakeOnLan(device: DeviceRuntime): Promise<void> {\n // @KI_HINWEIS: Feldtest Fire TV: Im tiefen Energiesparmodus schaltet das Ger\u00E4t das Netzwerk ab, ADB und\n // Remote sind nicht erreichbar. Viele Ger\u00E4te reagieren dann noch auf Wake-on-LAN (am Ger\u00E4t einschalten).\n // @KI_HINWEIS: Die MAC l\u00E4sst sich \u00FCber ADB nicht zuverl\u00E4ssig lesen (Fire TV/SHIELD: /sys gesperrt, ip link\n // leer) und das TV-Zertifikat enth\u00E4lt ggf. die WLAN-MAC. Deshalb nur die MAC aus der Konfiguration.\n const mac = device.config.wolMac?.trim();\n if (!mac) {\n throw new Error(\n 'not connected. To switch it on from deep standby, enter its MAC address in the instance settings (Wake-on-LAN)',\n );\n }\n const targets = wolTargets(device.config.host.trim(), device.config.wolBroadcast);\n const sent = await sendWakeOnLan(mac, targets);\n if (!sent) {\n throw new Error(`Wake-on-LAN could not be sent (${targets.join(', ')})`);\n }\n this.log.info(`${device.label}: not reachable, sent Wake-on-LAN to ${mac}`);\n device.wakePendingUntil = Date.now() + 90_000;\n device.remote?.reconnectNow();\n device.adb?.reconnectNow();\n }\n\n // ------------------------------------------------------------------------------------------------------------\n // Admin messages (pairing, status)\n // ------------------------------------------------------------------------------------------------------------\n\n private text(key: MessageKey): string {\n return MESSAGES[key][this.uiLanguage];\n }\n\n private reply(obj: ioBroker.Message, payload: { result: string } | { error: string } | StatusText): void {\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, payload, obj.callback);\n }\n }\n\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n if (!obj || typeof obj !== 'object') {\n return;\n }\n const msg = (obj.message ?? {}) as { host?: unknown; pin?: unknown; enabled?: unknown };\n const host = typeof msg.host === 'string' ? msg.host.trim() : '';\n try {\n switch (obj.command) {\n case 'startPairing':\n await this.startPairing(host);\n this.reply(obj, { result: this.text('pairingStarted') });\n break;\n case 'finishPairing': {\n const configured = await this.finishPairing(host, typeof msg.pin === 'string' ? msg.pin : '');\n this.reply(obj, { result: this.text(configured ? 'pairingDone' : 'pairingDoneSave') });\n break;\n }\n case 'deviceStatus':\n this.reply(obj, this.deviceStatus(host, msg.enabled !== false));\n return;\n default:\n this.log.debug(`Unknown message command \"${obj.command}\"`);\n }\n } catch (e) {\n const key: MessageKey =\n e instanceof PairingError\n ? e.code\n : e instanceof Error && e.message in MESSAGES\n ? (e.message as MessageKey)\n : 'closed';\n this.log.warn(`Pairing with ${host || '?'} failed: ${(e as Error).message}`);\n this.reply(obj, { error: this.text(key) });\n }\n }\n\n /**\n * Status line for one device in the admin UI (`textSendTo`).\n *\n * @param host - Host as currently entered in the form (may be unsaved)\n * @param enabled - Checkbox value in the form\n */\n private deviceStatus(host: string, enabled: boolean): StatusText {\n const grey = { color: '#888888' };\n if (!host) {\n return { text: this.text('statusNoHost'), icon: 'connection', style: grey };\n }\n if (!enabled) {\n return { text: this.text('statusDisabled'), icon: 'no-connection', style: grey };\n }\n const device = [...this.devices.values()].find(d => d.config.host.trim() === host);\n if (!device) {\n return { text: this.text('statusUnsaved'), icon: 'pair', style: grey };\n }\n return this.statusOf(device);\n }\n\n /**\n * Current status of a running device (admin status line and state `info.status`).\n *\n * @param device - Device runtime\n */\n private statusOf(device: DeviceRuntime): StatusText {\n const green = { color: '#2e7d32' };\n const orange = { color: '#ed6c02' };\n const red = { color: '#d32f2f' };\n const adbPart = (): { key: MessageKey; style: { color: string } } =>\n device.adbConnected\n ? { key: 'statusAdbOk', style: green }\n : device.adbAuthPending\n ? { key: 'statusAdbPending', style: orange }\n : { key: device.mode === 'adb' ? 'statusAdbOffline' : 'statusInfoOffline', style: red };\n if (device.mode === 'adb') {\n const a = adbPart();\n return {\n text: this.text(a.key === 'statusAdbOk' ? 'statusConnected' : a.key),\n icon: device.adbConnected ? 'connection' : 'no-connection',\n style: a.style,\n };\n }\n let main: StatusText;\n if (device.remoteConnected) {\n main = { text: this.text('statusConnected'), icon: 'connection', style: green };\n } else if (device.pairingRequired) {\n main = { text: this.text('statusPairing'), icon: 'pair', style: orange };\n } else {\n main = { text: this.text('statusOffline'), icon: 'no-connection', style: red };\n }\n if (device.mode === 'remote_adb') {\n const a = adbPart();\n main.text = `${main.text} \u00B7 ${this.text(a.key)}`;\n if (main.style === green && a.style !== green) {\n main.style = a.style;\n }\n }\n return main;\n }\n\n /**\n * Writes the human readable status to `<device>.info.status` (shown live in the admin UI).\n *\n * @param device - Device runtime\n */\n private async refreshStatus(device: DeviceRuntime): Promise<void> {\n await this.setStateChangedAsync(`${device.id}.info.status`, this.statusOf(device).text, true);\n }\n\n private async startPairing(host: string): Promise<void> {\n if (!host) {\n throw new Error('noHost');\n }\n if (!this.certificate) {\n throw new Error('noCertificate');\n }\n this.dropPairing(host);\n const session = new PairingSession({\n timers: this.adapterTimers,\n host,\n clientName: this.config.clientName?.trim() || 'ioBroker',\n certificate: this.certificate,\n });\n const pending: PendingPairing = { session, timer: undefined };\n this.pairings.set(host, pending);\n try {\n await session.start();\n } catch (e) {\n this.dropPairing(host);\n throw e;\n }\n pending.timer = this.setTimeout(() => this.dropPairing(host), PAIRING_SESSION_TTL_MS);\n }\n\n /**\n * Completes pairing and reconnects matching devices.\n *\n * @param host - TV address\n * @param pin - Code shown on the TV\n * @returns true if a running device uses this host\n */\n private async finishPairing(host: string, pin: string): Promise<boolean> {\n if (!host) {\n throw new Error('noHost');\n }\n const pending = this.pairings.get(host);\n if (!pending?.session.isOpen) {\n this.dropPairing(host);\n throw new Error('noSession');\n }\n try {\n await pending.session.finish(pin);\n } catch (e) {\n // Wrong/mistyped code: the TV keeps the session open, the user may retry.\n if (!(e instanceof PairingError && (e.code === 'wrongPin' || e.code === 'invalidPin'))) {\n this.dropPairing(host);\n }\n throw e;\n }\n this.dropPairing(host);\n this.log.info(`Pairing with ${host} successful`);\n let configured = false;\n for (const device of this.devices.values()) {\n if (device.config.host.trim() === host && device.remote) {\n configured = true;\n device.pairingRequired = false;\n await this.setStateChangedAsync(`${device.id}.info.pairingRequired`, false, true);\n device.remote.restart();\n }\n }\n return configured;\n }\n\n private dropPairing(host: string): void {\n const pending = this.pairings.get(host);\n if (pending) {\n this.clearTimeout(pending.timer);\n pending.session.close();\n this.pairings.delete(host);\n }\n }\n\n private onUnload(callback: () => void): void {\n try {\n for (const host of [...this.pairings.keys()]) {\n this.dropPairing(host);\n }\n for (const device of this.devices.values()) {\n device.remote?.stop();\n device.remote?.removeAllListeners();\n device.adb?.stop();\n device.adb?.removeAllListeners();\n }\n this.devices.clear();\n callback();\n } catch (error) {\n this.log.error(`Error during unloading: ${(error as Error).message}`);\n callback();\n }\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new Androidtv(options);\n} else {\n // otherwise start the instance directly\n (() => new Androidtv())();\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["_a"]
7
7
  }
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "androidtv",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "news": {
6
+ "0.2.3": {
7
+ "en": "Device and app IDs are now unique regardless of upper/lower case\nReconnect interval, playback update interval and ADB port are checked in the adapter, not only in the settings page\nREADME: links to the supported products\nPairing and connection status texts in all 11 languages (was English/German only)\nThe object for the internal files is now created from io-package.json",
8
+ "de": "Geräte- und App-IDs sind jetzt unabhängig von Groß- und Kleinschreibung\nVerbindungsintervall, Abspielintervall und ADB-Port sind im Adapter geprüft, nicht nur auf der Einstellungsseite\nREADME: Links zu unterstützten Produkten\nPairing- und Verbindungsstatus-Texte in allen 11 Sprachen (nur Englisch/Deutsch)\nDas Objekt für interne Dateien ist nun aus io-package.json",
9
+ "ru": "Идентификаторы устройств и приложений теперь уникальны независимо от того, используются ли они в верхнем или нижнем регистре\nИнтервал повторного подключения, интервал обновления воспроизведения и порт ADB проверяются в адаптере, а не только на странице настроек\nREADME: ссылки на поддерживаемые продукты\nТексты состояния сопряжения и подключения на всех 11 языках (только на английском и немецком языках)\nОбъект для внутренних файлов теперь создается из io-package.json",
10
+ "pt": "IDs de dispositivos e apps agora são únicos em maiúsculas e minúsculas\nO intervalo de reconexão, a atualização e a porta ADB são verificados no adaptador, não só na página de configurações\nREADME: links aos produtos aceitos\nTextos de status de emparelhamento e conexão em 11 idiomas (só em inglês/alemão)\nO objeto dos arquivos internos agora é criado em io-package.json",
11
+ "nl": "Apparaat- en app-ID's zijn nu uniek, ongeacht hoofdletters of kleine letters\nHet interval voor het opnieuw verbinden, het interval voor de afspeelupdate en de ADB-poort worden in de adapter gecontroleerd, niet alleen op de instellingenpagina\nREADME: links naar de ondersteunde producten\nTeksten over de koppelings- en verbindingsstatus in alle 11 talen (alleen Engels/Duits)\nHet object voor de interne bestanden is nu gemaakt op basis van io-package.json",
12
+ "fr": "Les ID d'appareil et d'app sont désormais uniques, majuscules ou minuscules\nLes intervalles de reconnexion, de lecture et de mise à jour et le port ADB sont enregistrés sur l'adaptateur\nREADME : liens vers les produits pris en charge\nTextes sur l'état du couplage et de la connexion en 11 langues (anglais/allemand uniquement)\nL'objet des fichiers internes est désormais créé depuis io-package.json",
13
+ "it": "Gli ID dispositivo e app ora sono univoci con lettere maiuscole e minuscole\nIntervallo di riconnessione, riproduzione e porta ADB sono registrati nell'adattatore, non solo\nREADME: link ai prodotti supportati\nTesti di abbinamento e connessione in tutte le 11 lingue (solo inglese/tedesco)\nL'oggetto per i file interni è ora creato da io-package.json",
14
+ "es": "Los ID del dispositivo y la aplicación son ahora únicos en mayúsculas o minúsculas\nEl intervalo de reconexión, reproducción y actualización y el puerto ADB están marcados en el adaptador, no solo en la página de ajustes\nREADME: enlaces a los productos apoyados\nTextos de emparejamiento y estado de conexión en los 11 idiomas (solo en inglés/alemán)\nEl objeto de los archivos internos se crea ahora a partir de io-package.json",
15
+ "pl": "Identyfikatory urządzeń i aplikacji są teraz unikalne niezależnie od wielkich i małych liter\nInterwał ponownego połączenia, interwał aktualizacji odtwarzania i port ADB są sprawdzane w adapterze, nie tylko na stronie ustawień\nREADME: linki do obsługiwanych produktów\nTeksty parowania i statusu połączenia we wszystkich 11 językach (tylko angielski/niemiecki)\nObiekt dla plików wewnętrznych jest teraz tworzony z io-package.json",
16
+ "uk": "Ідентифікатори пристроїв та додатків тепер унікальні незалежно від верхнього/малого регістру\nІнтервал повторного підключення, інтервал оновлення відтворення та порт ADB перевіряються в адаптері, не тільки на сторінці налаштувань\nREADME: посилання на підтримувані продукти\nТексти стану сполучення та підключення на всіх 11 мовах (лише англійська/німецька)\nОб'єкт для внутрішніх файлів тепер створений з io-package.json",
17
+ "zh-cn": "现在,无论大写/小写如何,设备和应用程序 ID 都是唯一的\n在适配器中检查重新连接间隔、播放更新间隔和 ADB 端口,而不仅仅是在设置页面中\n自述文件:支持产品的链接\n所有 11 种语言的配对和连接状态文本(仅限英语/德语)\n内部文件的对象现在是从 io-package.json 创建的"
18
+ },
19
+ "0.2.2": {
20
+ "en": "Object names translated into all 11 languages",
21
+ "de": "Objektnamen in alle 11 Sprachen übersetzt",
22
+ "ru": "Названия объектов переведены на все 11 языков",
23
+ "pt": "Nomes de objetos traduzidos em 11 idiomas",
24
+ "nl": "Objectnamen vertaald in alle 11 talen",
25
+ "fr": "Noms d'objets traduits dans les 11 langues",
26
+ "it": "Nomi oggetti tradotti in 11 lingue",
27
+ "es": "Nombres de objetos traducidos a los 11 idiomas",
28
+ "pl": "Nazwy obiektów przetłumaczone na wszystkie 11 języków",
29
+ "uk": "Назви об'єктів перекладені на всі 11 мов",
30
+ "zh-cn": "对象名称已翻译成所有 11 种语言"
31
+ },
6
32
  "0.2.1": {
7
33
  "en": "Clearer error text for an invalid MAC address, translated into all languages\nFast forward and rewind buttons use the official roles `button.fastforward` / `button.fastreverse`",
8
34
  "de": "Klarerer Fehlertext für ungültige MAC-Adresse, in alle Sprachen übersetzt\nVor- und Rücklauftasten haben die offiziellen Rollen `button.fastforward`/`button.fastreverse`",
@@ -167,6 +193,27 @@
167
193
  },
168
194
  "objects": [],
169
195
  "instanceObjects": [
196
+ {
197
+ "_id": "",
198
+ "type": "meta",
199
+ "common": {
200
+ "name": {
201
+ "en": "Android TV internal files",
202
+ "de": "Android TV interne Dateien",
203
+ "ru": "Внутренние файлы Android TV",
204
+ "pt": "Ficheiros internos do Android TV",
205
+ "nl": "Interne bestanden Android TV",
206
+ "fr": "Fichiers internes Android TV",
207
+ "it": "File interni Android TV",
208
+ "es": "Archivos internos de Android TV",
209
+ "pl": "Pliki wewnętrzne Android TV",
210
+ "uk": "Внутрішні файли Android TV",
211
+ "zh-cn": "Android TV 内部文件"
212
+ },
213
+ "type": "meta.user"
214
+ },
215
+ "native": {}
216
+ },
170
217
  {
171
218
  "_id": "info",
172
219
  "type": "channel",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.androidtv",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Control NVIDIA SHIELD, Android TV / Google TV and Amazon Fire TV devices from ioBroker",
5
5
  "author": {
6
6
  "name": "Schimi",
@@ -35,11 +35,11 @@
35
35
  }
36
36
  ],
37
37
  "engines": {
38
- "node": ">= 22"
38
+ "node": ">=22.0.0"
39
39
  },
40
40
  "dependencies": {
41
41
  "@iobroker/adapter-core": "^3.4.3",
42
- "node-forge": "^1.4.2"
42
+ "node-forge": "^1.4.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@alcalzone/release-script": "^5.2.1",
@@ -48,12 +48,12 @@
48
48
  "@alcalzone/release-script-plugin-manual-review": "^5.2.0",
49
49
  "@iobroker/adapter-dev": "^1.5.0",
50
50
  "@iobroker/eslint-config": "^2.3.4",
51
- "@iobroker/testing": "^6.3.1",
51
+ "@iobroker/testing": "^6.3.2",
52
52
  "@tsconfig/node22": "^22.0.6",
53
53
  "@types/iobroker": "npm:@iobroker/types@^7.2.5",
54
54
  "@types/node": "^22.20.5",
55
55
  "@types/node-forge": "^1.3.14",
56
- "rimraf": "^6.1.3",
56
+ "rimraf": "^6.1.4",
57
57
  "source-map-support": "^0.5.21",
58
58
  "ts-node": "^10.9.2",
59
59
  "typescript": "~5.9.3"