Use EntityDescription - ring (#55023)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
Marc Mueller 2021-08-22 22:26:24 +02:00 committed by GitHub
parent 562212bb5e
commit e5338c3f89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 167 additions and 162 deletions

View File

@ -1,25 +1,49 @@
"""This component provides HA sensor support for Ring Door Bell/Chimes.""" """This component provides HA sensor support for Ring Door Bell/Chimes."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
DEVICE_CLASS_MOTION, DEVICE_CLASS_MOTION,
DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_OCCUPANCY,
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription,
) )
from homeassistant.core import callback from homeassistant.core import callback
from . import DOMAIN from . import DOMAIN
from .entity import RingEntityMixin from .entity import RingEntityMixin
# Sensor types: Name, category, device_class
SENSOR_TYPES = { @dataclass
"ding": ["Ding", ["doorbots", "authorized_doorbots"], DEVICE_CLASS_OCCUPANCY], class RingRequiredKeysMixin:
"motion": [ """Mixin for required keys."""
"Motion",
["doorbots", "authorized_doorbots", "stickup_cams"], category: list[str]
DEVICE_CLASS_MOTION,
],
} @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): 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"] ring = hass.data[DOMAIN][config_entry.entry_id]["api"]
devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] 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"): async_add_entities(entities)
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)
class RingBinarySensor(RingEntityMixin, BinarySensorEntity): class RingBinarySensor(RingEntityMixin, BinarySensorEntity):
"""A binary sensor implementation for Ring device.""" """A binary sensor implementation for Ring device."""
_active_alert = None _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.""" """Initialize a sensor for Ring device."""
super().__init__(config_entry_id, device) super().__init__(config_entry_id, device)
self.entity_description = description
self._ring = ring self._ring = ring
self._sensor_type = sensor_type self._attr_name = f"{device.name} {description.name}"
self._name = f"{self._device.name} {SENSOR_TYPES.get(sensor_type)[0]}" self._attr_unique_id = f"{device.id}-{description.key}"
self._device_class = SENSOR_TYPES.get(sensor_type)[2]
self._state = None
self._unique_id = f"{device.id}-{sensor_type}"
self._update_alert() self._update_alert()
async def async_added_to_hass(self): async def async_added_to_hass(self):
@ -84,32 +109,17 @@ class RingBinarySensor(RingEntityMixin, BinarySensorEntity):
( (
alert alert
for alert in self._ring.active_alerts() 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 and alert["doorbot_id"] == self._device.id
), ),
None, None,
) )
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property @property
def is_on(self): def is_on(self):
"""Return True if the binary sensor is on.""" """Return True if the binary sensor is on."""
return self._active_alert is not None 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 @property
def extra_state_attributes(self): def extra_state_attributes(self):
"""Return the state attributes.""" """Return the state attributes."""

View File

