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 feedbackpull/54961/head
parent
debc6d632c
commit
1f2134a31a
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue