Use entity_description in shelly rest sensors (#64843)

* Use entity_description in shelly rest sensors

* Use _attr_device_info

* Adjust _attr_unique_id and _attr_device_info

Co-authored-by: epenet <epenet@users.noreply.github.com>
pull/64854/head
epenet 2022-01-24 18:43:43 +01:00 committed by GitHub
parent d1d33f0dc5
commit c8a63d4ffc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 78 deletions

View File

@ -1,11 +1,13 @@
"""Binary sensor for Shelly."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Final, cast
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_ON
@ -16,7 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import CONF_SLEEP_PERIOD
from .entity import (
BlockAttributeDescription,
RestAttributeDescription,
RestEntityDescription,
RpcAttributeDescription,
ShellyBlockAttributeEntity,
ShellyRestAttributeEntity,
@ -32,6 +34,12 @@ from .utils import (
is_rpc_momentary_input,
)
@dataclass
class RestBinarySensorDescription(RestEntityDescription, BinarySensorEntityDescription):
"""Class to describe a REST binary sensor."""
SENSORS: Final = {
("device", "overtemp"): BlockAttributeDescription(
name="Overheating",
@ -102,18 +110,20 @@ SENSORS: Final = {
}
REST_SENSORS: Final = {
"cloud": RestAttributeDescription(
"cloud": RestBinarySensorDescription(
key="cloud",
name="Cloud",
value=lambda status, _: status["cloud"]["connected"],
device_class=BinarySensorDeviceClass.CONNECTIVITY,
default_enabled=False,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"fwupdate": RestAttributeDescription(
"fwupdate": RestBinarySensorDescription(
key="fwupdate",
name="Firmware Update",
device_class=BinarySensorDeviceClass.UPDATE,
value=lambda status, _: status["update"]["has_update"],
default_enabled=False,
entity_registry_enabled_default=False,
extra_state_attributes=lambda status: {
"latest_stable_version": status["update"]["new_version"],
"installed_version": status["update"]["old_version"],
@ -200,9 +210,13 @@ class BlockBinarySensor(ShellyBlockAttributeEntity, BinarySensorEntity):
class RestBinarySensor(ShellyRestAttributeEntity, BinarySensorEntity):
"""Represent a REST binary sensor entity."""
entity_description: RestBinarySensorDescription
@property
def is_on(self) -> bool:
def is_on(self) -> bool | None:
"""Return true if REST sensor state is on."""
if self.attribute_value is None:
return None
return bool(self.attribute_value)

View File

@ -2,7 +2,7 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable
from collections.abc import Callable, Mapping
from dataclasses import dataclass
import logging
from typing import Any, Final, cast
@ -19,7 +19,7 @@ from homeassistant.helpers import (
entity_registry,
update_coordinator,
)
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity import DeviceInfo, EntityCategory, EntityDescription
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.typing import StateType
@ -205,7 +205,7 @@ async def async_setup_entry_rest(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
sensors: dict[str, RestAttributeDescription],
sensors: Mapping[str, RestEntityDescription],
sensor_class: Callable,
) -> None:
"""Set up entities for REST sensors."""
@ -271,18 +271,11 @@ class RpcAttributeDescription:
@dataclass
class RestAttributeDescription:
"""Class to describe a REST sensor."""
class RestEntityDescription(EntityDescription):
"""Class to describe a REST entity."""
name: str
icon: str | None = None
unit: str | None = None
value: Callable[[dict, Any], Any] | None = None
device_class: str | None = None
state_class: str | None = None
default_enabled: bool = True
extra_state_attributes: Callable[[dict], dict | None] | None = None
entity_category: EntityCategory | None = None
class ShellyBlockEntity(entity.Entity):
@ -494,37 +487,26 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity):
class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity):
"""Class to load info from REST."""
entity_description: RestEntityDescription
def __init__(
self,
wrapper: BlockDeviceWrapper,
attribute: str,
description: RestAttributeDescription,
description: RestEntityDescription,
) -> None:
"""Initialize sensor."""
super().__init__(wrapper)
self.wrapper = wrapper
self.attribute = attribute
self.description = description
self._name = get_block_entity_name(wrapper.device, None, self.description.name)
self.entity_description = description
self._attr_name = get_block_entity_name(wrapper.device, None, description.name)
self._attr_unique_id = f"{wrapper.mac}-{attribute}"
self._attr_device_info = DeviceInfo(
connections={(device_registry.CONNECTION_NETWORK_MAC, wrapper.mac)}
)
self._last_value = None
@property
def name(self) -> str:
"""Name of sensor."""
return self._name
@property
def device_info(self) -> DeviceInfo:
"""Device info."""
return {
"connections": {(device_registry.CONNECTION_NETWORK_MAC, self.wrapper.mac)}
}
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if it should be enabled by default."""
return self.description.default_enabled
@property
def available(self) -> bool:
"""Available."""
@ -533,39 +515,21 @@ class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity):
@property
def attribute_value(self) -> StateType:
"""Value of sensor."""
if callable(self.description.value):
self._last_value = self.description.value(
if callable(self.entity_description.value):
self._last_value = self.entity_description.value(
self.wrapper.device.status, self._last_value
)
return self._last_value
@property
def device_class(self) -> str | None:
"""Device class of sensor."""
return self.description.device_class
@property
def icon(self) -> str | None:
"""Icon of sensor."""
return self.description.icon
@property
def unique_id(self) -> str:
"""Return unique ID of entity."""
return f"{self.wrapper.mac}-{self.attribute}"
@property
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
if self.description.extra_state_attributes is None:
if self.entity_description.extra_state_attributes is None:
return None
return self.description.extra_state_attributes(self.wrapper.device.status)
@property
def entity_category(self) -> EntityCategory | None:
"""Return category of entity."""
return self.description.entity_category
return self.entity_description.extra_state_attributes(
self.wrapper.device.status
)
class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity):

