iobroker.heos 1.12.2 → 2.1.0
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/.github/dependabot.yml +0 -3
- package/.github/workflows/test-and-release.yml +1 -1
- package/README.md +11 -0
- package/io-package.json +40 -40
- package/lib/heos-player.js +58 -21
- package/lib/heos-upnp.js +30 -33
- package/main.js +20 -19
- package/package.json +8 -8
package/.github/dependabot.yml
CHANGED
package/README.md
CHANGED
|
@@ -144,6 +144,17 @@ Alternative you can use the script from Uhula: https://forum.iobroker.net/post/4
|
|
|
144
144
|
Placeholder for the next version (at the beginning of the line):
|
|
145
145
|
### **WORK IN PROGRESS**
|
|
146
146
|
-->
|
|
147
|
+
### 2.1.0 (2023-08-05)
|
|
148
|
+
* (withstu) replace got with axios
|
|
149
|
+
* (withstu) improve upnp handling
|
|
150
|
+
* (withstu) prevent duplicate connect messages
|
|
151
|
+
|
|
152
|
+
### 2.0.0 (2023-08-05)
|
|
153
|
+
* (withstu) fix pipelines and remove node 14.x support
|
|
154
|
+
|
|
155
|
+
### 1.12.3 (2023-08-05)
|
|
156
|
+
* (withstu) update dependencies
|
|
157
|
+
|
|
147
158
|
### 1.12.2 (2023-05-13)
|
|
148
159
|
* (withstu) optimize error handling
|
|
149
160
|
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "heos",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"news": {
|
|
6
|
+
"2.1.0": {
|
|
7
|
+
"en": "replace got with axios\nimprove upnp handling\nprevent duplicate connect messages",
|
|
8
|
+
"de": "ersetzen mit axios\nverbesserte upnp-behandlung\ndoppelte verbindungsnachrichten verhindern",
|
|
9
|
+
"ru": "заменить получилось с axios\nулучшить работу upnp\nпредотвратить дублирование сообщений подключения",
|
|
10
|
+
"pt": "substituir adquirido com axios\nmelhorar o manuseio de upnp\nevitar mensagens de conexão duplicadas",
|
|
11
|
+
"nl": "vertaling:\nverbeter de bovenste hand\nverhinder dubbele berichten",
|
|
12
|
+
"fr": "remplacer obtenu par axios\naméliorer la manutention des ailes\nempêcher les messages de connexion",
|
|
13
|
+
"it": "sostituire ottenuto con assios\nmigliorare la gestione della upnp\nimpedire i messaggi di connessione duplicati",
|
|
14
|
+
"es": "reemplazado por axios\nmejorar el manejo de upnp\nevitar duplicados mensajes de conexión",
|
|
15
|
+
"pl": "zastępować\npoprawić\nlinkować",
|
|
16
|
+
"uk": "заміну отримав з аксіосом\nполіпшити процес обробки\nзапобігання дублікатів повідомлень з'єднання",
|
|
17
|
+
"zh-cn": "改为\n改进处理\n防止信息重复"
|
|
18
|
+
},
|
|
19
|
+
"2.0.0": {
|
|
20
|
+
"en": "fix pipelines and remove node 14.x support",
|
|
21
|
+
"de": "pipelines fixieren und knoten 14.x-unterstützung entfernen",
|
|
22
|
+
"ru": "исправить трубопроводы и удалить node 14.x поддержка",
|
|
23
|
+
"pt": "corrigir pipelines e remover o nó 14.x suporte",
|
|
24
|
+
"nl": "verwijder node 14.x ondersteuning",
|
|
25
|
+
"fr": "fixer les pipelines et retirer le support node 14.x",
|
|
26
|
+
"it": "fissare le tubazioni e rimuovere il supporto nodo 14.x",
|
|
27
|
+
"es": "arreglar los oleoductos y eliminar el nodo 14.x soporte",
|
|
28
|
+
"pl": "ustawienie rurociągów i usunięcie węzła 14x",
|
|
29
|
+
"uk": "кріплення трубопроводів і видалення вузла 14.x",
|
|
30
|
+
"zh-cn": "f 配件和取消14x的支助"
|
|
31
|
+
},
|
|
32
|
+
"1.12.3": {
|
|
33
|
+
"en": "update dependencies",
|
|
34
|
+
"de": "aktualisierung der abhängigkeiten",
|
|
35
|
+
"ru": "обновление зависимостей",
|
|
36
|
+
"pt": "dependências",
|
|
37
|
+
"nl": "vertaling:",
|
|
38
|
+
"fr": "mettre à jour les dépendances",
|
|
39
|
+
"it": "aggiornamento dipendenze",
|
|
40
|
+
"es": "dependencias de actualización",
|
|
41
|
+
"pl": "zależności",
|
|
42
|
+
"uk": "оновлення залежності",
|
|
43
|
+
"zh-cn": "a. 最新受扶养人"
|
|
44
|
+
},
|
|
6
45
|
"1.12.2": {
|
|
7
46
|
"en": "optimize error handling",
|
|
8
47
|
"de": "optimierung der fehlerbehandlung",
|
|
@@ -54,45 +93,6 @@
|
|
|
54
93
|
"pl": "ulepszenie przycisków w przeglądarce",
|
|
55
94
|
"uk": "покращувати відтворення всіх кнопок у функції перегляду",
|
|
56
95
|
"zh-cn": "b. 代管特区改善所有但顿作用"
|
|
57
|
-
},
|
|
58
|
-
"1.11.3": {
|
|
59
|
-
"en": "update some dependencies\nimprove failure handling\nimprove play all button in browse feature",
|
|
60
|
-
"de": "einige abhängigkeiten aktualisieren\nverbesserung der fehlerbehebung\nverbessern spielen alle taste in der browser-funktion",
|
|
61
|
-
"ru": "обновить некоторые зависимости\nулучшить обработку сбоев\nулучшить играть все кнопки в функции просмотра",
|
|
62
|
-
"pt": "atualizar algumas dependências\nmelhorar o manuseio de falhas\nmelhorar jogar todos os botões em procurar recurso",
|
|
63
|
-
"nl": "vertaling:\nverbeter falen\nverbeter alle knoppen in browse",
|
|
64
|
-
"fr": "mettre à jour certaines dépendances\naméliorer la manipulation des défaillances\naméliorer jouer tous les boutons dans la fonction de navigation",
|
|
65
|
-
"it": "aggiornare alcune dipendenze\nmigliorare la gestione dei guasti\nmigliorare giocare tutto il pulsante nella funzione di navigazione",
|
|
66
|
-
"es": "actualizar algunas dependencias\nmejorar el manejo de fallos\nmejorar el juego de todos los botones en la función de navegación",
|
|
67
|
-
"pl": "aktualizacja\npolepszenie\nulepszenie przycisków w przeglądarce",
|
|
68
|
-
"uk": "оновлення деяких залежностей\nполіпшення безпеки\nпокращувати відтворення всіх кнопок у функції перегляду",
|
|
69
|
-
"zh-cn": "二. 更新一些依赖性\n改进失败\nb. 代管特区改善所有但顿作用"
|
|
70
|
-
},
|
|
71
|
-
"1.11.2": {
|
|
72
|
-
"en": "adopt to new adapter structure",
|
|
73
|
-
"de": "neue adapterstruktur übernehmen",
|
|
74
|
-
"ru": "принять к новой структуре адаптера",
|
|
75
|
-
"pt": "adotar a nova estrutura do adaptador",
|
|
76
|
-
"nl": "adopteren",
|
|
77
|
-
"fr": "adopter à la nouvelle structure de l'adaptateur",
|
|
78
|
-
"it": "adottare per la nuova struttura adattatore",
|
|
79
|
-
"es": "adoptar a nueva estructura de adaptador",
|
|
80
|
-
"pl": "zaadoptowanie nowej struktury adapterowej",
|
|
81
|
-
"uk": "прийняти до нової структури адаптера",
|
|
82
|
-
"zh-cn": "通过新的适应结构"
|
|
83
|
-
},
|
|
84
|
-
"1.11.1": {
|
|
85
|
-
"en": "fix release",
|
|
86
|
-
"de": "zurück zur übersicht",
|
|
87
|
-
"ru": "исправить релиз",
|
|
88
|
-
"pt": "liberação de correção",
|
|
89
|
-
"nl": "loslaten",
|
|
90
|
-
"fr": "correction",
|
|
91
|
-
"it": "rilascio corretto",
|
|
92
|
-
"es": "corrección de liberación",
|
|
93
|
-
"pl": "naprawa",
|
|
94
|
-
"uk": "фіксувати реліз",
|
|
95
|
-
"zh-cn": "f 固定释放"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"title": "HEOS",
|
package/lib/heos-player.js
CHANGED
|
@@ -86,7 +86,9 @@ class HeosPlayer {
|
|
|
86
86
|
this.heos.initPlayerStatistics(this.ip);
|
|
87
87
|
|
|
88
88
|
//Init Upnp
|
|
89
|
-
this.upnp
|
|
89
|
+
if(this.upnp === undefined) {
|
|
90
|
+
this.upnp = new HeosUPnP(this.heos, player.ip);
|
|
91
|
+
}
|
|
90
92
|
|
|
91
93
|
//Channel
|
|
92
94
|
await this.heos.setObjectAsync(this.base_state_path, {
|
|
@@ -1355,15 +1357,26 @@ class HeosPlayer {
|
|
|
1355
1357
|
}
|
|
1356
1358
|
|
|
1357
1359
|
async updateUpnp(){
|
|
1358
|
-
if(this.
|
|
1359
|
-
|
|
1360
|
+
if(this.connected){
|
|
1361
|
+
if(this.upnp.getServiceList() && Object.keys(this.upnp.getServiceList()).length === 0){
|
|
1362
|
+
try{
|
|
1363
|
+
await this.upnp.init();
|
|
1364
|
+
} catch(err){
|
|
1365
|
+
if(err.code != 'ECONNABORTED') {
|
|
1366
|
+
this.heos.raiseFailures(this.ip, ERROR_CODES.Upnp);
|
|
1367
|
+
this.logWarn('[upnp] failed to init upnp: ' + err, false);
|
|
1368
|
+
} else {
|
|
1369
|
+
this.logDebug('[upnp] failed to init upnp: ' + err, false);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
this.logDebug('[upnp] Allowed Services: ' + JSON.stringify(this.upnp.getServiceList()));
|
|
1374
|
+
await this.getUpnpRenderingControlState();
|
|
1375
|
+
await this.getUpnpACTState();
|
|
1376
|
+
await this.getUpnpGroupControlState();
|
|
1377
|
+
await this.getUpnpZoneControlState();
|
|
1378
|
+
await this.getUpnpAVTransportState();
|
|
1360
1379
|
}
|
|
1361
|
-
this.logDebug('[upnp] Allowed Services: ' + JSON.stringify(this.upnp.getServiceList()));
|
|
1362
|
-
await this.getUpnpRenderingControlState();
|
|
1363
|
-
await this.getUpnpACTState();
|
|
1364
|
-
await this.getUpnpGroupControlState();
|
|
1365
|
-
await this.getUpnpZoneControlState();
|
|
1366
|
-
await this.getUpnpAVTransportState();
|
|
1367
1380
|
}
|
|
1368
1381
|
|
|
1369
1382
|
async getUpnpRenderingControlState(){
|
|
@@ -1391,8 +1404,12 @@ class HeosPlayer {
|
|
|
1391
1404
|
}
|
|
1392
1405
|
}
|
|
1393
1406
|
} catch(err){
|
|
1394
|
-
|
|
1395
|
-
|
|
1407
|
+
if(err.code != 'ECONNABORTED') {
|
|
1408
|
+
this.heos.raiseFailures(this.ip, ERROR_CODES.Upnp);
|
|
1409
|
+
this.logWarn('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1410
|
+
} else {
|
|
1411
|
+
this.logDebug('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1412
|
+
}
|
|
1396
1413
|
}
|
|
1397
1414
|
} else {
|
|
1398
1415
|
this.logDebug('[upnp] get ' + service + ' state is not supported by player ' + this.name + ' (' + this.ip + ')', false);
|
|
@@ -1424,8 +1441,12 @@ class HeosPlayer {
|
|
|
1424
1441
|
}
|
|
1425
1442
|
}
|
|
1426
1443
|
} catch(err){
|
|
1427
|
-
|
|
1428
|
-
|
|
1444
|
+
if(err.code != 'ECONNABORTED') {
|
|
1445
|
+
this.heos.raiseFailures(this.ip, ERROR_CODES.Upnp);
|
|
1446
|
+
this.logWarn('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1447
|
+
} else {
|
|
1448
|
+
this.logDebug('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1449
|
+
}
|
|
1429
1450
|
}
|
|
1430
1451
|
if(state
|
|
1431
1452
|
&& state.DevicePower
|
|
@@ -1465,8 +1486,12 @@ class HeosPlayer {
|
|
|
1465
1486
|
}
|
|
1466
1487
|
}
|
|
1467
1488
|
} catch(err){
|
|
1468
|
-
|
|
1469
|
-
|
|
1489
|
+
if(err.code != 'ECONNABORTED') {
|
|
1490
|
+
this.heos.raiseFailures(this.ip, ERROR_CODES.Upnp);
|
|
1491
|
+
this.logWarn('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1492
|
+
} else {
|
|
1493
|
+
this.logDebug('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1494
|
+
}
|
|
1470
1495
|
}
|
|
1471
1496
|
} else {
|
|
1472
1497
|
this.logDebug('[upnp] get ' + service + ' state is not supported by player ' + this.name + ' (' + this.ip + ')', false);
|
|
@@ -1498,8 +1523,12 @@ class HeosPlayer {
|
|
|
1498
1523
|
}
|
|
1499
1524
|
}
|
|
1500
1525
|
} catch(err){
|
|
1501
|
-
|
|
1502
|
-
|
|
1526
|
+
if(err.code != 'ECONNABORTED') {
|
|
1527
|
+
this.heos.raiseFailures(this.ip, ERROR_CODES.Upnp);
|
|
1528
|
+
this.logWarn('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1529
|
+
} else {
|
|
1530
|
+
this.logDebug('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1531
|
+
}
|
|
1503
1532
|
}
|
|
1504
1533
|
} else {
|
|
1505
1534
|
this.logDebug('[upnp] get ' + service + ' state is not supported by player ' + this.name + ' (' + this.ip + ')', false);
|
|
@@ -1531,8 +1560,12 @@ class HeosPlayer {
|
|
|
1531
1560
|
}
|
|
1532
1561
|
}
|
|
1533
1562
|
} catch(err){
|
|
1534
|
-
|
|
1535
|
-
|
|
1563
|
+
if(err.code != 'ECONNABORTED') {
|
|
1564
|
+
this.heos.raiseFailures(this.ip, ERROR_CODES.Upnp);
|
|
1565
|
+
this.logWarn('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1566
|
+
} else {
|
|
1567
|
+
this.logDebug('[upnp] get ' + service + ' state failed: ' + err, false);
|
|
1568
|
+
}
|
|
1536
1569
|
}
|
|
1537
1570
|
|
|
1538
1571
|
//Track Meta
|
|
@@ -1598,8 +1631,12 @@ class HeosPlayer {
|
|
|
1598
1631
|
try{
|
|
1599
1632
|
const result = await this.upnp.sendCommand(service, action, data);
|
|
1600
1633
|
} catch(err){
|
|
1601
|
-
|
|
1602
|
-
|
|
1634
|
+
if(err.code != 'ECONNABORTED') {
|
|
1635
|
+
this.heos.raiseFailures(this.ip, ERROR_CODES.Upnp);
|
|
1636
|
+
this.logWarn('[upnp] set device power state failed: ' + err, false);
|
|
1637
|
+
} else {
|
|
1638
|
+
this.logDebug('[upnp] set device power state failed: ' + err, false);
|
|
1639
|
+
}
|
|
1603
1640
|
}
|
|
1604
1641
|
} else {
|
|
1605
1642
|
this.logWarn('[upnp] set device power state is not supported by player ' + this.name + ' (' + this.ip + ')', false);
|
package/lib/heos-upnp.js
CHANGED
|
@@ -6,14 +6,12 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const { URL } = require('url');
|
|
9
|
-
const
|
|
9
|
+
const axios = require('axios');
|
|
10
10
|
const {decode} = require('html-entities');
|
|
11
11
|
const keyInObject = require('./tools').keyInObject;
|
|
12
12
|
|
|
13
13
|
const {XMLParser, XMLBuilder} = require('fast-xml-parser');
|
|
14
14
|
|
|
15
|
-
const USER_AGENT = 'LINUX UPnP/1.0 Denon-Heos/149200';
|
|
16
|
-
|
|
17
15
|
class HeosUPnP {
|
|
18
16
|
/**
|
|
19
17
|
* @param {string} ip IP address of player
|
|
@@ -24,23 +22,20 @@ class HeosUPnP {
|
|
|
24
22
|
|
|
25
23
|
this.ip = ip;
|
|
26
24
|
this.url = 'http://' + this.ip + ':60006/upnp/desc/aios_device/aios_device.xml';
|
|
27
|
-
|
|
28
|
-
this.client = got.extend({
|
|
29
|
-
headers: {
|
|
30
|
-
'user-agent': USER_AGENT
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
25
|
}
|
|
34
26
|
|
|
35
27
|
async init() {
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
const response = await axios({
|
|
29
|
+
method: 'get',
|
|
30
|
+
url: this.url,
|
|
31
|
+
timeout: 30000
|
|
32
|
+
});
|
|
38
33
|
const options = {
|
|
39
34
|
parseTrueNumberOnly: true,
|
|
40
35
|
processEntities: false
|
|
41
36
|
};
|
|
42
37
|
const parser = new XMLParser(options);
|
|
43
|
-
const device = parser.parse(response.
|
|
38
|
+
const device = parser.parse(response.data).root.device;
|
|
44
39
|
|
|
45
40
|
this.deviceType = device.deviceType;
|
|
46
41
|
this.friendlyName = device.friendlyName;
|
|
@@ -80,17 +75,23 @@ class HeosUPnP {
|
|
|
80
75
|
}
|
|
81
76
|
const id = service.serviceType.split(':')[3];
|
|
82
77
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
if(!this.hasService(id)){
|
|
79
|
+
const response = await axios({
|
|
80
|
+
method: 'get',
|
|
81
|
+
url: service.SCPDURL,
|
|
82
|
+
timeout: 30000
|
|
83
|
+
});
|
|
84
|
+
const options = {
|
|
85
|
+
parseTrueNumberOnly: true,
|
|
86
|
+
processEntities: false
|
|
87
|
+
};
|
|
88
|
+
const parser = new XMLParser(options);
|
|
89
|
+
const serviceDefinition = await parser.parse(response.data).scpd;
|
|
90
|
+
service.actions = await this.parseActions(serviceDefinition);
|
|
91
|
+
|
|
92
|
+
this.services[id] = service;
|
|
93
|
+
}
|
|
94
|
+
services[id] = this.getService(id);
|
|
94
95
|
}
|
|
95
96
|
return services;
|
|
96
97
|
}
|
|
@@ -186,23 +187,19 @@ class HeosUPnP {
|
|
|
186
187
|
}
|
|
187
188
|
const soapBody = this.getSOAPBody(service, action, data);
|
|
188
189
|
|
|
189
|
-
const
|
|
190
|
-
|
|
190
|
+
const response = await axios({
|
|
191
|
+
method: 'post',
|
|
191
192
|
url: service.controlURL,
|
|
192
|
-
method: 'POST',
|
|
193
|
-
body: soapBody,
|
|
194
193
|
headers: {
|
|
195
194
|
'Content-Type': 'text/xml; charset="utf-8"',
|
|
196
195
|
'Content-Length': soapBody.length,
|
|
197
196
|
'Connection': 'close',
|
|
198
197
|
'SOAPACTION': `"${service.serviceType}#${action.name}"`
|
|
199
|
-
}
|
|
198
|
+
},
|
|
199
|
+
data: soapBody,
|
|
200
|
+
timeout: 30000
|
|
200
201
|
});
|
|
201
|
-
|
|
202
|
-
if (res.statusCode !== 200) {
|
|
203
|
-
throw Error('soap command ' + action.name + ' failure: ' + res.statusCode);
|
|
204
|
-
}
|
|
205
|
-
const result = this.parseSOAPResponse(res.body, action.name, action.argOut);
|
|
202
|
+
const result = this.parseSOAPResponse(response.data, action.name, action.argOut);
|
|
206
203
|
|
|
207
204
|
const options = {
|
|
208
205
|
parseTrueNumberOnly: true,
|
package/main.js
CHANGED
|
@@ -46,7 +46,7 @@ class Heos extends utils.Adapter {
|
|
|
46
46
|
|
|
47
47
|
this.start_players_errors = 0;
|
|
48
48
|
this.ssdp_retry_counter = 0;
|
|
49
|
-
this.
|
|
49
|
+
this.known_player_ips = [];
|
|
50
50
|
|
|
51
51
|
this.ssdp_player_ips = [];
|
|
52
52
|
|
|
@@ -2014,15 +2014,15 @@ class Heos extends utils.Adapter {
|
|
|
2014
2014
|
throw new Error('HEOS responded with invalid data.');
|
|
2015
2015
|
} else {
|
|
2016
2016
|
if (!(pid in this.players)) {
|
|
2017
|
-
const heosPlayer = new HeosPlayer(this, player);
|
|
2017
|
+
const heosPlayer = this.players[pid] = new HeosPlayer(this, player);
|
|
2018
2018
|
// wait until objects are created before connecting, because states shouldn't be set before objects exist
|
|
2019
2019
|
await heosPlayer.initMetaData(player);
|
|
2020
|
-
this.players[pid] = heosPlayer;
|
|
2021
2020
|
try {
|
|
2022
2021
|
await heosPlayer.connect();
|
|
2023
2022
|
} catch (err) {
|
|
2024
2023
|
this.logDebug("can't connect error: " + err, false);
|
|
2025
2024
|
this.logWarn("can't connect player " + player.name + ' (' + player.ip + '). Skip.', false);
|
|
2025
|
+
this.raiseFailures(player.ip, ERROR_CODES.General);
|
|
2026
2026
|
playerConnected = false;
|
|
2027
2027
|
}
|
|
2028
2028
|
} else {
|
|
@@ -2039,7 +2039,7 @@ class Heos extends utils.Adapter {
|
|
|
2039
2039
|
if (!connectedPlayers.includes(pid)) {
|
|
2040
2040
|
await this.stopPlayer(pid);
|
|
2041
2041
|
} else {
|
|
2042
|
-
connectedPlayerIps.push(this.players[pid].ip)
|
|
2042
|
+
connectedPlayerIps.push(this.players[pid].ip);
|
|
2043
2043
|
}
|
|
2044
2044
|
}
|
|
2045
2045
|
for(const ip in this.reboot_time){
|
|
@@ -2049,7 +2049,7 @@ class Heos extends utils.Adapter {
|
|
|
2049
2049
|
}
|
|
2050
2050
|
|
|
2051
2051
|
//Check for players in fail state
|
|
2052
|
-
let playerInFailState = false
|
|
2052
|
+
let playerInFailState = false;
|
|
2053
2053
|
for (const id in this.ssdp_player_ips) {
|
|
2054
2054
|
const ip = this.ssdp_player_ips[id];
|
|
2055
2055
|
if (this.getUptime(ip) >= 5 && !foundPlayerIps.includes(ip)) {
|
|
@@ -2071,7 +2071,7 @@ class Heos extends utils.Adapter {
|
|
|
2071
2071
|
}
|
|
2072
2072
|
} else {
|
|
2073
2073
|
this.getGroups();
|
|
2074
|
-
await this.
|
|
2074
|
+
await this.updateKnownPlayerIPs();
|
|
2075
2075
|
}
|
|
2076
2076
|
} catch (err) {
|
|
2077
2077
|
this.logError('[startPlayers] ' + err);
|
|
@@ -2080,15 +2080,15 @@ class Heos extends utils.Adapter {
|
|
|
2080
2080
|
}
|
|
2081
2081
|
}
|
|
2082
2082
|
|
|
2083
|
-
async
|
|
2083
|
+
async updateKnownPlayerIPs() {
|
|
2084
2084
|
const states = await this.getStatesAsync('players.*');
|
|
2085
|
-
this.
|
|
2085
|
+
this.known_player_ips = [];
|
|
2086
2086
|
for (const id in states) {
|
|
2087
2087
|
if (states[id] && states[id].val) {
|
|
2088
2088
|
const idSplit = id.split('.');
|
|
2089
2089
|
const state = idSplit[idSplit.length - 1];
|
|
2090
2090
|
if (state == 'ip') {
|
|
2091
|
-
this.
|
|
2091
|
+
this.known_player_ips.push(states[id].val);
|
|
2092
2092
|
}
|
|
2093
2093
|
}
|
|
2094
2094
|
}
|
|
@@ -2256,8 +2256,8 @@ class Heos extends utils.Adapter {
|
|
|
2256
2256
|
rebootAll() {
|
|
2257
2257
|
this.logWarn('rebooting all players', false);
|
|
2258
2258
|
this.reboot_ips = [];
|
|
2259
|
-
for (let i = 0; i < this.
|
|
2260
|
-
this.addRebootIp(this.
|
|
2259
|
+
for (let i = 0; i < this.known_player_ips.length; i++) {
|
|
2260
|
+
this.addRebootIp(this.known_player_ips[i]);
|
|
2261
2261
|
}
|
|
2262
2262
|
this.reboot();
|
|
2263
2263
|
}
|
|
@@ -2478,7 +2478,7 @@ class Heos extends utils.Adapter {
|
|
|
2478
2478
|
this.state = STATES.Searching;
|
|
2479
2479
|
|
|
2480
2480
|
//Update Player IPs
|
|
2481
|
-
await this.
|
|
2481
|
+
await this.updateKnownPlayerIPs();
|
|
2482
2482
|
this.updatePreferedPlayerIPs();
|
|
2483
2483
|
if (this.reboot_ips.length == 0) {
|
|
2484
2484
|
this.silent_log_mode = false;
|
|
@@ -2507,7 +2507,7 @@ class Heos extends utils.Adapter {
|
|
|
2507
2507
|
this.ssdp_retry_counter += 1;
|
|
2508
2508
|
}
|
|
2509
2509
|
this.ssdpLeaderElection();
|
|
2510
|
-
if (this.
|
|
2510
|
+
if (this.ssdp_retry_counter > 10 && this.known_player_ips.length > 0) {
|
|
2511
2511
|
this.manual_search_mode = true;
|
|
2512
2512
|
this.silent_log_mode = true;
|
|
2513
2513
|
this.logWarn("can't find any HEOS devices. Try to connect known device IPs and reboot them to exclude device failure...", false);
|
|
@@ -2516,6 +2516,7 @@ class Heos extends utils.Adapter {
|
|
|
2516
2516
|
this.logDebug('searching for HEOS devices ...', true);
|
|
2517
2517
|
this.ssdp_player_ips = [];
|
|
2518
2518
|
this.nodessdp_client.search(this.ssdp_search_target_name);
|
|
2519
|
+
this.getPlayers();
|
|
2519
2520
|
}
|
|
2520
2521
|
}, this.config.searchInterval);
|
|
2521
2522
|
}
|
|
@@ -2551,14 +2552,14 @@ class Heos extends utils.Adapter {
|
|
|
2551
2552
|
let ips = [];
|
|
2552
2553
|
let minFailures = 999;
|
|
2553
2554
|
|
|
2554
|
-
for (let i = 0; i < this.
|
|
2555
|
-
const failures = this.getOverallFailures(this.
|
|
2555
|
+
for (let i = 0; i < this.known_player_ips.length; i++) {
|
|
2556
|
+
const failures = this.getOverallFailures(this.known_player_ips[i]);
|
|
2556
2557
|
if (failures < minFailures) {
|
|
2557
2558
|
ips = [];
|
|
2558
|
-
ips.push(this.
|
|
2559
|
+
ips.push(this.known_player_ips[i]);
|
|
2559
2560
|
minFailures = failures;
|
|
2560
2561
|
} else if (failures == minFailures) {
|
|
2561
|
-
ips.push(this.
|
|
2562
|
+
ips.push(this.known_player_ips[i]);
|
|
2562
2563
|
}
|
|
2563
2564
|
}
|
|
2564
2565
|
return ips.filter(n => n);
|
|
@@ -2730,7 +2731,7 @@ class Heos extends utils.Adapter {
|
|
|
2730
2731
|
}
|
|
2731
2732
|
|
|
2732
2733
|
raiseFailures(ip, code) {
|
|
2733
|
-
if (ip.length > 0 && this.getUptime(ip) >= 5) {
|
|
2734
|
+
if (ip.length > 0 && (this.state != STATES.Connected || this.getUptime(ip) >= 5)) {
|
|
2734
2735
|
if (!(ip in this.failure_counter)) {
|
|
2735
2736
|
this.initFailures(ip);
|
|
2736
2737
|
}
|
|
@@ -2828,7 +2829,7 @@ class Heos extends utils.Adapter {
|
|
|
2828
2829
|
}
|
|
2829
2830
|
|
|
2830
2831
|
raiseLeaderFailures(ip) {
|
|
2831
|
-
if (ip.length > 0 && this.getUptime(ip) >= 5) {
|
|
2832
|
+
if (ip.length > 0 && (this.state != STATES.Connected || this.getUptime(ip) >= 5)) {
|
|
2832
2833
|
if (ip in this.leader_failure_counter) {
|
|
2833
2834
|
this.leader_failure_counter[ip] += 1;
|
|
2834
2835
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.heos",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "The adapter lets control HEOS from ioBroker",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "withstu",
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
"url": "https://github.com/withstu/ioBroker.heos"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
26
|
+
"node": ">= 16"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@iobroker/adapter-core": "^2.6.8",
|
|
30
|
-
"fast-xml-parser": "^4.
|
|
31
|
-
"
|
|
30
|
+
"fast-xml-parser": "^4.2.7",
|
|
31
|
+
"axios": "^1.4.0",
|
|
32
32
|
"html-entities": "^2.3.3",
|
|
33
33
|
"node-ssdp": "^4.0.1",
|
|
34
34
|
"node-vibrant": "^3.1.6"
|
|
@@ -41,20 +41,20 @@
|
|
|
41
41
|
"@iobroker/adapter-dev": "^1.2.0",
|
|
42
42
|
"@iobroker/testing": "^4.1.0",
|
|
43
43
|
"@tsconfig/node14": "^1.0.3",
|
|
44
|
-
"@types/chai": "^4.3.
|
|
44
|
+
"@types/chai": "^4.3.5",
|
|
45
45
|
"@types/chai-as-promised": "^7.1.5",
|
|
46
46
|
"@types/mocha": "^10.0.1",
|
|
47
|
-
"@types/node": "^
|
|
47
|
+
"@types/node": "^20.4.8",
|
|
48
48
|
"@types/proxyquire": "^1.3.28",
|
|
49
49
|
"@types/sinon": "^10.0.8",
|
|
50
50
|
"@types/sinon-chai": "^3.2.9",
|
|
51
|
-
"axios": "^1.
|
|
51
|
+
"axios": "^1.4.0",
|
|
52
52
|
"chai": "^4.3.7",
|
|
53
53
|
"chai-as-promised": "^7.1.1",
|
|
54
54
|
"eslint": "^8.39.0",
|
|
55
55
|
"mocha": "^10.2.0",
|
|
56
56
|
"proxyquire": "^2.1.3",
|
|
57
|
-
"sinon": "^15.0
|
|
57
|
+
"sinon": "^15.2.0",
|
|
58
58
|
"sinon-chai": "^3.7.0",
|
|
59
59
|
"typescript": "~5.0.4"
|
|
60
60
|
},
|