pycupra 0.0.1__py3-none-any.whl → 0.0.3__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.
pycupra/dashboard.py CHANGED
@@ -1,1258 +1,1287 @@
1
- # Utilities for integration with Home Assistant
2
- # Thanks to molobrakos and Farfar
3
-
4
- import logging
5
- from datetime import datetime
6
- from .utilities import camel2slug
7
-
8
- _LOGGER = logging.getLogger(__name__)
9
-
10
- class Instrument:
11
- def __init__(self, component, attr, name, icon=None):
12
- self.attr = attr
13
- self.component = component
14
- self.name = name
15
- self.vehicle = None
16
- self.icon = icon
17
- self.callback = None
18
-
19
- def __repr__(self):
20
- return self.full_name
21
-
22
- def configurate(self, **args):
23
- pass
24
-
25
- @property
26
- def slug_attr(self):
27
- return camel2slug(self.attr.replace(".", "_"))
28
-
29
- def setup(self, vehicle, **config):
30
- self.vehicle = vehicle
31
- if not self.is_supported:
32
- return False
33
-
34
- self.configurate(**config)
35
- return True
36
-
37
- @property
38
- def vehicle_name(self):
39
- return self.vehicle.vin
40
-
41
- @property
42
- def full_name(self):
43
- return f"{self.vehicle_name} {self.name}"
44
-
45
- @property
46
- def is_mutable(self):
47
- raise NotImplementedError("Must be set")
48
-
49
- @property
50
- def str_state(self):
51
- return self.state
52
-
53
- @property
54
- def state(self):
55
- if hasattr(self.vehicle, self.attr):
56
- return getattr(self.vehicle, self.attr)
57
- else:
58
- _LOGGER.debug(f'Could not find attribute "{self.attr}"')
59
- return self.vehicle.get_attr(self.attr)
60
-
61
- @property
62
- def attributes(self):
63
- return {}
64
-
65
- @property
66
- def is_supported(self):
67
- supported = 'is_' + self.attr + "_supported"
68
- if hasattr(self.vehicle, supported):
69
- return getattr(self.vehicle, supported)
70
- else:
71
- return False
72
-
73
-
74
- class Sensor(Instrument):
75
- def __init__(self, attr, name, icon, unit=None, device_class=None):
76
- super().__init__(component="sensor", attr=attr, name=name, icon=icon)
77
- self.device_class = device_class
78
- self.unit = unit
79
- self.convert = False
80
-
81
- def configurate(self, **config):
82
- if self.unit and config.get('miles', False) is True:
83
- if "km" == self.unit:
84
- self.unit = "mi"
85
- self.convert = True
86
- elif "km/h" == self.unit:
87
- self.unit = "mi/h"
88
- self.convert = True
89
- elif "l/100 km" == self.unit:
90
- self.unit = "l/100 mi"
91
- self.convert = True
92
- elif "kWh/100 km" == self.unit:
93
- self.unit = "kWh/100 mi"
94
- self.convert = True
95
- elif self.unit and config.get('scandinavian_miles', False) is True:
96
- if "km" == self.unit:
97
- self.unit = "mil"
98
- elif "km/h" == self.unit:
99
- self.unit = "mil/h"
100
- elif "l/100 km" == self.unit:
101
- self.unit = "l/100 mil"
102
- elif "kWh/100 km" == self.unit:
103
- self.unit = "kWh/100 mil"
104
-
105
- # Init placeholder for parking heater duration
106
- config.get('parkingheater', 30)
107
- if "pheater_duration" == self.attr:
108
- setValue = config.get('climatisation_duration', 30)
109
- self.vehicle.pheater_duration = setValue
110
-
111
- @property
112
- def is_mutable(self):
113
- return False
114
-
115
- @property
116
- def str_state(self):
117
- if self.unit:
118
- return f'{self.state} {self.unit}'
119
- else:
120
- return f'{self.state}'
121
-
122
- @property
123
- def state(self):
124
- val = super().state
125
- # Convert to miles
126
- if val and self.unit and "mi" in self.unit and self.convert is True:
127
- return int(round(val / 1.609344))
128
- elif val and self.unit and "mi/h" in self.unit and self.convert is True:
129
- return int(round(val / 1.609344))
130
- elif val and self.unit and "gal/100 mi" in self.unit and self.convert is True:
131
- return round(val * 0.4251438, 1)
132
- elif val and self.unit and "kWh/100 mi" in self.unit and self.convert is True:
133
- return round(val * 0.4251438, 1)
134
- elif val and self.unit and "°F" in self.unit and self.convert is True:
135
- temp = round((val * 9 / 5) + 32, 1)
136
- return temp
137
- elif val and self.unit in ['mil', 'mil/h']:
138
- return val / 10
139
- else:
140
- return val
141
-
142
-
143
- class BinarySensor(Instrument):
144
- def __init__(self, attr, name, device_class, icon='', reverse_state=False):
145
- super().__init__(component="binary_sensor", attr=attr, name=name, icon=icon)
146
- self.device_class = device_class
147
- self.reverse_state = reverse_state
148
-
149
- @property
150
- def is_mutable(self):
151
- return False
152
-
153
- @property
154
- def str_state(self):
155
- if self.device_class in ["door", "window"]:
156
- return "Closed" if self.state else "Open"
157
- if self.device_class == "lock":
158
- return "Locked" if self.state else "Unlocked"
159
- if self.device_class == "safety":
160
- return "Warning!" if self.state else "OK"
161
- if self.device_class == "plug":
162
- return "Connected" if self.state else "Disconnected"
163
- if self.state is None:
164
- _LOGGER.error(f"Can not encode state {self.attr} {self.state}")
165
- return "?"
166
- return "On" if self.state else "Off"
167
-
168
- @property
169
- def state(self):
170
- val = super().state
171
-
172
- if isinstance(val, (bool, list)):
173
- if self.reverse_state:
174
- if bool(val):
175
- return False
176
- else:
177
- return True
178
- else:
179
- return bool(val)
180
- elif isinstance(val, str):
181
- return val != "Normal"
182
- return val
183
-
184
- @property
185
- def is_on(self):
186
- return self.state
187
-
188
-
189
- class Switch(Instrument):
190
- def __init__(self, attr, name, icon):
191
- super().__init__(component="switch", attr=attr, name=name, icon=icon)
192
-
193
- @property
194
- def is_mutable(self):
195
- return True
196
-
197
- @property
198
- def str_state(self):
199
- return "On" if self.state else "Off"
200
-
201
- def is_on(self):
202
- return self.state
203
-
204
- def turn_on(self):
205
- pass
206
-
207
- def turn_off(self):
208
- pass
209
-
210
- @property
211
- def assumed_state(self):
212
- return True
213
-
214
-
215
- class Climate(Instrument):
216
- def __init__(self, attr, name, icon):
217
- super().__init__(component="climate", attr=attr, name=name, icon=icon)
218
-
219
- @property
220
- def hvac_mode(self):
221
- pass
222
-
223
- @property
224
- def target_temperature(self):
225
- pass
226
-
227
- def set_temperature(self, **kwargs):
228
- pass
229
-
230
- def set_hvac_mode(self, hvac_mode):
231
- pass
232
-
233
-
234
- class ElectricClimatisationClimate(Climate):
235
- def __init__(self):
236
- super().__init__(attr="electric_climatisation", name="Electric Climatisation", icon="mdi:radiator")
237
-
238
- @property
239
- def hvac_mode(self):
240
- return self.vehicle.electric_climatisation
241
-
242
- @property
243
- def target_temperature(self):
244
- return self.vehicle.climatisation_target_temperature
245
-
246
- async def set_temperature(self, temperature):
247
- await self.vehicle.climatisation_target(temperature)
248
-
249
- async def set_hvac_mode(self, hvac_mode):
250
- if hvac_mode:
251
- await self.vehicle.climatisation('electric')
252
- else:
253
- await self.vehicle.climatisation('off')
254
-
255
-
256
- class CombustionClimatisationClimate(Climate):
257
- def __init__(self):
258
- super().__init__(attr="pheater_heating", name="Parking Heater Climatisation", icon="mdi:radiator")
259
-
260
- def configurate(self, **config):
261
- self.spin = config.get('spin', '')
262
- self.duration = config.get('combustionengineheatingduration', 30)
263
-
264
- @property
265
- def hvac_mode(self):
266
- return self.vehicle.pheater_heating
267
-
268
- @property
269
- def target_temperature(self):
270
- return self.vehicle.climatisation_target_temperature
271
-
272
- async def set_temperature(self, temperature):
273
- await self.vehicle.setClimatisationTargetTemperature(temperature)
274
-
275
- async def set_hvac_mode(self, hvac_mode):
276
- if hvac_mode:
277
- await self.vehicle.pheater_climatisation(spin=self.spin, duration=self.duration, mode='heating')
278
- else:
279
- await self.vehicle.pheater_climatisation(spin=self.spin, mode='off')
280
-
281
-
282
- class Position(Instrument):
283
- def __init__(self):
284
- super().__init__(component="device_tracker", attr="position", name="Position")
285
-
286
- @property
287
- def is_mutable(self):
288
- return False
289
-
290
- @property
291
- def state(self):
292
- state = super().state #or {}
293
- return (
294
- state.get("lat", "?"),
295
- state.get("lng", "?"),
296
- state.get("address", "?"),
297
- state.get("timestamp", None),
298
- )
299
-
300
- @property
301
- def str_state(self):
302
- state = super().state #or {}
303
- ts = state.get("timestamp", None)
304
- if isinstance(ts, str):
305
- time = str(datetime.strptime(ts,'%Y-%m-%dT%H:%M:%SZ').astimezone(tz=None))
306
- elif isinstance(ts, datetime):
307
- time = str(ts.astimezone(tz=None))
308
- else:
309
- time = None
310
- return (
311
- state.get("lat", "?"),
312
- state.get("lng", "?"),
313
- state.get("address", "?"),
314
- time,
315
- )
316
-
317
-
318
- class DoorLock(Instrument):
319
- def __init__(self):
320
- super().__init__(component="lock", attr="door_locked", name="Door locked")
321
-
322
- def configurate(self, **config):
323
- self.spin = config.get('spin', '')
324
-
325
- @property
326
- def is_mutable(self):
327
- return True
328
-
329
- @property
330
- def str_state(self):
331
- return "Locked" if self.state else "Unlocked"
332
-
333
- @property
334
- def state(self):
335
- return self.vehicle.door_locked
336
-
337
- @property
338
- def is_locked(self):
339
- return self.state
340
-
341
- async def lock(self):
342
- try:
343
- response = await self.vehicle.set_lock('lock', self.spin)
344
- await self.vehicle.update()
345
- if self.callback is not None:
346
- self.callback()
347
- return response
348
- except Exception as e:
349
- _LOGGER.error(f"Lock failed: {e}")
350
- return False
351
-
352
- async def unlock(self):
353
- try:
354
- response = await self.vehicle.set_lock('unlock', self.spin)
355
- await self.vehicle.update()
356
- if self.callback is not None:
357
- self.callback()
358
- return response
359
- except Exception as e:
360
- _LOGGER.error(f"Unlock failed: {e}")
361
- return False
362
-
363
- @property
364
- def attributes(self):
365
- return dict(last_result = self.vehicle.lock_action_status)
366
-
367
-
368
- class TrunkLock(Instrument):
369
- def __init__(self):
370
- super().__init__(component="lock", attr="trunk_locked", name="Trunk locked")
371
-
372
- @property
373
- def is_mutable(self):
374
- return True
375
-
376
- @property
377
- def str_state(self):
378
- return "Locked" if self.state else "Unlocked"
379
-
380
- @property
381
- def state(self):
382
- return self.vehicle.trunk_locked
383
-
384
- @property
385
- def is_locked(self):
386
- return self.state
387
-
388
- async def lock(self):
389
- return None
390
-
391
- async def unlock(self):
392
- return None
393
-
394
- # Switches
395
- class RequestHonkAndFlash(Switch):
396
- def __init__(self):
397
- super().__init__(attr="request_honkandflash", name="Start honking and flashing", icon="mdi:car-emergency")
398
-
399
- @property
400
- def state(self):
401
- return self.vehicle.request_honkandflash
402
-
403
- async def turn_on(self):
404
- await self.vehicle.set_honkandflash('honkandflash')
405
- await self.vehicle.update()
406
- if self.callback is not None:
407
- self.callback()
408
-
409
- async def turn_off(self):
410
- pass
411
-
412
- @property
413
- def assumed_state(self):
414
- return False
415
-
416
- @property
417
- def attributes(self):
418
- return dict(last_result = self.vehicle.honkandflash_action_status)
419
-
420
-
421
- class RequestFlash(Switch):
422
- def __init__(self):
423
- super().__init__(attr="request_flash", name="Start flashing", icon="mdi:car-parking-lights")
424
-
425
- @property
426
- def state(self):
427
- return self.vehicle.request_flash
428
-
429
- async def turn_on(self):
430
- await self.vehicle.set_honkandflash('flash')
431
- await self.vehicle.update()
432
- if self.callback is not None:
433
- self.callback()
434
-
435
- async def turn_off(self):
436
- pass
437
-
438
- @property
439
- def assumed_state(self):
440
- return False
441
-
442
- @property
443
- def attributes(self):
444
- return dict(last_result = self.vehicle.honkandflash_action_status)
445
-
446
-
447
- class RequestUpdate(Switch):
448
- def __init__(self):
449
- super().__init__(attr="refresh_data", name="Force data refresh", icon="mdi:car-connected")
450
-
451
- @property
452
- def state(self):
453
- return self.vehicle.refresh_data
454
-
455
- async def turn_on(self):
456
- await self.vehicle.set_refresh()
457
- await self.vehicle.update()
458
- if self.callback is not None:
459
- self.callback()
460
-
461
- async def turn_off(self):
462
- pass
463
-
464
- @property
465
- def assumed_state(self):
466
- return False
467
-
468
- @property
469
- def attributes(self):
470
- return dict(last_result = self.vehicle.refresh_action_status)
471
-
472
-
473
- class ElectricClimatisation(Switch):
474
- def __init__(self):
475
- super().__init__(attr="electric_climatisation", name="Electric Climatisation", icon="mdi:radiator")
476
-
477
- @property
478
- def state(self):
479
- return self.vehicle.electric_climatisation
480
-
481
- async def turn_on(self):
482
- await self.vehicle.set_climatisation(mode = 'electric')
483
- await self.vehicle.update()
484
-
485
- async def turn_off(self):
486
- await self.vehicle.set_climatisation(mode = 'off')
487
- await self.vehicle.update()
488
-
489
- @property
490
- def assumed_state(self):
491
- return False
492
-
493
- @property
494
- def attributes(self):
495
- attrs = {}
496
- if self.vehicle.is_electric_climatisation_attributes_supported:
497
- attrs = self.vehicle.electric_climatisation_attributes
498
- attrs['last_result'] = self.vehicle.climater_action_status
499
- else:
500
- attrs['last_result'] = self.vehicle.climater_action_status
501
- return attrs
502
-
503
-
504
- class AuxiliaryClimatisation(Switch):
505
- def __init__(self):
506
- super().__init__(attr="auxiliary_climatisation", name="Auxiliary Climatisation", icon="mdi:radiator")
507
-
508
- def configurate(self, **config):
509
- self.spin = config.get('spin', '')
510
-
511
- @property
512
- def state(self):
513
- return self.vehicle.auxiliary_climatisation
514
-
515
- async def turn_on(self):
516
- await self.vehicle.set_climatisation(mode = 'auxiliary', spin = self.spin)
517
- await self.vehicle.update()
518
-
519
- async def turn_off(self):
520
- await self.vehicle.set_climatisation(mode = 'off')
521
- await self.vehicle.update()
522
-
523
- @property
524
- def assumed_state(self):
525
- return False
526
-
527
- @property
528
- def attributes(self):
529
- return dict(last_result = self.vehicle.climater_action_status)
530
-
531
-
532
- class Charging(Switch):
533
- def __init__(self):
534
- super().__init__(attr="charging", name="Charging", icon="mdi:battery")
535
-
536
- @property
537
- def state(self):
538
- return self.vehicle.charging
539
-
540
- async def turn_on(self):
541
- await self.vehicle.set_charger('start')
542
- await self.vehicle.update()
543
-
544
- async def turn_off(self):
545
- await self.vehicle.set_charger('stop')
546
- await self.vehicle.update()
547
-
548
- @property
549
- def assumed_state(self):
550
- return False
551
-
552
- @property
553
- def attributes(self):
554
- return dict(last_result = self.vehicle.charger_action_status)
555
-
556
-
557
- class WindowHeater(Switch):
558
- def __init__(self):
559
- super().__init__(attr="window_heater", name="Window Heater", icon="mdi:car-defrost-rear")
560
-
561
- @property
562
- def state(self):
563
- return self.vehicle.window_heater
564
-
565
- async def turn_on(self):
566
- await self.vehicle.set_window_heating('start')
567
- await self.vehicle.update()
568
-
569
- async def turn_off(self):
570
- await self.vehicle.set_window_heating('stop')
571
- await self.vehicle.update()
572
-
573
- @property
574
- def assumed_state(self):
575
- return False
576
-
577
-
578
- @property
579
- def attributes(self):
580
- return dict(last_result = self.vehicle.climater_action_status)
581
-
582
-
583
- class SeatHeating(Switch):
584
- def __init__(self):
585
- super().__init__(attr="seat_heating", name="Seat Heating", icon="mdi:seat-recline-normal")
586
-
587
- @property
588
- def state(self):
589
- return self.vehicle.seat_heating
590
-
591
- async def turn_on(self):
592
- #await self.vehicle.set_seat_heating('start')
593
- #await self.vehicle.update()
594
- pass
595
-
596
- async def turn_off(self):
597
- #await self.vehicle.set_seat_heating('stop')
598
- #await self.vehicle.update()
599
- pass
600
-
601
- @property
602
- def assumed_state(self):
603
- return False
604
-
605
- #@property
606
- #def attributes(self):
607
- # return dict(last_result = self.vehicle.climater_action_status)
608
-
609
-
610
- class BatteryClimatisation(Switch):
611
- def __init__(self):
612
- super().__init__(attr="climatisation_without_external_power", name="Climatisation from battery", icon="mdi:power-plug")
613
-
614
- @property
615
- def state(self):
616
- return self.vehicle.climatisation_without_external_power
617
-
618
- async def turn_on(self):
619
- await self.vehicle.set_battery_climatisation(True)
620
- await self.vehicle.update()
621
-
622
- async def turn_off(self):
623
- await self.vehicle.set_battery_climatisation(False)
624
- await self.vehicle.update()
625
-
626
- @property
627
- def assumed_state(self):
628
- return False
629
-
630
- @property
631
- def attributes(self):
632
- return dict(last_result = self.vehicle.climater_action_status)
633
-
634
-
635
- class PHeaterHeating(Switch):
636
- def __init__(self):
637
- super().__init__(attr="pheater_heating", name="Parking Heater Heating", icon="mdi:radiator")
638
-
639
- def configurate(self, **config):
640
- self.spin = config.get('spin', '')
641
- self.duration = config.get('combustionengineheatingduration', 30)
642
-
643
- @property
644
- def state(self):
645
- return self.vehicle.pheater_heating
646
-
647
- async def turn_on(self):
648
- await self.vehicle.set_pheater(mode='heating', spin=self.spin)
649
- await self.vehicle.update()
650
-
651
- async def turn_off(self):
652
- await self.vehicle.set_pheater(mode='off', spin=self.spin)
653
- await self.vehicle.update()
654
-
655
- @property
656
- def assumed_state(self):
657
- return False
658
-
659
- @property
660
- def attributes(self):
661
- return dict(last_result = self.vehicle.pheater_action_status)
662
-
663
-
664
- class PHeaterVentilation(Switch):
665
- def __init__(self):
666
- super().__init__(attr="pheater_ventilation", name="Parking Heater Ventilation", icon="mdi:radiator")
667
-
668
- def configurate(self, **config):
669
- self.spin = config.get('spin', '')
670
- self.duration = config.get('combustionengineclimatisationduration', 30)
671
-
672
- @property
673
- def state(self):
674
- return self.vehicle.pheater_ventilation
675
-
676
- async def turn_on(self):
677
- await self.vehicle.set_pheater(mode='ventilation', spin=self.spin)
678
- await self.vehicle.update()
679
-
680
- async def turn_off(self):
681
- await self.vehicle.set_pheater(mode='off', spin=self.spin)
682
- await self.vehicle.update()
683
-
684
- @property
685
- def assumed_state(self):
686
- return False
687
-
688
- @property
689
- def attributes(self):
690
- return dict(last_result = self.vehicle.pheater_action_status)
691
-
692
-
693
- class Warnings(Sensor):
694
- def __init__(self):
695
- super().__init__(attr="warnings", name="Warnings", icon="mdi:alarm-light")
696
-
697
- @property
698
- def state(self):
699
- return self.vehicle.warnings
700
-
701
- @property
702
- def assumed_state(self):
703
- return False
704
-
705
- class DepartureTimer1(Switch):
706
- def __init__(self):
707
- super().__init__(attr="departure1", name="Departure timer 1", icon="mdi:radiator")
708
-
709
- def configurate(self, **config):
710
- self.spin = config.get('spin', '')
711
-
712
- @property
713
- def state(self):
714
- status = self.vehicle.departure1.get("timerProgrammedStatus", "")
715
- if status == "programmed":
716
- return True
717
- else:
718
- return False
719
-
720
- async def turn_on(self):
721
- await self.vehicle.set_timer_active(id=1, action="on")
722
- await self.vehicle.update()
723
-
724
- async def turn_off(self):
725
- await self.vehicle.set_timer_active(id=1, action="off")
726
- await self.vehicle.update()
727
-
728
- @property
729
- def assumed_state(self):
730
- return False
731
-
732
- @property
733
- def attributes(self):
734
- return dict(self.vehicle.departure1)
735
-
736
-
737
- class DepartureTimer2(Switch):
738
- def __init__(self):
739
- super().__init__(attr="departure2", name="Departure timer 2", icon="mdi:radiator")
740
-
741
- def configurate(self, **config):
742
- self.spin = config.get('spin', '')
743
-
744
- @property
745
- def state(self):
746
- status = self.vehicle.departure2.get("timerProgrammedStatus", "")
747
- if status == "programmed":
748
- return True
749
- else:
750
- return False
751
-
752
- async def turn_on(self):
753
- await self.vehicle.set_timer_active(id=2, action="on")
754
- await self.vehicle.update()
755
-
756
- async def turn_off(self):
757
- await self.vehicle.set_timer_active(id=2, action="off")
758
- await self.vehicle.update()
759
-
760
- @property
761
- def assumed_state(self):
762
- return False
763
-
764
- @property
765
- def attributes(self):
766
- return dict(self.vehicle.departure2)
767
-
768
- class DepartureTimer3(Switch):
769
- def __init__(self):
770
- super().__init__(attr="departure3", name="Departure timer 3", icon="mdi:radiator")
771
-
772
- def configurate(self, **config):
773
- self.spin = config.get('spin', '')
774
-
775
- @property
776
- def state(self):
777
- status = self.vehicle.departure3.get("timerProgrammedStatus", "")
778
- if status == "programmed":
779
- return True
780
- else:
781
- return False
782
-
783
- async def turn_on(self):
784
- await self.vehicle.set_timer_active(id=3, action="on")
785
- await self.vehicle.update()
786
-
787
- async def turn_off(self):
788
- await self.vehicle.set_timer_active(id=3, action="off")
789
- await self.vehicle.update()
790
-
791
- @property
792
- def assumed_state(self):
793
- return False
794
-
795
- @property
796
- def attributes(self):
797
- return dict(self.vehicle.departure3)
798
-
799
-
800
- class RequestResults(Sensor):
801
- def __init__(self):
802
- super().__init__(attr="request_results", name="Request results", icon="mdi:chat-alert", unit=None)
803
-
804
- @property
805
- def state(self):
806
- if self.vehicle.request_results.get('state', False):
807
- return self.vehicle.request_results.get('state')
808
- return 'N/A'
809
-
810
- @property
811
- def assumed_state(self):
812
- return False
813
-
814
- @property
815
- def attributes(self):
816
- return dict(self.vehicle.request_results)
817
-
818
-
819
- def create_instruments():
820
- return [
821
- Position(),
822
- DoorLock(),
823
- TrunkLock(),
824
- RequestFlash(),
825
- RequestHonkAndFlash(),
826
- RequestUpdate(),
827
- WindowHeater(),
828
- BatteryClimatisation(),
829
- ElectricClimatisation(),
830
- AuxiliaryClimatisation(),
831
- PHeaterVentilation(),
832
- PHeaterHeating(),
833
- #ElectricClimatisationClimate(),
834
- #CombustionClimatisationClimate(),
835
- Charging(),
836
- Warnings(),
837
- RequestResults(),
838
- DepartureTimer1(),
839
- DepartureTimer2(),
840
- DepartureTimer3(),
841
- Sensor(
842
- attr="distance",
843
- name="Odometer",
844
- icon="mdi:speedometer",
845
- unit="km",
846
- ),
847
- Sensor(
848
- attr="battery_level",
849
- name="Battery level",
850
- icon="mdi:battery",
851
- unit="%",
852
- device_class="battery"
853
- ),
854
- Sensor(
855
- attr="min_charge_level",
856
- name="Minimum charge level",
857
- icon="mdi:battery-positive",
858
- unit="%",
859
- device_class="battery"
860
- ),
861
- Sensor(
862
- attr="adblue_level",
863
- name="Adblue level",
864
- icon="mdi:fuel",
865
- unit="km",
866
- ),
867
- Sensor(
868
- attr="fuel_level",
869
- name="Fuel level",
870
- icon="mdi:fuel",
871
- unit="%",
872
- ),
873
- Sensor(
874
- attr="service_inspection",
875
- name="Service inspection days",
876
- icon="mdi:garage",
877
- unit="days",
878
- ),
879
- Sensor(
880
- attr="service_inspection_distance",
881
- name="Service inspection distance",
882
- icon="mdi:garage",
883
- unit="km",
884
- ),
885
- Sensor(
886
- attr="oil_inspection",
887
- name="Oil inspection days",
888
- icon="mdi:oil",
889
- unit="days",
890
- ),
891
- Sensor(
892
- attr="oil_inspection_distance",
893
- name="Oil inspection distance",
894
- icon="mdi:oil",
895
- unit="km",
896
- ),
897
- Sensor(
898
- attr="last_connected",
899
- name="Last connected",
900
- icon="mdi:clock",
901
- device_class="timestamp"
902
- ),
903
- Sensor(
904
- attr="parking_time",
905
- name="Parking time",
906
- icon="mdi:clock",
907
- device_class="timestamp"
908
- ),
909
- Sensor(
910
- attr="charging_time_left",
911
- name="Charging time left",
912
- icon="mdi:battery-charging-100",
913
- unit="h",
914
- ),
915
- Sensor(
916
- attr="charging_power",
917
- name="Charging power",
918
- icon="mdi:flash",
919
- unit="W",
920
- device_class="power"
921
- ),
922
- Sensor(
923
- attr="charge_rate",
924
- name="Charging rate",
925
- icon="mdi:battery-heart",
926
- unit="km/h"
927
- ),
928
- Sensor(
929
- attr="electric_range",
930
- name="Electric range",
931
- icon="mdi:car-electric",
932
- unit="km",
933
- ),
934
- Sensor(
935
- attr="combustion_range",
936
- name="Combustion range",
937
- icon="mdi:car",
938
- unit="km",
939
- ),
940
- Sensor(
941
- attr="combined_range",
942
- name="Combined range",
943
- icon="mdi:car",
944
- unit="km",
945
- ),
946
- Sensor(
947
- attr="charge_max_ampere",
948
- name="Charger max ampere",
949
- icon="mdi:flash",
950
- unit="A",
951
- device_class="current"
952
- ),
953
- Sensor(
954
- attr="climatisation_target_temperature",
955
- name="Climatisation target temperature",
956
- icon="mdi:thermometer",
957
- unit="°C",
958
- device_class="temperature"
959
- ),
960
- Sensor(
961
- attr="climatisation_time_left",
962
- name="Climatisation time left",
963
- icon="mdi:clock",
964
- unit="h",
965
- ),
966
- Sensor(
967
- attr="trip_last_average_speed",
968
- name="Last trip average speed",
969
- icon="mdi:speedometer",
970
- unit="km/h",
971
- ),
972
- Sensor(
973
- attr="trip_last_average_electric_consumption",
974
- name="Last trip average electric consumption",
975
- icon="mdi:car-battery",
976
- unit="kWh/100 km",
977
- ),
978
- Sensor(
979
- attr="trip_last_average_fuel_consumption",
980
- name="Last trip average fuel consumption",
981
- icon="mdi:fuel",
982
- unit="l/100 km",
983
- ),
984
- Sensor(
985
- attr="trip_last_duration",
986
- name="Last trip duration",
987
- icon="mdi:clock",
988
- unit="min",
989
- ),
990
- Sensor(
991
- attr="trip_last_length",
992
- name="Last trip length",
993
- icon="mdi:map-marker-distance",
994
- unit="km",
995
- ),
996
- Sensor(
997
- attr="trip_last_recuperation",
998
- name="Last trip recuperation",
999
- icon="mdi:battery-plus",
1000
- unit="kWh/100 km",
1001
- ),
1002
- Sensor(
1003
- attr="trip_last_average_recuperation",
1004
- name="Last trip average recuperation",
1005
- icon="mdi:battery-plus",
1006
- unit="kWh/100 km",
1007
- ),
1008
- Sensor(
1009
- attr="trip_last_average_auxillary_consumption",
1010
- name="Last trip average auxillary consumption",
1011
- icon="mdi:flash",
1012
- unit="kWh/100 km",
1013
- ),
1014
- Sensor(
1015
- attr="trip_last_average_aux_consumer_consumption",
1016
- name="Last trip average auxillary consumer consumption",
1017
- icon="mdi:flash",
1018
- unit="kWh/100 km",
1019
- ),
1020
- Sensor(
1021
- attr="trip_last_total_electric_consumption",
1022
- name="Last trip total electric consumption",
1023
- icon="mdi:car-battery",
1024
- unit="kWh/100 km",
1025
- ),
1026
- Sensor(
1027
- attr="trip_last_cycle_average_speed",
1028
- name="Last cycle average speed",
1029
- icon="mdi:speedometer",
1030
- unit="km/h",
1031
- ),
1032
- Sensor(
1033
- attr="trip_last_cycle_average_electric_consumption",
1034
- name="Last cycle_average electric consumption",
1035
- icon="mdi:car-battery",
1036
- unit="kWh/100 km",
1037
- ),
1038
- Sensor(
1039
- attr="trip_last_cycle_average_fuel_consumption",
1040
- name="Last cycle average fuel consumption",
1041
- icon="mdi:fuel",
1042
- unit="l/100 km",
1043
- ),
1044
- Sensor(
1045
- attr="trip_last_cycle_average_auxillary_consumption",
1046
- name="Last cycle average auxillary consumption",
1047
- icon="mdi:flash",
1048
- unit="kWh/100 km",
1049
- ),
1050
- Sensor(
1051
- attr="trip_last_cycle_duration",
1052
- name="Last cycle duration",
1053
- icon="mdi:clock",
1054
- unit="min",
1055
- ),
1056
- Sensor(
1057
- attr="trip_last_cycle_length",
1058
- name="Last cycle length",
1059
- icon="mdi:map-marker-distance",
1060
- unit="km",
1061
- ),
1062
- Sensor(
1063
- attr="trip_last_cycle_recuperation",
1064
- name="Last cycle recuperation",
1065
- icon="mdi:battery-plus",
1066
- unit="kWh/100 km",
1067
- ),
1068
- Sensor(
1069
- attr="trip_last_cycle_average_recuperation",
1070
- name="Last cycle average recuperation",
1071
- icon="mdi:battery-plus",
1072
- unit="kWh/100 km",
1073
- ),
1074
- Sensor(
1075
- attr="trip_last_cycle_average_aux_consumer_consumption",
1076
- name="Last cycle average auxillary consumer consumption",
1077
- icon="mdi:flash",
1078
- unit="kWh/100 km",
1079
- ),
1080
- Sensor(
1081
- attr="trip_last_cycle_total_electric_consumption",
1082
- name="Last cycle total electric consumption",
1083
- icon="mdi:car-battery",
1084
- unit="kWh/100 km",
1085
- ),
1086
- Sensor(
1087
- attr="model_image_large",
1088
- name="Model image URL (Large)",
1089
- icon="mdi:file-image",
1090
- ),
1091
- Sensor(
1092
- attr="model_image_small",
1093
- name="Model image URL (Small)",
1094
- icon="mdi:file-image",
1095
- ),
1096
- Sensor(
1097
- attr="pheater_status",
1098
- name="Parking Heater heating/ventilation status",
1099
- icon="mdi:radiator",
1100
- ),
1101
- Sensor(
1102
- attr="pheater_duration",
1103
- name="Parking Heater heating/ventilation duration",
1104
- icon="mdi:timer",
1105
- unit="minutes",
1106
- ),
1107
- Sensor(
1108
- attr="outside_temperature",
1109
- name="Outside temperature",
1110
- icon="mdi:thermometer",
1111
- unit="°C",
1112
- device_class="temperature"
1113
- ),
1114
- Sensor(
1115
- attr="requests_remaining",
1116
- name="Requests remaining",
1117
- icon="mdi:chat-alert",
1118
- unit="",
1119
- ),
1120
- BinarySensor(
1121
- attr="external_power",
1122
- name="External power",
1123
- device_class="power"
1124
- ),
1125
- BinarySensor(
1126
- attr="energy_flow",
1127
- name="Energy flow",
1128
- device_class="power"
1129
- ),
1130
- BinarySensor(
1131
- attr="parking_light",
1132
- name="Parking light",
1133
- device_class="light",
1134
- icon="mdi:car-parking-lights"
1135
- ),
1136
- BinarySensor(
1137
- attr="door_locked",
1138
- name="Doors locked",
1139
- device_class="lock",
1140
- reverse_state=False
1141
- ),
1142
- BinarySensor(
1143
- attr="door_closed_left_front",
1144
- name="Door closed left front",
1145
- device_class="door",
1146
- reverse_state=False,
1147
- icon="mdi:car-door"
1148
- ),
1149
- BinarySensor(
1150
- attr="door_closed_right_front",
1151
- name="Door closed right front",
1152
- device_class="door",
1153
- reverse_state=False,
1154
- icon="mdi:car-door"
1155
- ),
1156
- BinarySensor(
1157
- attr="door_closed_left_back",
1158
- name="Door closed left back",
1159
- device_class="door",
1160
- reverse_state=False,
1161
- icon="mdi:car-door"
1162
- ),
1163
- BinarySensor(
1164
- attr="door_closed_right_back",
1165
- name="Door closed right back",
1166
- device_class="door",
1167
- reverse_state=False,
1168
- icon="mdi:car-door"
1169
- ),
1170
- BinarySensor(
1171
- attr="trunk_locked",
1172
- name="Trunk locked",
1173
- device_class="lock",
1174
- reverse_state=False
1175
- ),
1176
- BinarySensor(
1177
- attr="trunk_closed",
1178
- name="Trunk closed",
1179
- device_class="door",
1180
- reverse_state=False
1181
- ),
1182
- BinarySensor(
1183
- attr="hood_closed",
1184
- name="Hood closed",
1185
- device_class="door",
1186
- reverse_state=False
1187
- ),
1188
- BinarySensor(
1189
- attr="charging_cable_connected",
1190
- name="Charging cable connected",
1191
- device_class="plug",
1192
- reverse_state=False
1193
- ),
1194
- BinarySensor(
1195
- attr="charging_cable_locked",
1196
- name="Charging cable locked",
1197
- device_class="lock",
1198
- reverse_state=False
1199
- ),
1200
- BinarySensor(
1201
- attr="sunroof_closed",
1202
- name="Sunroof closed",
1203
- device_class="window",
1204
- reverse_state=False
1205
- ),
1206
- BinarySensor(
1207
- attr="windows_closed",
1208
- name="Windows closed",
1209
- device_class="window",
1210
- reverse_state=False
1211
- ),
1212
- BinarySensor(
1213
- attr="window_closed_left_front",
1214
- name="Window closed left front",
1215
- device_class="window",
1216
- reverse_state=False
1217
- ),
1218
- BinarySensor(
1219
- attr="window_closed_left_back",
1220
- name="Window closed left back",
1221
- device_class="window",
1222
- reverse_state=False
1223
- ),
1224
- BinarySensor(
1225
- attr="window_closed_right_front",
1226
- name="Window closed right front",
1227
- device_class="window",
1228
- reverse_state=False
1229
- ),
1230
- BinarySensor(
1231
- attr="window_closed_right_back",
1232
- name="Window closed right back",
1233
- device_class="window",
1234
- reverse_state=False
1235
- ),
1236
- BinarySensor(
1237
- attr="vehicle_moving",
1238
- name="Vehicle Moving",
1239
- device_class="moving"
1240
- ),
1241
- BinarySensor(
1242
- attr="request_in_progress",
1243
- name="Request in progress",
1244
- device_class="connectivity"
1245
- ),
1246
- ]
1247
-
1248
-
1249
- class Dashboard:
1250
- def __init__(self, vehicle, **config):
1251
- self._config = config
1252
- self.instruments = [
1253
- instrument
1254
- for instrument in create_instruments()
1255
- if instrument.setup(vehicle, **config)
1256
- ]
1257
- _LOGGER.debug("Supported instruments: " + ", ".join(str(inst.attr) for inst in self.instruments))
1258
-
1
+ # Utilities for integration with Home Assistant
2
+ # Thanks to molobrakos and Farfar
3
+
4
+ import logging
5
+ from datetime import datetime
6
+ from .utilities import camel2slug
7
+
8
+ _LOGGER = logging.getLogger(__name__)
9
+
10
+ class Instrument:
11
+ def __init__(self, component, attr, name, icon=None):
12
+ self.attr = attr
13
+ self.component = component
14
+ self.name = name
15
+ self.vehicle = None
16
+ self.icon = icon
17
+ self.callback = None
18
+
19
+ def __repr__(self):
20
+ return self.full_name
21
+
22
+ def configurate(self, **args):
23
+ pass
24
+
25
+ @property
26
+ def slug_attr(self):
27
+ return camel2slug(self.attr.replace(".", "_"))
28
+
29
+ def setup(self, vehicle, **config):
30
+ self.vehicle = vehicle
31
+ if not self.is_supported:
32
+ return False
33
+
34
+ self.configurate(**config)
35
+ return True
36
+
37
+ @property
38
+ def vehicle_name(self):
39
+ return self.vehicle.vin
40
+
41
+ @property
42
+ def full_name(self):
43
+ return f"{self.vehicle_name} {self.name}"
44
+
45
+ @property
46
+ def is_mutable(self):
47
+ raise NotImplementedError("Must be set")
48
+
49
+ @property
50
+ def str_state(self):
51
+ return self.state
52
+
53
+ @property
54
+ def state(self):
55
+ if hasattr(self.vehicle, self.attr):
56
+ return getattr(self.vehicle, self.attr)
57
+ else:
58
+ _LOGGER.debug(f'Could not find attribute "{self.attr}"')
59
+ return self.vehicle.get_attr(self.attr)
60
+
61
+ @property
62
+ def attributes(self):
63
+ return {}
64
+
65
+ @property
66
+ def is_supported(self):
67
+ supported = 'is_' + self.attr + "_supported"
68
+ if hasattr(self.vehicle, supported):
69
+ return getattr(self.vehicle, supported)
70
+ else:
71
+ return False
72
+
73
+
74
+ class Sensor(Instrument):
75
+ def __init__(self, attr, name, icon, unit=None, device_class=None):
76
+ super().__init__(component="sensor", attr=attr, name=name, icon=icon)
77
+ self.device_class = device_class
78
+ self.unit = unit
79
+ self.convert = False
80
+
81
+ def configurate(self, **config):
82
+ if self.unit and config.get('miles', False) is True:
83
+ if "km" == self.unit:
84
+ self.unit = "mi"
85
+ self.convert = True
86
+ elif "km/h" == self.unit:
87
+ self.unit = "mi/h"
88
+ self.convert = True
89
+ elif "l/100km" == self.unit:
90
+ self.unit = "l/100 mi"
91
+ self.convert = True
92
+ elif "kWh/100km" == self.unit:
93
+ self.unit = "kWh/100 mi"
94
+ self.convert = True
95
+ elif self.unit and config.get('scandinavian_miles', False) is True:
96
+ if "km" == self.unit:
97
+ self.unit = "mil"
98
+ elif "km/h" == self.unit:
99
+ self.unit = "mil/h"
100
+ elif "l/100km" == self.unit:
101
+ self.unit = "l/100 mil"
102
+ elif "kWh/100km" == self.unit:
103
+ self.unit = "kWh/100 mil"
104
+
105
+ # Init placeholder for parking heater duration
106
+ config.get('parkingheater', 30)
107
+ if "pheater_duration" == self.attr:
108
+ setValue = config.get('climatisation_duration', 30)
109
+ self.vehicle.pheater_duration = setValue
110
+
111
+ @property
112
+ def is_mutable(self):
113
+ return False
114
+
115
+ @property
116
+ def str_state(self):
117
+ if self.unit:
118
+ return f'{self.state} {self.unit}'
119
+ else:
120
+ return f'{self.state}'
121
+
122
+ @property
123
+ def state(self):
124
+ val = super().state
125
+ # Convert to miles
126
+ if val and self.unit and "mi" in self.unit and self.convert is True:
127
+ return int(round(val / 1.609344))
128
+ elif val and self.unit and "mi/h" in self.unit and self.convert is True:
129
+ return int(round(val / 1.609344))
130
+ elif val and self.unit and "gal/100 mi" in self.unit and self.convert is True:
131
+ return round(val * 0.4251438, 1)
132
+ elif val and self.unit and "kWh/100 mi" in self.unit and self.convert is True:
133
+ return round(val * 0.4251438, 1)
134
+ elif val and self.unit and "°F" in self.unit and self.convert is True:
135
+ temp = round((val * 9 / 5) + 32, 1)
136
+ return temp
137
+ elif val and self.unit in ['mil', 'mil/h']:
138
+ return val / 10
139
+ else:
140
+ return val
141
+
142
+
143
+ class BinarySensor(Instrument):
144
+ def __init__(self, attr, name, device_class, icon='', reverse_state=False):
145
+ super().__init__(component="binary_sensor", attr=attr, name=name, icon=icon)
146
+ self.device_class = device_class
147
+ self.reverse_state = reverse_state
148
+
149
+ @property
150
+ def is_mutable(self):
151
+ return False
152
+
153
+ @property
154
+ def str_state(self):
155
+ if self.device_class in ["door", "window"]:
156
+ return "Closed" if self.state else "Open"
157
+ if self.device_class == "lock":
158
+ return "Locked" if self.state else "Unlocked"
159
+ if self.device_class == "safety":
160
+ return "Warning!" if self.state else "OK"
161
+ if self.device_class == "plug":
162
+ return "Connected" if self.state else "Disconnected"
163
+ if self.state is None:
164
+ _LOGGER.error(f"Can not encode state {self.attr} {self.state}")
165
+ return "?"
166
+ return "On" if self.state else "Off"
167
+
168
+ @property
169
+ def state(self):
170
+ val = super().state
171
+
172
+ if isinstance(val, (bool, list)):
173
+ if self.reverse_state:
174
+ if bool(val):
175
+ return False
176
+ else:
177
+ return True
178
+ else:
179
+ return bool(val)
180
+ elif isinstance(val, str):
181
+ return val != "Normal"
182
+ return val
183
+
184
+ @property
185
+ def is_on(self):
186
+ return self.state
187
+
188
+
189
+ class Switch(Instrument):
190
+ def __init__(self, attr, name, icon):
191
+ super().__init__(component="switch", attr=attr, name=name, icon=icon)
192
+
193
+ @property
194
+ def is_mutable(self):
195
+ return True
196
+
197
+ @property
198
+ def str_state(self):
199
+ return "On" if self.state else "Off"
200
+
201
+ def is_on(self):
202
+ return self.state
203
+
204
+ def turn_on(self):
205
+ pass
206
+
207
+ def turn_off(self):
208
+ pass
209
+
210
+ @property
211
+ def assumed_state(self):
212
+ return True
213
+
214
+
215
+ class Climate(Instrument):
216
+ def __init__(self, attr, name, icon):
217
+ super().__init__(component="climate", attr=attr, name=name, icon=icon)
218
+
219
+ @property
220
+ def hvac_mode(self):
221
+ pass
222
+
223
+ @property
224
+ def target_temperature(self):
225
+ pass
226
+
227
+ def set_temperature(self, **kwargs):
228
+ pass
229
+
230
+ def set_hvac_mode(self, hvac_mode):
231
+ pass
232
+
233
+
234
+ class ElectricClimatisationClimate(Climate):
235
+ def __init__(self):
236
+ super().__init__(attr="electric_climatisation", name="Electric Climatisation", icon="mdi:radiator")
237
+
238
+ @property
239
+ def hvac_mode(self):
240
+ return self.vehicle.electric_climatisation
241
+
242
+ @property
243
+ def target_temperature(self):
244
+ return self.vehicle.climatisation_target_temperature
245
+
246
+ async def set_temperature(self, temperature):
247
+ await self.vehicle.climatisation_target(temperature)
248
+
249
+ async def set_hvac_mode(self, hvac_mode):
250
+ if hvac_mode:
251
+ await self.vehicle.climatisation('electric')
252
+ else:
253
+ await self.vehicle.climatisation('off')
254
+
255
+
256
+ class CombustionClimatisationClimate(Climate):
257
+ def __init__(self):
258
+ super().__init__(attr="pheater_heating", name="Parking Heater Climatisation", icon="mdi:radiator")
259
+
260
+ def configurate(self, **config):
261
+ self.spin = config.get('spin', '')
262
+ self.duration = config.get('combustionengineheatingduration', 30)
263
+
264
+ @property
265
+ def hvac_mode(self):
266
+ return self.vehicle.pheater_heating
267
+
268
+ @property
269
+ def target_temperature(self):
270
+ return self.vehicle.climatisation_target_temperature
271
+
272
+ async def set_temperature(self, temperature):
273
+ await self.vehicle.setClimatisationTargetTemperature(temperature)
274
+
275
+ async def set_hvac_mode(self, hvac_mode):
276
+ if hvac_mode:
277
+ await self.vehicle.pheater_climatisation(spin=self.spin, duration=self.duration, mode='heating')
278
+ else:
279
+ await self.vehicle.pheater_climatisation(spin=self.spin, mode='off')
280
+
281
+
282
+ class Position(Instrument):
283
+ def __init__(self):
284
+ super().__init__(component="device_tracker", attr="position", name="Position")
285
+
286
+ @property
287
+ def is_mutable(self):
288
+ return False
289
+
290
+ @property
291
+ def state(self):
292
+ state = super().state #or {}
293
+ return (
294
+ state.get("lat", "?"),
295
+ state.get("lng", "?"),
296
+ state.get("address", "?"),
297
+ state.get("timestamp", None),
298
+ )
299
+
300
+ @property
301
+ def str_state(self):
302
+ state = super().state #or {}
303
+ ts = state.get("timestamp", None)
304
+ if isinstance(ts, str):
305
+ time = str(datetime.strptime(ts,'%Y-%m-%dT%H:%M:%SZ').astimezone(tz=None))
306
+ elif isinstance(ts, datetime):
307
+ time = str(ts.astimezone(tz=None))
308
+ else:
309
+ time = None
310
+ return (
311
+ state.get("lat", "?"),
312
+ state.get("lng", "?"),
313
+ state.get("address", "?"),
314
+ time,
315
+ )
316
+
317
+
318
+ class DoorLock(Instrument):
319
+ def __init__(self):
320
+ super().__init__(component="lock", attr="door_locked", name="Door locked")
321
+
322
+ def configurate(self, **config):
323
+ self.spin = config.get('spin', '')
324
+
325
+ @property
326
+ def is_mutable(self):
327
+ return True
328
+
329
+ @property
330
+ def str_state(self):
331
+ return "Locked" if self.state else "Unlocked"
332
+
333
+ @property
334
+ def state(self):
335
+ return self.vehicle.door_locked
336
+
337
+ @property
338
+ def is_locked(self):
339
+ return self.state
340
+
341
+ async def lock(self):
342
+ try:
343
+ response = await self.vehicle.set_lock('lock', self.spin)
344
+ await self.vehicle.update()
345
+ if self.callback is not None:
346
+ self.callback()
347
+ return response
348
+ except Exception as e:
349
+ _LOGGER.error(f"Lock failed: {e}")
350
+ return False
351
+
352
+ async def unlock(self):
353
+ try:
354
+ response = await self.vehicle.set_lock('unlock', self.spin)
355
+ await self.vehicle.update()
356
+ if self.callback is not None:
357
+ self.callback()
358
+ return response
359
+ except Exception as e:
360
+ _LOGGER.error(f"Unlock failed: {e}")
361
+ return False
362
+
363
+ @property
364
+ def attributes(self):
365
+ return dict(last_result = self.vehicle.lock_action_status)
366
+
367
+
368
+ class TrunkLock(Instrument):
369
+ def __init__(self):
370
+ super().__init__(component="lock", attr="trunk_locked", name="Trunk locked")
371
+
372
+ @property
373
+ def is_mutable(self):
374
+ return True
375
+
376
+ @property
377
+ def str_state(self):
378
+ return "Locked" if self.state else "Unlocked"
379
+
380
+ @property
381
+ def state(self):
382
+ return self.vehicle.trunk_locked
383
+
384
+ @property
385
+ def is_locked(self):
386
+ return self.state
387
+
388
+ async def lock(self):
389
+ return None
390
+
391
+ async def unlock(self):
392
+ return None
393
+
394
+ # Switches
395
+ class RequestHonkAndFlash(Switch):
396
+ def __init__(self):
397
+ super().__init__(attr="request_honkandflash", name="Start honking and flashing", icon="mdi:car-emergency")
398
+
399
+ @property
400
+ def state(self):
401
+ return self.vehicle.request_honkandflash
402
+
403
+ async def turn_on(self):
404
+ await self.vehicle.set_honkandflash('honkandflash')
405
+ await self.vehicle.update()
406
+ if self.callback is not None:
407
+ self.callback()
408
+
409
+ async def turn_off(self):
410
+ pass
411
+
412
+ @property
413
+ def assumed_state(self):
414
+ return False
415
+
416
+ @property
417
+ def attributes(self):
418
+ return dict(last_result = self.vehicle.honkandflash_action_status)
419
+
420
+
421
+ class RequestFlash(Switch):
422
+ def __init__(self):
423
+ super().__init__(attr="request_flash", name="Start flashing", icon="mdi:car-parking-lights")
424
+
425
+ @property
426
+ def state(self):
427
+ return self.vehicle.request_flash
428
+
429
+ async def turn_on(self):
430
+ await self.vehicle.set_honkandflash('flash')
431
+ await self.vehicle.update()
432
+ if self.callback is not None:
433
+ self.callback()
434
+
435
+ async def turn_off(self):
436
+ pass
437
+
438
+ @property
439
+ def assumed_state(self):
440
+ return False
441
+
442
+ @property
443
+ def attributes(self):
444
+ return dict(last_result = self.vehicle.honkandflash_action_status)
445
+
446
+
447
+ class RequestUpdate(Switch):
448
+ def __init__(self):
449
+ super().__init__(attr="refresh_data", name="Force data refresh", icon="mdi:car-connected")
450
+
451
+ @property
452
+ def state(self):
453
+ return self.vehicle.refresh_data
454
+
455
+ async def turn_on(self):
456
+ await self.vehicle.set_refresh()
457
+ await self.vehicle.update()
458
+ if self.callback is not None:
459
+ self.callback()
460
+
461
+ async def turn_off(self):
462
+ pass
463
+
464
+ @property
465
+ def assumed_state(self):
466
+ return False
467
+
468
+ @property
469
+ def attributes(self):
470
+ return dict(last_result = self.vehicle.refresh_action_status)
471
+
472
+
473
+ class ElectricClimatisation(Switch):
474
+ def __init__(self):
475
+ super().__init__(attr="electric_climatisation", name="Electric Climatisation", icon="mdi:radiator")
476
+
477
+ @property
478
+ def state(self):
479
+ return self.vehicle.electric_climatisation
480
+
481
+ async def turn_on(self):
482
+ await self.vehicle.set_climatisation(mode = 'electric')
483
+ await self.vehicle.update()
484
+
485
+ async def turn_off(self):
486
+ await self.vehicle.set_climatisation(mode = 'off')
487
+ await self.vehicle.update()
488
+
489
+ @property
490
+ def assumed_state(self):
491
+ return False
492
+
493
+ @property
494
+ def attributes(self):
495
+ attrs = {}
496
+ if self.vehicle.is_electric_climatisation_attributes_supported:
497
+ attrs = self.vehicle.electric_climatisation_attributes
498
+ attrs['last_result'] = self.vehicle.climater_action_status
499
+ else:
500
+ attrs['last_result'] = self.vehicle.climater_action_status
501
+ return attrs
502
+
503
+
504
+ class AuxiliaryClimatisation(Switch):
505
+ def __init__(self):
506
+ super().__init__(attr="auxiliary_climatisation", name="Auxiliary Climatisation", icon="mdi:radiator")
507
+
508
+ def configurate(self, **config):
509
+ self.spin = config.get('spin', '')
510
+
511
+ @property
512
+ def state(self):
513
+ return self.vehicle.auxiliary_climatisation
514
+
515
+ async def turn_on(self):
516
+ await self.vehicle.set_climatisation(mode = 'auxiliary', spin = self.spin)
517
+ await self.vehicle.update()
518
+
519
+ async def turn_off(self):
520
+ await self.vehicle.set_climatisation(mode = 'off')
521
+ await self.vehicle.update()
522
+
523
+ @property
524
+ def assumed_state(self):
525
+ return False
526
+
527
+ @property
528
+ def attributes(self):
529
+ return dict(last_result = self.vehicle.climater_action_status)
530
+
531
+
532
+ class Charging(Switch):
533
+ def __init__(self):
534
+ super().__init__(attr="charging", name="Charging", icon="mdi:battery")
535
+
536
+ @property
537
+ def state(self):
538
+ return self.vehicle.charging
539
+
540
+ async def turn_on(self):
541
+ await self.vehicle.set_charger('start')
542
+ await self.vehicle.update()
543
+
544
+ async def turn_off(self):
545
+ await self.vehicle.set_charger('stop')
546
+ await self.vehicle.update()
547
+
548
+ @property
549
+ def assumed_state(self):
550
+ return False
551
+
552
+ @property
553
+ def attributes(self):
554
+ return dict(last_result = self.vehicle.charger_action_status)
555
+
556
+
557
+ class WindowHeater(Switch):
558
+ def __init__(self):
559
+ super().__init__(attr="window_heater", name="Window Heater", icon="mdi:car-defrost-rear")
560
+
561
+ @property
562
+ def state(self):
563
+ return self.vehicle.window_heater
564
+
565
+ async def turn_on(self):
566
+ await self.vehicle.set_window_heating('start')
567
+ await self.vehicle.update()
568
+
569
+ async def turn_off(self):
570
+ await self.vehicle.set_window_heating('stop')
571
+ await self.vehicle.update()
572
+
573
+ @property
574
+ def assumed_state(self):
575
+ return False
576
+
577
+
578
+ @property
579
+ def attributes(self):
580
+ return dict(last_result = self.vehicle.climater_action_status)
581
+
582
+
583
+ class SeatHeating(Switch):
584
+ def __init__(self):
585
+ super().__init__(attr="seat_heating", name="Seat Heating", icon="mdi:seat-recline-normal")
586
+
587
+ @property
588
+ def state(self):
589
+ return self.vehicle.seat_heating
590
+
591
+ async def turn_on(self):
592
+ #await self.vehicle.set_seat_heating('start')
593
+ #await self.vehicle.update()
594
+ pass
595
+
596
+ async def turn_off(self):
597
+ #await self.vehicle.set_seat_heating('stop')
598
+ #await self.vehicle.update()
599
+ pass
600
+
601
+ @property
602
+ def assumed_state(self):
603
+ return False
604
+
605
+ #@property
606
+ #def attributes(self):
607
+ # return dict(last_result = self.vehicle.climater_action_status)
608
+
609
+
610
+ class BatteryClimatisation(Switch):
611
+ def __init__(self):
612
+ super().__init__(attr="climatisation_without_external_power", name="Climatisation from battery", icon="mdi:power-plug")
613
+
614
+ @property
615
+ def state(self):
616
+ return self.vehicle.climatisation_without_external_power
617
+
618
+ async def turn_on(self):
619
+ await self.vehicle.set_battery_climatisation(True)
620
+ await self.vehicle.update()
621
+
622
+ async def turn_off(self):
623
+ await self.vehicle.set_battery_climatisation(False)
624
+ await self.vehicle.update()
625
+
626
+ @property
627
+ def assumed_state(self):
628
+ return False
629
+
630
+ @property
631
+ def attributes(self):
632
+ return dict(last_result = self.vehicle.climater_action_status)
633
+
634
+
635
+ class PHeaterHeating(Switch):
636
+ def __init__(self):
637
+ super().__init__(attr="pheater_heating", name="Parking Heater Heating", icon="mdi:radiator")
638
+
639
+ def configurate(self, **config):
640
+ self.spin = config.get('spin', '')
641
+ self.duration = config.get('combustionengineheatingduration', 30)
642
+
643
+ @property
644
+ def state(self):
645
+ return self.vehicle.pheater_heating
646
+
647
+ async def turn_on(self):
648
+ await self.vehicle.set_pheater(mode='heating', spin=self.spin)
649
+ await self.vehicle.update()
650
+
651
+ async def turn_off(self):
652
+ await self.vehicle.set_pheater(mode='off', spin=self.spin)
653
+ await self.vehicle.update()
654
+
655
+ @property
656
+ def assumed_state(self):
657
+ return False
658
+
659
+ @property
660
+ def attributes(self):
661
+ return dict(last_result = self.vehicle.pheater_action_status)
662
+
663
+
664
+ class PHeaterVentilation(Switch):
665
+ def __init__(self):
666
+ super().__init__(attr="pheater_ventilation", name="Parking Heater Ventilation", icon="mdi:radiator")
667
+
668
+ def configurate(self, **config):
669
+ self.spin = config.get('spin', '')
670
+ self.duration = config.get('combustionengineclimatisationduration', 30)
671
+
672
+ @property
673
+ def state(self):
674
+ return self.vehicle.pheater_ventilation
675
+
676
+ async def turn_on(self):
677
+ await self.vehicle.set_pheater(mode='ventilation', spin=self.spin)
678
+ await self.vehicle.update()
679
+
680
+ async def turn_off(self):
681
+ await self.vehicle.set_pheater(mode='off', spin=self.spin)
682
+ await self.vehicle.update()
683
+
684
+ @property
685
+ def assumed_state(self):
686
+ return False
687
+
688
+ @property
689
+ def attributes(self):
690
+ return dict(last_result = self.vehicle.pheater_action_status)
691
+
692
+
693
+ class Warnings(Sensor):
694
+ def __init__(self):
695
+ super().__init__(attr="warnings", name="Warnings", icon="mdi:alarm-light")
696
+
697
+ @property
698
+ def state(self):
699
+ return self.vehicle.warnings
700
+
701
+ @property
702
+ def assumed_state(self):
703
+ return False
704
+
705
+ class DepartureTimer1(Switch):
706
+ def __init__(self):
707
+ super().__init__(attr="departure1", name="Departure timer 1", icon="mdi:radiator")
708
+
709
+ def configurate(self, **config):
710
+ self.spin = config.get('spin', '')
711
+
712
+ @property
713
+ def state(self):
714
+ status = self.vehicle.departure1.get("timerProgrammedStatus", "")
715
+ if status == "programmed":
716
+ return True
717
+ else:
718
+ return False
719
+
720
+ async def turn_on(self):
721
+ await self.vehicle.set_timer_active(id=1, action="on")
722
+ await self.vehicle.update()
723
+
724
+ async def turn_off(self):
725
+ await self.vehicle.set_timer_active(id=1, action="off")
726
+ await self.vehicle.update()
727
+
728
+ @property
729
+ def assumed_state(self):
730
+ return False
731
+
732
+ @property
733
+ def attributes(self):
734
+ return dict(self.vehicle.departure1)
735
+
736
+
737
+ class DepartureTimer2(Switch):
738
+ def __init__(self):
739
+ super().__init__(attr="departure2", name="Departure timer 2", icon="mdi:radiator")
740
+
741
+ def configurate(self, **config):
742
+ self.spin = config.get('spin', '')
743
+
744
+ @property
745
+ def state(self):
746
+ status = self.vehicle.departure2.get("timerProgrammedStatus", "")
747
+ if status == "programmed":
748
+ return True
749
+ else:
750
+ return False
751
+
752
+ async def turn_on(self):
753
+ await self.vehicle.set_timer_active(id=2, action="on")
754
+ await self.vehicle.update()
755
+
756
+ async def turn_off(self):
757
+ await self.vehicle.set_timer_active(id=2, action="off")
758
+ await self.vehicle.update()
759
+
760
+ @property
761
+ def assumed_state(self):
762
+ return False
763
+
764
+ @property
765
+ def attributes(self):
766
+ return dict(self.vehicle.departure2)
767
+
768
+ class DepartureTimer3(Switch):
769
+ def __init__(self):
770
+ super().__init__(attr="departure3", name="Departure timer 3", icon="mdi:radiator")
771
+
772
+ def configurate(self, **config):
773
+ self.spin = config.get('spin', '')
774
+
775
+ @property
776
+ def state(self):
777
+ status = self.vehicle.departure3.get("timerProgrammedStatus", "")
778
+ if status == "programmed":
779
+ return True
780
+ else:
781
+ return False
782
+
783
+ async def turn_on(self):
784
+ await self.vehicle.set_timer_active(id=3, action="on")
785
+ await self.vehicle.update()
786
+
787
+ async def turn_off(self):
788
+ await self.vehicle.set_timer_active(id=3, action="off")
789
+ await self.vehicle.update()
790
+
791
+ @property
792
+ def assumed_state(self):
793
+ return False
794
+
795
+ @property
796
+ def attributes(self):
797
+ return dict(self.vehicle.departure3)
798
+
799
+
800
+ class RequestResults(Sensor):
801
+ def __init__(self):
802
+ super().__init__(attr="request_results", name="Request results", icon="mdi:chat-alert", unit=None)
803
+
804
+ @property
805
+ def state(self):
806
+ if self.vehicle.request_results.get('state', False):
807
+ return self.vehicle.request_results.get('state')
808
+ return 'N/A'
809
+
810
+ @property
811
+ def assumed_state(self):
812
+ return False
813
+
814
+ @property
815
+ def attributes(self):
816
+ return dict(self.vehicle.request_results)
817
+
818
+
819
+ def create_instruments():
820
+ return [
821
+ Position(),
822
+ DoorLock(),
823
+ TrunkLock(),
824
+ RequestFlash(),
825
+ RequestHonkAndFlash(),
826
+ RequestUpdate(),
827
+ WindowHeater(),
828
+ BatteryClimatisation(),
829
+ ElectricClimatisation(),
830
+ AuxiliaryClimatisation(),
831
+ PHeaterVentilation(),
832
+ PHeaterHeating(),
833
+ #ElectricClimatisationClimate(),
834
+ #CombustionClimatisationClimate(),
835
+ Charging(),
836
+ Warnings(),
837
+ RequestResults(),
838
+ DepartureTimer1(),
839
+ DepartureTimer2(),
840
+ DepartureTimer3(),
841
+ Sensor(
842
+ attr="distance",
843
+ name="Odometer",
844
+ icon="mdi:speedometer",
845
+ unit="km",
846
+ device_class="distance"
847
+ ),
848
+ Sensor(
849
+ attr="battery_level",
850
+ name="Battery level",
851
+ icon="mdi:battery",
852
+ unit="%",
853
+ device_class="battery"
854
+ ),
855
+ Sensor(
856
+ attr="min_charge_level",
857
+ name="Minimum charge level",
858
+ icon="mdi:battery-positive",
859
+ unit="%",
860
+ device_class="battery"
861
+ ),
862
+ Sensor(
863
+ attr="adblue_level",
864
+ name="Adblue level",
865
+ icon="mdi:fuel",
866
+ unit="km",
867
+ device_class="distance"
868
+ ),
869
+ Sensor(
870
+ attr="fuel_level",
871
+ name="Fuel level",
872
+ icon="mdi:fuel",
873
+ unit="%",
874
+ ),
875
+ Sensor(
876
+ attr="service_inspection",
877
+ name="Service inspection days",
878
+ icon="mdi:garage",
879
+ unit="days",
880
+ ),
881
+ Sensor(
882
+ attr="service_inspection_distance",
883
+ name="Service inspection distance",
884
+ icon="mdi:garage",
885
+ unit="km",
886
+ device_class="distance"
887
+ ),
888
+ Sensor(
889
+ attr="oil_inspection",
890
+ name="Oil inspection days",
891
+ icon="mdi:oil",
892
+ unit="days",
893
+ ),
894
+ Sensor(
895
+ attr="oil_inspection_distance",
896
+ name="Oil inspection distance",
897
+ icon="mdi:oil",
898
+ unit="km",
899
+ device_class="distance"
900
+ ),
901
+ Sensor(
902
+ attr="last_connected",
903
+ name="Last connected",
904
+ icon="mdi:clock",
905
+ device_class="timestamp"
906
+ ),
907
+ Sensor(
908
+ attr="parking_time",
909
+ name="Parking time",
910
+ icon="mdi:clock",
911
+ device_class="timestamp"
912
+ ),
913
+ Sensor(
914
+ attr="charging_time_left",
915
+ name="Charging time left",
916
+ icon="mdi:battery-charging-100",
917
+ unit="min",
918
+ device_class="duration"
919
+ ),
920
+ Sensor(
921
+ attr="charging_power",
922
+ name="Charging power",
923
+ icon="mdi:flash",
924
+ unit="W",
925
+ device_class="power"
926
+ ),
927
+ Sensor(
928
+ attr="charge_rate",
929
+ name="Charging rate",
930
+ icon="mdi:battery-heart",
931
+ unit="km/h",
932
+ device_class="speed"
933
+ ),
934
+ Sensor(
935
+ attr="electric_range",
936
+ name="Electric range",
937
+ icon="mdi:car-electric",
938
+ unit="km",
939
+ device_class="distance"
940
+ ),
941
+ Sensor(
942
+ attr="combustion_range",
943
+ name="Combustion range",
944
+ icon="mdi:car",
945
+ unit="km",
946
+ device_class="distance"
947
+ ),
948
+ Sensor(
949
+ attr="combined_range",
950
+ name="Combined range",
951
+ icon="mdi:car",
952
+ unit="km",
953
+ device_class="distance"
954
+ ),
955
+ Sensor(
956
+ attr="charge_max_ampere",
957
+ name="Charger max ampere",
958
+ icon="mdi:flash",
959
+ unit="A",
960
+ device_class="current"
961
+ ),
962
+ Sensor(
963
+ attr="climatisation_target_temperature",
964
+ name="Climatisation target temperature",
965
+ icon="mdi:thermometer",
966
+ unit="°C",
967
+ device_class="temperature"
968
+ ),
969
+ Sensor(
970
+ attr="climatisation_time_left",
971
+ name="Climatisation time left",
972
+ icon="mdi:clock",
973
+ unit="h",
974
+ device_class="duration"
975
+ ),
976
+ Sensor(
977
+ attr="trip_last_average_speed",
978
+ name="Last trip average speed",
979
+ icon="mdi:speedometer",
980
+ unit="km/h",
981
+ device_class="speed"
982
+ ),
983
+ Sensor(
984
+ attr="trip_last_average_electric_consumption",
985
+ name="Last trip average electric consumption",
986
+ icon="mdi:car-battery",
987
+ unit="kWh/100km",
988
+ device_class="energy_distance"
989
+ ),
990
+ Sensor(
991
+ attr="trip_last_average_fuel_consumption",
992
+ name="Last trip average fuel consumption",
993
+ icon="mdi:fuel",
994
+ unit="l/100km",
995
+ ),
996
+ Sensor(
997
+ attr="trip_last_duration",
998
+ name="Last trip duration",
999
+ icon="mdi:clock",
1000
+ unit="min",
1001
+ device_class="duration"
1002
+ ),
1003
+ Sensor(
1004
+ attr="trip_last_length",
1005
+ name="Last trip length",
1006
+ icon="mdi:map-marker-distance",
1007
+ unit="km",
1008
+ device_class="distance"
1009
+ ),
1010
+ Sensor(
1011
+ attr="trip_last_recuperation",
1012
+ name="Last trip recuperation",
1013
+ icon="mdi:battery-plus",
1014
+ unit="kWh/100km",
1015
+ device_class="energy_distance"
1016
+ ),
1017
+ Sensor(
1018
+ attr="trip_last_average_recuperation",
1019
+ name="Last trip average recuperation",
1020
+ icon="mdi:battery-plus",
1021
+ unit="kWh/100km",
1022
+ device_class="energy_distance"
1023
+ ),
1024
+ Sensor(
1025
+ attr="trip_last_average_auxillary_consumption",
1026
+ name="Last trip average auxillary consumption",
1027
+ icon="mdi:flash",
1028
+ unit="kWh/100km",
1029
+ device_class="energy_distance"
1030
+ ),
1031
+ Sensor(
1032
+ attr="trip_last_average_aux_consumer_consumption",
1033
+ name="Last trip average auxillary consumer consumption",
1034
+ icon="mdi:flash",
1035
+ unit="kWh/100km",
1036
+ device_class="energy_distance"
1037
+ ),
1038
+ Sensor(
1039
+ attr="trip_last_total_electric_consumption",
1040
+ name="Last trip total electric consumption",
1041
+ icon="mdi:car-battery",
1042
+ unit="kWh/100km",
1043
+ device_class="energy_distance"
1044
+ ),
1045
+ Sensor(
1046
+ attr="trip_last_cycle_average_speed",
1047
+ name="Last cycle average speed",
1048
+ icon="mdi:speedometer",
1049
+ unit="km/h",
1050
+ device_class="speed"
1051
+ ),
1052
+ Sensor(
1053
+ attr="trip_last_cycle_average_electric_consumption",
1054
+ name="Last cycle_average electric consumption",
1055
+ icon="mdi:car-battery",
1056
+ unit="kWh/100km",
1057
+ device_class="energy_distance"
1058
+ ),
1059
+ Sensor(
1060
+ attr="trip_last_cycle_average_fuel_consumption",
1061
+ name="Last cycle average fuel consumption",
1062
+ icon="mdi:fuel",
1063
+ unit="l/100km",
1064
+ ),
1065
+ Sensor(
1066
+ attr="trip_last_cycle_average_auxillary_consumption",
1067
+ name="Last cycle average auxillary consumption",
1068
+ icon="mdi:flash",
1069
+ unit="kWh/100km",
1070
+ device_class="energy_distance"
1071
+ ),
1072
+ Sensor(
1073
+ attr="trip_last_cycle_duration",
1074
+ name="Last cycle duration",
1075
+ icon="mdi:clock",
1076
+ unit="min",
1077
+ device_class="duration"
1078
+ ),
1079
+ Sensor(
1080
+ attr="trip_last_cycle_length",
1081
+ name="Last cycle length",
1082
+ icon="mdi:map-marker-distance",
1083
+ unit="km",
1084
+ device_class="distance"
1085
+ ),
1086
+ Sensor(
1087
+ attr="trip_last_cycle_recuperation",
1088
+ name="Last cycle recuperation",
1089
+ icon="mdi:battery-plus",
1090
+ unit="kWh/100km",
1091
+ device_class="energy_distance"
1092
+ ),
1093
+ Sensor(
1094
+ attr="trip_last_cycle_average_recuperation",
1095
+ name="Last cycle average recuperation",
1096
+ icon="mdi:battery-plus",
1097
+ unit="kWh/100km",
1098
+ device_class="energy_distance"
1099
+ ),
1100
+ Sensor(
1101
+ attr="trip_last_cycle_average_aux_consumer_consumption",
1102
+ name="Last cycle average auxillary consumer consumption",
1103
+ icon="mdi:flash",
1104
+ unit="kWh/100km",
1105
+ device_class="energy_distance"
1106
+ ),
1107
+ Sensor(
1108
+ attr="trip_last_cycle_total_electric_consumption",
1109
+ name="Last cycle total electric consumption",
1110
+ icon="mdi:car-battery",
1111
+ unit="kWh/100km",
1112
+ device_class="energy_distance"
1113
+ ),
1114
+ Sensor(
1115
+ attr="model_image_large",
1116
+ name="Model image URL (Large)",
1117
+ icon="mdi:file-image",
1118
+ ),
1119
+ Sensor(
1120
+ attr="model_image_small",
1121
+ name="Model image URL (Small)",
1122
+ icon="mdi:file-image",
1123
+ ),
1124
+ Sensor(
1125
+ attr="pheater_status",
1126
+ name="Parking Heater heating/ventilation status",
1127
+ icon="mdi:radiator",
1128
+ ),
1129
+ Sensor(
1130
+ attr="pheater_duration",
1131
+ name="Parking Heater heating/ventilation duration",
1132
+ icon="mdi:timer",
1133
+ unit="minutes",
1134
+ device_class="duration"
1135
+ ),
1136
+ Sensor(
1137
+ attr="outside_temperature",
1138
+ name="Outside temperature",
1139
+ icon="mdi:thermometer",
1140
+ unit="°C",
1141
+ device_class="temperature"
1142
+ ),
1143
+ Sensor(
1144
+ attr="requests_remaining",
1145
+ name="Requests remaining",
1146
+ icon="mdi:chat-alert",
1147
+ unit="",
1148
+ ),
1149
+ BinarySensor(
1150
+ attr="external_power",
1151
+ name="External power",
1152
+ device_class="power"
1153
+ ),
1154
+ BinarySensor(
1155
+ attr="energy_flow",
1156
+ name="Energy flow",
1157
+ device_class="power"
1158
+ ),
1159
+ BinarySensor(
1160
+ attr="parking_light",
1161
+ name="Parking light",
1162
+ device_class="light",
1163
+ icon="mdi:car-parking-lights"
1164
+ ),
1165
+ BinarySensor(
1166
+ attr="door_locked",
1167
+ name="Doors locked",
1168
+ device_class="lock",
1169
+ reverse_state=False
1170
+ ),
1171
+ BinarySensor(
1172
+ attr="door_closed_left_front",
1173
+ name="Door closed left front",
1174
+ device_class="door",
1175
+ reverse_state=False,
1176
+ icon="mdi:car-door"
1177
+ ),
1178
+ BinarySensor(
1179
+ attr="door_closed_right_front",
1180
+ name="Door closed right front",
1181
+ device_class="door",
1182
+ reverse_state=False,
1183
+ icon="mdi:car-door"
1184
+ ),
1185
+ BinarySensor(
1186
+ attr="door_closed_left_back",
1187
+ name="Door closed left back",
1188
+ device_class="door",
1189
+ reverse_state=False,
1190
+ icon="mdi:car-door"
1191
+ ),
1192
+ BinarySensor(
1193
+ attr="door_closed_right_back",
1194
+ name="Door closed right back",
1195
+ device_class="door",
1196
+ reverse_state=False,
1197
+ icon="mdi:car-door"
1198
+ ),
1199
+ BinarySensor(
1200
+ attr="trunk_locked",
1201
+ name="Trunk locked",
1202
+ device_class="lock",
1203
+ reverse_state=False
1204
+ ),
1205
+ BinarySensor(
1206
+ attr="trunk_closed",
1207
+ name="Trunk closed",
1208
+ device_class="door",
1209
+ reverse_state=False
1210
+ ),
1211
+ BinarySensor(
1212
+ attr="hood_closed",
1213
+ name="Hood closed",
1214
+ device_class="door",
1215
+ reverse_state=False
1216
+ ),
1217
+ BinarySensor(
1218
+ attr="charging_cable_connected",
1219
+ name="Charging cable connected",
1220
+ device_class="plug",
1221
+ reverse_state=False
1222
+ ),
1223
+ BinarySensor(
1224
+ attr="charging_cable_locked",
1225
+ name="Charging cable locked",
1226
+ device_class="lock",
1227
+ reverse_state=False
1228
+ ),
1229
+ BinarySensor(
1230
+ attr="sunroof_closed",
1231
+ name="Sunroof closed",
1232
+ device_class="window",
1233
+ reverse_state=False
1234
+ ),
1235
+ BinarySensor(
1236
+ attr="windows_closed",
1237
+ name="Windows closed",
1238
+ device_class="window",
1239
+ reverse_state=False
1240
+ ),
1241
+ BinarySensor(
1242
+ attr="window_closed_left_front",
1243
+ name="Window closed left front",
1244
+ device_class="window",
1245
+ reverse_state=False
1246
+ ),
1247
+ BinarySensor(
1248
+ attr="window_closed_left_back",
1249
+ name="Window closed left back",
1250
+ device_class="window",
1251
+ reverse_state=False
1252
+ ),
1253
+ BinarySensor(
1254
+ attr="window_closed_right_front",
1255
+ name="Window closed right front",
1256
+ device_class="window",
1257
+ reverse_state=False
1258
+ ),
1259
+ BinarySensor(
1260
+ attr="window_closed_right_back",
1261
+ name="Window closed right back",
1262
+ device_class="window",
1263
+ reverse_state=False
1264
+ ),
1265
+ BinarySensor(
1266
+ attr="vehicle_moving",
1267
+ name="Vehicle Moving",
1268
+ device_class="moving"
1269
+ ),
1270
+ BinarySensor(
1271
+ attr="request_in_progress",
1272
+ name="Request in progress",
1273
+ device_class="connectivity"
1274
+ ),
1275
+ ]
1276
+
1277
+
1278
+ class Dashboard:
1279
+ def __init__(self, vehicle, **config):
1280
+ self._config = config
1281
+ self.instruments = [
1282
+ instrument
1283
+ for instrument in create_instruments()
1284
+ if instrument.setup(vehicle, **config)
1285
+ ]
1286
+ _LOGGER.debug("Supported instruments: " + ", ".join(str(inst.attr) for inst in self.instruments))
1287
+