forked from home-assistant/core
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
This commit is contained in:
@@ -75,5 +75,24 @@ ATTR_REFRESH_ALL_VALUES = "refresh_all_values"
|
|||||||
ATTR_BROADCAST = "broadcast"
|
ATTR_BROADCAST = "broadcast"
|
||||||
# meter reset
|
# meter reset
|
||||||
ATTR_METER_TYPE = "meter_type"
|
ATTR_METER_TYPE = "meter_type"
|
||||||
|
ATTR_METER_TYPE_NAME = "meter_type_name"
|
||||||
|
|
||||||
ADDON_SLUG = "core_zwave_js"
|
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 awesomeversion import AwesomeVersion
|
||||||
from zwave_js_server.const import THERMOSTAT_CURRENT_TEMP_PROPERTY, CommandClass
|
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.device_class import DeviceClassItem
|
||||||
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
|
from zwave_js_server.model.value import Value as ZwaveValue
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
|
from .const import LOGGER
|
||||||
from .discovery_data_template import (
|
from .discovery_data_template import (
|
||||||
BaseDiscoverySchemaDataTemplate,
|
BaseDiscoverySchemaDataTemplate,
|
||||||
DynamicCurrentTempClimateDataTemplate,
|
DynamicCurrentTempClimateDataTemplate,
|
||||||
|
NumericSensorDataTemplate,
|
||||||
ZwaveValueID,
|
ZwaveValueID,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -59,14 +62,14 @@ class ZwaveDiscoveryInfo:
|
|||||||
assumed_state: bool
|
assumed_state: bool
|
||||||
# the home assistant platform for which an entity should be created
|
# the home assistant platform for which an entity should be created
|
||||||
platform: str
|
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
|
# hint for the platform about this discovered entity
|
||||||
platform_hint: str | None = ""
|
platform_hint: str | None = ""
|
||||||
# data template to use in platform logic
|
# data template to use in platform logic
|
||||||
platform_data_template: BaseDiscoverySchemaDataTemplate | None = None
|
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
|
# bool to specify whether entity should be enabled by default
|
||||||
entity_registry_enabled_default: bool = True
|
entity_registry_enabled_default: bool = True
|
||||||
|
|
||||||
@@ -487,6 +490,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
},
|
},
|
||||||
type={"number"},
|
type={"number"},
|
||||||
),
|
),
|
||||||
|
data_template=NumericSensorDataTemplate(),
|
||||||
),
|
),
|
||||||
ZWaveDiscoverySchema(
|
ZWaveDiscoverySchema(
|
||||||
platform="sensor",
|
platform="sensor",
|
||||||
@@ -495,6 +499,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
command_class={CommandClass.INDICATOR},
|
command_class={CommandClass.INDICATOR},
|
||||||
type={"number"},
|
type={"number"},
|
||||||
),
|
),
|
||||||
|
data_template=NumericSensorDataTemplate(),
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
),
|
),
|
||||||
# Meter sensors for Meter CC
|
# Meter sensors for Meter CC
|
||||||
@@ -508,6 +513,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
type={"number"},
|
type={"number"},
|
||||||
property={"value"},
|
property={"value"},
|
||||||
),
|
),
|
||||||
|
data_template=NumericSensorDataTemplate(),
|
||||||
),
|
),
|
||||||
# special list sensors (Notification CC)
|
# special list sensors (Notification CC)
|
||||||
ZWaveDiscoverySchema(
|
ZWaveDiscoverySchema(
|
||||||
@@ -542,6 +548,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
property={"targetValue"},
|
property={"targetValue"},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
data_template=NumericSensorDataTemplate(),
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
),
|
),
|
||||||
# binary switches
|
# binary switches
|
||||||
@@ -745,9 +752,15 @@ def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None
|
|||||||
|
|
||||||
# resolve helper data from template
|
# resolve helper data from template
|
||||||
resolved_data = None
|
resolved_data = None
|
||||||
additional_value_ids_to_watch = None
|
additional_value_ids_to_watch = set()
|
||||||
if schema.data_template:
|
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(
|
additional_value_ids_to_watch = schema.data_template.value_ids_to_watch(
|
||||||
resolved_data
|
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 __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
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.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 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
|
@dataclass
|
||||||
@@ -19,11 +87,10 @@ class ZwaveValueID:
|
|||||||
property_key: str | int | None = None
|
property_key: str | int | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class BaseDiscoverySchemaDataTemplate:
|
class BaseDiscoverySchemaDataTemplate:
|
||||||
"""Base class for discovery schema data templates."""
|
"""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.
|
Resolve helper class data for a discovered value.
|
||||||
|
|
||||||
@@ -33,7 +100,7 @@ class BaseDiscoverySchemaDataTemplate:
|
|||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
return {}
|
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.
|
Return list of all ZwaveValues resolved by helper that should be watched.
|
||||||
|
|
||||||
@@ -42,7 +109,7 @@ class BaseDiscoverySchemaDataTemplate:
|
|||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
return []
|
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.
|
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 lookup_table.get(lookup_key)
|
||||||
|
|
||||||
return None
|
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."""
|
"""Representation of Z-Wave sensors."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from zwave_js_server.client import Client as ZwaveClient
|
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.node import Node as ZwaveNode
|
||||||
from zwave_js_server.model.value import ConfigurationValue
|
from zwave_js_server.model.value import ConfigurationValue
|
||||||
|
from zwave_js_server.util.command_class import get_meter_type
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
DEVICE_CLASS_BATTERY,
|
|
||||||
DEVICE_CLASS_ENERGY,
|
DEVICE_CLASS_ENERGY,
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
|
||||||
DEVICE_CLASS_POWER,
|
|
||||||
DOMAIN as SENSOR_DOMAIN,
|
DOMAIN as SENSOR_DOMAIN,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
)
|
)
|
||||||
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_CO,
|
||||||
|
DEVICE_CLASS_CO2,
|
||||||
DEVICE_CLASS_CURRENT,
|
DEVICE_CLASS_CURRENT,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
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_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_TIMESTAMP,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
TEMP_FAHRENHEIT,
|
TEMP_FAHRENHEIT,
|
||||||
@@ -34,7 +49,30 @@ from homeassistant.helpers import entity_platform
|
|||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
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 .discovery import ZwaveDiscoveryInfo
|
||||||
from .entity import ZWaveBaseEntity
|
from .entity import ZWaveBaseEntity
|
||||||
from .helpers import get_device_id
|
from .helpers import get_device_id
|
||||||
@@ -42,6 +80,97 @@ from .helpers import get_device_id
|
|||||||
LOGGER = logging.getLogger(__name__)
|
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(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
@@ -55,16 +184,25 @@ async def async_setup_entry(
|
|||||||
"""Add Z-Wave Sensor."""
|
"""Add Z-Wave Sensor."""
|
||||||
entities: list[ZWaveBaseEntity] = []
|
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":
|
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":
|
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":
|
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":
|
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":
|
elif info.platform_hint == "meter":
|
||||||
entities.append(ZWaveMeterSensor(config_entry, client, info))
|
entities.append(ZWaveMeterSensor(config_entry, client, entity_description))
|
||||||
else:
|
else:
|
||||||
LOGGER.warning(
|
LOGGER.warning(
|
||||||
"Sensor not implemented for %s/%s",
|
"Sensor not implemented for %s/%s",
|
||||||
@@ -114,62 +252,16 @@ class ZwaveSensorBase(ZWaveBaseEntity, SensorEntity):
|
|||||||
self,
|
self,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
client: ZwaveClient,
|
client: ZwaveClient,
|
||||||
info: ZwaveDiscoveryInfo,
|
entity_description: ZwaveSensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a ZWaveSensorBase entity."""
|
"""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
|
# Entity class attributes
|
||||||
|
self._attr_force_update = True
|
||||||
self._attr_name = self.generate_name(include_value_name=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):
|
class ZWaveStringSensor(ZwaveSensorBase):
|
||||||
@@ -216,20 +308,16 @@ class ZWaveNumericSensor(ZwaveSensorBase):
|
|||||||
class ZWaveMeterSensor(ZWaveNumericSensor):
|
class ZWaveMeterSensor(ZWaveNumericSensor):
|
||||||
"""Representation of a Z-Wave Meter CC sensor."""
|
"""Representation of a Z-Wave Meter CC sensor."""
|
||||||
|
|
||||||
def __init__(
|
@property
|
||||||
self,
|
def extra_state_attributes(self) -> Mapping[str, int | str] | None:
|
||||||
config_entry: ConfigEntry,
|
"""Return extra state attributes."""
|
||||||
client: ZwaveClient,
|
meter_type = get_meter_type(self.info.primary_value)
|
||||||
info: ZwaveDiscoveryInfo,
|
if meter_type:
|
||||||
) -> None:
|
return {
|
||||||
"""Initialize a ZWaveNumericSensor entity."""
|
ATTR_METER_TYPE: meter_type.value,
|
||||||
super().__init__(config_entry, client, info)
|
ATTR_METER_TYPE_NAME: meter_type.name,
|
||||||
|
}
|
||||||
# Entity class attributes
|
return None
|
||||||
if self.device_class == DEVICE_CLASS_ENERGY:
|
|
||||||
self._attr_state_class = STATE_CLASS_TOTAL_INCREASING
|
|
||||||
else:
|
|
||||||
self._attr_state_class = STATE_CLASS_MEASUREMENT
|
|
||||||
|
|
||||||
async def async_reset_meter(
|
async def async_reset_meter(
|
||||||
self, meter_type: int | None = None, value: int | None = None
|
self, meter_type: int | None = None, value: int | None = None
|
||||||
@@ -239,9 +327,9 @@ class ZWaveMeterSensor(ZWaveNumericSensor):
|
|||||||
primary_value = self.info.primary_value
|
primary_value = self.info.primary_value
|
||||||
options = {}
|
options = {}
|
||||||
if meter_type is not None:
|
if meter_type is not None:
|
||||||
options["type"] = meter_type
|
options[RESET_METER_OPTION_TYPE] = meter_type
|
||||||
if value is not None:
|
if value is not None:
|
||||||
options["targetValue"] = value
|
options[RESET_METER_OPTION_TARGET_VALUE] = value
|
||||||
args = [options] if options else []
|
args = [options] if options else []
|
||||||
await node.endpoints[primary_value.endpoint].async_invoke_cc_api(
|
await node.endpoints[primary_value.endpoint].async_invoke_cc_api(
|
||||||
CommandClass.METER, "reset", *args, wait_for_result=False
|
CommandClass.METER, "reset", *args, wait_for_result=False
|
||||||
@@ -261,10 +349,10 @@ class ZWaveListSensor(ZwaveSensorBase):
|
|||||||
self,
|
self,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
client: ZwaveClient,
|
client: ZwaveClient,
|
||||||
info: ZwaveDiscoveryInfo,
|
entity_description: ZwaveSensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a ZWaveListSensor entity."""
|
"""Initialize a ZWaveListSensor entity."""
|
||||||
super().__init__(config_entry, client, info)
|
super().__init__(config_entry, client, entity_description)
|
||||||
|
|
||||||
# Entity class attributes
|
# Entity class attributes
|
||||||
self._attr_name = self.generate_name(
|
self._attr_name = self.generate_name(
|
||||||
@@ -291,7 +379,7 @@ class ZWaveListSensor(ZwaveSensorBase):
|
|||||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||||
"""Return the device specific state attributes."""
|
"""Return the device specific state attributes."""
|
||||||
# add the value's int value as property for multi-value (list) items
|
# 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):
|
class ZWaveConfigParameterSensor(ZwaveSensorBase):
|
||||||
@@ -301,10 +389,10 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase):
|
|||||||
self,
|
self,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
client: ZwaveClient,
|
client: ZwaveClient,
|
||||||
info: ZwaveDiscoveryInfo,
|
entity_description: ZwaveSensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a ZWaveConfigParameterSensor entity."""
|
"""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)
|
self._primary_value = cast(ConfigurationValue, self.info.primary_value)
|
||||||
|
|
||||||
# Entity class attributes
|
# Entity class attributes
|
||||||
@@ -338,7 +426,7 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase):
|
|||||||
if self._primary_value.configuration_value_type == ConfigurationValueType.RANGE:
|
if self._primary_value.configuration_value_type == ConfigurationValueType.RANGE:
|
||||||
return None
|
return None
|
||||||
# add the value's int value as property for multi-value (list) items
|
# 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):
|
class ZWaveNodeStatusSensor(SensorEntity):
|
||||||
|
Reference in New Issue
Block a user