iobroker.androidtv 0.2.2 → 0.2.3

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