iobroker.androidtvremote 0.2.6 → 0.2.8

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Henrik Schönhofen
3
+ Copyright (c) 2026 Henrik Schönhofen <henrik.schoenhofen@icloud.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -137,23 +137,24 @@ If the initial connection fails or an established connection is lost, the adapte
137
137
 
138
138
  ## Changelog
139
139
 
140
- ### 0.2.5 (2026-10-07)
141
-
142
- - Improved Remote v2 connection handling
143
- - Persistent Remote v2 session instead of periodically forcing reconnects
144
- - Failed initial connections are detected and retried automatically
145
- - Protocol ping handling is left to the Android TV Remote v2 library
146
- - Improved reconnect behavior after temporary network/device unavailability
147
- - Android TV / Google TV power, volume and current-app states
148
- - Friendly application-name mapping
149
- - Optional Google Cast media metadata
150
- - Remote keys, text input, app links and power control
151
- - Secure pairing with locally stored certificate
140
+ <!--
141
+ Placeholder for the next version (at the beginning of the line):
142
+ ### **WORK IN PROGRESS**
143
+ -->
144
+
145
+ ### 0.2.7 (2026-10-09)
146
+
147
+ - Added automated GitHub Actions release workflow
148
+ - Prepared npm publishing through Trusted Publishing
149
+ - Updated release documentation and changelog
150
+ - No changes to adapter runtime behavior
151
+
152
+ [Older changelog entries](CHANGELOG_OLD.md)
152
153
 
153
154
  ## License
154
155
 
155
156
  MIT License
156
157
 
157
- Copyright (c) 2026 Henrik Schönhofen (backfisch88)
158
+ Copyright (c) 2026 Henrik Schönhofen <henrik.schoenhofen@icloud.com>
158
159
 
159
160
  See [LICENSE](LICENSE) for the full license text.
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "IP-Adresse / Hostname",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "IP-Adresse oder Hostname des Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Anzeigename beim Pairing",
5
+ "Pairing-Port": "Pairing-Port",
6
+ "Remote-Port": "Remote-Port",
7
+ "Reconnect nach Sekunden": "Reconnect nach Sekunden",
8
+ "Protokoll-Debug aktivieren": "Protokoll-Debug aktivieren",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.",
10
+ "Cast-Medieninfos aktivieren": "Cast-Medieninfos aktivieren",
11
+ "Cast-Abfrage alle Sekunden": "Cast-Abfrage alle Sekunden"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "IP address / Hostname",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "IP address or hostname of the Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Display name during pairing",
5
+ "Pairing-Port": "Pairing port",
6
+ "Remote-Port": "Remote port",
7
+ "Reconnect nach Sekunden": "Reconnect after seconds",
8
+ "Protokoll-Debug aktivieren": "Enable protocol debugging",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "On first startup, the TV displays a pairing code. Enter it under Objects → androidtvremote.X → control → pairingCode. The certificate is then stored locally.",
10
+ "Cast-Medieninfos aktivieren": "Enable Cast media information",
11
+ "Cast-Abfrage alle Sekunden": "Cast polling interval in seconds"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "Dirección IP / Nombre de host",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "Dirección IP o nombre de host del Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Nombre mostrado durante la vinculación",
5
+ "Pairing-Port": "Puerto de vinculación",
6
+ "Remote-Port": "Puerto de control remoto",
7
+ "Reconnect nach Sekunden": "Reconectar después de unos segundos",
8
+ "Protokoll-Debug aktivieren": "Activar depuración del protocolo",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "En el primer inicio, el televisor muestra un código de vinculación. Introdúcelo en Objetos → androidtvremote.X → control → pairingCode. El certificado se guarda localmente.",
10
+ "Cast-Medieninfos aktivieren": "Activar información multimedia de Cast",
11
+ "Cast-Abfrage alle Sekunden": "Intervalo de consulta de Cast en segundos"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "Adresse IP / Nom d'hôte",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "Adresse IP ou nom d'hôte de l'Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Nom affiché lors de l'association",
5
+ "Pairing-Port": "Port d'association",
6
+ "Remote-Port": "Port de télécommande",
7
+ "Reconnect nach Sekunden": "Reconnexion après quelques secondes",
8
+ "Protokoll-Debug aktivieren": "Activer le débogage du protocole",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "Au premier démarrage, le téléviseur affiche un code d'association. Saisissez-le dans Objets → androidtvremote.X → control → pairingCode. Le certificat est ensuite enregistré localement.",
10
+ "Cast-Medieninfos aktivieren": "Activer les informations multimédias Cast",
11
+ "Cast-Abfrage alle Sekunden": "Intervalle d'interrogation Cast en secondes"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "Indirizzo IP / Nome host",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "Indirizzo IP o nome host di Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Nome visualizzato durante l'associazione",
5
+ "Pairing-Port": "Porta di associazione",
6
+ "Remote-Port": "Porta del telecomando",
7
+ "Reconnect nach Sekunden": "Riconnetti dopo alcuni secondi",
8
+ "Protokoll-Debug aktivieren": "Abilita debug del protocollo",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "Al primo avvio, la TV mostra un codice di associazione. Inseriscilo in Oggetti → androidtvremote.X → control → pairingCode. Il certificato viene quindi salvato localmente.",
10
+ "Cast-Medieninfos aktivieren": "Abilita informazioni multimediali Cast",
11
+ "Cast-Abfrage alle Sekunden": "Intervallo di interrogazione Cast in secondi"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "IP-adres / Hostnaam",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "IP-adres of hostnaam van de Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Weergavenaam tijdens koppelen",
5
+ "Pairing-Port": "Koppelingspoort",
6
+ "Remote-Port": "Afstandsbedieningspoort",
7
+ "Reconnect nach Sekunden": "Opnieuw verbinden na aantal seconden",
8
+ "Protokoll-Debug aktivieren": "Protocoldebugging inschakelen",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "Bij de eerste start toont de tv een koppelingscode. Voer deze in onder Objecten → androidtvremote.X → control → pairingCode. Het certificaat wordt daarna lokaal opgeslagen.",
10
+ "Cast-Medieninfos aktivieren": "Cast-media-informatie inschakelen",
11
+ "Cast-Abfrage alle Sekunden": "Cast-controle-interval in seconden"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "Adres IP / Nazwa hosta",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "Adres IP lub nazwa hosta Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Nazwa wyświetlana podczas parowania",
5
+ "Pairing-Port": "Port parowania",
6
+ "Remote-Port": "Port pilota",
7
+ "Reconnect nach Sekunden": "Połącz ponownie po kilku sekundach",
8
+ "Protokoll-Debug aktivieren": "Włącz debugowanie protokołu",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "Przy pierwszym uruchomieniu telewizor wyświetla kod parowania. Wprowadź go w Obiekty → androidtvremote.X → control → pairingCode. Certyfikat zostanie następnie zapisany lokalnie.",
10
+ "Cast-Medieninfos aktivieren": "Włącz informacje multimedialne Cast",
11
+ "Cast-Abfrage alle Sekunden": "Interwał odpytywania Cast w sekundach"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "Endereço IP / Nome do anfitrião",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "Endereço IP ou nome do anfitrião do Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Nome apresentado durante o emparelhamento",
5
+ "Pairing-Port": "Porta de emparelhamento",
6
+ "Remote-Port": "Porta de controlo remoto",
7
+ "Reconnect nach Sekunden": "Reconectar após alguns segundos",
8
+ "Protokoll-Debug aktivieren": "Ativar depuração do protocolo",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "Na primeira inicialização, a TV apresenta um código de emparelhamento. Introduza-o em Objetos → androidtvremote.X → control → pairingCode. O certificado será guardado localmente.",
10
+ "Cast-Medieninfos aktivieren": "Ativar informações multimédia do Cast",
11
+ "Cast-Abfrage alle Sekunden": "Intervalo de consulta do Cast em segundos"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "IP-адрес / Имя хоста",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "IP-адрес или имя хоста Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Отображаемое имя при сопряжении",
5
+ "Pairing-Port": "Порт сопряжения",
6
+ "Remote-Port": "Порт удалённого управления",
7
+ "Reconnect nach Sekunden": "Повторное подключение через несколько секунд",
8
+ "Protokoll-Debug aktivieren": "Включить отладку протокола",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "При первом запуске телевизор показывает код сопряжения. Введите его в Объекты → androidtvremote.X → control → pairingCode. После этого сертификат сохраняется локально.",
10
+ "Cast-Medieninfos aktivieren": "Включить информацию о мультимедиа Cast",
11
+ "Cast-Abfrage alle Sekunden": "Интервал опроса Cast в секундах"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "IP-адреса / Ім'я хоста",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "IP-адреса або ім'я хоста Android TV / Google TV",
4
+ "Anzeigename beim Pairing": "Ім'я під час сполучення",
5
+ "Pairing-Port": "Порт сполучення",
6
+ "Remote-Port": "Порт дистанційного керування",
7
+ "Reconnect nach Sekunden": "Повторне підключення через кілька секунд",
8
+ "Protokoll-Debug aktivieren": "Увімкнути налагодження протоколу",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "Під час першого запуску телевізор показує код сполучення. Введіть його в Об'єкти → androidtvremote.X → control → pairingCode. Сертифікат буде збережено локально.",
10
+ "Cast-Medieninfos aktivieren": "Увімкнути інформацію про медіа Cast",
11
+ "Cast-Abfrage alle Sekunden": "Інтервал опитування Cast у секундах"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "IP-Adresse / Hostname": "IP 地址 / 主机名",
3
+ "IP-Adresse oder Hostname des Android TV / Google TV": "Android TV / Google TV 的 IP 地址或主机名",
4
+ "Anzeigename beim Pairing": "配对时显示的名称",
5
+ "Pairing-Port": "配对端口",
6
+ "Remote-Port": "遥控端口",
7
+ "Reconnect nach Sekunden": "重新连接等待秒数",
8
+ "Protokoll-Debug aktivieren": "启用协议调试",
9
+ "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.": "首次启动时,电视会显示配对码。请在 对象 → androidtvremote.X → control → pairingCode 中输入。证书随后保存在本地。",
10
+ "Cast-Medieninfos aktivieren": "启用 Cast 媒体信息",
11
+ "Cast-Abfrage alle Sekunden": "Cast 轮询间隔(秒)"
12
+ }
@@ -5,13 +5,21 @@
5
5
  "type": "text",
