@homebridge-plugins/homebridge-tado 8.6.0-beta.0 → 8.6.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -2
- package/config.schema.json +13 -13
- package/homebridge-ui/public/index.html +4 -0
- package/homebridge-ui/public/js/main.js +53 -25
- package/homebridge-ui/public/js/schema.js +13 -13
- package/homebridge-ui/server.js +5 -3
- package/package.json +1 -1
- package/src/helper/handler.js +8 -6
- package/src/platform.js +18 -14
- package/src/tado/tado-api.js +8 -8
- package/src/tado/tado-config.js +31 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
## v8.6.0 - 2025-10-30
|
|
4
|
-
-
|
|
5
|
-
-
|
|
4
|
+
- BREAKING CHANGE: If you use tadoApiUrl or skipAuth, you must now define them under each corresponding home in your configuration (#176)
|
|
5
|
+
- Refactor configuration: moved tadoApiUrl and skipAuth into individual home configs to support multiple API URLs (#176)
|
|
6
|
+
- Persist tado zone states after every zone states update
|
|
7
|
+
- Improved zone update logic: when setting a state, all zones are now updated immediately if the next scheduled update is more than 10 seconds away
|
|
8
|
+
- Fix: Corrected zone update handling that could previously cause unintended heating changes (#178)
|
|
6
9
|
|
|
7
10
|
## v8.5.0 - 2025-10-27
|
|
8
11
|
- Change minimum polling interval to 30s due to improvements made in v8.2.0
|
package/config.schema.json
CHANGED
|
@@ -41,6 +41,16 @@
|
|
|
41
41
|
"description": "The user name that you use for the app and the web app of Tado.",
|
|
42
42
|
"required": true
|
|
43
43
|
},
|
|
44
|
+
"tadoApiUrl": {
|
|
45
|
+
"title": "Tado API URL",
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Optional: Use a custom tado api url (e.g. http://localhost:8080)."
|
|
48
|
+
},
|
|
49
|
+
"skipAuth": {
|
|
50
|
+
"title": "Skip Authentication",
|
|
51
|
+
"type": "boolean",
|
|
52
|
+
"description": "Optional: Skip authentication for tado api."
|
|
53
|
+
},
|
|
44
54
|
"polling": {
|
|
45
55
|
"title": "Polling",
|
|
46
56
|
"description": "The polling interval in seconds (recommended value: 300).",
|
|
@@ -554,16 +564,6 @@
|
|
|
554
564
|
}
|
|
555
565
|
}
|
|
556
566
|
},
|
|
557
|
-
"tadoApiUrl": {
|
|
558
|
-
"title": "Tado API URL",
|
|
559
|
-
"type": "string",
|
|
560
|
-
"description": "Optional: Use a custom tado api url (e.g. http://localhost:8080)."
|
|
561
|
-
},
|
|
562
|
-
"skipAuth": {
|
|
563
|
-
"title": "Skip Authentication",
|
|
564
|
-
"type": "boolean",
|
|
565
|
-
"description": "Optional: Skip authentication for tado api."
|
|
566
|
-
},
|
|
567
567
|
"disableHistoryService": {
|
|
568
568
|
"title": "Disable History Service",
|
|
569
569
|
"type": "boolean",
|
|
@@ -574,8 +574,6 @@
|
|
|
574
574
|
"layout": [
|
|
575
575
|
"name",
|
|
576
576
|
"debug",
|
|
577
|
-
"tadoApiUrl",
|
|
578
|
-
"skipAuth",
|
|
579
577
|
"disableHistoryService",
|
|
580
578
|
{
|
|
581
579
|
"key": "homes",
|
|
@@ -588,13 +586,15 @@
|
|
|
588
586
|
"homes[].temperatureUnit",
|
|
589
587
|
{
|
|
590
588
|
"key": "homes[]",
|
|
591
|
-
"title": "
|
|
589
|
+
"title": "Authentication",
|
|
592
590
|
"type": "section",
|
|
593
591
|
"expandable": true,
|
|
594
592
|
"expanded": false,
|
|
595
593
|
"orderable": false,
|
|
596
594
|
"items": [
|
|
597
595
|
"homes[].id",
|
|
596
|
+
"homes[].tadoApiUrl",
|
|
597
|
+
"homes[].skipAuth",
|
|
598
598
|
"homes[].username"
|
|
599
599
|
]
|
|
600
600
|
},
|
|
@@ -89,6 +89,10 @@
|
|
|
89
89
|
<div class="form-group">
|
|
90
90
|
<label for="homeUsername">Username <span class="text-danger">*</span> </label>
|
|
91
91
|
<input type="text" class="form-control" id="homeUsername">
|
|
92
|
+
<label for="homeTadoApiUrl">Tado API URL (optional) </label>
|
|
93
|
+
<input type="text" class="form-control" id="homeTadoApiUrl">
|
|
94
|
+
<label for="homeSkipAuth">Skip Authentication (optional) </label>
|
|
95
|
+
<input type="checkbox" class="form-control" id="homeSkipAuth">
|
|
92
96
|
</div>
|
|
93
97
|
<button id="auth" type="submit" class="btn btn-primary float-right mt-3 mr-0">Next</button>
|
|
94
98
|
</div>
|
|
@@ -132,8 +132,6 @@ async function createCustomSchema(home) {
|
|
|
132
132
|
customSchemaActive = homebridge.createForm(schema, {
|
|
133
133
|
name: pluginConfig[0].name,
|
|
134
134
|
debug: pluginConfig[0].debug,
|
|
135
|
-
tadoApiUrl: pluginConfig[0].tadoApiUrl,
|
|
136
|
-
skipAuth: pluginConfig[0].skipAuth,
|
|
137
135
|
disableHistoryService: pluginConfig[0].disableHistoryService,
|
|
138
136
|
homes: home
|
|
139
137
|
});
|
|
@@ -142,8 +140,6 @@ async function createCustomSchema(home) {
|
|
|
142
140
|
|
|
143
141
|
pluginConfig[0].name = config.name;
|
|
144
142
|
pluginConfig[0].debug = config.debug;
|
|
145
|
-
pluginConfig[0].tadoApiUrl = config.tadoApiUrl;
|
|
146
|
-
pluginConfig[0].skipAuth = config.skipAuth;
|
|
147
143
|
pluginConfig[0].disableHistoryService = config.disableHistoryService;
|
|
148
144
|
pluginConfig[0].homes = pluginConfig[0].homes.map(myHome => {
|
|
149
145
|
if (myHome.name === config.homes.name) {
|
|
@@ -185,6 +181,8 @@ async function resetUI() {
|
|
|
185
181
|
function resetForm() {
|
|
186
182
|
|
|
187
183
|
$('#homeUsername').val('');
|
|
184
|
+
$('#homeTadoApiUrl').val('');
|
|
185
|
+
$('#homeSkipAuth').prop('checked', false);
|
|
188
186
|
|
|
189
187
|
if (fetchDevicesBar)
|
|
190
188
|
fetchDevicesBar.set(0);
|
|
@@ -285,8 +283,6 @@ async function removeDeviceFromConfig(name) {
|
|
|
285
283
|
|
|
286
284
|
if (!pluginConfig[0].homes.length) {
|
|
287
285
|
delete pluginConfig[0].debug;
|
|
288
|
-
delete pluginConfig[0].tadoApiUrl;
|
|
289
|
-
delete pluginConfig[0].skipAuth;
|
|
290
286
|
delete pluginConfig[0].disableHistoryService;
|
|
291
287
|
}
|
|
292
288
|
|
|
@@ -313,9 +309,9 @@ async function removeDeviceFromConfig(name) {
|
|
|
313
309
|
|
|
314
310
|
}
|
|
315
311
|
|
|
316
|
-
async function fetchDevices(
|
|
312
|
+
async function fetchDevices(auth, refresh, resync) {
|
|
317
313
|
|
|
318
|
-
if (!
|
|
314
|
+
if (!auth && !resync)
|
|
319
315
|
return homebridge.toast.error('No credentials!', 'Error');
|
|
320
316
|
|
|
321
317
|
const config = JSON.parse(JSON.stringify(pluginConfig));
|
|
@@ -337,7 +333,7 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
337
333
|
|
|
338
334
|
if (!resync) {
|
|
339
335
|
//Init API with credentials
|
|
340
|
-
await fnAuthenticate(
|
|
336
|
+
await fnAuthenticate(auth);
|
|
341
337
|
|
|
342
338
|
await TIMEOUT(2000);
|
|
343
339
|
|
|
@@ -355,7 +351,7 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
355
351
|
return homebridge.toast.error('Cannot refresh ' + currentHome + '. Not found in config!', 'Error');
|
|
356
352
|
|
|
357
353
|
if (!home.id) {
|
|
358
|
-
homebridge.toast.info('No Home ID defined in config. Getting Home ID for ' + home.name,
|
|
354
|
+
homebridge.toast.info('No Home ID defined in config. Getting Home ID for ' + home.name, auth.username);
|
|
359
355
|
const me = await homebridge.request('/exec', { dest: 'getMe' });
|
|
360
356
|
me.homes.map(foundHome => {
|
|
361
357
|
if (foundHome.name === home.name)
|
|
@@ -363,7 +359,7 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
363
359
|
});
|
|
364
360
|
await TIMEOUT(1000);
|
|
365
361
|
if (!home.id)
|
|
366
|
-
return homebridge.toast.error('Cannot get a Home ID for ' + home.name + '. ' + home.name + ' not found for this user!',
|
|
362
|
+
return homebridge.toast.error('Cannot get a Home ID for ' + home.name + '. ' + home.name + ' not found for this user!', auth.username);
|
|
367
363
|
}
|
|
368
364
|
|
|
369
365
|
await TIMEOUT(2000);
|
|
@@ -377,7 +373,9 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
377
373
|
if (config[0].homes[i].name === homeInfo.name) {
|
|
378
374
|
|
|
379
375
|
config[0].homes[i].id = homeInfo.id;
|
|
380
|
-
config[0].homes[i].username =
|
|
376
|
+
config[0].homes[i].username = auth.username;
|
|
377
|
+
config[0].homes[i].tadoApiUrl = auth.tadoApiUrl;
|
|
378
|
+
config[0].homes[i].skipAuth = auth.skipAuth;
|
|
381
379
|
config[0].homes[i].temperatureUnit = homeInfo.temperatureUnit || 'CELSIUS';
|
|
382
380
|
config[0].homes[i].zones = config[0].homes[i].zones || [];
|
|
383
381
|
|
|
@@ -416,7 +414,7 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
416
414
|
}
|
|
417
415
|
});
|
|
418
416
|
if (!found) {
|
|
419
|
-
homebridge.toast.info(user.name + ' removed from config!',
|
|
417
|
+
homebridge.toast.info(user.name + ' removed from config!', auth.username);
|
|
420
418
|
config[0].homes[i].presence.user.splice(index, 1);
|
|
421
419
|
}
|
|
422
420
|
});
|
|
@@ -464,7 +462,7 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
464
462
|
}
|
|
465
463
|
});
|
|
466
464
|
if (!found) {
|
|
467
|
-
homebridge.toast.info(zone.name + ' removed from config!',
|
|
465
|
+
homebridge.toast.info(zone.name + ' removed from config!', auth.username);
|
|
468
466
|
config[0].homes[i].zones.splice(index, 1);
|
|
469
467
|
}
|
|
470
468
|
});
|
|
@@ -628,7 +626,7 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
628
626
|
}
|
|
629
627
|
});
|
|
630
628
|
if (!found) {
|
|
631
|
-
homebridge.toast.info(childLockSwitch.name + ' removed from config!',
|
|
629
|
+
homebridge.toast.info(childLockSwitch.name + ' removed from config!', auth.username);
|
|
632
630
|
config[0].homes[i].extras.childLockSwitches.splice(index, 1);
|
|
633
631
|
}
|
|
634
632
|
});
|
|
@@ -675,7 +673,11 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
675
673
|
if (home.name && home.username) {
|
|
676
674
|
|
|
677
675
|
//Init API with credentials
|
|
678
|
-
await fnAuthenticate({
|
|
676
|
+
await fnAuthenticate({
|
|
677
|
+
username: home.username,
|
|
678
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
679
|
+
skipAuth: home.skipAuth
|
|
680
|
+
});
|
|
679
681
|
|
|
680
682
|
//resync home (refresh/remove)
|
|
681
683
|
const me = await homebridge.request('/exec', { dest: 'getMe' });
|
|
@@ -684,7 +686,9 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
684
686
|
availableHomesInApis.push({
|
|
685
687
|
id: foundHome.id,
|
|
686
688
|
name: foundHome.name,
|
|
687
|
-
username: home.username
|
|
689
|
+
username: home.username,
|
|
690
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
691
|
+
skipAuth: home.skipAuth
|
|
688
692
|
});
|
|
689
693
|
});
|
|
690
694
|
|
|
@@ -708,7 +712,11 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
708
712
|
if (home.name && home.username) {
|
|
709
713
|
|
|
710
714
|
//Init API with credentials
|
|
711
|
-
await fnAuthenticate({
|
|
715
|
+
await fnAuthenticate({
|
|
716
|
+
username: home.username,
|
|
717
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
718
|
+
skipAuth: home.skipAuth
|
|
719
|
+
});
|
|
712
720
|
|
|
713
721
|
let foundHome;
|
|
714
722
|
|
|
@@ -748,7 +756,11 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
748
756
|
if (home.name && home.username) {
|
|
749
757
|
|
|
750
758
|
//Init API with credentials
|
|
751
|
-
await fnAuthenticate({
|
|
759
|
+
await fnAuthenticate({
|
|
760
|
+
username: home.username,
|
|
761
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
762
|
+
skipAuth: home.skipAuth
|
|
763
|
+
});
|
|
752
764
|
|
|
753
765
|
let foundHome;
|
|
754
766
|
|
|
@@ -767,6 +779,8 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
767
779
|
|
|
768
780
|
config[0].homes[i].id = homeInfo.id;
|
|
769
781
|
config[0].homes[i].username = foundHome.username;
|
|
782
|
+
config[0].homes[i].tadoApiUrl = foundHome.tadoApiUrl;
|
|
783
|
+
config[0].homes[i].skipAuth = foundHome.skipAuth;
|
|
770
784
|
config[0].homes[i].temperatureUnit = homeInfo.temperatureUnit || 'CELSIUS';
|
|
771
785
|
config[0].homes[i].zones = config[0].homes[i].zones || [];
|
|
772
786
|
|
|
@@ -1078,12 +1092,18 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
1078
1092
|
addedHomes += 1;
|
|
1079
1093
|
|
|
1080
1094
|
//Init API with credentials
|
|
1081
|
-
await fnAuthenticate({
|
|
1095
|
+
await fnAuthenticate({
|
|
1096
|
+
username: foundHome.username,
|
|
1097
|
+
tadoApiUrl: foundHome.tadoApiUrl,
|
|
1098
|
+
skipAuth: foundHome.skipAuth
|
|
1099
|
+
});
|
|
1082
1100
|
|
|
1083
1101
|
const homeConfig = {
|
|
1084
1102
|
id: foundHome.id,
|
|
1085
1103
|
name: foundHome.name,
|
|
1086
1104
|
username: foundHome.username,
|
|
1105
|
+
tadoApiUrl: foundHome.tadoApiUrl,
|
|
1106
|
+
skipAuth: foundHome.skipAuth,
|
|
1087
1107
|
polling: 300,
|
|
1088
1108
|
zones: [],
|
|
1089
1109
|
presence: {
|
|
@@ -1239,7 +1259,9 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
1239
1259
|
const homeConfig = {
|
|
1240
1260
|
id: foundHome.id,
|
|
1241
1261
|
name: foundHome.name,
|
|
1242
|
-
username:
|
|
1262
|
+
username: auth.username,
|
|
1263
|
+
tadoApiUrl: auth.tadoApiUrl,
|
|
1264
|
+
skipAuth: auth.skipAuth,
|
|
1243
1265
|
polling: 300,
|
|
1244
1266
|
zones: [],
|
|
1245
1267
|
presence: {
|
|
@@ -1384,7 +1406,7 @@ async function fetchDevices(credentials, refresh, resync) {
|
|
|
1384
1406
|
fetchDevicesBar.animate(1.00);
|
|
1385
1407
|
|
|
1386
1408
|
if (resync)
|
|
1387
|
-
homebridge.toast.info('Resynchronized!',
|
|
1409
|
+
homebridge.toast.info('Resynchronized!', auth.username);
|
|
1388
1410
|
|
|
1389
1411
|
await TIMEOUT(2000);
|
|
1390
1412
|
|
|
@@ -1501,13 +1523,15 @@ $('#auth').on('click', async () => {
|
|
|
1501
1523
|
|
|
1502
1524
|
try {
|
|
1503
1525
|
|
|
1504
|
-
let
|
|
1526
|
+
let auth = {
|
|
1505
1527
|
username: $('#homeUsername').val(),
|
|
1528
|
+
tadoApiUrl: $('#homeTadoApiUrl').val(),
|
|
1529
|
+
skipAuth: $('#homeSkipAuth').prop('checked')
|
|
1506
1530
|
};
|
|
1507
1531
|
|
|
1508
1532
|
transPage($('#configureDevice'), $('#fetchDevices'));
|
|
1509
1533
|
|
|
1510
|
-
const config = await fetchDevices(
|
|
1534
|
+
const config = await fetchDevices(auth, false, false);
|
|
1511
1535
|
|
|
1512
1536
|
if (config) {
|
|
1513
1537
|
await addNewDeviceToConfig(config, false, false);
|
|
@@ -1553,7 +1577,11 @@ $('#refreshDevice').on('click', async () => {
|
|
|
1553
1577
|
|
|
1554
1578
|
transPage($('#isConfigured'), $('#fetchDevices'));
|
|
1555
1579
|
|
|
1556
|
-
const config = await fetchDevices({
|
|
1580
|
+
const config = await fetchDevices({
|
|
1581
|
+
username: home.username,
|
|
1582
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
1583
|
+
skipAuth: home.skipAuth
|
|
1584
|
+
}, true, false);
|
|
1557
1585
|
|
|
1558
1586
|
if (config) {
|
|
1559
1587
|
await addNewDeviceToConfig(config, true, false);
|
|
@@ -34,6 +34,16 @@ const schema = {
|
|
|
34
34
|
'description': 'The user name that you use for the app and the web app of Tado.',
|
|
35
35
|
'required': true
|
|
36
36
|
},
|
|
37
|
+
'tadoApiUrl': {
|
|
38
|
+
'title': 'Tado API URL',
|
|
39
|
+
'type': 'string',
|
|
40
|
+
'description': 'Optional: Use a custom tado api url (e.g. http://localhost:8080).'
|
|
41
|
+
},
|
|
42
|
+
'skipAuth': {
|
|
43
|
+
'title': 'Skip Authentication',
|
|
44
|
+
'type': 'boolean',
|
|
45
|
+
'description': 'Optional: Skip authentication for tado api.'
|
|
46
|
+
},
|
|
37
47
|
'polling': {
|
|
38
48
|
'title': 'Polling',
|
|
39
49
|
'description': 'The polling interval in seconds (recommended value: 300).',
|
|
@@ -546,16 +556,6 @@ const schema = {
|
|
|
546
556
|
}
|
|
547
557
|
}
|
|
548
558
|
},
|
|
549
|
-
'tadoApiUrl': {
|
|
550
|
-
'title': 'Tado API URL',
|
|
551
|
-
'type': 'string',
|
|
552
|
-
'description': 'Optional: Use a custom tado api url (e.g. http://localhost:8080).'
|
|
553
|
-
},
|
|
554
|
-
'skipAuth': {
|
|
555
|
-
'title': 'Skip Authentication',
|
|
556
|
-
'type': 'boolean',
|
|
557
|
-
'description': 'Optional: Skip authentication for tado api.'
|
|
558
|
-
},
|
|
559
559
|
'disableHistoryService': {
|
|
560
560
|
'title': 'Disable History Service',
|
|
561
561
|
'type': 'boolean',
|
|
@@ -565,21 +565,21 @@ const schema = {
|
|
|
565
565
|
'layout': [
|
|
566
566
|
'name',
|
|
567
567
|
'debug',
|
|
568
|
-
'tadoApiUrl',
|
|
569
|
-
'skipAuth',
|
|
570
568
|
'disableHistoryService',
|
|
571
569
|
'homes.name',
|
|
572
570
|
'homes.polling',
|
|
573
571
|
'homes.temperatureUnit',
|
|
574
572
|
{
|
|
575
573
|
'key': 'homes',
|
|
576
|
-
'title': '
|
|
574
|
+
'title': 'Authentication',
|
|
577
575
|
'type': 'section',
|
|
578
576
|
'expandable': true,
|
|
579
577
|
'expanded': false,
|
|
580
578
|
'orderable': false,
|
|
581
579
|
'items': [
|
|
582
580
|
'homes.id',
|
|
581
|
+
'homes.tadoApiUrl',
|
|
582
|
+
'homes.skipAuth',
|
|
583
583
|
'homes.username'
|
|
584
584
|
]
|
|
585
585
|
},
|
package/homebridge-ui/server.js
CHANGED
|
@@ -17,10 +17,13 @@ class UiServer extends HomebridgePluginUiServer {
|
|
|
17
17
|
|
|
18
18
|
authenticate(config) {
|
|
19
19
|
|
|
20
|
-
this.tado = new TadoApi('Config UI X', {
|
|
20
|
+
this.tado = new TadoApi('Config UI X', {
|
|
21
|
+
username: config.username,
|
|
22
|
+
tadoApiUrl: config.tadoApiUrl,
|
|
23
|
+
skipAuth: config.skipAuth
|
|
24
|
+
}, this.homebridgeStoragePath);
|
|
21
25
|
|
|
22
26
|
return;
|
|
23
|
-
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
reset() {
|
|
@@ -28,7 +31,6 @@ class UiServer extends HomebridgePluginUiServer {
|
|
|
28
31
|
this.tado = false;
|
|
29
32
|
|
|
30
33
|
return;
|
|
31
|
-
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
async exec(payload) {
|
package/package.json
CHANGED
package/src/helper/handler.js
CHANGED
|
@@ -729,7 +729,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
729
729
|
}
|
|
730
730
|
}
|
|
731
731
|
|
|
732
|
-
async function
|
|
732
|
+
async function persistZoneStates(homeId, zoneStates) {
|
|
733
733
|
try {
|
|
734
734
|
if (zoneStates && Object.keys(zoneStates).length) {
|
|
735
735
|
const homeData = {};
|
|
@@ -741,6 +741,9 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
741
741
|
} catch (error) {
|
|
742
742
|
Logger.error(`Error while updating the tado states file for home ${homeId}: ${error.message || error}`);
|
|
743
743
|
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
async function persistStates() {
|
|
744
747
|
try {
|
|
745
748
|
const data = {};
|
|
746
749
|
data.counterData = await tado.getCounterData();
|
|
@@ -781,7 +784,6 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
781
784
|
|
|
782
785
|
async function getStates() {
|
|
783
786
|
lastGetStates = Date.now();
|
|
784
|
-
let zoneStates = {};
|
|
785
787
|
try {
|
|
786
788
|
//ME
|
|
787
789
|
if (!config.homeId) await updateMe();
|
|
@@ -790,7 +792,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
790
792
|
if (!config.temperatureUnit) await updateHome();
|
|
791
793
|
|
|
792
794
|
//Zones
|
|
793
|
-
if (config.zones.length)
|
|
795
|
+
if (config.zones.length) await updateZones();
|
|
794
796
|
|
|
795
797
|
//MobileDevices
|
|
796
798
|
if (config.presence.length) await updateMobileDevices();
|
|
@@ -809,7 +811,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
809
811
|
} catch (err) {
|
|
810
812
|
errorHandler(err);
|
|
811
813
|
} finally {
|
|
812
|
-
void persistStates(
|
|
814
|
+
void persistStates();
|
|
813
815
|
}
|
|
814
816
|
}
|
|
815
817
|
|
|
@@ -903,9 +905,9 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
903
905
|
}
|
|
904
906
|
|
|
905
907
|
const zoneStates = (await tado.getZoneStates(config.homeId))["zoneStates"] ?? {};
|
|
906
|
-
|
|
908
|
+
void persistZoneStates(config.homeId, zoneStates);
|
|
907
909
|
|
|
908
|
-
for (const zone of
|
|
910
|
+
for (const zone of config.zones) {
|
|
909
911
|
const zoneState = zoneStates[zone.id.toString()];
|
|
910
912
|
Logger.debug(`Update state of zone ${zone.id} to:`, zoneState);
|
|
911
913
|
|
package/src/platform.js
CHANGED
|
@@ -59,45 +59,49 @@ class TadoPlatform {
|
|
|
59
59
|
async setupPlugin(storagePath) {
|
|
60
60
|
try {
|
|
61
61
|
if (this.config.user && this.config.user.length) {
|
|
62
|
-
for (const
|
|
62
|
+
for (const auth of this.config.user) {
|
|
63
63
|
let error = false;
|
|
64
64
|
|
|
65
|
-
if (!
|
|
65
|
+
if (!auth.username) {
|
|
66
66
|
Logger.warn('There is no username configured for the user. This user will be skipped.');
|
|
67
67
|
error = true;
|
|
68
|
-
} else if (
|
|
68
|
+
} else if (auth.reconfigure === false) {
|
|
69
69
|
error = true;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (!error) {
|
|
73
73
|
this.user.push({
|
|
74
|
-
username:
|
|
74
|
+
username: auth.username,
|
|
75
|
+
tadoApiUrl: auth.tadoApiUrl,
|
|
76
|
+
skipAuth: auth.skipAuth
|
|
75
77
|
});
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
if (this.user.length) {
|
|
81
|
-
for (const
|
|
82
|
-
if (
|
|
83
|
+
for (const auth of this.user) {
|
|
84
|
+
if (auth.reconfigure || auth.reconfigure === undefined) {
|
|
83
85
|
if (this.config.homes && this.config.homes.length) {
|
|
84
|
-
let foundHome = this.config.homes.filter((home) => home && home.username ===
|
|
86
|
+
let foundHome = this.config.homes.filter((home) => home && home.username === auth.username);
|
|
85
87
|
|
|
86
88
|
if (foundHome.length) {
|
|
87
89
|
//refresh
|
|
88
90
|
if (foundHome[0].name && foundHome[0].username) {
|
|
89
91
|
Logger.info('Refreshing home...', foundHome[0].name);
|
|
90
92
|
this.config = await TadoConfig.refresh(foundHome[0].name, this.config, {
|
|
91
|
-
username: foundHome[0].username
|
|
92
|
-
|
|
93
|
+
username: foundHome[0].username,
|
|
94
|
+
tadoApiUrl: foundHome[0].tadoApiUrl,
|
|
95
|
+
skipAuth: foundHome[0].skipAuth
|
|
96
|
+
}, storagePath);
|
|
93
97
|
}
|
|
94
98
|
} else {
|
|
95
|
-
Logger.info('Generating new home...',
|
|
96
|
-
this.config = await TadoConfig.add(this.config, [
|
|
99
|
+
Logger.info('Generating new home...', auth.username);
|
|
100
|
+
this.config = await TadoConfig.add(this.config, [auth], storagePath);
|
|
97
101
|
}
|
|
98
102
|
} else {
|
|
99
|
-
Logger.info('Generating new home...',
|
|
100
|
-
this.config = await TadoConfig.add(this.config, [
|
|
103
|
+
Logger.info('Generating new home...', auth.username);
|
|
104
|
+
this.config = await TadoConfig.add(this.config, [auth], storagePath);
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
}
|
|
@@ -114,7 +118,7 @@ class TadoPlatform {
|
|
|
114
118
|
})
|
|
115
119
|
.filter((user) => user);
|
|
116
120
|
|
|
117
|
-
await TadoConfig.store(this.config, storagePath
|
|
121
|
+
await TadoConfig.store(this.config, storagePath);
|
|
118
122
|
|
|
119
123
|
Logger.info('Done!');
|
|
120
124
|
|
package/src/tado/tado-api.js
CHANGED
|
@@ -9,14 +9,14 @@ const tado_auth_url = "https://login.tado.com/oauth2";
|
|
|
9
9
|
const tado_client_id = "1bb50063-6b0c-4d11-bd99-387f4a91cc46";
|
|
10
10
|
|
|
11
11
|
export default class Tado {
|
|
12
|
-
constructor(name,
|
|
13
|
-
this.tadoApiUrl = tadoApiUrl || tado_url;
|
|
14
|
-
this.customTadoApiUrlActive = !!tadoApiUrl;
|
|
15
|
-
this.skipAuth = skipAuth?.toString() === "true";
|
|
12
|
+
constructor(name, auth, storagePath) {
|
|
13
|
+
this.tadoApiUrl = auth.tadoApiUrl || tado_url;
|
|
14
|
+
this.customTadoApiUrlActive = !!auth.tadoApiUrl;
|
|
15
|
+
this.skipAuth = auth.skipAuth?.toString() === "true";
|
|
16
16
|
this.storagePath = storagePath;
|
|
17
17
|
this.name = name;
|
|
18
|
-
const usesExternalTokenFile =
|
|
19
|
-
this._tadoExternalTokenFilePath = usesExternalTokenFile ?
|
|
18
|
+
const usesExternalTokenFile = auth.username?.toLowerCase().endsWith(".json");
|
|
19
|
+
this._tadoExternalTokenFilePath = usesExternalTokenFile ? auth.username : undefined;
|
|
20
20
|
const fnSimpleHash = (str) => {
|
|
21
21
|
let hash = 0;
|
|
22
22
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -25,8 +25,8 @@ export default class Tado {
|
|
|
25
25
|
}
|
|
26
26
|
return (hash >>> 0).toString(36).padStart(7, '0');
|
|
27
27
|
};
|
|
28
|
-
this.username = usesExternalTokenFile ? undefined :
|
|
29
|
-
this._tadoInternalTokenFilePath = usesExternalTokenFile ? undefined : join(this.storagePath, `.tado-token-${fnSimpleHash(
|
|
28
|
+
this.username = usesExternalTokenFile ? undefined : auth.username;
|
|
29
|
+
this._tadoInternalTokenFilePath = usesExternalTokenFile ? undefined : join(this.storagePath, `.tado-token-${fnSimpleHash(auth.username)}.json`);
|
|
30
30
|
this._tadoApiClientId = tado_client_id;
|
|
31
31
|
this._tadoTokenPromise = undefined;
|
|
32
32
|
this._tadoAuthenticationCallback = undefined;
|
package/src/tado/tado-config.js
CHANGED
|
@@ -18,15 +18,12 @@ const deviceHandler = new Map();
|
|
|
18
18
|
let telegram;
|
|
19
19
|
|
|
20
20
|
export default {
|
|
21
|
-
add: async function (config,
|
|
21
|
+
add: async function (config, auths, storagePath) {
|
|
22
22
|
config.homes = config.homes || [];
|
|
23
23
|
|
|
24
|
-
for (const
|
|
25
|
-
let username = user.username;
|
|
24
|
+
for (const auth of auths) {
|
|
26
25
|
|
|
27
|
-
const tado = new TadoApi('Configuration',
|
|
28
|
-
username: username,
|
|
29
|
-
}, storagePath, tadoApiUrl, skipAuth);
|
|
26
|
+
const tado = new TadoApi('Configuration', auth, storagePath);
|
|
30
27
|
|
|
31
28
|
const me = await tado.getMe();
|
|
32
29
|
|
|
@@ -42,7 +39,9 @@ export default {
|
|
|
42
39
|
const homeConfig = {
|
|
43
40
|
id: foundHome.id,
|
|
44
41
|
name: foundHome.name,
|
|
45
|
-
username: username,
|
|
42
|
+
username: auth.username,
|
|
43
|
+
tadoApiUrl: auth.tadoApiUrl,
|
|
44
|
+
skipAuth: auth.skipAuth,
|
|
46
45
|
polling: 300,
|
|
47
46
|
zones: [],
|
|
48
47
|
presence: {
|
|
@@ -152,14 +151,16 @@ export default {
|
|
|
152
151
|
return config;
|
|
153
152
|
},
|
|
154
153
|
|
|
155
|
-
resync: async function (config,
|
|
154
|
+
resync: async function (config, auths, storagePath) {
|
|
156
155
|
const availableHomesInApis = [];
|
|
157
156
|
|
|
158
|
-
for (const
|
|
157
|
+
for (const auth of auths) {
|
|
159
158
|
//Init API with credentials
|
|
160
159
|
const tado = new TadoApi('Configuration', {
|
|
161
|
-
username:
|
|
162
|
-
|
|
160
|
+
username: auth.username,
|
|
161
|
+
tadoApiUrl: auth.tadoApiUrl,
|
|
162
|
+
skipAuth: auth.skipAuth
|
|
163
|
+
}, storagePath);
|
|
163
164
|
|
|
164
165
|
const me = await tado.getMe();
|
|
165
166
|
|
|
@@ -167,7 +168,9 @@ export default {
|
|
|
167
168
|
availableHomesInApis.push({
|
|
168
169
|
id: foundHome.id,
|
|
169
170
|
name: foundHome.name,
|
|
170
|
-
username:
|
|
171
|
+
username: auth.username,
|
|
172
|
+
tadoApiUrl: auth.tadoApiUrl,
|
|
173
|
+
skipAuth: auth.skipAuth
|
|
171
174
|
});
|
|
172
175
|
});
|
|
173
176
|
}
|
|
@@ -191,22 +194,21 @@ export default {
|
|
|
191
194
|
for (let home of config.homes.entries()) {
|
|
192
195
|
if (home.name && home.username) {
|
|
193
196
|
config = await this.refresh(home.name, config, {
|
|
194
|
-
username: home.username
|
|
195
|
-
|
|
197
|
+
username: home.username,
|
|
198
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
199
|
+
skipAuth: home.skipAuth
|
|
200
|
+
}, storagePath);
|
|
196
201
|
}
|
|
197
202
|
}
|
|
198
203
|
|
|
199
|
-
config = await this.add(config, availableHomesInApis, storagePath
|
|
204
|
+
config = await this.add(config, availableHomesInApis, storagePath);
|
|
200
205
|
|
|
201
206
|
return config;
|
|
202
207
|
},
|
|
203
208
|
|
|
204
|
-
refresh: async function (currentHome, config,
|
|
205
|
-
let username = credentials.username;
|
|
209
|
+
refresh: async function (currentHome, config, auth, storagePath) {
|
|
206
210
|
|
|
207
|
-
const tado = new TadoApi('Configuration',
|
|
208
|
-
username: username
|
|
209
|
-
}, storagePath, tadoApiUrl, skipAuth);
|
|
211
|
+
const tado = new TadoApi('Configuration', auth, storagePath);
|
|
210
212
|
|
|
211
213
|
//Home Informations
|
|
212
214
|
let home = config.homes.find((home) => home && home.name === currentHome);
|
|
@@ -229,7 +231,9 @@ export default {
|
|
|
229
231
|
for (let [i, home] of config.homes.entries()) {
|
|
230
232
|
if (config.homes[i].name === homeInfo.name) {
|
|
231
233
|
config.homes[i].id = homeInfo.id;
|
|
232
|
-
config.homes[i].username =
|
|
234
|
+
config.homes[i].username = auth.username;
|
|
235
|
+
config.homes[i].tadoApiUrl = auth.tadoApiUrl;
|
|
236
|
+
config.homes[i].skipAuth = auth.skipAuth;
|
|
233
237
|
config.homes[i].temperatureUnit = homeInfo.temperatureUnit || 'CELSIUS';
|
|
234
238
|
config.homes[i].zones = config.homes[i].zones || [];
|
|
235
239
|
|
|
@@ -498,13 +502,17 @@ export default {
|
|
|
498
502
|
if (!error) {
|
|
499
503
|
//Base Config
|
|
500
504
|
const tado = new TadoApi(home.name, {
|
|
501
|
-
username: home.username
|
|
502
|
-
|
|
505
|
+
username: home.username,
|
|
506
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
507
|
+
skipAuth: home.skipAuth
|
|
508
|
+
}, storagePath);
|
|
503
509
|
|
|
504
510
|
const accessoryConfig = {
|
|
505
511
|
homeId: home.id,
|
|
506
512
|
homeName: home.name,
|
|
507
513
|
username: home.username,
|
|
514
|
+
tadoApiUrl: home.tadoApiUrl,
|
|
515
|
+
skipAuth: home.skipAuth,
|
|
508
516
|
temperatureUnit: home.temperatureUnit || 'CELSIUS',
|
|
509
517
|
geolocation: home.geolocation,
|
|
510
518
|
tado: tado,
|