@thecloudseeker/homebridge-skoda 0.1.3 → 0.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +42 -26
  3. package/config.schema.json +148 -69
  4. package/dist/accessory/services/battery.d.ts +21 -0
  5. package/dist/accessory/services/battery.js +38 -1
  6. package/dist/accessory/services/battery.js.map +1 -1
  7. package/dist/accessory/services/body.d.ts +5 -6
  8. package/dist/accessory/services/body.js +45 -8
  9. package/dist/accessory/services/body.js.map +1 -1
  10. package/dist/accessory/services/charging.js +31 -13
  11. package/dist/accessory/services/charging.js.map +1 -1
  12. package/dist/accessory/services/climate.js +51 -23
  13. package/dist/accessory/services/climate.js.map +1 -1
  14. package/dist/accessory/services/common.d.ts +10 -5
  15. package/dist/accessory/services/common.js +17 -8
  16. package/dist/accessory/services/common.js.map +1 -1
  17. package/dist/accessory/vehicleAccessory.d.ts +11 -2
  18. package/dist/accessory/vehicleAccessory.js +54 -5
  19. package/dist/accessory/vehicleAccessory.js.map +1 -1
  20. package/dist/api/client.d.ts +2 -2
  21. package/dist/api/client.js +8 -6
  22. package/dist/api/client.js.map +1 -1
  23. package/dist/api/rateBudget.d.ts +3 -3
  24. package/dist/api/rateBudget.js +1 -1
  25. package/dist/config.d.ts +10 -4
  26. package/dist/config.js +24 -8
  27. package/dist/config.js.map +1 -1
  28. package/dist/platform.js +19 -25
  29. package/dist/platform.js.map +1 -1
  30. package/dist/vehicle/mapping.d.ts +12 -0
  31. package/dist/vehicle/mapping.js +42 -0
  32. package/dist/vehicle/mapping.js.map +1 -1
  33. package/dist/vehicle/snapshot.js +47 -0
  34. package/dist/vehicle/snapshot.js.map +1 -1
  35. package/dist/vehicle/vehicleController.d.ts +30 -10
  36. package/dist/vehicle/vehicleController.js +92 -30
  37. package/dist/vehicle/vehicleController.js.map +1 -1
  38. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ - **Window heating** as an optional read-only contact sensor, open while the front or rear window heating is on (`features.windowHeating`). The API has no command for it.
6
+ - **Charging plug lock** as an optional contact sensor, closed while the cable is locked (`features.plugLock`).
7
+ - **Clearer settings in the Homebridge UI.** Settings are grouped by topic, each next to the feature it belongs to, and numbers are entered in text fields instead of sliders.
8
+ - **Removed `commandToken` and `commandRefreshDelaySeconds`.** A second API key brings no extra requests, because the rate limit applies per vehicle, and the expected state after a command makes an extra refresh unnecessary. The plugin uses the API key for everything and warns once if the old options are still in the config.
9
+ - **The log lists what your vehicle supports** after the first update, including features that are supported but turned off, with the setting that turns them on.
10
+ - `airConditioningWithoutExternalPower` is a choice of "Keep the vehicle's setting", "Allow" (`"allow"`) and "Do not allow" (`"deny"`). Existing `true` / `false` values keep working.
11
+
12
+ ## 0.2.0
13
+
14
+ - **Commands cost one request.** HomeKit keeps the expected state until a regular update carries the vehicle's new report of that part, at most 15 minutes, instead of an extra refresh after every command. With the default polling interval, that leaves 8 instead of about 4 commands per hour. `commandRefreshDelaySeconds` now defaults to `0` (no refresh); set it to 15–600 to keep the old behaviour. If you set it explicitly, nothing changes for you.
15
+ - **Controls follow the vehicle more carefully.** A control is removed only when two updates report its operation as unsupported, so a single odd response no longer costs you its room and automations in the Home app. A command the vehicle rejects as unsupported (`operation-not-supported`) removes its control as well. Disabled functions (`operation-disabled`) keep their control.
16
+ - **Fixed:** services added to or removed from a vehicle that was already in HomeKit were only saved to Homebridge's accessory cache on a clean shutdown. They are now saved right away.
17
+ - **Fixed:** a request the vehicle declined (`429 vehicle-not-accepting-requests`) was not counted against the request budget. The API counts it.
18
+ - **Fuel level** for petrol, diesel, CNG and LPG vehicles, as a battery "Fuel" that cannot be charged, with low battery below `lowBatteryThreshold`. It is on by default, so existing combustion vehicles get the new service after the update. An optional humidity sensor "Fuel Level" mirrors it for automations, also for plug-in hybrids (`features.fuelLevelSensor`).
19
+ - **Lights** as an optional contact sensor, open while the lights are on (`features.lights`).
20
+ - The log shows at startup how many requests per hour polling uses and how many are left for commands.
21
+
3
22
  ## 0.1.3
