volkswagencarnet 5.0.0b4__py3-none-any.whl → 5.0.0b5__py3-none-any.whl
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.
Potentially problematic release.
This version of volkswagencarnet might be problematic. Click here for more details.
- volkswagencarnet/version.py +1 -1
- volkswagencarnet/vw_connection.py +489 -398
- volkswagencarnet/vw_const.py +1 -1
- volkswagencarnet/vw_dashboard.py +548 -139
- volkswagencarnet/vw_utilities.py +24 -40
- volkswagencarnet/vw_vehicle.py +1712 -687
- {volkswagencarnet-5.0.0b4.dist-info → volkswagencarnet-5.0.0b5.dist-info}/METADATA +5 -5
- volkswagencarnet-5.0.0b5.dist-info/RECORD +12 -0
- volkswagencarnet/vw_exceptions.py +0 -7
- volkswagencarnet-5.0.0b4.dist-info/RECORD +0 -13
- {volkswagencarnet-5.0.0b4.dist-info → volkswagencarnet-5.0.0b5.dist-info}/LICENSE.txt +0 -0
- {volkswagencarnet-5.0.0b4.dist-info → volkswagencarnet-5.0.0b5.dist-info}/WHEEL +0 -0
- {volkswagencarnet-5.0.0b4.dist-info → volkswagencarnet-5.0.0b5.dist-info}/top_level.txt +0 -0
volkswagencarnet/vw_vehicle.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Vehicle class for
|
|
2
|
+
"""Vehicle class for Volkswagen Connect."""
|
|
3
|
+
|
|
3
4
|
from __future__ import annotations
|
|
4
5
|
|
|
5
6
|
import asyncio
|
|
6
|
-
import logging
|
|
7
7
|
from collections import OrderedDict
|
|
8
|
-
from datetime import datetime, timedelta
|
|
8
|
+
from datetime import UTC, datetime, timedelta
|
|
9
9
|
from json import dumps as to_json
|
|
10
|
+
import logging
|
|
10
11
|
|
|
11
|
-
from .vw_const import VehicleStatusParameter as P
|
|
12
|
+
from .vw_const import Services, VehicleStatusParameter as P
|
|
12
13
|
from .vw_utilities import find_path, is_valid_path
|
|
13
14
|
|
|
14
15
|
# TODO
|
|
@@ -24,15 +25,21 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
24
25
|
ENGINE_TYPE_ELECTRIC = "electric"
|
|
25
26
|
ENGINE_TYPE_DIESEL = "diesel"
|
|
26
27
|
ENGINE_TYPE_GASOLINE = "gasoline"
|
|
28
|
+
ENGINE_TYPE_CNG = "cng"
|
|
27
29
|
ENGINE_TYPE_HYBRID = "hybrid"
|
|
28
|
-
ENGINE_TYPE_COMBUSTION = [
|
|
30
|
+
ENGINE_TYPE_COMBUSTION = [
|
|
31
|
+
ENGINE_TYPE_DIESEL,
|
|
32
|
+
ENGINE_TYPE_GASOLINE,
|
|
33
|
+
ENGINE_TYPE_CNG,
|
|
34
|
+
]
|
|
35
|
+
ENGINE_TYPE_GAS = [ENGINE_TYPE_CNG]
|
|
29
36
|
DEFAULT_TARGET_TEMP = 24
|
|
30
37
|
|
|
31
38
|
|
|
32
39
|
class Vehicle:
|
|
33
40
|
"""Vehicle contains the state of sensors and methods for interacting with the car."""
|
|
34
41
|
|
|
35
|
-
def __init__(self, conn, url):
|
|
42
|
+
def __init__(self, conn, url) -> None:
|
|
36
43
|
"""Initialize the Vehicle with default values."""
|
|
37
44
|
self._connection = conn
|
|
38
45
|
self._url = url
|
|
@@ -40,18 +47,17 @@ class Vehicle:
|
|
|
40
47
|
self._discovered = False
|
|
41
48
|
self._states = {}
|
|
42
49
|
self._requests: dict[str, object] = {
|
|
43
|
-
"departuretimer": {"status": "", "timestamp": datetime.now(
|
|
44
|
-
"batterycharge": {"status": "", "timestamp": datetime.now(
|
|
45
|
-
"climatisation": {"status": "", "timestamp": datetime.now(
|
|
46
|
-
"refresh": {"status": "", "timestamp": datetime.now(
|
|
47
|
-
"lock": {"status": "", "timestamp": datetime.now(
|
|
50
|
+
"departuretimer": {"status": "", "timestamp": datetime.now(UTC)},
|
|
51
|
+
"batterycharge": {"status": "", "timestamp": datetime.now(UTC)},
|
|
52
|
+
"climatisation": {"status": "", "timestamp": datetime.now(UTC)},
|
|
53
|
+
"refresh": {"status": "", "timestamp": datetime.now(UTC)},
|
|
54
|
+
"lock": {"status": "", "timestamp": datetime.now(UTC)},
|
|
48
55
|
"latest": "",
|
|
49
56
|
"state": "",
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
# API Endpoints that might be enabled for car (that we support)
|
|
53
60
|
self._services: dict[str, dict[str, object]] = {
|
|
54
|
-
# TODO needs a complete rework...
|
|
55
61
|
Services.ACCESS: {"active": False},
|
|
56
62
|
Services.BATTERY_CHARGING_CARE: {"active": False},
|
|
57
63
|
Services.BATTERY_SUPPORT: {"active": False},
|
|
@@ -73,33 +79,50 @@ class Vehicle:
|
|
|
73
79
|
"""Check if request is already in progress."""
|
|
74
80
|
if self._requests.get(topic, {}).get("id", False):
|
|
75
81
|
timestamp = self._requests.get(topic, {}).get(
|
|
76
|
-
"timestamp",
|
|
82
|
+
"timestamp",
|
|
83
|
+
datetime.now(UTC) - timedelta(minutes=unknown_offset),
|
|
77
84
|
)
|
|
78
|
-
if timestamp + timedelta(minutes=3) < datetime.now(
|
|
85
|
+
if timestamp + timedelta(minutes=3) < datetime.now(UTC):
|
|
79
86
|
self._requests.get(topic, {}).pop("id")
|
|
80
87
|
else:
|
|
81
|
-
_LOGGER.info(
|
|
88
|
+
_LOGGER.info("Action (%s) already in progress", topic)
|
|
82
89
|
return True
|
|
83
90
|
return False
|
|
84
91
|
|
|
85
|
-
async def _handle_response(
|
|
92
|
+
async def _handle_response(
|
|
93
|
+
self, response, topic: str, error_msg: str | None = None
|
|
94
|
+
) -> bool:
|
|
86
95
|
"""Handle errors in response and get requests remaining."""
|
|
87
96
|
if not response:
|
|
88
|
-
self._requests[topic] = {"status": "Failed", "timestamp": datetime.now(timezone.utc)}
|
|
89
|
-
_LOGGER.error(error_msg if error_msg is not None else f"Failed to perform {topic} action")
|
|
90
|
-
raise Exception(error_msg if error_msg is not None else f"Failed to perform {topic} action")
|
|
91
|
-
else:
|
|
92
97
|
self._requests[topic] = {
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"id": response.get("id", 0),
|
|
98
|
+
"status": "Failed",
|
|
99
|
+
"timestamp": datetime.now(UTC),
|
|
96
100
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
_LOGGER.error(
|
|
102
|
+
error_msg
|
|
103
|
+
if error_msg is not None
|
|
104
|
+
else f"Failed to perform {topic} action"
|
|
105
|
+
)
|
|
106
|
+
# pylint: disable=broad-exception-raised
|
|
107
|
+
raise Exception(
|
|
108
|
+
error_msg
|
|
109
|
+
if error_msg is not None
|
|
110
|
+
else f"Failed to perform {topic} action"
|
|
111
|
+
)
|
|
112
|
+
self._requests[topic] = {
|
|
113
|
+
"timestamp": datetime.now(UTC),
|
|
114
|
+
"status": response.get("state", "Unknown"),
|
|
115
|
+
"id": response.get("id", 0),
|
|
116
|
+
}
|
|
117
|
+
if response.get("state", None) == "Throttled":
|
|
118
|
+
status = "Throttled"
|
|
119
|
+
_LOGGER.warning("Request throttled (%s)", topic)
|
|
120
|
+
else:
|
|
121
|
+
status = await self.wait_for_request(request=response.get("id", 0))
|
|
122
|
+
self._requests[topic] = {
|
|
123
|
+
"status": status,
|
|
124
|
+
"timestamp": datetime.now(UTC),
|
|
125
|
+
}
|
|
103
126
|
return True
|
|
104
127
|
|
|
105
128
|
# API get and set functions #
|
|
@@ -107,43 +130,64 @@ class Vehicle:
|
|
|
107
130
|
async def discover(self):
|
|
108
131
|
"""Discover vehicle and initial data."""
|
|
109
132
|
|
|
110
|
-
_LOGGER.debug("Attempting discovery of supported API endpoints for vehicle
|
|
133
|
+
_LOGGER.debug("Attempting discovery of supported API endpoints for vehicle")
|
|
134
|
+
|
|
111
135
|
capabilities_response = await self._connection.getOperationList(self.vin)
|
|
112
136
|
parameters_list = capabilities_response.get("parameters", {})
|
|
113
137
|
capabilities_list = capabilities_response.get("capabilities", {})
|
|
138
|
+
|
|
139
|
+
# Update services with parameters
|
|
114
140
|
if parameters_list:
|
|
115
141
|
self._services[Services.PARAMETERS].update(parameters_list)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
|
|
143
|
+
# If there are no capabilities, log a warning
|
|
144
|
+
if not capabilities_list:
|
|
145
|
+
_LOGGER.warning(
|
|
146
|
+
"Could not determine available API endpoints for %s", self.vin
|
|
147
|
+
)
|
|
148
|
+
self._discovered = True
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
for service_id, service in capabilities_list.items():
|
|
152
|
+
if service_id not in self._services:
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
service_name = service.get("id", "Unknown Service")
|
|
156
|
+
data = {}
|
|
157
|
+
|
|
158
|
+
if service.get("isEnabled", False):
|
|
159
|
+
data["active"] = True
|
|
160
|
+
_LOGGER.debug("Discovered enabled service: %s", service_name)
|
|
161
|
+
|
|
162
|
+
expiration_date = service.get("expirationDate", None)
|
|
163
|
+
if expiration_date:
|
|
164
|
+
data["expiration"] = expiration_date
|
|
165
|
+
|
|
166
|
+
operations = service.get("operations", {})
|
|
167
|
+
data["operations"] = [op.get("id", None) for op in operations.values()]
|
|
168
|
+
|
|
169
|
+
parameters = service.get("parameters", [])
|
|
170
|
+
data["parameters"] = parameters
|
|
171
|
+
|
|
172
|
+
else:
|
|
173
|
+
reason = service.get("status", "Unknown reason")
|
|
174
|
+
_LOGGER.debug(
|
|
175
|
+
"Service: %s is disabled due to: %s", service_name, reason
|
|
176
|
+
)
|
|
177
|
+
data["active"] = False
|
|
178
|
+
|
|
179
|
+
# Update the service data
|
|
180
|
+
try:
|
|
181
|
+
self._services[service_name].update(data)
|
|
182
|
+
except Exception as error: # pylint: disable=broad-exception-caught
|
|
183
|
+
_LOGGER.warning(
|
|
184
|
+
'Exception "%s" while updating service "%s": %s',
|
|
185
|
+
error,
|
|
186
|
+
service_name,
|
|
187
|
+
data,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
_LOGGER.debug("API endpoints: %s", self._services)
|
|
147
191
|
self._discovered = True
|
|
148
192
|
|
|
149
193
|
async def update(self):
|
|
@@ -175,7 +219,7 @@ class Vehicle:
|
|
|
175
219
|
)
|
|
176
220
|
await asyncio.gather(self.get_service_status())
|
|
177
221
|
else:
|
|
178
|
-
_LOGGER.info(
|
|
222
|
+
_LOGGER.info("Vehicle with VIN %s is deactivated", self.vin)
|
|
179
223
|
|
|
180
224
|
# Data collection functions
|
|
181
225
|
async def get_selectivestatus(self, services):
|
|
@@ -214,26 +258,28 @@ class Vehicle:
|
|
|
214
258
|
"""Update status of outstanding requests."""
|
|
215
259
|
retry_count -= 1
|
|
216
260
|
if retry_count == 0:
|
|
217
|
-
_LOGGER.info(
|
|
261
|
+
_LOGGER.info("Timeout while waiting for result of %s", request.requestId)
|
|
218
262
|
return "Timeout"
|
|
219
263
|
try:
|
|
220
264
|
status = await self._connection.get_request_status(self.vin, request)
|
|
221
|
-
_LOGGER.debug(
|
|
265
|
+
_LOGGER.debug("Request ID %s: %s", request, status)
|
|
222
266
|
self._requests["state"] = status
|
|
223
267
|
if status == "In Progress":
|
|
224
268
|
await asyncio.sleep(10)
|
|
225
269
|
return await self.wait_for_request(request, retry_count)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
270
|
+
except Exception as error: # pylint: disable=broad-exception-caught
|
|
271
|
+
_LOGGER.warning(
|
|
272
|
+
"Exception encountered while waiting for request status: %s", error
|
|
273
|
+
)
|
|
230
274
|
return "Exception"
|
|
275
|
+
else:
|
|
276
|
+
return status
|
|
231
277
|
|
|
232
278
|
async def wait_for_data_refresh(self, retry_count=18):
|
|
233
279
|
"""Update status of outstanding requests."""
|
|
234
280
|
retry_count -= 1
|
|
235
281
|
if retry_count == 0:
|
|
236
|
-
_LOGGER.info("Timeout while waiting for data refresh
|
|
282
|
+
_LOGGER.info("Timeout while waiting for data refresh")
|
|
237
283
|
return "Timeout"
|
|
238
284
|
try:
|
|
239
285
|
await self.get_selectivestatus([Services.MEASUREMENTS])
|
|
@@ -241,41 +287,32 @@ class Vehicle:
|
|
|
241
287
|
if self.last_connected < refresh_trigger_time:
|
|
242
288
|
await asyncio.sleep(10)
|
|
243
289
|
return await self.wait_for_data_refresh(retry_count)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
290
|
+
|
|
291
|
+
except Exception as error: # pylint: disable=broad-exception-caught
|
|
292
|
+
_LOGGER.warning(
|
|
293
|
+
"Exception encountered while waiting for data refresh: %s", error
|
|
294
|
+
)
|
|
248
295
|
return "Exception"
|
|
296
|
+
else:
|
|
297
|
+
return "successful"
|
|
249
298
|
|
|
250
299
|
# Data set functions
|
|
251
300
|
# Charging (BATTERYCHARGE)
|
|
252
|
-
async def set_charger_current(self, value):
|
|
253
|
-
"""Set charger current."""
|
|
254
|
-
if self.is_charging_supported:
|
|
255
|
-
if 1 <= int(value) <= 255:
|
|
256
|
-
data = {"action": {"settings": {"maxChargeCurrent": int(value)}, "type": "setSettings"}}
|
|
257
|
-
else:
|
|
258
|
-
_LOGGER.error(f"Set charger maximum current to {value} is not supported.")
|
|
259
|
-
raise Exception(f"Set charger maximum current to {value} is not supported.")
|
|
260
|
-
return await self.set_charger(data)
|
|
261
|
-
else:
|
|
262
|
-
_LOGGER.error("No charger support.")
|
|
263
|
-
raise Exception("No charger support.")
|
|
264
|
-
|
|
265
301
|
async def set_charger(self, action) -> bool:
|
|
266
302
|
"""Turn on/off charging."""
|
|
267
303
|
if self.is_charging_supported:
|
|
268
304
|
if action not in ["start", "stop"]:
|
|
269
|
-
_LOGGER.error(
|
|
270
|
-
raise Exception(f'Charging action "{action}" is not supported.')
|
|
305
|
+
_LOGGER.error('Charging action "%s" is not supported', action)
|
|
306
|
+
raise Exception(f'Charging action "{action}" is not supported.') # pylint: disable=broad-exception-raised
|
|
271
307
|
self._requests["latest"] = "Batterycharge"
|
|
272
308
|
response = await self._connection.setCharging(self.vin, (action == "start"))
|
|
273
309
|
return await self._handle_response(
|
|
274
|
-
response=response,
|
|
310
|
+
response=response,
|
|
311
|
+
topic="charging",
|
|
312
|
+
error_msg=f"Failed to {action} charging",
|
|
275
313
|
)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
raise Exception("No charging support.")
|
|
314
|
+
_LOGGER.error("No charging support")
|
|
315
|
+
raise Exception("No charging support.") # pylint: disable=broad-exception-raised
|
|
279
316
|
|
|
280
317
|
async def set_charging_settings(self, setting, value):
|
|
281
318
|
"""Set charging settings."""
|
|
@@ -283,62 +320,96 @@ class Vehicle:
|
|
|
283
320
|
self.is_charge_max_ac_setting_supported
|
|
284
321
|
or self.is_auto_release_ac_connector_supported
|
|
285
322
|
or self.is_battery_target_charge_level_supported
|
|
323
|
+
or self.is_charge_max_ac_ampere_supported
|
|
286
324
|
):
|
|
287
325
|
if setting == "reduced_ac_charging" and value not in ["reduced", "maximum"]:
|
|
288
|
-
_LOGGER.error(
|
|
289
|
-
raise Exception(f'Charging setting "{value}" is not supported.')
|
|
326
|
+
_LOGGER.error('Charging setting "%s" is not supported', value)
|
|
327
|
+
raise Exception(f'Charging setting "{value}" is not supported.') # pylint: disable=broad-exception-raised
|
|
328
|
+
if setting == "max_charge_amperage" and int(value) not in [5, 10, 13, 32]:
|
|
329
|
+
_LOGGER.error(
|
|
330
|
+
"Setting maximum charge amperage to %s is not supported", value
|
|
331
|
+
)
|
|
332
|
+
# pylint: disable=broad-exception-raised
|
|
333
|
+
raise Exception(
|
|
334
|
+
f"Setting maximum charge amperage to {value} is not supported."
|
|
335
|
+
)
|
|
290
336
|
data = {}
|
|
291
|
-
if
|
|
292
|
-
|
|
337
|
+
if (
|
|
338
|
+
self.is_charge_max_ac_setting_supported
|
|
339
|
+
and setting != "max_charge_amperage"
|
|
340
|
+
):
|
|
341
|
+
data["maxChargeCurrentAC"] = (
|
|
342
|
+
value
|
|
343
|
+
if setting == "reduced_ac_charging"
|
|
344
|
+
else self.charge_max_ac_setting
|
|
345
|
+
)
|
|
293
346
|
if self.is_auto_release_ac_connector_supported:
|
|
294
347
|
data["autoUnlockPlugWhenChargedAC"] = (
|
|
295
|
-
value
|
|
348
|
+
value
|
|
349
|
+
if setting == "auto_release_ac_connector"
|
|
350
|
+
else self.auto_release_ac_connector_state
|
|
296
351
|
)
|
|
297
352
|
if self.is_battery_target_charge_level_supported:
|
|
298
|
-
self.
|
|
353
|
+
self.battery_target_charge_level = value
|
|
299
354
|
data["targetSOC_pct"] = (
|
|
300
|
-
value
|
|
355
|
+
value
|
|
356
|
+
if setting == "battery_target_charge_level"
|
|
357
|
+
else self.battery_target_charge_level
|
|
358
|
+
)
|
|
359
|
+
if (
|
|
360
|
+
self.is_charge_max_ac_ampere_supported
|
|
361
|
+
and setting != "reduced_ac_charging"
|
|
362
|
+
):
|
|
363
|
+
data["maxChargeCurrentAC_A"] = (
|
|
364
|
+
int(value)
|
|
365
|
+
if setting == "max_charge_amperage"
|
|
366
|
+
else self.charge_max_ac_ampere
|
|
301
367
|
)
|
|
302
368
|
self._requests["latest"] = "Batterycharge"
|
|
303
369
|
response = await self._connection.setChargingSettings(self.vin, data)
|
|
304
370
|
return await self._handle_response(
|
|
305
|
-
response=response,
|
|
371
|
+
response=response,
|
|
372
|
+
topic="charging",
|
|
373
|
+
error_msg="Failed to change charging settings",
|
|
306
374
|
)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
raise Exception("Charging settings are not supported.")
|
|
375
|
+
_LOGGER.error("Charging settings are not supported")
|
|
376
|
+
raise Exception("Charging settings are not supported.") # pylint: disable=broad-exception-raised
|
|
310
377
|
|
|
311
378
|
async def set_charging_care_settings(self, value):
|
|
312
379
|
"""Set charging care settings."""
|
|
313
380
|
if self.is_battery_care_mode_supported:
|
|
314
381
|
if value not in ["activated", "deactivated"]:
|
|
315
|
-
_LOGGER.error(
|
|
316
|
-
raise Exception(f'Charging care mode "{value}" is not supported.')
|
|
382
|
+
_LOGGER.error('Charging care mode "%s" is not supported', value)
|
|
383
|
+
raise Exception(f'Charging care mode "{value}" is not supported.') # pylint: disable=broad-exception-raised
|
|
317
384
|
data = {"batteryCareMode": value}
|
|
318
385
|
self._requests["latest"] = "Batterycharge"
|
|
319
|
-
response = await self._connection.setChargingCareModeSettings(
|
|
386
|
+
response = await self._connection.setChargingCareModeSettings(
|
|
387
|
+
self.vin, data
|
|
388
|
+
)
|
|
320
389
|
return await self._handle_response(
|
|
321
|
-
response=response,
|
|
390
|
+
response=response,
|
|
391
|
+
topic="charging",
|
|
392
|
+
error_msg="Failed to change charging care settings",
|
|
322
393
|
)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
raise Exception("Charging care settings are not supported.")
|
|
394
|
+
_LOGGER.error("Charging care settings are not supported")
|
|
395
|
+
raise Exception("Charging care settings are not supported.") # pylint: disable=broad-exception-raised
|
|
326
396
|
|
|
327
397
|
async def set_readiness_battery_support(self, value):
|
|
328
398
|
"""Set readiness battery support settings."""
|
|
329
399
|
if self.is_optimised_battery_use_supported:
|
|
330
400
|
if value not in [True, False]:
|
|
331
|
-
_LOGGER.error(
|
|
332
|
-
raise Exception(f'Battery support mode "{value}" is not supported.')
|
|
401
|
+
_LOGGER.error('Battery support mode "%s" is not supported', value)
|
|
402
|
+
raise Exception(f'Battery support mode "{value}" is not supported.') # pylint: disable=broad-exception-raised
|
|
333
403
|
data = {"batterySupportEnabled": value}
|
|
334
404
|
self._requests["latest"] = "Batterycharge"
|
|
335
405
|
response = await self._connection.setReadinessBatterySupport(self.vin, data)
|
|
336
406
|
return await self._handle_response(
|
|
337
|
-
response=response,
|
|
407
|
+
response=response,
|
|
408
|
+
topic="charging",
|
|
409
|
+
error_msg="Failed to change battery support settings",
|
|
338
410
|
)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
raise Exception("Battery support settings are not supported.")
|
|
411
|
+
_LOGGER.error("Battery support settings are not supported")
|
|
412
|
+
raise Exception("Battery support settings are not supported.") # pylint: disable=broad-exception-raised
|
|
342
413
|
|
|
343
414
|
# Climatisation electric/auxiliary/windows (CLIMATISATION)
|
|
344
415
|
async def set_climatisation_settings(self, setting, value):
|
|
@@ -385,16 +456,26 @@ class Vehicle:
|
|
|
385
456
|
)
|
|
386
457
|
if self.is_auxiliary_air_conditioning_supported:
|
|
387
458
|
data["climatizationAtUnlock"] = (
|
|
388
|
-
value
|
|
459
|
+
value
|
|
460
|
+
if setting == "auxiliary_air_conditioning"
|
|
461
|
+
else self.auxiliary_air_conditioning
|
|
389
462
|
)
|
|
390
463
|
if self.is_automatic_window_heating_supported:
|
|
391
464
|
data["windowHeatingEnabled"] = (
|
|
392
|
-
value
|
|
465
|
+
value
|
|
466
|
+
if setting == "automatic_window_heating"
|
|
467
|
+
else self.automatic_window_heating
|
|
393
468
|
)
|
|
394
469
|
if self.is_zone_front_left_supported:
|
|
395
|
-
data["zoneFrontLeftEnabled"] =
|
|
470
|
+
data["zoneFrontLeftEnabled"] = (
|
|
471
|
+
value if setting == "zone_front_left" else self.zone_front_left
|
|
472
|
+
)
|
|
396
473
|
if self.is_zone_front_right_supported:
|
|
397
|
-
data["zoneFrontRightEnabled"] =
|
|
474
|
+
data["zoneFrontRightEnabled"] = (
|
|
475
|
+
value
|
|
476
|
+
if setting == "zone_front_right"
|
|
477
|
+
else self.zone_front_right
|
|
478
|
+
)
|
|
398
479
|
self._requests["latest"] = "Climatisation"
|
|
399
480
|
response = await self._connection.setClimaterSettings(self.vin, data)
|
|
400
481
|
return await self._handle_response(
|
|
@@ -402,27 +483,28 @@ class Vehicle:
|
|
|
402
483
|
topic="climatisation",
|
|
403
484
|
error_msg="Failed to set climatisation settings",
|
|
404
485
|
)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
_LOGGER.error("Climatisation settings are not supported.")
|
|
410
|
-
raise Exception("Climatisation settings are not supported.")
|
|
486
|
+
_LOGGER.error('Set climatisation setting to "%s" is not supported', value)
|
|
487
|
+
raise Exception(f'Set climatisation setting to "{value}" is not supported.') # pylint: disable=broad-exception-raised
|
|
488
|
+
_LOGGER.error("Climatisation settings are not supported")
|
|
489
|
+
raise Exception("Climatisation settings are not supported.") # pylint: disable=broad-exception-raised
|
|
411
490
|
|
|
412
491
|
async def set_window_heating(self, action="stop"):
|
|
413
492
|
"""Turn on/off window heater."""
|
|
414
493
|
if self.is_window_heater_supported:
|
|
415
494
|
if action not in ["start", "stop"]:
|
|
416
|
-
_LOGGER.error(
|
|
417
|
-
raise Exception(f'Window heater action "{action}" is not supported.')
|
|
495
|
+
_LOGGER.error('Window heater action "%s" is not supported', action)
|
|
496
|
+
raise Exception(f'Window heater action "{action}" is not supported.') # pylint: disable=broad-exception-raised
|
|
418
497
|
self._requests["latest"] = "Climatisation"
|
|
419
|
-
response = await self._connection.setWindowHeater(
|
|
498
|
+
response = await self._connection.setWindowHeater(
|
|
499
|
+
self.vin, (action == "start")
|
|
500
|
+
)
|
|
420
501
|
return await self._handle_response(
|
|
421
|
-
response=response,
|
|
502
|
+
response=response,
|
|
503
|
+
topic="climatisation",
|
|
504
|
+
error_msg=f"Failed to {action} window heating",
|
|
422
505
|
)
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
raise Exception("No climatisation support.")
|
|
506
|
+
_LOGGER.error("No climatisation support")
|
|
507
|
+
raise Exception("No climatisation support.") # pylint: disable=broad-exception-raised
|
|
426
508
|
|
|
427
509
|
async def set_climatisation(self, action="stop"):
|
|
428
510
|
"""Turn on/off climatisation with electric heater."""
|
|
@@ -433,7 +515,9 @@ class Vehicle:
|
|
|
433
515
|
"targetTemperatureUnit": "celsius",
|
|
434
516
|
}
|
|
435
517
|
if self.is_climatisation_without_external_power_supported:
|
|
436
|
-
data["climatisationWithoutExternalPower"] =
|
|
518
|
+
data["climatisationWithoutExternalPower"] = (
|
|
519
|
+
self.climatisation_without_external_power
|
|
520
|
+
)
|
|
437
521
|
if self.is_auxiliary_air_conditioning_supported:
|
|
438
522
|
data["climatizationAtUnlock"] = self.auxiliary_air_conditioning
|
|
439
523
|
if self.is_automatic_window_heating_supported:
|
|
@@ -445,18 +529,19 @@ class Vehicle:
|
|
|
445
529
|
elif action == "stop":
|
|
446
530
|
data = {}
|
|
447
531
|
else:
|
|
448
|
-
_LOGGER.error(
|
|
449
|
-
raise Exception(f"Invalid climatisation action: {action}")
|
|
532
|
+
_LOGGER.error("Invalid climatisation action: %s", action)
|
|
533
|
+
raise Exception(f"Invalid climatisation action: {action}") # pylint: disable=broad-exception-raised
|
|
450
534
|
self._requests["latest"] = "Climatisation"
|
|
451
|
-
response = await self._connection.setClimater(
|
|
535
|
+
response = await self._connection.setClimater(
|
|
536
|
+
self.vin, data, (action == "start")
|
|
537
|
+
)
|
|
452
538
|
return await self._handle_response(
|
|
453
539
|
response=response,
|
|
454
540
|
topic="climatisation",
|
|
455
541
|
error_msg=f"Failed to {action} climatisation with electric heater.",
|
|
456
542
|
)
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
raise Exception("No climatisation support.")
|
|
543
|
+
_LOGGER.error("No climatisation support")
|
|
544
|
+
raise Exception("No climatisation support.") # pylint: disable=broad-exception-raised
|
|
460
545
|
|
|
461
546
|
async def set_auxiliary_climatisation(self, action, spin):
|
|
462
547
|
"""Turn on/off climatisation with auxiliary heater."""
|
|
@@ -468,72 +553,102 @@ class Vehicle:
|
|
|
468
553
|
elif action == "stop":
|
|
469
554
|
data = {}
|
|
470
555
|
else:
|
|
471
|
-
_LOGGER.error(
|
|
472
|
-
raise Exception(f"Invalid auxiliary heater action: {action}")
|
|
556
|
+
_LOGGER.error("Invalid auxiliary heater action: %s", action)
|
|
557
|
+
raise Exception(f"Invalid auxiliary heater action: {action}") # pylint: disable=broad-exception-raised
|
|
473
558
|
self._requests["latest"] = "Climatisation"
|
|
474
|
-
response = await self._connection.setAuxiliary(
|
|
559
|
+
response = await self._connection.setAuxiliary(
|
|
560
|
+
self.vin, data, (action == "start")
|
|
561
|
+
)
|
|
475
562
|
return await self._handle_response(
|
|
476
563
|
response=response,
|
|
477
564
|
topic="climatisation",
|
|
478
565
|
error_msg=f"Failed to {action} climatisation with auxiliary heater.",
|
|
479
566
|
)
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
raise Exception("No climatisation support.")
|
|
567
|
+
_LOGGER.error("No climatisation support")
|
|
568
|
+
raise Exception("No climatisation support.") # pylint: disable=broad-exception-raised
|
|
483
569
|
|
|
484
570
|
async def set_departure_timer(self, timer_id, spin, enable) -> bool:
|
|
485
571
|
"""Turn on/off departure timer."""
|
|
486
572
|
if self.is_departure_timer_supported(timer_id):
|
|
487
|
-
if
|
|
488
|
-
_LOGGER.error("Charging departure timers setting is not supported
|
|
489
|
-
raise Exception("Charging departure timers setting is not supported.")
|
|
573
|
+
if not isinstance(enable, bool):
|
|
574
|
+
_LOGGER.error("Charging departure timers setting is not supported")
|
|
575
|
+
raise Exception("Charging departure timers setting is not supported.") # pylint: disable=broad-exception-raised
|
|
490
576
|
data = None
|
|
491
577
|
response = None
|
|
492
578
|
if is_valid_path(
|
|
493
|
-
self.attrs,
|
|
494
|
-
|
|
495
|
-
|
|
579
|
+
self.attrs,
|
|
580
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.timers",
|
|
581
|
+
) and is_valid_path(
|
|
582
|
+
self.attrs,
|
|
583
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.profiles",
|
|
584
|
+
):
|
|
585
|
+
timers = find_path(
|
|
586
|
+
self.attrs,
|
|
587
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.timers",
|
|
588
|
+
)
|
|
496
589
|
profiles = find_path(
|
|
497
|
-
self.attrs,
|
|
590
|
+
self.attrs,
|
|
591
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.profiles",
|
|
498
592
|
)
|
|
499
|
-
for
|
|
500
|
-
if
|
|
501
|
-
timers[
|
|
593
|
+
for index, timer in enumerate(timers):
|
|
594
|
+
if timer.get("id", 0) == timer_id:
|
|
595
|
+
timers[index]["enabled"] = enable
|
|
502
596
|
data = {"timers": timers, "profiles": profiles}
|
|
503
597
|
response = await self._connection.setDepartureProfiles(self.vin, data)
|
|
504
|
-
if is_valid_path(
|
|
598
|
+
if is_valid_path(
|
|
599
|
+
self.attrs,
|
|
600
|
+
f"{Services.CLIMATISATION_TIMERS}.auxiliaryHeatingTimersStatus.value.timers",
|
|
601
|
+
):
|
|
505
602
|
timers = find_path(
|
|
506
|
-
self.attrs,
|
|
603
|
+
self.attrs,
|
|
604
|
+
f"{Services.CLIMATISATION_TIMERS}.auxiliaryHeatingTimersStatus.value.timers",
|
|
507
605
|
)
|
|
508
|
-
for
|
|
509
|
-
if
|
|
510
|
-
timers[
|
|
606
|
+
for index, timer in enumerate(timers):
|
|
607
|
+
if timer.get("id", 0) == timer_id:
|
|
608
|
+
timers[index]["enabled"] = enable
|
|
511
609
|
data = {"spin": spin, "timers": timers}
|
|
512
|
-
response = await self._connection.setAuxiliaryHeatingTimers(
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
610
|
+
response = await self._connection.setAuxiliaryHeatingTimers(
|
|
611
|
+
self.vin, data
|
|
612
|
+
)
|
|
613
|
+
if is_valid_path(
|
|
614
|
+
self.attrs,
|
|
615
|
+
f"{Services.DEPARTURE_TIMERS}.departureTimersStatus.value.timers",
|
|
616
|
+
):
|
|
617
|
+
timers = find_path(
|
|
618
|
+
self.attrs,
|
|
619
|
+
f"{Services.DEPARTURE_TIMERS}.departureTimersStatus.value.timers",
|
|
620
|
+
)
|
|
621
|
+
for index, timer in enumerate(timers):
|
|
622
|
+
if timer.get("id", 0) == timer_id:
|
|
623
|
+
timers[index]["enabled"] = enable
|
|
518
624
|
data = {"timers": timers}
|
|
519
625
|
response = await self._connection.setDepartureTimers(self.vin, data)
|
|
520
626
|
return await self._handle_response(
|
|
521
|
-
response=response,
|
|
627
|
+
response=response,
|
|
628
|
+
topic="departuretimer",
|
|
629
|
+
error_msg="Failed to change departure timers setting.",
|
|
522
630
|
)
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
raise Exception("Departure timers are not supported.")
|
|
631
|
+
_LOGGER.error("Departure timers are not supported")
|
|
632
|
+
raise Exception("Departure timers are not supported.") # pylint: disable=broad-exception-raised
|
|
526
633
|
|
|
527
634
|
async def set_ac_departure_timer(self, timer_id, enable) -> bool:
|
|
528
635
|
"""Turn on/off ac departure timer."""
|
|
529
636
|
if self.is_ac_departure_timer_supported(timer_id):
|
|
530
|
-
if
|
|
531
|
-
_LOGGER.error(
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
637
|
+
if not isinstance(enable, bool):
|
|
638
|
+
_LOGGER.error(
|
|
639
|
+
"Charging climatisation departure timers setting is not supported"
|
|
640
|
+
)
|
|
641
|
+
# pylint: disable=broad-exception-raised
|
|
642
|
+
raise Exception(
|
|
643
|
+
"Charging climatisation departure timers setting is not supported."
|
|
644
|
+
)
|
|
645
|
+
timers = find_path(
|
|
646
|
+
self.attrs,
|
|
647
|
+
f"{Services.CLIMATISATION_TIMERS}.climatisationTimersStatus.value.timers",
|
|
648
|
+
)
|
|
649
|
+
for index, timer in enumerate(timers):
|
|
650
|
+
if timer.get("id", 0) == timer_id:
|
|
651
|
+
timers[index]["enabled"] = enable
|
|
537
652
|
data = {"timers": timers}
|
|
538
653
|
response = await self._connection.setClimatisationTimers(self.vin, data)
|
|
539
654
|
return await self._handle_response(
|
|
@@ -541,32 +656,38 @@ class Vehicle:
|
|
|
541
656
|
topic="departuretimer",
|
|
542
657
|
error_msg="Failed to change climatisation departure timers setting.",
|
|
543
658
|
)
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
raise Exception("Climatisation departure timers are not supported.")
|
|
659
|
+
_LOGGER.error("Climatisation departure timers are not supported")
|
|
660
|
+
raise Exception("Climatisation departure timers are not supported.") # pylint: disable=broad-exception-raised
|
|
547
661
|
|
|
548
662
|
# Lock (RLU)
|
|
549
663
|
async def set_lock(self, action, spin):
|
|
550
664
|
"""Remote lock and unlock actions."""
|
|
551
665
|
if not self._services.get(Services.ACCESS, {}).get("active", False):
|
|
552
|
-
_LOGGER.info("Remote lock/unlock is not supported
|
|
553
|
-
raise Exception("Remote lock/unlock is not supported.")
|
|
666
|
+
_LOGGER.info("Remote lock/unlock is not supported")
|
|
667
|
+
raise Exception("Remote lock/unlock is not supported.") # pylint: disable=broad-exception-raised
|
|
554
668
|
if self._in_progress("lock", unknown_offset=-5):
|
|
555
669
|
return False
|
|
556
670
|
if action not in ["lock", "unlock"]:
|
|
557
|
-
_LOGGER.error(
|
|
558
|
-
raise Exception(f"Invalid lock action: {action}")
|
|
671
|
+
_LOGGER.error("Invalid lock action: %s", action)
|
|
672
|
+
raise Exception(f"Invalid lock action: {action}") # pylint: disable=broad-exception-raised
|
|
559
673
|
|
|
560
674
|
try:
|
|
561
675
|
self._requests["latest"] = "Lock"
|
|
562
|
-
response = await self._connection.setLock(
|
|
676
|
+
response = await self._connection.setLock(
|
|
677
|
+
self.vin, (action == "lock"), spin
|
|
678
|
+
)
|
|
563
679
|
return await self._handle_response(
|
|
564
|
-
response=response,
|
|
680
|
+
response=response,
|
|
681
|
+
topic="access",
|
|
682
|
+
error_msg=f"Failed to {action} vehicle",
|
|
565
683
|
)
|
|
566
|
-
except Exception as error:
|
|
567
|
-
_LOGGER.warning(
|
|
568
|
-
self._requests["lock"] = {
|
|
569
|
-
|
|
684
|
+
except Exception as error: # pylint: disable=broad-exception-caught
|
|
685
|
+
_LOGGER.warning("Failed to %s vehicle - %s", action, error)
|
|
686
|
+
self._requests["lock"] = {
|
|
687
|
+
"status": "Exception",
|
|
688
|
+
"timestamp": datetime.now(UTC),
|
|
689
|
+
}
|
|
690
|
+
raise Exception("Lock action failed") # pylint: disable=broad-exception-raised
|
|
570
691
|
|
|
571
692
|
# Refresh vehicle data (VSR)
|
|
572
693
|
async def set_refresh(self):
|
|
@@ -580,40 +701,46 @@ class Vehicle:
|
|
|
580
701
|
if response.status == 204:
|
|
581
702
|
self._requests["state"] = "in_progress"
|
|
582
703
|
self._requests["refresh"] = {
|
|
583
|
-
"timestamp": datetime.now(
|
|
704
|
+
"timestamp": datetime.now(UTC),
|
|
584
705
|
"status": "in_progress",
|
|
585
706
|
"id": 0,
|
|
586
707
|
}
|
|
587
708
|
status = await self.wait_for_data_refresh()
|
|
588
709
|
elif response.status == 429:
|
|
589
710
|
status = "Throttled"
|
|
590
|
-
_LOGGER.debug("Server side throttled. Try again later
|
|
711
|
+
_LOGGER.debug("Server side throttled. Try again later")
|
|
591
712
|
else:
|
|
592
|
-
_LOGGER.debug(
|
|
713
|
+
_LOGGER.debug(
|
|
714
|
+
"Unable to refresh the data. Incorrect response code: %s",
|
|
715
|
+
response.status,
|
|
716
|
+
)
|
|
593
717
|
self._requests["state"] = status
|
|
594
|
-
self._requests["refresh"] = {
|
|
718
|
+
self._requests["refresh"] = {
|
|
719
|
+
"status": status,
|
|
720
|
+
"timestamp": datetime.now(UTC),
|
|
721
|
+
}
|
|
595
722
|
return True
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
723
|
+
_LOGGER.debug("Unable to refresh the data")
|
|
724
|
+
except Exception as error: # pylint: disable=broad-exception-caught
|
|
725
|
+
_LOGGER.warning("Failed to execute data refresh - %s", error)
|
|
726
|
+
self._requests["refresh"] = {
|
|
727
|
+
"status": "Exception",
|
|
728
|
+
"timestamp": datetime.now(UTC),
|
|
729
|
+
}
|
|
730
|
+
raise Exception("Data refresh failed") # pylint: disable=broad-exception-raised
|
|
602
731
|
|
|
603
732
|
# Vehicle class helpers #
|
|
604
733
|
# Vehicle info
|
|
605
734
|
@property
|
|
606
735
|
def attrs(self):
|
|
607
|
-
"""
|
|
608
|
-
Return all attributes.
|
|
736
|
+
"""Return all attributes.
|
|
609
737
|
|
|
610
738
|
:return:
|
|
611
739
|
"""
|
|
612
740
|
return self._states
|
|
613
741
|
|
|
614
742
|
def has_attr(self, attr) -> bool:
|
|
615
|
-
"""
|
|
616
|
-
Return true if attribute exists.
|
|
743
|
+
"""Return true if attribute exists.
|
|
617
744
|
|
|
618
745
|
:param attr:
|
|
619
746
|
:return:
|
|
@@ -621,8 +748,7 @@ class Vehicle:
|
|
|
621
748
|
return is_valid_path(self.attrs, attr)
|
|
622
749
|
|
|
623
750
|
def get_attr(self, attr):
|
|
624
|
-
"""
|
|
625
|
-
Return a specific attribute.
|
|
751
|
+
"""Return a specific attribute.
|
|
626
752
|
|
|
627
753
|
:param attr:
|
|
628
754
|
:return:
|
|
@@ -632,41 +758,44 @@ class Vehicle:
|
|
|
632
758
|
async def expired(self, service):
|
|
633
759
|
"""Check if access to service has expired."""
|
|
634
760
|
try:
|
|
635
|
-
now = datetime.
|
|
761
|
+
now = datetime.now(UTC)
|
|
636
762
|
if self._services.get(service, {}).get("expiration", False):
|
|
637
763
|
expiration = self._services.get(service, {}).get("expiration", False)
|
|
638
764
|
if not expiration:
|
|
639
|
-
expiration = datetime.
|
|
765
|
+
expiration = datetime.neow(UTC) + timedelta(days=1)
|
|
640
766
|
else:
|
|
641
|
-
_LOGGER.debug(
|
|
642
|
-
|
|
767
|
+
_LOGGER.debug(
|
|
768
|
+
"Could not determine end of access for service %s, assuming it is valid",
|
|
769
|
+
service,
|
|
770
|
+
)
|
|
771
|
+
expiration = datetime.now(UTC) + timedelta(days=1)
|
|
643
772
|
expiration = expiration.replace(tzinfo=None)
|
|
644
773
|
if now >= expiration:
|
|
645
|
-
_LOGGER.warning(
|
|
774
|
+
_LOGGER.warning("Access to %s has expired!", service)
|
|
646
775
|
self._discovered = False
|
|
647
776
|
return True
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
777
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
778
|
+
_LOGGER.debug(
|
|
779
|
+
"Exception. Could not determine end of access for service %s, assuming it is valid",
|
|
780
|
+
service,
|
|
781
|
+
)
|
|
782
|
+
return False
|
|
783
|
+
else:
|
|
652
784
|
return False
|
|
653
785
|
|
|
654
786
|
def dashboard(self, **config):
|
|
655
|
-
"""
|
|
656
|
-
Return dashboard with specified configuration.
|
|
787
|
+
"""Return dashboard with specified configuration.
|
|
657
788
|
|
|
658
789
|
:param config:
|
|
659
790
|
:return:
|
|
660
791
|
"""
|
|
661
|
-
#
|
|
662
|
-
from .vw_dashboard import Dashboard
|
|
792
|
+
from .vw_dashboard import Dashboard # pylint: disable=import-outside-toplevel
|
|
663
793
|
|
|
664
794
|
return Dashboard(self, **config)
|
|
665
795
|
|
|
666
796
|
@property
|
|
667
797
|
def vin(self) -> str:
|
|
668
|
-
"""
|
|
669
|
-
Vehicle identification number.
|
|
798
|
+
"""Vehicle identification number.
|
|
670
799
|
|
|
671
800
|
:return:
|
|
672
801
|
"""
|
|
@@ -674,8 +803,7 @@ class Vehicle:
|
|
|
674
803
|
|
|
675
804
|
@property
|
|
676
805
|
def unique_id(self) -> str:
|
|
677
|
-
"""
|
|
678
|
-
Return unique id for the vehicle (vin).
|
|
806
|
+
"""Return unique id for the vehicle (vin).
|
|
679
807
|
|
|
680
808
|
:return:
|
|
681
809
|
"""
|
|
@@ -685,8 +813,7 @@ class Vehicle:
|
|
|
685
813
|
# Car information
|
|
686
814
|
@property
|
|
687
815
|
def nickname(self) -> str | None:
|
|
688
|
-
"""
|
|
689
|
-
Return nickname of the vehicle.
|
|
816
|
+
"""Return nickname of the vehicle.
|
|
690
817
|
|
|
691
818
|
:return:
|
|
692
819
|
"""
|
|
@@ -694,8 +821,7 @@ class Vehicle:
|
|
|
694
821
|
|
|
695
822
|
@property
|
|
696
823
|
def is_nickname_supported(self) -> bool:
|
|
697
|
-
"""
|
|
698
|
-
Return true if naming the vehicle is supported.
|
|
824
|
+
"""Return true if naming the vehicle is supported.
|
|
699
825
|
|
|
700
826
|
:return:
|
|
701
827
|
"""
|
|
@@ -703,8 +829,7 @@ class Vehicle:
|
|
|
703
829
|
|
|
704
830
|
@property
|
|
705
831
|
def deactivated(self) -> bool | None:
|
|
706
|
-
"""
|
|
707
|
-
Return true if service is deactivated.
|
|
832
|
+
"""Return true if service is deactivated.
|
|
708
833
|
|
|
709
834
|
:return:
|
|
710
835
|
"""
|
|
@@ -712,8 +837,7 @@ class Vehicle:
|
|
|
712
837
|
|
|
713
838
|
@property
|
|
714
839
|
def is_deactivated_supported(self) -> bool:
|
|
715
|
-
"""
|
|
716
|
-
Return true if service deactivation status is supported.
|
|
840
|
+
"""Return true if service deactivation status is supported.
|
|
717
841
|
|
|
718
842
|
:return:
|
|
719
843
|
"""
|
|
@@ -747,8 +871,7 @@ class Vehicle:
|
|
|
747
871
|
|
|
748
872
|
@property
|
|
749
873
|
def is_model_image_supported(self) -> bool:
|
|
750
|
-
"""
|
|
751
|
-
Return true if vehicle model image is supported.
|
|
874
|
+
"""Return true if vehicle model image is supported.
|
|
752
875
|
|
|
753
876
|
:return:
|
|
754
877
|
"""
|
|
@@ -759,17 +882,27 @@ class Vehicle:
|
|
|
759
882
|
@property
|
|
760
883
|
def parking_light(self) -> bool:
|
|
761
884
|
"""Return true if parking light is on."""
|
|
762
|
-
lights =
|
|
885
|
+
lights = (
|
|
886
|
+
self.attrs.get(Services.VEHICLE_LIGHTS)
|
|
887
|
+
.get("lightsStatus")
|
|
888
|
+
.get("value")
|
|
889
|
+
.get("lights")
|
|
890
|
+
)
|
|
763
891
|
lights_on_count = 0
|
|
764
892
|
for light in lights:
|
|
765
893
|
if light["status"] == "on":
|
|
766
894
|
lights_on_count = lights_on_count + 1
|
|
767
|
-
return lights_on_count ==
|
|
895
|
+
return lights_on_count == 2
|
|
768
896
|
|
|
769
897
|
@property
|
|
770
898
|
def parking_light_last_updated(self) -> datetime:
|
|
771
899
|
"""Return attribute last updated timestamp."""
|
|
772
|
-
return
|
|
900
|
+
return (
|
|
901
|
+
self.attrs.get(Services.VEHICLE_LIGHTS)
|
|
902
|
+
.get("lightsStatus")
|
|
903
|
+
.get("value")
|
|
904
|
+
.get("carCapturedTimestamp")
|
|
905
|
+
)
|
|
773
906
|
|
|
774
907
|
@property
|
|
775
908
|
def is_parking_light_supported(self) -> bool:
|
|
@@ -785,33 +918,35 @@ class Vehicle:
|
|
|
785
918
|
# this field is only a dirty hack, because there is no overarching information for the car anymore,
|
|
786
919
|
# only information per service, so we just use the one for fuelStatus.rangeStatus when car is ideling
|
|
787
920
|
# and charing.batteryStatus when electic car is charging
|
|
788
|
-
|
|
921
|
+
# Return attribute last updated timestamp.
|
|
789
922
|
if self.is_battery_level_supported and self.charging:
|
|
790
923
|
return self.battery_level_last_updated
|
|
791
|
-
|
|
792
|
-
if
|
|
924
|
+
if self.is_distance_supported:
|
|
925
|
+
if isinstance(self.distance_last_updated, str):
|
|
793
926
|
return (
|
|
794
|
-
datetime.strptime(
|
|
927
|
+
datetime.strptime(
|
|
928
|
+
self.distance_last_updated, "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
929
|
+
)
|
|
795
930
|
.replace(microsecond=0)
|
|
796
|
-
.replace(tzinfo=
|
|
931
|
+
.replace(tzinfo=UTC)
|
|
797
932
|
)
|
|
798
|
-
|
|
799
|
-
return self.distance_last_updated
|
|
933
|
+
return self.distance_last_updated
|
|
800
934
|
|
|
801
935
|
@property
|
|
802
936
|
def last_connected_last_updated(self) -> datetime:
|
|
803
937
|
"""Return attribute last updated timestamp."""
|
|
804
938
|
if self.is_battery_level_supported and self.charging:
|
|
805
939
|
return self.battery_level_last_updated
|
|
806
|
-
|
|
807
|
-
if
|
|
940
|
+
if self.is_distance_supported:
|
|
941
|
+
if isinstance(self.distance_last_updated, str):
|
|
808
942
|
return (
|
|
809
|
-
datetime.strptime(
|
|
943
|
+
datetime.strptime(
|
|
944
|
+
self.distance_last_updated, "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
945
|
+
)
|
|
810
946
|
.replace(microsecond=0)
|
|
811
|
-
.replace(tzinfo=
|
|
947
|
+
.replace(tzinfo=UTC)
|
|
812
948
|
)
|
|
813
|
-
|
|
814
|
-
return self.distance_last_updated
|
|
949
|
+
return self.distance_last_updated
|
|
815
950
|
|
|
816
951
|
@property
|
|
817
952
|
def is_last_connected_supported(self) -> bool:
|
|
@@ -822,326 +957,495 @@ class Vehicle:
|
|
|
822
957
|
@property
|
|
823
958
|
def distance(self) -> int | None:
|
|
824
959
|
"""Return vehicle odometer."""
|
|
825
|
-
return find_path(
|
|
960
|
+
return find_path(
|
|
961
|
+
self.attrs, f"{Services.MEASUREMENTS}.odometerStatus.value.odometer"
|
|
962
|
+
)
|
|
826
963
|
|
|
827
964
|
@property
|
|
828
965
|
def distance_last_updated(self) -> datetime:
|
|
829
966
|
"""Return last updated timestamp."""
|
|
830
|
-
return find_path(
|
|
967
|
+
return find_path(
|
|
968
|
+
self.attrs,
|
|
969
|
+
f"{Services.MEASUREMENTS}.odometerStatus.value.carCapturedTimestamp",
|
|
970
|
+
)
|
|
831
971
|
|
|
832
972
|
@property
|
|
833
973
|
def is_distance_supported(self) -> bool:
|
|
834
974
|
"""Return true if odometer is supported."""
|
|
835
|
-
return is_valid_path(
|
|
975
|
+
return is_valid_path(
|
|
976
|
+
self.attrs, f"{Services.MEASUREMENTS}.odometerStatus.value.odometer"
|
|
977
|
+
)
|
|
836
978
|
|
|
837
979
|
@property
|
|
838
980
|
def service_inspection(self):
|
|
839
981
|
"""Return time left for service inspection."""
|
|
840
|
-
return find_path(
|
|
982
|
+
return find_path(
|
|
983
|
+
self.attrs,
|
|
984
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.inspectionDue_days",
|
|
985
|
+
)
|
|
841
986
|
|
|
842
987
|
@property
|
|
843
988
|
def service_inspection_last_updated(self) -> datetime:
|
|
844
989
|
"""Return attribute last updated timestamp."""
|
|
845
990
|
return find_path(
|
|
846
|
-
self.attrs,
|
|
991
|
+
self.attrs,
|
|
992
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.carCapturedTimestamp",
|
|
847
993
|
)
|
|
848
994
|
|
|
849
995
|
@property
|
|
850
996
|
def is_service_inspection_supported(self) -> bool:
|
|
851
|
-
"""
|
|
852
|
-
Return true if days to service inspection is supported.
|
|
997
|
+
"""Return true if days to service inspection is supported.
|
|
853
998
|
|
|
854
999
|
:return:
|
|
855
1000
|
"""
|
|
856
1001
|
return is_valid_path(
|
|
857
|
-
self.attrs,
|
|
1002
|
+
self.attrs,
|
|
1003
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.inspectionDue_days",
|
|
858
1004
|
)
|
|
859
1005
|
|
|
860
1006
|
@property
|
|
861
1007
|
def service_inspection_distance(self):
|
|
862
1008
|
"""Return distance left for service inspection."""
|
|
863
|
-
return find_path(
|
|
1009
|
+
return find_path(
|
|
1010
|
+
self.attrs,
|
|
1011
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.inspectionDue_km",
|
|
1012
|
+
)
|
|
864
1013
|
|
|
865
1014
|
@property
|
|
866
1015
|
def service_inspection_distance_last_updated(self) -> datetime:
|
|
867
1016
|
"""Return attribute last updated timestamp."""
|
|
868
1017
|
return find_path(
|
|
869
|
-
self.attrs,
|
|
1018
|
+
self.attrs,
|
|
1019
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.carCapturedTimestamp",
|
|
870
1020
|
)
|
|
871
1021
|
|
|
872
1022
|
@property
|
|
873
1023
|
def is_service_inspection_distance_supported(self) -> bool:
|
|
874
|
-
"""
|
|
875
|
-
Return true if distance to service inspection is supported.
|
|
1024
|
+
"""Return true if distance to service inspection is supported.
|
|
876
1025
|
|
|
877
1026
|
:return:
|
|
878
1027
|
"""
|
|
879
1028
|
return is_valid_path(
|
|
880
|
-
self.attrs,
|
|
1029
|
+
self.attrs,
|
|
1030
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.inspectionDue_km",
|
|
881
1031
|
)
|
|
882
1032
|
|
|
883
1033
|
@property
|
|
884
1034
|
def oil_inspection(self):
|
|
885
1035
|
"""Return time left for oil inspection."""
|
|
886
|
-
return find_path(
|
|
1036
|
+
return find_path(
|
|
1037
|
+
self.attrs,
|
|
1038
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.oilServiceDue_days",
|
|
1039
|
+
)
|
|
887
1040
|
|
|
888
1041
|
@property
|
|
889
1042
|
def oil_inspection_last_updated(self) -> datetime:
|
|
890
1043
|
"""Return attribute last updated timestamp."""
|
|
891
1044
|
return find_path(
|
|
892
|
-
self.attrs,
|
|
1045
|
+
self.attrs,
|
|
1046
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.carCapturedTimestamp",
|
|
893
1047
|
)
|
|
894
1048
|
|
|
895
1049
|
@property
|
|
896
1050
|
def is_oil_inspection_supported(self) -> bool:
|
|
897
|
-
"""
|
|
898
|
-
Return true if days to oil inspection is supported.
|
|
1051
|
+
"""Return true if days to oil inspection is supported.
|
|
899
1052
|
|
|
900
1053
|
:return:
|
|
901
1054
|
"""
|
|
902
1055
|
if not self.has_combustion_engine:
|
|
903
1056
|
return False
|
|
904
1057
|
return is_valid_path(
|
|
905
|
-
self.attrs,
|
|
1058
|
+
self.attrs,
|
|
1059
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.oilServiceDue_days",
|
|
906
1060
|
)
|
|
907
1061
|
|
|
908
1062
|
@property
|
|
909
1063
|
def oil_inspection_distance(self):
|
|
910
1064
|
"""Return distance left for oil inspection."""
|
|
911
|
-
return find_path(
|
|
1065
|
+
return find_path(
|
|
1066
|
+
self.attrs,
|
|
1067
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.oilServiceDue_km",
|
|
1068
|
+
)
|
|
912
1069
|
|
|
913
1070
|
@property
|
|
914
1071
|
def oil_inspection_distance_last_updated(self) -> datetime:
|
|
915
1072
|
"""Return attribute last updated timestamp."""
|
|
916
1073
|
return find_path(
|
|
917
|
-
self.attrs,
|
|
1074
|
+
self.attrs,
|
|
1075
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.carCapturedTimestamp",
|
|
918
1076
|
)
|
|
919
1077
|
|
|
920
1078
|
@property
|
|
921
1079
|
def is_oil_inspection_distance_supported(self) -> bool:
|
|
922
|
-
"""
|
|
923
|
-
Return true if oil inspection distance is supported.
|
|
1080
|
+
"""Return true if oil inspection distance is supported.
|
|
924
1081
|
|
|
925
1082
|
:return:
|
|
926
1083
|
"""
|
|
927
1084
|
if not self.has_combustion_engine:
|
|
928
1085
|
return False
|
|
929
1086
|
return is_valid_path(
|
|
930
|
-
self.attrs,
|
|
1087
|
+
self.attrs,
|
|
1088
|
+
f"{Services.VEHICLE_HEALTH_INSPECTION}.maintenanceStatus.value.oilServiceDue_km",
|
|
931
1089
|
)
|
|
932
1090
|
|
|
933
1091
|
@property
|
|
934
1092
|
def adblue_level(self) -> int:
|
|
935
1093
|
"""Return adblue level."""
|
|
936
|
-
return find_path(
|
|
1094
|
+
return find_path(
|
|
1095
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.adBlueRange"
|
|
1096
|
+
)
|
|
937
1097
|
|
|
938
1098
|
@property
|
|
939
1099
|
def adblue_level_last_updated(self) -> datetime:
|
|
940
1100
|
"""Return attribute last updated timestamp."""
|
|
941
|
-
return find_path(
|
|
1101
|
+
return find_path(
|
|
1102
|
+
self.attrs,
|
|
1103
|
+
f"{Services.MEASUREMENTS}.rangeStatus.value.carCapturedTimestamp",
|
|
1104
|
+
)
|
|
942
1105
|
|
|
943
1106
|
@property
|
|
944
1107
|
def is_adblue_level_supported(self) -> bool:
|
|
945
1108
|
"""Return true if adblue level is supported."""
|
|
946
|
-
return is_valid_path(
|
|
1109
|
+
return is_valid_path(
|
|
1110
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.adBlueRange"
|
|
1111
|
+
)
|
|
947
1112
|
|
|
948
1113
|
# Charger related states for EV and PHEV
|
|
949
1114
|
@property
|
|
950
1115
|
def charging(self) -> bool:
|
|
951
1116
|
"""Return charging state."""
|
|
952
|
-
cstate = find_path(
|
|
1117
|
+
cstate = find_path(
|
|
1118
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargingState"
|
|
1119
|
+
)
|
|
953
1120
|
return cstate == "charging"
|
|
954
1121
|
|
|
955
1122
|
@property
|
|
956
1123
|
def charging_last_updated(self) -> datetime:
|
|
957
1124
|
"""Return attribute last updated timestamp."""
|
|
958
|
-
return find_path(
|
|
1125
|
+
return find_path(
|
|
1126
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.carCapturedTimestamp"
|
|
1127
|
+
)
|
|
959
1128
|
|
|
960
1129
|
@property
|
|
961
1130
|
def is_charging_supported(self) -> bool:
|
|
962
1131
|
"""Return true if charging is supported."""
|
|
963
|
-
return is_valid_path(
|
|
1132
|
+
return is_valid_path(
|
|
1133
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargingState"
|
|
1134
|
+
)
|
|
964
1135
|
|
|
965
1136
|
@property
|
|
966
1137
|
def charging_power(self) -> int:
|
|
967
1138
|
"""Return charging power."""
|
|
968
|
-
return find_path(
|
|
1139
|
+
return find_path(
|
|
1140
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargePower_kW"
|
|
1141
|
+
)
|
|
969
1142
|
|
|
970
1143
|
@property
|
|
971
1144
|
def charging_power_last_updated(self) -> datetime:
|
|
972
1145
|
"""Return attribute last updated timestamp."""
|
|
973
|
-
return find_path(
|
|
1146
|
+
return find_path(
|
|
1147
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.carCapturedTimestamp"
|
|
1148
|
+
)
|
|
974
1149
|
|
|
975
1150
|
@property
|
|
976
1151
|
def is_charging_power_supported(self) -> bool:
|
|
977
1152
|
"""Return true if charging power is supported."""
|
|
978
|
-
return is_valid_path(
|
|
1153
|
+
return is_valid_path(
|
|
1154
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargePower_kW"
|
|
1155
|
+
)
|
|
979
1156
|
|
|
980
1157
|
@property
|
|
981
1158
|
def charging_rate(self) -> int:
|
|
982
1159
|
"""Return charging rate."""
|
|
983
|
-
return find_path(
|
|
1160
|
+
return find_path(
|
|
1161
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargeRate_kmph"
|
|
1162
|
+
)
|
|
984
1163
|
|
|
985
1164
|
@property
|
|
986
1165
|
def charging_rate_last_updated(self) -> datetime:
|
|
987
1166
|
"""Return attribute last updated timestamp."""
|
|
988
|
-
return find_path(
|
|
1167
|
+
return find_path(
|
|
1168
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.carCapturedTimestamp"
|
|
1169
|
+
)
|
|
989
1170
|
|
|
990
1171
|
@property
|
|
991
1172
|
def is_charging_rate_supported(self) -> bool:
|
|
992
1173
|
"""Return true if charging rate is supported."""
|
|
993
|
-
return is_valid_path(
|
|
1174
|
+
return is_valid_path(
|
|
1175
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargeRate_kmph"
|
|
1176
|
+
)
|
|
994
1177
|
|
|
995
1178
|
@property
|
|
996
1179
|
def charger_type(self) -> str:
|
|
997
1180
|
"""Return charger type."""
|
|
998
|
-
charger_type = find_path(
|
|
1181
|
+
charger_type = find_path(
|
|
1182
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargeType"
|
|
1183
|
+
)
|
|
999
1184
|
if charger_type == "ac":
|
|
1000
1185
|
return "AC"
|
|
1001
|
-
|
|
1186
|
+
if charger_type == "dc":
|
|
1002
1187
|
return "DC"
|
|
1003
1188
|
return "Unknown"
|
|
1004
1189
|
|
|
1005
1190
|
@property
|
|
1006
1191
|
def charger_type_last_updated(self) -> datetime:
|
|
1007
1192
|
"""Return attribute last updated timestamp."""
|
|
1008
|
-
return find_path(
|
|
1193
|
+
return find_path(
|
|
1194
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.carCapturedTimestamp"
|
|
1195
|
+
)
|
|
1009
1196
|
|
|
1010
1197
|
@property
|
|
1011
1198
|
def is_charger_type_supported(self) -> bool:
|
|
1012
1199
|
"""Return true if charger type is supported."""
|
|
1013
|
-
return is_valid_path(
|
|
1200
|
+
return is_valid_path(
|
|
1201
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargeType"
|
|
1202
|
+
)
|
|
1014
1203
|
|
|
1015
1204
|
@property
|
|
1016
1205
|
def battery_level(self) -> int:
|
|
1017
1206
|
"""Return battery level."""
|
|
1018
|
-
return find_path(
|
|
1207
|
+
return find_path(
|
|
1208
|
+
self.attrs, f"{Services.CHARGING}.batteryStatus.value.currentSOC_pct"
|
|
1209
|
+
)
|
|
1019
1210
|
|
|
1020
1211
|
@property
|
|
1021
1212
|
def battery_level_last_updated(self) -> datetime:
|
|
1022
1213
|
"""Return attribute last updated timestamp."""
|
|
1023
|
-
return find_path(
|
|
1214
|
+
return find_path(
|
|
1215
|
+
self.attrs, f"{Services.CHARGING}.batteryStatus.value.carCapturedTimestamp"
|
|
1216
|
+
)
|
|
1024
1217
|
|
|
1025
1218
|
@property
|
|
1026
1219
|
def is_battery_level_supported(self) -> bool:
|
|
1027
1220
|
"""Return true if battery level is supported."""
|
|
1028
|
-
return is_valid_path(
|
|
1221
|
+
return is_valid_path(
|
|
1222
|
+
self.attrs, f"{Services.CHARGING}.batteryStatus.value.currentSOC_pct"
|
|
1223
|
+
)
|
|
1029
1224
|
|
|
1030
1225
|
@property
|
|
1031
1226
|
def battery_target_charge_level(self) -> int:
|
|
1032
1227
|
"""Return target charge level."""
|
|
1033
|
-
return find_path(
|
|
1228
|
+
return find_path(
|
|
1229
|
+
self.attrs, f"{Services.CHARGING}.chargingSettings.value.targetSOC_pct"
|
|
1230
|
+
)
|
|
1034
1231
|
|
|
1035
1232
|
@property
|
|
1036
1233
|
def battery_target_charge_level_last_updated(self) -> datetime:
|
|
1037
1234
|
"""Return attribute last updated timestamp."""
|
|
1038
|
-
return find_path(
|
|
1235
|
+
return find_path(
|
|
1236
|
+
self.attrs,
|
|
1237
|
+
f"{Services.CHARGING}.chargingSettings.value.carCapturedTimestamp",
|
|
1238
|
+
)
|
|
1039
1239
|
|
|
1040
1240
|
@property
|
|
1041
1241
|
def is_battery_target_charge_level_supported(self) -> bool:
|
|
1042
1242
|
"""Return true if target charge level is supported."""
|
|
1043
|
-
return is_valid_path(
|
|
1243
|
+
return is_valid_path(
|
|
1244
|
+
self.attrs, f"{Services.CHARGING}.chargingSettings.value.targetSOC_pct"
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
@property
|
|
1248
|
+
def hv_battery_min_temperature(self) -> int:
|
|
1249
|
+
"""Return HV battery min temperature."""
|
|
1250
|
+
return (
|
|
1251
|
+
float(
|
|
1252
|
+
find_path(
|
|
1253
|
+
self.attrs,
|
|
1254
|
+
f"{Services.MEASUREMENTS}.temperatureBatteryStatus.value.temperatureHvBatteryMin_K",
|
|
1255
|
+
)
|
|
1256
|
+
)
|
|
1257
|
+
- 273.15
|
|
1258
|
+
)
|
|
1259
|
+
|
|
1260
|
+
@property
|
|
1261
|
+
def hv_battery_min_temperature_last_updated(self) -> datetime:
|
|
1262
|
+
"""Return attribute last updated timestamp."""
|
|
1263
|
+
return find_path(
|
|
1264
|
+
self.attrs,
|
|
1265
|
+
f"{Services.MEASUREMENTS}.temperatureBatteryStatus.value.carCapturedTimestamp",
|
|
1266
|
+
)
|
|
1267
|
+
|
|
1268
|
+
@property
|
|
1269
|
+
def is_hv_battery_min_temperature_supported(self) -> bool:
|
|
1270
|
+
"""Return true if HV battery min temperature is supported."""
|
|
1271
|
+
return is_valid_path(
|
|
1272
|
+
self.attrs,
|
|
1273
|
+
f"{Services.MEASUREMENTS}.temperatureBatteryStatus.value.temperatureHvBatteryMin_K",
|
|
1274
|
+
)
|
|
1275
|
+
|
|
1276
|
+
@property
|
|
1277
|
+
def hv_battery_max_temperature(self) -> int:
|
|
1278
|
+
"""Return HV battery max temperature."""
|
|
1279
|
+
return (
|
|
1280
|
+
float(
|
|
1281
|
+
find_path(
|
|
1282
|
+
self.attrs,
|
|
1283
|
+
f"{Services.MEASUREMENTS}.temperatureBatteryStatus.value.temperatureHvBatteryMax_K",
|
|
1284
|
+
)
|
|
1285
|
+
)
|
|
1286
|
+
- 273.15
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1289
|
+
@property
|
|
1290
|
+
def hv_battery_max_temperature_last_updated(self) -> datetime:
|
|
1291
|
+
"""Return attribute last updated timestamp."""
|
|
1292
|
+
return find_path(
|
|
1293
|
+
self.attrs,
|
|
1294
|
+
f"{Services.MEASUREMENTS}.temperatureBatteryStatus.value.carCapturedTimestamp",
|
|
1295
|
+
)
|
|
1296
|
+
|
|
1297
|
+
@property
|
|
1298
|
+
def is_hv_battery_max_temperature_supported(self) -> bool:
|
|
1299
|
+
"""Return true if HV battery max temperature is supported."""
|
|
1300
|
+
return is_valid_path(
|
|
1301
|
+
self.attrs,
|
|
1302
|
+
f"{Services.MEASUREMENTS}.temperatureBatteryStatus.value.temperatureHvBatteryMax_K",
|
|
1303
|
+
)
|
|
1044
1304
|
|
|
1045
1305
|
@property
|
|
1046
1306
|
def charge_max_ac_setting(self) -> str | int:
|
|
1047
1307
|
"""Return charger max ampere setting."""
|
|
1048
|
-
|
|
1049
|
-
|
|
1308
|
+
return find_path(
|
|
1309
|
+
self.attrs, f"{Services.CHARGING}.chargingSettings.value.maxChargeCurrentAC"
|
|
1310
|
+
)
|
|
1050
1311
|
|
|
1051
1312
|
@property
|
|
1052
1313
|
def charge_max_ac_setting_last_updated(self) -> datetime:
|
|
1053
1314
|
"""Return charger max ampere last updated."""
|
|
1054
|
-
return find_path(
|
|
1315
|
+
return find_path(
|
|
1316
|
+
self.attrs,
|
|
1317
|
+
f"{Services.CHARGING}.chargingSettings.value.carCapturedTimestamp",
|
|
1318
|
+
)
|
|
1055
1319
|
|
|
1056
1320
|
@property
|
|
1057
1321
|
def is_charge_max_ac_setting_supported(self) -> bool:
|
|
1058
1322
|
"""Return true if Charger Max Ampere is supported."""
|
|
1059
|
-
if is_valid_path(
|
|
1060
|
-
|
|
1323
|
+
if is_valid_path(
|
|
1324
|
+
self.attrs, f"{Services.CHARGING}.chargingSettings.value.maxChargeCurrentAC"
|
|
1325
|
+
):
|
|
1326
|
+
value = find_path(
|
|
1327
|
+
self.attrs,
|
|
1328
|
+
f"{Services.CHARGING}.chargingSettings.value.maxChargeCurrentAC",
|
|
1329
|
+
)
|
|
1061
1330
|
return value in ["reduced", "maximum", "invalid"]
|
|
1062
1331
|
return False
|
|
1063
1332
|
|
|
1064
1333
|
@property
|
|
1065
1334
|
def charge_max_ac_ampere(self) -> str | int:
|
|
1066
1335
|
"""Return charger max ampere setting."""
|
|
1067
|
-
return find_path(
|
|
1336
|
+
return find_path(
|
|
1337
|
+
self.attrs,
|
|
1338
|
+
f"{Services.CHARGING}.chargingSettings.value.maxChargeCurrentAC_A",
|
|
1339
|
+
)
|
|
1068
1340
|
|
|
1069
1341
|
@property
|
|
1070
1342
|
def charge_max_ac_ampere_last_updated(self) -> datetime:
|
|
1071
1343
|
"""Return charger max ampere last updated."""
|
|
1072
|
-
return find_path(
|
|
1344
|
+
return find_path(
|
|
1345
|
+
self.attrs,
|
|
1346
|
+
f"{Services.CHARGING}.chargingSettings.value.carCapturedTimestamp",
|
|
1347
|
+
)
|
|
1073
1348
|
|
|
1074
1349
|
@property
|
|
1075
1350
|
def is_charge_max_ac_ampere_supported(self) -> bool:
|
|
1076
1351
|
"""Return true if Charger Max Ampere is supported."""
|
|
1077
|
-
return is_valid_path(
|
|
1352
|
+
return is_valid_path(
|
|
1353
|
+
self.attrs,
|
|
1354
|
+
f"{Services.CHARGING}.chargingSettings.value.maxChargeCurrentAC_A",
|
|
1355
|
+
)
|
|
1078
1356
|
|
|
1079
1357
|
@property
|
|
1080
1358
|
def charging_cable_locked(self) -> bool:
|
|
1081
1359
|
"""Return plug locked state."""
|
|
1082
|
-
response = find_path(
|
|
1360
|
+
response = find_path(
|
|
1361
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.plugLockState"
|
|
1362
|
+
)
|
|
1083
1363
|
return response == "locked"
|
|
1084
1364
|
|
|
1085
1365
|
@property
|
|
1086
1366
|
def charging_cable_locked_last_updated(self) -> datetime:
|
|
1087
1367
|
"""Return plug locked state."""
|
|
1088
|
-
return find_path(
|
|
1368
|
+
return find_path(
|
|
1369
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.carCapturedTimestamp"
|
|
1370
|
+
)
|
|
1089
1371
|
|
|
1090
1372
|
@property
|
|
1091
1373
|
def is_charging_cable_locked_supported(self) -> bool:
|
|
1092
1374
|
"""Return true if plug locked state is supported."""
|
|
1093
|
-
return is_valid_path(
|
|
1375
|
+
return is_valid_path(
|
|
1376
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.plugLockState"
|
|
1377
|
+
)
|
|
1094
1378
|
|
|
1095
1379
|
@property
|
|
1096
1380
|
def charging_cable_connected(self) -> bool:
|
|
1097
1381
|
"""Return plug connected state."""
|
|
1098
|
-
response = find_path(
|
|
1382
|
+
response = find_path(
|
|
1383
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.plugConnectionState"
|
|
1384
|
+
)
|
|
1099
1385
|
return response == "connected"
|
|
1100
1386
|
|
|
1101
1387
|
@property
|
|
1102
1388
|
def charging_cable_connected_last_updated(self) -> datetime:
|
|
1103
1389
|
"""Return plug connected state last updated."""
|
|
1104
|
-
return find_path(
|
|
1390
|
+
return find_path(
|
|
1391
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.carCapturedTimestamp"
|
|
1392
|
+
)
|
|
1105
1393
|
|
|
1106
1394
|
@property
|
|
1107
1395
|
def is_charging_cable_connected_supported(self) -> bool:
|
|
1108
1396
|
"""Return true if supported."""
|
|
1109
|
-
return is_valid_path(
|
|
1397
|
+
return is_valid_path(
|
|
1398
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.plugConnectionState"
|
|
1399
|
+
)
|
|
1110
1400
|
|
|
1111
1401
|
@property
|
|
1112
1402
|
def charging_time_left(self) -> int:
|
|
1113
1403
|
"""Return minutes to charging complete."""
|
|
1114
|
-
if is_valid_path(
|
|
1404
|
+
if is_valid_path(
|
|
1405
|
+
self.attrs,
|
|
1406
|
+
f"{Services.CHARGING}.chargingStatus.value.remainingChargingTimeToComplete_min",
|
|
1407
|
+
):
|
|
1115
1408
|
return find_path(
|
|
1116
|
-
self.attrs,
|
|
1409
|
+
self.attrs,
|
|
1410
|
+
f"{Services.CHARGING}.chargingStatus.value.remainingChargingTimeToComplete_min",
|
|
1117
1411
|
)
|
|
1118
1412
|
return None
|
|
1119
1413
|
|
|
1120
1414
|
@property
|
|
1121
1415
|
def charging_time_left_last_updated(self) -> datetime:
|
|
1122
1416
|
"""Return minutes to charging complete last updated."""
|
|
1123
|
-
return find_path(
|
|
1417
|
+
return find_path(
|
|
1418
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.carCapturedTimestamp"
|
|
1419
|
+
)
|
|
1124
1420
|
|
|
1125
1421
|
@property
|
|
1126
1422
|
def is_charging_time_left_supported(self) -> bool:
|
|
1127
1423
|
"""Return true if charging is supported."""
|
|
1128
|
-
return is_valid_path(
|
|
1424
|
+
return is_valid_path(
|
|
1425
|
+
self.attrs, f"{Services.CHARGING}.chargingStatus.value.chargingState"
|
|
1426
|
+
)
|
|
1129
1427
|
|
|
1130
1428
|
@property
|
|
1131
1429
|
def external_power(self) -> bool:
|
|
1132
1430
|
"""Return true if external power is connected."""
|
|
1133
|
-
check = find_path(
|
|
1431
|
+
check = find_path(
|
|
1432
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.externalPower"
|
|
1433
|
+
)
|
|
1134
1434
|
return check in ["stationConnected", "available", "ready"]
|
|
1135
1435
|
|
|
1136
1436
|
@property
|
|
1137
1437
|
def external_power_last_updated(self) -> datetime:
|
|
1138
1438
|
"""Return external power last updated."""
|
|
1139
|
-
return find_path(
|
|
1439
|
+
return find_path(
|
|
1440
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.carCapturedTimestamp"
|
|
1441
|
+
)
|
|
1140
1442
|
|
|
1141
1443
|
@property
|
|
1142
1444
|
def is_external_power_supported(self) -> bool:
|
|
1143
1445
|
"""External power supported."""
|
|
1144
|
-
return is_valid_path(
|
|
1446
|
+
return is_valid_path(
|
|
1447
|
+
self.attrs, f"{Services.CHARGING}.plugStatus.value.externalPower"
|
|
1448
|
+
)
|
|
1145
1449
|
|
|
1146
1450
|
@property
|
|
1147
1451
|
def reduced_ac_charging(self) -> bool:
|
|
@@ -1161,64 +1465,89 @@ class Vehicle:
|
|
|
1161
1465
|
@property
|
|
1162
1466
|
def auto_release_ac_connector_state(self) -> str:
|
|
1163
1467
|
"""Return auto release ac connector state value."""
|
|
1164
|
-
return find_path(
|
|
1468
|
+
return find_path(
|
|
1469
|
+
self.attrs,
|
|
1470
|
+
f"{Services.CHARGING}.chargingSettings.value.autoUnlockPlugWhenChargedAC",
|
|
1471
|
+
)
|
|
1165
1472
|
|
|
1166
1473
|
@property
|
|
1167
1474
|
def auto_release_ac_connector(self) -> bool:
|
|
1168
1475
|
"""Return auto release ac connector state."""
|
|
1169
1476
|
return (
|
|
1170
|
-
find_path(
|
|
1477
|
+
find_path(
|
|
1478
|
+
self.attrs,
|
|
1479
|
+
f"{Services.CHARGING}.chargingSettings.value.autoUnlockPlugWhenChargedAC",
|
|
1480
|
+
)
|
|
1171
1481
|
== "permanent"
|
|
1172
1482
|
)
|
|
1173
1483
|
|
|
1174
1484
|
@property
|
|
1175
1485
|
def auto_release_ac_connector_last_updated(self) -> datetime:
|
|
1176
1486
|
"""Return attribute last updated timestamp."""
|
|
1177
|
-
return find_path(
|
|
1487
|
+
return find_path(
|
|
1488
|
+
self.attrs,
|
|
1489
|
+
f"{Services.CHARGING}.chargingSettings.value.carCapturedTimestamp",
|
|
1490
|
+
)
|
|
1178
1491
|
|
|
1179
1492
|
@property
|
|
1180
1493
|
def is_auto_release_ac_connector_supported(self) -> bool:
|
|
1181
1494
|
"""Return true if auto release ac connector is supported."""
|
|
1182
|
-
return is_valid_path(
|
|
1495
|
+
return is_valid_path(
|
|
1496
|
+
self.attrs,
|
|
1497
|
+
f"{Services.CHARGING}.chargingSettings.value.autoUnlockPlugWhenChargedAC",
|
|
1498
|
+
)
|
|
1183
1499
|
|
|
1184
1500
|
@property
|
|
1185
1501
|
def battery_care_mode(self) -> bool:
|
|
1186
1502
|
"""Return battery care mode state."""
|
|
1187
1503
|
return (
|
|
1188
|
-
find_path(
|
|
1504
|
+
find_path(
|
|
1505
|
+
self.attrs,
|
|
1506
|
+
f"{Services.BATTERY_CHARGING_CARE}.chargingCareSettings.value.batteryCareMode",
|
|
1507
|
+
)
|
|
1189
1508
|
== "activated"
|
|
1190
1509
|
)
|
|
1191
1510
|
|
|
1192
1511
|
@property
|
|
1193
1512
|
def battery_care_mode_last_updated(self) -> datetime:
|
|
1194
1513
|
"""Return attribute last updated timestamp."""
|
|
1195
|
-
return datetime.now(
|
|
1514
|
+
return datetime.now(UTC)
|
|
1196
1515
|
|
|
1197
1516
|
@property
|
|
1198
1517
|
def is_battery_care_mode_supported(self) -> bool:
|
|
1199
1518
|
"""Return true if battery care mode is supported."""
|
|
1200
|
-
return is_valid_path(
|
|
1519
|
+
return is_valid_path(
|
|
1520
|
+
self.attrs,
|
|
1521
|
+
f"{Services.BATTERY_CHARGING_CARE}.chargingCareSettings.value.batteryCareMode",
|
|
1522
|
+
)
|
|
1201
1523
|
|
|
1202
1524
|
@property
|
|
1203
1525
|
def optimised_battery_use(self) -> bool:
|
|
1204
1526
|
"""Return optimised battery use state."""
|
|
1205
1527
|
return (
|
|
1206
|
-
find_path(
|
|
1528
|
+
find_path(
|
|
1529
|
+
self.attrs,
|
|
1530
|
+
f"{Services.BATTERY_SUPPORT}.batterySupportStatus.value.batterySupport",
|
|
1531
|
+
)
|
|
1532
|
+
== "enabled"
|
|
1207
1533
|
)
|
|
1208
1534
|
|
|
1209
1535
|
@property
|
|
1210
1536
|
def optimised_battery_use_last_updated(self) -> datetime:
|
|
1211
1537
|
"""Return attribute last updated timestamp."""
|
|
1212
|
-
return datetime.now(
|
|
1538
|
+
return datetime.now(UTC)
|
|
1213
1539
|
|
|
1214
1540
|
@property
|
|
1215
1541
|
def is_optimised_battery_use_supported(self) -> bool:
|
|
1216
1542
|
"""Return true if optimised battery use is supported."""
|
|
1217
|
-
return is_valid_path(
|
|
1543
|
+
return is_valid_path(
|
|
1544
|
+
self.attrs,
|
|
1545
|
+
f"{Services.BATTERY_SUPPORT}.batterySupportStatus.value.batterySupport",
|
|
1546
|
+
)
|
|
1218
1547
|
|
|
1219
1548
|
@property
|
|
1220
1549
|
def energy_flow(self):
|
|
1221
|
-
# TODO untouched
|
|
1550
|
+
# TODO untouched # pylint: disable=fixme
|
|
1222
1551
|
"""Return true if energy is flowing through charging port."""
|
|
1223
1552
|
check = (
|
|
1224
1553
|
self.attrs.get("charger", {})
|
|
@@ -1231,7 +1560,7 @@ class Vehicle:
|
|
|
1231
1560
|
|
|
1232
1561
|
@property
|
|
1233
1562
|
def energy_flow_last_updated(self) -> datetime:
|
|
1234
|
-
# TODO untouched
|
|
1563
|
+
# TODO untouched # pylint: disable=fixme
|
|
1235
1564
|
"""Return energy flow last updated."""
|
|
1236
1565
|
return (
|
|
1237
1566
|
self.attrs.get("charger", {})
|
|
@@ -1243,9 +1572,14 @@ class Vehicle:
|
|
|
1243
1572
|
|
|
1244
1573
|
@property
|
|
1245
1574
|
def is_energy_flow_supported(self) -> bool:
|
|
1246
|
-
# TODO untouched
|
|
1575
|
+
# TODO untouched # pylint: disable=fixme
|
|
1247
1576
|
"""Energy flow supported."""
|
|
1248
|
-
return
|
|
1577
|
+
return (
|
|
1578
|
+
self.attrs.get("charger", {})
|
|
1579
|
+
.get("status", {})
|
|
1580
|
+
.get("chargingStatusData", {})
|
|
1581
|
+
.get("energyFlow", False)
|
|
1582
|
+
)
|
|
1249
1583
|
|
|
1250
1584
|
# Vehicle location states
|
|
1251
1585
|
@property
|
|
@@ -1258,9 +1592,11 @@ class Vehicle:
|
|
|
1258
1592
|
else:
|
|
1259
1593
|
lat = float(find_path(self.attrs, "parkingposition.lat"))
|
|
1260
1594
|
lng = float(find_path(self.attrs, "parkingposition.lon"))
|
|
1261
|
-
parking_time = find_path(
|
|
1595
|
+
parking_time = find_path(
|
|
1596
|
+
self.attrs, "parkingposition.carCapturedTimestamp"
|
|
1597
|
+
)
|
|
1262
1598
|
output = {"lat": lat, "lng": lng, "timestamp": parking_time}
|
|
1263
|
-
except Exception:
|
|
1599
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
1264
1600
|
output = {
|
|
1265
1601
|
"lat": "?",
|
|
1266
1602
|
"lng": "?",
|
|
@@ -1270,12 +1606,16 @@ class Vehicle:
|
|
|
1270
1606
|
@property
|
|
1271
1607
|
def position_last_updated(self) -> datetime:
|
|
1272
1608
|
"""Return position last updated."""
|
|
1273
|
-
return self.attrs.get("parkingposition", {}).get(
|
|
1609
|
+
return self.attrs.get("parkingposition", {}).get(
|
|
1610
|
+
"carCapturedTimestamp", "Unknown"
|
|
1611
|
+
)
|
|
1274
1612
|
|
|
1275
1613
|
@property
|
|
1276
1614
|
def is_position_supported(self) -> bool:
|
|
1277
1615
|
"""Return true if position is available."""
|
|
1278
|
-
return is_valid_path(
|
|
1616
|
+
return is_valid_path(
|
|
1617
|
+
self.attrs, "parkingposition.carCapturedTimestamp"
|
|
1618
|
+
) or self.attrs.get("isMoving", False)
|
|
1279
1619
|
|
|
1280
1620
|
@property
|
|
1281
1621
|
def vehicle_moving(self) -> bool:
|
|
@@ -1313,43 +1653,64 @@ class Vehicle:
|
|
|
1313
1653
|
# Vehicle fuel level and range
|
|
1314
1654
|
@property
|
|
1315
1655
|
def electric_range(self) -> int:
|
|
1316
|
-
"""
|
|
1317
|
-
Return electric range.
|
|
1656
|
+
"""Return electric range.
|
|
1318
1657
|
|
|
1319
1658
|
:return:
|
|
1320
1659
|
"""
|
|
1321
|
-
if is_valid_path(
|
|
1322
|
-
|
|
1323
|
-
|
|
1660
|
+
if is_valid_path(
|
|
1661
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.electricRange"
|
|
1662
|
+
):
|
|
1663
|
+
return find_path(
|
|
1664
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.electricRange"
|
|
1665
|
+
)
|
|
1666
|
+
return find_path(
|
|
1667
|
+
self.attrs,
|
|
1668
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.remainingRange_km",
|
|
1669
|
+
)
|
|
1324
1670
|
|
|
1325
1671
|
@property
|
|
1326
1672
|
def electric_range_last_updated(self) -> datetime:
|
|
1327
1673
|
"""Return electric range last updated."""
|
|
1328
|
-
if is_valid_path(
|
|
1329
|
-
|
|
1330
|
-
|
|
1674
|
+
if is_valid_path(
|
|
1675
|
+
self.attrs,
|
|
1676
|
+
f"{Services.MEASUREMENTS}.rangeStatus.value.carCapturedTimestamp",
|
|
1677
|
+
):
|
|
1678
|
+
return find_path(
|
|
1679
|
+
self.attrs,
|
|
1680
|
+
f"{Services.MEASUREMENTS}.rangeStatus.value.carCapturedTimestamp",
|
|
1681
|
+
)
|
|
1682
|
+
return find_path(
|
|
1683
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carCapturedTimestamp"
|
|
1684
|
+
)
|
|
1331
1685
|
|
|
1332
1686
|
@property
|
|
1333
1687
|
def is_electric_range_supported(self) -> bool:
|
|
1334
|
-
"""
|
|
1335
|
-
Return true if electric range is supported.
|
|
1688
|
+
"""Return true if electric range is supported.
|
|
1336
1689
|
|
|
1337
1690
|
:return:
|
|
1338
1691
|
"""
|
|
1339
|
-
return is_valid_path(
|
|
1692
|
+
return is_valid_path(
|
|
1693
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.electricRange"
|
|
1694
|
+
) or (
|
|
1340
1695
|
self.is_car_type_electric
|
|
1341
|
-
and is_valid_path(
|
|
1696
|
+
and is_valid_path(
|
|
1697
|
+
self.attrs,
|
|
1698
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.remainingRange_km",
|
|
1699
|
+
)
|
|
1342
1700
|
)
|
|
1343
1701
|
|
|
1344
1702
|
@property
|
|
1345
1703
|
def combustion_range(self) -> int:
|
|
1346
|
-
"""
|
|
1347
|
-
Return combustion engine range.
|
|
1704
|
+
"""Return combustion engine range.
|
|
1348
1705
|
|
|
1349
1706
|
:return:
|
|
1350
1707
|
"""
|
|
1351
1708
|
DIESEL_RANGE = f"{Services.MEASUREMENTS}.rangeStatus.value.dieselRange"
|
|
1352
1709
|
GASOLINE_RANGE = f"{Services.MEASUREMENTS}.rangeStatus.value.gasolineRange"
|
|
1710
|
+
CNG_RANGE = f"{Services.MEASUREMENTS}.rangeStatus.value.cngRange"
|
|
1711
|
+
TOTAL_RANGE = f"{Services.MEASUREMENTS}.rangeStatus.value.totalRange_km"
|
|
1712
|
+
if is_valid_path(self.attrs, CNG_RANGE):
|
|
1713
|
+
return find_path(self.attrs, TOTAL_RANGE)
|
|
1353
1714
|
if is_valid_path(self.attrs, DIESEL_RANGE):
|
|
1354
1715
|
return find_path(self.attrs, DIESEL_RANGE)
|
|
1355
1716
|
if is_valid_path(self.attrs, GASOLINE_RANGE):
|
|
@@ -1359,83 +1720,179 @@ class Vehicle:
|
|
|
1359
1720
|
@property
|
|
1360
1721
|
def combustion_range_last_updated(self) -> datetime | None:
|
|
1361
1722
|
"""Return combustion engine range last updated."""
|
|
1362
|
-
return find_path(
|
|
1723
|
+
return find_path(
|
|
1724
|
+
self.attrs,
|
|
1725
|
+
f"{Services.MEASUREMENTS}.rangeStatus.value.carCapturedTimestamp",
|
|
1726
|
+
)
|
|
1363
1727
|
|
|
1364
1728
|
@property
|
|
1365
1729
|
def is_combustion_range_supported(self) -> bool:
|
|
1730
|
+
"""Return true if combustion range is supported, i.e. false for EVs.
|
|
1731
|
+
|
|
1732
|
+
:return:
|
|
1366
1733
|
"""
|
|
1367
|
-
|
|
1734
|
+
return (
|
|
1735
|
+
is_valid_path(
|
|
1736
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.dieselRange"
|
|
1737
|
+
)
|
|
1738
|
+
or is_valid_path(
|
|
1739
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.gasolineRange"
|
|
1740
|
+
)
|
|
1741
|
+
or is_valid_path(
|
|
1742
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.cngRange"
|
|
1743
|
+
)
|
|
1744
|
+
)
|
|
1745
|
+
|
|
1746
|
+
@property
|
|
1747
|
+
def fuel_range(self) -> int:
|
|
1748
|
+
"""Return fuel engine range.
|
|
1368
1749
|
|
|
1369
1750
|
:return:
|
|
1370
1751
|
"""
|
|
1371
|
-
|
|
1752
|
+
DIESEL_RANGE = f"{Services.MEASUREMENTS}.rangeStatus.value.dieselRange"
|
|
1753
|
+
GASOLINE_RANGE = f"{Services.MEASUREMENTS}.rangeStatus.value.gasolineRange"
|
|
1754
|
+
if is_valid_path(self.attrs, DIESEL_RANGE):
|
|
1755
|
+
return find_path(self.attrs, DIESEL_RANGE)
|
|
1756
|
+
if is_valid_path(self.attrs, GASOLINE_RANGE):
|
|
1757
|
+
return find_path(self.attrs, GASOLINE_RANGE)
|
|
1758
|
+
return -1
|
|
1759
|
+
|
|
1760
|
+
@property
|
|
1761
|
+
def fuel_range_last_updated(self) -> datetime | None:
|
|
1762
|
+
"""Return fuel engine range last updated."""
|
|
1763
|
+
return find_path(
|
|
1764
|
+
self.attrs,
|
|
1765
|
+
f"{Services.MEASUREMENTS}.rangeStatus.value.carCapturedTimestamp",
|
|
1766
|
+
)
|
|
1767
|
+
|
|
1768
|
+
@property
|
|
1769
|
+
def is_fuel_range_supported(self) -> bool:
|
|
1770
|
+
"""Return true if fuel range is supported, i.e. false for EVs.
|
|
1771
|
+
|
|
1772
|
+
:return:
|
|
1773
|
+
"""
|
|
1774
|
+
return is_valid_path(
|
|
1775
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.dieselRange"
|
|
1776
|
+
) or is_valid_path(
|
|
1372
1777
|
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.gasolineRange"
|
|
1373
1778
|
)
|
|
1374
1779
|
|
|
1375
1780
|
@property
|
|
1376
|
-
def
|
|
1781
|
+
def gas_range(self) -> int:
|
|
1782
|
+
"""Return gas engine range.
|
|
1783
|
+
|
|
1784
|
+
:return:
|
|
1785
|
+
"""
|
|
1786
|
+
CNG_RANGE = f"{Services.MEASUREMENTS}.rangeStatus.value.cngRange"
|
|
1787
|
+
if is_valid_path(self.attrs, CNG_RANGE):
|
|
1788
|
+
return find_path(self.attrs, CNG_RANGE)
|
|
1789
|
+
return -1
|
|
1790
|
+
|
|
1791
|
+
@property
|
|
1792
|
+
def gas_range_last_updated(self) -> datetime | None:
|
|
1793
|
+
"""Return gas engine range last updated."""
|
|
1794
|
+
return find_path(
|
|
1795
|
+
self.attrs,
|
|
1796
|
+
f"{Services.MEASUREMENTS}.rangeStatus.value.carCapturedTimestamp",
|
|
1797
|
+
)
|
|
1798
|
+
|
|
1799
|
+
@property
|
|
1800
|
+
def is_gas_range_supported(self) -> bool:
|
|
1801
|
+
"""Return true if gas range is supported, i.e. false for EVs.
|
|
1802
|
+
|
|
1803
|
+
:return:
|
|
1377
1804
|
"""
|
|
1378
|
-
|
|
1805
|
+
return is_valid_path(
|
|
1806
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.cngRange"
|
|
1807
|
+
)
|
|
1808
|
+
|
|
1809
|
+
@property
|
|
1810
|
+
def combined_range(self) -> int:
|
|
1811
|
+
"""Return combined range.
|
|
1379
1812
|
|
|
1380
1813
|
:return:
|
|
1381
1814
|
"""
|
|
1382
|
-
return find_path(
|
|
1815
|
+
return find_path(
|
|
1816
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.totalRange_km"
|
|
1817
|
+
)
|
|
1383
1818
|
|
|
1384
1819
|
@property
|
|
1385
1820
|
def combined_range_last_updated(self) -> datetime | None:
|
|
1386
1821
|
"""Return combined range last updated."""
|
|
1387
|
-
return find_path(
|
|
1822
|
+
return find_path(
|
|
1823
|
+
self.attrs,
|
|
1824
|
+
f"{Services.MEASUREMENTS}.rangeStatus.value.carCapturedTimestamp",
|
|
1825
|
+
)
|
|
1388
1826
|
|
|
1389
1827
|
@property
|
|
1390
1828
|
def is_combined_range_supported(self) -> bool:
|
|
1391
|
-
"""
|
|
1392
|
-
Return true if combined range is supported.
|
|
1829
|
+
"""Return true if combined range is supported.
|
|
1393
1830
|
|
|
1394
1831
|
:return:
|
|
1395
1832
|
"""
|
|
1396
|
-
if is_valid_path(
|
|
1397
|
-
|
|
1833
|
+
if is_valid_path(
|
|
1834
|
+
self.attrs, f"{Services.MEASUREMENTS}.rangeStatus.value.totalRange_km"
|
|
1835
|
+
):
|
|
1836
|
+
return (
|
|
1837
|
+
self.is_electric_range_supported and self.is_combustion_range_supported
|
|
1838
|
+
)
|
|
1398
1839
|
return False
|
|
1399
1840
|
|
|
1400
1841
|
@property
|
|
1401
1842
|
def battery_cruising_range(self) -> int:
|
|
1402
|
-
"""
|
|
1403
|
-
Return battery cruising range.
|
|
1843
|
+
"""Return battery cruising range.
|
|
1404
1844
|
|
|
1405
1845
|
:return:
|
|
1406
1846
|
"""
|
|
1407
|
-
return find_path(
|
|
1847
|
+
return find_path(
|
|
1848
|
+
self.attrs,
|
|
1849
|
+
f"{Services.CHARGING}.batteryStatus.value.cruisingRangeElectric_km",
|
|
1850
|
+
)
|
|
1408
1851
|
|
|
1409
1852
|
@property
|
|
1410
1853
|
def battery_cruising_range_last_updated(self) -> datetime | None:
|
|
1411
1854
|
"""Return battery cruising range last updated."""
|
|
1412
|
-
return find_path(
|
|
1855
|
+
return find_path(
|
|
1856
|
+
self.attrs, f"{Services.CHARGING}.batteryStatus.value.carCapturedTimestamp"
|
|
1857
|
+
)
|
|
1413
1858
|
|
|
1414
1859
|
@property
|
|
1415
1860
|
def is_battery_cruising_range_supported(self) -> bool:
|
|
1416
|
-
"""
|
|
1417
|
-
Return true if battery cruising range is supported.
|
|
1861
|
+
"""Return true if battery cruising range is supported.
|
|
1418
1862
|
|
|
1419
1863
|
:return:
|
|
1420
1864
|
"""
|
|
1421
|
-
return is_valid_path(
|
|
1865
|
+
return is_valid_path(
|
|
1866
|
+
self.attrs,
|
|
1867
|
+
f"{Services.CHARGING}.batteryStatus.value.cruisingRangeElectric_km",
|
|
1868
|
+
)
|
|
1422
1869
|
|
|
1423
1870
|
@property
|
|
1424
1871
|
def fuel_level(self) -> int:
|
|
1425
|
-
"""
|
|
1426
|
-
Return fuel level.
|
|
1872
|
+
"""Return fuel level.
|
|
1427
1873
|
|
|
1428
1874
|
:return:
|
|
1429
1875
|
"""
|
|
1430
1876
|
fuel_level_pct = ""
|
|
1431
|
-
if
|
|
1877
|
+
if (
|
|
1878
|
+
is_valid_path(
|
|
1879
|
+
self.attrs,
|
|
1880
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.currentFuelLevel_pct",
|
|
1881
|
+
)
|
|
1882
|
+
and not self.is_primary_drive_gas()
|
|
1883
|
+
):
|
|
1432
1884
|
fuel_level_pct = find_path(
|
|
1433
|
-
self.attrs,
|
|
1885
|
+
self.attrs,
|
|
1886
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.currentFuelLevel_pct",
|
|
1434
1887
|
)
|
|
1435
1888
|
|
|
1436
|
-
if is_valid_path(
|
|
1889
|
+
if is_valid_path(
|
|
1890
|
+
self.attrs,
|
|
1891
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.currentFuelLevel_pct",
|
|
1892
|
+
):
|
|
1437
1893
|
fuel_level_pct = find_path(
|
|
1438
|
-
self.attrs,
|
|
1894
|
+
self.attrs,
|
|
1895
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.currentFuelLevel_pct",
|
|
1439
1896
|
)
|
|
1440
1897
|
return fuel_level_pct
|
|
1441
1898
|
|
|
@@ -1443,58 +1900,162 @@ class Vehicle:
|
|
|
1443
1900
|
def fuel_level_last_updated(self) -> datetime:
|
|
1444
1901
|
"""Return fuel level last updated."""
|
|
1445
1902
|
fuel_level_lastupdated = ""
|
|
1446
|
-
if is_valid_path(
|
|
1903
|
+
if is_valid_path(
|
|
1904
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carCapturedTimestamp"
|
|
1905
|
+
):
|
|
1447
1906
|
fuel_level_lastupdated = find_path(
|
|
1448
|
-
self.attrs,
|
|
1907
|
+
self.attrs,
|
|
1908
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.carCapturedTimestamp",
|
|
1449
1909
|
)
|
|
1450
1910
|
|
|
1451
|
-
if is_valid_path(
|
|
1911
|
+
if is_valid_path(
|
|
1912
|
+
self.attrs,
|
|
1913
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carCapturedTimestamp",
|
|
1914
|
+
):
|
|
1452
1915
|
fuel_level_lastupdated = find_path(
|
|
1453
|
-
self.attrs,
|
|
1916
|
+
self.attrs,
|
|
1917
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carCapturedTimestamp",
|
|
1454
1918
|
)
|
|
1455
1919
|
return fuel_level_lastupdated
|
|
1456
1920
|
|
|
1457
1921
|
@property
|
|
1458
1922
|
def is_fuel_level_supported(self) -> bool:
|
|
1923
|
+
"""Return true if fuel level reporting is supported.
|
|
1924
|
+
|
|
1925
|
+
:return:
|
|
1459
1926
|
"""
|
|
1460
|
-
|
|
1927
|
+
return (
|
|
1928
|
+
is_valid_path(
|
|
1929
|
+
self.attrs,
|
|
1930
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.currentFuelLevel_pct",
|
|
1931
|
+
)
|
|
1932
|
+
and not self.is_primary_drive_gas()
|
|
1933
|
+
) or is_valid_path(
|
|
1934
|
+
self.attrs,
|
|
1935
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.currentFuelLevel_pct",
|
|
1936
|
+
)
|
|
1937
|
+
|
|
1938
|
+
@property
|
|
1939
|
+
def gas_level(self) -> int:
|
|
1940
|
+
"""Return gas level.
|
|
1461
1941
|
|
|
1462
1942
|
:return:
|
|
1463
1943
|
"""
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1944
|
+
gas_level_pct = ""
|
|
1945
|
+
if (
|
|
1946
|
+
is_valid_path(
|
|
1947
|
+
self.attrs,
|
|
1948
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.currentFuelLevel_pct",
|
|
1949
|
+
)
|
|
1950
|
+
and self.is_primary_drive_gas()
|
|
1951
|
+
):
|
|
1952
|
+
gas_level_pct = find_path(
|
|
1953
|
+
self.attrs,
|
|
1954
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.currentFuelLevel_pct",
|
|
1955
|
+
)
|
|
1956
|
+
|
|
1957
|
+
if is_valid_path(
|
|
1958
|
+
self.attrs,
|
|
1959
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.currentCngLevel_pct",
|
|
1960
|
+
):
|
|
1961
|
+
gas_level_pct = find_path(
|
|
1962
|
+
self.attrs,
|
|
1963
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.currentCngLevel_pct",
|
|
1964
|
+
)
|
|
1965
|
+
return gas_level_pct
|
|
1467
1966
|
|
|
1468
1967
|
@property
|
|
1469
|
-
def
|
|
1968
|
+
def gas_level_last_updated(self) -> datetime:
|
|
1969
|
+
"""Return gas level last updated."""
|
|
1970
|
+
gas_level_lastupdated = ""
|
|
1971
|
+
if (
|
|
1972
|
+
is_valid_path(
|
|
1973
|
+
self.attrs,
|
|
1974
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.carCapturedTimestamp",
|
|
1975
|
+
)
|
|
1976
|
+
and self.is_primary_drive_gas()
|
|
1977
|
+
):
|
|
1978
|
+
gas_level_lastupdated = find_path(
|
|
1979
|
+
self.attrs,
|
|
1980
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.carCapturedTimestamp",
|
|
1981
|
+
)
|
|
1982
|
+
|
|
1983
|
+
if is_valid_path(
|
|
1984
|
+
self.attrs,
|
|
1985
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carCapturedTimestamp",
|
|
1986
|
+
):
|
|
1987
|
+
gas_level_lastupdated = find_path(
|
|
1988
|
+
self.attrs,
|
|
1989
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carCapturedTimestamp",
|
|
1990
|
+
)
|
|
1991
|
+
return gas_level_lastupdated
|
|
1992
|
+
|
|
1993
|
+
@property
|
|
1994
|
+
def is_gas_level_supported(self) -> bool:
|
|
1995
|
+
"""Return true if gas level reporting is supported.
|
|
1996
|
+
|
|
1997
|
+
:return:
|
|
1470
1998
|
"""
|
|
1471
|
-
|
|
1999
|
+
return (
|
|
2000
|
+
is_valid_path(
|
|
2001
|
+
self.attrs,
|
|
2002
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.currentFuelLevel_pct",
|
|
2003
|
+
)
|
|
2004
|
+
and self.is_primary_drive_gas()
|
|
2005
|
+
) or is_valid_path(
|
|
2006
|
+
self.attrs,
|
|
2007
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.currentCngLevel_pct",
|
|
2008
|
+
)
|
|
2009
|
+
|
|
2010
|
+
@property
|
|
2011
|
+
def car_type(self) -> str:
|
|
2012
|
+
"""Return car type.
|
|
1472
2013
|
|
|
1473
2014
|
:return:
|
|
1474
2015
|
"""
|
|
1475
|
-
if is_valid_path(
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
return find_path(
|
|
2016
|
+
if is_valid_path(
|
|
2017
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
2018
|
+
):
|
|
2019
|
+
return find_path(
|
|
2020
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
2021
|
+
).capitalize()
|
|
2022
|
+
if is_valid_path(
|
|
2023
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
2024
|
+
):
|
|
2025
|
+
return find_path(
|
|
2026
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
2027
|
+
).capitalize()
|
|
1479
2028
|
return "Unknown"
|
|
1480
2029
|
|
|
1481
2030
|
@property
|
|
1482
2031
|
def car_type_last_updated(self) -> datetime | None:
|
|
1483
2032
|
"""Return car type last updated."""
|
|
1484
|
-
if is_valid_path(
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
return find_path(
|
|
2033
|
+
if is_valid_path(
|
|
2034
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carCapturedTimestamp"
|
|
2035
|
+
):
|
|
2036
|
+
return find_path(
|
|
2037
|
+
self.attrs,
|
|
2038
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.carCapturedTimestamp",
|
|
2039
|
+
)
|
|
2040
|
+
if is_valid_path(
|
|
2041
|
+
self.attrs,
|
|
2042
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carCapturedTimestamp",
|
|
2043
|
+
):
|
|
2044
|
+
return find_path(
|
|
2045
|
+
self.attrs,
|
|
2046
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carCapturedTimestamp",
|
|
2047
|
+
)
|
|
1488
2048
|
return None
|
|
1489
2049
|
|
|
1490
2050
|
@property
|
|
1491
2051
|
def is_car_type_supported(self) -> bool:
|
|
1492
|
-
"""
|
|
1493
|
-
Return true if car type is supported.
|
|
2052
|
+
"""Return true if car type is supported.
|
|
1494
2053
|
|
|
1495
2054
|
:return:
|
|
1496
2055
|
"""
|
|
1497
|
-
return is_valid_path(
|
|
2056
|
+
return is_valid_path(
|
|
2057
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
2058
|
+
) or is_valid_path(
|
|
1498
2059
|
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
1499
2060
|
)
|
|
1500
2061
|
|
|
@@ -1502,109 +2063,167 @@ class Vehicle:
|
|
|
1502
2063
|
@property
|
|
1503
2064
|
def climatisation_target_temperature(self) -> float | None:
|
|
1504
2065
|
"""Return the target temperature from climater."""
|
|
1505
|
-
# TODO should we handle Fahrenheit??
|
|
1506
|
-
return float(
|
|
2066
|
+
# TODO should we handle Fahrenheit?? # pylint: disable=fixme
|
|
2067
|
+
return float(
|
|
2068
|
+
find_path(
|
|
2069
|
+
self.attrs,
|
|
2070
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.targetTemperature_C",
|
|
2071
|
+
)
|
|
2072
|
+
)
|
|
1507
2073
|
|
|
1508
2074
|
@property
|
|
1509
2075
|
def climatisation_target_temperature_last_updated(self) -> datetime:
|
|
1510
2076
|
"""Return the target temperature from climater last updated."""
|
|
1511
|
-
return find_path(
|
|
2077
|
+
return find_path(
|
|
2078
|
+
self.attrs,
|
|
2079
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.carCapturedTimestamp",
|
|
2080
|
+
)
|
|
1512
2081
|
|
|
1513
2082
|
@property
|
|
1514
2083
|
def is_climatisation_target_temperature_supported(self) -> bool:
|
|
1515
2084
|
"""Return true if climatisation target temperature is supported."""
|
|
1516
|
-
return is_valid_path(
|
|
2085
|
+
return is_valid_path(
|
|
2086
|
+
self.attrs,
|
|
2087
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.targetTemperature_C",
|
|
2088
|
+
)
|
|
1517
2089
|
|
|
1518
2090
|
@property
|
|
1519
2091
|
def climatisation_without_external_power(self):
|
|
1520
2092
|
"""Return state of climatisation from battery power."""
|
|
1521
2093
|
return find_path(
|
|
1522
|
-
self.attrs,
|
|
2094
|
+
self.attrs,
|
|
2095
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.climatisationWithoutExternalPower",
|
|
1523
2096
|
)
|
|
1524
2097
|
|
|
1525
2098
|
@property
|
|
1526
2099
|
def climatisation_without_external_power_last_updated(self) -> datetime:
|
|
1527
2100
|
"""Return state of climatisation from battery power last updated."""
|
|
1528
|
-
return find_path(
|
|
2101
|
+
return find_path(
|
|
2102
|
+
self.attrs,
|
|
2103
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.carCapturedTimestamp",
|
|
2104
|
+
)
|
|
1529
2105
|
|
|
1530
2106
|
@property
|
|
1531
2107
|
def is_climatisation_without_external_power_supported(self) -> bool:
|
|
1532
2108
|
"""Return true if climatisation on battery power is supported."""
|
|
1533
2109
|
return is_valid_path(
|
|
1534
|
-
self.attrs,
|
|
2110
|
+
self.attrs,
|
|
2111
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.climatisationWithoutExternalPower",
|
|
1535
2112
|
)
|
|
1536
2113
|
|
|
1537
2114
|
@property
|
|
1538
2115
|
def auxiliary_air_conditioning(self):
|
|
1539
2116
|
"""Return state of auxiliary air conditioning."""
|
|
1540
|
-
return find_path(
|
|
2117
|
+
return find_path(
|
|
2118
|
+
self.attrs,
|
|
2119
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.climatizationAtUnlock",
|
|
2120
|
+
)
|
|
1541
2121
|
|
|
1542
2122
|
@property
|
|
1543
2123
|
def auxiliary_air_conditioning_last_updated(self) -> datetime:
|
|
1544
2124
|
"""Return state of auxiliary air conditioning last updated."""
|
|
1545
|
-
return find_path(
|
|
2125
|
+
return find_path(
|
|
2126
|
+
self.attrs,
|
|
2127
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.carCapturedTimestamp",
|
|
2128
|
+
)
|
|
1546
2129
|
|
|
1547
2130
|
@property
|
|
1548
2131
|
def is_auxiliary_air_conditioning_supported(self) -> bool:
|
|
1549
2132
|
"""Return true if auxiliary air conditioning is supported."""
|
|
1550
|
-
return is_valid_path(
|
|
2133
|
+
return is_valid_path(
|
|
2134
|
+
self.attrs,
|
|
2135
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.climatizationAtUnlock",
|
|
2136
|
+
)
|
|
1551
2137
|
|
|
1552
2138
|
@property
|
|
1553
2139
|
def automatic_window_heating(self):
|
|
1554
2140
|
"""Return state of automatic window heating."""
|
|
1555
|
-
return find_path(
|
|
2141
|
+
return find_path(
|
|
2142
|
+
self.attrs,
|
|
2143
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.windowHeatingEnabled",
|
|
2144
|
+
)
|
|
1556
2145
|
|
|
1557
2146
|
@property
|
|
1558
2147
|
def automatic_window_heating_last_updated(self) -> datetime:
|
|
1559
2148
|
"""Return state of automatic window heating last updated."""
|
|
1560
|
-
return find_path(
|
|
2149
|
+
return find_path(
|
|
2150
|
+
self.attrs,
|
|
2151
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.carCapturedTimestamp",
|
|
2152
|
+
)
|
|
1561
2153
|
|
|
1562
2154
|
@property
|
|
1563
2155
|
def is_automatic_window_heating_supported(self) -> bool:
|
|
1564
2156
|
"""Return true if automatic window heating is supported."""
|
|
1565
|
-
return is_valid_path(
|
|
2157
|
+
return is_valid_path(
|
|
2158
|
+
self.attrs,
|
|
2159
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.windowHeatingEnabled",
|
|
2160
|
+
)
|
|
1566
2161
|
|
|
1567
2162
|
@property
|
|
1568
2163
|
def zone_front_left(self):
|
|
1569
2164
|
"""Return state of zone front left."""
|
|
1570
|
-
return find_path(
|
|
2165
|
+
return find_path(
|
|
2166
|
+
self.attrs,
|
|
2167
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.zoneFrontLeftEnabled",
|
|
2168
|
+
)
|
|
1571
2169
|
|
|
1572
2170
|
@property
|
|
1573
2171
|
def zone_front_left_last_updated(self) -> datetime:
|
|
1574
2172
|
"""Return state of zone front left last updated."""
|
|
1575
|
-
return find_path(
|
|
2173
|
+
return find_path(
|
|
2174
|
+
self.attrs,
|
|
2175
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.carCapturedTimestamp",
|
|
2176
|
+
)
|
|
1576
2177
|
|
|
1577
2178
|
@property
|
|
1578
2179
|
def is_zone_front_left_supported(self) -> bool:
|
|
1579
2180
|
"""Return true if zone front left is supported."""
|
|
1580
|
-
return is_valid_path(
|
|
2181
|
+
return is_valid_path(
|
|
2182
|
+
self.attrs,
|
|
2183
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.zoneFrontLeftEnabled",
|
|
2184
|
+
)
|
|
1581
2185
|
|
|
1582
2186
|
@property
|
|
1583
2187
|
def zone_front_right(self):
|
|
1584
2188
|
"""Return state of zone front left."""
|
|
1585
|
-
return find_path(
|
|
2189
|
+
return find_path(
|
|
2190
|
+
self.attrs,
|
|
2191
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.zoneFrontRightEnabled",
|
|
2192
|
+
)
|
|
1586
2193
|
|
|
1587
2194
|
@property
|
|
1588
2195
|
def zone_front_right_last_updated(self) -> datetime:
|
|
1589
2196
|
"""Return state of zone front left last updated."""
|
|
1590
|
-
return find_path(
|
|
2197
|
+
return find_path(
|
|
2198
|
+
self.attrs,
|
|
2199
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.carCapturedTimestamp",
|
|
2200
|
+
)
|
|
1591
2201
|
|
|
1592
2202
|
@property
|
|
1593
2203
|
def is_zone_front_right_supported(self) -> bool:
|
|
1594
2204
|
"""Return true if zone front left is supported."""
|
|
1595
|
-
return is_valid_path(
|
|
2205
|
+
return is_valid_path(
|
|
2206
|
+
self.attrs,
|
|
2207
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.zoneFrontRightEnabled",
|
|
2208
|
+
)
|
|
1596
2209
|
|
|
1597
2210
|
# Climatisation, electric
|
|
1598
2211
|
@property
|
|
1599
2212
|
def electric_climatisation(self) -> bool:
|
|
1600
2213
|
"""Return status of climatisation."""
|
|
1601
|
-
status = find_path(
|
|
2214
|
+
status = find_path(
|
|
2215
|
+
self.attrs,
|
|
2216
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.climatisationState",
|
|
2217
|
+
)
|
|
1602
2218
|
return status in ["ventilation", "heating", "cooling", "on"]
|
|
1603
2219
|
|
|
1604
2220
|
@property
|
|
1605
2221
|
def electric_climatisation_last_updated(self) -> datetime:
|
|
1606
2222
|
"""Return status of climatisation last updated."""
|
|
1607
|
-
return find_path(
|
|
2223
|
+
return find_path(
|
|
2224
|
+
self.attrs,
|
|
2225
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.carCapturedTimestamp",
|
|
2226
|
+
)
|
|
1608
2227
|
|
|
1609
2228
|
@property
|
|
1610
2229
|
def is_electric_climatisation_supported(self) -> bool:
|
|
@@ -1623,32 +2242,45 @@ class Vehicle:
|
|
|
1623
2242
|
def electric_remaining_climatisation_time(self) -> int:
|
|
1624
2243
|
"""Return remaining climatisation time for electric climatisation."""
|
|
1625
2244
|
return find_path(
|
|
1626
|
-
self.attrs,
|
|
2245
|
+
self.attrs,
|
|
2246
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.remainingClimatisationTime_min",
|
|
1627
2247
|
)
|
|
1628
2248
|
|
|
1629
2249
|
@property
|
|
1630
2250
|
def electric_remaining_climatisation_time_last_updated(self) -> bool:
|
|
1631
2251
|
"""Return status of electric climatisation remaining climatisation time last updated."""
|
|
1632
|
-
return find_path(
|
|
2252
|
+
return find_path(
|
|
2253
|
+
self.attrs,
|
|
2254
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.carCapturedTimestamp",
|
|
2255
|
+
)
|
|
1633
2256
|
|
|
1634
2257
|
@property
|
|
1635
2258
|
def is_electric_remaining_climatisation_time_supported(self) -> bool:
|
|
1636
2259
|
"""Return true if electric climatisation remaining climatisation time is supported."""
|
|
1637
2260
|
return is_valid_path(
|
|
1638
|
-
self.attrs,
|
|
2261
|
+
self.attrs,
|
|
2262
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.remainingClimatisationTime_min",
|
|
1639
2263
|
)
|
|
1640
2264
|
|
|
1641
2265
|
@property
|
|
1642
2266
|
def auxiliary_climatisation(self) -> bool:
|
|
1643
2267
|
"""Return status of auxiliary climatisation."""
|
|
1644
2268
|
climatisation_state = None
|
|
1645
|
-
if is_valid_path(
|
|
2269
|
+
if is_valid_path(
|
|
2270
|
+
self.attrs,
|
|
2271
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.climatisationState",
|
|
2272
|
+
):
|
|
1646
2273
|
climatisation_state = find_path(
|
|
1647
|
-
self.attrs,
|
|
2274
|
+
self.attrs,
|
|
2275
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.climatisationState",
|
|
1648
2276
|
)
|
|
1649
|
-
if is_valid_path(
|
|
2277
|
+
if is_valid_path(
|
|
2278
|
+
self.attrs,
|
|
2279
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.climatisationState",
|
|
2280
|
+
):
|
|
1650
2281
|
climatisation_state = find_path(
|
|
1651
|
-
self.attrs,
|
|
2282
|
+
self.attrs,
|
|
2283
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.climatisationState",
|
|
1652
2284
|
)
|
|
1653
2285
|
if climatisation_state in ["heating", "heatingAuxiliary", "on"]:
|
|
1654
2286
|
return True
|
|
@@ -1657,80 +2289,115 @@ class Vehicle:
|
|
|
1657
2289
|
@property
|
|
1658
2290
|
def auxiliary_climatisation_last_updated(self) -> datetime:
|
|
1659
2291
|
"""Return status of auxiliary climatisation last updated."""
|
|
1660
|
-
if is_valid_path(
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
2292
|
+
if is_valid_path(
|
|
2293
|
+
self.attrs,
|
|
2294
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.carCapturedTimestamp",
|
|
2295
|
+
):
|
|
2296
|
+
return find_path(
|
|
2297
|
+
self.attrs,
|
|
2298
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.carCapturedTimestamp",
|
|
2299
|
+
)
|
|
2300
|
+
if is_valid_path(
|
|
2301
|
+
self.attrs,
|
|
2302
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.carCapturedTimestamp",
|
|
2303
|
+
):
|
|
2304
|
+
return find_path(
|
|
2305
|
+
self.attrs,
|
|
2306
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.carCapturedTimestamp",
|
|
2307
|
+
)
|
|
1664
2308
|
return None
|
|
1665
2309
|
|
|
1666
2310
|
@property
|
|
1667
2311
|
def is_auxiliary_climatisation_supported(self) -> bool:
|
|
1668
2312
|
"""Return true if vehicle has auxiliary climatisation."""
|
|
1669
|
-
if is_valid_path(
|
|
2313
|
+
if is_valid_path(
|
|
2314
|
+
self.attrs,
|
|
2315
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.climatisationState",
|
|
2316
|
+
):
|
|
1670
2317
|
return True
|
|
1671
|
-
if is_valid_path(
|
|
1672
|
-
|
|
2318
|
+
if is_valid_path(
|
|
2319
|
+
self.attrs, f"{Services.USER_CAPABILITIES}.capabilitiesStatus.value"
|
|
2320
|
+
):
|
|
2321
|
+
capabilities = find_path(
|
|
2322
|
+
self.attrs, f"{Services.USER_CAPABILITIES}.capabilitiesStatus.value"
|
|
2323
|
+
)
|
|
1673
2324
|
for capability in capabilities:
|
|
1674
2325
|
if capability.get("id", None) == "hybridCarAuxiliaryHeating":
|
|
1675
2326
|
if 1007 in capability.get("status", []):
|
|
1676
2327
|
return False
|
|
1677
|
-
|
|
1678
|
-
return True
|
|
2328
|
+
return True
|
|
1679
2329
|
return False
|
|
1680
2330
|
|
|
1681
2331
|
@property
|
|
1682
2332
|
def auxiliary_duration(self) -> int:
|
|
1683
2333
|
"""Return heating duration for auxiliary heater."""
|
|
1684
2334
|
return find_path(
|
|
1685
|
-
self.attrs,
|
|
2335
|
+
self.attrs,
|
|
2336
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.auxiliaryHeatingSettings.duration_min",
|
|
1686
2337
|
)
|
|
1687
2338
|
|
|
1688
2339
|
@property
|
|
1689
2340
|
def auxiliary_duration_last_updated(self) -> bool:
|
|
1690
2341
|
"""Return status of auxiliary heater last updated."""
|
|
1691
|
-
return find_path(
|
|
2342
|
+
return find_path(
|
|
2343
|
+
self.attrs,
|
|
2344
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.carCapturedTimestamp",
|
|
2345
|
+
)
|
|
1692
2346
|
|
|
1693
2347
|
@property
|
|
1694
2348
|
def is_auxiliary_duration_supported(self) -> bool:
|
|
1695
2349
|
"""Return true if auxiliary heater is supported."""
|
|
1696
2350
|
return is_valid_path(
|
|
1697
|
-
self.attrs,
|
|
2351
|
+
self.attrs,
|
|
2352
|
+
f"{Services.CLIMATISATION}.climatisationSettings.value.auxiliaryHeatingSettings.duration_min",
|
|
1698
2353
|
)
|
|
1699
2354
|
|
|
1700
2355
|
@property
|
|
1701
2356
|
def auxiliary_remaining_climatisation_time(self) -> int:
|
|
1702
2357
|
"""Return remaining climatisation time for auxiliary heater."""
|
|
1703
2358
|
return find_path(
|
|
1704
|
-
self.attrs,
|
|
2359
|
+
self.attrs,
|
|
2360
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.remainingClimatisationTime_min",
|
|
1705
2361
|
)
|
|
1706
2362
|
|
|
1707
2363
|
@property
|
|
1708
2364
|
def auxiliary_remaining_climatisation_time_last_updated(self) -> bool:
|
|
1709
2365
|
"""Return status of auxiliary heater remaining climatisation time last updated."""
|
|
1710
|
-
return find_path(
|
|
2366
|
+
return find_path(
|
|
2367
|
+
self.attrs,
|
|
2368
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.carCapturedTimestamp",
|
|
2369
|
+
)
|
|
1711
2370
|
|
|
1712
2371
|
@property
|
|
1713
2372
|
def is_auxiliary_remaining_climatisation_time_supported(self) -> bool:
|
|
1714
2373
|
"""Return true if auxiliary heater remaining climatisation time is supported."""
|
|
1715
2374
|
return is_valid_path(
|
|
1716
|
-
self.attrs,
|
|
2375
|
+
self.attrs,
|
|
2376
|
+
f"{Services.CLIMATISATION}.auxiliaryHeatingStatus.value.remainingClimatisationTime_min",
|
|
1717
2377
|
)
|
|
1718
2378
|
|
|
1719
2379
|
@property
|
|
1720
2380
|
def is_climatisation_supported(self) -> bool:
|
|
1721
2381
|
"""Return true if climatisation has State."""
|
|
1722
|
-
return is_valid_path(
|
|
2382
|
+
return is_valid_path(
|
|
2383
|
+
self.attrs,
|
|
2384
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.climatisationState",
|
|
2385
|
+
)
|
|
1723
2386
|
|
|
1724
2387
|
@property
|
|
1725
2388
|
def is_climatisation_supported_last_updated(self) -> datetime:
|
|
1726
2389
|
"""Return attribute last updated timestamp."""
|
|
1727
|
-
return find_path(
|
|
2390
|
+
return find_path(
|
|
2391
|
+
self.attrs,
|
|
2392
|
+
f"{Services.CLIMATISATION}.climatisationStatus.value.carCapturedTimestamp",
|
|
2393
|
+
)
|
|
1728
2394
|
|
|
1729
2395
|
@property
|
|
1730
2396
|
def window_heater_front(self) -> bool:
|
|
1731
2397
|
"""Return status of front window heater."""
|
|
1732
2398
|
window_heating_status = find_path(
|
|
1733
|
-
self.attrs,
|
|
2399
|
+
self.attrs,
|
|
2400
|
+
f"{Services.CLIMATISATION}.windowHeatingStatus.value.windowHeatingStatus",
|
|
1734
2401
|
)
|
|
1735
2402
|
for window_heating_state in window_heating_status:
|
|
1736
2403
|
if window_heating_state["windowLocation"] == "front":
|
|
@@ -1741,18 +2408,25 @@ class Vehicle:
|
|
|
1741
2408
|
@property
|
|
1742
2409
|
def window_heater_front_last_updated(self) -> datetime:
|
|
1743
2410
|
"""Return front window heater last updated."""
|
|
1744
|
-
return find_path(
|
|
2411
|
+
return find_path(
|
|
2412
|
+
self.attrs,
|
|
2413
|
+
f"{Services.CLIMATISATION}.windowHeatingStatus.value.carCapturedTimestamp",
|
|
2414
|
+
)
|
|
1745
2415
|
|
|
1746
2416
|
@property
|
|
1747
2417
|
def is_window_heater_front_supported(self) -> bool:
|
|
1748
2418
|
"""Return true if vehicle has heater."""
|
|
1749
|
-
return is_valid_path(
|
|
2419
|
+
return is_valid_path(
|
|
2420
|
+
self.attrs,
|
|
2421
|
+
f"{Services.CLIMATISATION}.windowHeatingStatus.value.windowHeatingStatus",
|
|
2422
|
+
)
|
|
1750
2423
|
|
|
1751
2424
|
@property
|
|
1752
2425
|
def window_heater_back(self) -> bool:
|
|
1753
2426
|
"""Return status of rear window heater."""
|
|
1754
2427
|
window_heating_status = find_path(
|
|
1755
|
-
self.attrs,
|
|
2428
|
+
self.attrs,
|
|
2429
|
+
f"{Services.CLIMATISATION}.windowHeatingStatus.value.windowHeatingStatus",
|
|
1756
2430
|
)
|
|
1757
2431
|
for window_heating_state in window_heating_status:
|
|
1758
2432
|
if window_heating_state["windowLocation"] == "rear":
|
|
@@ -1763,12 +2437,18 @@ class Vehicle:
|
|
|
1763
2437
|
@property
|
|
1764
2438
|
def window_heater_back_last_updated(self) -> datetime:
|
|
1765
2439
|
"""Return front window heater last updated."""
|
|
1766
|
-
return find_path(
|
|
2440
|
+
return find_path(
|
|
2441
|
+
self.attrs,
|
|
2442
|
+
f"{Services.CLIMATISATION}.windowHeatingStatus.value.carCapturedTimestamp",
|
|
2443
|
+
)
|
|
1767
2444
|
|
|
1768
2445
|
@property
|
|
1769
2446
|
def is_window_heater_back_supported(self) -> bool:
|
|
1770
2447
|
"""Return true if vehicle has heater."""
|
|
1771
|
-
return is_valid_path(
|
|
2448
|
+
return is_valid_path(
|
|
2449
|
+
self.attrs,
|
|
2450
|
+
f"{Services.CLIMATISATION}.windowHeatingStatus.value.windowHeatingStatus",
|
|
2451
|
+
)
|
|
1772
2452
|
|
|
1773
2453
|
@property
|
|
1774
2454
|
def window_heater(self) -> bool:
|
|
@@ -1784,29 +2464,50 @@ class Vehicle:
|
|
|
1784
2464
|
def is_window_heater_supported(self) -> bool:
|
|
1785
2465
|
"""Return true if vehicle has heater."""
|
|
1786
2466
|
# ID models detection
|
|
1787
|
-
if
|
|
2467
|
+
if (
|
|
2468
|
+
self._services.get(Services.PARAMETERS, {}).get(
|
|
2469
|
+
"supportsStartWindowHeating", "false"
|
|
2470
|
+
)
|
|
2471
|
+
== "true"
|
|
2472
|
+
):
|
|
1788
2473
|
return True
|
|
1789
2474
|
# "Legacy" models detection
|
|
1790
|
-
parameters = self._services.get(Services.CLIMATISATION, {}).get(
|
|
2475
|
+
parameters = self._services.get(Services.CLIMATISATION, {}).get(
|
|
2476
|
+
"parameters", None
|
|
2477
|
+
)
|
|
1791
2478
|
if parameters:
|
|
1792
2479
|
for parameter in parameters:
|
|
1793
|
-
if
|
|
2480
|
+
if (
|
|
2481
|
+
parameter["key"] == "supportsStartWindowHeating"
|
|
2482
|
+
and parameter["value"] == "true"
|
|
2483
|
+
):
|
|
1794
2484
|
return True
|
|
1795
2485
|
return False
|
|
1796
2486
|
|
|
1797
2487
|
# Windows
|
|
1798
2488
|
@property
|
|
1799
2489
|
def windows_closed(self) -> bool:
|
|
1800
|
-
"""
|
|
1801
|
-
Return true if all supported windows are closed.
|
|
2490
|
+
"""Return true if all supported windows are closed.
|
|
1802
2491
|
|
|
1803
2492
|
:return:
|
|
1804
2493
|
"""
|
|
1805
2494
|
return (
|
|
1806
|
-
(
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
2495
|
+
(
|
|
2496
|
+
not self.is_window_closed_left_front_supported
|
|
2497
|
+
or self.window_closed_left_front
|
|
2498
|
+
)
|
|
2499
|
+
and (
|
|
2500
|
+
not self.is_window_closed_left_back_supported
|
|
2501
|
+
or self.window_closed_left_back
|
|
2502
|
+
)
|
|
2503
|
+
and (
|
|
2504
|
+
not self.is_window_closed_right_front_supported
|
|
2505
|
+
or self.window_closed_right_front
|
|
2506
|
+
)
|
|
2507
|
+
and (
|
|
2508
|
+
not self.is_window_closed_right_back_supported
|
|
2509
|
+
or self.window_closed_right_back
|
|
2510
|
+
)
|
|
1810
2511
|
)
|
|
1811
2512
|
|
|
1812
2513
|
@property
|
|
@@ -1826,15 +2527,17 @@ class Vehicle:
|
|
|
1826
2527
|
|
|
1827
2528
|
@property
|
|
1828
2529
|
def window_closed_left_front(self) -> bool:
|
|
1829
|
-
"""
|
|
1830
|
-
Return left front window closed state.
|
|
2530
|
+
"""Return left front window closed state.
|
|
1831
2531
|
|
|
1832
2532
|
:return:
|
|
1833
2533
|
"""
|
|
1834
2534
|
windows = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows")
|
|
1835
2535
|
for window in windows:
|
|
1836
2536
|
if window["name"] == "frontLeft":
|
|
1837
|
-
if not any(
|
|
2537
|
+
if not any(
|
|
2538
|
+
valid_status in window["status"]
|
|
2539
|
+
for valid_status in P.VALID_WINDOW_STATUS
|
|
2540
|
+
):
|
|
1838
2541
|
return None
|
|
1839
2542
|
return "closed" in window["status"]
|
|
1840
2543
|
return False
|
|
@@ -1842,29 +2545,38 @@ class Vehicle:
|
|
|
1842
2545
|
@property
|
|
1843
2546
|
def window_closed_left_front_last_updated(self) -> datetime:
|
|
1844
2547
|
"""Return attribute last updated timestamp."""
|
|
1845
|
-
return find_path(
|
|
2548
|
+
return find_path(
|
|
2549
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2550
|
+
)
|
|
1846
2551
|
|
|
1847
2552
|
@property
|
|
1848
2553
|
def is_window_closed_left_front_supported(self) -> bool:
|
|
1849
2554
|
"""Return true if supported."""
|
|
1850
2555
|
if is_valid_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"):
|
|
1851
|
-
windows = find_path(
|
|
2556
|
+
windows = find_path(
|
|
2557
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"
|
|
2558
|
+
)
|
|
1852
2559
|
for window in windows:
|
|
1853
|
-
if
|
|
2560
|
+
if (
|
|
2561
|
+
window["name"] == "frontLeft"
|
|
2562
|
+
and "unsupported" not in window["status"]
|
|
2563
|
+
):
|
|
1854
2564
|
return True
|
|
1855
2565
|
return False
|
|
1856
2566
|
|
|
1857
2567
|
@property
|
|
1858
2568
|
def window_closed_right_front(self) -> bool:
|
|
1859
|
-
"""
|
|
1860
|
-
Return right front window closed state.
|
|
2569
|
+
"""Return right front window closed state.
|
|
1861
2570
|
|
|
1862
2571
|
:return:
|
|
1863
2572
|
"""
|
|
1864
2573
|
windows = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows")
|
|
1865
2574
|
for window in windows:
|
|
1866
2575
|
if window["name"] == "frontRight":
|
|
1867
|
-
if not any(
|
|
2576
|
+
if not any(
|
|
2577
|
+
valid_status in window["status"]
|
|
2578
|
+
for valid_status in P.VALID_WINDOW_STATUS
|
|
2579
|
+
):
|
|
1868
2580
|
return None
|
|
1869
2581
|
return "closed" in window["status"]
|
|
1870
2582
|
return False
|
|
@@ -1872,29 +2584,38 @@ class Vehicle:
|
|
|
1872
2584
|
@property
|
|
1873
2585
|
def window_closed_right_front_last_updated(self) -> datetime:
|
|
1874
2586
|
"""Return attribute last updated timestamp."""
|
|
1875
|
-
return find_path(
|
|
2587
|
+
return find_path(
|
|
2588
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2589
|
+
)
|
|
1876
2590
|
|
|
1877
2591
|
@property
|
|
1878
2592
|
def is_window_closed_right_front_supported(self) -> bool:
|
|
1879
2593
|
"""Return true if supported."""
|
|
1880
2594
|
if is_valid_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"):
|
|
1881
|
-
windows = find_path(
|
|
2595
|
+
windows = find_path(
|
|
2596
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"
|
|
2597
|
+
)
|
|
1882
2598
|
for window in windows:
|
|
1883
|
-
if
|
|
2599
|
+
if (
|
|
2600
|
+
window["name"] == "frontRight"
|
|
2601
|
+
and "unsupported" not in window["status"]
|
|
2602
|
+
):
|
|
1884
2603
|
return True
|
|
1885
2604
|
return False
|
|
1886
2605
|
|
|
1887
2606
|
@property
|
|
1888
2607
|
def window_closed_left_back(self) -> bool:
|
|
1889
|
-
"""
|
|
1890
|
-
Return left back window closed state.
|
|
2608
|
+
"""Return left back window closed state.
|
|
1891
2609
|
|
|
1892
2610
|
:return:
|
|
1893
2611
|
"""
|
|
1894
2612
|
windows = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows")
|
|
1895
2613
|
for window in windows:
|
|
1896
2614
|
if window["name"] == "rearLeft":
|
|
1897
|
-
if not any(
|
|
2615
|
+
if not any(
|
|
2616
|
+
valid_status in window["status"]
|
|
2617
|
+
for valid_status in P.VALID_WINDOW_STATUS
|
|
2618
|
+
):
|
|
1898
2619
|
return None
|
|
1899
2620
|
return "closed" in window["status"]
|
|
1900
2621
|
return False
|
|
@@ -1902,29 +2623,38 @@ class Vehicle:
|
|
|
1902
2623
|
@property
|
|
1903
2624
|
def window_closed_left_back_last_updated(self) -> datetime:
|
|
1904
2625
|
"""Return attribute last updated timestamp."""
|
|
1905
|
-
return find_path(
|
|
2626
|
+
return find_path(
|
|
2627
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2628
|
+
)
|
|
1906
2629
|
|
|
1907
2630
|
@property
|
|
1908
2631
|
def is_window_closed_left_back_supported(self) -> bool:
|
|
1909
2632
|
"""Return true if supported."""
|
|
1910
2633
|
if is_valid_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"):
|
|
1911
|
-
windows = find_path(
|
|
2634
|
+
windows = find_path(
|
|
2635
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"
|
|
2636
|
+
)
|
|
1912
2637
|
for window in windows:
|
|
1913
|
-
if
|
|
2638
|
+
if (
|
|
2639
|
+
window["name"] == "rearLeft"
|
|
2640
|
+
and "unsupported" not in window["status"]
|
|
2641
|
+
):
|
|
1914
2642
|
return True
|
|
1915
2643
|
return False
|
|
1916
2644
|
|
|
1917
2645
|
@property
|
|
1918
2646
|
def window_closed_right_back(self) -> bool:
|
|
1919
|
-
"""
|
|
1920
|
-
Return right back window closed state.
|
|
2647
|
+
"""Return right back window closed state.
|
|
1921
2648
|
|
|
1922
2649
|
:return:
|
|
1923
2650
|
"""
|
|
1924
2651
|
windows = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows")
|
|
1925
2652
|
for window in windows:
|
|
1926
2653
|
if window["name"] == "rearRight":
|
|
1927
|
-
if not any(
|
|
2654
|
+
if not any(
|
|
2655
|
+
valid_status in window["status"]
|
|
2656
|
+
for valid_status in P.VALID_WINDOW_STATUS
|
|
2657
|
+
):
|
|
1928
2658
|
return None
|
|
1929
2659
|
return "closed" in window["status"]
|
|
1930
2660
|
return False
|
|
@@ -1932,29 +2662,38 @@ class Vehicle:
|
|
|
1932
2662
|
@property
|
|
1933
2663
|
def window_closed_right_back_last_updated(self) -> datetime:
|
|
1934
2664
|
"""Return attribute last updated timestamp."""
|
|
1935
|
-
return find_path(
|
|
2665
|
+
return find_path(
|
|
2666
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2667
|
+
)
|
|
1936
2668
|
|
|
1937
2669
|
@property
|
|
1938
2670
|
def is_window_closed_right_back_supported(self) -> bool:
|
|
1939
2671
|
"""Return true if supported."""
|
|
1940
2672
|
if is_valid_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"):
|
|
1941
|
-
windows = find_path(
|
|
2673
|
+
windows = find_path(
|
|
2674
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"
|
|
2675
|
+
)
|
|
1942
2676
|
for window in windows:
|
|
1943
|
-
if
|
|
2677
|
+
if (
|
|
2678
|
+
window["name"] == "rearRight"
|
|
2679
|
+
and "unsupported" not in window["status"]
|
|
2680
|
+
):
|
|
1944
2681
|
return True
|
|
1945
2682
|
return False
|
|
1946
2683
|
|
|
1947
2684
|
@property
|
|
1948
2685
|
def sunroof_closed(self) -> bool:
|
|
1949
|
-
"""
|
|
1950
|
-
Return sunroof closed state.
|
|
2686
|
+
"""Return sunroof closed state.
|
|
1951
2687
|
|
|
1952
2688
|
:return:
|
|
1953
2689
|
"""
|
|
1954
2690
|
windows = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows")
|
|
1955
2691
|
for window in windows:
|
|
1956
2692
|
if window["name"] == "sunRoof":
|
|
1957
|
-
if not any(
|
|
2693
|
+
if not any(
|
|
2694
|
+
valid_status in window["status"]
|
|
2695
|
+
for valid_status in P.VALID_WINDOW_STATUS
|
|
2696
|
+
):
|
|
1958
2697
|
return None
|
|
1959
2698
|
return "closed" in window["status"]
|
|
1960
2699
|
return False
|
|
@@ -1962,29 +2701,38 @@ class Vehicle:
|
|
|
1962
2701
|
@property
|
|
1963
2702
|
def sunroof_closed_last_updated(self) -> datetime:
|
|
1964
2703
|
"""Return attribute last updated timestamp."""
|
|
1965
|
-
return find_path(
|
|
2704
|
+
return find_path(
|
|
2705
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2706
|
+
)
|
|
1966
2707
|
|
|
1967
2708
|
@property
|
|
1968
2709
|
def is_sunroof_closed_supported(self) -> bool:
|
|
1969
2710
|
"""Return true if supported."""
|
|
1970
2711
|
if is_valid_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"):
|
|
1971
|
-
windows = find_path(
|
|
2712
|
+
windows = find_path(
|
|
2713
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"
|
|
2714
|
+
)
|
|
1972
2715
|
for window in windows:
|
|
1973
|
-
if
|
|
2716
|
+
if (
|
|
2717
|
+
window["name"] == "sunRoof"
|
|
2718
|
+
and "unsupported" not in window["status"]
|
|
2719
|
+
):
|
|
1974
2720
|
return True
|
|
1975
2721
|
return False
|
|
1976
2722
|
|
|
1977
2723
|
@property
|
|
1978
2724
|
def sunroof_rear_closed(self) -> bool:
|
|
1979
|
-
"""
|
|
1980
|
-
Return sunroof rear closed state.
|
|
2725
|
+
"""Return sunroof rear closed state.
|
|
1981
2726
|
|
|
1982
2727
|
:return:
|
|
1983
2728
|
"""
|
|
1984
2729
|
windows = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows")
|
|
1985
2730
|
for window in windows:
|
|
1986
2731
|
if window["name"] == "sunRoofRear":
|
|
1987
|
-
if not any(
|
|
2732
|
+
if not any(
|
|
2733
|
+
valid_status in window["status"]
|
|
2734
|
+
for valid_status in P.VALID_WINDOW_STATUS
|
|
2735
|
+
):
|
|
1988
2736
|
return None
|
|
1989
2737
|
return "closed" in window["status"]
|
|
1990
2738
|
return False
|
|
@@ -1992,29 +2740,38 @@ class Vehicle:
|
|
|
1992
2740
|
@property
|
|
1993
2741
|
def sunroof_rear_closed_last_updated(self) -> datetime:
|
|
1994
2742
|
"""Return attribute last updated timestamp."""
|
|
1995
|
-
return find_path(
|
|
2743
|
+
return find_path(
|
|
2744
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2745
|
+
)
|
|
1996
2746
|
|
|
1997
2747
|
@property
|
|
1998
2748
|
def is_sunroof_rear_closed_supported(self) -> bool:
|
|
1999
2749
|
"""Return true if supported."""
|
|
2000
2750
|
if is_valid_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"):
|
|
2001
|
-
windows = find_path(
|
|
2751
|
+
windows = find_path(
|
|
2752
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"
|
|
2753
|
+
)
|
|
2002
2754
|
for window in windows:
|
|
2003
|
-
if
|
|
2755
|
+
if (
|
|
2756
|
+
window["name"] == "sunRoofRear"
|
|
2757
|
+
and "unsupported" not in window["status"]
|
|
2758
|
+
):
|
|
2004
2759
|
return True
|
|
2005
2760
|
return False
|
|
2006
2761
|
|
|
2007
2762
|
@property
|
|
2008
2763
|
def roof_cover_closed(self) -> bool:
|
|
2009
|
-
"""
|
|
2010
|
-
Return roof cover closed state.
|
|
2764
|
+
"""Return roof cover closed state.
|
|
2011
2765
|
|
|
2012
2766
|
:return:
|
|
2013
2767
|
"""
|
|
2014
2768
|
windows = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.windows")
|
|
2015
2769
|
for window in windows:
|
|
2016
2770
|
if window["name"] == "roofCover":
|
|
2017
|
-
if not any(
|
|
2771
|
+
if not any(
|
|
2772
|
+
valid_status in window["status"]
|
|
2773
|
+
for valid_status in P.VALID_WINDOW_STATUS
|
|
2774
|
+
):
|
|
2018
2775
|
return None
|
|
2019
2776
|
return "closed" in window["status"]
|
|
2020
2777
|
return False
|
|
@@ -2022,15 +2779,22 @@ class Vehicle:
|
|
|
2022
2779
|
@property
|
|
2023
2780
|
def roof_cover_closed_last_updated(self) -> datetime:
|
|
2024
2781
|
"""Return attribute last updated timestamp."""
|
|
2025
|
-
return find_path(
|
|
2782
|
+
return find_path(
|
|
2783
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2784
|
+
)
|
|
2026
2785
|
|
|
2027
2786
|
@property
|
|
2028
2787
|
def is_roof_cover_closed_supported(self) -> bool:
|
|
2029
2788
|
"""Return true if supported."""
|
|
2030
2789
|
if is_valid_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.doors"):
|
|
2031
|
-
windows = find_path(
|
|
2790
|
+
windows = find_path(
|
|
2791
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.windows"
|
|
2792
|
+
)
|
|
2032
2793
|
for window in windows:
|
|
2033
|
-
if
|
|
2794
|
+
if (
|
|
2795
|
+
window["name"] == "roofCover"
|
|
2796
|
+
and "unsupported" not in window["status"]
|
|
2797
|
+
):
|
|
2034
2798
|
return True
|
|
2035
2799
|
return False
|
|
2036
2800
|
|
|
@@ -2042,51 +2806,60 @@ class Vehicle:
|
|
|
2042
2806
|
|
|
2043
2807
|
@property
|
|
2044
2808
|
def door_locked(self) -> bool:
|
|
2045
|
-
"""
|
|
2046
|
-
Return true if all doors are locked.
|
|
2809
|
+
"""Return true if all doors are locked.
|
|
2047
2810
|
|
|
2048
2811
|
:return:
|
|
2049
2812
|
"""
|
|
2050
|
-
return
|
|
2813
|
+
return (
|
|
2814
|
+
find_path(
|
|
2815
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.doorLockStatus"
|
|
2816
|
+
)
|
|
2817
|
+
== "locked"
|
|
2818
|
+
)
|
|
2051
2819
|
|
|
2052
2820
|
@property
|
|
2053
2821
|
def door_locked_last_updated(self) -> datetime:
|
|
2054
2822
|
"""Return door lock last updated."""
|
|
2055
|
-
return find_path(
|
|
2823
|
+
return find_path(
|
|
2824
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2825
|
+
)
|
|
2056
2826
|
|
|
2057
2827
|
@property
|
|
2058
2828
|
def door_locked_sensor_last_updated(self) -> datetime:
|
|
2059
2829
|
"""Return door lock last updated."""
|
|
2060
|
-
return find_path(
|
|
2830
|
+
return find_path(
|
|
2831
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2832
|
+
)
|
|
2061
2833
|
|
|
2062
2834
|
@property
|
|
2063
2835
|
def is_door_locked_supported(self) -> bool:
|
|
2064
|
-
"""
|
|
2065
|
-
Return true if supported.
|
|
2836
|
+
"""Return true if supported.
|
|
2066
2837
|
|
|
2067
2838
|
:return:
|
|
2068
2839
|
"""
|
|
2069
2840
|
# First check that the service is actually enabled
|
|
2070
2841
|
if not self._services.get(Services.ACCESS, {}).get("active", False):
|
|
2071
2842
|
return False
|
|
2072
|
-
return is_valid_path(
|
|
2843
|
+
return is_valid_path(
|
|
2844
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.doorLockStatus"
|
|
2845
|
+
)
|
|
2073
2846
|
|
|
2074
2847
|
@property
|
|
2075
2848
|
def is_door_locked_sensor_supported(self) -> bool:
|
|
2076
|
-
"""
|
|
2077
|
-
Return true if supported.
|
|
2849
|
+
"""Return true if supported.
|
|
2078
2850
|
|
|
2079
2851
|
:return:
|
|
2080
2852
|
"""
|
|
2081
2853
|
# Use real lock if the service is actually enabled
|
|
2082
2854
|
if self._services.get(Services.ACCESS, {}).get("active", False):
|
|
2083
2855
|
return False
|
|
2084
|
-
return is_valid_path(
|
|
2856
|
+
return is_valid_path(
|
|
2857
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.doorLockStatus"
|
|
2858
|
+
)
|
|
2085
2859
|
|
|
2086
2860
|
@property
|
|
2087
2861
|
def trunk_locked(self) -> bool:
|
|
2088
|
-
"""
|
|
2089
|
-
Return trunk locked state.
|
|
2862
|
+
"""Return trunk locked state.
|
|
2090
2863
|
|
|
2091
2864
|
:return:
|
|
2092
2865
|
"""
|
|
@@ -2099,12 +2872,13 @@ class Vehicle:
|
|
|
2099
2872
|
@property
|
|
2100
2873
|
def trunk_locked_last_updated(self) -> datetime:
|
|
2101
2874
|
"""Return attribute last updated timestamp."""
|
|
2102
|
-
return find_path(
|
|
2875
|
+
return find_path(
|
|
2876
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2877
|
+
)
|
|
2103
2878
|
|
|
2104
2879
|
@property
|
|
2105
2880
|
def is_trunk_locked_supported(self) -> bool:
|
|
2106
|
-
"""
|
|
2107
|
-
Return true if supported.
|
|
2881
|
+
"""Return true if supported.
|
|
2108
2882
|
|
|
2109
2883
|
:return:
|
|
2110
2884
|
"""
|
|
@@ -2119,8 +2893,7 @@ class Vehicle:
|
|
|
2119
2893
|
|
|
2120
2894
|
@property
|
|
2121
2895
|
def trunk_locked_sensor(self) -> bool:
|
|
2122
|
-
"""
|
|
2123
|
-
Return trunk locked state.
|
|
2896
|
+
"""Return trunk locked state.
|
|
2124
2897
|
|
|
2125
2898
|
:return:
|
|
2126
2899
|
"""
|
|
@@ -2133,12 +2906,13 @@ class Vehicle:
|
|
|
2133
2906
|
@property
|
|
2134
2907
|
def trunk_locked_sensor_last_updated(self) -> datetime:
|
|
2135
2908
|
"""Return attribute last updated timestamp."""
|
|
2136
|
-
return find_path(
|
|
2909
|
+
return find_path(
|
|
2910
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2911
|
+
)
|
|
2137
2912
|
|
|
2138
2913
|
@property
|
|
2139
2914
|
def is_trunk_locked_sensor_supported(self) -> bool:
|
|
2140
|
-
"""
|
|
2141
|
-
Return true if supported.
|
|
2915
|
+
"""Return true if supported.
|
|
2142
2916
|
|
|
2143
2917
|
:return:
|
|
2144
2918
|
"""
|
|
@@ -2154,15 +2928,17 @@ class Vehicle:
|
|
|
2154
2928
|
# Doors, hood and trunk
|
|
2155
2929
|
@property
|
|
2156
2930
|
def hood_closed(self) -> bool:
|
|
2157
|
-
"""
|
|
2158
|
-
Return hood closed state.
|
|
2931
|
+
"""Return hood closed state.
|
|
2159
2932
|
|
|
2160
2933
|
:return:
|
|
2161
2934
|
"""
|
|
2162
2935
|
doors = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.doors")
|
|
2163
2936
|
for door in doors:
|
|
2164
2937
|
if door["name"] == "bonnet":
|
|
2165
|
-
if not any(
|
|
2938
|
+
if not any(
|
|
2939
|
+
valid_status in door["status"]
|
|
2940
|
+
for valid_status in P.VALID_DOOR_STATUS
|
|
2941
|
+
):
|
|
2166
2942
|
return None
|
|
2167
2943
|
return "closed" in door["status"]
|
|
2168
2944
|
return False
|
|
@@ -2170,7 +2946,9 @@ class Vehicle:
|
|
|
2170
2946
|
@property
|
|
2171
2947
|
def hood_closed_last_updated(self) -> datetime:
|
|
2172
2948
|
"""Return attribute last updated timestamp."""
|
|
2173
|
-
return find_path(
|
|
2949
|
+
return find_path(
|
|
2950
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2951
|
+
)
|
|
2174
2952
|
|
|
2175
2953
|
@property
|
|
2176
2954
|
def is_hood_closed_supported(self) -> bool:
|
|
@@ -2184,15 +2962,17 @@ class Vehicle:
|
|
|
2184
2962
|
|
|
2185
2963
|
@property
|
|
2186
2964
|
def door_closed_left_front(self) -> bool:
|
|
2187
|
-
"""
|
|
2188
|
-
Return left front door closed state.
|
|
2965
|
+
"""Return left front door closed state.
|
|
2189
2966
|
|
|
2190
2967
|
:return:
|
|
2191
2968
|
"""
|
|
2192
2969
|
doors = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.doors")
|
|
2193
2970
|
for door in doors:
|
|
2194
2971
|
if door["name"] == "frontLeft":
|
|
2195
|
-
if not any(
|
|
2972
|
+
if not any(
|
|
2973
|
+
valid_status in door["status"]
|
|
2974
|
+
for valid_status in P.VALID_DOOR_STATUS
|
|
2975
|
+
):
|
|
2196
2976
|
return None
|
|
2197
2977
|
return "closed" in door["status"]
|
|
2198
2978
|
return False
|
|
@@ -2200,7 +2980,9 @@ class Vehicle:
|
|
|
2200
2980
|
@property
|
|
2201
2981
|
def door_closed_left_front_last_updated(self) -> datetime:
|
|
2202
2982
|
"""Return attribute last updated timestamp."""
|
|
2203
|
-
return find_path(
|
|
2983
|
+
return find_path(
|
|
2984
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
2985
|
+
)
|
|
2204
2986
|
|
|
2205
2987
|
@property
|
|
2206
2988
|
def is_door_closed_left_front_supported(self) -> bool:
|
|
@@ -2214,15 +2996,17 @@ class Vehicle:
|
|
|
2214
2996
|
|
|
2215
2997
|
@property
|
|
2216
2998
|
def door_closed_right_front(self) -> bool:
|
|
2217
|
-
"""
|
|
2218
|
-
Return right front door closed state.
|
|
2999
|
+
"""Return right front door closed state.
|
|
2219
3000
|
|
|
2220
3001
|
:return:
|
|
2221
3002
|
"""
|
|
2222
3003
|
doors = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.doors")
|
|
2223
3004
|
for door in doors:
|
|
2224
3005
|
if door["name"] == "frontRight":
|
|
2225
|
-
if not any(
|
|
3006
|
+
if not any(
|
|
3007
|
+
valid_status in door["status"]
|
|
3008
|
+
for valid_status in P.VALID_DOOR_STATUS
|
|
3009
|
+
):
|
|
2226
3010
|
return None
|
|
2227
3011
|
return "closed" in door["status"]
|
|
2228
3012
|
return False
|
|
@@ -2230,7 +3014,9 @@ class Vehicle:
|
|
|
2230
3014
|
@property
|
|
2231
3015
|
def door_closed_right_front_last_updated(self) -> datetime:
|
|
2232
3016
|
"""Return attribute last updated timestamp."""
|
|
2233
|
-
return find_path(
|
|
3017
|
+
return find_path(
|
|
3018
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
3019
|
+
)
|
|
2234
3020
|
|
|
2235
3021
|
@property
|
|
2236
3022
|
def is_door_closed_right_front_supported(self) -> bool:
|
|
@@ -2244,15 +3030,17 @@ class Vehicle:
|
|
|
2244
3030
|
|
|
2245
3031
|
@property
|
|
2246
3032
|
def door_closed_left_back(self) -> bool:
|
|
2247
|
-
"""
|
|
2248
|
-
Return left back door closed state.
|
|
3033
|
+
"""Return left back door closed state.
|
|
2249
3034
|
|
|
2250
3035
|
:return:
|
|
2251
3036
|
"""
|
|
2252
3037
|
doors = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.doors")
|
|
2253
3038
|
for door in doors:
|
|
2254
3039
|
if door["name"] == "rearLeft":
|
|
2255
|
-
if not any(
|
|
3040
|
+
if not any(
|
|
3041
|
+
valid_status in door["status"]
|
|
3042
|
+
for valid_status in P.VALID_DOOR_STATUS
|
|
3043
|
+
):
|
|
2256
3044
|
return None
|
|
2257
3045
|
return "closed" in door["status"]
|
|
2258
3046
|
return False
|
|
@@ -2260,7 +3048,9 @@ class Vehicle:
|
|
|
2260
3048
|
@property
|
|
2261
3049
|
def door_closed_left_back_last_updated(self) -> datetime:
|
|
2262
3050
|
"""Return attribute last updated timestamp."""
|
|
2263
|
-
return find_path(
|
|
3051
|
+
return find_path(
|
|
3052
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
3053
|
+
)
|
|
2264
3054
|
|
|
2265
3055
|
@property
|
|
2266
3056
|
def is_door_closed_left_back_supported(self) -> bool:
|
|
@@ -2274,15 +3064,17 @@ class Vehicle:
|
|
|
2274
3064
|
|
|
2275
3065
|
@property
|
|
2276
3066
|
def door_closed_right_back(self) -> bool:
|
|
2277
|
-
"""
|
|
2278
|
-
Return right back door closed state.
|
|
3067
|
+
"""Return right back door closed state.
|
|
2279
3068
|
|
|
2280
3069
|
:return:
|
|
2281
3070
|
"""
|
|
2282
3071
|
doors = find_path(self.attrs, f"{Services.ACCESS}.accessStatus.value.doors")
|
|
2283
3072
|
for door in doors:
|
|
2284
3073
|
if door["name"] == "rearRight":
|
|
2285
|
-
if not any(
|
|
3074
|
+
if not any(
|
|
3075
|
+
valid_status in door["status"]
|
|
3076
|
+
for valid_status in P.VALID_DOOR_STATUS
|
|
3077
|
+
):
|
|
2286
3078
|
return None
|
|
2287
3079
|
return "closed" in door["status"]
|
|
2288
3080
|
return False
|
|
@@ -2290,7 +3082,9 @@ class Vehicle:
|
|
|
2290
3082
|
@property
|
|
2291
3083
|
def door_closed_right_back_last_updated(self) -> datetime:
|
|
2292
3084
|
"""Return attribute last updated timestamp."""
|
|
2293
|
-
return find_path(
|
|
3085
|
+
return find_path(
|
|
3086
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
3087
|
+
)
|
|
2294
3088
|
|
|
2295
3089
|
@property
|
|
2296
3090
|
def is_door_closed_right_back_supported(self) -> bool:
|
|
@@ -2304,8 +3098,7 @@ class Vehicle:
|
|
|
2304
3098
|
|
|
2305
3099
|
@property
|
|
2306
3100
|
def trunk_closed(self) -> bool:
|
|
2307
|
-
"""
|
|
2308
|
-
Return trunk closed state.
|
|
3101
|
+
"""Return trunk closed state.
|
|
2309
3102
|
|
|
2310
3103
|
:return:
|
|
2311
3104
|
"""
|
|
@@ -2318,7 +3111,9 @@ class Vehicle:
|
|
|
2318
3111
|
@property
|
|
2319
3112
|
def trunk_closed_last_updated(self) -> datetime:
|
|
2320
3113
|
"""Return attribute last updated timestamp."""
|
|
2321
|
-
return find_path(
|
|
3114
|
+
return find_path(
|
|
3115
|
+
self.attrs, f"{Services.ACCESS}.accessStatus.value.carCapturedTimestamp"
|
|
3116
|
+
)
|
|
2322
3117
|
|
|
2323
3118
|
@property
|
|
2324
3119
|
def is_trunk_closed_supported(self) -> bool:
|
|
@@ -2350,20 +3145,28 @@ class Vehicle:
|
|
|
2350
3145
|
def departure_timer1_last_updated(self) -> datetime:
|
|
2351
3146
|
"""Return last updated timestamp."""
|
|
2352
3147
|
if is_valid_path(
|
|
2353
|
-
self.attrs,
|
|
3148
|
+
self.attrs,
|
|
3149
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.carCapturedTimestamp",
|
|
2354
3150
|
):
|
|
2355
3151
|
return find_path(
|
|
2356
|
-
self.attrs,
|
|
3152
|
+
self.attrs,
|
|
3153
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.carCapturedTimestamp",
|
|
2357
3154
|
)
|
|
2358
3155
|
if is_valid_path(
|
|
2359
|
-
self.attrs,
|
|
3156
|
+
self.attrs,
|
|
3157
|
+
f"{Services.CLIMATISATION_TIMERS}.auxiliaryHeatingTimersStatus.value.carCapturedTimestamp",
|
|
2360
3158
|
):
|
|
2361
3159
|
return find_path(
|
|
2362
|
-
self.attrs,
|
|
3160
|
+
self.attrs,
|
|
3161
|
+
f"{Services.CLIMATISATION_TIMERS}.auxiliaryHeatingTimersStatus.value.carCapturedTimestamp",
|
|
2363
3162
|
)
|
|
2364
|
-
if is_valid_path(
|
|
3163
|
+
if is_valid_path(
|
|
3164
|
+
self.attrs,
|
|
3165
|
+
f"{Services.DEPARTURE_TIMERS}.departureTimersStatus.value.carCapturedTimestamp",
|
|
3166
|
+
):
|
|
2365
3167
|
return find_path(
|
|
2366
|
-
self.attrs,
|
|
3168
|
+
self.attrs,
|
|
3169
|
+
f"{Services.DEPARTURE_TIMERS}.departureTimersStatus.value.carCapturedTimestamp",
|
|
2367
3170
|
)
|
|
2368
3171
|
return None
|
|
2369
3172
|
|
|
@@ -2410,40 +3213,61 @@ class Vehicle:
|
|
|
2410
3213
|
if timer.get("singleTimer", None):
|
|
2411
3214
|
timer_type = "single"
|
|
2412
3215
|
if timer.get("singleTimer", None).get("startDateTime", None):
|
|
2413
|
-
start_date_time = timer.get("singleTimer", None).get(
|
|
3216
|
+
start_date_time = timer.get("singleTimer", None).get(
|
|
3217
|
+
"startDateTime", None
|
|
3218
|
+
)
|
|
2414
3219
|
start_time = (
|
|
2415
|
-
start_date_time.replace(tzinfo=
|
|
3220
|
+
start_date_time.replace(tzinfo=UTC)
|
|
3221
|
+
.astimezone(tz=None)
|
|
3222
|
+
.strftime("%Y-%m-%dT%H:%M:%S")
|
|
2416
3223
|
)
|
|
2417
3224
|
if timer.get("singleTimer", None).get("startDateTimeLocal", None):
|
|
2418
|
-
start_date_time = timer.get("singleTimer", None).get(
|
|
2419
|
-
|
|
2420
|
-
|
|
3225
|
+
start_date_time = timer.get("singleTimer", None).get(
|
|
3226
|
+
"startDateTimeLocal", None
|
|
3227
|
+
)
|
|
3228
|
+
if isinstance(start_date_time, str):
|
|
3229
|
+
start_date_time = datetime.strptime(
|
|
3230
|
+
start_date_time, "%Y-%m-%dT%H:%M:%S"
|
|
3231
|
+
)
|
|
2421
3232
|
start_time = start_date_time
|
|
2422
3233
|
if timer.get("singleTimer", None).get("departureDateTimeLocal", None):
|
|
2423
|
-
start_date_time = timer.get("singleTimer", None).get(
|
|
2424
|
-
|
|
2425
|
-
|
|
3234
|
+
start_date_time = timer.get("singleTimer", None).get(
|
|
3235
|
+
"departureDateTimeLocal", None
|
|
3236
|
+
)
|
|
3237
|
+
if isinstance(start_date_time, str):
|
|
3238
|
+
start_date_time = datetime.strptime(
|
|
3239
|
+
start_date_time, "%Y-%m-%dT%H:%M:%S"
|
|
3240
|
+
)
|
|
2426
3241
|
start_time = start_date_time
|
|
2427
3242
|
elif timer.get("recurringTimer", None):
|
|
2428
3243
|
timer_type = "recurring"
|
|
2429
3244
|
if timer.get("recurringTimer", None).get("startTime", None):
|
|
2430
|
-
start_date_time = timer.get("recurringTimer", None).get(
|
|
3245
|
+
start_date_time = timer.get("recurringTimer", None).get(
|
|
3246
|
+
"startTime", None
|
|
3247
|
+
)
|
|
2431
3248
|
start_time = (
|
|
2432
3249
|
datetime.strptime(start_date_time, "%H:%M")
|
|
2433
|
-
.replace(tzinfo=
|
|
3250
|
+
.replace(tzinfo=UTC)
|
|
2434
3251
|
.astimezone(tz=None)
|
|
2435
3252
|
.strftime("%H:%M")
|
|
2436
3253
|
)
|
|
2437
3254
|
if timer.get("recurringTimer", None).get("startTimeLocal", None):
|
|
2438
|
-
start_date_time = timer.get("recurringTimer", None).get(
|
|
2439
|
-
|
|
3255
|
+
start_date_time = timer.get("recurringTimer", None).get(
|
|
3256
|
+
"startTimeLocal", None
|
|
3257
|
+
)
|
|
3258
|
+
start_time = datetime.strptime(start_date_time, "%H:%M").strftime(
|
|
3259
|
+
"%H:%M"
|
|
3260
|
+
)
|
|
2440
3261
|
if timer.get("recurringTimer", None).get("departureTimeLocal", None):
|
|
2441
|
-
start_date_time = timer.get("recurringTimer", None).get(
|
|
2442
|
-
|
|
3262
|
+
start_date_time = timer.get("recurringTimer", None).get(
|
|
3263
|
+
"departureTimeLocal", None
|
|
3264
|
+
)
|
|
3265
|
+
start_time = datetime.strptime(start_date_time, "%H:%M").strftime(
|
|
3266
|
+
"%H:%M"
|
|
3267
|
+
)
|
|
2443
3268
|
recurring_days = timer.get("recurringTimer", None).get("recurringOn", None)
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
recurring_on.append(day)
|
|
3269
|
+
recurring_days = timer.get("recurringTimer", {}).get("recurringOn", {})
|
|
3270
|
+
recurring_on = [day for day in recurring_days if recurring_days.get(day)]
|
|
2447
3271
|
data = {
|
|
2448
3272
|
"timer_id": timer.get("id", None),
|
|
2449
3273
|
"timer_type": timer_type,
|
|
@@ -2463,24 +3287,46 @@ class Vehicle:
|
|
|
2463
3287
|
data["climatisation_enabled"] = timer.get("climatisation", False)
|
|
2464
3288
|
if timer.get("preferredChargingTimes", None):
|
|
2465
3289
|
preferred_charging_times = timer.get("preferredChargingTimes", None)[0]
|
|
2466
|
-
data["preferred_charging_start_time"] = preferred_charging_times.get(
|
|
2467
|
-
|
|
3290
|
+
data["preferred_charging_start_time"] = preferred_charging_times.get(
|
|
3291
|
+
"startTimeLocal", None
|
|
3292
|
+
)
|
|
3293
|
+
data["preferred_charging_end_time"] = preferred_charging_times.get(
|
|
3294
|
+
"endTimeLocal", None
|
|
3295
|
+
)
|
|
2468
3296
|
return data
|
|
2469
3297
|
|
|
2470
3298
|
def departure_timer(self, timer_id: str | int):
|
|
2471
3299
|
"""Return departure timer."""
|
|
2472
|
-
if is_valid_path(
|
|
2473
|
-
|
|
3300
|
+
if is_valid_path(
|
|
3301
|
+
self.attrs,
|
|
3302
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.timers",
|
|
3303
|
+
):
|
|
3304
|
+
timers = find_path(
|
|
3305
|
+
self.attrs,
|
|
3306
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.timers",
|
|
3307
|
+
)
|
|
2474
3308
|
for timer in timers:
|
|
2475
3309
|
if timer.get("id", 0) == timer_id:
|
|
2476
3310
|
return timer
|
|
2477
|
-
if is_valid_path(
|
|
2478
|
-
|
|
3311
|
+
if is_valid_path(
|
|
3312
|
+
self.attrs,
|
|
3313
|
+
f"{Services.CLIMATISATION_TIMERS}.auxiliaryHeatingTimersStatus.value.timers",
|
|
3314
|
+
):
|
|
3315
|
+
timers = find_path(
|
|
3316
|
+
self.attrs,
|
|
3317
|
+
f"{Services.CLIMATISATION_TIMERS}.auxiliaryHeatingTimersStatus.value.timers",
|
|
3318
|
+
)
|
|
2479
3319
|
for timer in timers:
|
|
2480
3320
|
if timer.get("id", 0) == timer_id:
|
|
2481
3321
|
return timer
|
|
2482
|
-
if is_valid_path(
|
|
2483
|
-
|
|
3322
|
+
if is_valid_path(
|
|
3323
|
+
self.attrs,
|
|
3324
|
+
f"{Services.DEPARTURE_TIMERS}.departureTimersStatus.value.timers",
|
|
3325
|
+
):
|
|
3326
|
+
timers = find_path(
|
|
3327
|
+
self.attrs,
|
|
3328
|
+
f"{Services.DEPARTURE_TIMERS}.departureTimersStatus.value.timers",
|
|
3329
|
+
)
|
|
2484
3330
|
for timer in timers:
|
|
2485
3331
|
if timer.get("id", 0) == timer_id:
|
|
2486
3332
|
return timer
|
|
@@ -2488,8 +3334,14 @@ class Vehicle:
|
|
|
2488
3334
|
|
|
2489
3335
|
def departure_profile(self, profile_id: str | int):
|
|
2490
3336
|
"""Return departure profile."""
|
|
2491
|
-
if is_valid_path(
|
|
2492
|
-
|
|
3337
|
+
if is_valid_path(
|
|
3338
|
+
self.attrs,
|
|
3339
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.profiles",
|
|
3340
|
+
):
|
|
3341
|
+
profiles = find_path(
|
|
3342
|
+
self.attrs,
|
|
3343
|
+
f"{Services.DEPARTURE_PROFILES}.departureProfilesStatus.value.profiles",
|
|
3344
|
+
)
|
|
2493
3345
|
for profile in profiles:
|
|
2494
3346
|
if profile.get("id", 0) == profile_id:
|
|
2495
3347
|
return profile
|
|
@@ -2510,7 +3362,8 @@ class Vehicle:
|
|
|
2510
3362
|
def ac_departure_timer1_last_updated(self) -> datetime:
|
|
2511
3363
|
"""Return last updated timestamp."""
|
|
2512
3364
|
return find_path(
|
|
2513
|
-
self.attrs,
|
|
3365
|
+
self.attrs,
|
|
3366
|
+
f"{Services.CLIMATISATION_TIMERS}.climatisationTimersStatus.value.carCapturedTimestamp",
|
|
2514
3367
|
)
|
|
2515
3368
|
|
|
2516
3369
|
@property
|
|
@@ -2538,8 +3391,14 @@ class Vehicle:
|
|
|
2538
3391
|
|
|
2539
3392
|
def ac_departure_timer(self, timer_id: str | int):
|
|
2540
3393
|
"""Return ac departure timer."""
|
|
2541
|
-
if is_valid_path(
|
|
2542
|
-
|
|
3394
|
+
if is_valid_path(
|
|
3395
|
+
self.attrs,
|
|
3396
|
+
f"{Services.CLIMATISATION_TIMERS}.climatisationTimersStatus.value.timers",
|
|
3397
|
+
):
|
|
3398
|
+
timers = find_path(
|
|
3399
|
+
self.attrs,
|
|
3400
|
+
f"{Services.CLIMATISATION_TIMERS}.climatisationTimersStatus.value.timers",
|
|
3401
|
+
)
|
|
2543
3402
|
for timer in timers:
|
|
2544
3403
|
if timer.get("id", 0) == timer_id:
|
|
2545
3404
|
return timer
|
|
@@ -2554,33 +3413,33 @@ class Vehicle:
|
|
|
2554
3413
|
if timer.get("singleTimer", None):
|
|
2555
3414
|
timer_type = "single"
|
|
2556
3415
|
start_date_time = timer.get("singleTimer", None).get("startDateTime", None)
|
|
2557
|
-
start_time =
|
|
3416
|
+
start_time = (
|
|
3417
|
+
start_date_time.replace(tzinfo=UTC)
|
|
3418
|
+
.astimezone(tz=None)
|
|
3419
|
+
.strftime("%Y-%m-%dT%H:%M:%S")
|
|
3420
|
+
)
|
|
2558
3421
|
elif timer.get("recurringTimer", None):
|
|
2559
3422
|
timer_type = "recurring"
|
|
2560
3423
|
start_date_time = timer.get("recurringTimer", None).get("startTime", None)
|
|
2561
3424
|
start_time = (
|
|
2562
3425
|
datetime.strptime(start_date_time, "%H:%M")
|
|
2563
|
-
.replace(tzinfo=
|
|
3426
|
+
.replace(tzinfo=UTC)
|
|
2564
3427
|
.astimezone(tz=None)
|
|
2565
3428
|
.strftime("%H:%M")
|
|
2566
3429
|
)
|
|
2567
3430
|
recurring_days = timer.get("recurringTimer", None).get("recurringOn", None)
|
|
2568
|
-
for day in recurring_days
|
|
2569
|
-
|
|
2570
|
-
recurring_on.append(day)
|
|
2571
|
-
data = {
|
|
3431
|
+
recurring_on = [day for day in recurring_days if recurring_days.get(day)]
|
|
3432
|
+
return {
|
|
2572
3433
|
"timer_id": timer.get("id", None),
|
|
2573
3434
|
"timer_type": timer_type,
|
|
2574
3435
|
"start_time": start_time,
|
|
2575
3436
|
"recurring_on": recurring_on,
|
|
2576
3437
|
}
|
|
2577
|
-
return data
|
|
2578
3438
|
|
|
2579
3439
|
# Trip data
|
|
2580
3440
|
@property
|
|
2581
3441
|
def trip_last_entry(self):
|
|
2582
|
-
"""
|
|
2583
|
-
Return last trip data entry.
|
|
3442
|
+
"""Return last trip data entry.
|
|
2584
3443
|
|
|
2585
3444
|
:return:
|
|
2586
3445
|
"""
|
|
@@ -2588,8 +3447,7 @@ class Vehicle:
|
|
|
2588
3447
|
|
|
2589
3448
|
@property
|
|
2590
3449
|
def trip_last_average_speed(self):
|
|
2591
|
-
"""
|
|
2592
|
-
Return last trip average speed.
|
|
3450
|
+
"""Return last trip average speed.
|
|
2593
3451
|
|
|
2594
3452
|
:return:
|
|
2595
3453
|
"""
|
|
@@ -2602,23 +3460,25 @@ class Vehicle:
|
|
|
2602
3460
|
|
|
2603
3461
|
@property
|
|
2604
3462
|
def is_trip_last_average_speed_supported(self) -> bool:
|
|
2605
|
-
"""
|
|
2606
|
-
Return true if supported.
|
|
3463
|
+
"""Return true if supported.
|
|
2607
3464
|
|
|
2608
3465
|
:return:
|
|
2609
3466
|
"""
|
|
2610
|
-
return is_valid_path(
|
|
3467
|
+
return is_valid_path(
|
|
3468
|
+
self.attrs, f"{Services.TRIP_LAST}.averageSpeed_kmph"
|
|
3469
|
+
) and type(
|
|
2611
3470
|
find_path(self.attrs, f"{Services.TRIP_LAST}.averageSpeed_kmph")
|
|
2612
3471
|
) in (float, int)
|
|
2613
3472
|
|
|
2614
3473
|
@property
|
|
2615
3474
|
def trip_last_average_electric_engine_consumption(self):
|
|
2616
|
-
"""
|
|
2617
|
-
Return last trip average electric consumption.
|
|
3475
|
+
"""Return last trip average electric consumption.
|
|
2618
3476
|
|
|
2619
3477
|
:return:
|
|
2620
3478
|
"""
|
|
2621
|
-
return float(
|
|
3479
|
+
return float(
|
|
3480
|
+
find_path(self.attrs, f"{Services.TRIP_LAST}.averageElectricConsumption")
|
|
3481
|
+
)
|
|
2622
3482
|
|
|
2623
3483
|
@property
|
|
2624
3484
|
def trip_last_average_electric_engine_consumption_last_updated(self) -> datetime:
|
|
@@ -2627,23 +3487,25 @@ class Vehicle:
|
|
|
2627
3487
|
|
|
2628
3488
|
@property
|
|
2629
3489
|
def is_trip_last_average_electric_engine_consumption_supported(self) -> bool:
|
|
2630
|
-
"""
|
|
2631
|
-
Return true if supported.
|
|
3490
|
+
"""Return true if supported.
|
|
2632
3491
|
|
|
2633
3492
|
:return:
|
|
2634
3493
|
"""
|
|
2635
|
-
return is_valid_path(
|
|
3494
|
+
return is_valid_path(
|
|
3495
|
+
self.attrs, f"{Services.TRIP_LAST}.averageElectricConsumption"
|
|
3496
|
+
) and type(
|
|
2636
3497
|
find_path(self.attrs, f"{Services.TRIP_LAST}.averageElectricConsumption")
|
|
2637
3498
|
) in (float, int)
|
|
2638
3499
|
|
|
2639
3500
|
@property
|
|
2640
3501
|
def trip_last_average_fuel_consumption(self):
|
|
2641
|
-
"""
|
|
2642
|
-
Return last trip average fuel consumption.
|
|
3502
|
+
"""Return last trip average fuel consumption.
|
|
2643
3503
|
|
|
2644
3504
|
:return:
|
|
2645
3505
|
"""
|
|
2646
|
-
return float(
|
|
3506
|
+
return float(
|
|
3507
|
+
find_path(self.attrs, f"{Services.TRIP_LAST}.averageFuelConsumption")
|
|
3508
|
+
)
|
|
2647
3509
|
|
|
2648
3510
|
@property
|
|
2649
3511
|
def trip_last_average_fuel_consumption_last_updated(self) -> datetime:
|
|
@@ -2652,19 +3514,46 @@ class Vehicle:
|
|
|
2652
3514
|
|
|
2653
3515
|
@property
|
|
2654
3516
|
def is_trip_last_average_fuel_consumption_supported(self) -> bool:
|
|
2655
|
-
"""
|
|
2656
|
-
Return true if supported.
|
|
3517
|
+
"""Return true if supported.
|
|
2657
3518
|
|
|
2658
3519
|
:return:
|
|
2659
3520
|
"""
|
|
2660
|
-
return is_valid_path(
|
|
3521
|
+
return is_valid_path(
|
|
3522
|
+
self.attrs, f"{Services.TRIP_LAST}.averageFuelConsumption"
|
|
3523
|
+
) and type(
|
|
2661
3524
|
find_path(self.attrs, f"{Services.TRIP_LAST}.averageFuelConsumption")
|
|
2662
3525
|
) in (float, int)
|
|
2663
3526
|
|
|
2664
3527
|
@property
|
|
2665
|
-
def
|
|
3528
|
+
def trip_last_average_gas_consumption(self):
|
|
3529
|
+
"""Return last trip average gas consumption.
|
|
3530
|
+
|
|
3531
|
+
:return:
|
|
3532
|
+
"""
|
|
3533
|
+
return float(
|
|
3534
|
+
find_path(self.attrs, f"{Services.TRIP_LAST}.averageGasConsumption")
|
|
3535
|
+
)
|
|
3536
|
+
|
|
3537
|
+
@property
|
|
3538
|
+
def trip_last_average_gas_consumption_last_updated(self) -> datetime:
|
|
3539
|
+
"""Return last updated timestamp."""
|
|
3540
|
+
return find_path(self.attrs, f"{Services.TRIP_LAST}.tripEndTimestamp")
|
|
3541
|
+
|
|
3542
|
+
@property
|
|
3543
|
+
def is_trip_last_average_gas_consumption_supported(self) -> bool:
|
|
3544
|
+
"""Return true if supported.
|
|
3545
|
+
|
|
3546
|
+
:return:
|
|
2666
3547
|
"""
|
|
2667
|
-
|
|
3548
|
+
return is_valid_path(
|
|
3549
|
+
self.attrs, f"{Services.TRIP_LAST}.averageGasConsumption"
|
|
3550
|
+
) and type(
|
|
3551
|
+
find_path(self.attrs, f"{Services.TRIP_LAST}.averageGasConsumption")
|
|
3552
|
+
) in (float, int)
|
|
3553
|
+
|
|
3554
|
+
@property
|
|
3555
|
+
def trip_last_average_auxillary_consumption(self):
|
|
3556
|
+
"""Return last trip average auxiliary consumption.
|
|
2668
3557
|
|
|
2669
3558
|
:return:
|
|
2670
3559
|
"""
|
|
@@ -2678,19 +3567,19 @@ class Vehicle:
|
|
|
2678
3567
|
|
|
2679
3568
|
@property
|
|
2680
3569
|
def is_trip_last_average_auxillary_consumption_supported(self) -> bool:
|
|
2681
|
-
"""
|
|
2682
|
-
Return true if supported.
|
|
3570
|
+
"""Return true if supported.
|
|
2683
3571
|
|
|
2684
3572
|
:return:
|
|
2685
3573
|
"""
|
|
2686
|
-
return is_valid_path(
|
|
3574
|
+
return is_valid_path(
|
|
3575
|
+
self.attrs, f"{Services.TRIP_LAST}.averageAuxiliaryConsumption"
|
|
3576
|
+
) and type(
|
|
2687
3577
|
find_path(self.attrs, f"{Services.TRIP_LAST}.averageAuxiliaryConsumption")
|
|
2688
3578
|
) in (float, int)
|
|
2689
3579
|
|
|
2690
3580
|
@property
|
|
2691
3581
|
def trip_last_average_aux_consumer_consumption(self):
|
|
2692
|
-
"""
|
|
2693
|
-
Return last trip average auxiliary consumer consumption.
|
|
3582
|
+
"""Return last trip average auxiliary consumer consumption.
|
|
2694
3583
|
|
|
2695
3584
|
:return:
|
|
2696
3585
|
"""
|
|
@@ -2704,19 +3593,19 @@ class Vehicle:
|
|
|
2704
3593
|
|
|
2705
3594
|
@property
|
|
2706
3595
|
def is_trip_last_average_aux_consumer_consumption_supported(self) -> bool:
|
|
2707
|
-
"""
|
|
2708
|
-
Return true if supported.
|
|
3596
|
+
"""Return true if supported.
|
|
2709
3597
|
|
|
2710
3598
|
:return:
|
|
2711
3599
|
"""
|
|
2712
|
-
return is_valid_path(
|
|
3600
|
+
return is_valid_path(
|
|
3601
|
+
self.attrs, f"{Services.TRIP_LAST}.averageAuxConsumerConsumption"
|
|
3602
|
+
) and type(
|
|
2713
3603
|
find_path(self.attrs, f"{Services.TRIP_LAST}.averageAuxConsumerConsumption")
|
|
2714
3604
|
) in (float, int)
|
|
2715
3605
|
|
|
2716
3606
|
@property
|
|
2717
3607
|
def trip_last_duration(self):
|
|
2718
|
-
"""
|
|
2719
|
-
Return last trip duration in minutes(?).
|
|
3608
|
+
"""Return last trip duration in minutes(?).
|
|
2720
3609
|
|
|
2721
3610
|
:return:
|
|
2722
3611
|
"""
|
|
@@ -2729,8 +3618,7 @@ class Vehicle:
|
|
|
2729
3618
|
|
|
2730
3619
|
@property
|
|
2731
3620
|
def is_trip_last_duration_supported(self) -> bool:
|
|
2732
|
-
"""
|
|
2733
|
-
Return true if supported.
|
|
3621
|
+
"""Return true if supported.
|
|
2734
3622
|
|
|
2735
3623
|
:return:
|
|
2736
3624
|
"""
|
|
@@ -2740,8 +3628,7 @@ class Vehicle:
|
|
|
2740
3628
|
|
|
2741
3629
|
@property
|
|
2742
3630
|
def trip_last_length(self):
|
|
2743
|
-
"""
|
|
2744
|
-
Return last trip length.
|
|
3631
|
+
"""Return last trip length.
|
|
2745
3632
|
|
|
2746
3633
|
:return:
|
|
2747
3634
|
"""
|
|
@@ -2754,8 +3641,7 @@ class Vehicle:
|
|
|
2754
3641
|
|
|
2755
3642
|
@property
|
|
2756
3643
|
def is_trip_last_length_supported(self) -> bool:
|
|
2757
|
-
"""
|
|
2758
|
-
Return true if supported.
|
|
3644
|
+
"""Return true if supported.
|
|
2759
3645
|
|
|
2760
3646
|
:return:
|
|
2761
3647
|
"""
|
|
@@ -2765,8 +3651,7 @@ class Vehicle:
|
|
|
2765
3651
|
|
|
2766
3652
|
@property
|
|
2767
3653
|
def trip_last_recuperation(self):
|
|
2768
|
-
"""
|
|
2769
|
-
Return last trip recuperation.
|
|
3654
|
+
"""Return last trip recuperation.
|
|
2770
3655
|
|
|
2771
3656
|
:return:
|
|
2772
3657
|
"""
|
|
@@ -2780,8 +3665,7 @@ class Vehicle:
|
|
|
2780
3665
|
|
|
2781
3666
|
@property
|
|
2782
3667
|
def is_trip_last_recuperation_supported(self) -> bool:
|
|
2783
|
-
"""
|
|
2784
|
-
Return true if supported.
|
|
3668
|
+
"""Return true if supported.
|
|
2785
3669
|
|
|
2786
3670
|
:return:
|
|
2787
3671
|
"""
|
|
@@ -2791,8 +3675,7 @@ class Vehicle:
|
|
|
2791
3675
|
|
|
2792
3676
|
@property
|
|
2793
3677
|
def trip_last_average_recuperation(self):
|
|
2794
|
-
"""
|
|
2795
|
-
Return last trip total recuperation.
|
|
3678
|
+
"""Return last trip total recuperation.
|
|
2796
3679
|
|
|
2797
3680
|
:return:
|
|
2798
3681
|
"""
|
|
@@ -2805,18 +3688,19 @@ class Vehicle:
|
|
|
2805
3688
|
|
|
2806
3689
|
@property
|
|
2807
3690
|
def is_trip_last_average_recuperation_supported(self) -> bool:
|
|
2808
|
-
"""
|
|
2809
|
-
Return true if supported.
|
|
3691
|
+
"""Return true if supported.
|
|
2810
3692
|
|
|
2811
3693
|
:return:
|
|
2812
3694
|
"""
|
|
2813
3695
|
response = self.trip_last_entry
|
|
2814
|
-
return response and type(response.get("averageRecuperation", None)) in (
|
|
3696
|
+
return response and type(response.get("averageRecuperation", None)) in (
|
|
3697
|
+
float,
|
|
3698
|
+
int,
|
|
3699
|
+
)
|
|
2815
3700
|
|
|
2816
3701
|
@property
|
|
2817
3702
|
def trip_last_total_electric_consumption(self):
|
|
2818
|
-
"""
|
|
2819
|
-
Return last trip total electric consumption.
|
|
3703
|
+
"""Return last trip total electric consumption.
|
|
2820
3704
|
|
|
2821
3705
|
:return:
|
|
2822
3706
|
"""
|
|
@@ -2830,14 +3714,16 @@ class Vehicle:
|
|
|
2830
3714
|
|
|
2831
3715
|
@property
|
|
2832
3716
|
def is_trip_last_total_electric_consumption_supported(self) -> bool:
|
|
2833
|
-
"""
|
|
2834
|
-
Return true if supported.
|
|
3717
|
+
"""Return true if supported.
|
|
2835
3718
|
|
|
2836
3719
|
:return:
|
|
2837
3720
|
"""
|
|
2838
3721
|
# Not implemented
|
|
2839
3722
|
response = self.trip_last_entry
|
|
2840
|
-
return response and type(response.get("totalElectricConsumption", None)) in (
|
|
3723
|
+
return response and type(response.get("totalElectricConsumption", None)) in (
|
|
3724
|
+
float,
|
|
3725
|
+
int,
|
|
3726
|
+
)
|
|
2841
3727
|
|
|
2842
3728
|
# Status of set data requests
|
|
2843
3729
|
@property
|
|
@@ -2880,9 +3766,11 @@ class Vehicle:
|
|
|
2880
3766
|
def request_in_progress(self) -> bool:
|
|
2881
3767
|
"""Check of any requests are currently in progress."""
|
|
2882
3768
|
try:
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
3769
|
+
return any(
|
|
3770
|
+
isinstance(value, dict) and "id" in value and bool(value["id"])
|
|
3771
|
+
for value in self._requests.values()
|
|
3772
|
+
)
|
|
3773
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
2886
3774
|
_LOGGER.warning(e)
|
|
2887
3775
|
return False
|
|
2888
3776
|
|
|
@@ -2890,11 +3778,18 @@ class Vehicle:
|
|
|
2890
3778
|
def request_in_progress_last_updated(self) -> datetime:
|
|
2891
3779
|
"""Return attribute last updated timestamp."""
|
|
2892
3780
|
try:
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
3781
|
+
# Get all timestamps in the dictionary
|
|
3782
|
+
timestamps = [
|
|
3783
|
+
item["timestamp"]
|
|
3784
|
+
for item in self._requests.values()
|
|
3785
|
+
if isinstance(item, dict) and "timestamp" in item
|
|
3786
|
+
]
|
|
3787
|
+
|
|
3788
|
+
# Return the most recent timestamp
|
|
3789
|
+
return max(timestamps) if timestamps else datetime.now(UTC)
|
|
3790
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
2896
3791
|
_LOGGER.warning(e)
|
|
2897
|
-
return datetime.now(
|
|
3792
|
+
return datetime.now(UTC)
|
|
2898
3793
|
|
|
2899
3794
|
@property
|
|
2900
3795
|
def is_request_in_progress_supported(self):
|
|
@@ -2904,9 +3799,18 @@ class Vehicle:
|
|
|
2904
3799
|
@property
|
|
2905
3800
|
def request_results(self) -> dict:
|
|
2906
3801
|
"""Get last request result."""
|
|
2907
|
-
data = {
|
|
3802
|
+
data = {
|
|
3803
|
+
"latest": self._requests.get("latest", None),
|
|
3804
|
+
"state": self._requests.get("state", None),
|
|
3805
|
+
}
|
|
2908
3806
|
for section in self._requests:
|
|
2909
|
-
if section in [
|
|
3807
|
+
if section in [
|
|
3808
|
+
"departuretimer",
|
|
3809
|
+
"batterycharge",
|
|
3810
|
+
"climatisation",
|
|
3811
|
+
"refresh",
|
|
3812
|
+
"lock",
|
|
3813
|
+
]:
|
|
2910
3814
|
data[section] = self._requests[section].get("status", "Unknown")
|
|
2911
3815
|
return data
|
|
2912
3816
|
|
|
@@ -2914,10 +3818,18 @@ class Vehicle:
|
|
|
2914
3818
|
def request_results_last_updated(self) -> datetime | None:
|
|
2915
3819
|
"""Get last updated time."""
|
|
2916
3820
|
if self._requests.get("latest", "") != "":
|
|
2917
|
-
return self._requests.get(str(self._requests.get("latest")), {}).get(
|
|
3821
|
+
return self._requests.get(str(self._requests.get("latest")), {}).get(
|
|
3822
|
+
"timestamp"
|
|
3823
|
+
)
|
|
2918
3824
|
# all requests should have more or less the same timestamp anyway, so
|
|
2919
3825
|
# just return the first one
|
|
2920
|
-
for section in [
|
|
3826
|
+
for section in [
|
|
3827
|
+
"departuretimer",
|
|
3828
|
+
"batterycharge",
|
|
3829
|
+
"climatisation",
|
|
3830
|
+
"refresh",
|
|
3831
|
+
"lock",
|
|
3832
|
+
]:
|
|
2921
3833
|
if section in self._requests:
|
|
2922
3834
|
return self._requests[section].get("timestamp")
|
|
2923
3835
|
return None
|
|
@@ -2939,104 +3851,211 @@ class Vehicle:
|
|
|
2939
3851
|
|
|
2940
3852
|
@property
|
|
2941
3853
|
def json(self):
|
|
2942
|
-
"""
|
|
2943
|
-
Return vehicle data in JSON format.
|
|
3854
|
+
"""Return vehicle data in JSON format.
|
|
2944
3855
|
|
|
2945
3856
|
:return:
|
|
2946
3857
|
"""
|
|
2947
3858
|
|
|
2948
3859
|
def serialize(obj):
|
|
2949
|
-
"""
|
|
2950
|
-
Convert datetime instances back to JSON compatible format.
|
|
3860
|
+
"""Convert datetime instances back to JSON compatible format.
|
|
2951
3861
|
|
|
2952
3862
|
:param obj:
|
|
2953
3863
|
:return:
|
|
2954
3864
|
"""
|
|
2955
3865
|
return obj.isoformat() if isinstance(obj, datetime) else obj
|
|
2956
3866
|
|
|
2957
|
-
return to_json(
|
|
3867
|
+
return to_json(
|
|
3868
|
+
OrderedDict(sorted(self.attrs.items())), indent=4, default=serialize
|
|
3869
|
+
)
|
|
2958
3870
|
|
|
2959
3871
|
def is_primary_drive_electric(self):
|
|
2960
3872
|
"""Check if primary engine is electric."""
|
|
2961
3873
|
return (
|
|
2962
|
-
find_path(
|
|
3874
|
+
find_path(
|
|
3875
|
+
self.attrs,
|
|
3876
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.primaryEngineType",
|
|
3877
|
+
)
|
|
2963
3878
|
== ENGINE_TYPE_ELECTRIC
|
|
2964
3879
|
)
|
|
2965
3880
|
|
|
2966
3881
|
def is_secondary_drive_electric(self):
|
|
2967
3882
|
"""Check if secondary engine is electric."""
|
|
2968
3883
|
return (
|
|
2969
|
-
is_valid_path(
|
|
2970
|
-
|
|
3884
|
+
is_valid_path(
|
|
3885
|
+
self.attrs,
|
|
3886
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.secondaryEngineType",
|
|
3887
|
+
)
|
|
3888
|
+
and find_path(
|
|
3889
|
+
self.attrs,
|
|
3890
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.secondaryEngineType",
|
|
3891
|
+
)
|
|
2971
3892
|
== ENGINE_TYPE_ELECTRIC
|
|
2972
3893
|
)
|
|
2973
3894
|
|
|
2974
3895
|
def is_primary_drive_combustion(self):
|
|
2975
3896
|
"""Check if primary engine is combustion."""
|
|
2976
3897
|
engine_type = ""
|
|
2977
|
-
if is_valid_path(
|
|
2978
|
-
|
|
3898
|
+
if is_valid_path(
|
|
3899
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.type"
|
|
3900
|
+
):
|
|
3901
|
+
engine_type = find_path(
|
|
3902
|
+
self.attrs,
|
|
3903
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.primaryEngine.type",
|
|
3904
|
+
)
|
|
2979
3905
|
|
|
2980
|
-
if is_valid_path(
|
|
2981
|
-
|
|
3906
|
+
if is_valid_path(
|
|
3907
|
+
self.attrs,
|
|
3908
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.primaryEngineType",
|
|
3909
|
+
):
|
|
3910
|
+
engine_type = find_path(
|
|
3911
|
+
self.attrs,
|
|
3912
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.primaryEngineType",
|
|
3913
|
+
)
|
|
2982
3914
|
|
|
2983
3915
|
return engine_type in ENGINE_TYPE_COMBUSTION
|
|
2984
3916
|
|
|
2985
3917
|
def is_secondary_drive_combustion(self):
|
|
2986
3918
|
"""Check if secondary engine is combustion."""
|
|
2987
3919
|
engine_type = ""
|
|
2988
|
-
if is_valid_path(
|
|
2989
|
-
|
|
3920
|
+
if is_valid_path(
|
|
3921
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.secondaryEngine.type"
|
|
3922
|
+
):
|
|
3923
|
+
engine_type = find_path(
|
|
3924
|
+
self.attrs,
|
|
3925
|
+
f"{Services.FUEL_STATUS}.rangeStatus.value.secondaryEngine.type",
|
|
3926
|
+
)
|
|
2990
3927
|
|
|
2991
|
-
if is_valid_path(
|
|
2992
|
-
|
|
3928
|
+
if is_valid_path(
|
|
3929
|
+
self.attrs,
|
|
3930
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.secondaryEngineType",
|
|
3931
|
+
):
|
|
3932
|
+
engine_type = find_path(
|
|
3933
|
+
self.attrs,
|
|
3934
|
+
f"{Services.MEASUREMENTS}.fuelLevelStatus.value.secondaryEngineType",
|
|
3935
|
+
)
|
|
2993
3936
|
|
|
2994
3937
|
return engine_type in ENGINE_TYPE_COMBUSTION
|
|
2995
3938
|
|
|
3939
|
+
def is_primary_drive_gas(self):
|
|
3940
|
+
"""Check if primary engine is gas."""
|
|
3941
|
+
if is_valid_path(
|
|
3942
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
3943
|
+
):
|
|
3944
|
+
return (
|
|
3945
|
+
find_path(
|
|
3946
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
3947
|
+
)
|
|
3948
|
+
== ENGINE_TYPE_GAS
|
|
3949
|
+
)
|
|
3950
|
+
if is_valid_path(
|
|
3951
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
3952
|
+
):
|
|
3953
|
+
return (
|
|
3954
|
+
find_path(
|
|
3955
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
3956
|
+
)
|
|
3957
|
+
== ENGINE_TYPE_GAS
|
|
3958
|
+
)
|
|
3959
|
+
return False
|
|
3960
|
+
|
|
2996
3961
|
@property
|
|
2997
3962
|
def is_car_type_electric(self):
|
|
2998
3963
|
"""Check if car type is electric."""
|
|
2999
|
-
if is_valid_path(
|
|
3000
|
-
|
|
3001
|
-
|
|
3964
|
+
if is_valid_path(
|
|
3965
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
3966
|
+
):
|
|
3967
|
+
return (
|
|
3968
|
+
find_path(
|
|
3969
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
3970
|
+
)
|
|
3971
|
+
== ENGINE_TYPE_ELECTRIC
|
|
3972
|
+
)
|
|
3973
|
+
if is_valid_path(
|
|
3974
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
3975
|
+
):
|
|
3002
3976
|
return (
|
|
3003
|
-
find_path(
|
|
3977
|
+
find_path(
|
|
3978
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
3979
|
+
)
|
|
3980
|
+
== ENGINE_TYPE_ELECTRIC
|
|
3004
3981
|
)
|
|
3005
3982
|
return False
|
|
3006
3983
|
|
|
3007
3984
|
@property
|
|
3008
3985
|
def is_car_type_diesel(self):
|
|
3009
3986
|
"""Check if car type is diesel."""
|
|
3010
|
-
if is_valid_path(
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
return
|
|
3987
|
+
if is_valid_path(
|
|
3988
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
3989
|
+
):
|
|
3990
|
+
return (
|
|
3991
|
+
find_path(
|
|
3992
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
3993
|
+
)
|
|
3994
|
+
== ENGINE_TYPE_DIESEL
|
|
3995
|
+
)
|
|
3996
|
+
if is_valid_path(
|
|
3997
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
3998
|
+
):
|
|
3999
|
+
return (
|
|
4000
|
+
find_path(
|
|
4001
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
4002
|
+
)
|
|
4003
|
+
== ENGINE_TYPE_DIESEL
|
|
4004
|
+
)
|
|
3014
4005
|
return False
|
|
3015
4006
|
|
|
3016
4007
|
@property
|
|
3017
4008
|
def is_car_type_gasoline(self):
|
|
3018
4009
|
"""Check if car type is gasoline."""
|
|
3019
|
-
if is_valid_path(
|
|
3020
|
-
|
|
3021
|
-
|
|
4010
|
+
if is_valid_path(
|
|
4011
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
4012
|
+
):
|
|
4013
|
+
return (
|
|
4014
|
+
find_path(
|
|
4015
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
4016
|
+
)
|
|
4017
|
+
== ENGINE_TYPE_GASOLINE
|
|
4018
|
+
)
|
|
4019
|
+
if is_valid_path(
|
|
4020
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
4021
|
+
):
|
|
3022
4022
|
return (
|
|
3023
|
-
find_path(
|
|
4023
|
+
find_path(
|
|
4024
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
4025
|
+
)
|
|
4026
|
+
== ENGINE_TYPE_GASOLINE
|
|
3024
4027
|
)
|
|
3025
4028
|
return False
|
|
3026
4029
|
|
|
3027
4030
|
@property
|
|
3028
4031
|
def is_car_type_hybrid(self):
|
|
3029
4032
|
"""Check if car type is hybrid."""
|
|
3030
|
-
if is_valid_path(
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
return
|
|
4033
|
+
if is_valid_path(
|
|
4034
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
4035
|
+
):
|
|
4036
|
+
return (
|
|
4037
|
+
find_path(
|
|
4038
|
+
self.attrs, f"{Services.FUEL_STATUS}.rangeStatus.value.carType"
|
|
4039
|
+
)
|
|
4040
|
+
== ENGINE_TYPE_HYBRID
|
|
4041
|
+
)
|
|
4042
|
+
if is_valid_path(
|
|
4043
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
4044
|
+
):
|
|
4045
|
+
return (
|
|
4046
|
+
find_path(
|
|
4047
|
+
self.attrs, f"{Services.MEASUREMENTS}.fuelLevelStatus.value.carType"
|
|
4048
|
+
)
|
|
4049
|
+
== ENGINE_TYPE_HYBRID
|
|
4050
|
+
)
|
|
3034
4051
|
return False
|
|
3035
4052
|
|
|
3036
4053
|
@property
|
|
3037
4054
|
def has_combustion_engine(self):
|
|
3038
4055
|
"""Return true if car has a combustion engine."""
|
|
3039
|
-
return
|
|
4056
|
+
return (
|
|
4057
|
+
self.is_primary_drive_combustion() or self.is_secondary_drive_combustion()
|
|
4058
|
+
)
|
|
3040
4059
|
|
|
3041
4060
|
@property
|
|
3042
4061
|
def api_vehicles_status(self) -> bool:
|
|
@@ -3046,7 +4065,7 @@ class Vehicle:
|
|
|
3046
4065
|
@property
|
|
3047
4066
|
def api_vehicles_status_last_updated(self) -> datetime:
|
|
3048
4067
|
"""Return attribute last updated timestamp."""
|
|
3049
|
-
return datetime.now(
|
|
4068
|
+
return datetime.now(UTC)
|
|
3050
4069
|
|
|
3051
4070
|
@property
|
|
3052
4071
|
def is_api_vehicles_status_supported(self):
|
|
@@ -3056,12 +4075,14 @@ class Vehicle:
|
|
|
3056
4075
|
@property
|
|
3057
4076
|
def api_capabilities_status(self) -> bool:
|
|
3058
4077
|
"""Check capabilities API status."""
|
|
3059
|
-
return self.attrs.get(Services.SERVICE_STATUS, {}).get(
|
|
4078
|
+
return self.attrs.get(Services.SERVICE_STATUS, {}).get(
|
|
4079
|
+
"capabilities", "Unknown"
|
|
4080
|
+
)
|
|
3060
4081
|
|
|
3061
4082
|
@property
|
|
3062
4083
|
def api_capabilities_status_last_updated(self) -> datetime:
|
|
3063
4084
|
"""Return attribute last updated timestamp."""
|
|
3064
|
-
return datetime.now(
|
|
4085
|
+
return datetime.now(UTC)
|
|
3065
4086
|
|
|
3066
4087
|
@property
|
|
3067
4088
|
def is_api_capabilities_status_supported(self):
|
|
@@ -3076,7 +4097,7 @@ class Vehicle:
|
|
|
3076
4097
|
@property
|
|
3077
4098
|
def api_trips_status_last_updated(self) -> datetime:
|
|
3078
4099
|
"""Return attribute last updated timestamp."""
|
|
3079
|
-
return datetime.now(
|
|
4100
|
+
return datetime.now(UTC)
|
|
3080
4101
|
|
|
3081
4102
|
@property
|
|
3082
4103
|
def is_api_trips_status_supported(self):
|
|
@@ -3088,12 +4109,14 @@ class Vehicle:
|
|
|
3088
4109
|
@property
|
|
3089
4110
|
def api_selectivestatus_status(self) -> bool:
|
|
3090
4111
|
"""Check selectivestatus API status."""
|
|
3091
|
-
return self.attrs.get(Services.SERVICE_STATUS, {}).get(
|
|
4112
|
+
return self.attrs.get(Services.SERVICE_STATUS, {}).get(
|
|
4113
|
+
"selectivestatus", "Unknown"
|
|
4114
|
+
)
|
|
3092
4115
|
|
|
3093
4116
|
@property
|
|
3094
4117
|
def api_selectivestatus_status_last_updated(self) -> datetime:
|
|
3095
4118
|
"""Return attribute last updated timestamp."""
|
|
3096
|
-
return datetime.now(
|
|
4119
|
+
return datetime.now(UTC)
|
|
3097
4120
|
|
|
3098
4121
|
@property
|
|
3099
4122
|
def is_api_selectivestatus_status_supported(self):
|
|
@@ -3103,12 +4126,14 @@ class Vehicle:
|
|
|
3103
4126
|
@property
|
|
3104
4127
|
def api_parkingposition_status(self) -> bool:
|
|
3105
4128
|
"""Check parkingposition API status."""
|
|
3106
|
-
return self.attrs.get(Services.SERVICE_STATUS, {}).get(
|
|
4129
|
+
return self.attrs.get(Services.SERVICE_STATUS, {}).get(
|
|
4130
|
+
"parkingposition", "Unknown"
|
|
4131
|
+
)
|
|
3107
4132
|
|
|
3108
4133
|
@property
|
|
3109
4134
|
def api_parkingposition_status_last_updated(self) -> datetime:
|
|
3110
4135
|
"""Return attribute last updated timestamp."""
|
|
3111
|
-
return datetime.now(
|
|
4136
|
+
return datetime.now(UTC)
|
|
3112
4137
|
|
|
3113
4138
|
@property
|
|
3114
4139
|
def is_api_parkingposition_status_supported(self):
|
|
@@ -3125,7 +4150,7 @@ class Vehicle:
|
|
|
3125
4150
|
@property
|
|
3126
4151
|
def api_token_status_last_updated(self) -> datetime:
|
|
3127
4152
|
"""Return attribute last updated timestamp."""
|
|
3128
|
-
return datetime.now(
|
|
4153
|
+
return datetime.now(UTC)
|
|
3129
4154
|
|
|
3130
4155
|
@property
|
|
3131
4156
|
def is_api_token_status_supported(self):
|
|
@@ -3143,7 +4168,7 @@ class Vehicle:
|
|
|
3143
4168
|
@property
|
|
3144
4169
|
def last_data_refresh_last_updated(self) -> datetime:
|
|
3145
4170
|
"""Return attribute last updated timestamp."""
|
|
3146
|
-
return datetime.now(
|
|
4171
|
+
return datetime.now(UTC)
|
|
3147
4172
|
|
|
3148
4173
|
@property
|
|
3149
4174
|
def is_last_data_refresh_supported(self):
|