6
6
  "label": "IP-Adresse / Hostname",
7
7
  "help": "IP-Adresse oder Hostname des Android TV / Google TV",
8
- "sm": 6,
9
- "newLine": true
8
+ "newLine": true,
9
+ "xs": 12,
10
+ "md": 6,
11
+ "lg": 6,
12
+ "xl": 6,
13
+ "sm": 6
10
14
  },
11
15
  "deviceName": {
12
16
  "type": "text",
13
17
  "label": "Anzeigename beim Pairing",
14
18
  "default": "ioBroker",
19
+ "xs": 12,
20
+ "md": 6,
21
+ "lg": 6,
22
+ "xl": 6,
15
23
  "sm": 6
16
24
  },
17
25
  "pairingPort": {
@@ -20,8 +28,12 @@
20
28
  "default": 6467,
21
29
  "min": 1,
22
30
  "max": 65535,
23
- "sm": 4,
24
- "newLine": true
31
+ "newLine": true,
32
+ "xs": 12,
33
+ "md": 4,
34
+ "lg": 4,
35
+ "xl": 4,
36
+ "sm": 4
25
37
  },
26
38
  "remotePort": {
27
39
  "type": "number",
@@ -29,6 +41,10 @@
29
41
  "default": 6466,
30
42
  "min": 1,
31
43
  "max": 65535,
44
+ "xs": 12,
45
+ "md": 4,
46
+ "lg": 4,
47
+ "xl": 4,
32
48
  "sm": 4
33
49
  },
34
50
  "reconnectSeconds": {
@@ -37,23 +53,42 @@
37
53
  "default": 15,
38
54
  "min": 5,
39
55
  "max": 3600,
56
+ "xs": 12,
57
+ "md": 4,
58
+ "lg": 4,
59
+ "xl": 4,
40
60
  "sm": 4
41
61
  },
42
62
  "debugProtocol": {
43
63
  "type": "checkbox",
44
64
  "label": "Protokoll-Debug aktivieren",
45
65
  "default": false,
46
- "newLine": true
66
+ "newLine": true,
67
+ "xs": 12,
68
+ "sm": 12,
69
+ "md": 12,
70
+ "lg": 12,
71
+ "xl": 12
47
72
  },
