Add explicit unit mapping for zwave_js meters and sensors (#59659)

* Add explicit unit mapping for zwave_js meters and sensors

* review comment

* fix

* alternate approach
This commit is contained in:
Raman Gupta 2021-11-17 12:26:32 -05:00 committed by GitHub
parent 5ae311b111
commit edd068d6eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 261 additions and 66 deletions

View file

@ -11,6 +11,13 @@ from zwave_js_server.const.command_class.meter import (
ENERGY_TOTAL_INCREASING_METER_TYPES, ENERGY_TOTAL_INCREASING_METER_TYPES,
POWER_FACTOR_METER_TYPES, POWER_FACTOR_METER_TYPES,
POWER_METER_TYPES, POWER_METER_TYPES,
UNIT_AMPERE as METER_UNIT_AMPERE,
UNIT_CUBIC_FEET,
UNIT_CUBIC_METER as METER_UNIT_CUBIC_METER,
UNIT_KILOWATT_HOUR,
UNIT_US_GALLON,
UNIT_VOLT as METER_UNIT_VOLT,
UNIT_WATT as METER_UNIT_WATT,
VOLTAGE_METER_TYPES, VOLTAGE_METER_TYPES,
ElectricScale, ElectricScale,
MeterScaleType, MeterScaleType,
@ -26,16 +33,97 @@ from zwave_js_server.const.command_class.multilevel_sensor import (
PRESSURE_SENSORS, PRESSURE_SENSORS,
SIGNAL_STRENGTH_SENSORS, SIGNAL_STRENGTH_SENSORS,
TEMPERATURE_SENSORS, TEMPERATURE_SENSORS,
UNIT_AMPERE as SENSOR_UNIT_AMPERE,
UNIT_BTU_H,
UNIT_CELSIUS,
UNIT_CENTIMETER,
UNIT_CUBIC_FEET_PER_MINUTE,
UNIT_CUBIC_METER as SENSOR_UNIT_CUBIC_METER,
UNIT_CUBIC_METER_PER_HOUR,
UNIT_DECIBEL,
UNIT_DEGREES,
UNIT_DENSITY,
UNIT_FAHRENHEIT,
UNIT_FEET,
UNIT_GALLONS,
UNIT_HERTZ,
UNIT_INCHES_OF_MERCURY,
UNIT_INCHES_PER_HOUR,
UNIT_KILOGRAM,
UNIT_KILOHERTZ,
UNIT_LITER,
UNIT_LUX,
UNIT_M_S,
UNIT_METER,
UNIT_MICROGRAM_PER_CUBIC_METER,
UNIT_MILLIAMPERE,
UNIT_MILLIMETER_HOUR,
UNIT_MILLIVOLT,
UNIT_MPH,
UNIT_PARTS_MILLION,
UNIT_PERCENTAGE_VALUE,
UNIT_POUND_PER_SQUARE_INCH,
UNIT_POUNDS,
UNIT_POWER_LEVEL,
UNIT_RSSI,
UNIT_SECOND,
UNIT_SYSTOLIC,
UNIT_VOLT as SENSOR_UNIT_VOLT,
UNIT_WATT as SENSOR_UNIT_WATT,
UNIT_WATT_PER_SQUARE_METER,
VOLTAGE_SENSORS, VOLTAGE_SENSORS,
MultilevelSensorScaleType,
MultilevelSensorType, MultilevelSensorType,
) )
from zwave_js_server.model.node import Node as ZwaveNode 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.model.value import Value as ZwaveValue, get_value_id
from zwave_js_server.util.command_class.meter import get_meter_scale_type from zwave_js_server.util.command_class.meter import get_meter_scale_type
from zwave_js_server.util.command_class.multilevel_sensor import ( from zwave_js_server.util.command_class.multilevel_sensor import (
get_multilevel_sensor_scale_type,
get_multilevel_sensor_type, get_multilevel_sensor_type,
) )
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
ELECTRIC_CURRENT_AMPERE,
ELECTRIC_CURRENT_MILLIAMPERE,
ELECTRIC_POTENTIAL_MILLIVOLT,
ELECTRIC_POTENTIAL_VOLT,
ENERGY_KILO_WATT_HOUR,
FREQUENCY_HERTZ,
FREQUENCY_KILOHERTZ,
IRRADIATION_WATTS_PER_SQUARE_METER,
LENGTH_CENTIMETERS,
LENGTH_FEET,
LENGTH_METERS,
LIGHT_LUX,
MASS_KILOGRAMS,
MASS_POUNDS,
PERCENTAGE,
POWER_BTU_PER_HOUR,
POWER_WATT,
PRECIPITATION_INCHES_PER_HOUR,
PRECIPITATION_MILLIMETERS_PER_HOUR,
PRESSURE_INHG,
PRESSURE_MMHG,
PRESSURE_PSI,
SIGNAL_STRENGTH_DECIBELS,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
SPEED_METERS_PER_SECOND,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
TIME_SECONDS,
VOLUME_CUBIC_FEET,
VOLUME_CUBIC_METERS,
VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE,
VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR,
VOLUME_GALLONS,
VOLUME_LITERS,
)
from .const import ( from .const import (
ENTITY_DESC_KEY_BATTERY, ENTITY_DESC_KEY_BATTERY,
ENTITY_DESC_KEY_CO, ENTITY_DESC_KEY_CO,
@ -78,6 +166,58 @@ MULTILEVEL_SENSOR_DEVICE_CLASS_MAP: dict[str, set[MultilevelSensorType]] = {
ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_SENSORS, ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_SENSORS,
} }
METER_UNIT_MAP: dict[str, set[MeterScaleType]] = {
ELECTRIC_CURRENT_AMPERE: METER_UNIT_AMPERE,
VOLUME_CUBIC_FEET: UNIT_CUBIC_FEET,
VOLUME_CUBIC_METERS: METER_UNIT_CUBIC_METER,
VOLUME_GALLONS: UNIT_US_GALLON,
ENERGY_KILO_WATT_HOUR: UNIT_KILOWATT_HOUR,
ELECTRIC_POTENTIAL_VOLT: METER_UNIT_VOLT,
POWER_WATT: METER_UNIT_WATT,
}
MULTILEVEL_SENSOR_UNIT_MAP: dict[str, set[MultilevelSensorScaleType]] = {
ELECTRIC_CURRENT_AMPERE: SENSOR_UNIT_AMPERE,
POWER_BTU_PER_HOUR: UNIT_BTU_H,
TEMP_CELSIUS: UNIT_CELSIUS,
LENGTH_CENTIMETERS: UNIT_CENTIMETER,
VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: UNIT_CUBIC_FEET_PER_MINUTE,
VOLUME_CUBIC_METERS: SENSOR_UNIT_CUBIC_METER,
VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: UNIT_CUBIC_METER_PER_HOUR,
SIGNAL_STRENGTH_DECIBELS: UNIT_DECIBEL,
DEGREE: UNIT_DEGREES,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: {
*UNIT_DENSITY,
*UNIT_MICROGRAM_PER_CUBIC_METER,
},
TEMP_FAHRENHEIT: UNIT_FAHRENHEIT,
LENGTH_FEET: UNIT_FEET,
VOLUME_GALLONS: UNIT_GALLONS,
FREQUENCY_HERTZ: UNIT_HERTZ,
PRESSURE_INHG: UNIT_INCHES_OF_MERCURY,
PRECIPITATION_INCHES_PER_HOUR: UNIT_INCHES_PER_HOUR,
MASS_KILOGRAMS: UNIT_KILOGRAM,
FREQUENCY_KILOHERTZ: UNIT_KILOHERTZ,
VOLUME_LITERS: UNIT_LITER,
LIGHT_LUX: UNIT_LUX,
LENGTH_METERS: UNIT_METER,
ELECTRIC_CURRENT_MILLIAMPERE: UNIT_MILLIAMPERE,
PRECIPITATION_MILLIMETERS_PER_HOUR: UNIT_MILLIMETER_HOUR,
ELECTRIC_POTENTIAL_MILLIVOLT: UNIT_MILLIVOLT,
SPEED_MILES_PER_HOUR: UNIT_MPH,
SPEED_METERS_PER_SECOND: UNIT_M_S,
CONCENTRATION_PARTS_PER_MILLION: UNIT_PARTS_MILLION,
PERCENTAGE: {*UNIT_PERCENTAGE_VALUE, *UNIT_RSSI},
MASS_POUNDS: UNIT_POUNDS,
PRESSURE_PSI: UNIT_POUND_PER_SQUARE_INCH,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT: UNIT_POWER_LEVEL,
TIME_SECONDS: UNIT_SECOND,
PRESSURE_MMHG: UNIT_SYSTOLIC,
ELECTRIC_POTENTIAL_VOLT: SENSOR_UNIT_VOLT,
POWER_WATT: SENSOR_UNIT_WATT,
IRRADIATION_WATTS_PER_SQUARE_METER: UNIT_WATT_PER_SQUARE_METER,
}
@dataclass @dataclass
class ZwaveValueID: class ZwaveValueID:
@ -154,10 +294,8 @@ class DynamicCurrentTempClimateDataTemplate(BaseDiscoverySchemaDataTemplate):
value.node, self.dependent_value value.node, self.dependent_value
), ),
} }
for key in self.lookup_table: for key, value_id in self.lookup_table.items():
data["lookup_table"][key] = self._get_value_from_id( data["lookup_table"][key] = self._get_value_from_id(value.node, value_id)
value.node, self.lookup_table[key]
)
return data return data
@ -183,17 +321,45 @@ class DynamicCurrentTempClimateDataTemplate(BaseDiscoverySchemaDataTemplate):
return None return None
@dataclass
class NumericSensorDataTemplateData:
"""Class to represent returned data from NumericSensorDataTemplate."""
entity_description_key: str | None = None
unit_of_measurement: str | None = None
class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate): class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate):
"""Data template class for Z-Wave Sensor entities.""" """Data template class for Z-Wave Sensor entities."""
def resolve_data(self, value: ZwaveValue) -> str | None: @staticmethod
def find_key_from_matching_set(
enum_value: MultilevelSensorType | MultilevelSensorScaleType | MeterScaleType,
set_map: dict[
str, set[MultilevelSensorType | MultilevelSensorScaleType | MeterScaleType]
],
) -> str | None:
"""Find a key in a set map that matches a given enum value."""
for key, value_set in set_map.items():
for value_in_set in value_set:
# Since these are IntEnums and the different classes reuse the same
# values, we need to match the class as well
if (
value_in_set.__class__ == enum_value.__class__
and value_in_set == enum_value
):
return key
return None
def resolve_data(self, value: ZwaveValue) -> NumericSensorDataTemplateData:
"""Resolve helper class data for a discovered value.""" """Resolve helper class data for a discovered value."""
if value.command_class == CommandClass.BATTERY: if value.command_class == CommandClass.BATTERY:
return ENTITY_DESC_KEY_BATTERY return NumericSensorDataTemplateData(ENTITY_DESC_KEY_BATTERY, PERCENTAGE)
if value.command_class == CommandClass.METER: if value.command_class == CommandClass.METER:
scale_type = get_meter_scale_type(value) scale_type = get_meter_scale_type(value)
unit = self.find_key_from_matching_set(scale_type, METER_UNIT_MAP)
# We do this because even though these are energy scales, they don't meet # We do this because even though these are energy scales, they don't meet
# the unit requirements for the energy device class. # the unit requirements for the energy device class.
if scale_type in ( if scale_type in (
@ -201,28 +367,36 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate):
ElectricScale.KILOVOLT_AMPERE_HOUR, ElectricScale.KILOVOLT_AMPERE_HOUR,
ElectricScale.KILOVOLT_AMPERE_REACTIVE_HOUR, ElectricScale.KILOVOLT_AMPERE_REACTIVE_HOUR,
): ):
return ENTITY_DESC_KEY_TOTAL_INCREASING return NumericSensorDataTemplateData(
ENTITY_DESC_KEY_TOTAL_INCREASING, unit
)
# We do this because even though these are power scales, they don't meet # We do this because even though these are power scales, they don't meet
# the unit requirements for the power device class. # the unit requirements for the power device class.
if scale_type == ElectricScale.KILOVOLT_AMPERE_REACTIVE: if scale_type == ElectricScale.KILOVOLT_AMPERE_REACTIVE:
return ENTITY_DESC_KEY_MEASUREMENT return NumericSensorDataTemplateData(ENTITY_DESC_KEY_MEASUREMENT, unit)
for key, scale_type_set in METER_DEVICE_CLASS_MAP.items(): return NumericSensorDataTemplateData(
if scale_type in scale_type_set: self.find_key_from_matching_set(scale_type, METER_DEVICE_CLASS_MAP),
return key unit,
)
if value.command_class == CommandClass.SENSOR_MULTILEVEL: if value.command_class == CommandClass.SENSOR_MULTILEVEL:
sensor_type = get_multilevel_sensor_type(value) sensor_type = get_multilevel_sensor_type(value)
scale_type = get_multilevel_sensor_scale_type(value)
unit = self.find_key_from_matching_set(
scale_type, MULTILEVEL_SENSOR_UNIT_MAP
)
if sensor_type == MultilevelSensorType.TARGET_TEMPERATURE: if sensor_type == MultilevelSensorType.TARGET_TEMPERATURE:
return ENTITY_DESC_KEY_TARGET_TEMPERATURE return NumericSensorDataTemplateData(
for ( ENTITY_DESC_KEY_TARGET_TEMPERATURE, unit
key, )
sensor_type_set, key = self.find_key_from_matching_set(
) in MULTILEVEL_SENSOR_DEVICE_CLASS_MAP.items(): sensor_type, MULTILEVEL_SENSOR_DEVICE_CLASS_MAP
if sensor_type in sensor_type_set: )
return key if key:
return NumericSensorDataTemplateData(key, unit)
return None return NumericSensorDataTemplateData()
@dataclass @dataclass

