core/homeassistant/components/zha/sensor.py

158 lines
4.9 KiB
Python

"""Sensors on Zigbee Home Automation networks."""
from __future__ import annotations
from collections.abc import Mapping
import functools
import logging
from typing import Any
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from .entity import ZHAEntity
from .helpers import (
SIGNAL_ADD_ENTITIES,
EntityData,
async_add_entities as zha_async_add_entities,
exclude_none_values,
get_zha_data,
)
_LOGGER = logging.getLogger(__name__)
# For backwards compatibility and transparency, all expected extra state attributes are
# explicitly listed below. These should have been sensors themselves but for whatever
# reason were not created as such. They will be migrated to independent sensor entities
# in a future release.
_EXTRA_STATE_ATTRIBUTES: set[str] = {
# Battery
"battery_size",
"battery_quantity",
"battery_voltage",
# Power
"measurement_type",
"apparent_power_max",
"rms_current_max",
"rms_voltage_max",
"ac_frequency_max",
"power_factor_max",
"active_power_max",
# Smart Energy metering
"device_type",
"status",
"zcl_unit_of_measurement",
# Danfoss bitmaps
"In_progress",
"Valve_characteristic_found",
"Valve_characteristic_lost",
"Top_pcb_sensor_error",
"Side_pcb_sensor_error",
"Non_volatile_memory_error",
"Unknown_hw_error",
"Motor_error",
"Invalid_internal_communication",
"Invalid_clock_information",
"Radio_communication_error",
"Encoder_jammed",
"Low_battery",
"Critical_low_battery",
}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Zigbee Home Automation sensor from config entry."""
zha_data = get_zha_data(hass)
entities_to_create = zha_data.platforms[Platform.SENSOR]
unsub = async_dispatcher_connect(
hass,
SIGNAL_ADD_ENTITIES,
functools.partial(
zha_async_add_entities, async_add_entities, Sensor, entities_to_create
),
)
config_entry.async_on_unload(unsub)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
class Sensor(ZHAEntity, SensorEntity):
"""ZHA sensor."""
def __init__(self, entity_data: EntityData, **kwargs: Any) -> None:
"""Initialize the ZHA select entity."""
super().__init__(entity_data, **kwargs)
entity = self.entity_data.entity
if entity.device_class is not None:
self._attr_device_class = SensorDeviceClass(entity.device_class)
if entity.state_class is not None:
self._attr_state_class = SensorStateClass(entity.state_class)
if hasattr(entity.info_object, "unit") and entity.info_object.unit is not None:
self._attr_native_unit_of_measurement = entity.info_object.unit
if (
hasattr(entity, "entity_description")
and entity.entity_description is not None
):
entity_description = entity.entity_description
if entity_description.state_class is not None:
self._attr_state_class = SensorStateClass(
entity_description.state_class.value
)
if entity_description.scale is not None:
self._attr_scale = entity_description.scale
if entity_description.native_unit_of_measurement is not None:
self._attr_native_unit_of_measurement = (
entity_description.native_unit_of_measurement
)
if entity_description.device_class is not None:
self._attr_device_class = SensorDeviceClass(
entity_description.device_class.value
)
@property
def native_value(self) -> StateType:
"""Return the state of the entity."""
return self.entity_data.entity.native_value
@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
"""Return entity specific state attributes."""
entity = self.entity_data.entity
if entity.extra_state_attribute_names is None:
return None
if not entity.extra_state_attribute_names <= _EXTRA_STATE_ATTRIBUTES:
_LOGGER.warning(
"Unexpected extra state attributes found for sensor %s: %s",
entity,
entity.extra_state_attribute_names - _EXTRA_STATE_ATTRIBUTES,
)
return exclude_none_values(
{
name: entity.state.get(name)
for name in entity.extra_state_attribute_names
}
)