@homebridge-plugins/homebridge-tado 8.6.0-beta.3 → 8.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/CHANGELOG.md +11 -11
- package/config.schema.json +6 -0
- package/homebridge-ui/public/js/main.js +3 -0
- package/homebridge-ui/public/js/schema.js +6 -0
- package/package.json +5 -5
- package/src/accessories/heatercooler.js +9 -31
- package/src/accessories/thermostat.js +9 -21
- package/src/helper/handler.js +23 -14
- package/src/helper/update-buffer.js +69 -0
- package/src/platform.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## v8.6.0
|
|
4
|
-
- BREAKING CHANGE:
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
3
|
+
## v8.6.0 — 2025-11-01
|
|
4
|
+
- BREAKING CHANGE: `tadoApiUrl` and `skipAuth` must now be defined under each home configuration for proper multi-home support (#176)
|
|
5
|
+
- New parameter `preferSiriTemperature` for improved Siri handling — allows temperature changes via Siri without forcing Auto mode (#178). See [#178 (comment)](https://github.com/homebridge-plugins/homebridge-tado/issues/178#issuecomment-3476646430) for a detailed explanation
|
|
6
|
+
- Restored stable update behavior from v8.3.1 and earlier while keeping Siri compatibility (#178)
|
|
7
|
+
- Reworked thermostat update logic: batches state and temperature updates within 400 ms for more reliable state updates (#178)
|
|
8
|
+
- Improved zone update and persistence handling for faster, more consistent status updates
|
|
9
|
+
- Optimized task queue to prevent overlapping operations and API calls
|
|
10
|
+
- Fixed multi-home polling and individual API handling (#176)
|
|
11
|
+
- Added enhanced debug logs for zone updates and API interactions
|
|
12
|
+
- Note: This update resets the Tado API counter for the current day
|
|
13
|
+
- Apologies for the unexpected behavior introduced in 8.4.x–8.5.x — this release restores consistent and reliable behavior, with an optional fix for Siri users. Full statement: [#178 (comment)](https://github.com/homebridge-plugins/homebridge-tado/issues/178#issuecomment-3476646430)
|
|
14
14
|
|
|
15
15
|
## v8.5.0 - 2025-10-27
|
|
16
16
|
- Change minimum polling interval to 30s due to improvements made in v8.2.0
|
package/config.schema.json
CHANGED
|
@@ -568,6 +568,11 @@
|
|
|
568
568
|
"title": "Disable History Service",
|
|
569
569
|
"type": "boolean",
|
|
570
570
|
"description": "Optional: Skip creation of history service."
|
|
571
|
+
},
|
|
572
|
+
"preferSiriTemperature": {
|
|
573
|
+
"title": "Prefer Siri temperature changes",
|
|
574
|
+
"type": "boolean",
|
|
575
|
+
"description": "Prefers temperature changes when the Auto (state=3) mode is sent simultaneously. Default: false."
|
|
571
576
|
}
|
|
572
577
|
}
|
|
573
578
|
},
|
|
@@ -575,6 +580,7 @@
|
|
|
575
580
|
"name",
|
|
576
581
|
"debug",
|
|
577
582
|
"disableHistoryService",
|
|
583
|
+
"preferSiriTemperature",
|
|
578
584
|
{
|
|
579
585
|
"key": "homes",
|
|
580
586
|
"type": "array",
|
|
@@ -133,6 +133,7 @@ async function createCustomSchema(home) {
|
|
|
133
133
|
name: pluginConfig[0].name,
|
|
134
134
|
debug: pluginConfig[0].debug,
|
|
135
135
|
disableHistoryService: pluginConfig[0].disableHistoryService,
|
|
136
|
+
preferSiriTemperature: pluginConfig[0].preferSiriTemperature,
|
|
136
137
|
homes: home
|
|
137
138
|
});
|
|
138
139
|
|
|
@@ -141,6 +142,7 @@ async function createCustomSchema(home) {
|
|
|
141
142
|
pluginConfig[0].name = config.name;
|
|
142
143
|
pluginConfig[0].debug = config.debug;
|
|
143
144
|
pluginConfig[0].disableHistoryService = config.disableHistoryService;
|
|
145
|
+
pluginConfig[0].preferSiriTemperature = config.preferSiriTemperature;
|
|
144
146
|
pluginConfig[0].homes = pluginConfig[0].homes.map(myHome => {
|
|
145
147
|
if (myHome.name === config.homes.name) {
|
|
146
148
|
myHome = config.homes;
|
|
@@ -284,6 +286,7 @@ async function removeDeviceFromConfig(name) {
|
|
|
284
286
|
if (!pluginConfig[0].homes.length) {
|
|
285
287
|
delete pluginConfig[0].debug;
|
|
286
288
|
delete pluginConfig[0].disableHistoryService;
|
|
289
|
+
delete pluginConfig[0].preferSiriTemperature;
|
|
287
290
|
}
|
|
288
291
|
|
|
289
292
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -560,12 +560,18 @@ const schema = {
|
|
|
560
560
|
'title': 'Disable History Service',
|
|
561
561
|
'type': 'boolean',
|
|
562
562
|
'description': 'Optional: Skip creation of history service.'
|
|
563
|
+
},
|
|
564
|
+
'preferSiriTemperature': {
|
|
565
|
+
'title': 'Prefer Siri temperature changes',
|
|
566
|
+
'type': 'boolean',
|
|
567
|
+
'description': 'Prefers temperature changes when the Auto (state=3) mode is sent simultaneously. Default: false.'
|
|
563
568
|
}
|
|
564
569
|
},
|
|
565
570
|
'layout': [
|
|
566
571
|
'name',
|
|
567
572
|
'debug',
|
|
568
573
|
'disableHistoryService',
|
|
574
|
+
'preferSiriTemperature',
|
|
569
575
|
'homes.name',
|
|
570
576
|
'homes.polling',
|
|
571
577
|
'homes.temperatureUnit',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homebridge-plugins/homebridge-tado",
|
|
3
|
-
"version": "8.6.0
|
|
3
|
+
"version": "8.6.0",
|
|
4
4
|
"description": "Homebridge plugin for controlling tado° devices.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"@babel/core": "7.28.5",
|
|
44
44
|
"@babel/eslint-parser": "7.28.5",
|
|
45
45
|
"@babel/eslint-plugin": "7.27.1",
|
|
46
|
-
"@eslint/js": "^9.
|
|
47
|
-
"eslint": "^9.
|
|
46
|
+
"@eslint/js": "^9.39.0",
|
|
47
|
+
"eslint": "^9.39.0",
|
|
48
48
|
"eslint-config-prettier": "^10.1.8",
|
|
49
49
|
"eslint-plugin-import": "^2.32.0",
|
|
50
50
|
"eslint-plugin-prettier": "^5.5.4",
|
|
51
|
-
"globals": "^16.
|
|
51
|
+
"globals": "^16.5.0",
|
|
52
52
|
"prettier": "^3.6.2"
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import Logger from '../helper/logger.js';
|
|
2
2
|
import moment from 'moment';
|
|
3
|
+
import { TadoUpdateBuffer } from '../helper/update-buffer.js'
|
|
3
4
|
|
|
4
5
|
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
5
6
|
|
|
6
7
|
export default class HeaterCoolerAccessory {
|
|
7
|
-
constructor(api, accessory, accessories, tado, deviceHandler, FakeGatoHistoryService) {
|
|
8
|
+
constructor(api, accessory, accessories, tado, deviceHandler, preferSiriTemperature, FakeGatoHistoryService) {
|
|
8
9
|
this.api = api;
|
|
9
10
|
this.accessory = accessory;
|
|
10
11
|
this.accessories = accessories;
|
|
@@ -15,6 +16,10 @@ export default class HeaterCoolerAccessory {
|
|
|
15
16
|
|
|
16
17
|
this.autoDelayTimeout = null;
|
|
17
18
|
|
|
19
|
+
this.updateBuffer = new TadoUpdateBuffer((target, value) => {
|
|
20
|
+
return this.deviceHandler.setStates(this.accessory, this.accessories, target, value);
|
|
21
|
+
}, preferSiriTemperature);
|
|
22
|
+
|
|
18
23
|
this.getService();
|
|
19
24
|
}
|
|
20
25
|
|
|
@@ -258,16 +263,7 @@ export default class HeaterCoolerAccessory {
|
|
|
258
263
|
|
|
259
264
|
service
|
|
260
265
|
.getCharacteristic(this.api.hap.Characteristic.Active)
|
|
261
|
-
.onSet(
|
|
262
|
-
if (this.waitForEndValue) {
|
|
263
|
-
clearTimeout(this.waitForEndValue);
|
|
264
|
-
this.waitForEndValue = null;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
this.waitForEndValue = setTimeout(() => {
|
|
268
|
-
this.deviceHandler.setStates(this.accessory, this.accessories, 'State', value);
|
|
269
|
-
}, 500);
|
|
270
|
-
})
|
|
266
|
+
.onSet(value => this.updateBuffer.setState(value))
|
|
271
267
|
.on(
|
|
272
268
|
'change',
|
|
273
269
|
this.deviceHandler.changedStates.bind(this, this.accessory, this.historyService, this.accessory.displayName)
|
|
@@ -282,16 +278,7 @@ export default class HeaterCoolerAccessory {
|
|
|
282
278
|
|
|
283
279
|
service
|
|
284
280
|
.getCharacteristic(this.api.hap.Characteristic.HeatingThresholdTemperature)
|
|
285
|
-
.onSet(
|
|
286
|
-
if (this.waitForEndValue) {
|
|
287
|
-
clearTimeout(this.waitForEndValue);
|
|
288
|
-
this.waitForEndValue = null;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
this.waitForEndValue = setTimeout(() => {
|
|
292
|
-
this.deviceHandler.setStates(this.accessory, this.accessories, 'Temperature', value);
|
|
293
|
-
}, 250);
|
|
294
|
-
})
|
|
281
|
+
.onSet(value => this.updateBuffer.setTemperature(value))
|
|
295
282
|
.on(
|
|
296
283
|
'change',
|
|
297
284
|
this.deviceHandler.changedStates.bind(this, this.accessory, this.historyService, this.accessory.displayName)
|
|
@@ -301,16 +288,7 @@ export default class HeaterCoolerAccessory {
|
|
|
301
288
|
if (this.accessory.context.config.type === 'AIR_CONDITIONING') {
|
|
302
289
|
service
|
|
303
290
|
.getCharacteristic(this.api.hap.Characteristic.CoolingThresholdTemperature)
|
|
304
|
-
.onSet(
|
|
305
|
-
if (this.waitForEndValue) {
|
|
306
|
-
clearTimeout(this.waitForEndValue);
|
|
307
|
-
this.waitForEndValue = null;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
this.waitForEndValue = setTimeout(() => {
|
|
311
|
-
this.deviceHandler.setStates(this.accessory, this.accessories, 'Temperature', value);
|
|
312
|
-
}, 250);
|
|
313
|
-
})
|
|
291
|
+
.onSet(value => this.updateBuffer.setTemperature(value))
|
|
314
292
|
.on(
|
|
315
293
|
'change',
|
|
316
294
|
this.deviceHandler.changedStates.bind(this, this.accessory, this.historyService, this.accessory.displayName)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import Logger from '../helper/logger.js';
|
|
2
2
|
import moment from 'moment';
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
|
+
import { TadoUpdateBuffer } from '../helper/update-buffer.js'
|
|
4
5
|
|
|
5
6
|
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
6
7
|
|
|
7
8
|
export default class ThermostatAccessory {
|
|
8
|
-
constructor(api, accessory, accessories, tado, deviceHandler, FakeGatoHistoryService) {
|
|
9
|
+
constructor(api, accessory, accessories, tado, deviceHandler, preferSiriTemperature, FakeGatoHistoryService) {
|
|
9
10
|
this.api = api;
|
|
10
11
|
this.accessory = accessory;
|
|
11
12
|
this.accessories = accessories;
|
|
@@ -16,6 +17,10 @@ export default class ThermostatAccessory {
|
|
|
16
17
|
|
|
17
18
|
this.autoDelayTimeout = null;
|
|
18
19
|
|
|
20
|
+
this.updateBuffer = new TadoUpdateBuffer((target, value) => {
|
|
21
|
+
return this.deviceHandler.setStates(this.accessory, this.accessories, target, value);
|
|
22
|
+
}, preferSiriTemperature);
|
|
23
|
+
|
|
19
24
|
this.getService();
|
|
20
25
|
}
|
|
21
26
|
|
|
@@ -192,20 +197,8 @@ export default class ThermostatAccessory {
|
|
|
192
197
|
.getCharacteristic(this.api.hap.Characteristic.TemperatureDisplayUnits)
|
|
193
198
|
.onSet(this.changeUnit.bind(this, service));
|
|
194
199
|
|
|
195
|
-
service.getCharacteristic(this.api.hap.Characteristic.TargetHeatingCoolingState)
|
|
196
|
-
|
|
197
|
-
clearTimeout(this.waitForEndValue);
|
|
198
|
-
this.waitForEndValue = null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
this.waitForEndValue = setTimeout(() => {
|
|
202
|
-
if (this.settingTemperature) {
|
|
203
|
-
this.settingTemperature = false;
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
this.deviceHandler.setStates(this.accessory, this.accessories, 'State', value);
|
|
207
|
-
}, 500);
|
|
208
|
-
});
|
|
200
|
+
service.getCharacteristic(this.api.hap.Characteristic.TargetHeatingCoolingState)
|
|
201
|
+
.onSet(value => this.updateBuffer.setState(value));
|
|
209
202
|
|
|
210
203
|
service
|
|
211
204
|
.getCharacteristic(this.api.hap.Characteristic.CurrentTemperature)
|
|
@@ -216,12 +209,7 @@ export default class ThermostatAccessory {
|
|
|
216
209
|
|
|
217
210
|
service
|
|
218
211
|
.getCharacteristic(this.api.hap.Characteristic.TargetTemperature)
|
|
219
|
-
.onSet(
|
|
220
|
-
this.settingTemperature = true;
|
|
221
|
-
const targetState = service.getCharacteristic(this.api.hap.Characteristic.TargetHeatingCoolingState).value;
|
|
222
|
-
if (targetState) this.deviceHandler.setStates(this.accessory, this.accessories, 'Temperature', value);
|
|
223
|
-
setTimeout(() => this.settingTemperature = false, 1000);
|
|
224
|
-
})
|
|
212
|
+
.onSet(value => this.updateBuffer.setTemperature(value))
|
|
225
213
|
.on(
|
|
226
214
|
'change',
|
|
227
215
|
this.deviceHandler.changedStates.bind(this, this.accessory, this.historyService, this.accessory.displayName)
|
package/src/helper/handler.js
CHANGED
|
@@ -2,6 +2,7 @@ import Logger from '../helper/logger.js';
|
|
|
2
2
|
import moment from 'moment';
|
|
3
3
|
import { writeFile } from 'fs/promises';
|
|
4
4
|
import { join } from "path";
|
|
5
|
+
import { randomUUID } from 'crypto';
|
|
5
6
|
|
|
6
7
|
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
7
8
|
const helpers = {};
|
|
@@ -10,7 +11,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
10
11
|
//init helper variables for current home scope
|
|
11
12
|
if (!helpers[config.homeId]) {
|
|
12
13
|
helpers[config.homeId] = {
|
|
13
|
-
|
|
14
|
+
activeSettingStateRuns: {},
|
|
14
15
|
tasksInitialized: false,
|
|
15
16
|
lastGetStates: 0,
|
|
16
17
|
lastPersistZoneStates: 0,
|
|
@@ -24,11 +25,16 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
function settingStates() {
|
|
29
|
+
return Object.keys(helpers[config.homeId].activeSettingStateRuns).length > 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
async function setStates(accessory, accs, target, value) {
|
|
28
33
|
let zoneUpdated = false;
|
|
29
34
|
accessories = accs.filter((acc) => acc && acc.context.config.homeName === config.homeName);
|
|
35
|
+
const runId = randomUUID();
|
|
30
36
|
try {
|
|
31
|
-
helpers[config.homeId].
|
|
37
|
+
helpers[config.homeId].activeSettingStateRuns[runId] = true;
|
|
32
38
|
value = typeof value === 'number' ? parseFloat(value.toFixed(2)) : value;
|
|
33
39
|
Logger.info(target + ': ' + value, accessory.displayName);
|
|
34
40
|
|
|
@@ -560,11 +566,11 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
560
566
|
} catch (err) {
|
|
561
567
|
errorHandler(err);
|
|
562
568
|
} finally {
|
|
563
|
-
helpers[config.homeId].
|
|
569
|
+
delete helpers[config.homeId].activeSettingStateRuns[runId];
|
|
564
570
|
//update zones to ensure correct state in Apple Home
|
|
565
571
|
const timeSinceLastGetStates = helpers[config.homeId].lastGetStates === 0 ? 0 : (Date.now() - helpers[config.homeId].lastGetStates);
|
|
566
572
|
const statesIntervalTimeLeft = helpers[config.homeId].statesIntervalTime - timeSinceLastGetStates;
|
|
567
|
-
if (zoneUpdated && statesIntervalTimeLeft > (10 * 1000)) await updateZones();
|
|
573
|
+
if (!settingStates() && zoneUpdated && statesIntervalTimeLeft > (10 * 1000)) await updateZones();
|
|
568
574
|
}
|
|
569
575
|
}
|
|
570
576
|
|
|
@@ -797,7 +803,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
797
803
|
}
|
|
798
804
|
|
|
799
805
|
async function updateMe() {
|
|
800
|
-
if (
|
|
806
|
+
if (settingStates()) return;
|
|
801
807
|
|
|
802
808
|
Logger.debug('Polling User Info...', config.homeName);
|
|
803
809
|
|
|
@@ -809,7 +815,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
809
815
|
}
|
|
810
816
|
|
|
811
817
|
async function updateHome() {
|
|
812
|
-
if (
|
|
818
|
+
if (settingStates()) return;
|
|
813
819
|
|
|
814
820
|
Logger.debug('Polling Home Info...', config.homeName);
|
|
815
821
|
|
|
@@ -859,7 +865,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
859
865
|
}
|
|
860
866
|
|
|
861
867
|
async function _updateZones() {
|
|
862
|
-
if (
|
|
868
|
+
if (settingStates()) return;
|
|
863
869
|
|
|
864
870
|
Logger.debug('Polling Zones...', config.homeName);
|
|
865
871
|
|
|
@@ -910,8 +916,11 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
910
916
|
});
|
|
911
917
|
}
|
|
912
918
|
|
|
913
|
-
|
|
914
|
-
|
|
919
|
+
let zoneStates = {};
|
|
920
|
+
if (config.zones?.length) {
|
|
921
|
+
zoneStates = (await tado.getZoneStates(config.homeId))["zoneStates"] ?? {};
|
|
922
|
+
void persistZoneStates(config.homeId, zoneStates);
|
|
923
|
+
}
|
|
915
924
|
|
|
916
925
|
for (const zone of config.zones) {
|
|
917
926
|
const zoneState = zoneStates[zone.id.toString()];
|
|
@@ -1322,7 +1331,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1322
1331
|
}
|
|
1323
1332
|
|
|
1324
1333
|
async function updateMobileDevices() {
|
|
1325
|
-
if (
|
|
1334
|
+
if (settingStates()) return;
|
|
1326
1335
|
|
|
1327
1336
|
Logger.debug('Polling MobileDevices...', config.homeName);
|
|
1328
1337
|
|
|
@@ -1376,7 +1385,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1376
1385
|
}
|
|
1377
1386
|
|
|
1378
1387
|
async function updateWeather() {
|
|
1379
|
-
if (
|
|
1388
|
+
if (settingStates()) return;
|
|
1380
1389
|
|
|
1381
1390
|
const weatherTemperatureAccessory = accessories.filter(
|
|
1382
1391
|
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Weather'
|
|
@@ -1431,7 +1440,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1431
1440
|
}
|
|
1432
1441
|
|
|
1433
1442
|
async function updatePresence() {
|
|
1434
|
-
if (
|
|
1443
|
+
if (settingStates()) return;
|
|
1435
1444
|
|
|
1436
1445
|
const presenceLockAccessory = accessories.filter(
|
|
1437
1446
|
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Presence Lock'
|
|
@@ -1476,7 +1485,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1476
1485
|
}
|
|
1477
1486
|
|
|
1478
1487
|
async function updateRunningTime() {
|
|
1479
|
-
if (
|
|
1488
|
+
if (settingStates()) return;
|
|
1480
1489
|
|
|
1481
1490
|
const centralSwitchAccessory = accessories.filter(
|
|
1482
1491
|
(acc) => acc && acc.displayName === acc.context.config.homeName + ' Central Switch'
|
|
@@ -1521,7 +1530,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1521
1530
|
}
|
|
1522
1531
|
|
|
1523
1532
|
async function updateDevices() {
|
|
1524
|
-
if (
|
|
1533
|
+
if (settingStates()) return;
|
|
1525
1534
|
|
|
1526
1535
|
Logger.debug('Polling Devices...', config.homeName);
|
|
1527
1536
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { hrtime } from "process";
|
|
2
|
+
import Logger from '../helper/logger.js';
|
|
3
|
+
|
|
4
|
+
export class TadoUpdateBuffer {
|
|
5
|
+
constructor(sendUpdateFn, preferSiriTemperature = false) {
|
|
6
|
+
this.preferSiriTemperature = !!preferSiriTemperature;
|
|
7
|
+
this.sendUpdateFn = sendUpdateFn;
|
|
8
|
+
this.delay = 400;
|
|
9
|
+
this.timer = null;
|
|
10
|
+
this.pendingState = null;
|
|
11
|
+
this.pendingTemperature = null;
|
|
12
|
+
this.lastUpdateTime = null;
|
|
13
|
+
this.lastTemperature = 20;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
setState(value) {
|
|
17
|
+
this.pendingState = value;
|
|
18
|
+
this._schedule();
|
|
19
|
+
Logger.debug("[TadoUpdateBuffer] setState", value, hrtime.bigint());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setTemperature(value) {
|
|
23
|
+
this.pendingTemperature = value;
|
|
24
|
+
this._schedule();
|
|
25
|
+
Logger.debug("[TadoUpdateBuffer] setTemperature", value, hrtime.bigint());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_schedule() {
|
|
29
|
+
if (this.timer) clearTimeout(this.timer);
|
|
30
|
+
this.timer = setTimeout(() => this._apply(), this.delay);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_apply() {
|
|
34
|
+
this.timer = null;
|
|
35
|
+
const state = this.pendingState;
|
|
36
|
+
const temp = this.pendingTemperature;
|
|
37
|
+
const tempSet = temp !== null && temp !== undefined && temp >= 5;
|
|
38
|
+
this.pendingState = null;
|
|
39
|
+
this.pendingTemperature = null;
|
|
40
|
+
if (tempSet) this.lastTemperature = temp;
|
|
41
|
+
|
|
42
|
+
//Siri temperature heuristic
|
|
43
|
+
if (this.preferSiriTemperature && state === 3 && tempSet) {
|
|
44
|
+
if (temp === 5) {
|
|
45
|
+
//set auto mode on
|
|
46
|
+
Logger.debug("[TadoUpdateBuffer] preferSiriTemperature active but temp=5 -> treat as auto mode");
|
|
47
|
+
return this.sendUpdateFn("State", 3);
|
|
48
|
+
}
|
|
49
|
+
//set temperature
|
|
50
|
+
Logger.debug("[TadoUpdateBuffer] Siri temperature change detected -> ignore state 3, apply temperature only");
|
|
51
|
+
return this.sendUpdateFn("Temperature", temp);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//default behaviour
|
|
55
|
+
if (state === 0) {
|
|
56
|
+
//set heating off
|
|
57
|
+
return this.sendUpdateFn("State", 0);
|
|
58
|
+
} else if (state === 3) {
|
|
59
|
+
//set auto mode on
|
|
60
|
+
return this.sendUpdateFn("State", 3);
|
|
61
|
+
} else if (tempSet) {
|
|
62
|
+
//set heating on with temperature provided
|
|
63
|
+
return this.sendUpdateFn("Temperature", temp);
|
|
64
|
+
} else if (state === 1) {
|
|
65
|
+
//heating on without temperature provided
|
|
66
|
+
return this.sendUpdateFn("Temperature", this.lastTemperature);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/platform.js
CHANGED
|
@@ -237,12 +237,12 @@ class TadoPlatform {
|
|
|
237
237
|
|
|
238
238
|
switch (device.subtype) {
|
|
239
239
|
case 'zone-thermostat':
|
|
240
|
-
new ThermostatAccessory(this.api, accessory, this.accessories, tado, deviceHandler, FakeGatoHistoryService);
|
|
240
|
+
new ThermostatAccessory(this.api, accessory, this.accessories, tado, deviceHandler, this.config.preferSiriTemperature, FakeGatoHistoryService);
|
|
241
241
|
break;
|
|
242
242
|
case 'zone-heatercooler':
|
|
243
243
|
case 'zone-heatercooler-boiler':
|
|
244
244
|
case 'zone-heatercooler-ac':
|
|
245
|
-
new HeaterCoolerAccessory(this.api, accessory, this.accessories, tado, deviceHandler, FakeGatoHistoryService);
|
|
245
|
+
new HeaterCoolerAccessory(this.api, accessory, this.accessories, tado, deviceHandler, this.config.preferSiriTemperature, FakeGatoHistoryService);
|
|
246
246
|
break;
|
|
247
247
|
case 'zone-switch':
|
|
248
248
|
case 'zone-window-switch':
|