core/homeassistant/components/rfxtrx/sensor.py

336 lines
11 KiB
Python
Raw Normal View History

"""Support for RFXtrx sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from datetime import date, datetime
from decimal import Decimal
2015-07-23 17:36:05 +00:00
import logging
from typing import Any, cast
2015-07-23 17:36:05 +00:00
from RFXtrx import ControlEvent, RFXtrxDevice, RFXtrxEvent, SensorEvent
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
DEGREE,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
UV_INDEX,
EntityCategory,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
2015-07-23 17:36:05 +00:00
from . import DeviceTuple, RfxtrxEntity, async_setup_platform_entry, get_rfx_object
from .const import ATTR_EVENT
_LOGGER = logging.getLogger(__name__)
2015-07-23 17:36:05 +00:00
def _battery_convert(value: int | None) -> int | None:
"""Battery is given as a value between 0 and 9."""
if value is None:
return None
return (value + 1) * 10
def _rssi_convert(value: int | None) -> str | None:
"""Rssi is given as dBm value."""
if value is None:
return None
return f"{value*8-120}"
@dataclass
class RfxtrxSensorEntityDescription(SensorEntityDescription):
"""Description of sensor entities."""
convert: Callable[[Any], StateType | date | datetime | Decimal] = lambda x: cast(
StateType, x
)
SENSOR_TYPES = (
RfxtrxSensorEntityDescription(
key="Barometer",
device_class=SensorDeviceClass.PRESSURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.HPA,
),
RfxtrxSensorEntityDescription(
key="Battery numeric",
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
convert=_battery_convert,
entity_category=EntityCategory.DIAGNOSTIC,
),
RfxtrxSensorEntityDescription(
key="Current",
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
),
RfxtrxSensorEntityDescription(
key="Current Ch. 1",
translation_key="current_ch_1",
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
),
RfxtrxSensorEntityDescription(
key="Current Ch. 2",
translation_key="current_ch_2",
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
),
RfxtrxSensorEntityDescription(
key="Current Ch. 3",
translation_key="current_ch_3",
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
),
RfxtrxSensorEntityDescription(
key="Energy usage",
translation_key="instantaneous_power",
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
),
RfxtrxSensorEntityDescription(
key="Humidity",
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
),
RfxtrxSensorEntityDescription(
key="Rssi numeric",
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
convert=_rssi_convert,
entity_category=EntityCategory.DIAGNOSTIC,
),
RfxtrxSensorEntityDescription(
key="Temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
RfxtrxSensorEntityDescription(
key="Temperature2",
translation_key="temperature_2",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
RfxtrxSensorEntityDescription(
key="Total usage",
translation_key="total_energy_usage",
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
),
RfxtrxSensorEntityDescription(
key="Voltage",
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
),
RfxtrxSensorEntityDescription(
key="Wind direction",
translation_key="wind_direction",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=DEGREE,
),
RfxtrxSensorEntityDescription(
key="Rain rate",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
),
RfxtrxSensorEntityDescription(
key="Sound",
translation_key="sound",
),
RfxtrxSensorEntityDescription(
key="Sensor Status",
translation_key="sensor_status",
),
RfxtrxSensorEntityDescription(
key="Count",
translation_key="count",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement="count",
),
RfxtrxSensorEntityDescription(
key="Counter value",
translation_key="counter_value",
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement="count",
),
RfxtrxSensorEntityDescription(
key="Chill",
translation_key="chill",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
RfxtrxSensorEntityDescription(
key="Wind average speed",
translation_key="wind_average_speed",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
device_class=SensorDeviceClass.WIND_SPEED,
),
RfxtrxSensorEntityDescription(
key="Wind gust",
translation_key="wind_gust",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
device_class=SensorDeviceClass.WIND_SPEED,
),
RfxtrxSensorEntityDescription(
key="Rain total",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS,
device_class=SensorDeviceClass.PRECIPITATION,
),
RfxtrxSensorEntityDescription(
key="Forecast",
translation_key="forecast_status",
),
RfxtrxSensorEntityDescription(
key="Forecast numeric",
translation_key="forecast",
),
RfxtrxSensorEntityDescription(
key="Humidity status",
translation_key="humidity_status",
),
RfxtrxSensorEntityDescription(
key="UV",
translation_key="uv_index",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UV_INDEX,
),
)
SENSOR_TYPES_DICT = {desc.key: desc for desc in SENSOR_TYPES}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up config entry."""
def _supported(event: RFXtrxEvent) -> bool:
return isinstance(event, (ControlEvent, SensorEvent))
def _constructor(
event: RFXtrxEvent,
auto: RFXtrxEvent | None,
device_id: DeviceTuple,
entity_info: dict[str, Any],
) -> list[Entity]:
entities: list[Entity] = []
for data_type in set(event.values) & set(SENSOR_TYPES_DICT):
entities.append(
RfxtrxSensor(
event.device,
device_id,
SENSOR_TYPES_DICT[data_type],
event=event if auto else None,
)
)
return entities
2015-09-27 09:13:49 +00:00
await async_setup_platform_entry(
hass, config_entry, async_add_entities, _supported, _constructor
)
2015-07-23 17:36:05 +00:00
# pylint: disable-next=hass-invalid-inheritance # needs fixing
class RfxtrxSensor(RfxtrxEntity, SensorEntity):
"""Representation of a RFXtrx sensor.
2015-07-23 17:36:05 +00:00
Since all repeated events have meaning, these types of sensors
need to have force update enabled.
"""
_attr_force_update = True
entity_description: RfxtrxSensorEntityDescription
def __init__(
self,
device: RFXtrxDevice,
device_id: DeviceTuple,
entity_description: RfxtrxSensorEntityDescription,
event: RFXtrxEvent | None = None,
) -> None:
2016-03-08 15:46:34 +00:00
"""Initialize the sensor."""
super().__init__(device, device_id, event=event)
self.entity_description = entity_description
self._attr_unique_id = "_".join(x for x in (*device_id, entity_description.key))
2022-09-06 07:47:35 +00:00
async def async_added_to_hass(self) -> None:
"""Restore device state."""
await super().async_added_to_hass()
if (
self._event is None
and (old_state := await self.async_get_last_state()) is not None
and (event := old_state.attributes.get(ATTR_EVENT))
):
self._apply_event(get_rfx_object(event))
2015-07-23 17:36:05 +00:00
@property
def native_value(self) -> StateType | date | datetime | Decimal:
2016-03-08 15:46:34 +00:00
"""Return the state of the sensor."""
if not self._event:
2016-09-01 16:58:32 +00:00
return None
value = self._event.values.get(self.entity_description.key)
return self.entity_description.convert(value)
@callback
def _handle_event(self, event: RFXtrxEvent, device_id: DeviceTuple) -> None:
"""Check if event applies to me and update."""
if device_id != self._device_id:
return
if self.entity_description.key not in event.values:
return
_LOGGER.debug(
"Sensor update (Device ID: %s Class: %s Sub: %s)",
event.device.id_string,
event.device.__class__.__name__,
event.device.subtype,
)
self._apply_event(event)
self.async_write_ha_state()