volkswagencarnet 5.0.0b4__py3-none-any.whl → 5.0.1__py3-none-any.whl

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

Potentially problematic release.


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

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