diff --git a/homeassistant/components/starline/binary_sensor.py b/homeassistant/components/starline/binary_sensor.py index 3468d141cf6..e2e3d7ea4fa 100644 --- a/homeassistant/components/starline/binary_sensor.py +++ b/homeassistant/components/starline/binary_sensor.py @@ -1,57 +1,92 @@ """Reads vehicle status from StarLine API.""" +from __future__ import annotations + +from dataclasses import dataclass + from homeassistant.components.binary_sensor import ( DEVICE_CLASS_DOOR, DEVICE_CLASS_LOCK, DEVICE_CLASS_POWER, DEVICE_CLASS_PROBLEM, BinarySensorEntity, + BinarySensorEntityDescription, ) from .account import StarlineAccount, StarlineDevice from .const import DOMAIN from .entity import StarlineEntity -SENSOR_TYPES = { - "hbrake": ["Hand Brake", DEVICE_CLASS_POWER], - "hood": ["Hood", DEVICE_CLASS_DOOR], - "trunk": ["Trunk", DEVICE_CLASS_DOOR], - "alarm": ["Alarm", DEVICE_CLASS_PROBLEM], - "door": ["Doors", DEVICE_CLASS_LOCK], -} + +@dataclass +class StarlineRequiredKeysMixin: + """Mixin for required keys.""" + + name_: str + + +@dataclass +class StarlineBinarySensorEntityDescription( + BinarySensorEntityDescription, StarlineRequiredKeysMixin +): + """Describes Starline binary_sensor entity.""" + + +BINARY_SENSOR_TYPES: tuple[StarlineBinarySensorEntityDescription, ...] = ( + StarlineBinarySensorEntityDescription( + key="hbrake", + name_="Hand Brake", + device_class=DEVICE_CLASS_POWER, + ), + StarlineBinarySensorEntityDescription( + key="hood", + name_="Hood", + device_class=DEVICE_CLASS_DOOR, + ), + StarlineBinarySensorEntityDescription( + key="trunk", + name_="Trunk", + device_class=DEVICE_CLASS_DOOR, + ), + StarlineBinarySensorEntityDescription( + key="alarm", + name_="Alarm", + device_class=DEVICE_CLASS_PROBLEM, + ), + StarlineBinarySensorEntityDescription( + key="door", + name_="Doors", + device_class=DEVICE_CLASS_LOCK, + ), +) async def async_setup_entry(hass, entry, async_add_entities): """Set up the StarLine sensors.""" account: StarlineAccount = hass.data[DOMAIN][entry.entry_id] - entities = [] - for device in account.api.devices.values(): - for key, value in SENSOR_TYPES.items(): - if key in device.car_state: - sensor = StarlineSensor(account, device, key, *value) - if sensor.is_on is not None: - entities.append(sensor) + entities = [ + sensor + for device in account.api.devices.values() + for description in BINARY_SENSOR_TYPES + if description.key in device.car_state + if (sensor := StarlineSensor(account, device, description)).is_on is not None + ] async_add_entities(entities) class StarlineSensor(StarlineEntity, BinarySensorEntity): """Representation of a StarLine binary sensor.""" + entity_description: StarlineBinarySensorEntityDescription + def __init__( self, account: StarlineAccount, device: StarlineDevice, - key: str, - name: str, - device_class: str, + description: StarlineBinarySensorEntityDescription, ) -> None: """Initialize sensor.""" - super().__init__(account, device, key, name) - self._device_class = device_class - - @property - def device_class(self): - """Return the class of the binary sensor.""" - return self._device_class + super().__init__(account, device, description.key, description.name_) + self.entity_description = description @property def is_on(self): diff --git a/homeassistant/components/starline/sensor.py b/homeassistant/components/starline/sensor.py index 92c6acbab0b..26834cc384c 100644 --- a/homeassistant/components/starline/sensor.py +++ b/homeassistant/components/starline/sensor.py @@ -1,5 +1,13 @@ """Reads vehicle status from StarLine API.""" -from homeassistant.components.sensor import DEVICE_CLASS_TEMPERATURE, SensorEntity +from __future__ import annotations + +from dataclasses import dataclass + +from homeassistant.components.sensor import ( + DEVICE_CLASS_TEMPERATURE, + SensorEntity, + SensorEntityDescription, +) from homeassistant.const import ( ELECTRIC_POTENTIAL_VOLT, LENGTH_KILOMETERS, @@ -13,48 +21,94 @@ from .account import StarlineAccount, StarlineDevice from .const import DOMAIN from .entity import StarlineEntity -SENSOR_TYPES = { - "battery": ["Battery", None, ELECTRIC_POTENTIAL_VOLT, None], - "balance": ["Balance", None, None, "mdi:cash-multiple"], - "ctemp": ["Interior Temperature", DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None], - "etemp": ["Engine Temperature", DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, None], - "gsm_lvl": ["GSM Signal", None, PERCENTAGE, None], - "fuel": ["Fuel Volume", None, None, "mdi:fuel"], - "errors": ["OBD Errors", None, None, "mdi:alert-octagon"], - "mileage": ["Mileage", None, LENGTH_KILOMETERS, "mdi:counter"], -} + +@dataclass +class StarlineRequiredKeysMixin: + """Mixin for required keys.""" + + name_: str + + +@dataclass +class StarlineSensorEntityDescription( + SensorEntityDescription, StarlineRequiredKeysMixin +): + """Describes Starline binary_sensor entity.""" + + +SENSOR_TYPES: tuple[StarlineSensorEntityDescription, ...] = ( + StarlineSensorEntityDescription( + key="battery", + name_="Battery", + native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + ), + StarlineSensorEntityDescription( + key="balance", + name_="Balance", + icon="mdi:cash-multiple", + ), + StarlineSensorEntityDescription( + key="ctemp", + name_="Interior Temperature", + device_class=DEVICE_CLASS_TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + ), + StarlineSensorEntityDescription( + key="etemp", + name_="Engine Temperature", + device_class=DEVICE_CLASS_TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + ), + StarlineSensorEntityDescription( + key="gsm_lvl", + name_="GSM Signal", + native_unit_of_measurement=PERCENTAGE, + ), + StarlineSensorEntityDescription( + key="fuel", + name_="Fuel Volume", + icon="mdi:fuel", + ), + StarlineSensorEntityDescription( + key="errors", + name_="OBD Errors", + icon="mdi:alert-octagon", + ), + StarlineSensorEntityDescription( + key="mileage", + name_="Mileage", + native_unit_of_measurement=LENGTH_KILOMETERS, + icon="mdi:counter", + ), +) async def async_setup_entry(hass, entry, async_add_entities): """Set up the StarLine sensors.""" account: StarlineAccount = hass.data[DOMAIN][entry.entry_id] - entities = [] - for device in account.api.devices.values(): - for key, value in SENSOR_TYPES.items(): - sensor = StarlineSensor(account, device, key, *value) - if sensor.state is not None: - entities.append(sensor) + entities = [ + sensor + for device in account.api.devices.values() + for description in SENSOR_TYPES + if (sensor := StarlineSensor(account, device, description)).state is not None + ] async_add_entities(entities) class StarlineSensor(StarlineEntity, SensorEntity): """Representation of a StarLine sensor.""" + entity_description: StarlineSensorEntityDescription + def __init__( self, account: StarlineAccount, device: StarlineDevice, - key: str, - name: str, - device_class: str, - unit: str, - icon: str, + description: StarlineSensorEntityDescription, ) -> None: """Initialize StarLine sensor.""" - super().__init__(account, device, key, name) - self._device_class = device_class - self._unit = unit - self._icon = icon + super().__init__(account, device, description.key, description.name_) + self.entity_description = description @property def icon(self): @@ -66,7 +120,7 @@ class StarlineSensor(StarlineEntity, SensorEntity): ) if self._key == "gsm_lvl": return icon_for_signal_level(signal_level=self._device.gsm_level_percent) - return self._icon + return self.entity_description.icon @property def native_value(self): @@ -100,12 +154,7 @@ class StarlineSensor(StarlineEntity, SensorEntity): return PERCENTAGE if type_value == "litres": return VOLUME_LITERS - return self._unit - - @property - def device_class(self): - """Return the class of the sensor.""" - return self._device_class + return self.entity_description.native_unit_of_measurement @property def extra_state_attributes(self): diff --git a/homeassistant/components/starline/switch.py b/homeassistant/components/starline/switch.py index c8afc41cb2d..684e7ecc662 100644 --- a/homeassistant/components/starline/switch.py +++ b/homeassistant/components/starline/switch.py @@ -1,51 +1,86 @@ """Support for StarLine switch.""" -from homeassistant.components.switch import SwitchEntity +from __future__ import annotations + +from dataclasses import dataclass + +from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from .account import StarlineAccount, StarlineDevice from .const import DOMAIN from .entity import StarlineEntity -SWITCH_TYPES = { - "ign": ["Engine", "mdi:engine-outline", "mdi:engine-off-outline"], - "webasto": ["Webasto", "mdi:radiator", "mdi:radiator-off"], - "out": [ - "Additional Channel", - "mdi:access-point-network", - "mdi:access-point-network-off", - ], - "poke": ["Horn", "mdi:bullhorn-outline", "mdi:bullhorn-outline"], -} + +@dataclass +class StarlineRequiredKeysMixin: + """Mixin for required keys.""" + + name_: str + icon_on: str + icon_off: str + + +@dataclass +class StarlineSwitchEntityDescription( + SwitchEntityDescription, StarlineRequiredKeysMixin +): + """Describes Starline switch entity.""" + + +SWITCH_TYPES: tuple[StarlineSwitchEntityDescription, ...] = ( + StarlineSwitchEntityDescription( + key="ign", + name_="Engine", + icon_on="mdi:engine-outline", + icon_off="mdi:engine-off-outline", + ), + StarlineSwitchEntityDescription( + key="webasto", + name_="Webasto", + icon_on="mdi:radiator", + icon_off="mdi:radiator-off", + ), + StarlineSwitchEntityDescription( + key="out", + name_="Additional Channel", + icon_on="mdi:access-point-network", + icon_off="mdi:access-point-network-off", + ), + StarlineSwitchEntityDescription( + key="poke", + name_="Horn", + icon_on="mdi:bullhorn-outline", + icon_off="mdi:bullhorn-outline", + ), +) async def async_setup_entry(hass, entry, async_add_entities): """Set up the StarLine switch.""" account: StarlineAccount = hass.data[DOMAIN][entry.entry_id] - entities = [] - for device in account.api.devices.values(): - if device.support_state: - for key, value in SWITCH_TYPES.items(): - switch = StarlineSwitch(account, device, key, *value) - if switch.is_on is not None: - entities.append(switch) + entities = [ + switch + for device in account.api.devices.values() + if device.support_state + for description in SWITCH_TYPES + if (switch := StarlineSwitch(account, device, description)).is_on is not None + ] async_add_entities(entities) class StarlineSwitch(StarlineEntity, SwitchEntity): """Representation of a StarLine switch.""" + entity_description: StarlineSwitchEntityDescription + def __init__( self, account: StarlineAccount, device: StarlineDevice, - key: str, - name: str, - icon_on: str, - icon_off: str, + description: StarlineSwitchEntityDescription, ) -> None: """Initialize the switch.""" - super().__init__(account, device, key, name) - self._icon_on = icon_on - self._icon_off = icon_off + super().__init__(account, device, description.key, description.name_) + self.entity_description = description @property def available(self): @@ -62,7 +97,11 @@ class StarlineSwitch(StarlineEntity, SwitchEntity): @property def icon(self): """Icon to use in the frontend, if any.""" - return self._icon_on if self.is_on else self._icon_off + return ( + self.entity_description.icon_on + if self.is_on + else self.entity_description.icon_off + ) @property def assumed_state(self):