48
73
  "hint": {
49
74
  "type": "staticText",
50
- "text": "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert."
75
+ "text": "Beim ersten Start zeigt der Fernseher einen Pairing-Code. Diesen unter Objekte → androidtvremote.X → control → pairingCode eintragen. Danach wird das Zertifikat lokal gespeichert.",
76
+ "xs": 12,
77
+ "sm": 12,
78
+ "md": 12,
79
+ "lg": 12,
80
+ "xl": 12
51
81
  },
52
82
  "enableCast": {
53
83
  "type": "checkbox",
54
84
  "label": "Cast-Medieninfos aktivieren",
55
85
  "default": true,
56
- "newLine": true
86
+ "newLine": true,
87
+ "xs": 12,
88
+ "sm": 12,
89
+ "md": 12,
90
+ "lg": 12,
91
+ "xl": 12
57
92
  },
58
93
  "castPollSeconds": {
59
94
  "type": "number",
@@ -61,7 +96,12 @@
61
96
  "default": 5,
62
97
  "min": 3,
63
98
  "max": 60,
99
+ "xs": 12,
100
+ "md": 4,
101
+ "lg": 4,
102
+ "xl": 4,
64
103
  "sm": 4
65
104
  }
66
- }
105
+ },
106
+ "i18n": true
67
107
  }
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "androidtvremote",
4
- "version": "0.2.6",
4
+ "version": "0.2.8",
5
5
  "titleLang": {
6
6
  "en": "Android TV Remote",
7
7
  "de": "Android TV Remote",
@@ -31,7 +31,6 @@
31
31
  "authors": [
32
32
  "Henrik Schönhofen <henrik.schoenhofen@icloud.com>"
33
33
  ],
34
- "license": "MIT",
35
34
  "platform": "Javascript/Node.js",
36
35
  "mode": "daemon",
37
36
  "enabled": true,
@@ -71,22 +70,31 @@
71
70
  "tier": 3,
72
71
  "type": "multimedia",
73
72
  "news": {
73
+ "0.2.7": {
74
+ "en": "Added automated GitHub Actions release workflow for GitHub and npm publishing.",
75
+ "de": "Automatischen GitHub-Actions-Release-Workflow für Veröffentlichungen auf GitHub und npm eingerichtet.",
76
+ "ru": "Добавлен автоматизированный процесс публикации на GitHub и npm через GitHub Actions.",
77
+ "pt": "Adicionado um fluxo automatizado do GitHub Actions para publicação no GitHub e npm.",
78
+ "nl": "Geautomatiseerde GitHub Actions-workflow toegevoegd voor publicatie op GitHub en npm.",
79
+ "fr": "Ajout d'un workflow GitHub Actions automatisé pour la publication sur GitHub et npm.",
80
+ "it": "Aggiunto un workflow GitHub Actions automatizzato per la pubblicazione su GitHub e npm.",
81
+ "es": "Añadido un flujo de trabajo automatizado de GitHub Actions para publicar en GitHub y npm.",
82
+ "pl": "Dodano automatyczny proces GitHub Actions do publikowania w GitHub i npm.",
83
+ "uk": "Додано автоматизований процес GitHub Actions для публікації на GitHub і npm.",
84
+ "zh-cn": "新增用于发布到 GitHub 和 npm 的自动化 GitHub Actions 工作流程."
85
+ },
74
86
  "0.2.6": {
75
87
  "en": "Improved pairing credential persistence. Credentials are no longer deleted automatically on unpaired events.",
76
- "de": "Verbesserte Speicherung der Pairing-Zugangsdaten. Bei Unpaired-Ereignissen werden Zertifikate nicht mehr automatisch gelöscht."
77
- },
78
- "0.2.5": {
79
- "en": "Initial release with Android TV Remote v2 support, secure pairing, power and volume states, current app detection, remote control commands and optional Google Cast media information.",
80
- "de": "Erste Veröffentlichung mit Android TV Remote v2, sicherem Pairing, Power- und Lautstärkestatus, Erkennung der aktuellen App, Fernbedienungsbefehlen und optionalen Google-Cast-Medieninformationen.",
81
- "ru": "Первый выпуск с поддержкой Android TV Remote v2, безопасным сопряжением и дистанционным управлением.",
82
- "pt": "Versão inicial com suporte ao Android TV Remote v2, emparelhamento seguro e controlo remoto.",
83
- "nl": "Eerste release met Android TV Remote v2-ondersteuning, veilige koppeling en afstandsbediening.",
84
- "fr": "Première version avec prise en charge d’Android TV Remote v2, appairage sécurisé et télécommande.",
85
- "it": "Prima versione con supporto Android TV Remote v2, associazione sicura e controllo remoto.",
86
- "es": "Versión inicial con compatibilidad con Android TV Remote v2, emparejamiento seguro y control remoto.",
87
- "pl": "Pierwsza wersja z obsługą Android TV Remote v2, bezpiecznym parowaniem i zdalnym sterowaniem.",
88
- "uk": "Перший випуск із підтримкою Android TV Remote v2, безпечним сполученням і дистанційним керуванням.",
89
- "zh-cn": "首个版本,支持 Android TV Remote v2、安全配对和远程控制。"
88
+ "de": "Verbesserte Speicherung der Pairing-Zugangsdaten. Bei Unpaired-Ereignissen werden Zertifikate nicht mehr automatisch gelöscht.",
89
+ "ru": "Улучшено сохранение данных сопряжения. Сертификаты больше не удаляются автоматически при потере сопряжения.",
90
+ "pt": "Melhorada a persistência das credenciais de emparelhamento. Os certificados deixam de ser eliminados automaticamente quando ocorre um evento de desemparelhamento.",
91
+ "nl": "Opslag van koppelgegevens verbeterd. Certificaten worden niet langer automatisch verwijderd bij ontkoppelingsgebeurtenissen.",
92
+ "fr": "Amélioration de la conservation des identifiants d'appairage. Les certificats ne sont plus supprimés automatiquement lors d'un événement de désappairage.",
93
+ "it": "Migliorata la conservazione delle credenziali di associazione. I certificati non vengono più eliminati automaticamente in caso di disassociazione.",
94
+ "es": "Mejorada la conservación de las credenciales de emparejamiento. Los certificados ya no se eliminan automáticamente al producirse una desvinculación.",
95
+ "pl": "Ulepszono przechowywanie danych parowania. Certyfikaty nie są już automatycznie usuwane podczas zdarzeń rozparowania.",
96
+ "uk": "Покращено збереження даних сполучення. Сертифікати більше не видаляються автоматично під час роз'єднання.",
97
+ "zh-cn": "改进配对凭据的持久化存储。发生取消配对事件时不再自动删除证书."
90
98
  }
91
99
  }
92
100
  },
