@homebridge-plugins/homebridge-tado 8.7.0 → 8.7.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 +13 -6
- package/README.md +27 -11
- package/config.schema.json +20 -20
- package/homebridge-ui/public/index.html +10 -10
- package/homebridge-ui/public/js/main.js +3 -3
- package/homebridge-ui/public/js/schema.js +19 -19
- package/package.json +4 -4
- package/src/helper/handler.js +3 -3
- package/src/platform.js +1 -1
- package/src/tado/tado-api.js +37 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v8.7.2 - 2025-11-08
|
|
4
|
+
- Add notice about tado X compatibility to documentation (#179)
|
|
5
|
+
- Use consistent spelling for tado and Apple Home across the entire plugin
|
|
6
|
+
|
|
7
|
+
## v8.7.1 - 2025-11-04
|
|
8
|
+
- Add debug logs for API responses (#179)
|
|
9
|
+
|
|
3
10
|
## v8.7.0 — 2025-11-03
|
|
4
11
|
- Update documentation and add new config parameters with explanations (#176, #182)
|
|
5
12
|
- Update `example-config.json` (#182)
|
|
@@ -23,7 +30,7 @@
|
|
|
23
30
|
- Optimized task queue to prevent overlapping operations and API calls
|
|
24
31
|
- Fixed multi-home polling and individual API handling (#176)
|
|
25
32
|
- Added enhanced debug logs for zone updates and API interactions
|
|
26
|
-
- Note: This update resets the
|
|
33
|
+
- Note: This update resets the tado API counter for the current day
|
|
27
34
|
- Apologies for the unexpected behavior introduced in v8.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)
|
|
28
35
|
|
|
29
36
|
## v8.5.0 - 2025-10-27
|
|
@@ -64,14 +71,14 @@
|
|
|
64
71
|
- Update form-data due to vulnerability
|
|
65
72
|
|
|
66
73
|
## v8.0.1 - 2025-06-12
|
|
67
|
-
-
|
|
74
|
+
- tado-API: Verify that refresh token is not empty before saving to file
|
|
68
75
|
|
|
69
76
|
## v8.0.0 - 2025-06-09
|
|
70
77
|
- Add support for tado AC devices
|
|
71
78
|
|
|
72
79
|
## v7.6.0 - 2025-04-24
|
|
73
80
|
- Fixed crash on telegram event (#164)
|
|
74
|
-
- Rewrote
|
|
81
|
+
- Rewrote tado-Platform as ES6 class
|
|
75
82
|
- Updated dependencies
|
|
76
83
|
|
|
77
84
|
## v7.5.3 - 2025-03-27
|
|
@@ -105,7 +112,7 @@
|
|
|
105
112
|
- Added full support for Homebridge v2.0 (it is also shown as compatible when using Homebridge v1.X)
|
|
106
113
|
- Updated all dependencies to their latest version
|
|
107
114
|
- Added new authentication workflow to config-ui
|
|
108
|
-
- Fixed an issue that caused
|
|
115
|
+
- Fixed an issue that caused Apple Home from pairing with the bridge (Error: Accessory out of compliance)
|
|
109
116
|
- Fixed an issue where the plugin crashed after startup due to a type error after upgrading got
|
|
110
117
|
- Removed AirQuality feature [homebridge-tado-platform/issues/152#issuecomment-2708942491](https://github.com/seydx/homebridge-tado-platform/issues/152#issuecomment-2708942491)
|
|
111
118
|
- Fixed an issue where the current temperature has been set to the target temperature after changing the target temperature through Apple Home
|
|
@@ -173,7 +180,7 @@
|
|
|
173
180
|
|
|
174
181
|
**Note:**
|
|
175
182
|
|
|
176
|
-
Hot water devices show 0° when they are first started in
|
|
183
|
+
Hot water devices show 0° when they are first started in Apple Home if they are in "OFF" mode. The reason for this is that in "OFF" mode there is no temperature value in the API. When you turn on the device, the plugin saves the value for the further use case.
|
|
177
184
|
|
|
178
185
|
**IMPORTANT:**
|
|
179
186
|
|
|
@@ -191,7 +198,7 @@ If you previously used your "HOT_WATER" device/zone as a faucet, then disable th
|
|
|
191
198
|
- Refactored code
|
|
192
199
|
- Multiple tado accounts
|
|
193
200
|
- Possibility to control multiple homes
|
|
194
|
-
- Customizable temperature unit via
|
|
201
|
+
- Customizable temperature unit via Apple Home
|
|
195
202
|
- Customizable Modes (AUTO | HEAT | COOL | OFF) or (ON | OFF)
|
|
196
203
|
- Deactivatable battery indicator (support for old gen thermostats)
|
|
197
204
|
- Customizable zone termination, separate for each zone
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
This plugin has originally been created by [Seydx](https://github.com/seydx/). Donate to him: [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NP4T3KASWQLD8). It is currently maintained by [smart7324](https://buymeacoffee.com/smart7324).
|
|
16
16
|
|
|
17
|
-
**Homebridge
|
|
17
|
+
**Homebridge tado** is possibly the biggest homebridge plugin for tado devices.
|
|
18
18
|
|
|
19
19
|
**Multiple home Support**
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ The plugin even offers support for multiple houses. So it is finally possible to
|
|
|
22
22
|
|
|
23
23
|
**Full Apple Home Support**
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Most features that tado offers are fully supported and can be displayed through this plugin in Apple Home. The thermostat buttons themselves have custom characteristics. Full hot water support, weather temperature, solar intensity, weather, tado quick actions, child lock, presence lock, Air Conditioning and much more awaits you with this plugin!
|
|
26
26
|
|
|
27
27
|
## Installation
|
|
28
28
|
|
|
@@ -57,6 +57,7 @@ After [Homebridge](https://github.com/nfarina/homebridge) has been installed:
|
|
|
57
57
|
- [History Service](#history-service)
|
|
58
58
|
- [Telegram](#telegram)
|
|
59
59
|
- [Supported Clients](#supported-clients)
|
|
60
|
+
- [tado X Compatibility](#tado-x-compatibility)
|
|
60
61
|
- [Contributing](#contributing)
|
|
61
62
|
- [Troubleshooting](#troubleshooting)
|
|
62
63
|
|
|
@@ -108,17 +109,17 @@ See [Example Config](https://github.com/homebridge-plugins/homebridge-tado/blob/
|
|
|
108
109
|
|
|
109
110
|
## Authentication
|
|
110
111
|
|
|
111
|
-
This plugin implements the
|
|
112
|
+
This plugin implements the tado-supported authentication using the [Device Code Grant Flow](https://support.tado.com/en/articles/8565472-how-do-i-authenticate-to-access-the-rest-api).
|
|
112
113
|
|
|
113
|
-
During the initial setup, the plugin will prompt you to open a specific URL in your web browser. You will then be asked to log in with your
|
|
114
|
+
During the initial setup, the plugin will prompt you to open a specific URL in your web browser. You will then be asked to log in with your tado account and grant access to the new device. Once this process has been completed successfully, the plugin automatically handles all subsequent authentication tasks, including secure token storage, token refresh, and renewal. This ensures continuous access to the tado API v2 without further user interaction.
|
|
114
115
|
|
|
115
116
|
### Custom API Configuration
|
|
116
117
|
|
|
117
|
-
For advanced use cases, the plugin allows overriding the default
|
|
118
|
+
For advanced use cases, the plugin allows overriding the default tado API endpoint by specifying a custom API URL through the `tadoApiUrl` parameter within each home configuration.
|
|
118
119
|
|
|
119
120
|
Additionally, it is possible to disable the built-in authentication flow entirely by setting the `skipAuth` parameter to `true`. When enabled, all API requests will be sent without an authentication token.
|
|
120
121
|
|
|
121
|
-
**Important:** Use `skipAuth` only if you are certain that your setup does not require token-based authentication. Improper use may result in limited or failed communication with the
|
|
122
|
+
**Important:** Use `skipAuth` only if you are certain that your setup does not require token-based authentication. Improper use may result in limited or failed communication with the tado API.
|
|
122
123
|
|
|
123
124
|
For further details and configuration examples, refer to the related discussion:
|
|
124
125
|
[homebridge-tado issue #176 – Authentication Options](https://github.com/homebridge-plugins/homebridge-tado/issues/176#issuecomment-3419839118)
|
|
@@ -231,7 +232,7 @@ Each zone in the config.json with `"type": "AIR_CONDITIONING"` is exposed to App
|
|
|
231
232
|
- Elgato EVE history feature (FakeGato)
|
|
232
233
|
|
|
233
234
|
**AC-Specific Features**
|
|
234
|
-
Air conditioning zones support both heating and cooling modes with dedicated cooling threshold temperature control. Fan speed and swing controls are handled through the
|
|
235
|
+
Air conditioning zones support both heating and cooling modes with dedicated cooling threshold temperature control. Fan speed and swing controls are handled through the tado app, while Apple Home integration focuses on temperature and mode control for optimal compatibility. Note that the RotationSpeed characteristic is not supported for AC units.
|
|
235
236
|
|
|
236
237
|
**Mode / Mode Timer**
|
|
237
238
|
`mode` for the commands to be sent with. can be 'MANUAL' for manual control until ended by the user, 'AUTO' for manual control until next schedule change in tado° app OR 'TIMER' for manual control until timer ends. `modeTimer` for the `MANUAL` mode in minutes.
|
|
@@ -412,7 +413,7 @@ This option changes how combined state + temperature updates are interpreted:
|
|
|
412
413
|
## Extras
|
|
413
414
|
|
|
414
415
|
### Central Switch
|
|
415
|
-
Shows a switch accessory with additional switches in Apple Home which mimics the "Boost" and "Turnoff" switch from
|
|
416
|
+
Shows a switch accessory with additional switches in Apple Home which mimics the "Boost" and "Turnoff" switch from tado. It also shows the Heater Running information as a custom characteristic for the month (in hours) and it shows also how many thermostats are in auto, manual or off mode. Its also possible to show a dummy switch withiun the central switch for eg. automation purposes.
|
|
416
417
|
|
|
417
418
|
```
|
|
418
419
|
"homes": [
|
|
@@ -432,7 +433,7 @@ Shows a switch accessory with additional switches in Apple Home which mimics the
|
|
|
432
433
|
```
|
|
433
434
|
|
|
434
435
|
#### Boost Switch
|
|
435
|
-
Shows a switch accessory in Apple Home (added to central switch) which mimics the "Boost" switch from
|
|
436
|
+
Shows a switch accessory in Apple Home (added to central switch) which mimics the "Boost" switch from tado and switches all heaters to max temperature.
|
|
436
437
|
_Note: Central Switch needs to be truned on._
|
|
437
438
|
|
|
438
439
|
```
|
|
@@ -453,7 +454,7 @@ _Note: Central Switch needs to be truned on._
|
|
|
453
454
|
```
|
|
454
455
|
|
|
455
456
|
#### Shedule Switch
|
|
456
|
-
Shows a switch accessory in Apple Home (added to central switch) which mimics the "Shedule" switch from
|
|
457
|
+
Shows a switch accessory in Apple Home (added to central switch) which mimics the "Shedule" switch from tado and switches all heaters to their default shedule
|
|
457
458
|
_Note: Central Switch needs to be truned on._
|
|
458
459
|
|
|
459
460
|
```
|
|
@@ -474,7 +475,7 @@ _Note: Central Switch needs to be truned on._
|
|
|
474
475
|
```
|
|
475
476
|
|
|
476
477
|
#### Turnoff Switch
|
|
477
|
-
Shows a switch accessory in Apple Home (added to central switch) which mimics the "Turn Off" switch from
|
|
478
|
+
Shows a switch accessory in Apple Home (added to central switch) which mimics the "Turn Off" switch from tado and switches all heaters off
|
|
478
479
|
_Note: Central Switch needs to be truned on._
|
|
479
480
|
|
|
480
481
|
```
|
|
@@ -631,6 +632,21 @@ This plugin has been verified to work with the latest versions of:
|
|
|
631
632
|
* 3rd party apps like Elgato Eve
|
|
632
633
|
* Homebridge
|
|
633
634
|
|
|
635
|
+
## tado X Compatibility
|
|
636
|
+
|
|
637
|
+
**tado X devices are not supported.**
|
|
638
|
+
|
|
639
|
+
The new generation of tado X devices uses a completely different API endpoint (`https://hops.tado.com`) and communication methods that are not compatible with the existing tado API (`https://my.tado.com/api/v2`) used by this plugin.
|
|
640
|
+
|
|
641
|
+
Supporting tado X devices would require a full integration of the new API, which is currently **not planned** due to the significant development effort involved.
|
|
642
|
+
|
|
643
|
+
For more technical details about the tado X API, see:
|
|
644
|
+
- [tado X – API differences (wiki)](https://github.com/kritsel/tado-openapispec-v2/wiki/tado-X)
|
|
645
|
+
- [Swagger documentation](https://kritsel.github.io/tado-openapispec-v2/swagger.html#/)
|
|
646
|
+
- [Postman collection & additional resources](https://github.com/gedhi/tadox-postman-collection?tab=readme-ov-file#additional-resources)
|
|
647
|
+
|
|
648
|
+
If you are using tado X devices, please note that this plugin will **not be able to control or display** those devices at the moment.
|
|
649
|
+
|
|
634
650
|
## Contributing
|
|
635
651
|
|
|
636
652
|
You can contribute to this homebridge plugin in following ways:
|
package/config.schema.json
CHANGED
|
@@ -26,30 +26,30 @@
|
|
|
26
26
|
"properties": {
|
|
27
27
|
"id": {
|
|
28
28
|
"title": "Home ID",
|
|
29
|
-
"description": "The id of your Home. Has to be the exact same id as in the web app of
|
|
29
|
+
"description": "The id of your Home. Has to be the exact same id as in the web app of tado.",
|
|
30
30
|
"type": "integer"
|
|
31
31
|
},
|
|
32
32
|
"name": {
|
|
33
33
|
"title": "Home Name",
|
|
34
|
-
"description": "The name of your Home. Has to be the exact same name as in the web app of
|
|
34
|
+
"description": "The name of your Home. Has to be the exact same name as in the web app of tado.",
|
|
35
35
|
"type": "string",
|
|
36
36
|
"required": true
|
|
37
37
|
},
|
|
38
38
|
"username": {
|
|
39
39
|
"title": "Username",
|
|
40
40
|
"type": "string",
|
|
41
|
-
"description": "The user name that you use for the app and the web app of
|
|
41
|
+
"description": "The user name that you use for the app and the web app of tado.",
|
|
42
42
|
"required": true
|
|
43
43
|
},
|
|
44
44
|
"tadoApiUrl": {
|
|
45
|
-
"title": "
|
|
45
|
+
"title": "tado API URL",
|
|
46
46
|
"type": "string",
|
|
47
|
-
"description": "Optional: Use a custom tado
|
|
47
|
+
"description": "Optional: Use a custom tado API url (e.g. http://localhost:8080)."
|
|
48
48
|
},
|
|
49
49
|
"skipAuth": {
|
|
50
50
|
"title": "Skip Authentication",
|
|
51
51
|
"type": "boolean",
|
|
52
|
-
"description": "Optional: Skip authentication for tado
|
|
52
|
+
"description": "Optional: Skip authentication for tado API."
|
|
53
53
|
},
|
|
54
54
|
"polling": {
|
|
55
55
|
"title": "Polling",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"temperatureUnit": {
|
|
62
62
|
"title": "Temperature Unit",
|
|
63
|
-
"description": "Temperature Unit for accessories in
|
|
63
|
+
"description": "Temperature Unit for accessories in Apple Home exposed by this plugin.",
|
|
64
64
|
"type": "string",
|
|
65
65
|
"default": "CELSIUS",
|
|
66
66
|
"oneOf": [
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
"active": {
|
|
135
135
|
"title": "Active",
|
|
136
136
|
"type": "boolean",
|
|
137
|
-
"description": "If enabled, a new motion/occupancy sensor for this user will be exposed to
|
|
137
|
+
"description": "If enabled, a new motion/occupancy sensor for this user will be exposed to Apple Home."
|
|
138
138
|
},
|
|
139
139
|
"name": {
|
|
140
140
|
"title": "Name",
|
|
@@ -176,12 +176,12 @@
|
|
|
176
176
|
"temperatureSensor": {
|
|
177
177
|
"title": "Temperature Sensor",
|
|
178
178
|
"type": "boolean",
|
|
179
|
-
"description": "If enabled, a temperature sensor for the weather will be exposed to
|
|
179
|
+
"description": "If enabled, a temperature sensor for the weather will be exposed to Apple Home."
|
|
180
180
|
},
|
|
181
181
|
"solarIntensity": {
|
|
182
182
|
"title": "Solar Intensity",
|
|
183
183
|
"type": "boolean",
|
|
184
|
-
"description": "If enabled, a lightbulb accessory for the solar intensity will be exposed to
|
|
184
|
+
"description": "If enabled, a lightbulb accessory for the solar intensity will be exposed to Apple Home."
|
|
185
185
|
},
|
|
186
186
|
"accTypeSolarIntensity": {
|
|
187
187
|
"title": "Solar Intensity Accessory Type",
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
"centralSwitch": {
|
|
215
215
|
"title": "Central Switch",
|
|
216
216
|
"type": "boolean",
|
|
217
|
-
"description": "If enabled, a switch will be exposed to
|
|
217
|
+
"description": "If enabled, a switch will be exposed to Apple Home to enable/disable all thermostats/boiler at once."
|
|
218
218
|
},
|
|
219
219
|
"runningInformation": {
|
|
220
220
|
"title": "Heat Running Information",
|
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
"presenceLock": {
|
|
228
228
|
"title": "Presence Lock",
|
|
229
229
|
"type": "boolean",
|
|
230
|
-
"description": "If enabled, a Home/Away switch will be exposed to
|
|
230
|
+
"description": "If enabled, a Home/Away switch will be exposed to Apple Home."
|
|
231
231
|
},
|
|
232
232
|
"accTypePresenceLock": {
|
|
233
233
|
"title": "Presence Lock Accessory Type",
|
|
@@ -289,7 +289,7 @@
|
|
|
289
289
|
"active": {
|
|
290
290
|
"title": "Active",
|
|
291
291
|
"type": "boolean",
|
|
292
|
-
"description": "If enabled, a child lock switch for this device will be exposed to
|
|
292
|
+
"description": "If enabled, a child lock switch for this device will be exposed to Apple Home."
|
|
293
293
|
},
|
|
294
294
|
"name": {
|
|
295
295
|
"title": "Name",
|
|
@@ -300,7 +300,7 @@
|
|
|
300
300
|
"serialNumber": {
|
|
301
301
|
"title": "Device Serial Number",
|
|
302
302
|
"type": "string",
|
|
303
|
-
"description": "Enter the serial number of the device from which you want to display a 'Child Lock Switch' in
|
|
303
|
+
"description": "Enter the serial number of the device from which you want to display a 'Child Lock Switch' in Apple Home. (only for devices with child lock support)",
|
|
304
304
|
"required": true,
|
|
305
305
|
"condition": {
|
|
306
306
|
"functionBody": "try { return model.homes[arrayIndices[0]].extras.childLockSwitches[arrayIndices[1]].active } catch(e){ return false }"
|
|
@@ -320,12 +320,12 @@
|
|
|
320
320
|
"active": {
|
|
321
321
|
"title": "Active",
|
|
322
322
|
"type": "boolean",
|
|
323
|
-
"description": "If enabled, a new thermostat/boiler switch will be exposed to
|
|
323
|
+
"description": "If enabled, a new thermostat/boiler switch will be exposed to Apple Home."
|
|
324
324
|
},
|
|
325
325
|
"id": {
|
|
326
326
|
"title": "ID",
|
|
327
327
|
"type": "integer",
|
|
328
|
-
"description": "Zone ID. Has to be the exact same id as in the web app of
|
|
328
|
+
"description": "Zone ID. Has to be the exact same id as in the web app of tado.",
|
|
329
329
|
"condition": {
|
|
330
330
|
"functionBody": "try { return model.homes[arrayIndices[0]].zones[arrayIndices[1]].active } catch(e){ return false }"
|
|
331
331
|
}
|
|
@@ -333,7 +333,7 @@
|
|
|
333
333
|
"name": {
|
|
334
334
|
"title": "Name",
|
|
335
335
|
"type": "string",
|
|
336
|
-
"description": "Zone Name. Has to be the exact same name as in the web app of
|
|
336
|
+
"description": "Zone Name. Has to be the exact same name as in the web app of tado.",
|
|
337
337
|
"required": true
|
|
338
338
|
},
|
|
339
339
|
"delaySwitch": {
|
|
@@ -349,12 +349,12 @@
|
|
|
349
349
|
"openWindowSensor": {
|
|
350
350
|
"title": "Open Window Sensor",
|
|
351
351
|
"type": "boolean",
|
|
352
|
-
"description": "If enabled, additional window contact accessory for each zone will be exposed to
|
|
352
|
+
"description": "If enabled, additional window contact accessory for each zone will be exposed to Apple Home which triggers if tado detects an open window."
|
|
353
353
|
},
|
|
354
354
|
"openWindowSwitch": {
|
|
355
355
|
"title": "Open Window Switch",
|
|
356
356
|
"type": "boolean",
|
|
357
|
-
"description": "If enabled, additional window switch accessory for each zone will be exposed to
|
|
357
|
+
"description": "If enabled, additional window switch accessory for each zone will be exposed to Apple Home to trigger and enable/disable open window."
|
|
358
358
|
},
|
|
359
359
|
"separateTemperature": {
|
|
360
360
|
"title": "Separate Temperature Sensors",
|
|
@@ -439,7 +439,7 @@
|
|
|
439
439
|
"condition": {
|
|
440
440
|
"functionBody": "try { return model.homes[arrayIndices[0]].zones[arrayIndices[1]].type === 'HEATING' } catch(e){ return false }"
|
|
441
441
|
},
|
|
442
|
-
"description": "If enabled, the battery indicator will be removed from
|
|
442
|
+
"description": "If enabled, the battery indicator will be removed from Apple Home."
|
|
443
443
|
},
|
|
444
444
|
"type": {
|
|
445
445
|
"title": "Type",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
<button type="submit" class="btn btn-elegant oldConfig" style="position: absolute;">
|
|
39
39
|
<i class="fas fa-cogs" aria-hidden="true"></i>
|
|
40
40
|
</button>
|
|
41
|
-
<img src="images/tado_logo.png" alt="
|
|
41
|
+
<img src="images/tado_logo.png" alt="tado meets Homebridge" width="150px" class="center-it tadoLogo">
|
|
42
42
|
<h3 class="text-center"><span style="font-size: 20px">Welcome to</span> <br> <b style="font-weight: 800;">Homebridge
|
|
43
|
-
|
|
44
|
-
<h6 class="text-center">Homebridge plugin for
|
|
43
|
+
tado</b></h3>
|
|
44
|
+
<h6 class="text-center">Homebridge plugin for tado devices</h6>
|
|
45
45
|
<div class="container mt-4" style="max-width: 360px;">
|
|
46
46
|
<div class="form-row">
|
|
47
47
|
<div class="col-10">
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
<button type="submit" class="btn btn-elegant oldConfig" style="position: absolute;">
|
|
72
72
|
<i class="fas fa-cogs" aria-hidden="true"></i>
|
|
73
73
|
</button>
|
|
74
|
-
<img src="images/tado_logo.png" alt="
|
|
75
|
-
<h3 class="text-center">Welcome to <br> <b style="font-weight: 800;">Homebridge
|
|
76
|
-
<h6 class="text-center">Homebridge plugin for
|
|
74
|
+
<img src="images/tado_logo.png" alt="tado meets Homebridge" width="150px" class="center-it tadoLogo">
|
|
75
|
+
<h3 class="text-center">Welcome to <br> <b style="font-weight: 800;">Homebridge tado</b></h3>
|
|
76
|
+
<h6 class="text-center">Homebridge plugin for tado devices</h6>
|
|
77
77
|
<button id="start" type="submit" class="btn btn-primary center-it mt-3">Start</button>
|
|
78
78
|
</div>
|
|
79
79
|
|
|
@@ -82,14 +82,14 @@
|
|
|
82
82
|
<button type="submit" class="btn btn-elegant back" style="position: absolute;">
|
|
83
83
|
<i class="fa fa-arrow-left" aria-hidden="true"></i>
|
|
84
84
|
</button>
|
|
85
|
-
<img src="images/tado_logo.png" alt="
|
|
86
|
-
<h6 class="text-center">To start the process, we need to authenticate with the
|
|
85
|
+
<img src="images/tado_logo.png" alt="tado meets Homebridge" width="150px" class="center-it mt-4 tadoLogo">
|
|
86
|
+
<h6 class="text-center">To start the process, we need to authenticate with the tado account of the home you want to
|
|
87
87
|
add to Homebridge.<br>Please fill out all marked fields:</h6>
|
|
88
88
|
<div class="container mt-5">
|
|
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" placeholder="your-tado@mail.com">
|
|
92
|
-
<label for="homeTadoApiUrl">
|
|
92
|
+
<label for="homeTadoApiUrl">tado API URL (optional)</label>
|
|
93
93
|
<input type="text" class="form-control" id="homeTadoApiUrl">
|
|
94
94
|
<label for="homeSkipAuth">Skip Authentication (optional)</label>
|
|
95
95
|
<input type="checkbox" class="form-checkbox" id="homeSkipAuth">
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
|
|
101
101
|
<!-- fetchDevices -->
|
|
102
102
|
<div id="fetchDevices" style="display:none;">
|
|
103
|
-
<img src="images/tado_logo.png" alt="
|
|
103
|
+
<img src="images/tado_logo.png" alt="tado meets Homebridge" width="150px" class="center-it mt-2 tadoLogo">
|
|
104
104
|
<h6 class="text-center">Devices are being searched.<br>Please follow the instructions below and be patient for a
|
|
105
105
|
moment.</h6>
|
|
106
106
|
<h6 class="text-center" id="authenticationInstructions" style="display:none;font-weight:bold;"></h6>
|
|
@@ -705,7 +705,7 @@ async function fetchDevices(auth, refresh, resync) {
|
|
|
705
705
|
|
|
706
706
|
await TIMEOUT(2000);
|
|
707
707
|
|
|
708
|
-
homebridge.toast.info('Search for homes in the config to remove that were not found in the
|
|
708
|
+
homebridge.toast.info('Search for homes in the config to remove that were not found in the API...', 'Info');
|
|
709
709
|
|
|
710
710
|
let removedHomes = 0;
|
|
711
711
|
|
|
@@ -1072,13 +1072,13 @@ async function fetchDevices(auth, refresh, resync) {
|
|
|
1072
1072
|
|
|
1073
1073
|
}
|
|
1074
1074
|
|
|
1075
|
-
homebridge.toast.info('Looking for homes which are found in the
|
|
1075
|
+
homebridge.toast.info('Looking for homes which are found in the API but are not configured...', 'Info');
|
|
1076
1076
|
|
|
1077
1077
|
await TIMEOUT(2000);
|
|
1078
1078
|
|
|
1079
1079
|
let addedHomes = 0;
|
|
1080
1080
|
|
|
1081
|
-
//add new homes from
|
|
1081
|
+
//add new homes from API that doesnt exist in config
|
|
1082
1082
|
for (const foundHome of availableHomesInApis) {
|
|
1083
1083
|
|
|
1084
1084
|
let found = false;
|
|
@@ -19,30 +19,30 @@ const schema = {
|
|
|
19
19
|
'properties': {
|
|
20
20
|
'id': {
|
|
21
21
|
'title': 'Home ID',
|
|
22
|
-
'description': 'The id of your Home. Has to be the exact same id as in the web app of
|
|
22
|
+
'description': 'The id of your Home. Has to be the exact same id as in the web app of tado.',
|
|
23
23
|
'type': 'integer'
|
|
24
24
|
},
|
|
25
25
|
'name': {
|
|
26
26
|
'title': 'Home Name',
|
|
27
|
-
'description': 'The name of your Home. Has to be the exact same name as in the web app of
|
|
27
|
+
'description': 'The name of your Home. Has to be the exact same name as in the web app of tado.',
|
|
28
28
|
'type': 'string',
|
|
29
29
|
'required': true
|
|
30
30
|
},
|
|
31
31
|
'username': {
|
|
32
32
|
'title': 'Username',
|
|
33
33
|
'type': 'string',
|
|
34
|
-
'description': 'The user name that you use for the app and the web app of
|
|
34
|
+
'description': 'The user name that you use for the app and the web app of tado.',
|
|
35
35
|
'required': true
|
|
36
36
|
},
|
|
37
37
|
'tadoApiUrl': {
|
|
38
38
|
'title': 'Tado API URL',
|
|
39
39
|
'type': 'string',
|
|
40
|
-
'description': 'Optional: Use a custom tado
|
|
40
|
+
'description': 'Optional: Use a custom tado API url (e.g. http://localhost:8080).'
|
|
41
41
|
},
|
|
42
42
|
'skipAuth': {
|
|
43
43
|
'title': 'Skip Authentication',
|
|
44
44
|
'type': 'boolean',
|
|
45
|
-
'description': 'Optional: Skip authentication for tado
|
|
45
|
+
'description': 'Optional: Skip authentication for tado API.'
|
|
46
46
|
},
|
|
47
47
|
'polling': {
|
|
48
48
|
'title': 'Polling',
|
|
@@ -53,7 +53,7 @@ const schema = {
|
|
|
53
53
|
},
|
|
54
54
|
'temperatureUnit': {
|
|
55
55
|
'title': 'Temperature Unit',
|
|
56
|
-
'description': 'Temperature Unit for accessories in
|
|
56
|
+
'description': 'Temperature Unit for accessories in Apple Home exposed by this plugin.',
|
|
57
57
|
'type': 'string',
|
|
58
58
|
'default': 'CELSIUS',
|
|
59
59
|
'oneOf': [
|
|
@@ -127,7 +127,7 @@ const schema = {
|
|
|
127
127
|
'active': {
|
|
128
128
|
'title': 'Active',
|
|
129
129
|
'type': 'boolean',
|
|
130
|
-
'description': 'If enabled, a new motion/occupancy sensor for this user will be exposed to
|
|
130
|
+
'description': 'If enabled, a new motion/occupancy sensor for this user will be exposed to Apple Home.'
|
|
131
131
|
},
|
|
132
132
|
'name': {
|
|
133
133
|
'title': 'Name',
|
|
@@ -169,12 +169,12 @@ const schema = {
|
|
|
169
169
|
'temperatureSensor': {
|
|
170
170
|
'title': 'Temperature Sensor',
|
|
171
171
|
'type': 'boolean',
|
|
172
|
-
'description': 'If enabled, a temperature sensor for the weather will be exposed to
|
|
172
|
+
'description': 'If enabled, a temperature sensor for the weather will be exposed to Apple Home.'
|
|
173
173
|
},
|
|
174
174
|
'solarIntensity': {
|
|
175
175
|
'title': 'Solar Intensity',
|
|
176
176
|
'type': 'boolean',
|
|
177
|
-
'description': 'If enabled, a lightbulb accessory for the solar intensity will be exposed to
|
|
177
|
+
'description': 'If enabled, a lightbulb accessory for the solar intensity will be exposed to Apple Home.'
|
|
178
178
|
},
|
|
179
179
|
'accTypeSolarIntensity': {
|
|
180
180
|
'title': 'Solar Intensity Accessory Type',
|
|
@@ -207,7 +207,7 @@ const schema = {
|
|
|
207
207
|
'centralSwitch': {
|
|
208
208
|
'title': 'Central Switch',
|
|
209
209
|
'type': 'boolean',
|
|
210
|
-
'description': 'If enabled, a switch will be exposed to
|
|
210
|
+
'description': 'If enabled, a switch will be exposed to Apple Home to enable/disable all thermostats/boiler at once.'
|
|
211
211
|
},
|
|
212
212
|
'runningInformation': {
|
|
213
213
|
'title': 'Heat Running Information',
|
|
@@ -220,7 +220,7 @@ const schema = {
|
|
|
220
220
|
'presenceLock': {
|
|
221
221
|
'title': 'Presence Lock',
|
|
222
222
|
'type': 'boolean',
|
|
223
|
-
'description': 'If enabled, a Home/Away switch will be exposed to
|
|
223
|
+
'description': 'If enabled, a Home/Away switch will be exposed to Apple Home.'
|
|
224
224
|
},
|
|
225
225
|
'accTypePresenceLock': {
|
|
226
226
|
'title': 'Presence Lock Accessory Type',
|
|
@@ -282,7 +282,7 @@ const schema = {
|
|
|
282
282
|
'active': {
|
|
283
283
|
'title': 'Active',
|
|
284
284
|
'type': 'boolean',
|
|
285
|
-
'description': 'If enabled, a child lock switch for this device will be exposed to
|
|
285
|
+
'description': 'If enabled, a child lock switch for this device will be exposed to Apple Home.'
|
|
286
286
|
},
|
|
287
287
|
'name': {
|
|
288
288
|
'title': 'Name',
|
|
@@ -293,7 +293,7 @@ const schema = {
|
|
|
293
293
|
'serialNumber': {
|
|
294
294
|
'title': 'Device Serial Number',
|
|
295
295
|
'type': 'string',
|
|
296
|
-
'description': 'Enter the serial number of the device from which you want to display a \'Child Lock Switch\' in
|
|
296
|
+
'description': 'Enter the serial number of the device from which you want to display a \'Child Lock Switch\' in Apple Home. (only for devices with child lock support)',
|
|
297
297
|
'required': true,
|
|
298
298
|
'condition': {
|
|
299
299
|
'functionBody': 'try { return model.homes.extras.childLockSwitches[arrayIndices[0]].active } catch(e){ return false }'
|
|
@@ -313,12 +313,12 @@ const schema = {
|
|
|
313
313
|
'active': {
|
|
314
314
|
'title': 'Active',
|
|
315
315
|
'type': 'boolean',
|
|
316
|
-
'description': 'If enabled, a new thermostat/boiler switch will be exposed to
|
|
316
|
+
'description': 'If enabled, a new thermostat/boiler switch will be exposed to Apple Home.'
|
|
317
317
|
},
|
|
318
318
|
'id': {
|
|
319
319
|
'title': 'ID',
|
|
320
320
|
'type': 'integer',
|
|
321
|
-
'description': 'Zone ID. Has to be the exact same id as in the web app of
|
|
321
|
+
'description': 'Zone ID. Has to be the exact same id as in the web app of tado.',
|
|
322
322
|
'condition': {
|
|
323
323
|
'functionBody': 'try { return model.homes.zones[arrayIndices[0]].active } catch(e){ return false }'
|
|
324
324
|
}
|
|
@@ -326,7 +326,7 @@ const schema = {
|
|
|
326
326
|
'name': {
|
|
327
327
|
'title': 'Name',
|
|
328
328
|
'type': 'string',
|
|
329
|
-
'description': 'Zone Name. Has to be the exact same name as in the web app of
|
|
329
|
+
'description': 'Zone Name. Has to be the exact same name as in the web app of tado.',
|
|
330
330
|
'required': true
|
|
331
331
|
},
|
|
332
332
|
'delaySwitch': {
|
|
@@ -342,12 +342,12 @@ const schema = {
|
|
|
342
342
|
'openWindowSensor': {
|
|
343
343
|
'title': 'Open Window Sensor',
|
|
344
344
|
'type': 'boolean',
|
|
345
|
-
'description': 'If enabled, additional window contact accessory for each zone will be exposed to
|
|
345
|
+
'description': 'If enabled, additional window contact accessory for each zone will be exposed to Apple Home which triggers if tado detects an open window.'
|
|
346
346
|
},
|
|
347
347
|
'openWindowSwitch': {
|
|
348
348
|
'title': 'Open Window Switch',
|
|
349
349
|
'type': 'boolean',
|
|
350
|
-
'description': 'If enabled, additional window switch accessory for each zone will be exposed to
|
|
350
|
+
'description': 'If enabled, additional window switch accessory for each zone will be exposed to Apple Home to trigger and enable/disable open window.'
|
|
351
351
|
},
|
|
352
352
|
'separateTemperature': {
|
|
353
353
|
'title': 'Separate Temperature Sensors',
|
|
@@ -432,7 +432,7 @@ const schema = {
|
|
|
432
432
|
'condition': {
|
|
433
433
|
'functionBody': 'try { return model.homes.zones[arrayIndices[0]].type === \'HEATING\' } catch(e){ return false }'
|
|
434
434
|
},
|
|
435
|
-
'description': 'If enabled, the battery indicator will be removed from
|
|
435
|
+
'description': 'If enabled, the battery indicator will be removed from Apple Home.'
|
|
436
436
|
},
|
|
437
437
|
'type': {
|
|
438
438
|
'title': 'Type',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homebridge-plugins/homebridge-tado",
|
|
3
|
-
"version": "8.7.
|
|
3
|
+
"version": "8.7.2",
|
|
4
4
|
"description": "Homebridge plugin for controlling tado° devices.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,15 +36,15 @@
|
|
|
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.3",
|
|
40
40
|
"moment": "^2.30.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
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.39.
|
|
47
|
-
"eslint": "^9.39.
|
|
46
|
+
"@eslint/js": "^9.39.1",
|
|
47
|
+
"eslint": "^9.39.1",
|
|
48
48
|
"eslint-config-prettier": "^10.1.8",
|
|
49
49
|
"eslint-plugin-import": "^2.32.0",
|
|
50
50
|
"eslint-plugin-prettier": "^5.5.4",
|
package/src/helper/handler.js
CHANGED
|
@@ -214,7 +214,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
214
214
|
// Use AC-specific overlay for AIR_CONDITIONING zones
|
|
215
215
|
if (accessory.context.config.type === 'AIR_CONDITIONING') {
|
|
216
216
|
|
|
217
|
-
// Map
|
|
217
|
+
// Map Apple Home target state to tado AC mode
|
|
218
218
|
let acMode = 'COOL'; // Default to COOL
|
|
219
219
|
|
|
220
220
|
// Get current target state from HeaterCooler service for proper mode detection
|
|
@@ -222,7 +222,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
222
222
|
if (heaterCoolerService) {
|
|
223
223
|
let targetState = heaterCoolerService.getCharacteristic(api.hap.Characteristic.TargetHeaterCoolerState).value;
|
|
224
224
|
|
|
225
|
-
// Map
|
|
225
|
+
// Map Apple Home target states to tado AC modes
|
|
226
226
|
// 1 = Heat, 2 = Cool, 3 = Auto
|
|
227
227
|
switch (targetState) {
|
|
228
228
|
case 1:
|
|
@@ -1092,7 +1092,7 @@ export default (api, accessories, config, tado, telegram) => {
|
|
|
1092
1092
|
const acMode = zoneState.setting.mode || 'COOL';
|
|
1093
1093
|
tempEqual = currentTemp && targetTemp ? Math.abs(currentTemp - targetTemp) < 0.5 : false;
|
|
1094
1094
|
|
|
1095
|
-
// Map AC modes to
|
|
1095
|
+
// Map AC modes to Apple Home states
|
|
1096
1096
|
switch (acMode.toUpperCase()) {
|
|
1097
1097
|
case 'HEAT':
|
|
1098
1098
|
targetState = 1; // Heating
|
package/src/platform.js
CHANGED
package/src/tado/tado-api.js
CHANGED
|
@@ -42,7 +42,7 @@ export default class Tado {
|
|
|
42
42
|
this._counter = persistedCounterData?.counter ?? 0;
|
|
43
43
|
this._counterTimestamp = persistedCounterData?.counterTimestamp ?? new Date().toISOString();
|
|
44
44
|
this._checkCounterMidnightReset();
|
|
45
|
-
//wait some seconds to catch recent
|
|
45
|
+
//wait some seconds to catch recent API calls
|
|
46
46
|
setTimeout(() => {
|
|
47
47
|
void this._logCounter();
|
|
48
48
|
setInterval(() => void this._logCounter(), 60 * 60 * 1000);
|
|
@@ -59,7 +59,7 @@ export default class Tado {
|
|
|
59
59
|
if (data) return JSON.parse(data);
|
|
60
60
|
} catch (error) {
|
|
61
61
|
//no persisted counter data => ignore
|
|
62
|
-
Logger.debug(`Failed to read tado
|
|
62
|
+
Logger.debug(`Failed to read tado API file for user ${this.hashedUsername}: ${error.message || error}`);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -82,7 +82,7 @@ export default class Tado {
|
|
|
82
82
|
this._counter++;
|
|
83
83
|
this._counterTimestamp = new Date().toISOString();
|
|
84
84
|
} catch (error) {
|
|
85
|
-
Logger.warn(`Failed to increase tado
|
|
85
|
+
Logger.warn(`Failed to increase tado API counter: ${error.message || error}`);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -100,16 +100,16 @@ export default class Tado {
|
|
|
100
100
|
data.counterData = await this._getCounterData();
|
|
101
101
|
await writeFile(join(this.storagePath, `tado-api-${this.hashedUsername}.json`), JSON.stringify(data, null, 2), "utf-8");
|
|
102
102
|
} catch (error) {
|
|
103
|
-
Logger.error(`Error while updating the tado
|
|
103
|
+
Logger.error(`Error while updating the tado API file for user ${this.hashedUsername}: ${error.message || error}`);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
async _logCounter() {
|
|
108
108
|
try {
|
|
109
109
|
const counter = (await this._getCounterData()).counter;
|
|
110
|
-
Logger.info(`
|
|
110
|
+
Logger.info(`tado API counter: ${counter.toLocaleString('en-US')}`);
|
|
111
111
|
} catch (error) {
|
|
112
|
-
Logger.warn(`Failed to get
|
|
112
|
+
Logger.warn(`Failed to get tado API counter: ${error.message || error}`);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -256,30 +256,15 @@ export default class Tado {
|
|
|
256
256
|
|
|
257
257
|
async apiCall(path, method = 'GET', data = {}, params = {}, tado_url_dif) {
|
|
258
258
|
const access_token = this.skipAuth ? undefined : await this.getToken();
|
|
259
|
+
const url = `${tado_url_dif || this.tadoApiUrl}${path}`;
|
|
259
260
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
Logger.debug('Using ' + tadoLink, this.name);
|
|
263
|
-
|
|
264
|
-
Logger.debug(
|
|
265
|
-
'API request ' +
|
|
266
|
-
method +
|
|
267
|
-
' ' +
|
|
268
|
-
path +
|
|
269
|
-
' ' +
|
|
270
|
-
(data && Object.keys(data).length ? JSON.stringify(data) + ' <pending>' : '<pending>'),
|
|
271
|
-
this.name
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
let config = {
|
|
275
|
-
method: method,
|
|
261
|
+
const config = {
|
|
262
|
+
method,
|
|
276
263
|
responseType: 'json',
|
|
277
|
-
headers: access_token ?
|
|
278
|
-
Authorization:
|
|
279
|
-
|
|
280
|
-
timeout: {
|
|
281
|
-
request: 30000
|
|
282
|
-
},
|
|
264
|
+
headers: access_token ?
|
|
265
|
+
{ Authorization: `Bearer ${access_token}` } :
|
|
266
|
+
undefined,
|
|
267
|
+
timeout: { request: 30000 },
|
|
283
268
|
retry: {
|
|
284
269
|
limit: 2,
|
|
285
270
|
statusCodes: [408, 429, 503, 504],
|
|
@@ -294,12 +279,34 @@ export default class Tado {
|
|
|
294
279
|
|
|
295
280
|
if (Object.keys(params).length) config.searchParams = params;
|
|
296
281
|
|
|
282
|
+
Logger.debug('API request start', {
|
|
283
|
+
name: this.name,
|
|
284
|
+
method,
|
|
285
|
+
url,
|
|
286
|
+
params: Object.keys(params).length ? params : undefined,
|
|
287
|
+
data: Object.keys(data).length ? data : undefined,
|
|
288
|
+
});
|
|
289
|
+
|
|
297
290
|
try {
|
|
298
|
-
const response = await got(
|
|
291
|
+
const response = await got(url, config);
|
|
299
292
|
await this._increaseCounter();
|
|
293
|
+
Logger.debug('API request success', {
|
|
294
|
+
name: this.name,
|
|
295
|
+
method,
|
|
296
|
+
url,
|
|
297
|
+
statusCode: response.statusCode,
|
|
298
|
+
body: response.body ?? 'empty response',
|
|
299
|
+
});
|
|
300
300
|
return response.body;
|
|
301
301
|
} catch (error) {
|
|
302
|
-
Logger.error(
|
|
302
|
+
Logger.error('API request failed', {
|
|
303
|
+
name: this.name,
|
|
304
|
+
method,
|
|
305
|
+
url,
|
|
306
|
+
message: error.message,
|
|
307
|
+
statusCode: error.response?.statusCode,
|
|
308
|
+
body: error.response?.body,
|
|
309
|
+
});
|
|
303
310
|
throw error;
|
|
304
311
|
}
|
|
305
312
|
}
|