iobroker.androidtv 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -8
- package/build/lib/states.js +711 -46
- package/build/lib/states.js.map +2 -2
- package/build/main.js +64 -4
- package/build/main.js.map +2 -2
- package/io-package.json +14 -1
- package/package.json +1 -1
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/** 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 [\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,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "androidtv",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.2.2": {
|
|
7
|
+
"en": "Object names translated into all 11 languages",
|
|
8
|
+
"de": "Objektnamen in alle 11 Sprachen übersetzt",
|
|
9
|
+
"ru": "Названия объектов переведены на все 11 языков",
|
|
10
|
+
"pt": "Nomes de objetos traduzidos em 11 idiomas",
|
|
11
|
+
"nl": "Objectnamen vertaald in alle 11 talen",
|
|
12
|
+
"fr": "Noms d'objets traduits dans les 11 langues",
|
|
13
|
+
"it": "Nomi oggetti tradotti in 11 lingue",
|
|
14
|
+
"es": "Nombres de objetos traducidos a los 11 idiomas",
|
|
15
|
+
"pl": "Nazwy obiektów przetłumaczone na wszystkie 11 języków",
|
|
16
|
+
"uk": "Назви об'єктів перекладені на всі 11 мов",
|
|
17
|
+
"zh-cn": "对象名称已翻译成所有 11 种语言"
|
|
18
|
+
},
|
|
6
19
|
"0.2.1": {
|
|
7
20
|
"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
21
|
"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`",
|