package/main.js CHANGED
@@ -57,34 +57,206 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
57
57
 
58
58
  async ensureObjects() {
59
59
  const defs = {
60
- 'info.connection': { name: 'Connected', type: 'boolean', role: 'indicator.connected', read: true, write: false, def: false },
61
- 'info.lastRemoteReady': { name: 'Last remote connection', type: 'number', role: 'value.time', read: true, write: false, def: 0 },
62
- 'info.pairingRequired': { name: 'Pairing required', type: 'boolean', role: 'indicator', read: true, write: false, def: false },
63
- 'info.powered': { name: 'Powered', type: 'boolean', role: 'indicator.power', read: true, write: false, def: false },
64
- 'info.currentAppId': { name: 'Current app package', type: 'string', role: 'text', read: true, write: false, def: '' },
65
- 'info.currentApp': { name: 'Current app', type: 'string', role: 'text', read: true, write: false, def: '' },
66
- 'info.currentAppUnknown': { name: 'Unknown current app package', type: 'string', role: 'text', read: true, write: false, def: '' },
67
- 'info.cast.connection': { name: 'Cast connected', type: 'boolean', role: 'indicator.connected', read: true, write: false, def: false },
68
- 'info.cast.appId': { name: 'Cast app ID', type: 'string', role: 'text', read: true, write: false, def: '' },
69
- 'info.cast.appName': { name: 'Cast app name', type: 'string', role: 'text', read: true, write: false, def: '' },
70
- 'info.media.title': { name: 'Media title', type: 'string', role: 'media.title', read: true, write: false, def: '' },
71
- 'info.media.subtitle': { name: 'Media subtitle', type: 'string', role: 'text', read: true, write: false, def: '' },
72
- 'info.media.playerState': { name: 'Media player state', type: 'string', role: 'media.state', read: true, write: false, def: '' },
73
- 'info.media.contentId': { name: 'Media content ID', type: 'string', role: 'text', read: true, write: false, def: '' },
74
- 'info.volume.level': { name: 'Volume level', type: 'number', role: 'level.volume', read: true, write: false, def: 0 },
75
- 'info.volume.maximum': { name: 'Maximum volume', type: 'number', role: 'value', read: true, write: false, def: 0 },
76
- 'info.volume.muted': { name: 'Muted', type: 'boolean', role: 'indicator.mute', read: true, write: false, def: false },
77
- 'control.pairingCode': { name: 'Pairing code', type: 'string', role: 'text', read: true, write: true, def: '' },
78
- 'control.key': { name: 'Remote key', type: 'string', role: 'text', read: true, write: true, def: '' },
79
- 'control.text': { name: 'Send text', type: 'string', role: 'text', read: true, write: true, def: '' },
80
- 'control.appLink': { name: 'Open app/deep link', type: 'string', role: 'text.url', read: true, write: true, def: '' },
81
- 'control.power': { name: 'Power target / toggle', type: 'boolean', role: 'switch.power', read: true, write: true, def: false },
82
- 'control.reconnect': { name: 'Reconnect', type: 'boolean', role: 'button', read: true, write: true, def: false },
83
- 'control.forgetPairing': { name: 'Forget pairing', type: 'boolean', role: 'button', read: true, write: true, def: false }
60
+ 'info.connection': {
61
+ name: 'Connected',
62
+ type: 'boolean',
63
+ role: 'indicator.connected',
64
+ read: true,
65
+ write: false,
66
+ def: false,
67
+ },
68
+ 'info.lastRemoteReady': {
69
+ name: 'Last remote connection',
70
+ type: 'number',
71
+ role: 'value.time',
72
+ read: true,
73
+ write: false,
74
+ def: 0,
75
+ },
76
+ 'info.pairingRequired': {
77
+ name: 'Pairing required',
78
+ type: 'boolean',
79
+ role: 'indicator',
80
+ read: true,
81
+ write: false,
82
+ def: false,
83
+ },
84
+ 'info.powered': {
85
+ name: 'Powered',
86
+ type: 'boolean',
87
+ role: 'indicator.power',
88
+ read: true,
89
+ write: false,
90
+ def: false,
91
+ },
92
+ 'info.currentAppId': {
93
+ name: 'Current app package',
94
+ type: 'string',
95
+ role: 'text',
96
+ read: true,
97
+ write: false,
98
+ def: '',
99
+ },
100
+ 'info.currentApp': {
101
+ name: 'Current app',
102
+ type: 'string',
103
+ role: 'text',
104
+ read: true,
105
+ write: false,
106
+ def: '',
107
+ },
108
+ 'info.currentAppUnknown': {
109
+ name: 'Unknown current app package',
110
+ type: 'string',
111
+ role: 'text',
112
+ read: true,
113
+ write: false,
114
+ def: '',
115
+ },
116
+ 'info.cast.connection': {
117
+ name: 'Cast connected',
118
+ type: 'boolean',
119
+ role: 'indicator.connected',
120
+ read: true,
121
+ write: false,
122
+ def: false,
123
+ },
124
+ 'info.cast.appId': {
125
+ name: 'Cast app ID',
126
+ type: 'string',
127
+ role: 'text',
128
+ read: true,
129
+ write: false,
130
+ def: '',
131
+ },
132
+ 'info.cast.appName': {
133
+ name: 'Cast app name',
134
+ type: 'string',
135
+ role: 'text',
136
+ read: true,
137
+ write: false,
138
+ def: '',
139
+ },
140
+ 'info.media.title': {
141
+ name: 'Media title',
142
+ type: 'string',
143
+ role: 'media.title',
144
+ read: true,
145
+ write: false,
146
+ def: '',
147
+ },
148
+ 'info.media.subtitle': {
149
+ name: 'Media subtitle',
150
+ type: 'string',
151
+ role: 'text',
152
+ read: true,
153
+ write: false,
154
+ def: '',
155
+ },
156
+ 'info.media.playerState': {
157
+ name: 'Media player state',
158
+ type: 'string',
159
+ role: 'media.state',
160
+ read: true,
161
+ write: false,
162
+ def: '',
163
+ },
164
+ 'info.media.contentId': {
165
+ name: 'Media content ID',
166
+ type: 'string',
167
+ role: 'text',
168
+ read: true,
169
+ write: false,
170
+ def: '',
171
+ },
172
+ 'info.volume.level': {
173
+ name: 'Volume level',
174
+ type: 'number',
175
+ role: 'level.volume',
176
+ read: true,
177
+ write: false,
178
+ def: 0,
179
+ },
180
+ 'info.volume.maximum': {
181
+ name: 'Maximum volume',
182
+ type: 'number',
183
+ role: 'value',
184
+ read: true,
185
+ write: false,
186
+ def: 0,
187
+ },
188
+ 'info.volume.muted': {
189
+ name: 'Muted',
190
+ type: 'boolean',
191
+ role: 'indicator.mute',
192
+ read: true,
193
+ write: false,
194
+ def: false,
195
+ },
196
+ 'control.pairingCode': {
197
+ name: 'Pairing code',
198
+ type: 'string',
199
+ role: 'text',
200
+ read: true,
201
+ write: true,
202
+ def: '',
203
+ },
204
+ 'control.key': {
205
+ name: 'Remote key',
206
+ type: 'string',
207
+ role: 'text',
208
+ read: true,
209
+ write: true,
210
+ def: '',
211
+ },
212
+ 'control.text': {
213
+ name: 'Send text',
214
+ type: 'string',
215
+ role: 'text',
216
+ read: true,
217
+ write: true,
218
+ def: '',
219
+ },
220
+ 'control.appLink': {
221
+ name: 'Open app/deep link',
222
+ type: 'string',
223
+ role: 'text.url',
224
+ read: true,
225
+ write: true,
226
+ def: '',
227
+ },
228
+ 'control.power': {
229
+ name: 'Power target / toggle',
230
+ type: 'boolean',
231
+ role: 'switch.power',
232
+ read: true,
233
+ write: true,
234
+ def: false,
235
+ },
236
+ 'control.reconnect': {
237
+ name: 'Reconnect',
238
+ type: 'boolean',
239
+ role: 'button',
240
+ read: true,
241
+ write: true,
242
+ def: false,
243
+ },
244
+ 'control.forgetPairing': {
245
+ name: 'Forget pairing',
246
+ type: 'boolean',
247
+ role: 'button',
248
+ read: true,
249
+ write: true,
250
+ def: false,
251
+ },
84
252
  };
85
253
 
86
254
  for (const [id, common] of Object.entries(defs)) {
87
- await this.setObjectNotExistsAsync(id, { type: 'state', common, native: {} });
255
+ await this.setObjectNotExistsAsync(id, {
256
+ type: 'state',
257
+ common,
258
+ native: {},
259
+ });
88
260
  }
89
261
  }
