From e5338c3f89db5863bc5d099e4bb9f106b3de7478 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 22 Aug 2021 22:26:24 +0200 Subject: [PATCH] Use EntityDescription - ring (#55023) Co-authored-by: Franck Nijhof --- .../components/ring/binary_sensor.py | 96 ++++---- homeassistant/components/ring/sensor.py | 233 +++++++++--------- 2 files changed, 167 insertions(+), 162 deletions(-) diff --git a/homeassistant/components/ring/binary_sensor.py b/homeassistant/components/ring/binary_sensor.py index d2c412a691d..de854022301 100644 --- a/homeassistant/components/ring/binary_sensor.py +++ b/homeassistant/components/ring/binary_sensor.py @@ -1,25 +1,49 @@ """This component provides HA sensor support for Ring Door Bell/Chimes.""" +from __future__ import annotations + +from dataclasses import dataclass from datetime import datetime from homeassistant.components.binary_sensor import ( DEVICE_CLASS_MOTION, DEVICE_CLASS_OCCUPANCY, BinarySensorEntity, + BinarySensorEntityDescription, ) from homeassistant.core import callback from . import DOMAIN from .entity import RingEntityMixin -# Sensor types: Name, category, device_class -SENSOR_TYPES = { - "ding": ["Ding", ["doorbots", "authorized_doorbots"], DEVICE_CLASS_OCCUPANCY], - "motion": [ - "Motion", - ["doorbots", "authorized_doorbots", "stickup_cams"], - DEVICE_CLASS_MOTION, - ], -} + +@dataclass +class RingRequiredKeysMixin: + """Mixin for required keys.""" + + category: list[str] + + +@dataclass +class RingBinarySensorEntityDescription( + BinarySensorEntityDescription, RingRequiredKeysMixin +): + """Describes Ring binary sensor entity.""" + + +BINARY_SENSOR_TYPES: tuple[RingBinarySensorEntityDescription, ...] = ( + RingBinarySensorEntityDescription( + key="ding", + name="Ding", + category=["doorbots", "authorized_doorbots"], + device_class=DEVICE_CLASS_OCCUPANCY, + ), + RingBinarySensorEntityDescription( + key="motion", + name="Motion", + category=["doorbots", "authorized_doorbots", "stickup_cams"], + device_class=DEVICE_CLASS_MOTION, + ), +) async def async_setup_entry(hass, config_entry, async_add_entities): @@ -27,35 +51,36 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ring = hass.data[DOMAIN][config_entry.entry_id]["api"] devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] - sensors = [] + entities = [ + RingBinarySensor(config_entry.entry_id, ring, device, description) + for device_type in ("doorbots", "authorized_doorbots", "stickup_cams") + for description in BINARY_SENSOR_TYPES + if device_type in description.category + for device in devices[device_type] + ] - for device_type in ("doorbots", "authorized_doorbots", "stickup_cams"): - for sensor_type, sensor in SENSOR_TYPES.items(): - if device_type not in sensor[1]: - continue - - for device in devices[device_type]: - sensors.append( - RingBinarySensor(config_entry.entry_id, ring, device, sensor_type) - ) - - async_add_entities(sensors) + async_add_entities(entities) class RingBinarySensor(RingEntityMixin, BinarySensorEntity): """A binary sensor implementation for Ring device.""" _active_alert = None + entity_description: RingBinarySensorEntityDescription - def __init__(self, config_entry_id, ring, device, sensor_type): + def __init__( + self, + config_entry_id, + ring, + device, + description: RingBinarySensorEntityDescription, + ): """Initialize a sensor for Ring device.""" super().__init__(config_entry_id, device) + self.entity_description = description self._ring = ring - self._sensor_type = sensor_type - self._name = f"{self._device.name} {SENSOR_TYPES.get(sensor_type)[0]}" - self._device_class = SENSOR_TYPES.get(sensor_type)[2] - self._state = None - self._unique_id = f"{device.id}-{sensor_type}" + self._attr_name = f"{device.name} {description.name}" + self._attr_unique_id = f"{device.id}-{description.key}" self._update_alert() async def async_added_to_hass(self): @@ -84,32 +109,17 @@ class RingBinarySensor(RingEntityMixin, BinarySensorEntity): ( alert for alert in self._ring.active_alerts() - if alert["kind"] == self._sensor_type + if alert["kind"] == self.entity_description.key and alert["doorbot_id"] == self._device.id ), None, ) - @property - def name(self): - """Return the name of the sensor.""" - return self._name - @property def is_on(self): """Return True if the binary sensor is on.""" return self._active_alert is not None - @property - def device_class(self): - """Return the class of the binary sensor.""" - return self._device_class - - @property - def unique_id(self): - """Return a unique ID.""" - return self._unique_id - @property def extra_state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/ring/sensor.py b/homeassistant/components/ring/sensor.py index 192ba03c010..c36b44f5ee5 100644 --- a/homeassistant/components/ring/sensor.py +++ b/homeassistant/components/ring/sensor.py @@ -1,5 +1,9 @@ """This component provides HA sensor support for Ring Door Bell/Chimes.""" -from homeassistant.components.sensor import SensorEntity +from __future__ import annotations + +from dataclasses import dataclass + +from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.const import ( DEVICE_CLASS_TIMESTAMP, PERCENTAGE, @@ -16,77 +20,58 @@ async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a sensor for a Ring device.""" devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] - sensors = [] + entities = [ + description.cls(config_entry.entry_id, device, description) + for device_type in ("chimes", "doorbots", "authorized_doorbots", "stickup_cams") + for description in SENSOR_TYPES + if device_type in description.category + for device in devices[device_type] + if not (device_type == "battery" and device.battery_life is None) + ] - for device_type in ("chimes", "doorbots", "authorized_doorbots", "stickup_cams"): - for sensor_type, sensor in SENSOR_TYPES.items(): - if device_type not in sensor[1]: - continue - - for device in devices[device_type]: - if device_type == "battery" and device.battery_life is None: - continue - - sensors.append(sensor[6](config_entry.entry_id, device, sensor_type)) - - async_add_entities(sensors) + async_add_entities(entities) class RingSensor(RingEntityMixin, SensorEntity): """A sensor implementation for Ring device.""" - def __init__(self, config_entry_id, device, sensor_type): + entity_description: RingSensorEntityDescription + _attr_should_poll = False # updates are controlled via the hub + + def __init__( + self, + config_entry_id, + device, + description: RingSensorEntityDescription, + ): """Initialize a sensor for Ring device.""" super().__init__(config_entry_id, device) - self._sensor_type = sensor_type + self.entity_description = description self._extra = None - self._icon = f"mdi:{SENSOR_TYPES.get(sensor_type)[3]}" - self._kind = SENSOR_TYPES.get(sensor_type)[4] - self._name = f"{self._device.name} {SENSOR_TYPES.get(sensor_type)[0]}" - self._unique_id = f"{device.id}-{sensor_type}" - - @property - def should_poll(self): - """Return False, updates are controlled via the hub.""" - return False - - @property - def name(self): - """Return the name of the sensor.""" - return self._name + self._attr_name = f"{device.name} {description.name}" + self._attr_unique_id = f"{device.id}-{description.key}" @property def native_value(self): """Return the state of the sensor.""" - if self._sensor_type == "volume": + sensor_type = self.entity_description.key + if sensor_type == "volume": return self._device.volume - if self._sensor_type == "battery": + if sensor_type == "battery": return self._device.battery_life - @property - def unique_id(self): - """Return a unique ID.""" - return self._unique_id - - @property - def device_class(self): - """Return sensor device class.""" - return SENSOR_TYPES[self._sensor_type][5] - @property def icon(self): """Icon to use in the frontend, if any.""" - if self._sensor_type == "battery" and self._device.battery_life is not None: + if ( + self.entity_description.key == "battery" + and self._device.battery_life is not None + ): return icon_for_battery_level( battery_level=self._device.battery_life, charging=False ) - return self._icon - - @property - def native_unit_of_measurement(self): - """Return the units of measurement.""" - return SENSOR_TYPES.get(self._sensor_type)[2] + return self.entity_description.icon class HealthDataRingSensor(RingSensor): @@ -122,10 +107,11 @@ class HealthDataRingSensor(RingSensor): @property def native_value(self): """Return the state of the sensor.""" - if self._sensor_type == "wifi_signal_category": + sensor_type = self.entity_description.key + if sensor_type == "wifi_signal_category": return self._device.wifi_signal_category - if self._sensor_type == "wifi_signal_strength": + if sensor_type == "wifi_signal_strength": return self._device.wifi_signal_strength @@ -156,12 +142,13 @@ class HistoryRingSensor(RingSensor): if not history_data: return + kind = self.entity_description.kind found = None - if self._kind is None: + if kind is None: found = history_data[0] else: for entry in history_data: - if entry["kind"] == self._kind: + if entry["kind"] == kind: found = entry break @@ -193,69 +180,77 @@ class HistoryRingSensor(RingSensor): return attrs -# Sensor types: Name, category, units, icon, kind, device_class, class -SENSOR_TYPES = { - "battery": [ - "Battery", - ["doorbots", "authorized_doorbots", "stickup_cams"], - PERCENTAGE, - None, - None, - "battery", - RingSensor, - ], - "last_activity": [ - "Last Activity", - ["doorbots", "authorized_doorbots", "stickup_cams"], - None, - "history", - None, - DEVICE_CLASS_TIMESTAMP, - HistoryRingSensor, - ], - "last_ding": [ - "Last Ding", - ["doorbots", "authorized_doorbots"], - None, - "history", - "ding", - DEVICE_CLASS_TIMESTAMP, - HistoryRingSensor, - ], - "last_motion": [ - "Last Motion", - ["doorbots", "authorized_doorbots", "stickup_cams"], - None, - "history", - "motion", - DEVICE_CLASS_TIMESTAMP, - HistoryRingSensor, - ], - "volume": [ - "Volume", - ["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], - None, - "bell-ring", - None, - None, - RingSensor, - ], - "wifi_signal_category": [ - "WiFi Signal Category", - ["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], - None, - "wifi", - None, - None, - HealthDataRingSensor, - ], - "wifi_signal_strength": [ - "WiFi Signal Strength", - ["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], - SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - "wifi", - None, - "signal_strength", - HealthDataRingSensor, - ], -} +@dataclass +class RingRequiredKeysMixin: + """Mixin for required keys.""" + + category: list[str] + cls: type[RingSensor] + + +@dataclass +class RingSensorEntityDescription(SensorEntityDescription, RingRequiredKeysMixin): + """Describes Ring sensor entity.""" + + kind: str | None = None + + +SENSOR_TYPES: tuple[RingSensorEntityDescription, ...] = ( + RingSensorEntityDescription( + key="battery", + name="Battery", + category=["doorbots", "authorized_doorbots", "stickup_cams"], + native_unit_of_measurement=PERCENTAGE, + device_class="battery", + cls=RingSensor, + ), + RingSensorEntityDescription( + key="last_activity", + name="Last Activity", + category=["doorbots", "authorized_doorbots", "stickup_cams"], + icon="mdi:history", + device_class=DEVICE_CLASS_TIMESTAMP, + cls=HistoryRingSensor, + ), + RingSensorEntityDescription( + key="last_ding", + name="Last Ding", + category=["doorbots", "authorized_doorbots"], + icon="mdi:history", + kind="ding", + device_class=DEVICE_CLASS_TIMESTAMP, + cls=HistoryRingSensor, + ), + RingSensorEntityDescription( + key="last_motion", + name="Last Motion", + category=["doorbots", "authorized_doorbots", "stickup_cams"], + icon="mdi:history", + kind="motion", + device_class=DEVICE_CLASS_TIMESTAMP, + cls=HistoryRingSensor, + ), + RingSensorEntityDescription( + key="volume", + name="Volume", + category=["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], + icon="mdi:bell-ring", + cls=RingSensor, + ), + RingSensorEntityDescription( + key="wifi_signal_category", + name="WiFi Signal Category", + category=["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], + icon="mdi:wifi", + cls=HealthDataRingSensor, + ), + RingSensorEntityDescription( + key="wifi_signal_strength", + name="WiFi Signal Strength", + category=["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + icon="mdi:wifi", + device_class="signal_strength", + cls=HealthDataRingSensor, + ), +)