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