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."""
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."""

View File

@ -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,
),
)