View file

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping from collections.abc import Mapping
from dataclasses import dataclass
import logging import logging
from typing import cast from typing import cast
@ -25,6 +24,9 @@ from homeassistant.components.sensor import (
SensorEntity, SensorEntity,
SensorEntityDescription, SensorEntityDescription,
) )
from homeassistant.components.zwave_js.discovery_data_template import (
NumericSensorDataTemplateData,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
@ -40,8 +42,6 @@ from homeassistant.const import (
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_platform from homeassistant.helpers import entity_platform
@ -89,98 +89,91 @@ STATUS_ICON: dict[NodeStatus, str] = {
} }
@dataclass ENTITY_DESCRIPTION_KEY_MAP: dict[str, SensorEntityDescription] = {
class ZwaveSensorEntityDescription(SensorEntityDescription): ENTITY_DESC_KEY_BATTERY: 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, ENTITY_DESC_KEY_BATTERY,
device_class=DEVICE_CLASS_BATTERY, device_class=DEVICE_CLASS_BATTERY,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_CURRENT: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_CURRENT: SensorEntityDescription(
ENTITY_DESC_KEY_CURRENT, ENTITY_DESC_KEY_CURRENT,
device_class=DEVICE_CLASS_CURRENT, device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_VOLTAGE: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_VOLTAGE: SensorEntityDescription(
ENTITY_DESC_KEY_VOLTAGE, ENTITY_DESC_KEY_VOLTAGE,
device_class=DEVICE_CLASS_VOLTAGE, device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_ENERGY_MEASUREMENT: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_ENERGY_MEASUREMENT: SensorEntityDescription(
ENTITY_DESC_KEY_ENERGY_MEASUREMENT, ENTITY_DESC_KEY_ENERGY_MEASUREMENT,
device_class=DEVICE_CLASS_ENERGY, device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING: SensorEntityDescription(
ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING, ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING,
device_class=DEVICE_CLASS_ENERGY, device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING, state_class=STATE_CLASS_TOTAL_INCREASING,
), ),
ENTITY_DESC_KEY_POWER: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_POWER: SensorEntityDescription(
ENTITY_DESC_KEY_POWER, ENTITY_DESC_KEY_POWER,
device_class=DEVICE_CLASS_POWER, device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_POWER_FACTOR: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_POWER_FACTOR: SensorEntityDescription(
ENTITY_DESC_KEY_POWER_FACTOR, ENTITY_DESC_KEY_POWER_FACTOR,
device_class=DEVICE_CLASS_POWER_FACTOR, device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_CO: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_CO: SensorEntityDescription(
ENTITY_DESC_KEY_CO, ENTITY_DESC_KEY_CO,
device_class=DEVICE_CLASS_CO, device_class=DEVICE_CLASS_CO,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_CO2: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_CO2: SensorEntityDescription(
ENTITY_DESC_KEY_CO2, ENTITY_DESC_KEY_CO2,
device_class=DEVICE_CLASS_CO2, device_class=DEVICE_CLASS_CO2,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_HUMIDITY: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_HUMIDITY: SensorEntityDescription(
ENTITY_DESC_KEY_HUMIDITY, ENTITY_DESC_KEY_HUMIDITY,
device_class=DEVICE_CLASS_HUMIDITY, device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_ILLUMINANCE: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_ILLUMINANCE: SensorEntityDescription(
ENTITY_DESC_KEY_ILLUMINANCE, ENTITY_DESC_KEY_ILLUMINANCE,
device_class=DEVICE_CLASS_ILLUMINANCE, device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_PRESSURE: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_PRESSURE: SensorEntityDescription(
ENTITY_DESC_KEY_PRESSURE, ENTITY_DESC_KEY_PRESSURE,
device_class=DEVICE_CLASS_PRESSURE, device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_SIGNAL_STRENGTH: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_SIGNAL_STRENGTH: SensorEntityDescription(
ENTITY_DESC_KEY_SIGNAL_STRENGTH, ENTITY_DESC_KEY_SIGNAL_STRENGTH,
device_class=DEVICE_CLASS_SIGNAL_STRENGTH, device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_TEMPERATURE: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_TEMPERATURE: SensorEntityDescription(
ENTITY_DESC_KEY_TEMPERATURE, ENTITY_DESC_KEY_TEMPERATURE,
device_class=DEVICE_CLASS_TEMPERATURE, device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_TARGET_TEMPERATURE: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_TARGET_TEMPERATURE: SensorEntityDescription(
ENTITY_DESC_KEY_TARGET_TEMPERATURE, ENTITY_DESC_KEY_TARGET_TEMPERATURE,
device_class=DEVICE_CLASS_TEMPERATURE, device_class=DEVICE_CLASS_TEMPERATURE,
state_class=None, state_class=None,
), ),
ENTITY_DESC_KEY_MEASUREMENT: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_MEASUREMENT: SensorEntityDescription(
ENTITY_DESC_KEY_MEASUREMENT, ENTITY_DESC_KEY_MEASUREMENT,
device_class=None, device_class=None,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
ENTITY_DESC_KEY_TOTAL_INCREASING: ZwaveSensorEntityDescription( ENTITY_DESC_KEY_TOTAL_INCREASING: SensorEntityDescription(
ENTITY_DESC_KEY_TOTAL_INCREASING, ENTITY_DESC_KEY_TOTAL_INCREASING,
device_class=None, device_class=None,
state_class=STATE_CLASS_TOTAL_INCREASING, state_class=STATE_CLASS_TOTAL_INCREASING,
@ -201,25 +194,42 @@ async def async_setup_entry(
"""Add Z-Wave Sensor.""" """Add Z-Wave Sensor."""
entities: list[ZWaveBaseEntity] = [] entities: list[ZWaveBaseEntity] = []
if info.platform_data:
data: NumericSensorDataTemplateData = info.platform_data
else:
data = NumericSensorDataTemplateData()
entity_description = ENTITY_DESCRIPTION_KEY_MAP.get( entity_description = ENTITY_DESCRIPTION_KEY_MAP.get(
info.platform_data data.entity_description_key or "", SensorEntityDescription("base_sensor")
) or ZwaveSensorEntityDescription("base_sensor") )
entity_description.info = info
if info.platform_hint == "string_sensor": if info.platform_hint == "string_sensor":
entities.append(ZWaveStringSensor(config_entry, client, entity_description)) entities.append(
ZWaveStringSensor(config_entry, client, info, entity_description)
)
elif info.platform_hint == "numeric_sensor": elif info.platform_hint == "numeric_sensor":
entities.append( entities.append(
ZWaveNumericSensor(config_entry, client, entity_description) ZWaveNumericSensor(
config_entry,
client,
info,
entity_description,
data.unit_of_measurement,
)
) )
elif info.platform_hint == "list_sensor": elif info.platform_hint == "list_sensor":
entities.append(ZWaveListSensor(config_entry, client, entity_description)) entities.append(
ZWaveListSensor(config_entry, client, info, entity_description)
)
elif info.platform_hint == "config_parameter": elif info.platform_hint == "config_parameter":
entities.append( entities.append(
ZWaveConfigParameterSensor(config_entry, client, entity_description) ZWaveConfigParameterSensor(
config_entry, client, info, entity_description
)
) )
elif info.platform_hint == "meter": elif info.platform_hint == "meter":
entities.append(ZWaveMeterSensor(config_entry, client, entity_description)) entities.append(
ZWaveMeterSensor(config_entry, client, info, entity_description)
)
else: else:
LOGGER.warning( LOGGER.warning(
"Sensor not implemented for %s/%s", "Sensor not implemented for %s/%s",
@ -269,12 +279,14 @@ class ZwaveSensorBase(ZWaveBaseEntity, SensorEntity):
self, self,
config_entry: ConfigEntry, config_entry: ConfigEntry,
client: ZwaveClient, client: ZwaveClient,
entity_description: ZwaveSensorEntityDescription, info: ZwaveDiscoveryInfo,
entity_description: SensorEntityDescription,
unit_of_measurement: str | None = None,
) -> None: ) -> None:
"""Initialize a ZWaveSensorBase entity.""" """Initialize a ZWaveSensorBase entity."""
assert entity_description.info super().__init__(config_entry, client, info)
super().__init__(config_entry, client, entity_description.info)
self.entity_description = entity_description self.entity_description = entity_description
self._attr_native_unit_of_measurement = unit_of_measurement
# Entity class attributes # Entity class attributes
self._attr_force_update = True self._attr_force_update = True
@ -312,12 +324,10 @@ class ZWaveNumericSensor(ZwaveSensorBase):
@property @property
def native_unit_of_measurement(self) -> str | None: def native_unit_of_measurement(self) -> str | None:
"""Return unit of measurement the value is expressed in.""" """Return unit of measurement the value is expressed in."""
if self._attr_native_unit_of_measurement is not None:
return self._attr_native_unit_of_measurement
if self.info.primary_value.metadata.unit is None: if self.info.primary_value.metadata.unit is None:
return None return None
if self.info.primary_value.metadata.unit == "C":
return TEMP_CELSIUS
if self.info.primary_value.metadata.unit == "F":
return TEMP_FAHRENHEIT
return str(self.info.primary_value.metadata.unit) return str(self.info.primary_value.metadata.unit)
@ -365,10 +375,14 @@ class ZWaveListSensor(ZwaveSensorBase):
self, self,
config_entry: ConfigEntry, config_entry: ConfigEntry,
client: ZwaveClient, client: ZwaveClient,
entity_description: ZwaveSensorEntityDescription, info: ZwaveDiscoveryInfo,
entity_description: SensorEntityDescription,
unit_of_measurement: str | None = None,
) -> None: ) -> None:
"""Initialize a ZWaveListSensor entity.""" """Initialize a ZWaveListSensor entity."""
super().__init__(config_entry, client, entity_description) super().__init__(
config_entry, client, info, entity_description, unit_of_measurement
)
# Entity class attributes # Entity class attributes
self._attr_name = self.generate_name( self._attr_name = self.generate_name(
@ -405,10 +419,14 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase):
self, self,
config_entry: ConfigEntry, config_entry: ConfigEntry,
client: ZwaveClient, client: ZwaveClient,
entity_description: ZwaveSensorEntityDescription, info: ZwaveDiscoveryInfo,
entity_description: SensorEntityDescription,
unit_of_measurement: str | None = None,
) -> None: ) -> None:
"""Initialize a ZWaveConfigParameterSensor entity.""" """Initialize a ZWaveConfigParameterSensor entity."""
super().__init__(config_entry, client, entity_description) super().__init__(
config_entry, client, info, entity_description, unit_of_measurement
)
self._primary_value = cast(ConfigurationValue, self.info.primary_value) self._primary_value = cast(ConfigurationValue, self.info.primary_value)
# Entity class attributes # Entity class attributes

View file

@ -421,6 +421,7 @@ ATTR_TEMPERATURE: Final = "temperature"
POWER_WATT: Final = "W" POWER_WATT: Final = "W"
POWER_KILO_WATT: Final = "kW" POWER_KILO_WATT: Final = "kW"
POWER_VOLT_AMPERE: Final = "VA" POWER_VOLT_AMPERE: Final = "VA"
POWER_BTU_PER_HOUR: Final = "BTU/h"
# Energy units # Energy units
ENERGY_WATT_HOUR: Final = "Wh" ENERGY_WATT_HOUR: Final = "Wh"
@ -472,6 +473,7 @@ LENGTH_MILES: Final = "mi"
# Frequency units # Frequency units
FREQUENCY_HERTZ: Final = "Hz" FREQUENCY_HERTZ: Final = "Hz"
FREQUENCY_KILOHERTZ: Final = "kHz"
FREQUENCY_MEGAHERTZ: Final = "MHz" FREQUENCY_MEGAHERTZ: Final = "MHz"
FREQUENCY_GIGAHERTZ: Final = "GHz" FREQUENCY_GIGAHERTZ: Final = "GHz"
@ -482,6 +484,7 @@ PRESSURE_KPA: Final = "kPa"
PRESSURE_BAR: Final = "bar" PRESSURE_BAR: Final = "bar"
PRESSURE_CBAR: Final = "cbar" PRESSURE_CBAR: Final = "cbar"
PRESSURE_MBAR: Final = "mbar" PRESSURE_MBAR: Final = "mbar"
PRESSURE_MMHG: Final = "mmHg"
PRESSURE_INHG: Final = "inHg" PRESSURE_INHG: Final = "inHg"
PRESSURE_PSI: Final = "psi" PRESSURE_PSI: Final = "psi"