@ -1,5 +1,9 @@
"""This component provides HA sensor support for Ring Door Bell/Chimes.""" """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 ( from homeassistant.const import (
DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_TIMESTAMP,
PERCENTAGE, PERCENTAGE,
@ -16,77 +20,58 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up a sensor for a Ring device.""" """Set up a sensor for a Ring device."""
devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] 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"): async_add_entities(entities)
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)
class RingSensor(RingEntityMixin, SensorEntity): class RingSensor(RingEntityMixin, SensorEntity):
"""A sensor implementation for Ring device.""" """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.""" """Initialize a sensor for Ring device."""
super().__init__(config_entry_id, device) super().__init__(config_entry_id, device)
self._sensor_type = sensor_type self.entity_description = description
self._extra = None self._extra = None
self._icon = f"mdi:{SENSOR_TYPES.get(sensor_type)[3]}" self._attr_name = f"{device.name} {description.name}"
self._kind = SENSOR_TYPES.get(sensor_type)[4] self._attr_unique_id = f"{device.id}-{description.key}"
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
@property @property
def native_value(self): def native_value(self):
"""Return the state of the sensor.""" """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 return self._device.volume
if self._sensor_type == "battery": if sensor_type == "battery":
return self._device.battery_life 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 @property
def icon(self): def icon(self):
"""Icon to use in the frontend, if any.""" """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( return icon_for_battery_level(
battery_level=self._device.battery_life, charging=False battery_level=self._device.battery_life, charging=False
) )
return self._icon return self.entity_description.icon
@property
def native_unit_of_measurement(self):
"""Return the units of measurement."""
return SENSOR_TYPES.get(self._sensor_type)[2]
class HealthDataRingSensor(RingSensor): class HealthDataRingSensor(RingSensor):
@ -122,10 +107,11 @@ class HealthDataRingSensor(RingSensor):
@property @property
def native_value(self): def native_value(self):
"""Return the state of the sensor.""" """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 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 return self._device.wifi_signal_strength
@ -156,12 +142,13 @@ class HistoryRingSensor(RingSensor):
if not history_data: if not history_data:
return return
kind = self.entity_description.kind
found = None found = None
if self._kind is None: if kind is None:
found = history_data[0] found = history_data[0]
else: else:
for entry in history_data: for entry in history_data:
if entry["kind"] == self._kind: if entry["kind"] == kind:
found = entry found = entry
break break
@ -193,69 +180,77 @@ class HistoryRingSensor(RingSensor):
return attrs return attrs
# Sensor types: Name, category, units, icon, kind, device_class, class @dataclass
SENSOR_TYPES = { class RingRequiredKeysMixin:
"battery": [ """Mixin for required keys."""
"Battery",
["doorbots", "authorized_doorbots", "stickup_cams"], category: list[str]
PERCENTAGE, cls: type[RingSensor]
None,
None,
"battery", @dataclass
RingSensor, class RingSensorEntityDescription(SensorEntityDescription, RingRequiredKeysMixin):
], """Describes Ring sensor entity."""
"last_activity": [
"Last Activity", kind: str | None = None
["doorbots", "authorized_doorbots", "stickup_cams"],
None,
"history", SENSOR_TYPES: tuple[RingSensorEntityDescription, ...] = (
None, RingSensorEntityDescription(
DEVICE_CLASS_TIMESTAMP, key="battery",
HistoryRingSensor, name="Battery",
], category=["doorbots", "authorized_doorbots", "stickup_cams"],
"last_ding": [ native_unit_of_measurement=PERCENTAGE,
"Last Ding", device_class="battery",
["doorbots", "authorized_doorbots"], cls=RingSensor,
None, ),
"history", RingSensorEntityDescription(
"ding", key="last_activity",
DEVICE_CLASS_TIMESTAMP, name="Last Activity",
HistoryRingSensor, category=["doorbots", "authorized_doorbots", "stickup_cams"],
], icon="mdi:history",
"last_motion": [ device_class=DEVICE_CLASS_TIMESTAMP,
"Last Motion", cls=HistoryRingSensor,
["doorbots", "authorized_doorbots", "stickup_cams"], ),
None, RingSensorEntityDescription(
"history", key="last_ding",
"motion", name="Last Ding",
DEVICE_CLASS_TIMESTAMP, category=["doorbots", "authorized_doorbots"],
HistoryRingSensor, icon="mdi:history",
], kind="ding",
"volume": [ device_class=DEVICE_CLASS_TIMESTAMP,
"Volume", cls=HistoryRingSensor,
["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], ),
None, RingSensorEntityDescription(
"bell-ring", key="last_motion",
None, name="Last Motion",
None, category=["doorbots", "authorized_doorbots", "stickup_cams"],
RingSensor, icon="mdi:history",
], kind="motion",
"wifi_signal_category": [ device_class=DEVICE_CLASS_TIMESTAMP,
"WiFi Signal Category", cls=HistoryRingSensor,
["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], ),
None, RingSensorEntityDescription(
"wifi", key="volume",
None, name="Volume",
None, category=["chimes", "doorbots", "authorized_doorbots", "stickup_cams"],
HealthDataRingSensor, icon="mdi:bell-ring",
], cls=RingSensor,
"wifi_signal_strength": [ ),
"WiFi Signal Strength", RingSensorEntityDescription(
["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], key="wifi_signal_category",
SIGNAL_STRENGTH_DECIBELS_MILLIWATT, name="WiFi Signal Category",
"wifi", category=["chimes", "doorbots", "authorized_doorbots", "stickup_cams"],
None, icon="mdi:wifi",
"signal_strength", cls=HealthDataRingSensor,
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,
),
)