From 1f2134a31acf81bb917d0101612a192d996d006e Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Fri, 20 Aug 2021 16:25:39 -0400 Subject: [PATCH] Use entity descriptions for zwave_js sensors (#53744) * Use entity descriptions for zwave_js sensors * reorder * use new type * revert typing changes * switch to using maps * Get device and state class from discovery instead * ues constants for keys * Add meter type attribute and simplify platform data access * comments * second refactor * Add None lookup value * readability * Switch base data template to type Any for more flexibility * Additional changes based on feedback * rewrite based on new upstream util functions * Use new combo type * Handle UnknownValueData in discovery * bug fixes * remove redundant comment * re-add force_update * fixes and tweaks * pylint and feedback --- homeassistant/components/zwave_js/const.py | 19 ++ .../components/zwave_js/discovery.py | 25 +- .../zwave_js/discovery_data_template.py | 106 +++++++- homeassistant/components/zwave_js/sensor.py | 254 ++++++++++++------ 4 files changed, 310 insertions(+), 94 deletions(-) diff --git a/homeassistant/components/zwave_js/const.py b/homeassistant/components/zwave_js/const.py index 4a311012690..8e545975faa 100644 --- a/homeassistant/components/zwave_js/const.py +++ b/homeassistant/components/zwave_js/const.py @@ -75,5 +75,24 @@ ATTR_REFRESH_ALL_VALUES = "refresh_all_values" ATTR_BROADCAST = "broadcast" # meter reset ATTR_METER_TYPE = "meter_type" +ATTR_METER_TYPE_NAME = "meter_type_name" ADDON_SLUG = "core_zwave_js" + +# Sensor entity description constants +ENTITY_DESC_KEY_BATTERY = "battery" +ENTITY_DESC_KEY_CURRENT = "current" +ENTITY_DESC_KEY_VOLTAGE = "voltage" +ENTITY_DESC_KEY_ENERGY_MEASUREMENT = "energy_measurement" +ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING = "energy_total_increasing" +ENTITY_DESC_KEY_POWER = "power" +ENTITY_DESC_KEY_POWER_FACTOR = "power_factor" +ENTITY_DESC_KEY_CO = "co" +ENTITY_DESC_KEY_CO2 = "co2" +ENTITY_DESC_KEY_HUMIDITY = "humidity" +ENTITY_DESC_KEY_ILLUMINANCE = "illuminance" +ENTITY_DESC_KEY_PRESSURE = "pressure" +ENTITY_DESC_KEY_SIGNAL_STRENGTH = "signal_strength" +ENTITY_DESC_KEY_TEMPERATURE = "temperature" +ENTITY_DESC_KEY_TARGET_TEMPERATURE = "target_temperature" +ENTITY_DESC_KEY_TIMESTAMP = "timestamp" diff --git a/homeassistant/components/zwave_js/discovery.py b/homeassistant/components/zwave_js/discovery.py index d59a3d935a0..7a4955d693a 100644 --- a/homeassistant/components/zwave_js/discovery.py +++ b/homeassistant/components/zwave_js/discovery.py @@ -7,15 +7,18 @@ from typing import Any from awesomeversion import AwesomeVersion from zwave_js_server.const import THERMOSTAT_CURRENT_TEMP_PROPERTY, CommandClass +from zwave_js_server.exceptions import UnknownValueData from zwave_js_server.model.device_class import DeviceClassItem from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.core import callback +from .const import LOGGER from .discovery_data_template import ( BaseDiscoverySchemaDataTemplate, DynamicCurrentTempClimateDataTemplate, + NumericSensorDataTemplate, ZwaveValueID, ) @@ -59,14 +62,14 @@ class ZwaveDiscoveryInfo: assumed_state: bool # the home assistant platform for which an entity should be created platform: str + # helper data to use in platform setup + platform_data: Any + # additional values that need to be watched by entity + additional_value_ids_to_watch: set[str] # hint for the platform about this discovered entity platform_hint: str | None = "" # data template to use in platform logic platform_data_template: BaseDiscoverySchemaDataTemplate | None = None - # helper data to use in platform setup - platform_data: dict[str, Any] | None = None - # additional values that need to be watched by entity - additional_value_ids_to_watch: set[str] | None = None # bool to specify whether entity should be enabled by default entity_registry_enabled_default: bool = True @@ -487,6 +490,7 @@ DISCOVERY_SCHEMAS = [ }, type={"number"}, ), + data_template=NumericSensorDataTemplate(), ), ZWaveDiscoverySchema( platform="sensor", @@ -495,6 +499,7 @@ DISCOVERY_SCHEMAS = [ command_class={CommandClass.INDICATOR}, type={"number"}, ), + data_template=NumericSensorDataTemplate(), entity_registry_enabled_default=False, ), # Meter sensors for Meter CC @@ -508,6 +513,7 @@ DISCOVERY_SCHEMAS = [ type={"number"}, property={"value"}, ), + data_template=NumericSensorDataTemplate(), ), # special list sensors (Notification CC) ZWaveDiscoverySchema( @@ -542,6 +548,7 @@ DISCOVERY_SCHEMAS = [ property={"targetValue"}, ) ], + data_template=NumericSensorDataTemplate(), entity_registry_enabled_default=False, ), # binary switches @@ -745,9 +752,15 @@ def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None # resolve helper data from template resolved_data = None - additional_value_ids_to_watch = None + additional_value_ids_to_watch = set() if schema.data_template: - resolved_data = schema.data_template.resolve_data(value) + try: + resolved_data = schema.data_template.resolve_data(value) + except UnknownValueData as err: + LOGGER.error( + "Discovery for value %s will be skipped: %s", value, err + ) + continue additional_value_ids_to_watch = schema.data_template.value_ids_to_watch( resolved_data ) diff --git a/homeassistant/components/zwave_js/discovery_data_template.py b/homeassistant/components/zwave_js/discovery_data_template.py index 7962b6b1c05..3ef74a7e17d 100644 --- a/homeassistant/components/zwave_js/discovery_data_template.py +++ b/homeassistant/components/zwave_js/discovery_data_template.py @@ -1,12 +1,80 @@ -"""Data template classes for discovery used to generate device specific data for setup.""" +"""Data template classes for discovery used to generate additional data for setup.""" from __future__ import annotations from collections.abc import Iterable from dataclasses import dataclass from typing import Any +from zwave_js_server.const import ( + CO2_SENSORS, + CO_SENSORS, + CURRENT_METER_TYPES, + CURRENT_SENSORS, + ENERGY_METER_TYPES, + ENERGY_SENSORS, + HUMIDITY_SENSORS, + ILLUMINANCE_SENSORS, + POWER_FACTOR_METER_TYPES, + POWER_METER_TYPES, + POWER_SENSORS, + PRESSURE_SENSORS, + SIGNAL_STRENGTH_SENSORS, + TEMPERATURE_SENSORS, + TIMESTAMP_SENSORS, + VOLTAGE_METER_TYPES, + VOLTAGE_SENSORS, + CommandClass, + MeterScaleType, + MultilevelSensorType, +) from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.value import Value as ZwaveValue, get_value_id +from zwave_js_server.util.command_class import ( + get_meter_scale_type, + get_multilevel_sensor_type, +) + +from .const import ( + ENTITY_DESC_KEY_BATTERY, + ENTITY_DESC_KEY_CO, + ENTITY_DESC_KEY_CO2, + ENTITY_DESC_KEY_CURRENT, + ENTITY_DESC_KEY_ENERGY_MEASUREMENT, + ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING, + ENTITY_DESC_KEY_HUMIDITY, + ENTITY_DESC_KEY_ILLUMINANCE, + ENTITY_DESC_KEY_POWER, + ENTITY_DESC_KEY_POWER_FACTOR, + ENTITY_DESC_KEY_PRESSURE, + ENTITY_DESC_KEY_SIGNAL_STRENGTH, + ENTITY_DESC_KEY_TARGET_TEMPERATURE, + ENTITY_DESC_KEY_TEMPERATURE, + ENTITY_DESC_KEY_TIMESTAMP, + ENTITY_DESC_KEY_VOLTAGE, +) + +METER_DEVICE_CLASS_MAP: dict[str, set[MeterScaleType]] = { + ENTITY_DESC_KEY_CURRENT: CURRENT_METER_TYPES, + ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_METER_TYPES, + ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING: ENERGY_METER_TYPES, + ENTITY_DESC_KEY_POWER: POWER_METER_TYPES, + ENTITY_DESC_KEY_POWER_FACTOR: POWER_FACTOR_METER_TYPES, +} + +MULTILEVEL_SENSOR_DEVICE_CLASS_MAP: dict[str, set[MultilevelSensorType]] = { + ENTITY_DESC_KEY_CO: CO_SENSORS, + ENTITY_DESC_KEY_CO2: CO2_SENSORS, + ENTITY_DESC_KEY_CURRENT: CURRENT_SENSORS, + ENTITY_DESC_KEY_ENERGY_MEASUREMENT: ENERGY_SENSORS, + ENTITY_DESC_KEY_HUMIDITY: HUMIDITY_SENSORS, + ENTITY_DESC_KEY_ILLUMINANCE: ILLUMINANCE_SENSORS, + ENTITY_DESC_KEY_POWER: POWER_SENSORS, + ENTITY_DESC_KEY_PRESSURE: PRESSURE_SENSORS, + ENTITY_DESC_KEY_SIGNAL_STRENGTH: SIGNAL_STRENGTH_SENSORS, + ENTITY_DESC_KEY_TEMPERATURE: TEMPERATURE_SENSORS, + ENTITY_DESC_KEY_TIMESTAMP: TIMESTAMP_SENSORS, + ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_SENSORS, +} @dataclass @@ -19,11 +87,10 @@ class ZwaveValueID: property_key: str | int | None = None -@dataclass class BaseDiscoverySchemaDataTemplate: """Base class for discovery schema data templates.""" - def resolve_data(self, value: ZwaveValue) -> dict[str, Any]: + def resolve_data(self, value: ZwaveValue) -> Any: """ Resolve helper class data for a discovered value. @@ -33,7 +100,7 @@ class BaseDiscoverySchemaDataTemplate: # pylint: disable=no-self-use return {} - def values_to_watch(self, resolved_data: dict[str, Any]) -> Iterable[ZwaveValue]: + def values_to_watch(self, resolved_data: Any) -> Iterable[ZwaveValue]: """ Return list of all ZwaveValues resolved by helper that should be watched. @@ -42,7 +109,7 @@ class BaseDiscoverySchemaDataTemplate: # pylint: disable=no-self-use return [] - def value_ids_to_watch(self, resolved_data: dict[str, Any]) -> set[str]: + def value_ids_to_watch(self, resolved_data: Any) -> set[str]: """ Return list of all Value IDs resolved by helper that should be watched. @@ -107,3 +174,32 @@ class DynamicCurrentTempClimateDataTemplate(BaseDiscoverySchemaDataTemplate): return lookup_table.get(lookup_key) return None + + +class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate): + """Data template class for Z-Wave Sensor entities.""" + + def resolve_data(self, value: ZwaveValue) -> str | None: + """Resolve helper class data for a discovered value.""" + + if value.command_class == CommandClass.BATTERY: + return ENTITY_DESC_KEY_BATTERY + + if value.command_class == CommandClass.METER: + scale_type = get_meter_scale_type(value) + for key, scale_type_set in METER_DEVICE_CLASS_MAP.items(): + if scale_type in scale_type_set: + return key + + if value.command_class == CommandClass.SENSOR_MULTILEVEL: + sensor_type = get_multilevel_sensor_type(value) + if sensor_type == MultilevelSensorType.TARGET_TEMPERATURE: + return ENTITY_DESC_KEY_TARGET_TEMPERATURE + for ( + key, + sensor_type_set, + ) in MULTILEVEL_SENSOR_DEVICE_CLASS_MAP.items(): + if sensor_type in sensor_type_set: + return key + + return None diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index 1ffa263dae7..5c8ed8633f1 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -1,30 +1,45 @@ """Representation of Z-Wave sensors.""" from __future__ import annotations +from collections.abc import Mapping +from dataclasses import dataclass import logging from typing import cast import voluptuous as vol from zwave_js_server.client import Client as ZwaveClient -from zwave_js_server.const import CommandClass, ConfigurationValueType +from zwave_js_server.const import ( + RESET_METER_OPTION_TARGET_VALUE, + RESET_METER_OPTION_TYPE, + CommandClass, + ConfigurationValueType, +) from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.value import ConfigurationValue +from zwave_js_server.util.command_class import get_meter_type from homeassistant.components.sensor import ( - DEVICE_CLASS_BATTERY, DEVICE_CLASS_ENERGY, - DEVICE_CLASS_ILLUMINANCE, - DEVICE_CLASS_POWER, DOMAIN as SENSOR_DOMAIN, STATE_CLASS_MEASUREMENT, STATE_CLASS_TOTAL_INCREASING, SensorEntity, + SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_CO, + DEVICE_CLASS_CO2, DEVICE_CLASS_CURRENT, DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_POWER, + DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLTAGE, TEMP_CELSIUS, TEMP_FAHRENHEIT, @@ -34,7 +49,30 @@ from homeassistant.helpers import entity_platform from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_METER_TYPE, ATTR_VALUE, DATA_CLIENT, DOMAIN, SERVICE_RESET_METER +from .const import ( + ATTR_METER_TYPE, + ATTR_METER_TYPE_NAME, + ATTR_VALUE, + DATA_CLIENT, + DOMAIN, + ENTITY_DESC_KEY_BATTERY, + ENTITY_DESC_KEY_CO, + ENTITY_DESC_KEY_CO2, + ENTITY_DESC_KEY_CURRENT, + ENTITY_DESC_KEY_ENERGY_MEASUREMENT, + ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING, + ENTITY_DESC_KEY_HUMIDITY, + ENTITY_DESC_KEY_ILLUMINANCE, + ENTITY_DESC_KEY_POWER, + ENTITY_DESC_KEY_POWER_FACTOR, + ENTITY_DESC_KEY_PRESSURE, + ENTITY_DESC_KEY_SIGNAL_STRENGTH, + ENTITY_DESC_KEY_TARGET_TEMPERATURE, + ENTITY_DESC_KEY_TEMPERATURE, + ENTITY_DESC_KEY_TIMESTAMP, + ENTITY_DESC_KEY_VOLTAGE, + SERVICE_RESET_METER, +) from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity from .helpers import get_device_id @@ -42,6 +80,97 @@ from .helpers import get_device_id LOGGER = logging.getLogger(__name__) +@dataclass +class ZwaveSensorEntityDescription(SensorEntityDescription): + """Base description of a Zwave Sensor entity.""" + + info: ZwaveDiscoveryInfo | None = None + + +ENTITY_DESCRIPTION_KEY_MAP: dict[str, ZwaveSensorEntityDescription] = { + ENTITY_DESC_KEY_BATTERY: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_BATTERY, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_CURRENT: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_CURRENT, + device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_VOLTAGE: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_VOLTAGE, + device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_ENERGY_MEASUREMENT: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_TOTAL_INCREASING, + ), + ENTITY_DESC_KEY_POWER: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_POWER, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_POWER_FACTOR: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_POWER_FACTOR, + device_class=DEVICE_CLASS_POWER_FACTOR, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_CO: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_CO, + device_class=DEVICE_CLASS_CO, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_CO2: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_CO2, + device_class=DEVICE_CLASS_CO2, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_HUMIDITY: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_HUMIDITY, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_ILLUMINANCE: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_ILLUMINANCE, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_PRESSURE: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_PRESSURE, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_SIGNAL_STRENGTH: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_SIGNAL_STRENGTH, + device_class=DEVICE_CLASS_SIGNAL_STRENGTH, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_TEMPERATURE: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_TEMPERATURE, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_TIMESTAMP: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_TIMESTAMP, + device_class=DEVICE_CLASS_TIMESTAMP, + state_class=STATE_CLASS_MEASUREMENT, + ), + ENTITY_DESC_KEY_TARGET_TEMPERATURE: ZwaveSensorEntityDescription( + ENTITY_DESC_KEY_TARGET_TEMPERATURE, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=None, + ), +} + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -55,16 +184,25 @@ async def async_setup_entry( """Add Z-Wave Sensor.""" entities: list[ZWaveBaseEntity] = [] + entity_description = ENTITY_DESCRIPTION_KEY_MAP.get( + info.platform_data + ) or ZwaveSensorEntityDescription("base_sensor") + entity_description.info = info + if info.platform_hint == "string_sensor": - entities.append(ZWaveStringSensor(config_entry, client, info)) + entities.append(ZWaveStringSensor(config_entry, client, entity_description)) elif info.platform_hint == "numeric_sensor": - entities.append(ZWaveNumericSensor(config_entry, client, info)) + entities.append( + ZWaveNumericSensor(config_entry, client, entity_description) + ) elif info.platform_hint == "list_sensor": - entities.append(ZWaveListSensor(config_entry, client, info)) + entities.append(ZWaveListSensor(config_entry, client, entity_description)) elif info.platform_hint == "config_parameter": - entities.append(ZWaveConfigParameterSensor(config_entry, client, info)) + entities.append( + ZWaveConfigParameterSensor(config_entry, client, entity_description) + ) elif info.platform_hint == "meter": - entities.append(ZWaveMeterSensor(config_entry, client, info)) + entities.append(ZWaveMeterSensor(config_entry, client, entity_description)) else: LOGGER.warning( "Sensor not implemented for %s/%s", @@ -114,62 +252,16 @@ class ZwaveSensorBase(ZWaveBaseEntity, SensorEntity): self, config_entry: ConfigEntry, client: ZwaveClient, - info: ZwaveDiscoveryInfo, + entity_description: ZwaveSensorEntityDescription, ) -> None: """Initialize a ZWaveSensorBase entity.""" - super().__init__(config_entry, client, info) + assert entity_description.info + super().__init__(config_entry, client, entity_description.info) + self.entity_description = entity_description # Entity class attributes + self._attr_force_update = True self._attr_name = self.generate_name(include_value_name=True) - self._attr_device_class = self._get_device_class() - self._attr_state_class = self._get_state_class() - - def _get_device_class(self) -> str | None: - """ - Get the device class of the sensor. - - This should be run once during initialization so we don't have to calculate - this value on every state update. - """ - if self.info.primary_value.command_class == CommandClass.BATTERY: - return DEVICE_CLASS_BATTERY - if isinstance(self.info.primary_value.property_, str): - property_lower = self.info.primary_value.property_.lower() - if "humidity" in property_lower: - return DEVICE_CLASS_HUMIDITY - if "temperature" in property_lower: - return DEVICE_CLASS_TEMPERATURE - if self.info.primary_value.metadata.unit == "A": - return DEVICE_CLASS_CURRENT - if self.info.primary_value.metadata.unit == "W": - return DEVICE_CLASS_POWER - if self.info.primary_value.metadata.unit == "kWh": - return DEVICE_CLASS_ENERGY - if self.info.primary_value.metadata.unit == "V": - return DEVICE_CLASS_VOLTAGE - if self.info.primary_value.metadata.unit == "Lux": - return DEVICE_CLASS_ILLUMINANCE - return None - - def _get_state_class(self) -> str | None: - """ - Get the state class of the sensor. - - This should be run once during initialization so we don't have to calculate - this value on every state update. - """ - if self.info.primary_value.command_class == CommandClass.BATTERY: - return STATE_CLASS_MEASUREMENT - if isinstance(self.info.primary_value.property_, str): - property_lower = self.info.primary_value.property_.lower() - if "humidity" in property_lower or "temperature" in property_lower: - return STATE_CLASS_MEASUREMENT - return None - - @property - def force_update(self) -> bool: - """Force updates.""" - return True class ZWaveStringSensor(ZwaveSensorBase): @@ -216,20 +308,16 @@ class ZWaveNumericSensor(ZwaveSensorBase): class ZWaveMeterSensor(ZWaveNumericSensor): """Representation of a Z-Wave Meter CC sensor.""" - def __init__( - self, - config_entry: ConfigEntry, - client: ZwaveClient, - info: ZwaveDiscoveryInfo, - ) -> None: - """Initialize a ZWaveNumericSensor entity.""" - super().__init__(config_entry, client, info) - - # Entity class attributes - if self.device_class == DEVICE_CLASS_ENERGY: - self._attr_state_class = STATE_CLASS_TOTAL_INCREASING - else: - self._attr_state_class = STATE_CLASS_MEASUREMENT + @property + def extra_state_attributes(self) -> Mapping[str, int | str] | None: + """Return extra state attributes.""" + meter_type = get_meter_type(self.info.primary_value) + if meter_type: + return { + ATTR_METER_TYPE: meter_type.value, + ATTR_METER_TYPE_NAME: meter_type.name, + } + return None async def async_reset_meter( self, meter_type: int | None = None, value: int | None = None @@ -239,9 +327,9 @@ class ZWaveMeterSensor(ZWaveNumericSensor): primary_value = self.info.primary_value options = {} if meter_type is not None: - options["type"] = meter_type + options[RESET_METER_OPTION_TYPE] = meter_type if value is not None: - options["targetValue"] = value + options[RESET_METER_OPTION_TARGET_VALUE] = value args = [options] if options else [] await node.endpoints[primary_value.endpoint].async_invoke_cc_api( CommandClass.METER, "reset", *args, wait_for_result=False @@ -261,10 +349,10 @@ class ZWaveListSensor(ZwaveSensorBase): self, config_entry: ConfigEntry, client: ZwaveClient, - info: ZwaveDiscoveryInfo, + entity_description: ZwaveSensorEntityDescription, ) -> None: """Initialize a ZWaveListSensor entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, client, entity_description) # Entity class attributes self._attr_name = self.generate_name( @@ -291,7 +379,7 @@ class ZWaveListSensor(ZwaveSensorBase): def extra_state_attributes(self) -> dict[str, str] | None: """Return the device specific state attributes.""" # add the value's int value as property for multi-value (list) items - return {"value": self.info.primary_value.value} + return {ATTR_VALUE: self.info.primary_value.value} class ZWaveConfigParameterSensor(ZwaveSensorBase): @@ -301,10 +389,10 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase): self, config_entry: ConfigEntry, client: ZwaveClient, - info: ZwaveDiscoveryInfo, + entity_description: ZwaveSensorEntityDescription, ) -> None: """Initialize a ZWaveConfigParameterSensor entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, client, entity_description) self._primary_value = cast(ConfigurationValue, self.info.primary_value) # Entity class attributes @@ -338,7 +426,7 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase): if self._primary_value.configuration_value_type == ConfigurationValueType.RANGE: return None # add the value's int value as property for multi-value (list) items - return {"value": self.info.primary_value.value} + return {ATTR_VALUE: self.info.primary_value.value} class ZWaveNodeStatusSensor(SensorEntity):