mirror of
https://github.com/home-assistant/core.git
synced 2025-11-09 10:59:40 +00:00
250 lines
9.2 KiB
Python
250 lines
9.2 KiB
Python
"""Provide models for the Z-Wave integration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable
|
|
from dataclasses import asdict, dataclass, field
|
|
from enum import StrEnum
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from awesomeversion import AwesomeVersion
|
|
from zwave_js_server.const import LogLevel
|
|
from zwave_js_server.model.node import Node as ZwaveNode
|
|
from zwave_js_server.model.value import (
|
|
ConfigurationValue as ZwaveConfigurationValue,
|
|
Value as ZwaveValue,
|
|
get_value_id_str,
|
|
)
|
|
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import EntityCategory, Platform
|
|
from homeassistant.helpers.entity import EntityDescription
|
|
|
|
if TYPE_CHECKING:
|
|
from _typeshed import DataclassInstance
|
|
from zwave_js_server.client import Client as ZwaveClient
|
|
|
|
from . import DriverEvents
|
|
|
|
|
|
@dataclass
|
|
class ZwaveJSData:
|
|
"""Data for zwave_js runtime data."""
|
|
|
|
client: ZwaveClient
|
|
driver_events: DriverEvents
|
|
old_server_log_level: LogLevel | None = None
|
|
|
|
|
|
type ZwaveJSConfigEntry = ConfigEntry[ZwaveJSData]
|
|
|
|
|
|
@dataclass
|
|
class ZwaveValueID:
|
|
"""Class to represent a value ID."""
|
|
|
|
property_: str | int
|
|
command_class: int
|
|
endpoint: int | None = None
|
|
property_key: str | int | None = None
|
|
|
|
|
|
class ValueType(StrEnum):
|
|
"""Enum with all value types."""
|
|
|
|
ANY = "any"
|
|
BOOLEAN = "boolean"
|
|
NUMBER = "number"
|
|
STRING = "string"
|
|
|
|
|
|
class DataclassMustHaveAtLeastOne:
|
|
"""A dataclass that must have at least one input parameter that is not None."""
|
|
|
|
def __post_init__(self: DataclassInstance) -> None:
|
|
"""Post dataclass initialization."""
|
|
if all(val is None for val in asdict(self).values()):
|
|
raise ValueError("At least one input parameter must not be None")
|
|
|
|
|
|
@dataclass
|
|
class FirmwareVersionRange(DataclassMustHaveAtLeastOne):
|
|
"""Firmware version range dictionary."""
|
|
|
|
min: str | None = None
|
|
max: str | None = None
|
|
min_ver: AwesomeVersion | None = field(default=None, init=False)
|
|
max_ver: AwesomeVersion | None = field(default=None, init=False)
|
|
|
|
def __post_init__(self) -> None:
|
|
"""Post dataclass initialization."""
|
|
super().__post_init__()
|
|
if self.min:
|
|
self.min_ver = AwesomeVersion(self.min)
|
|
if self.max:
|
|
self.max_ver = AwesomeVersion(self.max)
|
|
|
|
|
|
@dataclass
|
|
class PlatformZwaveDiscoveryInfo:
|
|
"""Info discovered from (primary) ZWave Value to create entity."""
|
|
|
|
# node to which the value(s) belongs
|
|
node: ZwaveNode
|
|
# the value object itself for primary value
|
|
primary_value: ZwaveValue
|
|
# bool to specify whether state is assumed and events should be fired on value
|
|
# update
|
|
assumed_state: bool
|
|
# the home assistant platform for which an entity should be created
|
|
platform: Platform
|
|
# additional values that need to be watched by entity
|
|
additional_value_ids_to_watch: set[str]
|
|
|
|
|
|
@dataclass
|
|
class ZwaveDiscoveryInfo(PlatformZwaveDiscoveryInfo):
|
|
"""Info discovered from (primary) ZWave Value to create entity."""
|
|
|
|
# helper data to use in platform setup
|
|
platform_data: Any = None
|
|
# data template to use in platform logic
|
|
platform_data_template: BaseDiscoverySchemaDataTemplate | None = None
|
|
# hint for the platform about this discovered entity
|
|
platform_hint: str | None = ""
|
|
# bool to specify whether entity should be enabled by default
|
|
entity_registry_enabled_default: bool = True
|
|
# the entity category for the discovered entity
|
|
entity_category: EntityCategory | None = None
|
|
|
|
|
|
@dataclass
|
|
class ZWaveValueDiscoverySchema(DataclassMustHaveAtLeastOne):
|
|
"""Z-Wave Value discovery schema.
|
|
|
|
The Z-Wave Value must match these conditions.
|
|
Use the Z-Wave specifications to find out the values for these parameters:
|
|
https://github.com/zwave-js/specs/tree/master
|
|
"""
|
|
|
|
# [optional] the value's command class must match ANY of these values
|
|
command_class: set[int] | None = None
|
|
# [optional] the value's endpoint must match ANY of these values
|
|
endpoint: set[int] | None = None
|
|
# [optional] the value's property must match ANY of these values
|
|
property: set[str | int] | None = None
|
|
# [optional] the value's property name must match ANY of these values
|
|
property_name: set[str] | None = None
|
|
# [optional] the value's property key must match ANY of these values
|
|
property_key: set[str | int | None] | None = None
|
|
# [optional] the value's property key must NOT match ANY of these values
|
|
not_property_key: set[str | int | None] | None = None
|
|
# [optional] the value's metadata_type must match ANY of these values
|
|
type: set[str] | None = None
|
|
# [optional] the value's metadata_readable must match this value
|
|
readable: bool | None = None
|
|
# [optional] the value's metadata_writeable must match this value
|
|
writeable: bool | None = None
|
|
# [optional] the value's states map must include ANY of these key/value pairs
|
|
any_available_states: set[tuple[int, str]] | None = None
|
|
# [optional] the value's states map must include ANY of these keys
|
|
any_available_states_keys: set[int] | None = None
|
|
# [optional] the value's cc specific map must include ANY of these key/value pairs
|
|
any_available_cc_specific: set[tuple[Any, Any]] | None = None
|
|
# [optional] the value's value must match this value
|
|
value: Any | None = None
|
|
# [optional] the value's metadata_stateful must match this value
|
|
stateful: bool | None = None
|
|
|
|
|
|
@dataclass
|
|
class NewZWaveDiscoverySchema:
|
|
"""Z-Wave discovery schema.
|
|
|
|
The Z-Wave node and it's (primary) value for an entity must match these conditions.
|
|
Use the Z-Wave specifications to find out the values for these parameters:
|
|
https://github.com/zwave-js/node-zwave-js/tree/master/specs
|
|
"""
|
|
|
|
# specify the hass platform for which this scheme applies (e.g. light, sensor)
|
|
platform: Platform
|
|
# platform-specific entity description
|
|
entity_description: EntityDescription
|
|
# entity class to use to instantiate the entity
|
|
entity_class: type
|
|
# primary value belonging to this discovery scheme
|
|
primary_value: ZWaveValueDiscoverySchema
|
|
# [optional] template to generate platform specific data to use in setup
|
|
data_template: BaseDiscoverySchemaDataTemplate | None = None
|
|
# [optional] the node's manufacturer_id must match ANY of these values
|
|
manufacturer_id: set[int] | None = None
|
|
# [optional] the node's product_id must match ANY of these values
|
|
product_id: set[int] | None = None
|
|
# [optional] the node's product_type must match ANY of these values
|
|
product_type: set[int] | None = None
|
|
# [optional] the node's firmware_version must be within this range
|
|
firmware_version_range: FirmwareVersionRange | None = None
|
|
# [optional] the node's firmware_version must match ANY of these values
|
|
firmware_version: set[str] | None = None
|
|
# [optional] the node's basic device class must match ANY of these values
|
|
device_class_basic: set[str | int] | None = None
|
|
# [optional] the node's generic device class must match ANY of these values
|
|
device_class_generic: set[str | int] | None = None
|
|
# [optional] the node's specific device class must match ANY of these values
|
|
device_class_specific: set[str | int] | None = None
|
|
# [optional] additional values that ALL need to be present
|
|
# on the node for this scheme to pass
|
|
required_values: list[ZWaveValueDiscoverySchema] | None = None
|
|
# [optional] additional values that MAY NOT be present
|
|
# on the node for this scheme to pass
|
|
absent_values: list[ZWaveValueDiscoverySchema] | None = None
|
|
# [optional] bool to specify if this primary value may be discovered
|
|
# by multiple platforms
|
|
allow_multi: bool = False
|
|
# [optional] bool to specify whether state is assumed
|
|
# and events should be fired on value update
|
|
assumed_state: bool = False
|
|
|
|
|
|
@dataclass
|
|
class BaseDiscoverySchemaDataTemplate:
|
|
"""Base class for discovery schema data templates."""
|
|
|
|
static_data: Any | None = None
|
|
|
|
def resolve_data(self, value: ZwaveValue) -> Any:
|
|
"""Resolve helper class data for a discovered value.
|
|
|
|
Can optionally be implemented by subclasses if input data needs to be
|
|
transformed once discovered Value is available.
|
|
"""
|
|
return {}
|
|
|
|
def values_to_watch(self, resolved_data: Any) -> Iterable[ZwaveValue | None]:
|
|
"""Return list of all ZwaveValues resolved by helper that should be watched.
|
|
|
|
Should be implemented by subclasses only if there are values to watch.
|
|
"""
|
|
return []
|
|
|
|
def value_ids_to_watch(self, resolved_data: Any) -> set[str]:
|
|
"""Return list of all Value IDs resolved by helper that should be watched.
|
|
|
|
Not to be overwritten by subclasses.
|
|
"""
|
|
return {val.value_id for val in self.values_to_watch(resolved_data) if val}
|
|
|
|
@staticmethod
|
|
def _get_value_from_id(
|
|
node: ZwaveNode, value_id_obj: ZwaveValueID
|
|
) -> ZwaveValue | ZwaveConfigurationValue | None:
|
|
"""Get a ZwaveValue from a node using a ZwaveValueDict."""
|
|
value_id = get_value_id_str(
|
|
node,
|
|
value_id_obj.command_class,
|
|
value_id_obj.property_,
|
|
endpoint=value_id_obj.endpoint,
|
|
property_key=value_id_obj.property_key,
|
|
)
|
|
return node.values.get(value_id)
|