@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 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-beta.5",
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.0",
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
+ }
@@ -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 data = {};
709
- data.zoneStates = zoneStates ?? {};
710
- await writeFile(join(storagePath, `tado-states-${homeId}.json`), JSON.stringify(data, null, 2), "utf-8");
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-counter.json"), JSON.stringify(data, null, 2), "utf-8");
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 counter file: ${error.message || error}`);
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
@@ -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 path from 'path';
4
- import fs, { access, readFile } from 'fs/promises';
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 : path.join(this.storagePath, `.tado-token-${fnSimpleHash(config.username)}.json`);
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
- let counterData;
38
- try {
39
- const sFilePath = path.join(this.storagePath, `tado-counter.json`);
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 fs.access(this._tadoInternalTokenFilePath);
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 fs.readFile(this._tadoInternalTokenFilePath, "utf8");
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 fs.writeFile(this._tadoInternalTokenFilePath, JSON.stringify({ access_token, refresh_token }));
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 fs.writeFile(this._tadoInternalTokenFilePath, JSON.stringify({ access_token, refresh_token }));
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 fs.readFile(this._tadoExternalTokenFilePath, 'utf8');
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() };