90
262
 
@@ -104,29 +276,45 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
104
276
  }
105
277
 
106
278
  async saveCertificate(cert) {
107
- if (!cert || !cert.key || !cert.cert) return;
279
+ if (!cert || !cert.key || !cert.cert) {
280
+ return;
281
+ }
108
282
  const tmp = `${this.credentialsFile}.tmp`;
109
- await fs.promises.writeFile(tmp, JSON.stringify(cert, null, 2), { mode: 0o600 });
283
+ await fs.promises.writeFile(tmp, JSON.stringify(cert, null, 2), {
284
+ mode: 0o600,
285
+ });
110
286
  await fs.promises.rename(tmp, this.credentialsFile);
111
- try { await fs.promises.chmod(this.credentialsFile, 0o600); } catch (_) {}
287
+ try {
288
+ await fs.promises.chmod(this.credentialsFile, 0o600);
289
+ } catch {
290
+ // Ignore cleanup errors.
291
+ }
112
292
  }
113
293
 
114
294
  async deleteCertificate() {
115
295
  try {
116
296
  await fs.promises.unlink(this.credentialsFile);
117
297
  } catch (error) {
118
- if (error && error.code !== 'ENOENT') throw error;
298
+ if (error && error.code !== 'ENOENT') {
299
+ throw error;
300
+ }
119
301
  }
120
302
  }
121
303
 
122
304
  async connect() {
123
- if (this.stopping || this.connecting || !this.remoteModule) return;
305
+ if (this.stopping || this.connecting || !this.remoteModule) {
306
+ return;
307
+ }
124
308
  this.connecting = true;
125
309
  this.clearReconnect();
126
310
 
127
311
  try {
128
312
  if (this.remote) {
129
- try { this.remote.stop(); } catch (_) {}
313
+ try {
314
+ this.remote.stop();
315
+ } catch {
316
+ // Ignore cleanup errors.
317
+ }
130
318
  this.remote = null;
131
319
  }
132
320
 
@@ -135,9 +323,11 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
135
323
  pairing_port: Number(this.config.pairingPort) || 6467,
136
324
  remote_port: Number(this.config.remotePort) || 6466,
137
325
  service_name: String(this.config.deviceName || 'ioBroker'),
138
- debug: !!this.config.debugProtocol
326
+ debug: !!this.config.debugProtocol,
139
327
  };
140
- if (cert) options.cert = cert;
328
+ if (cert) {
329
+ options.cert = cert;
330
+ }
141
331
 
142
332
  const { createAndroidRemote } = this.remoteModule;
143
333
  this.remote = createAndroidRemote(String(this.config.host).trim(), options);
@@ -147,9 +337,15 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
147
337
  const started = await this.remote.start();
148
338
  if (!started) {
149
339
  await this.setStateAsync('info.connection', false, true);
150
- this.log.info(`Android TV derzeit nicht erreichbar – neuer Versuch in ${Math.max(5, Number(this.config.reconnectSeconds) || 15)} Sekunden.`);
340
+ this.log.info(
341
+ `Android TV derzeit nicht erreichbar – neuer Versuch in ${Math.max(5, Number(this.config.reconnectSeconds) || 15)} Sekunden.`,
342
+ );
151
343
  if (this.remote) {
152
- try { this.remote.stop(); } catch (_) {}
344
+ try {
345
+ this.remote.stop();
346
+ } catch {
347
+ // Ignore cleanup errors.
348
+ }
153
349
  this.remote = null;
154
350
  }
155
351
  this.scheduleReconnect();
@@ -192,18 +388,23 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
192
388
  });
