@homebridge-plugins/homebridge-tado 8.3.0-beta.5 → 8.3.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/CHANGELOG.md +1 -1
- package/package.json +3 -3
- package/src/helper/handler.js +19 -7
- package/src/tado/tado-api.js +12 -19
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- Add tado API counter
|
|
5
5
|
- Improve task scheduling and interval handling
|
|
6
6
|
- Refresh history service directly after polling
|
|
7
|
-
- Add option to disable the history service
|
|
7
|
+
- Add option to disable the history service completely
|
|
8
8
|
- Persist tado zone states to storage directory after every polling
|
|
9
9
|
|
|
10
10
|
## v8.2.0 - 2025-10-23
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homebridge-plugins/homebridge-tado",
|
|
3
|
-
"version": "8.3.0
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"description": "Homebridge plugin for controlling tado° devices.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"fakegato-history": "^0.6.7",
|
|
37
37
|
"form-data": "^4.0.4",
|
|
38
38
|
"fs-extra": "^11.3.2",
|
|
39
|
-
"got": "^14.6.
|
|
39
|
+
"got": "^14.6.1",
|
|
40
40
|
"moment": "^2.30.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
@@ -51,4 +51,4 @@
|
|
|
51
51
|
"globals": "^16.4.0",
|
|
52
52
|
"prettier": "^3.6.2"
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|
package/src/helper/handler.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Logger from '../helper/logger.js';
|
|
2
2
|
import moment from 'moment';
|
|
3
|
-
import { writeFile } from 'fs/promises';
|
|
3
|
+
import { writeFile, access, readFile } from 'fs/promises';
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
|
|
6
6
|
var settingState = false;
|
|
@@ -10,6 +10,18 @@ let tasksInitialized = false;
|
|
|
10
10
|
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
11
11
|
const aRefreshHistoryHandlers = [];
|
|
12
12
|
|
|
13
|
+
export async function getPersistedStates(storagePath) {
|
|
14
|
+
try {
|
|
15
|
+
const sFilePath = join(storagePath, "tado-states.json");
|
|
16
|
+
await access(sFilePath);
|
|
17
|
+
const sData = (await readFile(sFilePath, "utf-8"));
|
|
18
|
+
if (sData) return JSON.parse(sData);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
//no states data => ignore
|
|
21
|
+
Logger.debug(`Failed to read tado states file: ${error.message || error}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
export default (api, accessories, config, tado, telegram) => {
|
|
14
26
|
const storagePath = api.user.storagePath();
|
|
15
27
|
|
|
@@ -705,18 +717,18 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
705
717
|
|
|
706
718
|
async function persistStates(homeId, zoneStates) {
|
|
707
719
|
try {
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
await writeFile(join(storagePath, `tado-states-${homeId}.json`), JSON.stringify(
|
|
720
|
+
const homeData = {};
|
|
721
|
+
homeData.zoneStates = zoneStates ?? {};
|
|
722
|
+
await writeFile(join(storagePath, `tado-states-${homeId}.json`), JSON.stringify(homeData, null, 2), "utf-8");
|
|
711
723
|
} catch (error) {
|
|
712
|
-
Logger.error(`Error while updating tado states file for home id ${homeId}: ${error.message || error}`);
|
|
724
|
+
Logger.error(`Error while updating the tado states file for home id ${homeId}: ${error.message || error}`);
|
|
713
725
|
}
|
|
714
726
|
try {
|
|
715
727
|
const data = {};
|
|
716
728
|
data.counterData = await tado.getCounterData();
|
|
717
|
-
await writeFile(join(storagePath, "tado-
|
|
729
|
+
await writeFile(join(storagePath, "tado-states.json"), JSON.stringify(data, null, 2), "utf-8");
|
|
718
730
|
} catch (error) {
|
|
719
|
-
Logger.error(`Error while updating tado
|
|
731
|
+
Logger.error(`Error while updating the tado states file: ${error.message || error}`);
|
|
720
732
|
}
|
|
721
733
|
try {
|
|
722
734
|
//wait for fakegato services to be loaded
|
package/src/tado/tado-api.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Logger from '../helper/logger.js';
|
|
2
|
+
import { getPersistedStates } from '../helper/handler.js';
|
|
2
3
|
import got from 'got';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { access, readFile, writeFile } from 'fs/promises';
|
|
5
6
|
|
|
6
7
|
const tado_url = "https://my.tado.com";
|
|
7
8
|
const tado_auth_url = "https://login.tado.com/oauth2";
|
|
@@ -25,7 +26,7 @@ export default class Tado {
|
|
|
25
26
|
return (hash >>> 0).toString(36).padStart(7, '0');
|
|
26
27
|
};
|
|
27
28
|
this.username = usesExternalTokenFile ? undefined : config.username;
|
|
28
|
-
this._tadoInternalTokenFilePath = usesExternalTokenFile ? undefined :
|
|
29
|
+
this._tadoInternalTokenFilePath = usesExternalTokenFile ? undefined : join(this.storagePath, `.tado-token-${fnSimpleHash(config.username)}.json`);
|
|
29
30
|
this._tadoApiClientId = tado_client_id;
|
|
30
31
|
this._tadoTokenPromise = undefined;
|
|
31
32
|
this._tadoAuthenticationCallback = undefined;
|
|
@@ -34,17 +35,9 @@ export default class Tado {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
async _initCounter() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
await access(sFilePath);
|
|
41
|
-
const sData = (await readFile(sFilePath, "utf-8"));
|
|
42
|
-
counterData = JSON.parse(sData)?.counterData;
|
|
43
|
-
} catch (_err) {
|
|
44
|
-
//no counter data => ignore
|
|
45
|
-
}
|
|
46
|
-
this._counter = counterData?.counter ?? 0;
|
|
47
|
-
this._counterTimestamp = counterData?.counterTimestamp ?? new Date().toISOString();
|
|
38
|
+
const persistedCounterData = (await getPersistedStates(this.storagePath))?.counterData;
|
|
39
|
+
this._counter = persistedCounterData?.counter ?? 0;
|
|
40
|
+
this._counterTimestamp = persistedCounterData?.counterTimestamp ?? new Date().toISOString();
|
|
48
41
|
this._checkCounterMidnightReset();
|
|
49
42
|
}
|
|
50
43
|
|
|
@@ -106,7 +99,7 @@ export default class Tado {
|
|
|
106
99
|
async _retrieveToken() {
|
|
107
100
|
try {
|
|
108
101
|
if (this._tadoBearerToken.refresh_token) return this._refreshToken(this._tadoBearerToken.refresh_token);
|
|
109
|
-
await
|
|
102
|
+
await access(this._tadoInternalTokenFilePath);
|
|
110
103
|
const refresh_token = await this._retrieveRefreshTokenFromInternalFile();
|
|
111
104
|
return this._refreshToken(refresh_token);
|
|
112
105
|
|
|
@@ -119,7 +112,7 @@ export default class Tado {
|
|
|
119
112
|
const maxRetries = 3;
|
|
120
113
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
121
114
|
try {
|
|
122
|
-
const data = await
|
|
115
|
+
const data = await readFile(this._tadoInternalTokenFilePath, "utf8");
|
|
123
116
|
const json = JSON.parse(data);
|
|
124
117
|
if (json.refresh_token) return json.refresh_token;
|
|
125
118
|
} catch (error) {
|
|
@@ -145,7 +138,7 @@ export default class Tado {
|
|
|
145
138
|
await this._increaseCounter();
|
|
146
139
|
const { access_token, refresh_token } = response.body;
|
|
147
140
|
if (!access_token || !refresh_token) throw new Error("Empty access/refresh token.");
|
|
148
|
-
await
|
|
141
|
+
await writeFile(this._tadoInternalTokenFilePath, JSON.stringify({ access_token, refresh_token }));
|
|
149
142
|
this._tadoBearerToken = { access_token, refresh_token, timestamp: Date.now() };
|
|
150
143
|
} catch (error) {
|
|
151
144
|
Logger.warn(`Error while refreshing token: ${error.message || error}`);
|
|
@@ -187,7 +180,7 @@ export default class Tado {
|
|
|
187
180
|
if (tokenResponse?.body) {
|
|
188
181
|
const { access_token, refresh_token } = tokenResponse.body;
|
|
189
182
|
if (access_token && refresh_token) {
|
|
190
|
-
await
|
|
183
|
+
await writeFile(this._tadoInternalTokenFilePath, JSON.stringify({ access_token, refresh_token }));
|
|
191
184
|
this._tadoBearerToken = { access_token, refresh_token, timestamp: Date.now() };
|
|
192
185
|
Logger.info("Authentication successful!");
|
|
193
186
|
return;
|
|
@@ -202,7 +195,7 @@ export default class Tado {
|
|
|
202
195
|
const maxRetries = 3;
|
|
203
196
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
204
197
|
try {
|
|
205
|
-
const data = await
|
|
198
|
+
const data = await readFile(this._tadoExternalTokenFilePath, 'utf8');
|
|
206
199
|
const json = JSON.parse(data);
|
|
207
200
|
if (json.access_token) {
|
|
208
201
|
this._tadoBearerToken = { access_token: json.access_token, refresh_token: undefined, timestamp: Date.now() };
|