View File

@ -1,11 +1,13 @@
"""Sensor for Shelly."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Final, cast
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
@ -29,7 +31,7 @@ from homeassistant.helpers.typing import StateType
from .const import CONF_SLEEP_PERIOD, SHAIR_MAX_WORK_HOURS
from .entity import (
BlockAttributeDescription,
RestAttributeDescription,
RestEntityDescription,
RpcAttributeDescription,
ShellyBlockAttributeEntity,
ShellyRestAttributeEntity,
@ -41,6 +43,12 @@ from .entity import (
)
from .utils import get_device_entry_gen, get_device_uptime, temperature_unit
@dataclass
class RestSensorDescription(RestEntityDescription, SensorEntityDescription):
"""Class to describe a REST sensor."""
SENSORS: Final = {
("device", "battery"): BlockAttributeDescription(
name="Battery",
@ -229,20 +237,22 @@ SENSORS: Final = {
}
REST_SENSORS: Final = {
"rssi": RestAttributeDescription(
"rssi": RestSensorDescription(
key="rssi",
name="RSSI",
unit=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
value=lambda status, _: status["wifi_sta"]["rssi"],
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
state_class=SensorStateClass.MEASUREMENT,
default_enabled=False,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
"uptime": RestAttributeDescription(
"uptime": RestSensorDescription(
key="uptime",
name="Uptime",
value=lambda status, last: get_device_uptime(status["uptime"], last),
device_class=SensorDeviceClass.TIMESTAMP,
default_enabled=False,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
),
}
@ -359,21 +369,13 @@ class BlockSensor(ShellyBlockAttributeEntity, SensorEntity):
class RestSensor(ShellyRestAttributeEntity, SensorEntity):
"""Represent a REST sensor."""
entity_description: RestSensorDescription
@property
def native_value(self) -> StateType:
"""Return value of sensor."""
return self.attribute_value
@property
def state_class(self) -> str | None:
"""State class of sensor."""
return self.description.state_class
@property
def native_unit_of_measurement(self) -> str | None:
"""Return unit of sensor."""
return self.description.unit
class RpcSensor(ShellyRpcAttributeEntity, SensorEntity):
"""Represent a RPC sensor."""