193
389
 
194
390
  remote.on('volume', async volume => {
195
- if (!volume) return;
391
+ if (!volume) {
392
+ return;
393
+ }
196
394
  await this.setStateAsync('info.volume.level', Number(volume.level) || 0, true);
197
395
  await this.setStateAsync('info.volume.maximum', Number(volume.maximum) || 0, true);
198
396
  await this.setStateAsync('info.volume.muted', !!volume.muted, true);
199
397
  });
200
398
 
201
399
  remote.on('current_app', async app => {
202
- const appId = typeof app === 'string'
203
- ? app.trim()
204
- : String(app?.packageName || app?.package || app?.app || '').trim();
400
+ const appId =
401
+ typeof app === 'string'
402
+ ? app.trim()
403
+ : String(app?.packageName || app?.package || app?.app || '').trim();
205
404
 
206
- if (!appId) return;
405
+ if (!appId) {
406
+ return;
407
+ }
207
408
 
208
409
  const mapped = this.appNameMap()[appId];
209
410
  await this.setStateAsync('info.currentAppId', appId, true);
@@ -223,12 +424,14 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
223
424
  // control.forgetPairing bewusst gelöscht.
224
425
  this.log.warn(
225
426
  'Android TV meldet "unpaired". Gespeichertes Pairing bleibt erhalten. ' +
226
- 'Falls ein neues Pairing wirklich erforderlich ist, control.forgetPairing verwenden.'
427
+ 'Falls ein neues Pairing wirklich erforderlich ist, control.forgetPairing verwenden.',
227
428
  );
228
429
  });
229
430
 
