iobroker.senec 1.5.1 → 1.6.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/README.md +4 -0
- package/admin/jsonConfig.json +157 -0
- package/io-package.json +34 -2
- package/lib/state_attr.js +104 -0
- package/main.js +128 -8
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -366,6 +366,10 @@ This channel contains calculated values. Currently these are day/week/month/year
|
|
|
366
366
|
*Read-only number, which designates the number of wallbox [0..3]. This is only available on systems with configured wallboxes.*
|
|
367
367
|
|
|
368
368
|
## Changelog
|
|
369
|
+
### 1.6.0 (NoBl)
|
|
370
|
+
* Added option to also poll SENEC App API. This requires user credentials for mein-senec.de
|
|
371
|
+
* We are starting with just some information - more to follow. But with Dashboard we at least have current values and day statistics back.
|
|
372
|
+
|
|
369
373
|
### 1.5.1 (NoBl)
|
|
370
374
|
* Added more datapoints. If you experience messages in log - feel free to add them yourself to state_attr on github (pull request)
|
|
371
375
|
* Autarky calculations will stopp working because SENEC removed STATISTICS branch.
|
package/admin/jsonConfig.json
CHANGED
|
@@ -1086,6 +1086,163 @@
|
|
|
1086
1086
|
"newLine": false
|
|
1087
1087
|
}
|
|
1088
1088
|
}
|
|
1089
|
+
},
|
|
1090
|
+
"_api": {
|
|
1091
|
+
"type": "panel",
|
|
1092
|
+
"label": {
|
|
1093
|
+
"en": "SENEC API",
|
|
1094
|
+
"de": "SENEC API",
|
|
1095
|
+
"ru": "SENEC API",
|
|
1096
|
+
"pt": "API SENEC",
|
|
1097
|
+
"nl": "SENEC",
|
|
1098
|
+
"fr": "SENEC API",
|
|
1099
|
+
"it": "API SENEC",
|
|
1100
|
+
"es": "SENEC API",
|
|
1101
|
+
"pl": "SENEC API",
|
|
1102
|
+
"uk": "СЕНЕК API",
|
|
1103
|
+
"zh-cn": "SENECAPI"
|
|
1104
|
+
},
|
|
1105
|
+
"items": {
|
|
1106
|
+
"api_use": {
|
|
1107
|
+
"type": "checkbox",
|
|
1108
|
+
"sm": 12,
|
|
1109
|
+
"md": 6,
|
|
1110
|
+
"lg": 6,
|
|
1111
|
+
"label": {
|
|
1112
|
+
"en": "Use SENEC API?",
|
|
1113
|
+
"de": "SENEC API nutzen?",
|
|
1114
|
+
"ru": "Используйте SENEC API?",
|
|
1115
|
+
"pt": "Usar API SENEC?",
|
|
1116
|
+
"nl": "SENEC API gebruiken?",
|
|
1117
|
+
"fr": "Utilisez l'API SENEC?",
|
|
1118
|
+
"it": "Utilizzare API SENEC?",
|
|
1119
|
+
"es": "¿Usar SENEC API?",
|
|
1120
|
+
"pl": "Zastosowanie API SENEC?",
|
|
1121
|
+
"uk": "Використовуйте SENEC API?",
|
|
1122
|
+
"zh-cn": "使用ENECAPI?"
|
|
1123
|
+
},
|
|
1124
|
+
"help": {
|
|
1125
|
+
"en": "Check if the adapter shall make use of the SENEC API.",
|
|
1126
|
+
"de": "Aktivieren, falls der Adapter die SENEC API nutzen soll.",
|
|
1127
|
+
"ru": "Проверьте, следует ли адаптер использовать SENEC API.",
|
|
1128
|
+
"pt": "Verifique se o adaptador deve fazer uso da API SENEC.",
|
|
1129
|
+
"nl": "Kijk of de adapter gebruik maakt van de SENEC API.",
|
|
1130
|
+
"fr": "Vérifiez si l'adaptateur doit utiliser l'API SENEC.",
|
|
1131
|
+
"it": "Controllare se l'adattatore deve utilizzare l'API SENEC.",
|
|
1132
|
+
"es": "Compruebe si el adaptador hará uso de la API SENEC.",
|
|
1133
|
+
"pl": "W przypadku, gdy adapter będzie korzystał z API SENEC.",
|
|
1134
|
+
"uk": "Перевірте, чи використовує адаптер SENEC API.",
|
|
1135
|
+
"zh-cn": "如果适应者应使用ENECAPI。."
|
|
1136
|
+
},
|
|
1137
|
+
"default": "",
|
|
1138
|
+
"newLine": true
|
|
1139
|
+
},
|
|
1140
|
+
"api_mail": {
|
|
1141
|
+
"type": "text",
|
|
1142
|
+
"sm": 12,
|
|
1143
|
+
"md": 6,
|
|
1144
|
+
"lg": 6,
|
|
1145
|
+
"label": {
|
|
1146
|
+
"en": "Email-Address",
|
|
1147
|
+
"de": "E-Mail-Adresse",
|
|
1148
|
+
"ru": "Email-Адрес",
|
|
1149
|
+
"pt": "Endereço de email",
|
|
1150
|
+
"nl": "E-Addres",
|
|
1151
|
+
"fr": "Email-Address",
|
|
1152
|
+
"it": "Indirizzo e-mail",
|
|
1153
|
+
"es": "Email-Address",
|
|
1154
|
+
"pl": "Email-Addresser",
|
|
1155
|
+
"uk": "Електронна пошта",
|
|
1156
|
+
"zh-cn": "Email-Addres"
|
|
1157
|
+
},
|
|
1158
|
+
"help": {
|
|
1159
|
+
"en": "Email-Address used to login to mein-senec.de",
|
|
1160
|
+
"de": "Email-Adresse, um sich bei mein-senec.de anzumelden",
|
|
1161
|
+
"ru": "Email-адрес используется для входа в mein-senec.de",
|
|
1162
|
+
"pt": "Email-Endereço usado para login para mein-senec.de",
|
|
1163
|
+
"nl": "Email-Address loge in mein-senec",
|
|
1164
|
+
"fr": "Email-Adresse utilisé pour se connecter à mein-senec.de",
|
|
1165
|
+
"it": "Email-Address usato per accedere a mein-senec.de",
|
|
1166
|
+
"es": "Email-Address solía iniciar sesión en mein-senec.de",
|
|
1167
|
+
"pl": "Email-Address używany do logowania mein-senec",
|
|
1168
|
+
"uk": "Email-Address використовується для входу в mein-senec.de",
|
|
1169
|
+
"zh-cn": "Email-Address used to mein-senec.de"
|
|
1170
|
+
},
|
|
1171
|
+
"default": "",
|
|
1172
|
+
"newLine": true
|
|
1173
|
+
},
|
|
1174
|
+
"api_pwd": {
|
|
1175
|
+
"type": "password",
|
|
1176
|
+
"visible": true,
|
|
1177
|
+
"sm": 12,
|
|
1178
|
+
"md": 6,
|
|
1179
|
+
"lg": 6,
|
|
1180
|
+
"label": {
|
|
1181
|
+
"en": "Password",
|
|
1182
|
+
"de": "Passwort",
|
|
1183
|
+
"ru": "Пароль",
|
|
1184
|
+
"pt": "Senha",
|
|
1185
|
+
"nl": "Wachtwoord",
|
|
1186
|
+
"fr": "Mot de passe",
|
|
1187
|
+
"it": "Password",
|
|
1188
|
+
"es": "Contraseña",
|
|
1189
|
+
"pl": "Password",
|
|
1190
|
+
"uk": "Логін",
|
|
1191
|
+
"zh-cn": "护照"
|
|
1192
|
+
},
|
|
1193
|
+
"help": {
|
|
1194
|
+
"en": "Password used to login to mein-senec.de",
|
|
1195
|
+
"de": "Passwort für die Anmeldung an mein-senec.de",
|
|
1196
|
+
"ru": "Пароль используется для входа в mein-senec.de",
|
|
1197
|
+
"pt": "Senha usada para login para mein-senec.de",
|
|
1198
|
+
"nl": "Het wachtwoord loginde in mein-senec",
|
|
1199
|
+
"fr": "Mot de passe utilisé pour se connecter à mein-senec.de",
|
|
1200
|
+
"it": "Password usata per accedere a mein-senec.de",
|
|
1201
|
+
"es": "Contraseña usada para iniciar sesión en mein-senec.de",
|
|
1202
|
+
"pl": "Miecz używany do logowania na mein-senec.de",
|
|
1203
|
+
"uk": "Пароль використовується для входу в mein-senec.de",
|
|
1204
|
+
"zh-cn": "目 录"
|
|
1205
|
+
},
|
|
1206
|
+
"default": "",
|
|
1207
|
+
"newLine": false
|
|
1208
|
+
},
|
|
1209
|
+
"api_interval": {
|
|
1210
|
+
"type": "number",
|
|
1211
|
+
"sm": 12,
|
|
1212
|
+
"md": 6,
|
|
1213
|
+
"lg": 6,
|
|
1214
|
+
"label": {
|
|
1215
|
+
"en": "Polling Interval (minutes)",
|
|
1216
|
+
"de": "Polling Interval (Minuten)",
|
|
1217
|
+
"ru": "Поллинг Интервал (минуты)",
|
|
1218
|
+
"pt": "Polling Interval (minutos)",
|
|
1219
|
+
"nl": "Polling Interval (minuten)",
|
|
1220
|
+
"fr": "Intervalle de vote (minutes)",
|
|
1221
|
+
"it": "Polling Interval (minuti)",
|
|
1222
|
+
"es": "Intervalo de votación (minutos)",
|
|
1223
|
+
"pl": "Polling Interval (ang.)",
|
|
1224
|
+
"uk": "Поллінг Інтервал (хвилини)",
|
|
1225
|
+
"zh-cn": "中间(分钟)"
|
|
1226
|
+
},
|
|
1227
|
+
"help": {
|
|
1228
|
+
"en": "Here you can define the polling interval [min 3, max 1440 minutes]. Default = 5. SENEC updates API Data every 5 minutes.",
|
|
1229
|
+
"de": "Hier können Sie das Abfrageintervall definieren [min 3, max 1440 Minuten]. Standard = 5. SENEC aktualisiert die API-Daten alle 5 Minuten.",
|
|
1230
|
+
"ru": "Здесь вы можете определить интервал опроса [мин 3, макс 1440 минут]. По умолчанию = 5. SENEC обновление API Данные каждые 5 минут.",
|
|
1231
|
+
"pt": "Aqui você pode definir o intervalo de votação [min 3, max 1440 minutes]. Padrão = 5. API de atualizações do SENEC Dados a cada 5 minutos.",
|
|
1232
|
+
"nl": "Hier kun je de peiling interval definiëren. SenEC updates Data om de vijf minuten.",
|
|
1233
|
+
"fr": "Ici vous pouvez définir l'intervalle de vote [min 3, max 1440 minutes]. Défaut = 5. Données toutes les 5 minutes.",
|
|
1234
|
+
"it": "Qui è possibile definire l'intervallo di polling [min 3, max 1440 minuti]. Default = 5. Aggiornamenti SENEC API Dati ogni 5 minuti.",
|
|
1235
|
+
"es": "Aquí puede definir el intervalo de votación [min 3, max 1440 minutos]. Default = 5. Actualización de SENEC API Datos cada 5 minutos.",
|
|
1236
|
+
"pl": "Może ona zdefiniować przedział wyborczy (min 3, max 1440 minut). Default = 5. Dane co 5 minut.",
|
|
1237
|
+
"uk": "Тут можна визначити інтервал опитування [min 3, макс. 1440 хвилин]. JavaScript licenses API Веб-сайт Дані кожні 5 хвилин.",
|
|
1238
|
+
"zh-cn": "在此,你可以界定投票间隔[3, max1440分钟]。 Default=5 SENEC最新情况 数据每5分钟。."
|
|
1239
|
+
},
|
|
1240
|
+
"default": 5,
|
|
1241
|
+
"min": 3,
|
|
1242
|
+
"max": 1440,
|
|
1243
|
+
"newLine": true
|
|
1244
|
+
},
|
|
1245
|
+
}
|
|
1089
1246
|
}
|
|
1090
1247
|
}
|
|
1091
1248
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "senec",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.0",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.6.0": {
|
|
7
|
+
"en": "You can now configure to request SENEC App API.",
|
|
8
|
+
"de": "Sie können nun die SENEC App API abfragen.",
|
|
9
|
+
"ru": "Теперь вы можете настроить для запроса SENEC App API.",
|
|
10
|
+
"pt": "Agora você pode configurar para solicitar a API do SENEC App.",
|
|
11
|
+
"nl": "Je kunt nu configureren om SENEC App API te vragen.",
|
|
12
|
+
"fr": "Vous pouvez maintenant configurer pour demander l'API App SENEC.",
|
|
13
|
+
"it": "Ora è possibile configurare per richiedere l'API SENEC App.",
|
|
14
|
+
"es": "Ahora puede configurarse para solicitar SENEC App API.",
|
|
15
|
+
"pl": "Możesz teraz skonfigurować żądanie SENEC App API.",
|
|
16
|
+
"uk": "Ви можете налаштувати запит на SENEC App API.",
|
|
17
|
+
"zh-cn": "如今,你可以准备要求SENECSP。."
|
|
18
|
+
},
|
|
19
|
+
"1.5.2": {
|
|
20
|
+
"en": "Added more datapoints. If you experience messages in log - feel free to add them yourself to state_attr on github (pull request)",
|
|
21
|
+
"de": "Weitere Datenpunkte hinzugefügt. Du kennst weitere? Bitte PullRequest auf Github für state_attr.",
|
|
22
|
+
"ru": "Добавили больше точек данных. Если вы испытываете сообщения в логе - не стесняйтесь добавлять их самостоятельно в state_attr на github (по запросу)",
|
|
23
|
+
"pt": "Adicionado mais datapoints. Se você tiver mensagens no log - sinta-se livre para adicioná-las ao state_attr no github (requisito de envio)",
|
|
24
|
+
"nl": "Voegde meer datapoints toe. Als je berichten in het logboek ervaart, voel je vrij om ze zelf toe te voegen aan github (pull verzoek)",
|
|
25
|
+
"fr": "Ajout d'autres points de données. Si vous éprouvez des messages dans log - n'hésitez pas à les ajouter à state_attr sur github (demande de puissance)",
|
|
26
|
+
"it": "Aggiunti altri datapoint. Se si verificano messaggi in log - sentitevi liberi di aggiungerli a state_attr su github (domanda di voto)",
|
|
27
|
+
"es": "Se agregaron más puntos de datos. Si experimentas mensajes en el registro - no dudes en añadirlos a state_attr en github (pregunta completa)",
|
|
28
|
+
"pl": "Dodać więcej danych. Jeśli znajdziesz wiadomości w dzienniku – czuje się wolny, aby dodać ich do stanu_attr na github (pułki)",
|
|
29
|
+
"uk": "Додано більше точок даних. Якщо ви відчуваєте повідомлення в журналі - не соромтеся додати їх до стану_attr на github",
|
|
30
|
+
"zh-cn": "增加数据点。 如果你在逻辑上有信息,就认为可以自由地补充你自己的国家:对Tithub(要求)表示怀疑。"
|
|
31
|
+
},
|
|
6
32
|
"1.5.1": {
|
|
7
33
|
"en": "Added more datapoints. If you experience messages in log - feel free to add them yourself to state_attr on github (pull request)",
|
|
8
34
|
"de": "Weitere Datenpunkte hinzugefügt. Du kennst weitere? Bitte PullRequest auf Github für state_attr.",
|
|
@@ -182,7 +208,13 @@
|
|
|
182
208
|
"highPrio_BAT1OBJ4": "",
|
|
183
209
|
"highPrio_BAT1OBJ4_active": false,
|
|
184
210
|
"highPrio_TEMPMEASURE": "",
|
|
185
|
-
"highPrio_TEMPMEASURE_active": false
|
|
211
|
+
"highPrio_TEMPMEASURE_active": false,
|
|
212
|
+
"api_use": false,
|
|
213
|
+
"api_interval": 5
|
|
214
|
+
},
|
|
215
|
+
"nativeEncrypted": {
|
|
216
|
+
"api_mail": "",
|
|
217
|
+
"api_pwd": ""
|
|
186
218
|
},
|
|
187
219
|
"objects": [],
|
|
188
220
|
"instanceObjects": [{
|
package/lib/state_attr.js
CHANGED
|
@@ -7413,6 +7413,110 @@ const state_attr = {
|
|
|
7413
7413
|
iptype: false,
|
|
7414
7414
|
multiply: 1
|
|
7415
7415
|
},
|
|
7416
|
+
'WIZARD.SG_READY_POWER_NORMAL': {
|
|
7417
|
+
name: 'SG_READY_POWER_NORMAL',
|
|
7418
|
+
unit: '',
|
|
7419
|
+
booltype: true,
|
|
7420
|
+
datetype: false,
|
|
7421
|
+
iptype: false,
|
|
7422
|
+
multiply: 1
|
|
7423
|
+
},
|
|
7424
|
+
'WALLBOX.NUMBER_OF_PHASE': {
|
|
7425
|
+
name: 'NUMBER_OF_PHASE',
|
|
7426
|
+
unit: '',
|
|
7427
|
+
booltype: false,
|
|
7428
|
+
datetype: false,
|
|
7429
|
+
iptype: false,
|
|
7430
|
+
multiply: 1
|
|
7431
|
+
},
|
|
7432
|
+
'WALLBOX.ERROR_DETAILS': {
|
|
7433
|
+
name: 'ERROR_DETAILS',
|
|
7434
|
+
unit: '',
|
|
7435
|
+
booltype: false,
|
|
7436
|
+
datetype: false,
|
|
7437
|
+
iptype: false,
|
|
7438
|
+
multiply: 1
|
|
7439
|
+
},
|
|
7440
|
+
'LOG.LOG_IN_NOK_COUNT': {
|
|
7441
|
+
name: 'LOG_IN_NOK_COUNT',
|
|
7442
|
+
unit: '',
|
|
7443
|
+
booltype: false,
|
|
7444
|
+
datetype: false,
|
|
7445
|
+
iptype: false,
|
|
7446
|
+
multiply: 1
|
|
7447
|
+
},
|
|
7448
|
+
'GRIDCONFIG.VDEPT1RESPONSETIME': {
|
|
7449
|
+
name: 'VDEPT1RESPONSETIME',
|
|
7450
|
+
unit: '',
|
|
7451
|
+
booltype: false,
|
|
7452
|
+
datetype: false,
|
|
7453
|
+
iptype: false,
|
|
7454
|
+
multiply: 1
|
|
7455
|
+
},
|
|
7456
|
+
'FACTORY.EPA_GRID_FILTER': {
|
|
7457
|
+
name: 'EPA_GRID_FILTER',
|
|
7458
|
+
unit: '',
|
|
7459
|
+
booltype: false,
|
|
7460
|
+
datetype: false,
|
|
7461
|
+
iptype: false,
|
|
7462
|
+
multiply: 1
|
|
7463
|
+
},
|
|
7464
|
+
'BMS_PARA.OPERATIONAL_MODE': {
|
|
7465
|
+
name: 'OPERATIONAL_MODE',
|
|
7466
|
+
unit: '',
|
|
7467
|
+
booltype: false,
|
|
7468
|
+
datetype: false,
|
|
7469
|
+
iptype: false,
|
|
7470
|
+
multiply: 1
|
|
7471
|
+
},
|
|
7472
|
+
'BMS_PARA.MAX_MODULE_DISCHARGE_CURRENT_LIMIT_A': {
|
|
7473
|
+
name: 'MAX_MODULE_DISCHARGE_CURRENT_LIMIT_A',
|
|
7474
|
+
unit: 'A',
|
|
7475
|
+
booltype: false,
|
|
7476
|
+
datetype: false,
|
|
7477
|
+
iptype: false,
|
|
7478
|
+
multiply: 1
|
|
7479
|
+
},
|
|
7480
|
+
'BMS_PARA.MAX_MODULE_CHARGE_CURRENT_LIMIT_A': {
|
|
7481
|
+
name: 'MAX_MODULE_CHARGE_CURRENT_LIMIT_A',
|
|
7482
|
+
unit: 'A',
|
|
7483
|
+
booltype: false,
|
|
7484
|
+
datetype: false,
|
|
7485
|
+
iptype: false,
|
|
7486
|
+
multiply: 1
|
|
7487
|
+
},
|
|
7488
|
+
'BMS_PARA.FULL_CELL_VOLTAGE_MV': {
|
|
7489
|
+
name: 'FULL_CELL_VOLTAGE_MV',
|
|
7490
|
+
unit: 'V',
|
|
7491
|
+
booltype: false,
|
|
7492
|
+
datetype: false,
|
|
7493
|
+
iptype: false,
|
|
7494
|
+
multiply: 1
|
|
7495
|
+
},
|
|
7496
|
+
'BMS_PARA.FORCE_OP_MODE': {
|
|
7497
|
+
name: 'FORCE_OP_MODE',
|
|
7498
|
+
unit: '',
|
|
7499
|
+
booltype: true,
|
|
7500
|
+
datetype: false,
|
|
7501
|
+
iptype: false,
|
|
7502
|
+
multiply: 1
|
|
7503
|
+
},
|
|
7504
|
+
'BMS_PARA.FORCE_BMS_ERROR': {
|
|
7505
|
+
name: 'FORCE_BMS_ERROR',
|
|
7506
|
+
unit: '',
|
|
7507
|
+
booltype: true,
|
|
7508
|
+
datetype: false,
|
|
7509
|
+
iptype: false,
|
|
7510
|
+
multiply: 1
|
|
7511
|
+
},
|
|
7512
|
+
'BMS_PARA.USE_ROA_PARAMETER': {
|
|
7513
|
+
name: 'USE_ROA_PARAMETER',
|
|
7514
|
+
unit: '',
|
|
7515
|
+
booltype: true,
|
|
7516
|
+
datetype: false,
|
|
7517
|
+
iptype: false,
|
|
7518
|
+
multiply: 1
|
|
7519
|
+
},
|
|
7416
7520
|
|
|
7417
7521
|
}
|
|
7418
7522
|
|
package/main.js
CHANGED
|
@@ -8,10 +8,19 @@ const agent = new https.Agent({
|
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
const utils = require('@iobroker/adapter-core');
|
|
11
|
+
|
|
11
12
|
const axios = require('axios').default;
|
|
13
|
+
axios.defaults.headers.common['Content-Type'] = "application/json";
|
|
14
|
+
|
|
12
15
|
const state_attr = require(__dirname + '/lib/state_attr.js');
|
|
13
16
|
const state_trans = require(__dirname + '/lib/state_trans.js');
|
|
17
|
+
const apiUrl = "https://app-gateway-prod.senecops.com/v1/senec";
|
|
18
|
+
const apiLoginUrl = apiUrl + "/login";
|
|
19
|
+
const apiSystemsUrl = apiUrl + "/anlagen";
|
|
20
|
+
const apiKnownSystems = []
|
|
14
21
|
|
|
22
|
+
let apiConnected = false;
|
|
23
|
+
let apiLoginToken = "";
|
|
15
24
|
let retry = 0; // retry-counter
|
|
16
25
|
let retryLowPrio = 0; // retry-counter
|
|
17
26
|
let connectVia = "http://";
|
|
@@ -52,8 +61,12 @@ class Senec extends utils.Adapter {
|
|
|
52
61
|
await this.checkConfig();
|
|
53
62
|
await this.initPollSettings();
|
|
54
63
|
await this.checkConnection();
|
|
64
|
+
await this.initSenecAppApi();
|
|
65
|
+
if (apiConnected) await this.getApiSystems();
|
|
55
66
|
await this.pollSenec(true, 0); // highPrio
|
|
56
67
|
await this.pollSenec(false, 0); // lowPrio
|
|
68
|
+
await this.pollSenecAppApi(0); // App API
|
|
69
|
+
this.setState('info.connection', true, true);
|
|
57
70
|
} catch (error) {
|
|
58
71
|
this.log.error(error);
|
|
59
72
|
this.setState('info.connection', false, true);
|
|
@@ -70,8 +83,8 @@ class Senec extends utils.Adapter {
|
|
|
70
83
|
if (this.timer) {
|
|
71
84
|
clearTimeout(this.timer);
|
|
72
85
|
}
|
|
73
|
-
if (this.
|
|
74
|
-
clearTimeout(this.
|
|
86
|
+
if (this.timerAPI) {
|
|
87
|
+
clearTimeout(this.timerAPI);
|
|
75
88
|
}
|
|
76
89
|
this.log.info('cleaned everything up...');
|
|
77
90
|
this.setState('info.connection', false, true);
|
|
@@ -207,6 +220,11 @@ class Senec extends utils.Adapter {
|
|
|
207
220
|
connectVia = "https://";
|
|
208
221
|
this.log.debug("(checkConf) Switching to https ... " + this.config.useHttps);
|
|
209
222
|
}
|
|
223
|
+
this.log.debug("(checkConf) Configured api polling interval: " + this.config.api_interval);
|
|
224
|
+
if (this.config.api_interval < 3 || this.config.api_interval > 1440) {
|
|
225
|
+
this.log.warn("(checkConf) Config api polling interval " + this.config.api_interval + " not [3..1440] seconds. Using default: 5");
|
|
226
|
+
this.config.api_interval = 5;
|
|
227
|
+
}
|
|
210
228
|
}
|
|
211
229
|
|
|
212
230
|
/**
|
|
@@ -217,23 +235,74 @@ class Senec extends utils.Adapter {
|
|
|
217
235
|
const form = '{"ENERGY":{"STAT_STATE":""}}';
|
|
218
236
|
try {
|
|
219
237
|
this.log.info('connecting to Senec: ' + url);
|
|
220
|
-
const body = await this.doGet(url, form, this, this.config.pollingTimeout);
|
|
238
|
+
const body = await this.doGet(url, form, this, this.config.pollingTimeout, true);
|
|
221
239
|
this.log.info('connected to Senec: ' + url);
|
|
222
|
-
this.setState('info.connection', true, true);
|
|
223
240
|
} catch (error) {
|
|
224
241
|
throw new Error("Error connecting to Senec (IP: " + connectVia + this.config.senecip + "). Exiting! (" + error + "). Try to toggle https-mode in settings and check FQDN of SENEC appliance.");
|
|
225
242
|
}
|
|
226
243
|
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Inits connection to senec app api
|
|
247
|
+
*/
|
|
248
|
+
async initSenecAppApi() {
|
|
249
|
+
if (!this.config.api_use) {
|
|
250
|
+
this.log.info('Usage of SENEC App API not configured. Not using it');
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
this.log.info('connecting to Senec App API: ' + apiLoginUrl);
|
|
254
|
+
const loginData = JSON.stringify({
|
|
255
|
+
password: this.config.api_pwd,
|
|
256
|
+
username: this.config.api_mail
|
|
257
|
+
});
|
|
258
|
+
try {
|
|
259
|
+
const body = await this.doGet(apiLoginUrl, loginData, this, this.config.pollingTimeout, true);
|
|
260
|
+
this.log.info('connected to Senec AppAPI.');
|
|
261
|
+
apiLoginToken = JSON.parse(body).token;
|
|
262
|
+
apiConnected = true;
|
|
263
|
+
axios.defaults.headers.common['authorization'] = apiLoginToken;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
throw new Error("Error connecting to Senec AppAPI. Exiting! (" + error + ").");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Reads system data from senec app api
|
|
271
|
+
*/
|
|
272
|
+
async getApiSystems() {
|
|
273
|
+
const pfx = "_api.Anlagen.";
|
|
274
|
+
if (!this.config.api_use || !apiConnected) {
|
|
275
|
+
this.log.info('Usage of SENEC App API not configured or not connected.');
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
this.log.info('Reading Systems Information from Senec App API ' + apiSystemsUrl);
|
|
279
|
+
try {
|
|
280
|
+
const body = await this.doGet(apiSystemsUrl, "", this, this.config.pollingTimeout, false);
|
|
281
|
+
this.log.info('Read Systems Information from Senec AppAPI.');
|
|
282
|
+
var obj = JSON.parse(body);
|
|
283
|
+
const systems = [];
|
|
284
|
+
for (const[key, value] of Object.entries(obj)) {
|
|
285
|
+
const systemId = value.id;
|
|
286
|
+
apiKnownSystems.push(systemId);
|
|
287
|
+
for (const[key2, value2] of Object.entries(value)) {
|
|
288
|
+
this.doState(pfx + systemId + "." + key2, JSON.stringify(value2), "", "", false);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
this.doState(pfx + 'IDs', JSON.stringify(apiKnownSystems), "Anlagen IDs", "", false);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
throw new Error("Error reading Systems Information from Senec AppAPI. (" + error + ").");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
227
296
|
|
|
228
297
|
/**
|
|
229
298
|
* Read from url via axios
|
|
230
299
|
* @param url to read from
|
|
231
300
|
* @param form to post
|
|
232
301
|
*/
|
|
233
|
-
doGet(pUrl, pForm, caller, pollingTimeout) {
|
|
302
|
+
doGet(pUrl, pForm, caller, pollingTimeout, isPost) {
|
|
234
303
|
return new Promise(function (resolve, reject) {
|
|
235
304
|
axios({
|
|
236
|
-
method: 'post',
|
|
305
|
+
method: isPost ? 'post' : 'get',
|
|
237
306
|
httpsAgent: agent,
|
|
238
307
|
url: pUrl,
|
|
239
308
|
data: pForm,
|
|
@@ -251,6 +320,10 @@ class Senec extends utils.Adapter {
|
|
|
251
320
|
if (error.response) {
|
|
252
321
|
// The request was made and the server responded with a status code
|
|
253
322
|
caller.log.warn('(Poll) received error ' + error.response.status + ' response from SENEC with content: ' + JSON.stringify(error.response.data));
|
|
323
|
+
if (error.response.status == 403 && apiConnected) {
|
|
324
|
+
apiConnected = false; // apparently the api is inaccessible
|
|
325
|
+
this.initSenecAppApi();
|
|
326
|
+
}
|
|
254
327
|
reject(error.response.status);
|
|
255
328
|
} else if (error.request) {
|
|
256
329
|
// The request was made but no response was received
|
|
@@ -280,7 +353,7 @@ class Senec extends utils.Adapter {
|
|
|
280
353
|
}
|
|
281
354
|
|
|
282
355
|
try {
|
|
283
|
-
var body = await this.doGet(url, (isHighPrio ? highPrioForm : lowPrioForm), this, this.config.pollingTimeout);
|
|
356
|
+
var body = await this.doGet(url, (isHighPrio ? highPrioForm : lowPrioForm), this, this.config.pollingTimeout, true);
|
|
284
357
|
if (body.includes('\\"')) {
|
|
285
358
|
// in rare cases senec reports back extra escape sequences on some machines ...
|
|
286
359
|
this.log.info("(Poll) Double escapes detected! Body inc: " + body);
|
|
@@ -303,6 +376,53 @@ class Senec extends utils.Adapter {
|
|
|
303
376
|
this.timer = setTimeout(() => this.pollSenec(isHighPrio, retry), interval * this.config.retrymultiplier * retry);
|
|
304
377
|
}
|
|
305
378
|
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Read values from Senec App API
|
|
383
|
+
*/
|
|
384
|
+
async pollSenecAppApi(retry) {
|
|
385
|
+
var interval = this.config.api_interval * 60000;
|
|
386
|
+
this.log.debug("Polling API ...");
|
|
387
|
+
var body = "";
|
|
388
|
+
try {
|
|
389
|
+
for (let i = 0; i < apiKnownSystems.length; i++) {
|
|
390
|
+
// dashboard
|
|
391
|
+
var url = apiSystemsUrl + "/" + apiKnownSystems[i] + "/dashboard";
|
|
392
|
+
body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
393
|
+
await this.decodeDashboard(apiKnownSystems[i], JSON.parse(body));
|
|
394
|
+
// // wallboxes - only if wallbox exists? - Without: error 500
|
|
395
|
+
//var url = apiSystemsUrl + "/" + apiKnownSystems[i] + "/wallboxes/1";
|
|
396
|
+
//body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
397
|
+
//this.log.info("Abilities: " + body);
|
|
398
|
+
//await this.decodeWallbox(apiKnownSystems[i], JSON.parse(body));
|
|
399
|
+
}
|
|
400
|
+
retry = 0;
|
|
401
|
+
if (unloaded) return;
|
|
402
|
+
this.timerAPI = setTimeout(() => this.pollSenecAppApi(retry), interval);
|
|
403
|
+
} catch (error) {
|
|
404
|
+
if ((retry == this.config.retries) && this.config.retries < 999) {
|
|
405
|
+
this.log.error("Error reading from Senec AppAPI. Retried " + retry + " times. Giving up now. Check config and restart adapter. (" + error + ")");
|
|
406
|
+
this.setState('info.connection', false, true);
|
|
407
|
+
} else {
|
|
408
|
+
retry += 1;
|
|
409
|
+
this.log.warn("Error reading from Senec AppAPI. Retry " + retry + "/" + this.config.retries + " in " + (interval * this.config.retrymultiplier * retry) / 1000 + " seconds! (" + error + ")");
|
|
410
|
+
this.timerAPI = setTimeout(() => this.pollSenecAppApi(retry), interval * this.config.retrymultiplier * retry);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async decodeDashboard(system, obj) {
|
|
416
|
+
const pfx = "_api.Anlagen." + system + ".Dashboard.";
|
|
417
|
+
for (const[key, value] of Object.entries(obj)) {
|
|
418
|
+
if (key == "zeitstempel" || key == "electricVehicleConnected") {
|
|
419
|
+
this.doState(pfx + key, value, "", "", false);
|
|
420
|
+
} else {
|
|
421
|
+
for (const[key2, value2] of Object.entries(value)) {
|
|
422
|
+
this.doState(pfx + key + "." + key2, value2.wert, "", value2.einheit, false);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
306
426
|
|
|
307
427
|
}
|
|
308
428
|
|
|
@@ -408,7 +528,7 @@ class Senec extends utils.Adapter {
|
|
|
408
528
|
if (value2 !== "VARIABLE_NOT_FOUND" && key2 !== "OBJECT_NOT_FOUND") {
|
|
409
529
|
const key = key1 + '.' + key2;
|
|
410
530
|
if (state_attr[key] === undefined) {
|
|
411
|
-
this.log.
|
|
531
|
+
this.log.debug('REPORT_TO_DEV: State attribute definition missing for: ' + key + ', Val: ' + value2);
|
|
412
532
|
}
|
|
413
533
|
const desc = (state_attr[key] !== undefined) ? state_attr[key].name : key2;
|
|
414
534
|
const unit = (state_attr[key] !== undefined) ? state_attr[key].unit : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.senec",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Senec Home",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "NoBl",
|
|
@@ -28,21 +28,21 @@
|
|
|
28
28
|
"@iobroker/adapter-core": "^2.6.8"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@alcalzone/release-script": "^3.
|
|
31
|
+
"@alcalzone/release-script": "^3.6.0",
|
|
32
32
|
"@alcalzone/release-script-plugin-iobroker": "^3.5.9",
|
|
33
33
|
"@alcalzone/release-script-plugin-license": "^3.5.9",
|
|
34
34
|
"@alcalzone/release-script-plugin-manual-review": "^3.5.9",
|
|
35
35
|
"@iobroker/adapter-dev": "^1.2.0",
|
|
36
36
|
"@iobroker/testing": "^4.1.0",
|
|
37
|
-
"@tsconfig/node14": "^1.0
|
|
37
|
+
"@tsconfig/node14": "^14.1.0",
|
|
38
38
|
"@types/chai": "^4.3.5",
|
|
39
39
|
"@types/chai-as-promised": "^7.1.5",
|
|
40
40
|
"@types/mocha": "^10.0.1",
|
|
41
|
-
"@types/node": "^20.
|
|
41
|
+
"@types/node": "^20.5.7",
|
|
42
42
|
"@types/proxyquire": "^1.3.28",
|
|
43
|
-
"@types/sinon": "^10.0.
|
|
43
|
+
"@types/sinon": "^10.0.16",
|
|
44
44
|
"@types/sinon-chai": "^3.2.9",
|
|
45
|
-
"chai": "^4.3.
|
|
45
|
+
"chai": "^4.3.8",
|
|
46
46
|
"chai-as-promised": "^7.1.1",
|
|
47
47
|
"eslint": "^8.47.0",
|
|
48
48
|
"mocha": "^10.2.0",
|