4
23
 
5
24
  - The author link on npm points to [eikedreier.com](https://eikedreier.com/).
package/README.md CHANGED
@@ -17,12 +17,17 @@ air conditioning, auxiliary heating, "at home" detection, doors, windows and loc
17
17
  ## Features
18
18
 
19
19
  Each vehicle becomes one HomeKit accessory. A service is only created if it is enabled in the
20
- config **and** the vehicle reports that it supports it.
20
+ config **and** the vehicle reports that it supports it. For the controls, that is the list of
21
+ remote operations the API reports for the vehicle. If two updates report a control as no longer
22
+ supported, or the vehicle rejects a command as unsupported, the service is removed. A single odd
23
+ update or an unreachable API never removes services.
21
24
 
22
25
  | HomeKit service | Shows | Controls | Default |
23
26
  | ---------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------- | ------- |
24
27
  | Battery | State of charge, charging state, low battery below a threshold | – | on |
25
28
  | Humidity sensor "State of Charge" | State of charge in %, so you can build automations on it | – | off |
29
+ | Battery "Fuel" | Fuel level, low below the threshold (petrol, diesel, CNG, LPG) | – | on |
30
+ | Humidity sensor "Fuel Level" | Fuel level in %, so you can build automations on it | – | off |
26
31
  | Outlet "Charging" | _On_ = charging, _Outlet In Use_ = cable plugged in | Start / stop charging | on |
27
32
  | Lightbulb "Charge Limit" | Brightness = charging limit (50–100 % in steps of 10) | Set charging limit | on |
28
33
  | Heater Cooler "Climate" | Active, heating / cooling, target temperature | Start / stop air conditioning, set temperature | on |
@@ -31,6 +36,11 @@ config **and** the vehicle reports that it supports it.
31
36
  | Occupancy sensor "Home" | Parked within a radius around your home | – | on |
32
37
  | Contact sensors "Doors", "Windows", "Trunk", "Bonnet", "Sunroof" | Open / closed | – | on |
33
38
  | Contact sensor "Locked" | Closed = locked, open = unlocked | – (the API cannot lock or unlock) | on |
39
+ | Contact sensor "Lights" | Open = lights on | – | off |
40
+ | Contact sensor "Window Heating" | Open = front or rear window heating on | – (the API has no command for it) | off |
41
+ | Contact sensor "Plug Locked" | Closed = charging cable locked | – | off |
42
+
43
+ HomeKit has no services for range or mileage, so the plugin does not show them.
34
44
 
35
45
  What the API does **not** offer, and therefore this plugin neither: locking and unlocking, honking
36
46
  and flashing, waking the vehicle up, cabin temperature, and push updates.
@@ -52,8 +62,15 @@ and flashing, waking the vehicle up, cabin temperature, and push updates.
52
62
  therefore always shows the reported state: after you switch it on in HomeKit it returns to _off_
53
63
  once the vehicle has accepted the start (see the log), and it shows _on_ when the ventilation
54
64
  was started in the app.
65
+ - **Fuel.** HomeKit has no fuel service, so combustion vehicles show their fuel level as a battery
66
+ that cannot be charged. Low battery uses `lowBatteryThreshold`. Plug-in hybrids keep the
67
+ high-voltage battery in the battery service; their fuel level is available as the "Fuel Level"
68
+ humidity sensor.
55
69
  - **Lock.** Exposed as a read-only contact sensor rather than a lock, because the API cannot
56
70
  lock or unlock. A lock tile in the Home app would suggest otherwise.
71
+ - **Expected state after a command.** HomeKit shows the expected state right away and keeps it
72
+ until the vehicle reports that part again, at most 15 minutes. If the vehicle reports something
73
+ else, for example because charging could not start, HomeKit shows what the vehicle reports.
57
74
  - **Stale data.** If the last successful update is older than three polling intervals, the sensors
58
75
  show a fault (_StatusFault_). Battery, outlet, switches and the heater cooler have no fault
59
76
  indicator in HomeKit.
@@ -93,10 +110,6 @@ days before a key expires. Once it has expired, the plugin logs an error and ret
93
110
  (failed authentication does not count against the rate limit). Renew the key in the app and
94
111
  update the config.
95
112
 
96
- You may configure a second key for commands only (_Command API key_), for example to keep
97
- permissions apart. **A second key does not give you more requests**: the rate limit applies per
98
- vehicle, and both keys share it.
99
-
100
113
  ## Configuration
101
114
 
102
115
  The easiest way is the Homebridge UI. In `config.json` it looks like this:
@@ -117,22 +130,20 @@ The easiest way is the Homebridge UI. In `config.json` it looks like this:
117
130
  }
