mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Add long-term statistics support for homematic sensors (#57396)
* Add long-term statistics support for homematic * Refactor cast list to SensorEntityDescription dict Additional: - Gas power, gas energy counter, air pressure and voltage uses long-term-statistics - Gas power, gas energy counter uses device class gas - Voltage uses device class voltage - air pressure uses device class pressure * Refactor expensive loop to separate dictionarys * Use entity description property + fix humidity sensor * Log missing sensor descriptions * Use state class measurement for illumination sensors * Move sensor entity desc missing warning to setup_platform * Set type for hmdevice and homematic to fix mypy error * Use EntityDescription instead of SensorEntityDescription * Update entity.py * fix type * Update climate.py * fix v2 Co-authored-by: Pascal Vizeli <pascal.vizeli@syshack.ch>
This commit is contained in:
parent
9241d80730
commit
5151c4d99b
@ -1,11 +1,16 @@
|
|||||||
"""Homematic base entity."""
|
"""Homematic base entity."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from pyhomematic import HMConnection
|
||||||
|
from pyhomematic.devicetypes.generic import HMGeneric
|
||||||
|
|
||||||
from homeassistant.const import ATTR_NAME
|
from homeassistant.const import ATTR_NAME
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_ADDRESS,
|
ATTR_ADDRESS,
|
||||||
@ -27,7 +32,14 @@ SCAN_INTERVAL_VARIABLES = timedelta(seconds=30)
|
|||||||
class HMDevice(Entity):
|
class HMDevice(Entity):
|
||||||
"""The HomeMatic device base object."""
|
"""The HomeMatic device base object."""
|
||||||
|
|
||||||
def __init__(self, config):
|
_homematic: HMConnection
|
||||||
|
_hmdevice: HMGeneric
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
config: dict[str, str],
|
||||||
|
entity_description: EntityDescription | None = None,
|
||||||
|
) -> None:
|
||||||
"""Initialize a generic HomeMatic device."""
|
"""Initialize a generic HomeMatic device."""
|
||||||
self._name = config.get(ATTR_NAME)
|
self._name = config.get(ATTR_NAME)
|
||||||
self._address = config.get(ATTR_ADDRESS)
|
self._address = config.get(ATTR_ADDRESS)
|
||||||
@ -35,12 +47,13 @@ class HMDevice(Entity):
|
|||||||
self._channel = config.get(ATTR_CHANNEL)
|
self._channel = config.get(ATTR_CHANNEL)
|
||||||
self._state = config.get(ATTR_PARAM)
|
self._state = config.get(ATTR_PARAM)
|
||||||
self._unique_id = config.get(ATTR_UNIQUE_ID)
|
self._unique_id = config.get(ATTR_UNIQUE_ID)
|
||||||
self._data = {}
|
self._data: dict[str, str] = {}
|
||||||
self._homematic = None
|
|
||||||
self._hmdevice = None
|
|
||||||
self._connected = False
|
self._connected = False
|
||||||
self._available = False
|
self._available = False
|
||||||
self._channel_map = set()
|
self._channel_map: set[str] = set()
|
||||||
|
|
||||||
|
if entity_description is not None:
|
||||||
|
self.entity_description = entity_description
|
||||||
|
|
||||||
# Set parameter to uppercase
|
# Set parameter to uppercase
|
||||||
if self._state:
|
if self._state:
|
||||||
|
@ -1,15 +1,29 @@
|
|||||||
"""Support for HomeMatic sensors."""
|
"""Support for HomeMatic sensors."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import (
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_NAME,
|
||||||
CONCENTRATION_PARTS_PER_MILLION,
|
CONCENTRATION_PARTS_PER_MILLION,
|
||||||
DEGREE,
|
DEGREE,
|
||||||
DEVICE_CLASS_CO2,
|
DEVICE_CLASS_CO2,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
DEVICE_CLASS_GAS,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
DEVICE_CLASS_POWER,
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
ELECTRIC_CURRENT_MILLIAMPERE,
|
ELECTRIC_CURRENT_MILLIAMPERE,
|
||||||
ELECTRIC_POTENTIAL_VOLT,
|
ELECTRIC_POTENTIAL_VOLT,
|
||||||
ENERGY_WATT_HOUR,
|
ENERGY_WATT_HOUR,
|
||||||
@ -24,7 +38,7 @@ from homeassistant.const import (
|
|||||||
VOLUME_CUBIC_METERS,
|
VOLUME_CUBIC_METERS,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .const import ATTR_DISCOVER_DEVICES
|
from .const import ATTR_DISCOVER_DEVICES, ATTR_PARAM
|
||||||
from .entity import HMDevice
|
from .entity import HMDevice
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -45,54 +59,174 @@ HM_STATE_HA_CAST = {
|
|||||||
"IPLockDLD": {0: None, 1: "locked", 2: "unlocked"},
|
"IPLockDLD": {0: None, 1: "locked", 2: "unlocked"},
|
||||||
}
|
}
|
||||||
|
|
||||||
HM_UNIT_HA_CAST = {
|
|
||||||
"HUMIDITY": PERCENTAGE,
|
SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = {
|
||||||
"TEMPERATURE": TEMP_CELSIUS,
|
"HUMIDITY": SensorEntityDescription(
|
||||||
"ACTUAL_TEMPERATURE": TEMP_CELSIUS,
|
key="HUMIDITY",
|
||||||
"BRIGHTNESS": "#",
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
"POWER": POWER_WATT,
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
"CURRENT": ELECTRIC_CURRENT_MILLIAMPERE,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
"VOLTAGE": ELECTRIC_POTENTIAL_VOLT,
|
),
|
||||||
"ENERGY_COUNTER": ENERGY_WATT_HOUR,
|
"ACTUAL_TEMPERATURE": SensorEntityDescription(
|
||||||
"GAS_POWER": VOLUME_CUBIC_METERS,
|
key="ACTUAL_TEMPERATURE",
|
||||||
"GAS_ENERGY_COUNTER": VOLUME_CUBIC_METERS,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
"IEC_POWER": POWER_WATT,
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
"IEC_ENERGY_COUNTER": ENERGY_WATT_HOUR,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
"LUX": LIGHT_LUX,
|
),
|
||||||
"ILLUMINATION": LIGHT_LUX,
|
"TEMPERATURE": SensorEntityDescription(
|
||||||
"CURRENT_ILLUMINATION": LIGHT_LUX,
|
key="TEMPERATURE",
|
||||||
"AVERAGE_ILLUMINATION": LIGHT_LUX,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
"LOWEST_ILLUMINATION": LIGHT_LUX,
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
"HIGHEST_ILLUMINATION": LIGHT_LUX,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
"RAIN_COUNTER": LENGTH_MILLIMETERS,
|
),
|
||||||
"WIND_SPEED": SPEED_KILOMETERS_PER_HOUR,
|
"LUX": SensorEntityDescription(
|
||||||
"WIND_DIRECTION": DEGREE,
|
key="LUX",
|
||||||
"WIND_DIRECTION_RANGE": DEGREE,
|
native_unit_of_measurement=LIGHT_LUX,
|
||||||
"SUNSHINEDURATION": "#",
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
"AIR_PRESSURE": PRESSURE_HPA,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
"FREQUENCY": FREQUENCY_HERTZ,
|
),
|
||||||
"VALUE": "#",
|
"CURRENT_ILLUMINATION": SensorEntityDescription(
|
||||||
"VALVE_STATE": PERCENTAGE,
|
key="CURRENT_ILLUMINATION",
|
||||||
"CARRIER_SENSE_LEVEL": PERCENTAGE,
|
native_unit_of_measurement=LIGHT_LUX,
|
||||||
"DUTY_CYCLE_LEVEL": PERCENTAGE,
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
"CONCENTRATION": CONCENTRATION_PARTS_PER_MILLION,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"ILLUMINATION": SensorEntityDescription(
|
||||||
|
key="ILLUMINATION",
|
||||||
|
native_unit_of_measurement=LIGHT_LUX,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"AVERAGE_ILLUMINATION": SensorEntityDescription(
|
||||||
|
key="AVERAGE_ILLUMINATION",
|
||||||
|
native_unit_of_measurement=LIGHT_LUX,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"LOWEST_ILLUMINATION": SensorEntityDescription(
|
||||||
|
key="LOWEST_ILLUMINATION",
|
||||||
|
native_unit_of_measurement=LIGHT_LUX,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"HIGHEST_ILLUMINATION": SensorEntityDescription(
|
||||||
|
key="HIGHEST_ILLUMINATION",
|
||||||
|
native_unit_of_measurement=LIGHT_LUX,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"POWER": SensorEntityDescription(
|
||||||
|
key="POWER",
|
||||||
|
native_unit_of_measurement=POWER_WATT,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"IEC_POWER": SensorEntityDescription(
|
||||||
|
key="IEC_POWER",
|
||||||
|
native_unit_of_measurement=POWER_WATT,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"CURRENT": SensorEntityDescription(
|
||||||
|
key="CURRENT",
|
||||||
|
native_unit_of_measurement=ELECTRIC_CURRENT_MILLIAMPERE,
|
||||||
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"CONCENTRATION": SensorEntityDescription(
|
||||||
|
key="CONCENTRATION",
|
||||||
|
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||||
|
device_class=DEVICE_CLASS_CO2,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"ENERGY_COUNTER": SensorEntityDescription(
|
||||||
|
key="ENERGY_COUNTER",
|
||||||
|
native_unit_of_measurement=ENERGY_WATT_HOUR,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
"IEC_ENERGY_COUNTER": SensorEntityDescription(
|
||||||
|
key="IEC_ENERGY_COUNTER",
|
||||||
|
native_unit_of_measurement=ENERGY_WATT_HOUR,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
"VOLTAGE": SensorEntityDescription(
|
||||||
|
key="VOLTAGE",
|
||||||
|
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"GAS_POWER": SensorEntityDescription(
|
||||||
|
key="GAS_POWER",
|
||||||
|
native_unit_of_measurement=VOLUME_CUBIC_METERS,
|
||||||
|
device_class=DEVICE_CLASS_GAS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"GAS_ENERGY_COUNTER": SensorEntityDescription(
|
||||||
|
key="GAS_ENERGY_COUNTER",
|
||||||
|
native_unit_of_measurement=VOLUME_CUBIC_METERS,
|
||||||
|
device_class=DEVICE_CLASS_GAS,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
"RAIN_COUNTER": SensorEntityDescription(
|
||||||
|
key="RAIN_COUNTER",
|
||||||
|
native_unit_of_measurement=LENGTH_MILLIMETERS,
|
||||||
|
),
|
||||||
|
"WIND_SPEED": SensorEntityDescription(
|
||||||
|
key="WIND_SPEED",
|
||||||
|
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||||
|
icon="mdi:weather-windy",
|
||||||
|
),
|
||||||
|
"WIND_DIRECTION": SensorEntityDescription(
|
||||||
|
key="WIND_DIRECTION",
|
||||||
|
native_unit_of_measurement=DEGREE,
|
||||||
|
),
|
||||||
|
"WIND_DIRECTION_RANGE": SensorEntityDescription(
|
||||||
|
key="WIND_DIRECTION_RANGE",
|
||||||
|
native_unit_of_measurement=DEGREE,
|
||||||
|
),
|
||||||
|
"SUNSHINEDURATION": SensorEntityDescription(
|
||||||
|
key="SUNSHINEDURATION",
|
||||||
|
native_unit_of_measurement="#",
|
||||||
|
),
|
||||||
|
"AIR_PRESSURE": SensorEntityDescription(
|
||||||
|
key="AIR_PRESSURE",
|
||||||
|
native_unit_of_measurement=PRESSURE_HPA,
|
||||||
|
device_class=DEVICE_CLASS_PRESSURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
"FREQUENCY": SensorEntityDescription(
|
||||||
|
key="FREQUENCY",
|
||||||
|
native_unit_of_measurement=FREQUENCY_HERTZ,
|
||||||
|
),
|
||||||
|
"VALUE": SensorEntityDescription(
|
||||||
|
key="VALUE",
|
||||||
|
native_unit_of_measurement="#",
|
||||||
|
),
|
||||||
|
"VALVE_STATE": SensorEntityDescription(
|
||||||
|
key="VALVE_STATE",
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
),
|
||||||
|
"CARRIER_SENSE_LEVEL": SensorEntityDescription(
|
||||||
|
key="CARRIER_SENSE_LEVEL",
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
),
|
||||||
|
"DUTY_CYCLE_LEVEL": SensorEntityDescription(
|
||||||
|
key="DUTY_CYCLE_LEVEL",
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
),
|
||||||
|
"BRIGHTNESS": SensorEntityDescription(
|
||||||
|
key="BRIGHTNESS",
|
||||||
|
native_unit_of_measurement="#",
|
||||||
|
icon="mdi:invert-colors",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
HM_DEVICE_CLASS_HA_CAST = {
|
DEFAULT_SENSOR_DESCRIPTION = SensorEntityDescription(
|
||||||
"HUMIDITY": DEVICE_CLASS_HUMIDITY,
|
key="",
|
||||||
"TEMPERATURE": DEVICE_CLASS_TEMPERATURE,
|
entity_registry_enabled_default=True,
|
||||||
"ACTUAL_TEMPERATURE": DEVICE_CLASS_TEMPERATURE,
|
)
|
||||||
"LUX": DEVICE_CLASS_ILLUMINANCE,
|
|
||||||
"CURRENT_ILLUMINATION": DEVICE_CLASS_ILLUMINANCE,
|
|
||||||
"AVERAGE_ILLUMINATION": DEVICE_CLASS_ILLUMINANCE,
|
|
||||||
"LOWEST_ILLUMINATION": DEVICE_CLASS_ILLUMINANCE,
|
|
||||||
"HIGHEST_ILLUMINATION": DEVICE_CLASS_ILLUMINANCE,
|
|
||||||
"POWER": DEVICE_CLASS_POWER,
|
|
||||||
"CURRENT": DEVICE_CLASS_POWER,
|
|
||||||
"CONCENTRATION": DEVICE_CLASS_CO2,
|
|
||||||
}
|
|
||||||
|
|
||||||
HM_ICON_HA_CAST = {"WIND_SPEED": "mdi:weather-windy", "BRIGHTNESS": "mdi:invert-colors"}
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
@ -102,7 +236,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
|
|
||||||
devices = []
|
devices = []
|
||||||
for conf in discovery_info[ATTR_DISCOVER_DEVICES]:
|
for conf in discovery_info[ATTR_DISCOVER_DEVICES]:
|
||||||
new_device = HMSensor(conf)
|
state = conf.get(ATTR_PARAM)
|
||||||
|
entity_desc = SENSOR_DESCRIPTIONS.get(state)
|
||||||
|
if entity_desc is None:
|
||||||
|
name = conf.get(ATTR_NAME)
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Sensor (%s) entity description is missing. Sensor state (%s) needs to be maintained",
|
||||||
|
name,
|
||||||
|
state,
|
||||||
|
)
|
||||||
|
entity_desc = copy(DEFAULT_SENSOR_DESCRIPTION)
|
||||||
|
|
||||||
|
new_device = HMSensor(conf, entity_desc)
|
||||||
devices.append(new_device)
|
devices.append(new_device)
|
||||||
|
|
||||||
add_entities(devices, True)
|
add_entities(devices, True)
|
||||||
@ -122,21 +267,6 @@ class HMSensor(HMDevice, SensorEntity):
|
|||||||
# No cast, return original value
|
# No cast, return original value
|
||||||
return self._hm_get_state()
|
return self._hm_get_state()
|
||||||
|
|
||||||
@property
|
|
||||||
def native_unit_of_measurement(self):
|
|
||||||
"""Return the unit of measurement of this entity, if any."""
|
|
||||||
return HM_UNIT_HA_CAST.get(self._state)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the device class to use in the frontend, if any."""
|
|
||||||
return HM_DEVICE_CLASS_HA_CAST.get(self._state)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def icon(self):
|
|
||||||
"""Return the icon to use in the frontend, if any."""
|
|
||||||
return HM_ICON_HA_CAST.get(self._state)
|
|
||||||
|
|
||||||
def _init_data_struct(self):
|
def _init_data_struct(self):
|
||||||
"""Generate a data dictionary (self._data) from metadata."""
|
"""Generate a data dictionary (self._data) from metadata."""
|
||||||
if self._state:
|
if self._state:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user