pycupra 0.1.11__py3-2ndver-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 ADDED
@@ -0,0 +1,1527 @@
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 RequestRefresh(Switch):
448
+ def __init__(self):
449
+ super().__init__(attr="refresh_data", name="Request wakeup vehicle", 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
+ _LOGGER.debug('User has called RequestRefresh().')
457
+ await self.vehicle.set_refresh()
458
+ #await self.vehicle.update(updateType=1) #full update after set_refresh
459
+ if self.callback is not None:
460
+ self.callback()
461
+
462
+ async def turn_off(self):
463
+ pass
464
+
465
+ @property
466
+ def assumed_state(self):
467
+ return False
468
+
469
+ @property
470
+ def attributes(self):
471
+ return dict(last_result = self.vehicle.refresh_action_status)
472
+
473
+
474
+ class RequestUpdate(Switch):
475
+ def __init__(self):
476
+ super().__init__(attr="update_data", name="Request full update", icon="mdi:timer-refresh")
477
+
478
+ @property
479
+ def state(self):
480
+ return False #self.vehicle.update
481
+
482
+ async def turn_on(self):
483
+ _LOGGER.debug('User has called RequestUpdate().')
484
+ await self.vehicle.update(updateType=1) #full update after set_refresh
485
+ if self.callback is not None:
486
+ self.callback()
487
+
488
+ async def turn_off(self):
489
+ pass
490
+
491
+ @property
492
+ def assumed_state(self):
493
+ return False
494
+
495
+ #@property
496
+ #def attributes(self):
497
+ # return dict()
498
+
499
+
500
+ class ElectricClimatisation(Switch):
501
+ def __init__(self):
502
+ super().__init__(attr="electric_climatisation", name="Electric Climatisation", icon="mdi:radiator")
503
+
504
+ @property
505
+ def state(self):
506
+ return self.vehicle.electric_climatisation
507
+
508
+ async def turn_on(self):
509
+ await self.vehicle.set_climatisation(mode = 'electric')
510
+ #await self.vehicle.update()
511
+
512
+ async def turn_off(self):
513
+ await self.vehicle.set_climatisation(mode = 'off')
514
+ #await self.vehicle.update()
515
+
516
+ @property
517
+ def assumed_state(self):
518
+ return False
519
+
520
+ @property
521
+ def attributes(self):
522
+ attrs = {}
523
+ if self.vehicle.is_electric_climatisation_attributes_supported:
524
+ attrs = self.vehicle.electric_climatisation_attributes
525
+ attrs['last_result'] = self.vehicle.climater_action_status
526
+ else:
527
+ attrs['last_result'] = self.vehicle.climater_action_status
528
+ return attrs
529
+
530
+
531
+ class AuxiliaryClimatisation(Switch):
532
+ def __init__(self):
533
+ super().__init__(attr="auxiliary_climatisation", name="Auxiliary Climatisation", icon="mdi:radiator")
534
+
535
+ def configurate(self, **config):
536
+ self.spin = config.get('spin', '')
537
+
538
+ @property
539
+ def state(self):
540
+ return self.vehicle.auxiliary_climatisation
541
+
542
+ async def turn_on(self):
543
+ await self.vehicle.set_climatisation(mode = 'auxiliary', spin = self.spin)
544
+ #await self.vehicle.update()
545
+
546
+ async def turn_off(self):
547
+ await self.vehicle.set_climatisation(mode = 'off')
548
+ #await self.vehicle.update()
549
+
550
+ @property
551
+ def assumed_state(self):
552
+ return False
553
+
554
+ @property
555
+ def attributes(self):
556
+ return dict(last_result = self.vehicle.climater_action_status)
557
+
558
+
559
+ class Charging(Switch):
560
+ def __init__(self):
561
+ super().__init__(attr="charging", name="Charging", icon="mdi:battery")
562
+
563
+ @property
564
+ def state(self):
565
+ return self.vehicle.charging
566
+
567
+ async def turn_on(self):
568
+ await self.vehicle.set_charger('start')
569
+ #await self.vehicle.update()
570
+
571
+ async def turn_off(self):
572
+ await self.vehicle.set_charger('stop')
573
+ #await self.vehicle.update()
574
+
575
+ @property
576
+ def assumed_state(self):
577
+ return False
578
+
579
+ @property
580
+ def attributes(self):
581
+ return dict(last_result = self.vehicle.charger_action_status)
582
+
583
+
584
+ class WindowHeater(Switch):
585
+ def __init__(self):
586
+ super().__init__(attr="window_heater", name="Window Heater", icon="mdi:car-defrost-rear")
587
+
588
+ @property
589
+ def state(self):
590
+ return self.vehicle.window_heater
591
+
592
+ async def turn_on(self):
593
+ await self.vehicle.set_window_heating('start')
594
+ #await self.vehicle.update()
595
+
596
+ async def turn_off(self):
597
+ await self.vehicle.set_window_heating('stop')
598
+ #await self.vehicle.update()
599
+
600
+ @property
601
+ def assumed_state(self):
602
+ return False
603
+
604
+
605
+ @property
606
+ def attributes(self):
607
+ return dict(last_result = self.vehicle.climater_action_status)
608
+
609
+
610
+ class SeatHeating(Switch):
611
+ def __init__(self):
612
+ super().__init__(attr="seat_heating", name="Seat Heating", icon="mdi:seat-recline-normal")
613
+
614
+ @property
615
+ def state(self):
616
+ return self.vehicle.seat_heating
617
+
618
+ async def turn_on(self):
619
+ #await self.vehicle.set_seat_heating('start')
620
+ #await self.vehicle.update()
621
+ pass
622
+
623
+ async def turn_off(self):
624
+ #await self.vehicle.set_seat_heating('stop')
625
+ #await self.vehicle.update()
626
+ pass
627
+
628
+ @property
629
+ def assumed_state(self):
630
+ return False
631
+
632
+ #@property
633
+ #def attributes(self):
634
+ # return dict(last_result = self.vehicle.climater_action_status)
635
+
636
+
637
+ class BatteryClimatisation(Switch):
638
+ def __init__(self):
639
+ super().__init__(attr="climatisation_without_external_power", name="Climatisation from battery", icon="mdi:power-plug")
640
+
641
+ @property
642
+ def state(self):
643
+ return self.vehicle.climatisation_without_external_power
644
+
645
+ async def turn_on(self):
646
+ await self.vehicle.set_battery_climatisation(True)
647
+ #await self.vehicle.update()
648
+
649
+ async def turn_off(self):
650
+ await self.vehicle.set_battery_climatisation(False)
651
+ #await self.vehicle.update()
652
+
653
+ @property
654
+ def assumed_state(self):
655
+ return False
656
+
657
+ @property
658
+ def attributes(self):
659
+ return dict(last_result = self.vehicle.climater_action_status)
660
+
661
+
662
+ class PHeaterHeating(Switch):
663
+ def __init__(self):
664
+ super().__init__(attr="pheater_heating", name="Parking Heater Heating", icon="mdi:radiator")
665
+
666
+ def configurate(self, **config):
667
+ self.spin = config.get('spin', '')
668
+ self.duration = config.get('combustionengineheatingduration', 30)
669
+
670
+ @property
671
+ def state(self):
672
+ return self.vehicle.pheater_heating
673
+
674
+ async def turn_on(self):
675
+ await self.vehicle.set_pheater(mode='heating', spin=self.spin)
676
+ #await self.vehicle.update()
677
+
678
+ async def turn_off(self):
679
+ await self.vehicle.set_pheater(mode='off', spin=self.spin)
680
+ #await self.vehicle.update()
681
+
682
+ @property
683
+ def assumed_state(self):
684
+ return False
685
+
686
+ @property
687
+ def attributes(self):
688
+ return dict(last_result = self.vehicle.pheater_action_status)
689
+
690
+
691
+ class PHeaterVentilation(Switch):
692
+ def __init__(self):
693
+ super().__init__(attr="pheater_ventilation", name="Parking Heater Ventilation", icon="mdi:radiator")
694
+
695
+ def configurate(self, **config):
696
+ self.spin = config.get('spin', '')
697
+ self.duration = config.get('combustionengineclimatisationduration', 30)
698
+
699
+ @property
700
+ def state(self):
701
+ return self.vehicle.pheater_ventilation
702
+
703
+ async def turn_on(self):
704
+ await self.vehicle.set_pheater(mode='ventilation', spin=self.spin)
705
+ #await self.vehicle.update()
706
+
707
+ async def turn_off(self):
708
+ await self.vehicle.set_pheater(mode='off', spin=self.spin)
709
+ #await self.vehicle.update()
710
+
711
+ @property
712
+ def assumed_state(self):
713
+ return False
714
+
715
+ @property
716
+ def attributes(self):
717
+ return dict(last_result = self.vehicle.pheater_action_status)
718
+
719
+
720
+ class SlowCharge(Switch):
721
+ def __init__(self):
722
+ super().__init__(attr="slow_charge", name="Slow charge", icon="mdi:battery")
723
+
724
+ @property
725
+ def state(self):
726
+ return self.vehicle.slow_charge
727
+
728
+ async def turn_on(self):
729
+ await self.vehicle.set_charger_current('reduced')
730
+ #await self.vehicle.update()
731
+
732
+ async def turn_off(self):
733
+ await self.vehicle.set_charger_current('maximum')
734
+ #await self.vehicle.update()
735
+
736
+ @property
737
+ def assumed_state(self):
738
+ return False
739
+
740
+
741
+ @property
742
+ def attributes(self):
743
+ return dict(last_result = self.vehicle.charger_action_status)
744
+
745
+
746
+ class Warnings(Sensor):
747
+ def __init__(self):
748
+ super().__init__(attr="warnings", name="Warnings", icon="mdi:alarm-light")
749
+
750
+ @property
751
+ def state(self):
752
+ return self.vehicle.warnings
753
+
754
+ @property
755
+ def assumed_state(self):
756
+ return False
757
+
758
+ @property
759
+ def attributes(self):
760
+ attrs = {'warnings': 'No warnings'}
761
+ if self.vehicle.attrs.get('warninglights', {}).get('statuses',[]):
762
+ warningTextList = []
763
+ for elem in self.vehicle.attrs['warninglights']['statuses']:
764
+ if isinstance(elem, dict):
765
+ if elem.get('text',''):
766
+ warningTextList.append(elem.get('text',''))
767
+ attrs['warnings'] = warningTextList
768
+ return attrs
769
+
770
+ class DepartureTimer1(Switch):
771
+ def __init__(self):
772
+ super().__init__(attr="departure1", name="Departure timer 1", icon="mdi:radiator")
773
+
774
+ def configurate(self, **config):
775
+ self.spin = config.get('spin', '')
776
+
777
+ @property
778
+ def state(self):
779
+ if self.vehicle.departure1 != None:
780
+ status = self.vehicle.departure1.get("enabled", "")
781
+ if status:
782
+ return True
783
+ #else:
784
+ return False
785
+
786
+ async def turn_on(self):
787
+ await self.vehicle.set_timer_active(id=1, action="on")
788
+ #await self.vehicle.update()
789
+
790
+ async def turn_off(self):
791
+ await self.vehicle.set_timer_active(id=1, action="off")
792
+ #await self.vehicle.update()
793
+
794
+ @property
795
+ def assumed_state(self):
796
+ return False
797
+
798
+ @property
799
+ def attributes(self):
800
+ return dict(self.vehicle.departure1)
801
+
802
+
803
+ class DepartureTimer2(Switch):
804
+ def __init__(self):
805
+ super().__init__(attr="departure2", name="Departure timer 2", icon="mdi:radiator")
806
+
807
+ def configurate(self, **config):
808
+ self.spin = config.get('spin', '')
809
+
810
+ @property
811
+ def state(self):
812
+ if self.vehicle.departure2 != None:
813
+ status = self.vehicle.departure2.get("enabled", "")
814
+ if status:
815
+ return True
816
+ #else:
817
+ return False
818
+
819
+ async def turn_on(self):
820
+ await self.vehicle.set_timer_active(id=2, action="on")
821
+ #await self.vehicle.update()
822
+
823
+ async def turn_off(self):
824
+ await self.vehicle.set_timer_active(id=2, action="off")
825
+ #await self.vehicle.update()
826
+
827
+ @property
828
+ def assumed_state(self):
829
+ return False
830
+
831
+ @property
832
+ def attributes(self):
833
+ return dict(self.vehicle.departure2)
834
+
835
+ class DepartureTimer3(Switch):
836
+ def __init__(self):
837
+ super().__init__(attr="departure3", name="Departure timer 3", icon="mdi:radiator")
838
+
839
+ def configurate(self, **config):
840
+ self.spin = config.get('spin', '')
841
+
842
+ @property
843
+ def state(self):
844
+ if self.vehicle.departure3 != None:
845
+ status = self.vehicle.departure3.get("enabled", "")
846
+ if status:
847
+ return True
848
+ #else:
849
+ return False
850
+
851
+ async def turn_on(self):
852
+ await self.vehicle.set_timer_active(id=3, action="on")
853
+ #await self.vehicle.update()
854
+
855
+ async def turn_off(self):
856
+ await self.vehicle.set_timer_active(id=3, action="off")
857
+ #await self.vehicle.update()
858
+
859
+ @property
860
+ def assumed_state(self):
861
+ return False
862
+
863
+ @property
864
+ def attributes(self):
865
+ return dict(self.vehicle.departure3)
866
+
867
+ class DepartureProfile1(Switch):
868
+ def __init__(self):
869
+ super().__init__(attr="departure_profile1", name="Departure profile 1", icon="mdi:radiator")
870
+
871
+ def configurate(self, **config):
872
+ self.spin = config.get('spin', '')
873
+
874
+ @property
875
+ def state(self):
876
+ status = self.vehicle.departure_profile1.get("enabled", "")
877
+ if status:
878
+ return True
879
+ else:
880
+ return False
881
+
882
+ async def turn_on(self):
883
+ await self.vehicle.set_departure_profile_active(id=1, action="on")
884
+ #await self.vehicle.update()
885
+
886
+ async def turn_off(self):
887
+ await self.vehicle.set_departure_profile_active(id=1, action="off")
888
+ #await self.vehicle.update()
889
+
890
+ @property
891
+ def assumed_state(self):
892
+ return False
893
+
894
+ @property
895
+ def attributes(self):
896
+ return dict(self.vehicle.departure_profile1)
897
+
898
+ class DepartureProfile2(Switch):
899
+ def __init__(self):
900
+ super().__init__(attr="departure_profile2", name="Departure profile 2", icon="mdi:radiator")
901
+
902
+ def configurate(self, **config):
903
+ self.spin = config.get('spin', '')
904
+
905
+ @property
906
+ def state(self):
907
+ status = self.vehicle.departure_profile2.get("enabled", "")
908
+ if status:
909
+ return True
910
+ else:
911
+ return False
912
+
913
+ async def turn_on(self):
914
+ await self.vehicle.set_departure_profile_active(id=2, action="on")
915
+ #await self.vehicle.update()
916
+
917
+ async def turn_off(self):
918
+ await self.vehicle.set_departure_profile_active(id=2, action="off")
919
+ #await self.vehicle.update()
920
+
921
+ @property
922
+ def assumed_state(self):
923
+ return False
924
+
925
+ @property
926
+ def attributes(self):
927
+ return dict(self.vehicle.departure_profile2)
928
+
929
+ class DepartureProfile3(Switch):
930
+ def __init__(self):
931
+ super().__init__(attr="departure_profile3", name="Departure profile 3", icon="mdi:radiator")
932
+
933
+ def configurate(self, **config):
934
+ self.spin = config.get('spin', '')
935
+
936
+ @property
937
+ def state(self):
938
+ status = self.vehicle.departure_profile3.get("enabled", "")
939
+ if status:
940
+ return True
941
+ else:
942
+ return False
943
+
944
+ async def turn_on(self):
945
+ await self.vehicle.set_departure_profile_active(id=3, action="on")
946
+ #await self.vehicle.update()
947
+
948
+ async def turn_off(self):
949
+ await self.vehicle.set_departure_profile_active(id=3, action="off")
950
+ #await self.vehicle.update()
951
+
952
+ @property
953
+ def assumed_state(self):
954
+ return False
955
+
956
+ @property
957
+ def attributes(self):
958
+ return dict(self.vehicle.departure_profile3)
959
+
960
+
961
+ class RequestResults(Sensor):
962
+ def __init__(self):
963
+ super().__init__(attr="request_results", name="Request results", icon="mdi:chat-alert", unit=None)
964
+
965
+ @property
966
+ def state(self):
967
+ if self.vehicle.request_results.get('state', False):
968
+ return self.vehicle.request_results.get('state')
969
+ return 'N/A'
970
+
971
+ @property
972
+ def assumed_state(self):
973
+ return False
974
+
975
+ @property
976
+ def attributes(self):
977
+ return dict(self.vehicle.request_results)
978
+
979
+ class ChargingState(BinarySensor):
980
+ def __init__(self):
981
+ super().__init__(attr="charging_state", name="Charging state", icon="mdi:battery-charging", device_class='power')
982
+
983
+ @property
984
+ def state(self):
985
+ return self.vehicle.charging_state
986
+
987
+ @property
988
+ def assumed_state(self):
989
+ return False
990
+
991
+ @property
992
+ def attributes(self):
993
+ attr = {}
994
+ #state = self.vehicle.attrs.get('charging', {}).get('status', {}).get('state', '')
995
+ #type = self.vehicle.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('type', '')
996
+ #mode = self.vehicle.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('mode', '')
997
+ state = self.vehicle.attrs.get('mycar', {}).get('services', {}).get('charging', {}).get('status', '')
998
+ type = self.vehicle.attrs.get('charging', {}).get('status', {}).get('charging', {}).get('type', '')
999
+ mode = self.vehicle.attrs.get('mycar', {}).get('services', {}).get('charging', {}).get('chargeMode', '')
1000
+ if state in {'charging','Charging', 'conservation','Conservation'}:
1001
+ attr['state']=state.lower()
1002
+ if type != '':
1003
+ attr['type']=type
1004
+ if mode != '':
1005
+ attr['mode']=mode
1006
+ return attr
1007
+
1008
+ class AreaAlarm(BinarySensor):
1009
+ def __init__(self):
1010
+ super().__init__(attr="area_alarm", name="Area alarm", icon="mdi:alarm-light", device_class=None)
1011
+
1012
+ @property
1013
+ def state(self):
1014
+ return self.vehicle.area_alarm
1015
+
1016
+ @property
1017
+ def assumed_state(self):
1018
+ return False
1019
+
1020
+ @property
1021
+ def attributes(self):
1022
+ attr = {}
1023
+ type = self.vehicle.attrs.get('areaAlarm', {}).get('type', '')
1024
+ zones = self.vehicle.attrs.get('areaAlarm', {}).get('zones', [])
1025
+ timestamp = self.vehicle.attrs.get('areaAlarm', {}).get('timestamp', 0)
1026
+ if type != '':
1027
+ attr['type']=type
1028
+ if len(zones) > 0:
1029
+ attr['zone']=zones[0]
1030
+ if timestamp != 0:
1031
+ attr['timestamp']=timestamp
1032
+ return attr
1033
+
1034
+ def create_instruments():
1035
+ return [
1036
+ Position(),
1037
+ DoorLock(),
1038
+ #TrunkLock(),
1039
+ RequestFlash(),
1040
+ RequestHonkAndFlash(),
1041
+ RequestRefresh(),
1042
+ RequestUpdate(),
1043
+ WindowHeater(),
1044
+ BatteryClimatisation(),
1045
+ ElectricClimatisation(),
1046
+ AuxiliaryClimatisation(),
1047
+ PHeaterVentilation(),
1048
+ PHeaterHeating(),
1049
+ #ElectricClimatisationClimate(),
1050
+ #CombustionClimatisationClimate(),
1051
+ Charging(),
1052
+ Warnings(),
1053
+ SlowCharge(),
1054
+ RequestResults(),
1055
+ DepartureTimer1(),
1056
+ DepartureTimer2(),
1057
+ DepartureTimer3(),
1058
+ DepartureProfile1(),
1059
+ DepartureProfile2(),
1060
+ DepartureProfile3(),
1061
+ ChargingState(),
1062
+ AreaAlarm(),
1063
+ Sensor(
1064
+ attr="distance",
1065
+ name="Odometer",
1066
+ icon="mdi:speedometer",
1067
+ unit="km",
1068
+ device_class="distance"
1069
+ ),
1070
+ Sensor(
1071
+ attr="battery_level",
1072
+ name="Battery level",
1073
+ icon="mdi:battery",
1074
+ unit="%",
1075
+ device_class="battery"
1076
+ ),
1077
+ Sensor(
1078
+ attr="min_charge_level",
1079
+ name="Minimum charge level",
1080
+ icon="mdi:battery-positive",
1081
+ unit="%",
1082
+ device_class="battery"
1083
+ ),
1084
+ Sensor(
1085
+ attr="target_soc",
1086
+ name="Target state of charge",
1087
+ icon="mdi:battery-positive",
1088
+ unit="%",
1089
+ device_class="battery"
1090
+ ),
1091
+ Sensor(
1092
+ attr="adblue_level",
1093
+ name="Adblue level",
1094
+ icon="mdi:fuel",
1095
+ unit="km",
1096
+ device_class="distance"
1097
+ ),
1098
+ Sensor(
1099
+ attr="fuel_level",
1100
+ name="Fuel level",
1101
+ icon="mdi:fuel",
1102
+ unit="%",
1103
+ ),
1104
+ Sensor(
1105
+ attr="service_inspection",
1106
+ name="Service inspection days",
1107
+ icon="mdi:garage",
1108
+ unit="days",
1109
+ ),
1110
+ Sensor(
1111
+ attr="service_inspection_distance",
1112
+ name="Service inspection distance",
1113
+ icon="mdi:garage",
1114
+ unit="km",
1115
+ device_class="distance"
1116
+ ),
1117
+ Sensor(
1118
+ attr="oil_inspection",
1119
+ name="Oil inspection days",
1120
+ icon="mdi:oil",
1121
+ unit="days",
1122
+ ),
1123
+ Sensor(
1124
+ attr="oil_inspection_distance",
1125
+ name="Oil inspection distance",
1126
+ icon="mdi:oil",
1127
+ unit="km",
1128
+ device_class="distance"
1129
+ ),
1130
+ Sensor(
1131
+ attr="last_connected",
1132
+ name="Last connected",
1133
+ icon="mdi:clock",
1134
+ device_class="timestamp"
1135
+ ),
1136
+ Sensor(
1137
+ attr="last_full_update",
1138
+ name="Last full update",
1139
+ icon="mdi:clock",
1140
+ device_class="timestamp"
1141
+ ),
1142
+ Sensor(
1143
+ attr="parking_time",
1144
+ name="Parking time",
1145
+ icon="mdi:clock",
1146
+ device_class="timestamp"
1147
+ ),
1148
+ Sensor(
1149
+ attr="charging_time_left",
1150
+ name="Charging time left",
1151
+ icon="mdi:battery-charging-100",
1152
+ unit="min",
1153
+ device_class="duration"
1154
+ ),
1155
+ Sensor(
1156
+ attr="charging_power",
1157
+ name="Charging power",
1158
+ icon="mdi:flash",
1159
+ unit="W",
1160
+ device_class="power"
1161
+ ),
1162
+ Sensor(
1163
+ attr="charge_rate",
1164
+ name="Charging rate",
1165
+ icon="mdi:battery-heart",
1166
+ unit="km/h",
1167
+ device_class="speed"
1168
+ ),
1169
+ Sensor(
1170
+ attr="electric_range",
1171
+ name="Electric range",
1172
+ icon="mdi:car-electric",
1173
+ unit="km",
1174
+ device_class="distance"
1175
+ ),
1176
+ Sensor(
1177
+ attr="combustion_range",
1178
+ name="Combustion range",
1179
+ icon="mdi:car",
1180
+ unit="km",
1181
+ device_class="distance"
1182
+ ),
1183
+ Sensor(
1184
+ attr="combined_range",
1185
+ name="Combined range",
1186
+ icon="mdi:car",
1187
+ unit="km",
1188
+ device_class="distance"
1189
+ ),
1190
+ Sensor(
1191
+ attr="charge_max_ampere",
1192
+ name="Charger max ampere",
1193
+ icon="mdi:flash",
1194
+ #unit="A",
1195
+ #device_class="current"
1196
+ ),
1197
+ Sensor(
1198
+ attr="climatisation_target_temperature",
1199
+ name="Climatisation target temperature",
1200
+ icon="mdi:thermometer",
1201
+ unit="°C",
1202
+ device_class="temperature"
1203
+ ),
1204
+ Sensor(
1205
+ attr="climatisation_time_left",
1206
+ name="Climatisation time left",
1207
+ icon="mdi:clock",
1208
+ unit="h",
1209
+ device_class="duration"
1210
+ ),
1211
+ Sensor(
1212
+ attr="trip_last_average_speed",
1213
+ name="Last trip average speed",
1214
+ icon="mdi:speedometer",
1215
+ unit="km/h",
1216
+ device_class="speed"
1217
+ ),
1218
+ Sensor(
1219
+ attr="trip_last_average_electric_consumption",
1220
+ name="Last trip average electric consumption",
1221
+ icon="mdi:car-battery",
1222
+ unit="kWh/100km",
1223
+ device_class="energy_distance"
1224
+ ),
1225
+ Sensor(
1226
+ attr="trip_last_average_fuel_consumption",
1227
+ name="Last trip average fuel consumption",
1228
+ icon="mdi:fuel",
1229
+ unit="l/100km",
1230
+ ),
1231
+ Sensor(
1232
+ attr="trip_last_duration",
1233
+ name="Last trip duration",
1234
+ icon="mdi:clock",
1235
+ unit="min",
1236
+ device_class="duration"
1237
+ ),
1238
+ Sensor(
1239
+ attr="trip_last_length",
1240
+ name="Last trip length",
1241
+ icon="mdi:map-marker-distance",
1242
+ unit="km",
1243
+ device_class="distance"
1244
+ ),
1245
+ Sensor(
1246
+ attr="trip_last_recuperation",
1247
+ name="Last trip recuperation",
1248
+ icon="mdi:battery-plus",
1249
+ unit="kWh/100km",
1250
+ device_class="energy_distance"
1251
+ ),
1252
+ Sensor(
1253
+ attr="trip_last_average_recuperation",
1254
+ name="Last trip average recuperation",
1255
+ icon="mdi:battery-plus",
1256
+ unit="kWh/100km",
1257
+ device_class="energy_distance"
1258
+ ),
1259
+ Sensor(
1260
+ attr="trip_last_average_auxillary_consumption",
1261
+ name="Last trip average auxillary consumption",
1262
+ icon="mdi:flash",
1263
+ unit="kWh/100km",
1264
+ device_class="energy_distance"
1265
+ ),
1266
+ Sensor(
1267
+ attr="trip_last_average_aux_consumer_consumption",
1268
+ name="Last trip average auxillary consumer consumption",
1269
+ icon="mdi:flash",
1270
+ unit="kWh/100km",
1271
+ device_class="energy_distance"
1272
+ ),
1273
+ Sensor(
1274
+ attr="trip_last_total_electric_consumption",
1275
+ name="Last trip total electric consumption",
1276
+ icon="mdi:car-battery",
1277
+ unit="kWh/100km",
1278
+ device_class="energy_distance"
1279
+ ),
1280
+ Sensor(
1281
+ attr="trip_last_cycle_average_speed",
1282
+ name="Last cycle average speed",
1283
+ icon="mdi:speedometer",
1284
+ unit="km/h",
1285
+ device_class="speed"
1286
+ ),
1287
+ Sensor(
1288
+ attr="trip_last_cycle_average_electric_consumption",
1289
+ name="Last cycle average electric consumption",
1290
+ icon="mdi:car-battery",
1291
+ unit="kWh/100km",
1292
+ device_class="energy_distance"
1293
+ ),
1294
+ Sensor(
1295
+ attr="trip_last_cycle_average_fuel_consumption",
1296
+ name="Last cycle average fuel consumption",
1297
+ icon="mdi:fuel",
1298
+ unit="l/100km",
1299
+ ),
1300
+ Sensor(
1301
+ attr="trip_last_cycle_average_auxillary_consumption",
1302
+ name="Last cycle average auxillary consumption",
1303
+ icon="mdi:flash",
1304
+ unit="kWh/100km",
1305
+ device_class="energy_distance"
1306
+ ),
1307
+ Sensor(
1308
+ attr="trip_last_cycle_duration",
1309
+ name="Last cycle duration",
1310
+ icon="mdi:clock",
1311
+ unit="min",
1312
+ device_class="duration"
1313
+ ),
1314
+ Sensor(
1315
+ attr="trip_last_cycle_length",
1316
+ name="Last cycle length",
1317
+ icon="mdi:map-marker-distance",
1318
+ unit="km",
1319
+ device_class="distance"
1320
+ ),
1321
+ Sensor(
1322
+ attr="trip_last_cycle_recuperation",
1323
+ name="Last cycle recuperation",
1324
+ icon="mdi:battery-plus",
1325
+ unit="kWh/100km",
1326
+ device_class="energy_distance"
1327
+ ),
1328
+ Sensor(
1329
+ attr="trip_last_cycle_average_recuperation",
1330
+ name="Last cycle average recuperation",
1331
+ icon="mdi:battery-plus",
1332
+ unit="kWh/100km",
1333
+ device_class="energy_distance"
1334
+ ),
1335
+ Sensor(
1336
+ attr="trip_last_cycle_average_aux_consumer_consumption",
1337
+ name="Last cycle average auxillary consumer consumption",
1338
+ icon="mdi:flash",
1339
+ unit="kWh/100km",
1340
+ device_class="energy_distance"
1341
+ ),
1342
+ Sensor(
1343
+ attr="trip_last_cycle_total_electric_consumption",
1344
+ name="Last cycle total electric consumption",
1345
+ icon="mdi:car-battery",
1346
+ unit="kWh/100km",
1347
+ device_class="energy_distance"
1348
+ ),
1349
+ Sensor(
1350
+ attr="model_image_large",
1351
+ name="Model image URL (Large)",
1352
+ icon="mdi:file-image",
1353
+ ),
1354
+ Sensor(
1355
+ attr="model_image_small",
1356
+ name="Model image URL (Small)",
1357
+ icon="mdi:file-image",
1358
+ ),
1359
+ Sensor(
1360
+ attr="pheater_status",
1361
+ name="Parking Heater heating/ventilation status",
1362
+ icon="mdi:radiator",
1363
+ ),
1364
+ Sensor(
1365
+ attr="pheater_duration",
1366
+ name="Parking Heater heating/ventilation duration",
1367
+ icon="mdi:timer",
1368
+ unit="minutes",
1369
+ device_class="duration"
1370
+ ),
1371
+ Sensor(
1372
+ attr="outside_temperature",
1373
+ name="Outside temperature",
1374
+ icon="mdi:thermometer",
1375
+ unit="°C",
1376
+ device_class="temperature"
1377
+ ),
1378
+ Sensor(
1379
+ attr="requests_remaining",
1380
+ name="Requests remaining",
1381
+ icon="mdi:chat-alert",
1382
+ unit="",
1383
+ ),
1384
+ BinarySensor(
1385
+ attr="external_power",
1386
+ name="External power",
1387
+ device_class="power"
1388
+ ),
1389
+ BinarySensor(
1390
+ attr="energy_flow",
1391
+ name="Energy flow",
1392
+ device_class="power"
1393
+ ),
1394
+ #BinarySensor(
1395
+ # attr="charging_state",
1396
+ # name="Charging state",
1397
+ # device_class="power"
1398
+ #),
1399
+ BinarySensor(
1400
+ attr="parking_light",
1401
+ name="Parking light",
1402
+ device_class="light",
1403
+ icon="mdi:car-parking-lights"
1404
+ ),
1405
+ BinarySensor(
1406
+ attr="door_locked",
1407
+ name="Doors locked",
1408
+ device_class="lock",
1409
+ reverse_state=False
1410
+ ),
1411
+ BinarySensor(
1412
+ attr="door_closed_left_front",
1413
+ name="Door closed left front",
1414
+ device_class="door",
1415
+ reverse_state=False,
1416
+ icon="mdi:car-door"
1417
+ ),
1418
+ BinarySensor(
1419
+ attr="door_closed_right_front",
1420
+ name="Door closed right front",
1421
+ device_class="door",
1422
+ reverse_state=False,
1423
+ icon="mdi:car-door"
1424
+ ),
1425
+ BinarySensor(
1426
+ attr="door_closed_left_back",
1427
+ name="Door closed left back",
1428
+ device_class="door",
1429
+ reverse_state=False,
1430
+ icon="mdi:car-door"
1431
+ ),
1432
+ BinarySensor(
1433
+ attr="door_closed_right_back",
1434
+ name="Door closed right back",
1435
+ device_class="door",
1436
+ reverse_state=False,
1437
+ icon="mdi:car-door"
1438
+ ),
1439
+ BinarySensor(
1440
+ attr="trunk_locked",
1441
+ name="Trunk locked",
1442
+ device_class="lock",
1443
+ reverse_state=False
1444
+ ),
1445
+ BinarySensor(
1446
+ attr="trunk_closed",
1447
+ name="Trunk closed",
1448
+ device_class="door",
1449
+ reverse_state=False
1450
+ ),
1451
+ BinarySensor(
1452
+ attr="hood_closed",
1453
+ name="Hood closed",
1454
+ device_class="door",
1455
+ reverse_state=False
1456
+ ),
1457
+ BinarySensor(
1458
+ attr="charging_cable_connected",
1459
+ name="Charging cable connected",
1460
+ device_class="plug",
1461
+ reverse_state=False
1462
+ ),
1463
+ BinarySensor(
1464
+ attr="charging_cable_locked",
1465
+ name="Charging cable locked",
1466
+ device_class="lock",
1467
+ reverse_state=False
1468
+ ),
1469
+ BinarySensor(
1470
+ attr="sunroof_closed",
1471
+ name="Sunroof closed",
1472
+ device_class="window",
1473
+ reverse_state=False
1474
+ ),
1475
+ BinarySensor(
1476
+ attr="windows_closed",
1477
+ name="Windows closed",
1478
+ device_class="window",
1479
+ reverse_state=False
1480
+ ),
1481
+ BinarySensor(
1482
+ attr="window_closed_left_front",
1483
+ name="Window closed left front",
1484
+ device_class="window",
1485
+ reverse_state=False
1486
+ ),
1487
+ BinarySensor(
1488
+ attr="window_closed_left_back",
1489
+ name="Window closed left back",
1490
+ device_class="window",
1491
+ reverse_state=False
1492
+ ),
1493
+ BinarySensor(
1494
+ attr="window_closed_right_front",
1495
+ name="Window closed right front",
1496
+ device_class="window",
1497
+ reverse_state=False
1498
+ ),
1499
+ BinarySensor(
1500
+ attr="window_closed_right_back",
1501
+ name="Window closed right back",
1502
+ device_class="window",
1503
+ reverse_state=False
1504
+ ),
1505
+ BinarySensor(
1506
+ attr="vehicle_moving",
1507
+ name="Vehicle Moving",
1508
+ device_class="moving"
1509
+ ),
1510
+ BinarySensor(
1511
+ attr="request_in_progress",
1512
+ name="Request in progress",
1513
+ device_class="connectivity"
1514
+ ),
1515
+ ]
1516
+
1517
+
1518
+ class Dashboard:
1519
+ def __init__(self, vehicle, **config):
1520
+ self._config = config
1521
+ self.instruments = [
1522
+ instrument
1523
+ for instrument in create_instruments()
1524
+ if instrument.setup(vehicle, **config)
1525
+ ]
1526
+ _LOGGER.debug("Supported instruments: " + ", ".join(str(inst.attr) for inst in self.instruments))
1527
+