118
131
  ```
119
132
 
120
- | Option | Default | Description |
121
- | ------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
122
- | `name` | `Škoda <last 4 of VIN>` | Accessory name in HomeKit |
123
- | `vin` | – | Vehicle identification number (required) |
124
- | `token` | – | API key (required) |
125
- | `commandToken` | – | Separate API key for commands |
126
- | `spin` | – | S-PIN, only needed for the auxiliary heating |
127
- | `home.lat`, `home.lon` | – | Home coordinates for the "Home" sensor |
128
- | `home.radiusMeters` | `150` | Radius around home, at least 10 m |
129
- | `pollingIntervalMinutes` | `5` | Polling interval, at least 4 (see below) |
130
- | `commandRefreshDelaySeconds` | `90` | Delay before the refresh after a command (15–600) |
131
- | `lowBatteryThreshold` | `20` | Low battery below this state of charge (%) |
132
- | `climateTemperature` | `21` | Cabin temperature (°C) until the vehicle reports one |
133
- | `airConditioningWithoutExternalPower` | – | Allow air conditioning on battery; unset keeps the vehicle setting |
134
- | `auxiliaryHeatingDurationMinutes` | – | Auxiliary heating run time; unset keeps the vehicle setting |
135
- | `features.*` | see table above | `battery`, `stateOfChargeSensor`, `charging`, `chargingLimit`, `climate`, `auxiliaryHeating`, `activeVentilation`, `presence`, `doors`, `windows`, `trunk`, `bonnet`, `sunroof`, `lock` |
133
+ | Option | Default | Description |
134
+ | ------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
+ | `name` | `Škoda <last 4 of VIN>` | Accessory name in HomeKit |
136
+ | `vin` | – | Vehicle identification number (required) |
137
+ | `token` | – | API key (required) |
138
+ | `spin` | – | S-PIN, only needed for the auxiliary heating |
139
+ | `home.lat`, `home.lon` | – | Home coordinates for the "Home" sensor |
140
+ | `home.radiusMeters` | `150` | Radius around home, at least 10 m |
141
+ | `pollingIntervalMinutes` | `5` | Polling interval, at least 4 (see below) |
142
+ | `lowBatteryThreshold` | `20` | Low battery below this state of charge or fuel level (%) |
143
+ | `climateTemperature` | `21` | Cabin temperature (°C) until the vehicle reports one |
144
+ | `airConditioningWithoutExternalPower` | – | `"allow"` or `"deny"` air conditioning from the drive battery when not plugged in; unset keeps the vehicle setting |
145
+ | `auxiliaryHeatingDurationMinutes` | – | Auxiliary heating run time; unset keeps the vehicle setting |
146
+ | `features.*` | see table above | `battery`, `stateOfChargeSensor`, `fuelLevel`, `fuelLevelSensor`, `charging`, `chargingLimit`, `climate`, `auxiliaryHeating`, `activeVentilation`, `presence`, `doors`, `windows`, `trunk`, `bonnet`, `sunroof`, `lock`, `lights`, `windowHeating`, `plugLock` |
136
147
 
137
148
  ## Rate limit
138
149
 
@@ -144,11 +155,12 @@ The plugin is built around that limit:
144
155
 
145
156
  - **One request per update.** A single call returns all vehicle data.
146
157
  - **HomeKit never triggers requests.** Opening the Home app reads from the plugin's cache.
147
- - **Four requests per hour stay free** for commands and their follow-up refresh. That makes 4
148
- minutes the shortest polling interval; shorter values are raised with a warning. The default of
149
- 5 minutes uses 12 requests per hour.
150
- - **After a command**, HomeKit shows the expected state right away, and the plugin refreshes once
151
- after `commandRefreshDelaySeconds` to confirm it. Several commands in a row share one refresh.
158
+ - **Four requests per hour stay free** for commands. That makes 4 minutes the shortest polling
159
+ interval; shorter values are raised with a warning. The default of 5 minutes uses 12 requests
160
+ per hour and leaves 8 for commands. The log shows this split at startup.
161
+ - **A command costs one request.** The vehicle reports new state with a delay, so instead of an
162
+ extra refresh the plugin keeps the expected state until a regular update carries the vehicle's
163
+ new report.
152
164
  - **When the budget is used up**, commands are rejected ("No Response" in the Home app) instead of
153
165
  being sent, and polling waits until requests are available again.
154
166
  - **Errors back off.** On `429` and `503` the plugin waits for `Retry-After`. On other server and
@@ -175,6 +187,7 @@ suggests, and a command may take a minute or two to show up.
175
187
  | `rate limit budget exhausted` | Too many commands in the last hour; try again later |
176
188
  | `the vehicle declined the request` | The vehicle refused for now (e.g. low battery); try again later |
177
189
  | `this function is currently disabled for the vehicle` | Enable the service for the vehicle in the MyŠkoda app |
190
+ | `The vehicle does not support …` | The vehicle rejected the command; its control is removed |
178
191
 
179
192
  ## Development
180
193
 
@@ -188,6 +201,9 @@ npm run build
188
201
 
189
202
  The tests mock the API completely and contain no real keys or vehicle data.
190
203
 
204
+ Upstream changes worth following (the API, the Python library and the Home Assistant integration)
205
+ are listed in [MAINTAINING.md](MAINTAINING.md).
206
+
191
207
  ## License
192
208
 
193
209
  [BSD 3-Clause](LICENSE)
@@ -33,17 +33,12 @@
33
33
  "title": "API key",
34
34
  "type": "string",
35
35
  "required": true,
36
- "description": "API key from the MyŠkoda app. Used for polling, and for commands unless a command key is set."
37
- },
38
- "commandToken": {
39
- "title": "Command API key (optional)",
40
- "type": "string",
41
- "description": "Separate key for commands, e.g. with different permissions. Both keys share the vehicle's rate limit, so a second key does not allow more requests."
36
+ "description": "API key from the MyŠkoda app (Profile › Smart Home). Used for reading the vehicle and for commands."
42
37
  },
43
38
  "spin": {
44
39
  "title": "S-PIN (optional)",
45
40
  "type": "string",
46
- "description": "Security PIN of your MyŠkoda account. Only needed for the auxiliary heating; it is sent with each start and never logged."
41
+ "description": "Security PIN of your MyŠkoda account. Only needed to start the auxiliary heating; it is sent with each start and never logged."
47
42
  },
48
43
  "home": {
49
44
  "title": "Home location",
@@ -70,65 +65,83 @@
70
65
  }
71
66
  },
72
67
  "pollingIntervalMinutes": {
73
- "title": "Polling interval (minutes)",
68
+ "title": "Update interval (minutes)",
74
69
  "type": "integer",
75
70
  "minimum": 4,
76
71
  "maximum": 1440,
77
72
  "placeholder": 5,
78
- "description": "Minimum 4: 20 requests per hour minus 4 kept free for commands and their follow-up refresh."
79
- },
80
- "commandRefreshDelaySeconds": {
81
- "title": "Refresh after command (seconds)",
82
- "type": "integer",
83
- "minimum": 15,
84
- "maximum": 600,
85
- "placeholder": 90,
86
- "description": "Delay before the single follow-up refresh after a command."
73
+ "description": "How often the plugin fetches the vehicle's state. At least 4, default 5: the API allows 20 requests per hour, and 4 are kept free for commands."
87
74
  },
88
75
  "lowBatteryThreshold": {
89
- "title": "Low battery threshold (%)",
76
+ "title": "Low battery / fuel warning below (%)",
90
77
  "type": "integer",
91
78
  "minimum": 0,
92
79
  "maximum": 100,
93
- "placeholder": 20
80
+ "placeholder": 20,
81
+ "description": "HomeKit shows low battery when the battery or fuel level drops below this value. Default 20."
94
82
  },
95
83
  "climateTemperature": {
96
- "title": "Default cabin temperature (°C)",
84
+ "title": "Default temperature (°C)",
97
85
  "type": "number",
98
86
  "minimum": 16,
99
87
  "maximum": 29.5,
100
88
  "multipleOf": 0.5,
101
89
  "placeholder": 21,
102
- "description": "Used until the vehicle reports its own target temperature."
90
+ "description": "Used for the first start until the vehicle reports its own target temperature. 16-29.5 in steps of 0.5, default 21."
103
91
  },
104
92
  "airConditioningWithoutExternalPower": {
105
- "title": "Allow air conditioning on battery",
106
- "type": "boolean",
107
- "description": "Sent with each start. Leave unset to keep the vehicle's setting."
93
+ "title": "Air conditioning when not plugged in",
94
+ "type": "string",
95
+ "oneOf": [
96
+ {
97
+ "title": "Keep the vehicle's setting",
98
+ "enum": ["vehicle"]
99
+ },
100
+ {
101
+ "title": "Allow (uses the drive battery)",
102
+ "enum": ["allow"]
103
+ },
104
+ {
105
+ "title": "Do not allow",
106
+ "enum": ["deny"]
107
+ }
108
+ ],
109
+ "placeholder": "vehicle",
110
+ "description": "For electric vehicles: whether the air conditioning may run from the drive battery when no charging cable is connected."
108
111
  },
109
112
  "auxiliaryHeatingDurationMinutes": {
110
- "title": "Auxiliary heating duration (minutes)",
113
+ "title": "Auxiliary heating run time (minutes, optional)",
111
114
  "type": "integer",
112
115
  "minimum": 1,
113
116
  "maximum": 60,
114
- "description": "Leave empty to use the vehicle's setting."
117
+ "description": "1-60. Leave empty to use the run time set in the vehicle."
115
118
  },
116
119
  "features": {
117
120
  "title": "Features",
118
121
  "type": "object",
119
122
  "properties": {
120
123
  "battery": {
121
- "title": "Battery",
124
+ "title": "Battery (electric and plug-in hybrid)",
122
125
  "type": "boolean",
123
126
  "default": true
124
127
  },
125
128
  "stateOfChargeSensor": {
126
- "title": "State of charge as humidity sensor (for automations)",
129
+ "title": "State of charge as extra sensor, for automations",
130
+ "type": "boolean",
131
+ "default": false
132
+ },
133
+ "fuelLevel": {
134
+ "title": "Fuel level as battery (petrol, diesel, CNG, LPG)",
135
+ "type": "boolean",
136
+ "default": true
137
+ },
138
+ "fuelLevelSensor": {
139
+ "title": "Fuel level as extra sensor, for automations",
127
140
  "type": "boolean",
128
141
  "default": false
129
142
  },
130
143
  "charging": {
131
- "title": "Charging (outlet)",
144
+ "title": "Start / stop charging (outlet)",
132
145
  "type": "boolean",
133
146
  "default": true
134
147
  },
@@ -143,17 +156,17 @@
143
156
  "default": true
144
157
  },
145
158
  "auxiliaryHeating": {
146
- "title": "Auxiliary heating (needs S-PIN)",
159
+ "title": "Auxiliary heating (switch, needs S-PIN)",
147
160
  "type": "boolean",
148
161
  "default": true
149
162
  },
150
163
  "activeVentilation": {
151
- "title": "Active ventilation",
164
+ "title": "Ventilation (switch)",
152
165
  "type": "boolean",
153
166
  "default": false
154
167
  },
155
168
  "presence": {
156
- "title": "At home (needs home location)",
169
+ "title": "\"Home\" sensor (needs home location)",
157
170
  "type": "boolean",
158
171
  "default": true
159
172
  },
@@ -183,9 +196,24 @@
183
196
  "default": true
184
197
  },
185
198
  "lock": {
186
- "title": "Locked (read-only)",
199
+ "title": "Locked (sensor)",
187
200
  "type": "boolean",
188
201
  "default": true
202
+ },
203
+ "lights": {
204
+ "title": "Lights on (sensor)",
205
+ "type": "boolean",
206
+ "default": false
207
+ },
208
+ "windowHeating": {
209
+ "title": "Window heating on (sensor)",
210
+ "type": "boolean",
211
+ "default": false
212
+ },
213
+ "plugLock": {
214
+ "title": "Charging plug locked (sensor)",
215
+ "type": "boolean",
216
+ "default": false
189
217
  }
190
218
  }
191
219
  }
@@ -210,24 +238,85 @@
210
238
  "key": "vehicles[].token",
211
239
  "type": "password"
212
240
  },
241
+ {
242
+ "type": "help",
243
+ "helpvalue": "<p>The plugin detects what your vehicle supports and only creates those services in HomeKit, so you can keep the defaults below. Switches marked <em>sensor</em> or <em>for automations</em> are off by default. After the first update the Homebridge log lists everything your vehicle supports, including what is turned off.</p>"
244
+ },
245
+ {
246
+ "type": "fieldset",
247
+ "title": "Doors, windows and lock",
248
+ "expandable": true,
249
+ "expanded": false,
250
+ "items": [
251
+ "vehicles[].features.doors",
252
+ "vehicles[].features.windows",
253
+ "vehicles[].features.trunk",
254
+ "vehicles[].features.bonnet",
255
+ "vehicles[].features.sunroof",
256
+ "vehicles[].features.lock",
257
+ "vehicles[].features.lights"
258
+ ]
259
+ },
213
260
  {
214
261
  "type": "fieldset",
215
- "title": "Commands",
262
+ "title": "Battery and fuel",
216
263
  "expandable": true,
217
264
  "expanded": false,
218
265
  "items": [
266
+ "vehicles[].features.battery",
267
+ "vehicles[].features.stateOfChargeSensor",
268
+ "vehicles[].features.fuelLevel",
269
+ "vehicles[].features.fuelLevelSensor",
219
270
  {
220
- "key": "vehicles[].commandToken",
221
- "type": "password"
271
+ "key": "vehicles[].lowBatteryThreshold",
272
+ "type": "number"
273
+ }
274
+ ]
275
+ },
276
+ {
277
+ "type": "fieldset",
278
+ "title": "Charging",
279
+ "description": "Electric and plug-in hybrid vehicles.",
280
+ "expandable": true,
281
+ "expanded": false,
282
+ "items": [
283
+ "vehicles[].features.charging",
284
+ "vehicles[].features.chargingLimit",
285
+ "vehicles[].features.plugLock"
286
+ ]
287
+ },
288
+ {
289
+ "type": "fieldset",
290
+ "title": "Air conditioning and ventilation",
291
+ "expandable": true,
292
+ "expanded": false,
293
+ "items": [
294
+ "vehicles[].features.climate",
295
+ {
296
+ "key": "vehicles[].climateTemperature",
297
+ "type": "number"
222
298
  },
299
+ "vehicles[].airConditioningWithoutExternalPower",
300
+ "vehicles[].features.windowHeating",
301
+ "vehicles[].features.activeVentilation"
302
+ ]
303
+ },
304
+ {
305
+ "type": "fieldset",
306
+ "title": "Auxiliary heating",
307
+ "description": "Vehicles with a fuel-operated auxiliary heater.",
308
+ "expandable": true,
309
+ "expanded": false,
310
+ "items": [
311
+ "vehicles[].features.auxiliaryHeating",
223
312
  {
224
313
  "key": "vehicles[].spin",
225
314
  "type": "password"
226
315
  },
227
- "vehicles[].commandRefreshDelaySeconds",
228
- "vehicles[].climateTemperature",
229
- "vehicles[].airConditioningWithoutExternalPower",
230
- "vehicles[].auxiliaryHeatingDurationMinutes"
316
+ {
317
+ "key": "vehicles[].auxiliaryHeatingDurationMinutes",
318
+ "type": "number"
319
+ }
231
320
  ]
232
321
  },
233
322
  {
@@ -237,48 +326,38 @@
237
326
  "expandable": true,
238
327
  "expanded": false,
239
328
  "items": [
329
+ "vehicles[].features.presence",
240
330
  {
241
331
  "type": "flex",
242
332
  "flex-flow": "row wrap",
243
333
  "items": [
244
- "vehicles[].home.lat",
245
- "vehicles[].home.lon",
246
- "vehicles[].home.radiusMeters"
334
+ {
335
+ "key": "vehicles[].home.lat",
336
+ "type": "number"
337
+ },
338
+ {
339
+ "key": "vehicles[].home.lon",
340
+ "type": "number"
341
+ },
342
+ {
343
+ "key": "vehicles[].home.radiusMeters",
344
+ "type": "number"
345
+ }
247
346
  ]
248
347
  }
249
348
  ]
250
349
  },
251
350
  {
252
351
  "type": "fieldset",
253
- "title": "Polling",
254
- "expandable": true,
255
- "expanded": false,
256
- "items": [
257
- "vehicles[].pollingIntervalMinutes",
258
- "vehicles[].lowBatteryThreshold"
259
- ]
260
- },
261
- {
262
- "type": "fieldset",
263
- "title": "Features",
264
- "description": "Services are only created if the vehicle supports them.",
352
+ "title": "Updates",
353
+ "description": "The default fits most setups.",
265
354
  "expandable": true,
266
355
  "expanded": false,
267
356
  "items": [
268
- "vehicles[].features.battery",
269
- "vehicles[].features.stateOfChargeSensor",
270
- "vehicles[].features.charging",
271
- "vehicles[].features.chargingLimit",
272
- "vehicles[].features.climate",
273
- "vehicles[].features.auxiliaryHeating",
274
- "vehicles[].features.activeVentilation",
275
- "vehicles[].features.presence",
276
- "vehicles[].features.doors",
277
- "vehicles[].features.windows",
278
- "vehicles[].features.trunk",
279
- "vehicles[].features.bonnet",
280
- "vehicles[].features.sunroof",
281
- "vehicles[].features.lock"
357
+ {
358
+ "key": "vehicles[].pollingIntervalMinutes",
359
+ "type": "number"
360
+ }
282
361
  ]
283
362
  }
284
363
  ]
@@ -18,3 +18,24 @@ export declare class StateOfChargeSensorBinding extends ServiceBinding {
18
18
  protected get hasStatusFault(): boolean;
19
19
  protected render(_service: Service, vehicle: Vehicle): void;
20
20
  }
21
+ /**
22
+ * Battery service for combustion vehicles: the level is the fuel level, low below the configured
23
+ * threshold. HomeKit has no fuel service; hybrids show their high-voltage battery instead.
24
+ */
25
+ export declare class FuelBinding extends ServiceBinding {
26
+ readonly feature = "fuelLevel";
27
+ protected readonly serviceType: ServiceType;
28
+ protected readonly subtype = "fuel";
29
+ protected readonly displayName = "Fuel";
30
+ protected configure(service: Service): void;
31
+ protected render(_service: Service, vehicle: Vehicle): void;
32
+ }
33
+ /** Humidity sensor that mirrors the fuel level, because HomeKit cannot trigger on battery levels. */
34
+ export declare class FuelLevelSensorBinding extends ServiceBinding {
35
+ readonly feature = "fuelLevelSensor";
36
+ protected readonly serviceType: ServiceType;
37
+ protected readonly subtype = "fuel-level";
38
+ protected readonly displayName = "Fuel Level";
39
+ protected get hasStatusFault(): boolean;
40
+ protected render(_service: Service, vehicle: Vehicle): void;
41
+ }
@@ -1,4 +1,4 @@
1
- import { batteryValues, stateOfCharge } from "../../vehicle/mapping.js";
1
+ import { batteryValues, fuelLevel, fuelValues, stateOfCharge, } from "../../vehicle/mapping.js";
2
2
  import { ServiceBinding } from "./common.js";
3
3
  /** Battery: level, charging state and low battery below the configured threshold. */
4
4
  export class BatteryBinding extends ServiceBinding {
@@ -33,4 +33,41 @@ export class StateOfChargeSensorBinding extends ServiceBinding {
33
33
  this.set(this.ctx.hap.Characteristic.CurrentRelativeHumidity, stateOfCharge(vehicle));
34
34
  }
35
35
  }
36
+ /**
37
+ * Battery service for combustion vehicles: the level is the fuel level, low below the configured
38
+ * threshold. HomeKit has no fuel service; hybrids show their high-voltage battery instead.
39
+ */
40
+ export class FuelBinding extends ServiceBinding {
41
+ feature = "fuelLevel";
42
+ serviceType = this.ctx.hap.Service.Battery;
43
+ subtype = "fuel";
44
+ displayName = "Fuel";
45
+ configure(service) {
46
+ const { ChargingState } = this.ctx.hap.Characteristic;
47
+ service.updateCharacteristic(ChargingState, ChargingState.NOT_CHARGEABLE);
48
+ }
49
+ render(_service, vehicle) {
50
+ const { BatteryLevel, StatusLowBattery } = this.ctx.hap.Characteristic;
51
+ const values = fuelValues(vehicle, this.ctx.settings.lowBatteryThreshold);
52
+ this.set(BatteryLevel, values.level);
53
+ if (values.low !== undefined) {
54
+ this.set(StatusLowBattery, values.low
55
+ ? StatusLowBattery.BATTERY_LEVEL_LOW
56
+ : StatusLowBattery.BATTERY_LEVEL_NORMAL);
57
+ }
58
+ }
59
+ }
60
+ /** Humidity sensor that mirrors the fuel level, because HomeKit cannot trigger on battery levels. */
61
+ export class FuelLevelSensorBinding extends ServiceBinding {
62
+ feature = "fuelLevelSensor";
63
+ serviceType = this.ctx.hap.Service.HumiditySensor;
64
+ subtype = "fuel-level";
65
+ displayName = "Fuel Level";
66
+ get hasStatusFault() {
67
+ return true;
68
+ }
69
+ render(_service, vehicle) {
70
+ this.set(this.ctx.hap.Characteristic.CurrentRelativeHumidity, fuelLevel(vehicle));
71
+ }
72
+ }
36
73
  //# sourceMappingURL=battery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"battery.js","sourceRoot":"","sources":["../../../src/accessory/services/battery.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAC;AAE/D,qFAAqF;AACrF,MAAM,OAAO,cAAe,SAAQ,cAAc;IACvC,OAAO,GAAG,SAAS,CAAC;IACV,WAAW,GAAgB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;IACxD,OAAO,GAAG,SAAS,CAAC;IACpB,WAAW,GAAG,SAAS,CAAC;IAEjC,MAAM,CAAC,QAAiB,EAAE,OAAgB;QAClD,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,GACrD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;QAC9B,MAAM,MAAM,GAAG,aAAa,CAC1B,OAAO,EACP,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CACtC,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC,GAAG,CACN,aAAa,EACb,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,YAAY,CACtE,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC,GAAG,CACN,gBAAgB,EAChB,MAAM,CAAC,GAAG;gBACR,CAAC,CAAC,gBAAgB,CAAC,iBAAiB;gBACpC,CAAC,CAAC,gBAAgB,CAAC,oBAAoB,CAC1C,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAED,0GAA0G;AAC1G,MAAM,OAAO,0BAA2B,SAAQ,cAAc;IACnD,OAAO,GAAG,qBAAqB,CAAC;IACtB,WAAW,GAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC;IACnB,OAAO,GAAG,iBAAiB,CAAC;IAC5B,WAAW,GAAG,iBAAiB,CAAC;IAEnD,IAAuB,cAAc;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAES,MAAM,CAAC,QAAiB,EAAE,OAAgB;QAClD,IAAI,CAAC,GAAG,CACN,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,uBAAuB,EACnD,aAAa,CAAC,OAAO,CAAC,CACvB,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"battery.js","sourceRoot":"","sources":["../../../src/accessory/services/battery.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,17 @@
1
1
  import type { Service } from "homebridge";
2
2
  import type { Vehicle } from "../../api/types.js";
3
3
  import type { FeatureToggles } from "../../config.js";
4
- import { type BodyValues } from "../../vehicle/mapping.js";
5
4
  import { ServiceBinding, type BindingContext, type ServiceType } from "./common.js";
6
- type BodyFeature = Extract<keyof FeatureToggles, "doors" | "windows" | "trunk" | "bonnet" | "sunroof" | "lock">;
5
+ type ContactFeature = Extract<keyof FeatureToggles, "doors" | "windows" | "trunk" | "bonnet" | "sunroof" | "lock" | "lights" | "windowHeating" | "plugLock">;
7
6
  interface ContactDefinition {
8
- feature: BodyFeature;
7
+ feature: ContactFeature;
9
8
  displayName: string;
10
- /** `true` means "contact not detected" (open, or unlocked for the lock sensor). */
11
- isOpen: (values: BodyValues) => boolean | undefined;
9
+ /** `true` means "contact not detected": open, unlocked for the lock sensors, on otherwise. */
10
+ isOpen: (vehicle: Vehicle) => boolean | undefined;
12
11
  }
13
12
  export declare class ContactBinding extends ServiceBinding {
14
13
  private readonly definition;
15
- readonly feature: BodyFeature;
14
+ readonly feature: ContactFeature;
16
15
  protected readonly serviceType: ServiceType;
17
16
  protected readonly subtype: string;
18
17
  protected readonly displayName: string;