Home Assistant Unofficial Reference 2024.12.1
discovery.py
Go to the documentation of this file.
1 """Map Z-Wave nodes and values to Home Assistant entities."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Generator
6 from dataclasses import asdict, dataclass, field
7 from enum import StrEnum
8 from typing import TYPE_CHECKING, Any, cast
9 
10 from awesomeversion import AwesomeVersion
11 from zwave_js_server.const import (
12  CURRENT_STATE_PROPERTY,
13  CURRENT_VALUE_PROPERTY,
14  TARGET_STATE_PROPERTY,
15  TARGET_VALUE_PROPERTY,
16  CommandClass,
17 )
18 from zwave_js_server.const.command_class.barrier_operator import (
19  SIGNALING_STATE_PROPERTY,
20 )
21 from zwave_js_server.const.command_class.color_switch import CURRENT_COLOR_PROPERTY
22 from zwave_js_server.const.command_class.humidity_control import (
23  HUMIDITY_CONTROL_MODE_PROPERTY,
24 )
25 from zwave_js_server.const.command_class.lock import (
26  CURRENT_MODE_PROPERTY,
27  DOOR_STATUS_PROPERTY,
28  LOCKED_PROPERTY,
29 )
30 from zwave_js_server.const.command_class.meter import (
31  RESET_PROPERTY as RESET_METER_PROPERTY,
32  VALUE_PROPERTY,
33 )
34 from zwave_js_server.const.command_class.protection import LOCAL_PROPERTY, RF_PROPERTY
35 from zwave_js_server.const.command_class.sound_switch import (
36  DEFAULT_TONE_ID_PROPERTY,
37  DEFAULT_VOLUME_PROPERTY,
38  TONE_ID_PROPERTY,
39 )
40 from zwave_js_server.const.command_class.thermostat import (
41  THERMOSTAT_CURRENT_TEMP_PROPERTY,
42  THERMOSTAT_FAN_MODE_PROPERTY,
43  THERMOSTAT_MODE_PROPERTY,
44  THERMOSTAT_SETPOINT_PROPERTY,
45 )
46 from zwave_js_server.exceptions import UnknownValueData
47 from zwave_js_server.model.node import Node as ZwaveNode
48 from zwave_js_server.model.value import (
49  ConfigurationValue,
50  ConfigurationValueType,
51  Value as ZwaveValue,
52 )
53 
54 from homeassistant.const import EntityCategory, Platform
55 from homeassistant.core import callback
56 from homeassistant.helpers.device_registry import DeviceEntry
57 
58 from .const import COVER_POSITION_PROPERTY_KEYS, COVER_TILT_PROPERTY_KEYS, LOGGER
59 from .discovery_data_template import (
60  BaseDiscoverySchemaDataTemplate,
61  ConfigurableFanValueMappingDataTemplate,
62  CoverTiltDataTemplate,
63  DynamicCurrentTempClimateDataTemplate,
64  FanValueMapping,
65  FixedFanValueMappingDataTemplate,
66  NumericSensorDataTemplate,
67 )
68 from .helpers import ZwaveValueID
69 
70 if TYPE_CHECKING:
71  from _typeshed import DataclassInstance
72 
73 
74 class ValueType(StrEnum):
75  """Enum with all value types."""
76 
77  ANY = "any"
78  BOOLEAN = "boolean"
79  NUMBER = "number"
80  STRING = "string"
81 
82 
84  """A dataclass that must have at least one input parameter that is not None."""
85 
86  def __post_init__(self: DataclassInstance) -> None:
87  """Post dataclass initialization."""
88  if all(val is None for val in asdict(self).values()):
89  raise ValueError("At least one input parameter must not be None")
90 
91 
92 @dataclass
94  """Firmware version range dictionary."""
95 
96  min: str | None = None
97  max: str | None = None
98  min_ver: AwesomeVersion | None = field(default=None, init=False)
99  max_ver: AwesomeVersion | None = field(default=None, init=False)
100 
101  def __post_init__(self) -> None:
102  """Post dataclass initialization."""
103  super().__post_init__()
104  if self.min:
105  self.min_vermin_ver = AwesomeVersion(self.min)
106  if self.max:
107  self.max_vermax_ver = AwesomeVersion(self.max)
108 
109 
110 @dataclass
112  """Info discovered from (primary) ZWave Value to create entity."""
113 
114  # node to which the value(s) belongs
115  node: ZwaveNode
116  # the value object itself for primary value
117  primary_value: ZwaveValue
118  # bool to specify whether state is assumed and events should be fired on value
119  # update
120  assumed_state: bool
121  # the home assistant platform for which an entity should be created
122  platform: Platform
123  # helper data to use in platform setup
124  platform_data: Any
125  # additional values that need to be watched by entity
126  additional_value_ids_to_watch: set[str]
127  # hint for the platform about this discovered entity
128  platform_hint: str | None = ""
129  # data template to use in platform logic
130  platform_data_template: BaseDiscoverySchemaDataTemplate | None = None
131  # bool to specify whether entity should be enabled by default
132  entity_registry_enabled_default: bool = True
133  # the entity category for the discovered entity
134  entity_category: EntityCategory | None = None
135 
136 
137 @dataclass
139  """Z-Wave Value discovery schema.
140 
141  The Z-Wave Value must match these conditions.
142  Use the Z-Wave specifications to find out the values for these parameters:
143  https://github.com/zwave-js/specs/tree/master
144  """
145 
146  # [optional] the value's command class must match ANY of these values
147  command_class: set[int] | None = None
148  # [optional] the value's endpoint must match ANY of these values
149  endpoint: set[int] | None = None
150  # [optional] the value's property must match ANY of these values
151  property: set[str | int] | None = None
152  # [optional] the value's property name must match ANY of these values
153  property_name: set[str] | None = None
154  # [optional] the value's property key must match ANY of these values
155  property_key: set[str | int | None] | None = None
156  # [optional] the value's property key must NOT match ANY of these values
157  not_property_key: set[str | int | None] | None = None
158  # [optional] the value's metadata_type must match ANY of these values
159  type: set[str] | None = None
160  # [optional] the value's metadata_readable must match this value
161  readable: bool | None = None
162  # [optional] the value's metadata_writeable must match this value
163  writeable: bool | None = None
164  # [optional] the value's states map must include ANY of these key/value pairs
165  any_available_states: set[tuple[int, str]] | None = None
166  # [optional] the value's value must match this value
167  value: Any | None = None
168  # [optional] the value's metadata_stateful must match this value
169  stateful: bool | None = None
170 
171 
172 @dataclass
174  """Z-Wave discovery schema.
175 
176  The Z-Wave node and it's (primary) value for an entity must match these conditions.
177  Use the Z-Wave specifications to find out the values for these parameters:
178  https://github.com/zwave-js/specs/tree/master
179  """
180 
181  # specify the hass platform for which this scheme applies (e.g. light, sensor)
182  platform: Platform
183  # primary value belonging to this discovery scheme
184  primary_value: ZWaveValueDiscoverySchema
185  # [optional] hint for platform
186  hint: str | None = None
187  # [optional] template to generate platform specific data to use in setup
188  data_template: BaseDiscoverySchemaDataTemplate | None = None
189  # [optional] the node's manufacturer_id must match ANY of these values
190  manufacturer_id: set[int] | None = None
191  # [optional] the node's product_id must match ANY of these values
192  product_id: set[int] | None = None
193  # [optional] the node's product_type must match ANY of these values
194  product_type: set[int] | None = None
195  # [optional] the node's firmware_version must be within this range
196  firmware_version_range: FirmwareVersionRange | None = None
197  # [optional] the node's generic device class must match ANY of these values
198  device_class_generic: set[str] | None = None
199  # [optional] the node's specific device class must match ANY of these values
200  device_class_specific: set[str] | None = None
201  # [optional] additional values that ALL need to be present
202  # on the node for this scheme to pass
203  required_values: list[ZWaveValueDiscoverySchema] | None = None
204  # [optional] additional values that MAY NOT be present
205  # on the node for this scheme to pass
206  absent_values: list[ZWaveValueDiscoverySchema] | None = None
207  # [optional] bool to specify if this primary value may be discovered
208  # by multiple platforms
209  allow_multi: bool = False
210  # [optional] bool to specify whether state is assumed
211  # and events should be fired on value update
212  assumed_state: bool = False
213  # [optional] bool to specify whether entity should be enabled by default
214  entity_registry_enabled_default: bool = True
215  # [optional] the entity category for the discovered entity
216  entity_category: EntityCategory | None = None
217 
218 
219 DOOR_LOCK_CURRENT_MODE_SCHEMA = ZWaveValueDiscoverySchema(
220  command_class={CommandClass.DOOR_LOCK},
221  property={CURRENT_MODE_PROPERTY},
222  type={ValueType.NUMBER},
223 )
224 
225 SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
226  command_class={CommandClass.SWITCH_MULTILEVEL},
227  property={CURRENT_VALUE_PROPERTY},
228  type={ValueType.NUMBER},
229 )
230 
231 SWITCH_MULTILEVEL_TARGET_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
232  command_class={CommandClass.SWITCH_MULTILEVEL},
233  property={TARGET_VALUE_PROPERTY},
234  type={ValueType.NUMBER},
235 )
236 
237 SWITCH_BINARY_CURRENT_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
238  command_class={CommandClass.SWITCH_BINARY}, property={CURRENT_VALUE_PROPERTY}
239 )
240 
241 COLOR_SWITCH_CURRENT_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
242  command_class={CommandClass.SWITCH_COLOR},
243  property={CURRENT_COLOR_PROPERTY},
244  property_key={None},
245 )
246 
247 SIREN_TONE_SCHEMA = ZWaveValueDiscoverySchema(
248  command_class={CommandClass.SOUND_SWITCH},
249  property={TONE_ID_PROPERTY},
250  type={ValueType.NUMBER},
251 )
252 
253 WINDOW_COVERING_COVER_CURRENT_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
254  command_class={CommandClass.WINDOW_COVERING},
255  property={CURRENT_VALUE_PROPERTY},
256  property_key=COVER_POSITION_PROPERTY_KEYS,
257 )
258 
259 WINDOW_COVERING_SLAT_CURRENT_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
260  command_class={CommandClass.WINDOW_COVERING},
261  property={CURRENT_VALUE_PROPERTY},
262  property_key=COVER_TILT_PROPERTY_KEYS,
263 )
264 
265 # For device class mapping see:
266 # https://github.com/zwave-js/node-zwave-js/blob/master/packages/config/config/deviceClasses.json
267 DISCOVERY_SCHEMAS = [
268  # ====== START OF DEVICE SPECIFIC MAPPING SCHEMAS =======
269  # Honeywell 39358 In-Wall Fan Control using switch multilevel CC
271  platform=Platform.FAN,
272  hint="has_fan_value_mapping",
273  manufacturer_id={0x0039},
274  product_id={0x3131},
275  product_type={0x4944},
276  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
277  required_values=[SWITCH_MULTILEVEL_TARGET_VALUE_SCHEMA],
278  data_template=FixedFanValueMappingDataTemplate(
279  FanValueMapping(speeds=[(1, 32), (33, 66), (67, 99)]),
280  ),
281  ),
282  # GE/Jasco - In-Wall Smart Fan Control - 12730 / ZW4002
284  platform=Platform.FAN,
285  hint="has_fan_value_mapping",
286  manufacturer_id={0x0063},
287  product_id={0x3034},
288  product_type={0x4944},
289  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
290  data_template=FixedFanValueMappingDataTemplate(
291  FanValueMapping(speeds=[(1, 33), (34, 67), (68, 99)]),
292  ),
293  ),
294  # GE/Jasco - In-Wall Smart Fan Control - 14287 / 55258 / ZW4002
296  platform=Platform.FAN,
297  hint="has_fan_value_mapping",
298  manufacturer_id={0x0063},
299  product_id={0x3131, 0x3337},
300  product_type={0x4944},
301  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
302  data_template=FixedFanValueMappingDataTemplate(
303  FanValueMapping(speeds=[(1, 32), (33, 66), (67, 99)]),
304  ),
305  ),
306  # GE/Jasco - In-Wall Smart Fan Control - 14314 / ZW4002
308  platform=Platform.FAN,
309  manufacturer_id={0x0063},
310  product_id={0x3138},
311  product_type={0x4944},
312  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
313  ),
314  # Leviton ZW4SF fan controllers using switch multilevel CC
316  platform=Platform.FAN,
317  hint="has_fan_value_mapping",
318  manufacturer_id={0x001D},
319  product_id={0x0002},
320  product_type={0x0038},
321  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
322  data_template=FixedFanValueMappingDataTemplate(
323  FanValueMapping(speeds=[(1, 25), (26, 50), (51, 75), (76, 99)]),
324  ),
325  ),
326  # Inovelli LZW36 light / fan controller combo using switch multilevel CC
327  # The fan is endpoint 2, the light is endpoint 1.
329  platform=Platform.FAN,
330  hint="has_fan_value_mapping",
331  manufacturer_id={0x031E},
332  product_id={0x0001},
333  product_type={0x000E},
334  primary_value=ZWaveValueDiscoverySchema(
335  command_class={CommandClass.SWITCH_MULTILEVEL},
336  endpoint={2},
337  property={CURRENT_VALUE_PROPERTY},
338  type={ValueType.NUMBER},
339  ),
340  data_template=FixedFanValueMappingDataTemplate(
342  presets={1: "breeze"}, speeds=[(2, 33), (34, 66), (67, 99)]
343  ),
344  ),
345  ),
346  # HomeSeer HS-FC200+
348  platform=Platform.FAN,
349  hint="has_fan_value_mapping",
350  manufacturer_id={0x000C},
351  product_id={0x0001},
352  product_type={0x0203},
353  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
355  configuration_option=ZwaveValueID(
356  property_=5, command_class=CommandClass.CONFIGURATION, endpoint=0
357  ),
358  configuration_value_to_fan_value_mapping={
359  0: FanValueMapping(speeds=[(1, 33), (34, 66), (67, 99)]),
360  1: FanValueMapping(speeds=[(1, 24), (25, 49), (50, 74), (75, 99)]),
361  },
362  ),
363  ),
364  # Fibaro Shutter Fibaro FGR222
366  platform=Platform.COVER,
367  hint="shutter_tilt",
368  manufacturer_id={0x010F},
369  product_id={0x1000, 0x1001},
370  product_type={0x0301, 0x0302},
371  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
372  data_template=CoverTiltDataTemplate(
373  current_tilt_value_id=ZwaveValueID(
374  property_="fibaro",
375  command_class=CommandClass.MANUFACTURER_PROPRIETARY,
376  endpoint=0,
377  property_key="venetianBlindsTilt",
378  ),
379  target_tilt_value_id=ZwaveValueID(
380  property_="fibaro",
381  command_class=CommandClass.MANUFACTURER_PROPRIETARY,
382  endpoint=0,
383  property_key="venetianBlindsTilt",
384  ),
385  ),
386  required_values=[
388  command_class={CommandClass.MANUFACTURER_PROPRIETARY},
389  property={"fibaro"},
390  property_key={"venetianBlindsTilt"},
391  )
392  ],
393  ),
394  # Fibaro Shutter Fibaro FGR223
395  # Combine both switch_multilevel endpoints into shutter_tilt
396  # if operating mode (151) is set to venetian blind (2)
398  platform=Platform.COVER,
399  hint="shutter_tilt",
400  manufacturer_id={0x010F},
401  product_id={0x1000, 0x1001},
402  product_type={0x0303},
403  primary_value=ZWaveValueDiscoverySchema(
404  command_class={CommandClass.SWITCH_MULTILEVEL},
405  property={CURRENT_VALUE_PROPERTY},
406  endpoint={1},
407  type={ValueType.NUMBER},
408  ),
409  data_template=CoverTiltDataTemplate(
410  current_tilt_value_id=ZwaveValueID(
411  property_=CURRENT_VALUE_PROPERTY,
412  command_class=CommandClass.SWITCH_MULTILEVEL,
413  endpoint=2,
414  ),
415  target_tilt_value_id=ZwaveValueID(
416  property_=TARGET_VALUE_PROPERTY,
417  command_class=CommandClass.SWITCH_MULTILEVEL,
418  endpoint=2,
419  ),
420  ),
421  required_values=[
423  command_class={CommandClass.CONFIGURATION},
424  property={151},
425  endpoint={0},
426  value={2},
427  )
428  ],
429  ),
430  # Fibaro Shutter Fibaro FGR223
431  # Disable endpoint 2 (slat),
432  # as these are either combined with endpoint one as shutter_tilt
433  # or it has no practical function.
434  # CC: Switch_Multilevel
436  platform=Platform.COVER,
437  hint="shutter",
438  manufacturer_id={0x010F},
439  product_id={0x1000, 0x1001},
440  product_type={0x0303},
441  primary_value=ZWaveValueDiscoverySchema(
442  command_class={CommandClass.SWITCH_MULTILEVEL},
443  property={CURRENT_VALUE_PROPERTY},
444  endpoint={2},
445  type={ValueType.NUMBER},
446  ),
447  entity_registry_enabled_default=False,
448  ),
449  # Fibaro Nice BiDi-ZWave (IBT4ZWAVE)
451  platform=Platform.COVER,
452  hint="gate",
453  manufacturer_id={0x0441},
454  product_id={0x1000},
455  product_type={0x2400},
456  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
457  required_values=[SWITCH_MULTILEVEL_TARGET_VALUE_SCHEMA],
458  ),
459  # Shelly Qubino Wave Shutter QNSH-001P10
460  # Combine both switch_multilevel endpoints into shutter_tilt
461  # if operating mode (71) is set to venetian blind (1)
463  platform=Platform.COVER,
464  hint="shutter_tilt",
465  manufacturer_id={0x0460},
466  product_id={0x0082},
467  product_type={0x0003},
468  primary_value=ZWaveValueDiscoverySchema(
469  command_class={CommandClass.SWITCH_MULTILEVEL},
470  property={CURRENT_VALUE_PROPERTY},
471  endpoint={1},
472  type={ValueType.NUMBER},
473  ),
474  data_template=CoverTiltDataTemplate(
475  current_tilt_value_id=ZwaveValueID(
476  property_=CURRENT_VALUE_PROPERTY,
477  command_class=CommandClass.SWITCH_MULTILEVEL,
478  endpoint=2,
479  ),
480  target_tilt_value_id=ZwaveValueID(
481  property_=TARGET_VALUE_PROPERTY,
482  command_class=CommandClass.SWITCH_MULTILEVEL,
483  endpoint=2,
484  ),
485  ),
486  required_values=[
488  command_class={CommandClass.CONFIGURATION},
489  property={71},
490  endpoint={0},
491  value={1},
492  )
493  ],
494  ),
495  # Shelly Qubino Wave Shutter QNSH-001P10
496  # Disable endpoint 2 (slat),
497  # as these are either combined with endpoint one as shutter_tilt
498  # or it has no practical function.
499  # CC: Switch_Multilevel
501  platform=Platform.COVER,
502  hint="shutter",
503  manufacturer_id={0x0460},
504  product_id={0x0082},
505  product_type={0x0003},
506  primary_value=ZWaveValueDiscoverySchema(
507  command_class={CommandClass.SWITCH_MULTILEVEL},
508  property={CURRENT_VALUE_PROPERTY},
509  endpoint={2},
510  type={ValueType.NUMBER},
511  ),
512  entity_registry_enabled_default=False,
513  ),
514  # Qubino flush shutter
516  platform=Platform.COVER,
517  hint="shutter",
518  manufacturer_id={0x0159},
519  product_id={0x0052, 0x0053},
520  product_type={0x0003},
521  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
522  ),
523  # Graber/Bali/Spring Fashion Covers
525  platform=Platform.COVER,
526  hint="blind",
527  manufacturer_id={0x026E},
528  product_id={0x5A31},
529  product_type={0x4353},
530  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
531  ),
532  # iBlinds v2 window blind motor
534  platform=Platform.COVER,
535  hint="blind",
536  manufacturer_id={0x0287},
537  product_id={0x000D},
538  product_type={0x0003},
539  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
540  ),
541  # Merten 507801 Connect Roller Shutter
543  platform=Platform.COVER,
544  hint="shutter",
545  manufacturer_id={0x007A},
546  product_id={0x0001},
547  product_type={0x8003},
548  primary_value=ZWaveValueDiscoverySchema(
549  command_class={CommandClass.SWITCH_MULTILEVEL},
550  property={CURRENT_VALUE_PROPERTY},
551  endpoint={0, 1},
552  type={ValueType.NUMBER},
553  ),
554  assumed_state=True,
555  ),
556  # Merten 507801 Connect Roller Shutter.
557  # Disable endpoint 2, as it has no practical function. CC: Switch_Multilevel
559  platform=Platform.COVER,
560  hint="shutter",
561  manufacturer_id={0x007A},
562  product_id={0x0001},
563  product_type={0x8003},
564  primary_value=ZWaveValueDiscoverySchema(
565  command_class={CommandClass.SWITCH_MULTILEVEL},
566  property={CURRENT_VALUE_PROPERTY},
567  endpoint={2},
568  type={ValueType.NUMBER},
569  ),
570  assumed_state=True,
571  entity_registry_enabled_default=False,
572  ),
573  # Merten 507801 Connect Roller Shutter.
574  # Disable endpoint 2, as it has no practical function. CC: Protection
576  platform=Platform.SELECT,
577  manufacturer_id={0x007A},
578  product_id={0x0001},
579  product_type={0x8003},
580  primary_value=ZWaveValueDiscoverySchema(
581  command_class={CommandClass.PROTECTION},
582  property={LOCAL_PROPERTY, RF_PROPERTY},
583  endpoint={2},
584  type={ValueType.NUMBER},
585  ),
586  entity_registry_enabled_default=False,
587  ),
588  # ZVIDAR Z-CM-V01 (SmartWings/Deyi WM25L/V Z-Wave Motor for Roller Shade)
590  platform=Platform.COVER,
591  hint="shade",
592  manufacturer_id={0x045A},
593  product_id={0x0507},
594  product_type={0x0904},
595  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
596  ),
597  # Vision Security ZL7432 In Wall Dual Relay Switch
599  platform=Platform.SWITCH,
600  manufacturer_id={0x0109},
601  product_id={0x1711, 0x1717},
602  product_type={0x2017},
603  primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
604  assumed_state=True,
605  ),
606  # Heatit Z-TRM6
608  platform=Platform.CLIMATE,
609  hint="dynamic_current_temp",
610  manufacturer_id={0x019B},
611  product_id={0x3001},
612  product_type={0x0030},
613  primary_value=ZWaveValueDiscoverySchema(
614  command_class={CommandClass.THERMOSTAT_MODE},
615  property={THERMOSTAT_MODE_PROPERTY},
616  type={ValueType.NUMBER},
617  ),
619  lookup_table={
620  # Floor sensor
621  "Floor": ZwaveValueID(
622  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
623  command_class=CommandClass.SENSOR_MULTILEVEL,
624  endpoint=4,
625  ),
626  # Internal sensor
627  "Internal": ZwaveValueID(
628  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
629  command_class=CommandClass.SENSOR_MULTILEVEL,
630  endpoint=2,
631  ),
632  # Internal with limit by floor sensor
633  "Internal with floor limit": ZwaveValueID(
634  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
635  command_class=CommandClass.SENSOR_MULTILEVEL,
636  endpoint=2,
637  ),
638  # External sensor (connected to device)
639  "External": ZwaveValueID(
640  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
641  command_class=CommandClass.SENSOR_MULTILEVEL,
642  endpoint=3,
643  ),
644  # External sensor (connected to device) with limit by floor sensor (2x sensors)
645  "External with floor limit": ZwaveValueID(
646  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
647  command_class=CommandClass.SENSOR_MULTILEVEL,
648  endpoint=3,
649  ),
650  # PWER - Power regulator mode (no sensor used).
651  # This mode is not supported by the climate entity.
652  # Heating is set by adjusting parameter 25.
653  # P25: Set % of time the relay should be active when using PWER mode.
654  # (30-minute duty cycle)
655  # Use the air temperature as current temperature in the climate entity
656  # as we have nothing else.
657  "Power regulator": ZwaveValueID(
658  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
659  command_class=CommandClass.SENSOR_MULTILEVEL,
660  endpoint=2,
661  ),
662  },
663  dependent_value=ZwaveValueID(
664  property_=2, command_class=CommandClass.CONFIGURATION, endpoint=0
665  ),
666  ),
667  ),
668  # Heatit Z-TRM3
670  platform=Platform.CLIMATE,
671  hint="dynamic_current_temp",
672  manufacturer_id={0x019B},
673  product_id={0x0203},
674  product_type={0x0003},
675  primary_value=ZWaveValueDiscoverySchema(
676  command_class={CommandClass.THERMOSTAT_MODE},
677  property={THERMOSTAT_MODE_PROPERTY},
678  type={ValueType.NUMBER},
679  ),
681  lookup_table={
682  # Internal Sensor
683  "A": ZwaveValueID(
684  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
685  command_class=CommandClass.SENSOR_MULTILEVEL,
686  endpoint=2,
687  ),
688  "AF": ZwaveValueID(
689  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
690  command_class=CommandClass.SENSOR_MULTILEVEL,
691  endpoint=2,
692  ),
693  # External Sensor
694  "A2": ZwaveValueID(
695  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
696  command_class=CommandClass.SENSOR_MULTILEVEL,
697  endpoint=3,
698  ),
699  "A2F": ZwaveValueID(
700  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
701  command_class=CommandClass.SENSOR_MULTILEVEL,
702  endpoint=3,
703  ),
704  # Floor sensor
705  "F": ZwaveValueID(
706  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
707  command_class=CommandClass.SENSOR_MULTILEVEL,
708  endpoint=4,
709  ),
710  },
711  dependent_value=ZwaveValueID(
712  property_=2, command_class=CommandClass.CONFIGURATION, endpoint=0
713  ),
714  ),
715  ),
716  # Heatit Z-TRM2fx
718  platform=Platform.CLIMATE,
719  hint="dynamic_current_temp",
720  manufacturer_id={0x019B},
721  product_id={0x0202},
722  product_type={0x0003},
723  firmware_version_range=FirmwareVersionRange(min="3.0"),
724  primary_value=ZWaveValueDiscoverySchema(
725  command_class={CommandClass.THERMOSTAT_MODE},
726  property={THERMOSTAT_MODE_PROPERTY},
727  type={ValueType.NUMBER},
728  ),
730  lookup_table={
731  # External Sensor
732  "A2": ZwaveValueID(
733  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
734  command_class=CommandClass.SENSOR_MULTILEVEL,
735  endpoint=2,
736  ),
737  "A2F": ZwaveValueID(
738  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
739  command_class=CommandClass.SENSOR_MULTILEVEL,
740  endpoint=2,
741  ),
742  # Floor sensor
743  "F": ZwaveValueID(
744  property_=THERMOSTAT_CURRENT_TEMP_PROPERTY,
745  command_class=CommandClass.SENSOR_MULTILEVEL,
746  endpoint=3,
747  ),
748  },
749  dependent_value=ZwaveValueID(
750  property_=2, command_class=CommandClass.CONFIGURATION, endpoint=0
751  ),
752  ),
753  ),
754  # FortrezZ SSA1/SSA2/SSA3
756  platform=Platform.SELECT,
757  hint="multilevel_switch",
758  manufacturer_id={0x0084},
759  product_id={0x0107, 0x0108, 0x010B, 0x0205},
760  product_type={0x0311, 0x0313, 0x0331, 0x0341, 0x0343},
761  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
762  data_template=BaseDiscoverySchemaDataTemplate(
763  {
764  0: "Off",
765  33: "Strobe ONLY",
766  66: "Siren ONLY",
767  99: "Siren & Strobe FULL Alarm",
768  },
769  ),
770  ),
771  # ====== START OF GENERIC MAPPING SCHEMAS =======
772  # locks
773  # Door Lock CC
775  platform=Platform.LOCK,
776  primary_value=DOOR_LOCK_CURRENT_MODE_SCHEMA,
777  allow_multi=True,
778  ),
780  platform=Platform.SELECT,
781  primary_value=DOOR_LOCK_CURRENT_MODE_SCHEMA,
782  hint="door_lock",
783  ),
784  # Only discover the Lock CC if the Door Lock CC isn't also present on the node
786  platform=Platform.LOCK,
787  primary_value=ZWaveValueDiscoverySchema(
788  command_class={CommandClass.LOCK},
789  property={LOCKED_PROPERTY},
790  type={ValueType.BOOLEAN},
791  ),
792  absent_values=[DOOR_LOCK_CURRENT_MODE_SCHEMA],
793  ),
794  # door lock door status
796  platform=Platform.BINARY_SENSOR,
797  hint="property",
798  primary_value=ZWaveValueDiscoverySchema(
799  command_class={
800  CommandClass.LOCK,
801  CommandClass.DOOR_LOCK,
802  },
803  property={DOOR_STATUS_PROPERTY},
804  type={ValueType.ANY},
805  ),
806  ),
807  # thermostat fan
809  platform=Platform.FAN,
810  hint="thermostat_fan",
811  primary_value=ZWaveValueDiscoverySchema(
812  command_class={CommandClass.THERMOSTAT_FAN_MODE},
813  property={THERMOSTAT_FAN_MODE_PROPERTY},
814  type={ValueType.NUMBER},
815  ),
816  entity_registry_enabled_default=False,
817  ),
818  # humidifier
819  # hygrostats supporting mode (and optional setpoint)
821  platform=Platform.HUMIDIFIER,
822  primary_value=ZWaveValueDiscoverySchema(
823  command_class={CommandClass.HUMIDITY_CONTROL_MODE},
824  property={HUMIDITY_CONTROL_MODE_PROPERTY},
825  type={ValueType.NUMBER},
826  ),
827  ),
828  # climate
829  # thermostats supporting mode (and optional setpoint)
831  platform=Platform.CLIMATE,
832  primary_value=ZWaveValueDiscoverySchema(
833  command_class={CommandClass.THERMOSTAT_MODE},
834  property={THERMOSTAT_MODE_PROPERTY},
835  type={ValueType.NUMBER},
836  ),
837  ),
838  # thermostats supporting setpoint only (and thus not mode)
840  platform=Platform.CLIMATE,
841  primary_value=ZWaveValueDiscoverySchema(
842  command_class={CommandClass.THERMOSTAT_SETPOINT},
843  property={THERMOSTAT_SETPOINT_PROPERTY},
844  type={ValueType.NUMBER},
845  ),
846  absent_values=[ # mode must not be present to prevent dupes
848  command_class={CommandClass.THERMOSTAT_MODE},
849  property={THERMOSTAT_MODE_PROPERTY},
850  type={ValueType.NUMBER},
851  ),
852  ],
853  ),
854  # binary sensors
855  # When CC is Sensor Binary and device class generic is Binary Sensor, entity should
856  # be enabled by default
858  platform=Platform.BINARY_SENSOR,
859  hint="boolean",
860  device_class_generic={"Binary Sensor"},
861  primary_value=ZWaveValueDiscoverySchema(
862  command_class={CommandClass.SENSOR_BINARY},
863  type={ValueType.BOOLEAN},
864  ),
865  ),
866  # Legacy binary sensors are phased out (replaced by notification sensors)
867  # Disable by default to not confuse users
869  platform=Platform.BINARY_SENSOR,
870  hint="boolean",
871  primary_value=ZWaveValueDiscoverySchema(
872  command_class={CommandClass.SENSOR_BINARY},
873  type={ValueType.BOOLEAN},
874  ),
875  entity_registry_enabled_default=False,
876  ),
878  platform=Platform.BINARY_SENSOR,
879  hint="boolean",
880  primary_value=ZWaveValueDiscoverySchema(
881  command_class={
882  CommandClass.BATTERY,
883  CommandClass.SENSOR_ALARM,
884  },
885  type={ValueType.BOOLEAN},
886  ),
887  ),
888  # binary sensor for Indicator CC
890  platform=Platform.BINARY_SENSOR,
891  hint="boolean",
892  primary_value=ZWaveValueDiscoverySchema(
893  command_class={CommandClass.INDICATOR},
894  type={ValueType.BOOLEAN},
895  readable=True,
896  writeable=False,
897  ),
898  entity_category=EntityCategory.DIAGNOSTIC,
899  ),
900  # generic text sensors
902  platform=Platform.SENSOR,
903  hint="string_sensor",
904  primary_value=ZWaveValueDiscoverySchema(
905  command_class={CommandClass.SENSOR_ALARM},
906  type={ValueType.STRING},
907  ),
908  ),
909  # generic numeric sensors
911  platform=Platform.SENSOR,
912  hint="numeric_sensor",
913  primary_value=ZWaveValueDiscoverySchema(
914  command_class={
915  CommandClass.BATTERY,
916  CommandClass.ENERGY_PRODUCTION,
917  CommandClass.SENSOR_ALARM,
918  CommandClass.SENSOR_MULTILEVEL,
919  },
920  type={ValueType.NUMBER},
921  ),
922  data_template=NumericSensorDataTemplate(),
923  ),
925  platform=Platform.SENSOR,
926  hint="numeric_sensor",
927  primary_value=ZWaveValueDiscoverySchema(
928  command_class={CommandClass.INDICATOR},
929  type={ValueType.NUMBER},
930  readable=True,
931  writeable=False,
932  ),
933  data_template=NumericSensorDataTemplate(),
934  entity_category=EntityCategory.DIAGNOSTIC,
935  ),
936  # Meter sensors for Meter CC
938  platform=Platform.SENSOR,
939  hint="meter",
940  primary_value=ZWaveValueDiscoverySchema(
941  command_class={
942  CommandClass.METER,
943  },
944  type={ValueType.NUMBER},
945  property={VALUE_PROPERTY},
946  ),
947  data_template=NumericSensorDataTemplate(),
948  ),
949  # number for Indicator CC (exclude property keys 3-5)
951  platform=Platform.NUMBER,
952  primary_value=ZWaveValueDiscoverySchema(
953  command_class={CommandClass.INDICATOR},
954  type={ValueType.NUMBER},
955  not_property_key={3, 4, 5},
956  readable=True,
957  writeable=True,
958  ),
959  entity_category=EntityCategory.CONFIG,
960  ),
961  # button for Indicator CC
963  platform=Platform.BUTTON,
964  primary_value=ZWaveValueDiscoverySchema(
965  command_class={CommandClass.INDICATOR},
966  type={ValueType.BOOLEAN},
967  readable=False,
968  writeable=True,
969  ),
970  entity_category=EntityCategory.CONFIG,
971  ),
972  # switch for Indicator CC
974  platform=Platform.SWITCH,
975  hint="indicator",
976  primary_value=ZWaveValueDiscoverySchema(
977  command_class={CommandClass.INDICATOR},
978  type={ValueType.BOOLEAN},
979  readable=True,
980  writeable=True,
981  ),
982  entity_category=EntityCategory.CONFIG,
983  ),
984  # binary switch
985  # barrier operator signaling states
987  platform=Platform.SWITCH,
988  hint="barrier_event_signaling_state",
989  primary_value=ZWaveValueDiscoverySchema(
990  command_class={CommandClass.BARRIER_OPERATOR},
991  property={SIGNALING_STATE_PROPERTY},
992  type={ValueType.NUMBER},
993  ),
994  ),
995  # cover
996  # window coverings
998  platform=Platform.COVER,
999  hint="window_covering",
1000  primary_value=WINDOW_COVERING_COVER_CURRENT_VALUE_SCHEMA,
1001  ),
1003  platform=Platform.COVER,
1004  hint="window_covering",
1005  primary_value=WINDOW_COVERING_SLAT_CURRENT_VALUE_SCHEMA,
1006  absent_values=[WINDOW_COVERING_COVER_CURRENT_VALUE_SCHEMA],
1007  ),
1009  platform=Platform.COVER,
1010  hint="multilevel_switch",
1011  device_class_generic={"Multilevel Switch"},
1012  device_class_specific={
1013  "Motor Control Class A",
1014  "Motor Control Class B",
1015  "Motor Control Class C",
1016  "Multiposition Motor",
1017  },
1018  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
1019  absent_values=[
1020  WINDOW_COVERING_COVER_CURRENT_VALUE_SCHEMA,
1021  WINDOW_COVERING_SLAT_CURRENT_VALUE_SCHEMA,
1022  ],
1023  ),
1024  # cover
1025  # motorized barriers
1027  platform=Platform.COVER,
1028  hint="motorized_barrier",
1029  primary_value=ZWaveValueDiscoverySchema(
1030  command_class={CommandClass.BARRIER_OPERATOR},
1031  property={CURRENT_STATE_PROPERTY},
1032  type={ValueType.NUMBER},
1033  ),
1034  required_values=[
1036  command_class={CommandClass.BARRIER_OPERATOR},
1037  property={TARGET_STATE_PROPERTY},
1038  type={ValueType.NUMBER},
1039  ),
1040  ],
1041  ),
1042  # fan
1044  platform=Platform.FAN,
1045  hint="fan",
1046  device_class_generic={"Multilevel Switch"},
1047  device_class_specific={"Fan Switch"},
1048  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
1049  required_values=[SWITCH_MULTILEVEL_TARGET_VALUE_SCHEMA],
1050  ),
1051  # number platform
1052  # valve control for thermostats
1054  platform=Platform.NUMBER,
1055  hint="Valve control",
1056  device_class_generic={"Thermostat"},
1057  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
1058  ),
1059  # Handle the different combinations of Binary Switch, Multilevel Switch and Color Switch
1060  # to create switches and/or (colored) lights. The goal is to:
1061  # - couple Color Switch CC with Multilevel Switch CC if possible
1062  # - couple Color Switch CC with Binary Switch CC as the first fallback
1063  # - use Color Switch CC standalone as the last fallback
1064  #
1065  # Multilevel Switch CC (+ Color Switch CC) -> Dimmable light with or without color support.
1067  platform=Platform.LIGHT,
1068  primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
1069  ),
1070  # Binary Switch CC when Multilevel Switch and Color Switch CC exist ->
1071  # On/Off switch, assign color to light entity instead
1073  platform=Platform.SWITCH,
1074  primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
1075  required_values=[
1076  SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
1077  COLOR_SWITCH_CURRENT_VALUE_SCHEMA,
1078  ],
1079  ),
1080  # Binary Switch CC and Color Switch CC ->
1081  # Colored light that uses Binary Switch CC for turning on/off.
1083  platform=Platform.LIGHT,
1084  hint="color_onoff",
1085  primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
1086  required_values=[COLOR_SWITCH_CURRENT_VALUE_SCHEMA],
1087  ),
1088  # Binary Switch CC without Color Switch CC -> On/Off switch
1090  platform=Platform.SWITCH,
1091  primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
1092  absent_values=[COLOR_SWITCH_CURRENT_VALUE_SCHEMA],
1093  ),
1094  # Colored light (legacy device) that can only be controlled through Color Switch CC.
1096  platform=Platform.LIGHT,
1097  hint="color_onoff",
1098  primary_value=COLOR_SWITCH_CURRENT_VALUE_SCHEMA,
1099  absent_values=[
1100  SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
1101  SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
1102  ],
1103  ),
1104  # light for Basic CC with target
1106  platform=Platform.LIGHT,
1107  primary_value=ZWaveValueDiscoverySchema(
1108  command_class={CommandClass.BASIC},
1109  type={ValueType.NUMBER},
1110  property={CURRENT_VALUE_PROPERTY},
1111  ),
1112  required_values=[
1114  command_class={CommandClass.BASIC},
1115  type={ValueType.NUMBER},
1116  property={TARGET_VALUE_PROPERTY},
1117  )
1118  ],
1119  ),
1120  # sensor for Basic CC without target
1122  platform=Platform.SENSOR,
1123  hint="numeric_sensor",
1124  primary_value=ZWaveValueDiscoverySchema(
1125  command_class={CommandClass.BASIC},
1126  type={ValueType.NUMBER},
1127  property={CURRENT_VALUE_PROPERTY},
1128  ),
1129  absent_values=[
1131  command_class={CommandClass.BASIC},
1132  type={ValueType.NUMBER},
1133  property={TARGET_VALUE_PROPERTY},
1134  )
1135  ],
1136  ),
1137  # sirens
1139  platform=Platform.SIREN,
1140  primary_value=SIREN_TONE_SCHEMA,
1141  ),
1142  # select
1143  # siren default tone
1145  platform=Platform.SELECT,
1146  hint="Default tone",
1147  primary_value=ZWaveValueDiscoverySchema(
1148  command_class={CommandClass.SOUND_SWITCH},
1149  property={DEFAULT_TONE_ID_PROPERTY},
1150  type={ValueType.NUMBER},
1151  ),
1152  required_values=[SIREN_TONE_SCHEMA],
1153  ),
1154  # number
1155  # siren default volume
1157  platform=Platform.NUMBER,
1158  hint="volume",
1159  primary_value=ZWaveValueDiscoverySchema(
1160  command_class={CommandClass.SOUND_SWITCH},
1161  property={DEFAULT_VOLUME_PROPERTY},
1162  type={ValueType.NUMBER},
1163  ),
1164  required_values=[SIREN_TONE_SCHEMA],
1165  ),
1166  # select
1167  # protection CC
1169  platform=Platform.SELECT,
1170  primary_value=ZWaveValueDiscoverySchema(
1171  command_class={CommandClass.PROTECTION},
1172  property={LOCAL_PROPERTY, RF_PROPERTY},
1173  type={ValueType.NUMBER},
1174  ),
1175  ),
1176  # button
1177  # Notification CC idle
1179  platform=Platform.BUTTON,
1180  hint="notification idle",
1181  primary_value=ZWaveValueDiscoverySchema(
1182  command_class={CommandClass.NOTIFICATION},
1183  type={ValueType.NUMBER},
1184  any_available_states={(0, "idle")},
1185  ),
1186  allow_multi=True,
1187  ),
1188  # event
1189  # stateful = False
1191  platform=Platform.EVENT,
1192  hint="stateless",
1193  primary_value=ZWaveValueDiscoverySchema(
1194  stateful=False,
1195  ),
1196  ),
1197  # button
1198  # Meter CC idle
1200  platform=Platform.BUTTON,
1201  hint="meter reset",
1202  primary_value=ZWaveValueDiscoverySchema(
1203  command_class={CommandClass.METER},
1204  property={RESET_METER_PROPERTY},
1205  type={ValueType.BOOLEAN},
1206  ),
1207  entity_category=EntityCategory.DIAGNOSTIC,
1208  ),
1210  platform=Platform.BINARY_SENSOR,
1211  hint="notification",
1212  primary_value=ZWaveValueDiscoverySchema(
1213  command_class={
1214  CommandClass.NOTIFICATION,
1215  },
1216  type={ValueType.NUMBER},
1217  ),
1218  # set allow-multi to true because some of the notification sensors
1219  # can not be mapped to a binary sensor and must be handled as a regular sensor
1220  allow_multi=True,
1221  ),
1222  # alarmType, alarmLevel (Notification CC)
1224  platform=Platform.SENSOR,
1225  hint="notification_alarm",
1226  primary_value=ZWaveValueDiscoverySchema(
1227  command_class={
1228  CommandClass.NOTIFICATION,
1229  },
1230  property={"alarmType", "alarmLevel"},
1231  type={ValueType.NUMBER},
1232  ),
1233  entity_registry_enabled_default=False,
1234  ),
1235  # fallback sensors within Notification CC
1237  platform=Platform.SENSOR,
1238  hint="notification",
1239  primary_value=ZWaveValueDiscoverySchema(
1240  command_class={
1241  CommandClass.NOTIFICATION,
1242  },
1243  type={ValueType.NUMBER},
1244  ),
1245  ),
1246 ]
1247 
1248 
1249 @callback
1250 def async_discover_node_values(
1251  node: ZwaveNode, device: DeviceEntry, discovered_value_ids: dict[str, set[str]]
1252 ) -> Generator[ZwaveDiscoveryInfo]:
1253  """Run discovery on ZWave node and return matching (primary) values."""
1254  for value in node.values.values():
1255  # We don't need to rediscover an already processed value_id
1256  if value.value_id not in discovered_value_ids[device.id]:
1257  yield from async_discover_single_value(value, device, discovered_value_ids)
1258 
1259 
1260 @callback
1261 def async_discover_single_value(
1262  value: ZwaveValue, device: DeviceEntry, discovered_value_ids: dict[str, set[str]]
1263 ) -> Generator[ZwaveDiscoveryInfo]:
1264  """Run discovery on a single ZWave value and return matching schema info."""
1265  for schema in DISCOVERY_SCHEMAS:
1266  # abort if attribute(s) already discovered
1267  if value.value_id in discovered_value_ids[device.id]:
1268  continue
1269 
1270  # check manufacturer_id, product_id, product_type
1271  if (
1272  (
1273  schema.manufacturer_id is not None
1274  and value.node.manufacturer_id not in schema.manufacturer_id
1275  )
1276  or (
1277  schema.product_id is not None
1278  and value.node.product_id not in schema.product_id
1279  )
1280  or (
1281  schema.product_type is not None
1282  and value.node.product_type not in schema.product_type
1283  )
1284  ):
1285  continue
1286 
1287  # check firmware_version_range
1288  if schema.firmware_version_range is not None and (
1289  (
1290  schema.firmware_version_range.min is not None
1291  and schema.firmware_version_range.min_ver
1292  > AwesomeVersion(value.node.firmware_version)
1293  )
1294  or (
1295  schema.firmware_version_range.max is not None
1296  and schema.firmware_version_range.max_ver
1297  < AwesomeVersion(value.node.firmware_version)
1298  )
1299  ):
1300  continue
1301 
1302  # check device_class_generic
1303  if schema.device_class_generic and (
1304  not value.node.device_class
1305  or not any(
1306  value.node.device_class.generic.label == val
1307  for val in schema.device_class_generic
1308  )
1309  ):
1310  continue
1311 
1312  # check device_class_specific
1313  if schema.device_class_specific and (
1314  not value.node.device_class
1315  or not any(
1316  value.node.device_class.specific.label == val
1317  for val in schema.device_class_specific
1318  )
1319  ):
1320  continue
1321 
1322  # check primary value
1323  if not check_value(value, schema.primary_value):
1324  continue
1325 
1326  # check additional required values
1327  if schema.required_values is not None and not all(
1328  any(
1329  check_value(val, val_scheme, primary_value=value)
1330  for val in value.node.values.values()
1331  )
1332  for val_scheme in schema.required_values
1333  ):
1334  continue
1335 
1336  # check for values that may not be present
1337  if schema.absent_values is not None and any(
1338  any(
1339  check_value(val, val_scheme, primary_value=value)
1340  for val in value.node.values.values()
1341  )
1342  for val_scheme in schema.absent_values
1343  ):
1344  continue
1345 
1346  # resolve helper data from template
1347  resolved_data = None
1348  additional_value_ids_to_watch = set()
1349  if schema.data_template:
1350  try:
1351  resolved_data = schema.data_template.resolve_data(value)
1352  except UnknownValueData as err:
1353  LOGGER.error(
1354  "Discovery for value %s on device '%s' (%s) will be skipped: %s",
1355  value,
1356  device.name_by_user or device.name,
1357  value.node,
1358  err,
1359  )
1360  continue
1361  additional_value_ids_to_watch = schema.data_template.value_ids_to_watch(
1362  resolved_data
1363  )
1364 
1365  # all checks passed, this value belongs to an entity
1366  yield ZwaveDiscoveryInfo(
1367  node=value.node,
1368  primary_value=value,
1369  assumed_state=schema.assumed_state,
1370  platform=schema.platform,
1371  platform_hint=schema.hint,
1372  platform_data_template=schema.data_template,
1373  platform_data=resolved_data,
1374  additional_value_ids_to_watch=additional_value_ids_to_watch,
1375  entity_registry_enabled_default=schema.entity_registry_enabled_default,
1376  entity_category=schema.entity_category,
1377  )
1378 
1379  # prevent re-discovery of the (primary) value if not allowed
1380  if not schema.allow_multi:
1381  discovered_value_ids[device.id].add(value.value_id)
1382 
1383  # prevent re-discovery of the (primary) value after all schemas have been checked
1384  discovered_value_ids[device.id].add(value.value_id)
1385 
1386  if value.command_class == CommandClass.CONFIGURATION:
1387  yield from async_discover_single_configuration_value(
1388  cast(ConfigurationValue, value)
1389  )
1390 
1391 
1392 @callback
1393 def async_discover_single_configuration_value(
1394  value: ConfigurationValue,
1395 ) -> Generator[ZwaveDiscoveryInfo]:
1396  """Run discovery on single Z-Wave configuration value and return schema matches."""
1397  if value.metadata.writeable and value.metadata.readable:
1398  if value.configuration_value_type == ConfigurationValueType.ENUMERATED:
1399  yield ZwaveDiscoveryInfo(
1400  node=value.node,
1401  primary_value=value,
1402  assumed_state=False,
1403  platform=Platform.SELECT,
1404  platform_hint="config_parameter",
1405  platform_data=None,
1406  additional_value_ids_to_watch=set(),
1407  entity_registry_enabled_default=False,
1408  )
1409  elif value.configuration_value_type in (
1410  ConfigurationValueType.RANGE,
1411  ConfigurationValueType.MANUAL_ENTRY,
1412  ):
1413  yield ZwaveDiscoveryInfo(
1414  node=value.node,
1415  primary_value=value,
1416  assumed_state=False,
1417  platform=Platform.NUMBER,
1418  platform_hint="config_parameter",
1419  platform_data=None,
1420  additional_value_ids_to_watch=set(),
1421  entity_registry_enabled_default=False,
1422  )
1423  elif value.configuration_value_type == ConfigurationValueType.BOOLEAN:
1424  yield ZwaveDiscoveryInfo(
1425  node=value.node,
1426  primary_value=value,
1427  assumed_state=False,
1428  platform=Platform.SWITCH,
1429  platform_hint="config_parameter",
1430  platform_data=None,
1431  additional_value_ids_to_watch=set(),
1432  entity_registry_enabled_default=False,
1433  )
1434  elif not value.metadata.writeable and value.metadata.readable:
1435  if value.configuration_value_type == ConfigurationValueType.BOOLEAN:
1436  yield ZwaveDiscoveryInfo(
1437  node=value.node,
1438  primary_value=value,
1439  assumed_state=False,
1440  platform=Platform.BINARY_SENSOR,
1441  platform_hint="config_parameter",
1442  platform_data=None,
1443  additional_value_ids_to_watch=set(),
1444  entity_registry_enabled_default=False,
1445  )
1446  else:
1447  yield ZwaveDiscoveryInfo(
1448  node=value.node,
1449  primary_value=value,
1450  assumed_state=False,
1451  platform=Platform.SENSOR,
1452  platform_hint="config_parameter",
1453  platform_data=None,
1454  additional_value_ids_to_watch=set(),
1455  entity_registry_enabled_default=False,
1456  )
1457 
1458 
1459 @callback
1460 def check_value(
1461  value: ZwaveValue,
1462  schema: ZWaveValueDiscoverySchema,
1463  primary_value: ZwaveValue | None = None,
1464 ) -> bool:
1465  """Check if value matches scheme."""
1466  # check command_class
1467  if (
1468  schema.command_class is not None
1469  and value.command_class not in schema.command_class
1470  ):
1471  return False
1472  # check endpoint
1473  if schema.endpoint is not None and value.endpoint not in schema.endpoint:
1474  return False
1475  # If the schema does not require an endpoint, make sure the value is on the
1476  # same endpoint as the primary value
1477  if (
1478  schema.endpoint is None
1479  and primary_value is not None
1480  and value.endpoint != primary_value.endpoint
1481  ):
1482  return False
1483  # check property
1484  if schema.property is not None and value.property_ not in schema.property:
1485  return False
1486  # check property_name
1487  if (
1488  schema.property_name is not None
1489  and value.property_name not in schema.property_name
1490  ):
1491  return False
1492  # check property_key
1493  if (
1494  schema.property_key is not None
1495  and value.property_key not in schema.property_key
1496  ):
1497  return False
1498  # check property_key against not_property_key set
1499  if (
1500  schema.not_property_key is not None
1501  and value.property_key in schema.not_property_key
1502  ):
1503  return False
1504  # check metadata_type
1505  if schema.type is not None and value.metadata.type not in schema.type:
1506  return False
1507  # check metadata_readable
1508  if schema.readable is not None and value.metadata.readable != schema.readable:
1509  return False
1510  # check metadata_writeable
1511  if schema.writeable is not None and value.metadata.writeable != schema.writeable:
1512  return False
1513  # check available states
1514  if (
1515  schema.any_available_states is not None
1516  and value.metadata.states is not None
1517  and not any(
1518  str(key) in value.metadata.states and value.metadata.states[str(key)] == val
1519  for key, val in schema.any_available_states
1520  )
1521  ):
1522  return False
1523  # check value
1524  if schema.value is not None and value.value not in schema.value:
1525  return False
1526  # check metadata_stateful
1527  if schema.stateful is not None and value.metadata.stateful != schema.stateful:
1528  return False
1529  return True
bool add(self, _T matcher)
Definition: match.py:185