@homebridge-plugins/homebridge-tado 8.6.0-beta.1 → 8.6.0-beta.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/CHANGELOG.md +9 -3
- package/homebridge-ui/server.js +1 -1
- package/package.json +1 -1
- package/src/helper/handler.js +88 -79
- package/src/tado/tado-api.js +55 -13
- package/src/tado/tado-config.js +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## v8.6.0 - 2025-10-
|
|
4
|
-
- BREAKING CHANGE: If you use tadoApiUrl or skipAuth,
|
|
5
|
-
-
|
|
3
|
+
## v8.6.0 - 2025-10-31
|
|
4
|
+
- BREAKING CHANGE: If you use tadoApiUrl or skipAuth, they must now be defined 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 zone state updates
|
|
6
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
|
+
- Advanced queue handling for update and persistence tasks
|
|
9
|
+
- Tado API counter now tracks and persists for each authenticated user
|
|
10
|
+
- Fix: Polling and tasks for multiple homes (#178)
|
|
7
11
|
- Fix: Corrected zone update handling that could previously cause unintended heating changes (#178)
|
|
12
|
+
- Added additional debug log messages for zone updates
|
|
13
|
+
- Note: This update will reset your tado api counter for the current day to zero.
|
|
8
14
|
|
|
9
15
|
## v8.5.0 - 2025-10-27
|
|
10
16
|
- Change minimum polling interval to 30s due to improvements made in v8.2.0
|
package/homebridge-ui/server.js
CHANGED
package/package.json
CHANGED
package/src/helper/handler.js
CHANGED
|
@@ -1,42 +1,35 @@
|
|
|
1
1
|
import Logger from '../helper/logger.js';
|
|
2
2
|
import moment from 'moment';
|
|
3
|
-
import { writeFile
|
|
3
|
+
import { writeFile } from 'fs/promises';
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
|
|
6
|
-
let settingState = false;
|
|
7
|
-
let tasksInitialized = false;
|
|
8
|
-
let lastGetStates = 0;
|
|
9
|
-
const delayTimer = {};
|
|
10
|
-
|
|
11
6
|
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
export async function getPersistedStates(storagePath) {
|
|
15
|
-
try {
|
|
16
|
-
const sFilePath = join(storagePath, "tado-states.json");
|
|
17
|
-
await access(sFilePath);
|
|
18
|
-
const sData = (await readFile(sFilePath, "utf-8"));
|
|
19
|
-
if (sData) return JSON.parse(sData);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
//no states data => ignore
|
|
22
|
-
Logger.debug(`Failed to read tado states file: ${error.message || error}`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
7
|
+
const helpers = {};
|
|
25
8
|
|
|
26
9
|
export default (api, accessories, config, tado, telegram) => {
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
//init helper variables for current home scope
|
|
11
|
+
if (!helpers[config.homeId]) {
|
|
12
|
+
helpers[config.homeId] = {
|
|
13
|
+
settingState: false,
|
|
14
|
+
tasksInitialized: false,
|
|
15
|
+
lastGetStates: 0,
|
|
16
|
+
lastPersistZoneStates: 0,
|
|
17
|
+
persistPromise: Promise.resolve(),
|
|
18
|
+
updateZonesRunning: false,
|
|
19
|
+
updateZonesNextQueued: false,
|
|
20
|
+
delayTimer: {},
|
|
21
|
+
refreshHistoryHandlers: [],
|
|
22
|
+
statesIntervalTime: Math.max(config.polling, 30) * 1000,
|
|
23
|
+
storagePath: api.user.storagePath(),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
29
26
|
|
|
30
27
|
async function setStates(accessory, accs, target, value) {
|
|
31
28
|
let zoneUpdated = false;
|
|
32
|
-
|
|
33
29
|
accessories = accs.filter((acc) => acc && acc.context.config.homeName === config.homeName);
|
|
34
|
-
|
|
35
30
|
try {
|
|
36
|
-
settingState = true;
|
|
37
|
-
|
|
31
|
+
helpers[config.homeId].settingState = true;
|
|
38
32
|
value = typeof value === 'number' ? parseFloat(value.toFixed(2)) : value;
|
|
39
|
-
|
|
40
33
|
Logger.info(target + ': ' + value, accessory.displayName);
|
|
41
34
|
|
|
42
35
|
switch (accessory.context.config.subtype) {
|
|
@@ -63,10 +56,10 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
63
56
|
value < 5
|
|
64
57
|
) {
|
|
65
58
|
if (value === 0) {
|
|
66
|
-
if (delayTimer[accessory.displayName]) {
|
|
59
|
+
if (helpers[config.homeId].delayTimer[accessory.displayName]) {
|
|
67
60
|
Logger.info('Resetting delay timer', accessory.displayName);
|
|
68
|
-
clearTimeout(delayTimer[accessory.displayName]);
|
|
69
|
-
delayTimer[accessory.displayName] = null;
|
|
61
|
+
clearTimeout(helpers[config.homeId].delayTimer[accessory.displayName]);
|
|
62
|
+
helpers[config.homeId].delayTimer[accessory.displayName] = null;
|
|
70
63
|
}
|
|
71
64
|
|
|
72
65
|
power = 'OFF';
|
|
@@ -111,14 +104,14 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
111
104
|
let timer = accessory.context.delayTimer;
|
|
112
105
|
let tarState = value === 1 ? 'HEAT' : 'AUTO';
|
|
113
106
|
|
|
114
|
-
if (delayTimer[accessory.displayName]) {
|
|
115
|
-
clearTimeout(delayTimer[accessory.displayName]);
|
|
116
|
-
delayTimer[accessory.displayName] = null;
|
|
107
|
+
if (helpers[config.homeId].delayTimer[accessory.displayName]) {
|
|
108
|
+
clearTimeout(helpers[config.homeId].delayTimer[accessory.displayName]);
|
|
109
|
+
helpers[config.homeId].delayTimer[accessory.displayName] = null;
|
|
117
110
|
}
|
|
118
111
|
|
|
119
112
|
Logger.info('Wait ' + timer + ' seconds before switching state', accessory.displayName);
|
|
120
113
|
|
|
121
|
-
delayTimer[accessory.displayName] = setTimeout(async () => {
|
|
114
|
+
helpers[config.homeId].delayTimer[accessory.displayName] = setTimeout(async () => {
|
|
122
115
|
Logger.info('Delay timer finished, switching state to ' + tarState, accessory.displayName);
|
|
123
116
|
|
|
124
117
|
//targetState
|
|
@@ -172,7 +165,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
172
165
|
);
|
|
173
166
|
}
|
|
174
167
|
|
|
175
|
-
delayTimer[accessory.displayName] = null;
|
|
168
|
+
helpers[config.homeId].delayTimer[accessory.displayName] = null;
|
|
176
169
|
}, timer * 1000);
|
|
177
170
|
}
|
|
178
171
|
} else {
|
|
@@ -567,11 +560,11 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
567
560
|
} catch (err) {
|
|
568
561
|
errorHandler(err);
|
|
569
562
|
} finally {
|
|
570
|
-
settingState = false;
|
|
563
|
+
helpers[config.homeId].settingState = false;
|
|
571
564
|
//update zones to ensure correct state in Apple Home
|
|
572
|
-
const timeSinceLastGetStates = Date.now() - lastGetStates;
|
|
573
|
-
const statesIntervalTimeLeft = statesIntervalTime - timeSinceLastGetStates;
|
|
574
|
-
if (zoneUpdated && statesIntervalTimeLeft >
|
|
565
|
+
const timeSinceLastGetStates = helpers[config.homeId].lastGetStates === 0 ? 0 : (Date.now() - helpers[config.homeId].lastGetStates);
|
|
566
|
+
const statesIntervalTimeLeft = helpers[config.homeId].statesIntervalTime - timeSinceLastGetStates;
|
|
567
|
+
if (zoneUpdated && statesIntervalTimeLeft > (10 * 1000)) await updateZones();
|
|
575
568
|
}
|
|
576
569
|
}
|
|
577
570
|
|
|
@@ -729,59 +722,49 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
729
722
|
}
|
|
730
723
|
}
|
|
731
724
|
|
|
732
|
-
|
|
725
|
+
function persistZoneStates(homeId, zoneStates) {
|
|
726
|
+
helpers[config.homeId].persistPromise = helpers[config.homeId].persistPromise.then(() => _persistZoneStates(homeId, zoneStates));
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
async function _persistZoneStates(homeId, zoneStates) {
|
|
730
|
+
if ((Date.now() - helpers[config.homeId].lastPersistZoneStates) < (10 * 1000)) return;
|
|
733
731
|
try {
|
|
734
732
|
if (zoneStates && Object.keys(zoneStates).length) {
|
|
735
733
|
const homeData = {};
|
|
736
734
|
homeData.zoneStates = zoneStates;
|
|
737
|
-
await writeFile(join(storagePath, `tado-states-${homeId}.json`), JSON.stringify(homeData, null, 2), "utf-8");
|
|
735
|
+
await writeFile(join(helpers[config.homeId].storagePath, `tado-states-${homeId}.json`), JSON.stringify(homeData, null, 2), "utf-8");
|
|
736
|
+
helpers[config.homeId].lastPersistZoneStates = Date.now();
|
|
738
737
|
} else {
|
|
739
|
-
Logger.
|
|
738
|
+
Logger.warn(`Skipping persistence of tado states file for home ${homeId}: zone states are empty.`);
|
|
740
739
|
}
|
|
741
740
|
} catch (error) {
|
|
742
741
|
Logger.error(`Error while updating the tado states file for home ${homeId}: ${error.message || error}`);
|
|
743
742
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
} catch (error) {
|
|
749
|
-
Logger.error(`Error while updating the tado states file: ${error.message || error}`);
|
|
750
|
-
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
async function refreshHistoryServices() {
|
|
746
|
+
if (!helpers[config.homeId].refreshHistoryHandlers.length) return;
|
|
751
747
|
try {
|
|
752
748
|
//wait for fakegato history services to be loaded
|
|
753
749
|
await timeout(4000);
|
|
754
|
-
for (const refreshHistory of
|
|
750
|
+
for (const refreshHistory of helpers[config.homeId].refreshHistoryHandlers) {
|
|
755
751
|
refreshHistory();
|
|
756
752
|
}
|
|
757
753
|
} catch (error) {
|
|
758
|
-
Logger.error(`Error while refreshing history: ${error.message || error}`);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
async function logCounter() {
|
|
763
|
-
try {
|
|
764
|
-
const iCounter = (await tado.getCounterData()).counter;
|
|
765
|
-
Logger.info(`Tado API counter: ${iCounter.toLocaleString('en-US')}`);
|
|
766
|
-
} catch (error) {
|
|
767
|
-
Logger.warn(`Failed to get Tado API counter: ${error.message || error}`);
|
|
754
|
+
Logger.error(`Error while refreshing history services: ${error.message || error}`);
|
|
768
755
|
}
|
|
769
756
|
}
|
|
770
757
|
|
|
771
758
|
function initTasks() {
|
|
772
|
-
if (tasksInitialized) return;
|
|
773
|
-
tasksInitialized = true;
|
|
759
|
+
if (helpers[config.homeId].tasksInitialized) return;
|
|
760
|
+
helpers[config.homeId].tasksInitialized = true;
|
|
774
761
|
|
|
775
762
|
void getStates();
|
|
776
|
-
setInterval(() => getStates(), statesIntervalTime);
|
|
777
|
-
|
|
778
|
-
void logCounter();
|
|
779
|
-
setInterval(() => logCounter(), 60 * 60 * 1000);
|
|
763
|
+
setInterval(() => void getStates(), helpers[config.homeId].statesIntervalTime);
|
|
780
764
|
}
|
|
781
765
|
|
|
782
766
|
async function getStates() {
|
|
783
|
-
lastGetStates = Date.now();
|
|
784
|
-
let zoneStates = {};
|
|
767
|
+
helpers[config.homeId].lastGetStates = Date.now();
|
|
785
768
|
try {
|
|
786
769
|
//ME
|
|
787
770
|
if (!config.homeId) await updateMe();
|
|
@@ -790,7 +773,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
790
773
|
if (!config.temperatureUnit) await updateHome();
|
|
791
774
|
|
|
792
775
|
//Zones
|
|
793
|
-
if (config.zones.length)
|
|
776
|
+
if (config.zones.length) await updateZones();
|
|
794
777
|
|
|
795
778
|
//MobileDevices
|
|
796
779
|
if (config.presence.length) await updateMobileDevices();
|
|
@@ -809,12 +792,12 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
809
792
|
} catch (err) {
|
|
810
793
|
errorHandler(err);
|
|
811
794
|
} finally {
|
|
812
|
-
void
|
|
795
|
+
void refreshHistoryServices();
|
|
813
796
|
}
|
|
814
797
|
}
|
|
815
798
|
|
|
816
799
|
async function updateMe() {
|
|
817
|
-
if (settingState) return;
|
|
800
|
+
if (helpers[config.homeId].settingState) return;
|
|
818
801
|
|
|
819
802
|
Logger.debug('Polling User Info...', config.homeName);
|
|
820
803
|
|
|
@@ -826,7 +809,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
826
809
|
}
|
|
827
810
|
|
|
828
811
|
async function updateHome() {
|
|
829
|
-
if (settingState) return;
|
|
812
|
+
if (helpers[config.homeId].settingState) return;
|
|
830
813
|
|
|
831
814
|
Logger.debug('Polling Home Info...', config.homeName);
|
|
832
815
|
|
|
@@ -851,7 +834,32 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
851
834
|
}
|
|
852
835
|
|
|
853
836
|
async function updateZones() {
|
|
854
|
-
if (
|
|
837
|
+
if (helpers[config.homeId].updateZonesRunning) {
|
|
838
|
+
helpers[config.homeId].updateZonesNextQueued = true;
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
helpers[config.homeId].updateZonesRunning = true;
|
|
842
|
+
try {
|
|
843
|
+
while (true) {
|
|
844
|
+
try {
|
|
845
|
+
await _updateZones();
|
|
846
|
+
} catch (error) {
|
|
847
|
+
Logger.error(`Failed to update zones: ${error.message || error}`);
|
|
848
|
+
}
|
|
849
|
+
if (helpers[config.homeId].updateZonesNextQueued) {
|
|
850
|
+
helpers[config.homeId].updateZonesNextQueued = false;
|
|
851
|
+
//continue with loop
|
|
852
|
+
} else {
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
} finally {
|
|
857
|
+
helpers[config.homeId].updateZonesRunning = false;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
async function _updateZones() {
|
|
862
|
+
if (helpers[config.homeId].settingState) return;
|
|
855
863
|
|
|
856
864
|
Logger.debug('Polling Zones...', config.homeName);
|
|
857
865
|
|
|
@@ -903,6 +911,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
903
911
|
}
|
|
904
912
|
|
|
905
913
|
const zoneStates = (await tado.getZoneStates(config.homeId))["zoneStates"] ?? {};
|
|
914
|
+
void persistZoneStates(config.homeId, zoneStates);
|
|
906
915
|
|
|
907
916
|
for (const zone of config.zones) {
|
|
908
917
|
const zoneState = zoneStates[zone.id.toString()];
|
|
@@ -1313,7 +1322,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1313
1322
|
}
|
|
1314
1323
|
|
|
1315
1324
|
async function updateMobileDevices() {
|
|
1316
|
-
if (settingState) return;
|
|
1325
|
+
if (helpers[config.homeId].settingState) return;
|
|
1317
1326
|
|
|
1318
1327
|
Logger.debug('Polling MobileDevices...', config.homeName);
|
|
1319
1328
|
|
|
@@ -1367,7 +1376,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1367
1376
|
}
|
|
1368
1377
|
|
|
1369
1378
|
async function updateWeather() {
|
|
1370
|
-
if (settingState) return;
|
|
1379
|
+
if (helpers[config.homeId].settingState) return;
|
|
1371
1380
|
|
|
1372
1381
|
const weatherTemperatureAccessory = accessories.filter(
|
|
1373
1382
|
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Weather'
|
|
@@ -1422,7 +1431,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1422
1431
|
}
|
|
1423
1432
|
|
|
1424
1433
|
async function updatePresence() {
|
|
1425
|
-
if (settingState) return;
|
|
1434
|
+
if (helpers[config.homeId].settingState) return;
|
|
1426
1435
|
|
|
1427
1436
|
const presenceLockAccessory = accessories.filter(
|
|
1428
1437
|
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Presence Lock'
|
|
@@ -1467,7 +1476,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1467
1476
|
}
|
|
1468
1477
|
|
|
1469
1478
|
async function updateRunningTime() {
|
|
1470
|
-
if (settingState) return;
|
|
1479
|
+
if (helpers[config.homeId].settingState) return;
|
|
1471
1480
|
|
|
1472
1481
|
const centralSwitchAccessory = accessories.filter(
|
|
1473
1482
|
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Central Switch'
|
|
@@ -1512,7 +1521,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1512
1521
|
}
|
|
1513
1522
|
|
|
1514
1523
|
async function updateDevices() {
|
|
1515
|
-
if (settingState) return;
|
|
1524
|
+
if (helpers[config.homeId].settingState) return;
|
|
1516
1525
|
|
|
1517
1526
|
Logger.debug('Polling Devices...', config.homeName);
|
|
1518
1527
|
|
|
@@ -1583,6 +1592,6 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1583
1592
|
getStates: getStates,
|
|
1584
1593
|
setStates: setStates,
|
|
1585
1594
|
changedStates: changedStates,
|
|
1586
|
-
refreshHistoryHandlers:
|
|
1595
|
+
refreshHistoryHandlers: helpers[config.homeId].refreshHistoryHandlers
|
|
1587
1596
|
};
|
|
1588
|
-
};
|
|
1597
|
+
};
|
package/src/tado/tado-api.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Logger from '../helper/logger.js';
|
|
2
|
-
import { getPersistedStates } from '../helper/handler.js';
|
|
3
2
|
import got from 'got';
|
|
4
3
|
import { join } from 'path';
|
|
5
4
|
import { access, readFile, writeFile } from 'fs/promises';
|
|
@@ -8,8 +7,17 @@ const tado_url = "https://my.tado.com";
|
|
|
8
7
|
const tado_auth_url = "https://login.tado.com/oauth2";
|
|
9
8
|
const tado_client_id = "1bb50063-6b0c-4d11-bd99-387f4a91cc46";
|
|
10
9
|
|
|
10
|
+
function _getSimpleHash(str) {
|
|
11
|
+
let hash = 0;
|
|
12
|
+
for (let i = 0; i < str.length; i++) {
|
|
13
|
+
const char = str.charCodeAt(i);
|
|
14
|
+
hash = (hash << 5) - hash + char;
|
|
15
|
+
}
|
|
16
|
+
return (hash >>> 0).toString(36).padStart(7, '0');
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export default class Tado {
|
|
12
|
-
constructor(name, auth, storagePath) {
|
|
20
|
+
constructor(name, auth, storagePath, counterActivated) {
|
|
13
21
|
this.tadoApiUrl = auth.tadoApiUrl || tado_url;
|
|
14
22
|
this.customTadoApiUrlActive = !!auth.tadoApiUrl;
|
|
15
23
|
this.skipAuth = auth.skipAuth?.toString() === "true";
|
|
@@ -17,28 +25,42 @@ export default class Tado {
|
|
|
17
25
|
this.name = name;
|
|
18
26
|
const usesExternalTokenFile = auth.username?.toLowerCase().endsWith(".json");
|
|
19
27
|
this._tadoExternalTokenFilePath = usesExternalTokenFile ? auth.username : undefined;
|
|
20
|
-
|
|
21
|
-
let hash = 0;
|
|
22
|
-
for (let i = 0; i < str.length; i++) {
|
|
23
|
-
const char = str.charCodeAt(i);
|
|
24
|
-
hash = (hash << 5) - hash + char;
|
|
25
|
-
}
|
|
26
|
-
return (hash >>> 0).toString(36).padStart(7, '0');
|
|
27
|
-
};
|
|
28
|
+
this.hashedUsername = _getSimpleHash(auth.username);
|
|
28
29
|
this.username = usesExternalTokenFile ? undefined : auth.username;
|
|
29
|
-
this._tadoInternalTokenFilePath = usesExternalTokenFile ? undefined : join(this.storagePath, `.tado-token-${
|
|
30
|
+
this._tadoInternalTokenFilePath = usesExternalTokenFile ? undefined : join(this.storagePath, `.tado-token-${this.hashedUsername}.json`);
|
|
30
31
|
this._tadoApiClientId = tado_client_id;
|
|
31
32
|
this._tadoTokenPromise = undefined;
|
|
32
33
|
this._tadoAuthenticationCallback = undefined;
|
|
34
|
+
this._counterActivated = counterActivated?.toString() === "true";
|
|
33
35
|
this._counterInitPromise = this._initCounter();
|
|
34
36
|
Logger.debug("API successfull initialized", this.name);
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
async _initCounter() {
|
|
38
|
-
|
|
40
|
+
if (!this._counterActivated) return;
|
|
41
|
+
const persistedCounterData = (await this._getPersistedCounter())?.counterData;
|
|
39
42
|
this._counter = persistedCounterData?.counter ?? 0;
|
|
40
43
|
this._counterTimestamp = persistedCounterData?.counterTimestamp ?? new Date().toISOString();
|
|
41
44
|
this._checkCounterMidnightReset();
|
|
45
|
+
//wait some seconds to catch recent api calls
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
void this._logCounter();
|
|
48
|
+
setInterval(() => void this._logCounter(), 60 * 60 * 1000);
|
|
49
|
+
void this._persistCounterData();
|
|
50
|
+
setInterval(() => void this._persistCounterData(), 5 * 60 * 1000);
|
|
51
|
+
}, 4 * 1000);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async _getPersistedCounter() {
|
|
55
|
+
try {
|
|
56
|
+
const filePath = join(this.storagePath, `tado-api-${this.hashedUsername}.json`);
|
|
57
|
+
await access(filePath);
|
|
58
|
+
const data = (await readFile(filePath, "utf-8"));
|
|
59
|
+
if (data) return JSON.parse(data);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
//no persisted counter data => ignore
|
|
62
|
+
Logger.debug(`Failed to read tado api file for user ${this.hashedUsername}: ${error.message || error}`);
|
|
63
|
+
}
|
|
42
64
|
}
|
|
43
65
|
|
|
44
66
|
_checkCounterMidnightReset() {
|
|
@@ -53,6 +75,7 @@ export default class Tado {
|
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
async _increaseCounter() {
|
|
78
|
+
if (!this._counterActivated) return;
|
|
56
79
|
try {
|
|
57
80
|
await this._counterInitPromise;
|
|
58
81
|
this._checkCounterMidnightReset();
|
|
@@ -63,7 +86,7 @@ export default class Tado {
|
|
|
63
86
|
}
|
|
64
87
|
}
|
|
65
88
|
|
|
66
|
-
async
|
|
89
|
+
async _getCounterData() {
|
|
67
90
|
await this._counterInitPromise;
|
|
68
91
|
return {
|
|
69
92
|
counter: this._counter,
|
|
@@ -71,6 +94,25 @@ export default class Tado {
|
|
|
71
94
|
};
|
|
72
95
|
}
|
|
73
96
|
|
|
97
|
+
async _persistCounterData() {
|
|
98
|
+
try {
|
|
99
|
+
const data = {};
|
|
100
|
+
data.counterData = await this._getCounterData();
|
|
101
|
+
await writeFile(join(this.storagePath, `tado-api-${this.hashedUsername}.json`), JSON.stringify(data, null, 2), "utf-8");
|
|
102
|
+
} catch (error) {
|
|
103
|
+
Logger.error(`Error while updating the tado api file for user ${this.hashedUsername}: ${error.message || error}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async _logCounter() {
|
|
108
|
+
try {
|
|
109
|
+
const counter = (await this._getCounterData()).counter;
|
|
110
|
+
Logger.info(`Tado API counter: ${counter.toLocaleString('en-US')}`);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
Logger.warn(`Failed to get Tado API counter: ${error.message || error}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
74
116
|
async getToken() {
|
|
75
117
|
Logger.debug('Get access token...', this.name);
|
|
76
118
|
if (!this._tadoTokenPromise) {
|
package/src/tado/tado-config.js
CHANGED
|
@@ -23,7 +23,7 @@ export default {
|
|
|
23
23
|
|
|
24
24
|
for (const auth of auths) {
|
|
25
25
|
|
|
26
|
-
const tado = new TadoApi('Configuration', auth, storagePath);
|
|
26
|
+
const tado = new TadoApi('Configuration', auth, storagePath, false);
|
|
27
27
|
|
|
28
28
|
const me = await tado.getMe();
|
|
29
29
|
|
|
@@ -160,7 +160,7 @@ export default {
|
|
|
160
160
|
username: auth.username,
|
|
161
161
|
tadoApiUrl: auth.tadoApiUrl,
|
|
162
162
|
skipAuth: auth.skipAuth
|
|
163
|
-
}, storagePath);
|
|
163
|
+
}, storagePath, false);
|
|
164
164
|
|
|
165
165
|
const me = await tado.getMe();
|
|
166
166
|
|
|
@@ -208,7 +208,7 @@ export default {
|
|
|
208
208
|
|
|
209
209
|
refresh: async function (currentHome, config, auth, storagePath) {
|
|
210
210
|
|
|
211
|
-
const tado = new TadoApi('Configuration', auth, storagePath);
|
|
211
|
+
const tado = new TadoApi('Configuration', auth, storagePath, false);
|
|
212
212
|
|
|
213
213
|
//Home Informations
|
|
214
214
|
let home = config.homes.find((home) => home && home.name === currentHome);
|
|
@@ -505,7 +505,7 @@ export default {
|
|
|
505
505
|
username: home.username,
|
|
506
506
|
tadoApiUrl: home.tadoApiUrl,
|
|
507
507
|
skipAuth: home.skipAuth
|
|
508
|
-
}, storagePath);
|
|
508
|
+
}, storagePath, true);
|
|
509
509
|
|
|
510
510
|
const accessoryConfig = {
|
|
511
511
|
homeId: home.id,
|