mirror of https://github.com/home-assistant/core
135 lines
4.6 KiB
Python
135 lines
4.6 KiB
Python
"""Support for esphome sensors."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date, datetime
|
|
import math
|
|
|
|
from aioesphomeapi import (
|
|
EntityInfo,
|
|
SensorInfo,
|
|
SensorState,
|
|
SensorStateClass as EsphomeSensorStateClass,
|
|
TextSensorInfo,
|
|
TextSensorState,
|
|
)
|
|
from aioesphomeapi.model import LastResetType
|
|
|
|
from homeassistant.components.sensor import (
|
|
SensorDeviceClass,
|
|
SensorEntity,
|
|
SensorStateClass,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.util import dt as dt_util
|
|
from homeassistant.util.enum import try_parse_enum
|
|
|
|
from .entity import EsphomeEntity, platform_async_setup_entry
|
|
from .enum_mapper import EsphomeEnumMapper
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
|
) -> None:
|
|
"""Set up esphome sensors based on a config entry."""
|
|
await platform_async_setup_entry(
|
|
hass,
|
|
entry,
|
|
async_add_entities,
|
|
info_type=SensorInfo,
|
|
entity_type=EsphomeSensor,
|
|
state_type=SensorState,
|
|
)
|
|
await platform_async_setup_entry(
|
|
hass,
|
|
entry,
|
|
async_add_entities,
|
|
info_type=TextSensorInfo,
|
|
entity_type=EsphomeTextSensor,
|
|
state_type=TextSensorState,
|
|
)
|
|
|
|
|
|
_STATE_CLASSES: EsphomeEnumMapper[EsphomeSensorStateClass, SensorStateClass | None] = (
|
|
EsphomeEnumMapper(
|
|
{
|
|
EsphomeSensorStateClass.NONE: None,
|
|
EsphomeSensorStateClass.MEASUREMENT: SensorStateClass.MEASUREMENT,
|
|
EsphomeSensorStateClass.TOTAL_INCREASING: SensorStateClass.TOTAL_INCREASING,
|
|
EsphomeSensorStateClass.TOTAL: SensorStateClass.TOTAL,
|
|
}
|
|
)
|
|
)
|
|
|
|
|
|
class EsphomeSensor(EsphomeEntity[SensorInfo, SensorState], SensorEntity):
|
|
"""A sensor implementation for esphome."""
|
|
|
|
@callback
|
|
def _on_static_info_update(self, static_info: EntityInfo) -> None:
|
|
"""Set attrs from static info."""
|
|
super()._on_static_info_update(static_info)
|
|
static_info = self._static_info
|
|
self._attr_force_update = static_info.force_update
|
|
# protobuf doesn't support nullable strings so we need to check
|
|
# if the string is empty
|
|
if unit_of_measurement := static_info.unit_of_measurement:
|
|
self._attr_native_unit_of_measurement = unit_of_measurement
|
|
self._attr_device_class = try_parse_enum(
|
|
SensorDeviceClass, static_info.device_class
|
|
)
|
|
if not (state_class := static_info.state_class):
|
|
return
|
|
if (
|
|
state_class == EsphomeSensorStateClass.MEASUREMENT
|
|
and static_info.last_reset_type == LastResetType.AUTO
|
|
):
|
|
# Legacy, last_reset_type auto was the equivalent to the
|
|
# TOTAL_INCREASING state class
|
|
self._attr_state_class = SensorStateClass.TOTAL_INCREASING
|
|
else:
|
|
self._attr_state_class = _STATE_CLASSES.from_esphome(state_class)
|
|
|
|
@property
|
|
def native_value(self) -> datetime | str | None:
|
|
"""Return the state of the entity."""
|
|
if not self._has_state or (state := self._state).missing_state:
|
|
return None
|
|
state_float = state.state
|
|
if not math.isfinite(state_float):
|
|
return None
|
|
if self.device_class is SensorDeviceClass.TIMESTAMP:
|
|
return dt_util.utc_from_timestamp(state_float)
|
|
return f"{state_float:.{self._static_info.accuracy_decimals}f}"
|
|
|
|
|
|
class EsphomeTextSensor(EsphomeEntity[TextSensorInfo, TextSensorState], SensorEntity):
|
|
"""A text sensor implementation for ESPHome."""
|
|
|
|
@callback
|
|
def _on_static_info_update(self, static_info: EntityInfo) -> None:
|
|
"""Set attrs from static info."""
|
|
super()._on_static_info_update(static_info)
|
|
static_info = self._static_info
|
|
self._attr_device_class = try_parse_enum(
|
|
SensorDeviceClass, static_info.device_class
|
|
)
|
|
|
|
@property
|
|
def native_value(self) -> str | datetime | date | None:
|
|
"""Return the state of the entity."""
|
|
if not self._has_state or (state := self._state).missing_state:
|
|
return None
|
|
state_str = state.state
|
|
device_class = self.device_class
|
|
if device_class is SensorDeviceClass.TIMESTAMP:
|
|
return dt_util.parse_datetime(state_str)
|
|
if (
|
|
device_class is SensorDeviceClass.DATE
|
|
and (value := dt_util.parse_datetime(state_str)) is not None
|
|
):
|
|
return value.date()
|
|
return state_str
|