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
parent
d1d33f0dc5
commit
c8a63d4ffc
|
@ -1,11 +1,13 @@
|
||||||
"""Binary sensor for Shelly."""
|
"""Binary sensor for Shelly."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Final, cast
|
from typing import Final, cast
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import STATE_ON
|
from homeassistant.const import STATE_ON
|
||||||
|
@ -16,7 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from .const import CONF_SLEEP_PERIOD
|
from .const import CONF_SLEEP_PERIOD
|
||||||
from .entity import (
|
from .entity import (
|
||||||
BlockAttributeDescription,
|
BlockAttributeDescription,
|
||||||
RestAttributeDescription,
|
RestEntityDescription,
|
||||||
RpcAttributeDescription,
|
RpcAttributeDescription,
|
||||||
ShellyBlockAttributeEntity,
|
ShellyBlockAttributeEntity,
|
||||||
ShellyRestAttributeEntity,
|
ShellyRestAttributeEntity,
|
||||||
|
@ -32,6 +34,12 @@ from .utils import (
|
||||||
is_rpc_momentary_input,
|
is_rpc_momentary_input,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RestBinarySensorDescription(RestEntityDescription, BinarySensorEntityDescription):
|
||||||
|
"""Class to describe a REST binary sensor."""
|
||||||
|
|
||||||
|
|
||||||
SENSORS: Final = {
|
SENSORS: Final = {
|
||||||
("device", "overtemp"): BlockAttributeDescription(
|
("device", "overtemp"): BlockAttributeDescription(
|
||||||
name="Overheating",
|
name="Overheating",
|
||||||
|
@ -102,18 +110,20 @@ SENSORS: Final = {
|
||||||
}
|
}
|
||||||
|
|
||||||
REST_SENSORS: Final = {
|
REST_SENSORS: Final = {
|
||||||
"cloud": RestAttributeDescription(
|
"cloud": RestBinarySensorDescription(
|
||||||
|
key="cloud",
|
||||||
name="Cloud",
|
name="Cloud",
|
||||||
value=lambda status, _: status["cloud"]["connected"],
|
value=lambda status, _: status["cloud"]["connected"],
|
||||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||||
default_enabled=False,
|
entity_registry_enabled_default=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
"fwupdate": RestAttributeDescription(
|
"fwupdate": RestBinarySensorDescription(
|
||||||
|
key="fwupdate",
|
||||||
name="Firmware Update",
|
name="Firmware Update",
|
||||||
device_class=BinarySensorDeviceClass.UPDATE,
|
device_class=BinarySensorDeviceClass.UPDATE,
|
||||||
value=lambda status, _: status["update"]["has_update"],
|
value=lambda status, _: status["update"]["has_update"],
|
||||||
default_enabled=False,
|
entity_registry_enabled_default=False,
|
||||||
extra_state_attributes=lambda status: {
|
extra_state_attributes=lambda status: {
|
||||||
"latest_stable_version": status["update"]["new_version"],
|
"latest_stable_version": status["update"]["new_version"],
|
||||||
"installed_version": status["update"]["old_version"],
|
"installed_version": status["update"]["old_version"],
|
||||||
|
@ -200,9 +210,13 @@ class BlockBinarySensor(ShellyBlockAttributeEntity, BinarySensorEntity):
|
||||||
class RestBinarySensor(ShellyRestAttributeEntity, BinarySensorEntity):
|
class RestBinarySensor(ShellyRestAttributeEntity, BinarySensorEntity):
|
||||||
"""Represent a REST binary sensor entity."""
|
"""Represent a REST binary sensor entity."""
|
||||||
|
|
||||||
|
entity_description: RestBinarySensorDescription
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool | None:
|
||||||
"""Return true if REST sensor state is on."""
|
"""Return true if REST sensor state is on."""
|
||||||
|
if self.attribute_value is None:
|
||||||
|
return None
|
||||||
return bool(self.attribute_value)
|
return bool(self.attribute_value)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable, Mapping
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Final, cast
|
from typing import Any, Final, cast
|
||||||
|
@ -19,7 +19,7 @@ from homeassistant.helpers import (
|
||||||
entity_registry,
|
entity_registry,
|
||||||
update_coordinator,
|
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.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
@ -205,7 +205,7 @@ async def async_setup_entry_rest(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
sensors: dict[str, RestAttributeDescription],
|
sensors: Mapping[str, RestEntityDescription],
|
||||||
sensor_class: Callable,
|
sensor_class: Callable,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up entities for REST sensors."""
|
"""Set up entities for REST sensors."""
|
||||||
|
@ -271,18 +271,11 @@ class RpcAttributeDescription:
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RestAttributeDescription:
|
class RestEntityDescription(EntityDescription):
|
||||||
"""Class to describe a REST sensor."""
|
"""Class to describe a REST entity."""
|
||||||
|
|
||||||
name: str
|
|
||||||
icon: str | None = None
|
|
||||||
unit: str | None = None
|
|
||||||
value: Callable[[dict, Any], Any] | 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
|
extra_state_attributes: Callable[[dict], dict | None] | None = None
|
||||||
entity_category: EntityCategory | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class ShellyBlockEntity(entity.Entity):
|
class ShellyBlockEntity(entity.Entity):
|
||||||
|
@ -494,37 +487,26 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity):
|
||||||
class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity):
|
class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity):
|
||||||
"""Class to load info from REST."""
|
"""Class to load info from REST."""
|
||||||
|
|
||||||
|
entity_description: RestEntityDescription
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
wrapper: BlockDeviceWrapper,
|
wrapper: BlockDeviceWrapper,
|
||||||
attribute: str,
|
attribute: str,
|
||||||
description: RestAttributeDescription,
|
description: RestEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize sensor."""
|
"""Initialize sensor."""
|
||||||
super().__init__(wrapper)
|
super().__init__(wrapper)
|
||||||
self.wrapper = wrapper
|
self.wrapper = wrapper
|
||||||
self.attribute = attribute
|
self.attribute = attribute
|
||||||
self.description = description
|
self.entity_description = description
|
||||||
self._name = get_block_entity_name(wrapper.device, None, self.description.name)
|
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
|
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
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Available."""
|
"""Available."""
|
||||||
|
@ -533,39 +515,21 @@ class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity):
|
||||||
@property
|
@property
|
||||||
def attribute_value(self) -> StateType:
|
def attribute_value(self) -> StateType:
|
||||||
"""Value of sensor."""
|
"""Value of sensor."""
|
||||||
if callable(self.description.value):
|
if callable(self.entity_description.value):
|
||||||
self._last_value = self.description.value(
|
self._last_value = self.entity_description.value(
|
||||||
self.wrapper.device.status, self._last_value
|
self.wrapper.device.status, self._last_value
|
||||||
)
|
)
|
||||||
return 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
|
@property
|
||||||
def extra_state_attributes(self) -> dict[str, Any] | None:
|
def extra_state_attributes(self) -> dict[str, Any] | None:
|
||||||
"""Return the state attributes."""
|
"""Return the state attributes."""
|
||||||
if self.description.extra_state_attributes is None:
|
if self.entity_description.extra_state_attributes is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.description.extra_state_attributes(self.wrapper.device.status)
|
return self.entity_description.extra_state_attributes(
|
||||||
|
self.wrapper.device.status
|
||||||
@property
|
)
|
||||||
def entity_category(self) -> EntityCategory | None:
|
|
||||||
"""Return category of entity."""
|
|
||||||
return self.description.entity_category
|
|
||||||
|
|
||||||
|
|
||||||
class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity):
|
class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity):
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
"""Sensor for Shelly."""
|
"""Sensor for Shelly."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Final, cast
|
from typing import Final, cast
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
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 .const import CONF_SLEEP_PERIOD, SHAIR_MAX_WORK_HOURS
|
||||||
from .entity import (
|
from .entity import (
|
||||||
BlockAttributeDescription,
|
BlockAttributeDescription,
|
||||||
RestAttributeDescription,
|
RestEntityDescription,
|
||||||
RpcAttributeDescription,
|
RpcAttributeDescription,
|
||||||
ShellyBlockAttributeEntity,
|
ShellyBlockAttributeEntity,
|
||||||
ShellyRestAttributeEntity,
|
ShellyRestAttributeEntity,
|
||||||
|
@ -41,6 +43,12 @@ from .entity import (
|
||||||
)
|
)
|
||||||
from .utils import get_device_entry_gen, get_device_uptime, temperature_unit
|
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 = {
|
SENSORS: Final = {
|
||||||
("device", "battery"): BlockAttributeDescription(
|
("device", "battery"): BlockAttributeDescription(
|
||||||
name="Battery",
|
name="Battery",
|
||||||
|
@ -229,20 +237,22 @@ SENSORS: Final = {
|
||||||
}
|
}
|
||||||
|
|
||||||
REST_SENSORS: Final = {
|
REST_SENSORS: Final = {
|
||||||
"rssi": RestAttributeDescription(
|
"rssi": RestSensorDescription(
|
||||||
|
key="rssi",
|
||||||
name="RSSI",
|
name="RSSI",
|
||||||
unit=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||||
value=lambda status, _: status["wifi_sta"]["rssi"],
|
value=lambda status, _: status["wifi_sta"]["rssi"],
|
||||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
default_enabled=False,
|
entity_registry_enabled_default=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
"uptime": RestAttributeDescription(
|
"uptime": RestSensorDescription(
|
||||||
|
key="uptime",
|
||||||
name="Uptime",
|
name="Uptime",
|
||||||
value=lambda status, last: get_device_uptime(status["uptime"], last),
|
value=lambda status, last: get_device_uptime(status["uptime"], last),
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
default_enabled=False,
|
entity_registry_enabled_default=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -359,21 +369,13 @@ class BlockSensor(ShellyBlockAttributeEntity, SensorEntity):
|
||||||
class RestSensor(ShellyRestAttributeEntity, SensorEntity):
|
class RestSensor(ShellyRestAttributeEntity, SensorEntity):
|
||||||
"""Represent a REST sensor."""
|
"""Represent a REST sensor."""
|
||||||
|
|
||||||
|
entity_description: RestSensorDescription
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> StateType:
|
def native_value(self) -> StateType:
|
||||||
"""Return value of sensor."""
|
"""Return value of sensor."""
|
||||||
return self.attribute_value
|
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):
|
class RpcSensor(ShellyRpcAttributeEntity, SensorEntity):
|
||||||
"""Represent a RPC sensor."""
|
"""Represent a RPC sensor."""
|
||||||
|
|
Loading…
Reference in New Issue