volkswagencarnet 5.0.0b4__py3-none-any.whl → 5.0.0b5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of volkswagencarnet might be problematic. Click here for more details.

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