230
431
  remote.on('error', async error => {
231
- if (this.stopping) return;
432
+ if (this.stopping) {
433
+ return;
434
+ }
232
435
  await this.setStateAsync('info.connection', false, true);
233
436
  this.log.warn(`Android-TV-Protokollfehler: ${this.errorText(error)}`);
234
437
  this.scheduleReconnect();
@@ -236,7 +439,9 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
236
439
  }
237
440
 
238
441
  scheduleReconnect() {
239
- if (this.stopping || this.reconnectTimer || this.pairingRequested) return;
442
+ if (this.stopping || this.reconnectTimer || this.pairingRequested) {
443
+ return;
444
+ }
240
445
  const seconds = Math.max(5, Number(this.config.reconnectSeconds) || 15);
241
446
  this.reconnectTimer = this.setTimeout(() => {
242
447
  this.reconnectTimer = null;
@@ -252,21 +457,29 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
252
457
  }
253
458
 
254
459
  async onStateChange(id, state) {
255
- if (!state || state.ack) return;
460
+ if (!state || state.ack) {
461
+ return;
462
+ }
256
463
  const prefix = `${this.namespace}.`;
257
- if (!id.startsWith(prefix)) return;
464
+ if (!id.startsWith(prefix)) {
465
+ return;
466
+ }
258
467
  const rel = id.slice(prefix.length);
259
468
 
260
469
  try {
261
470
  switch (rel) {
262
471
  case 'control.pairingCode': {
263
472
  const code = String(state.val || '').trim();
264
- if (!code) return;
473
+ if (!code) {
474
+ return;
475
+ }
265
476
  if (!this.remote || !this.pairingRequested) {
266
477
  this.log.warn('Aktuell wartet der Adapter auf keinen Pairing-Code.');
267
478
  } else {
268
479
  const accepted = this.remote.sendCode(code);
269
- if (!accepted) this.log.warn('Pairing-Code konnte nicht gesendet werden.');
480
+ if (!accepted) {
481
+ this.log.warn('Pairing-Code konnte nicht gesendet werden.');
482
+ }
270
483
  }
271
484
  await this.setStateAsync(rel, '', true);
272
485
  break;
@@ -274,21 +487,27 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
274
487
 
275
488
  case 'control.key': {
276
489
  const key = String(state.val || '').trim();
277
- if (key) this.sendKey(key);
490
+ if (key) {
491
+ this.sendKey(key);
492
+ }
278
493
  await this.setStateAsync(rel, '', true);
279
494
  break;
280
495
  }
281
496
 
282
497
  case 'control.text': {
283
498
  const text = String(state.val ?? '');
284
- if (text && this.requireRemote()) this.remote.sendText(text);
499
+ if (text && this.requireRemote()) {
500
+ this.remote.sendText(text);
501
+ }
285
502
  await this.setStateAsync(rel, '', true);
286
503
  break;
287
504
  }
288
505
 
289
506
  case 'control.appLink': {
290
507
  const link = String(state.val || '').trim();
291
- if (link && this.requireRemote()) this.remote.sendAppLink(link);
508
+ if (link && this.requireRemote()) {
509
+ this.remote.sendAppLink(link);
510
+ }
292
511
  await this.setStateAsync(rel, '', true);
293
512
  break;
294
513
  }
@@ -317,7 +536,11 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
317
536
  await this.deleteCertificate();
318
537
  this.pairingRequested = false;
319
538
  if (this.remote) {
320
- try { this.remote.stop(); } catch (_) {}
539
+ try {
540
+ this.remote.stop();
541
+ } catch {
542
+ // Ignore cleanup errors.
543
+ }
321
544
  this.remote = null;
322
545
  }
323
546
  await this.setStateAsync('info.connection', false, true);
@@ -332,8 +555,6 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
332
555
  }
333
556
  }
334
557
 
335
-
336
-
337
558
  appNameMap() {
338
559
  return {
339
560
  // Launcher / System
@@ -378,15 +599,19 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
378
599
  'com.plexapp.android': 'Plex',
379
600
  'org.xbmc.kodi': 'Kodi',
380
601
  'org.videolan.vlc': 'VLC',
381
- 'tv.twitch.android.app': 'Twitch'
602
+ 'tv.twitch.android.app': 'Twitch',
382
603
  };
383
604
  }
384
605
 
385
606
  friendlyAppName(appId) {
386
607
  const id = String(appId || '').trim();
387
- if (!id) return '';
608
+ if (!id) {
609
+ return '';
610
+ }
388
611
  const known = this.appNameMap();
389
- if (known[id]) return known[id];
612
+ if (known[id]) {
613
+ return known[id];
614
+ }
390
615
 
391
616
  // Lesbarer Fallback, Paket-ID bleibt zusätzlich in currentAppUnknown erhalten.
392
617
  const parts = id.split('.').filter(Boolean);
@@ -395,10 +620,14 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
395
620
  }
396
621
 
397
622
  startCastMonitor() {
398
- if (this.config.enableCast === false) return;
623
+ if (this.config.enableCast === false) {
624
+ return;
625
+ }
399
626
  const interval = Math.max(3, Number(this.config.castPollSeconds) || 5);
400
627
  const run = async () => {
401
- if (!this.stopping) await this.pollCast();
628
+ if (!this.stopping) {
629
+ await this.pollCast();
630
+ }
402
631
  };
403
632
  void run();
404
633
  this.castTimer = this.setInterval(run, interval * 1000);
@@ -410,14 +639,20 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
410
639
  this.castTimer = null;
411
640
  }
412
641
  if (this.castClient) {
413
- try { this.castClient.close(); } catch (_) {}
642
+ try {
643
+ this.castClient.close();
644
+ } catch {
645
+ // Ignore cleanup errors.
646
+ }
414
647
  this.castClient = null;
415
648
  this.castPlayer = null;
416
649
  }
417
650
  }
418
651
 
419
652
  async pollCast() {
420
- if (this.castConnecting || !this.config.host) return;
653
+ if (this.castConnecting || !this.config.host) {
654
+ return;
655
+ }
421
656
  this.castConnecting = true;
422
657
  try {
423
658
  await this.ensureCastConnected();
@@ -432,7 +667,9 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
432
667
  await this.setStateAsync('info.cast.appName', String(session.displayName || ''), true);
433
668
 
434
669
  const status = await this.castJoinAndGetStatus(session);
435
- if (!status) return;
670
+ if (!status) {
671
+ return;
672
+ }
436
673
  const media = status.media || {};
437
674
  const metadata = media.metadata || {};
438
675
  const title = metadata.title || metadata.seriesTitle || metadata.albumName || '';
@@ -444,9 +681,15 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
444
681
  } catch (error) {
445
682
  await this.setStateAsync('info.cast.connection', false, true);
446
683
  this.castPlayer = null;
447
- if (this.config.debugProtocol) this.log.debug(`Cast-Abfrage: ${this.errorText(error)}`);
684
+ if (this.config.debugProtocol) {
685
+ this.log.debug(`Cast-Abfrage: ${this.errorText(error)}`);
686
+ }
448
687
  if (this.castClient) {
449
- try { this.castClient.close(); } catch (_) {}
688
+ try {
689
+ this.castClient.close();
690
+ } catch {
691
+ // Ignore cleanup errors.
692
+ }
450
693
  this.castClient = null;
451
694
  }
452
695
  } finally {
@@ -455,24 +698,36 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
455
698
  }
456
699
 
457
700
  ensureCastConnected() {
458
- if (this.castClient) return Promise.resolve();
701
+ if (this.castClient) {
702
+ return Promise.resolve();
703
+ }
459
704
  return new Promise((resolve, reject) => {
460
705
  const client = new CastClient();
461
706
  let settled = false;
462
707
  const fail = err => {
463
- if (settled) return;
708
+ if (settled) {
709
+ return;
710
+ }
464
711
  settled = true;
465
- try { client.close(); } catch (_) {}
712
+ try {
713
+ client.close();
714
+ } catch {
715
+ // Ignore cleanup errors.
716
+ }
466
717
  reject(err instanceof Error ? err : new Error(String(err)));
467
718
  };
468
719
  client.once('error', fail);
469
720
  client.connect(String(this.config.host).trim(), () => {
470
- if (settled) return;
721
+ if (settled) {
722
+ return;
723
+ }
471
724
  settled = true;
472
725
  client.removeListener('error', fail);
473
726
  client.on('error', () => {
474
727
  void this.setStateAsync('info.cast.connection', false, true);
475
- if (this.castClient === client) this.castClient = null;
728
+ if (this.castClient === client) {
729
+ this.castClient = null;
730
+ }
476
731
  });
477
732
  this.castClient = client;
478
733
  void this.setStateAsync('info.cast.connection', true, true);
@@ -483,17 +738,21 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
483
738
 
484
739
  castGetSessions() {
485
740
  return new Promise((resolve, reject) => {
486
- this.castClient.getSessions((err, sessions) => err ? reject(err) : resolve(sessions || []));
741
+ this.castClient.getSessions((err, sessions) => (err ? reject(err) : resolve(sessions || [])));
487
742
  });
488
743
  }
489
744
 
490
745
  castJoinAndGetStatus(session) {
491
746
  return new Promise((resolve, reject) => {
492
747
  this.castClient.join(session, DefaultMediaReceiver, (err, player) => {
493
- if (err) return reject(err);
748
+ if (err) {
749
+ return reject(err);
750
+ }
494
751
  this.castPlayer = player;
495
752
  player.getStatus((statusErr, status) => {
496
- if (statusErr) return reject(statusErr);
753
+ if (statusErr) {
754
+ return reject(statusErr);
755
+ }
497
756
  resolve(status || null);
498
757
  });
499
758
  });
@@ -510,18 +769,41 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
510
769
  }
511
770
 
512
771
  sendKey(input) {
513
- if (!this.requireRemote()) return;
772
+ if (!this.requireRemote()) {
773
+ return;
774
+ }
514
775
  const { RemoteKeyCode } = this.remoteModule;
515
776
  const normalized = input.trim().toUpperCase().replace(/[ -]+/g, '_');
516
777
  const aliases = {
517
- UP: 'KEYCODE_DPAD_UP', DOWN: 'KEYCODE_DPAD_DOWN', LEFT: 'KEYCODE_DPAD_LEFT', RIGHT: 'KEYCODE_DPAD_RIGHT',
518
- OK: 'KEYCODE_DPAD_CENTER', ENTER: 'KEYCODE_DPAD_CENTER', CENTER: 'KEYCODE_DPAD_CENTER', SELECT: 'KEYCODE_DPAD_CENTER',
519
- HOME: 'KEYCODE_HOME', BACK: 'KEYCODE_BACK', MENU: 'KEYCODE_MENU', POWER: 'KEYCODE_POWER',
520
- PLAY_PAUSE: 'KEYCODE_MEDIA_PLAY_PAUSE', PLAY: 'KEYCODE_MEDIA_PLAY', PAUSE: 'KEYCODE_MEDIA_PAUSE', STOP: 'KEYCODE_MEDIA_STOP',
521
- NEXT: 'KEYCODE_MEDIA_NEXT', PREVIOUS: 'KEYCODE_MEDIA_PREVIOUS', REWIND: 'KEYCODE_MEDIA_REWIND', FAST_FORWARD: 'KEYCODE_MEDIA_FAST_FORWARD',
522
- VOLUME_UP: 'KEYCODE_VOLUME_UP', VOLUP: 'KEYCODE_VOLUME_UP', VOLUME_DOWN: 'KEYCODE_VOLUME_DOWN', VOLDOWN: 'KEYCODE_VOLUME_DOWN', MUTE: 'KEYCODE_VOLUME_MUTE'
778
+ UP: 'KEYCODE_DPAD_UP',
779
+ DOWN: 'KEYCODE_DPAD_DOWN',
780
+ LEFT: 'KEYCODE_DPAD_LEFT',
781
+ RIGHT: 'KEYCODE_DPAD_RIGHT',
782
+ OK: 'KEYCODE_DPAD_CENTER',
783
+ ENTER: 'KEYCODE_DPAD_CENTER',
784
+ CENTER: 'KEYCODE_DPAD_CENTER',
785
+ SELECT: 'KEYCODE_DPAD_CENTER',
786
+ HOME: 'KEYCODE_HOME',
787
+ BACK: 'KEYCODE_BACK',
788
+ MENU: 'KEYCODE_MENU',
789
+ POWER: 'KEYCODE_POWER',
790
+ PLAY_PAUSE: 'KEYCODE_MEDIA_PLAY_PAUSE',
791
+ PLAY: 'KEYCODE_MEDIA_PLAY',
792
+ PAUSE: 'KEYCODE_MEDIA_PAUSE',
793
+ STOP: 'KEYCODE_MEDIA_STOP',
794
+ NEXT: 'KEYCODE_MEDIA_NEXT',
795
+ PREVIOUS: 'KEYCODE_MEDIA_PREVIOUS',
796
+ REWIND: 'KEYCODE_MEDIA_REWIND',
797
+ FAST_FORWARD: 'KEYCODE_MEDIA_FAST_FORWARD',
798
+ VOLUME_UP: 'KEYCODE_VOLUME_UP',
799
+ VOLUP: 'KEYCODE_VOLUME_UP',
800
+ VOLUME_DOWN: 'KEYCODE_VOLUME_DOWN',
801
+ VOLDOWN: 'KEYCODE_VOLUME_DOWN',
802
+ MUTE: 'KEYCODE_VOLUME_MUTE',
523
803
  };
524
- const enumName = normalized.startsWith('KEYCODE_') ? normalized : (aliases[normalized] || `KEYCODE_${normalized}`);
804
+ const enumName = normalized.startsWith('KEYCODE_')
805
+ ? normalized
806
+ : aliases[normalized] || `KEYCODE_${normalized}`;
525
807
  const code = RemoteKeyCode[enumName];
526
808
  if (code === undefined) {
527
809
  this.log.warn(`Unbekannte Remote-Taste: ${input} (${enumName})`);
@@ -549,10 +831,14 @@ class AndroidTvRemoteAdapter extends utils.Adapter {
549
831
  this.stopCastMonitor();
550
832
  await this.setStateAsync('info.connection', false, true);
551
833
  if (this.remote) {
552
- try { this.remote.stop(); } catch (_) {}
834
+ try {
835
+ this.remote.stop();
836
+ } catch {
837
+ // Ignore cleanup errors.
838
+ }
553
839
  this.remote = null;
554
840
  }
555
- } catch (_) {
841
+ } catch {
556
842
  // ignore during shutdown
557
843
  } finally {
558
844
  callback();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.androidtvremote",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "ioBroker adapter for Android TV / Google TV using the Android TV Remote v2 protocol",
5
5
  "author": {
6
6
  "name": "Henrik Schönhofen",
@@ -9,7 +9,7 @@
9
9
  "homepage": "https://github.com/backfisch88/ioBroker.androidtvremote",
10
10
  "license": "MIT",
11
11
  "keywords": [
12
- "iobroker",
12
+ "ioBroker",
13
13
  "android tv",
14
14
  "google tv",
15
15
  "remote",
@@ -37,5 +37,28 @@
37
37
  },
38
38
  "bugs": {
39
39
  "url": "https://github.com/backfisch88/ioBroker.androidtvremote/issues"
40
+ },
41
+ "scripts": {
42
+ "test": "npm run test:package && npm run test:unit",
43
+ "lint": "eslint main.js",
44
+ "check": "node --check main.js",
45
+ "test:package": "node ./node_modules/@iobroker/testing/node_modules/mocha/bin/mocha.js test/package.test.js",
46
+ "test:integration": "node ./node_modules/@iobroker/testing/node_modules/mocha/bin/mocha.js test/integration.test.js",
47
+ "test:unit": "node --check main.js",
48
+ "typecheck": "tsc",
49
+ "release": "release-script",
50
+ "dev:build": "adapter-dev build"
51
+ },
52
+ "devDependencies": {
53
+ "@alcalzone/release-script": "^5.2.1",
54
+ "@alcalzone/release-script-plugin-iobroker": "^5.2.0",
55
+ "@alcalzone/release-script-plugin-license": "^5.2.2",
56
+ "@alcalzone/release-script-plugin-manual-review": "^5.2.0",
57
+ "@iobroker/adapter-dev": "^1.5.0",
58
+ "@iobroker/eslint-config": "^2.3.4",
59
+ "@iobroker/testing": "^6.3.0",
60
+ "@tsconfig/node22": "^22.0.6",
61
+ "@types/node": "^22.20.5",
62
+ "typescript": "^5.9.3"
40
63
  }
41
64
  }