zwave-js-server-python 0.28.0__py3-none-any.whl → 0.29.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
zwave_js_server/const.py CHANGED
@@ -1,11 +1,11 @@
1
1
  """Constants for the Z-Wave JS python library."""
2
2
  from enum import Enum, IntEnum
3
- from typing import Dict, List
3
+ from typing import Dict, List, Set, Type, Union
4
4
 
5
5
  # minimal server schema version we can handle
6
- MIN_SERVER_SCHEMA_VERSION = 7
6
+ MIN_SERVER_SCHEMA_VERSION = 8
7
7
  # max server schema version we can handle (and our code is compatible with)
8
- MAX_SERVER_SCHEMA_VERSION = 7
8
+ MAX_SERVER_SCHEMA_VERSION = 8
9
9
 
10
10
  VALUE_UNKNOWN = "unknown"
11
11
 
@@ -90,7 +90,6 @@ class LogLevel(str, Enum):
90
90
  class CommandClass(IntEnum):
91
91
  """Enum with all known CommandClasses."""
92
92
 
93
- ALARM = 113
94
93
  SENSOR_ALARM = 156
95
94
  SILENCE_ALARM = 157
96
95
  SWITCH_ALL = 39
@@ -265,6 +264,7 @@ THERMOSTAT_MODE_PROPERTY = "mode"
265
264
  THERMOSTAT_SETPOINT_PROPERTY = "setpoint"
266
265
  THERMOSTAT_OPERATING_STATE_PROPERTY = "state"
267
266
  THERMOSTAT_CURRENT_TEMP_PROPERTY = "Air temperature"
267
+ THERMOSTAT_HUMIDITY_PROPERTY = "Humidity"
268
268
 
269
269
 
270
270
  class ThermostatMode(IntEnum):
@@ -390,6 +390,14 @@ class BarrierState(IntEnum):
390
390
  OPEN = 255
391
391
 
392
392
 
393
+ COVER_OPEN_PROPERTY = "Open"
394
+ COVER_UP_PROPERTY = "Up"
395
+ COVER_ON_PROPERTY = "On"
396
+ COVER_CLOSE_PROPERTY = "Close"
397
+ COVER_DOWN_PROPERTY = "Down"
398
+ COVER_OFF_PROPERTY = "Off"
399
+
400
+
393
401
  class ColorComponent(IntEnum):
394
402
  """Enum with all (known/used) Color Switch CC colors."""
395
403
 
@@ -405,6 +413,23 @@ class ColorComponent(IntEnum):
405
413
  INDEX = 8
406
414
 
407
415
 
416
+ # Keys for the Color Switch CC combined colors value
417
+ # https://github.com/zwave-js/node-zwave-js/pull/1782
418
+ COLOR_SWITCH_COMBINED_RED = "red"
419
+ COLOR_SWITCH_COMBINED_GREEN = "green"
420
+ COLOR_SWITCH_COMBINED_BLUE = "blue"
421
+ COLOR_SWITCH_COMBINED_AMBER = "amber"
422
+ COLOR_SWITCH_COMBINED_CYAN = "cyan"
423
+ COLOR_SWITCH_COMBINED_PURPLE = "purple"
424
+ COLOR_SWITCH_COMBINED_WARM_WHITE = "warmWhite"
425
+ COLOR_SWITCH_COMBINED_COLD_WHITE = "coldWhite"
426
+
427
+ CURRENT_COLOR_PROPERTY = "currentColor"
428
+ TARGET_COLOR_PROPERTY = "targetColor"
429
+ TARGET_STATE_PROPERTY = "targetState"
430
+ TARGET_VALUE_PROPERTY = "targetValue"
431
+
432
+
408
433
  class ToneID(IntEnum):
409
434
  """Enum with all known Sound Switch CC tone IDs."""
410
435
 
@@ -414,13 +439,263 @@ class ToneID(IntEnum):
414
439
 
415
440
 
416
441
  class NodeStatus(IntEnum):
417
- """Enum with all Node status values.
418
-
419
- https://zwave-js.github.io/node-zwave-js/#/api/node?id=status
420
- """
442
+ """Enum with all Node status values."""
421
443
 
444
+ # https://zwave-js.github.io/node-zwave-js/#/api/node?id=status
422
445
  UNKNOWN = 0
423
446
  ASLEEP = 1
424
447
  AWAKE = 2
425
448
  DEAD = 3
426
449
  ALIVE = 4
450
+
451
+
452
+ CC_SPECIFIC_SCALE = "scale"
453
+
454
+
455
+ # Multilevel Sensor constants
456
+ CC_SPECIFIC_SENSOR_TYPE = "sensorType"
457
+
458
+
459
+ class MultilevelSensorType(IntEnum):
460
+ """Enum with all known multilevel sensor types."""
461
+
462
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/config/config/sensorTypes.json
463
+ AIR_TEMPERATURE = 1
464
+ GENERAL_PURPOSE = 2
465
+ ILLUMINANCE = 3
466
+ POWER = 4
467
+ HUMIDITY = 5
468
+ VELOCITY = 6
469
+ DIRECTION = 7
470
+ ATMOSPHERIC_PRESSURE = 8
471
+ BAROMETRIC_PRESSURE = 9
472
+ SOLAR_RADIATION = 10
473
+ DEW_POINT = 11
474
+ RAIN_RATE = 12
475
+ TIDE_LEVEL = 13
476
+ WEIGHT = 14
477
+ VOLTAGE = 15
478
+ CURRENT = 16
479
+ CARBON_DIOXIDE_LEVEL = 17
480
+ AIR_FLOW = 18
481
+ TANK_CAPACITY = 19
482
+ DISTANCE = 20
483
+ ANGLE_POSITION = 21
484
+ ROTATION = 22
485
+ WATER_TEMPERATURE = 23
486
+ SOIL_TEMPERATURE = 24
487
+ SEISMIC_INTENSITY = 25
488
+ SEISMIC_MAGNITUDE = 26
489
+ ULTRAVIOLET = 27
490
+ ELECTRICAL_RESISTIVITY = 28
491
+ ELECTRICAL_CONDUCTIVITY = 29
492
+ LOUDNESS = 30
493
+ MOISTURE = 31
494
+ FREQUENCY = 32
495
+ TIME = 33
496
+ TARGET_TEMPERATURE = 34
497
+ PARTICULATE_MATTER_25 = 35
498
+ FORMALDEHYDE_LEVEL = 36
499
+ RADON_CONCENTRATION = 37
500
+ METHANE_DENSITY = 38
501
+ VOLATILE_ORGANIC_COMPOUND_LEVEL = 39
502
+ CARBON_MONOXIDE_LEVEL = 40
503
+ SOIL_HUMIDITY = 41
504
+ SOIL_REACTIVITY = 42
505
+ SOIL_SALINITY = 43
506
+ HEART_RATE = 44
507
+ BLOOD_PRESSURE = 45
508
+ MUSCLE_MASS = 46
509
+ FAT_MASS = 47
510
+ BONE_MASS = 48
511
+ TOTAL_BODY_WATER = 49
512
+ BASIS_METABOLIC_RATE = 50
513
+ BODY_MASS_INDEX = 51
514
+ ACCELERATION_X_AXIS = 52
515
+ ACCELERATION_Y_AXIS = 53
516
+ ACCELERATION_Z_AXIS = 54
517
+ SMOKE_DENSITY = 55
518
+ WATER_FLOW = 56
519
+ WATER_PRESSURE = 57
520
+ RF_SIGNAL_STRENGTH = 58
521
+ PARTICULATE_MATTER_10 = 59
522
+ RESPIRATORY_RATE = 60
523
+ RELATIVE_MODULATION_LEVEL = 61
524
+ BOILER_WATER_TEMPERATURE = 62
525
+ DOMESTIC_HOT_WATER_TEMPERATURE = 63
526
+ OUTSIDE_TEMPERATURE = 64
527
+ EXHAUST_TEMPERATURE = 65
528
+ WATER_CHLORINE_LEVEL = 66
529
+ WATER_ACIDITY = 67
530
+ WATER_OXIDATION_REDUCTION_POTENTIAL = 68
531
+ HEART_RATE_LF_HF_RATIO = 69
532
+ MOTION_DIRECTION = 70
533
+ APPLIED_FORCE_ON_THE_SENSOR = 71
534
+ RETURN_AIR_TEMPERATURE = 72
535
+ SUPPLY_AIR_TEMPERATURE = 73
536
+ CONDENSER_COIL_TEMPERATURE = 74
537
+ EVAPORATOR_COIL_TEMPERATURE = 75
538
+ LIQUID_LINE_TEMPERATURE = 76
539
+ DISCHARGE_LINE_TEMPERATURE = 77
540
+ SUCTION_PRESSURE = 78
541
+ DISCHARGE_PRESSURE = 79
542
+ DEFROST_TEMPERATURE = 80
543
+ OZONE = 81
544
+ SULFUR_DIOXIDE = 82
545
+ NITROGEN_DIOXIDE = 83
546
+ AMMONIA = 84
547
+ LEAD = 85
548
+ PARTICULATE_MATTER_1 = 86
549
+
550
+
551
+ CO_SENSORS = {MultilevelSensorType.CARBON_MONOXIDE_LEVEL}
552
+ CO2_SENSORS = {MultilevelSensorType.CARBON_DIOXIDE_LEVEL}
553
+ CURRENT_SENSORS = {MultilevelSensorType.CURRENT}
554
+ ENERGY_SENSORS = {MultilevelSensorType.BASIS_METABOLIC_RATE}
555
+ HUMIDITY_SENSORS = {MultilevelSensorType.HUMIDITY}
556
+ ILLUMINANCE_SENSORS = {MultilevelSensorType.ILLUMINANCE}
557
+ POWER_SENSORS = {MultilevelSensorType.POWER}
558
+ PRESSURE_SENSORS = {
559
+ MultilevelSensorType.BLOOD_PRESSURE,
560
+ MultilevelSensorType.WATER_PRESSURE,
561
+ MultilevelSensorType.SUCTION_PRESSURE,
562
+ MultilevelSensorType.DISCHARGE_PRESSURE,
563
+ MultilevelSensorType.BAROMETRIC_PRESSURE,
564
+ MultilevelSensorType.ATMOSPHERIC_PRESSURE,
565
+ }
566
+ SIGNAL_STRENGTH_SENSORS = {MultilevelSensorType.RF_SIGNAL_STRENGTH}
567
+ TEMPERATURE_SENSORS = {
568
+ MultilevelSensorType.AIR_TEMPERATURE,
569
+ MultilevelSensorType.DEW_POINT,
570
+ MultilevelSensorType.WATER_TEMPERATURE,
571
+ MultilevelSensorType.SOIL_TEMPERATURE,
572
+ MultilevelSensorType.TARGET_TEMPERATURE,
573
+ MultilevelSensorType.BOILER_WATER_TEMPERATURE,
574
+ MultilevelSensorType.DOMESTIC_HOT_WATER_TEMPERATURE,
575
+ MultilevelSensorType.OUTSIDE_TEMPERATURE,
576
+ MultilevelSensorType.EXHAUST_TEMPERATURE,
577
+ MultilevelSensorType.RETURN_AIR_TEMPERATURE,
578
+ MultilevelSensorType.SUPPLY_AIR_TEMPERATURE,
579
+ MultilevelSensorType.CONDENSER_COIL_TEMPERATURE,
580
+ MultilevelSensorType.EVAPORATOR_COIL_TEMPERATURE,
581
+ MultilevelSensorType.LIQUID_LINE_TEMPERATURE,
582
+ MultilevelSensorType.DISCHARGE_LINE_TEMPERATURE,
583
+ MultilevelSensorType.DEFROST_TEMPERATURE,
584
+ }
585
+ TIMESTAMP_SENSORS = {MultilevelSensorType.TIME}
586
+ VOLTAGE_SENSORS = {
587
+ MultilevelSensorType.VOLTAGE,
588
+ MultilevelSensorType.WATER_OXIDATION_REDUCTION_POTENTIAL,
589
+ }
590
+
591
+
592
+ # Meter CC constants
593
+
594
+ CC_SPECIFIC_METER_TYPE = "meterType"
595
+ CC_SPECIFIC_RATE_TYPE = "rateType"
596
+
597
+ RESET_METER_CC_API = "reset"
598
+
599
+ # optional attributes when calling the Meter CC reset API.
600
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/commandclass/MeterCC.ts#L873-L881
601
+ RESET_METER_OPTION_TARGET_VALUE = TARGET_VALUE_PROPERTY
602
+ RESET_METER_OPTION_TYPE = "type"
603
+
604
+
605
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/config/config/meters.json
606
+ class MeterType(IntEnum):
607
+ """Enum with all known meter types."""
608
+
609
+ ELECTRIC = 1
610
+ GAS = 2
611
+ WATER = 3
612
+ HEATING = 4
613
+ COOLING = 5
614
+
615
+
616
+ class ElectricScale(IntEnum):
617
+ """Enum with all known electric meter scale values."""
618
+
619
+ KILOWATT_HOUR = 0
620
+ KILOVOLT_AMPERE_HOUR = 1
621
+ WATT = 2
622
+ PULSE = 3
623
+ VOLT = 4
624
+ AMPERE = 5
625
+ POWER_FACTOR = 6
626
+ KILOVOLT_AMPERE_REACTIVE = 7
627
+ KILOVOLT_AMPERE_REACTIVE_HOUR = 8
628
+
629
+
630
+ class GasScale(IntEnum):
631
+ """Enum with all known gas meter scale values."""
632
+
633
+ CUBIC_METER = 0
634
+ CUBIC_FEET = 1
635
+ PULSE_COUNT = 3
636
+
637
+
638
+ class WaterScale(IntEnum):
639
+ """Enum with all known water meter scale values."""
640
+
641
+ CUBIC_METER = 0
642
+ CUBIC_FEET = 1
643
+ US_GALLON = 2
644
+ PULSE_COUNT = 3
645
+
646
+
647
+ class HeatingScale(IntEnum):
648
+ """Enum with all known heating meter scale values."""
649
+
650
+ KILOWATT_HOUR = 0
651
+
652
+
653
+ CoolingScale = HeatingScale
654
+
655
+ MeterScaleType = Union[CoolingScale, ElectricScale, GasScale, HeatingScale, WaterScale]
656
+
657
+ METER_TYPE_TO_SCALE_ENUM_MAP: Dict[MeterType, Type[MeterScaleType]] = {
658
+ MeterType.ELECTRIC: ElectricScale,
659
+ MeterType.GAS: GasScale,
660
+ MeterType.WATER: WaterScale,
661
+ MeterType.HEATING: HeatingScale,
662
+ MeterType.COOLING: CoolingScale,
663
+ }
664
+
665
+ ENERGY_METER_TYPES: Set[MeterScaleType] = {
666
+ ElectricScale.KILOWATT_HOUR,
667
+ ElectricScale.KILOVOLT_AMPERE_HOUR,
668
+ ElectricScale.KILOVOLT_AMPERE_REACTIVE_HOUR,
669
+ HeatingScale.KILOWATT_HOUR,
670
+ CoolingScale.KILOWATT_HOUR,
671
+ }
672
+ POWER_METER_TYPES: Set[MeterScaleType] = {
673
+ ElectricScale.WATT,
674
+ ElectricScale.PULSE,
675
+ ElectricScale.KILOVOLT_AMPERE_REACTIVE,
676
+ }
677
+ POWER_FACTOR_METER_TYPES: Set[MeterScaleType] = {ElectricScale.POWER_FACTOR}
678
+ VOLTAGE_METER_TYPES: Set[MeterScaleType] = {ElectricScale.VOLT}
679
+ CURRENT_METER_TYPES: Set[MeterScaleType] = {ElectricScale.AMPERE}
680
+
681
+
682
+ class InclusionStrategy(IntEnum):
683
+ """Enum for all known inclusion strategies."""
684
+
685
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/controller/Inclusion.ts#L9-L46
686
+ DEFAULT = 0
687
+ SMART_START = 1
688
+ INSECURE = 2
689
+ SECURITY_S0 = 3
690
+ SECURITY_S2 = 4
691
+
692
+
693
+ class SecurityClass(IntEnum):
694
+ """Enum for all known security classes."""
695
+
696
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/core/src/security/SecurityClass.ts#L3-L17
697
+ NONE = -1
698
+ S2_UNAUTHENTICATED = 0
699
+ S2_AUTHENTICATED = 1
700
+ S2_ACCESS_CONTROL = 2
701
+ S0_LEGACY = 7
@@ -1,7 +1,10 @@
1
1
  """Exceptions for zwave-js-server."""
2
+ from typing import Optional, TYPE_CHECKING
2
3
 
4
+ from .const import CommandClass
3
5
 
4
- from typing import Optional
6
+ if TYPE_CHECKING:
7
+ from .model.value import Value
5
8
 
6
9
 
7
10
  class BaseZwaveJSServerError(Exception):
@@ -120,3 +123,34 @@ class BulkSetConfigParameterFailed(BaseZwaveJSServerError):
120
123
 
121
124
  Derived from another exception
122
125
  """
126
+
127
+
128
+ class InvalidCommandClass(BaseZwaveJSServerError):
129
+ """Exception raised when Zwave Value has an invalid command class."""
130
+
131
+ def __init__(self, value: "Value", command_class: CommandClass) -> None:
132
+ """Initialize an invalid Command Class error."""
133
+ self.value = value
134
+ self.command_class = command_class
135
+ super().__init__(
136
+ f"Value {value} does not match expected command class: {command_class}"
137
+ )
138
+
139
+
140
+ class UnknownValueData(BaseZwaveJSServerError):
141
+ """
142
+ Exception raised when Zwave Value has data that the library can't parse.
143
+
144
+ This can be caused by an upstream issue with the driver, or missing support in the
145
+ library.
146
+ """
147
+
148
+ def __init__(self, value: "Value", path: str) -> None:
149
+ """Initialize an unknown data error."""
150
+ self.value = value
151
+ self.path = path
152
+ super().__init__(
153
+ f"Value {value} has unknown data in the following location: {path}. "
154
+ "Please report this issue, it can be caused by either an upstream issue "
155
+ "with the driver or missing support for this data in the library"
156
+ )
@@ -1,6 +1,8 @@
1
1
  """Provide a model for the Z-Wave JS controller."""
2
- from typing import TYPE_CHECKING, Dict, List, Optional, TypedDict, cast
2
+ from dataclasses import dataclass
3
+ from typing import Literal, TYPE_CHECKING, Dict, List, Optional, TypedDict, Union, cast
3
4
 
5
+ from ..const import InclusionStrategy, SecurityClass
4
6
  from ..event import Event, EventBase
5
7
  from .association import Association, AssociationGroup
6
8
  from .node import Node
@@ -9,9 +11,41 @@ if TYPE_CHECKING:
9
11
  from ..client import Client
10
12
 
11
13
 
14
+ class InclusionGrantDataType(TypedDict):
15
+ """Representation of an inclusion grant data dict type."""
16
+
17
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/controller/Inclusion.ts#L48-L56
18
+ securityClasses: List[SecurityClass]
19
+ clientSideAuth: bool
20
+
21
+
22
+ @dataclass
23
+ class InclusionGrant:
24
+ """Representation of an inclusion grant."""
25
+
26
+ security_classes: List[SecurityClass]
27
+ client_side_auth: bool
28
+
29
+ def to_dict(self) -> InclusionGrantDataType:
30
+ """Return InclusionGrantDataType dict from self."""
31
+ return {
32
+ "securityClasses": self.security_classes,
33
+ "clientSideAuth": self.client_side_auth,
34
+ }
35
+
36
+ @classmethod
37
+ def from_dict(cls, data: InclusionGrantDataType) -> "InclusionGrant":
38
+ """Return InclusionGrant from InclusionGrantDataType dict."""
39
+ return cls(
40
+ [SecurityClass(sec_cls) for sec_cls in data["securityClasses"]],
41
+ data["clientSideAuth"],
42
+ )
43
+
44
+
12
45
  class ControllerStatisticsDataType(TypedDict):
13
46
  """Represent a controller statistics data dict type."""
14
47
 
48
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/controller/ControllerStatistics.ts#L20-L39
15
49
  messagesTX: int
16
50
  messagesRX: int
17
51
  messagesDroppedTX: int
@@ -244,14 +278,37 @@ class Controller(EventBase):
244
278
  return self._heal_network_progress
245
279
 
246
280
  async def async_begin_inclusion(
247
- self, include_non_secure: Optional[bool] = None
281
+ self,
282
+ inclusion_strategy: Union[
283
+ Literal[InclusionStrategy.SECURITY_S0],
284
+ Literal[InclusionStrategy.SECURITY_S2],
285
+ Literal[InclusionStrategy.INSECURE],
286
+ ],
248
287
  ) -> bool:
249
288
  """Send beginInclusion command to Controller."""
250
289
  data = await self.client.async_send_command(
251
290
  {
252
291
  "command": "controller.begin_inclusion",
253
- "includeNonSecure": include_non_secure,
254
- }
292
+ "options": {"inclusionStrategy": inclusion_strategy},
293
+ },
294
+ require_schema=8,
295
+ )
296
+ return cast(bool, data["success"])
297
+
298
+ async def async_begin_inclusion_default(
299
+ self,
300
+ force_security: Optional[bool] = None,
301
+ ) -> bool:
302
+ """Send beginInclusion command to Controller using Default inclusion method."""
303
+ data = await self.client.async_send_command(
304
+ {
305
+ "command": "controller.begin_inclusion",
306
+ "options": {
307
+ "inclusionStrategy": InclusionStrategy.DEFAULT,
308
+ "forceSecurity": force_security,
309
+ },
310
+ },
311
+ require_schema=8,
255
312
  )
256
313
  return cast(bool, data["success"])
257
314
 
@@ -283,15 +340,22 @@ class Controller(EventBase):
283
340
  )
284
341
 
285
342
  async def async_replace_failed_node(
286
- self, node_id: int, include_non_secure: Optional[bool] = None
343
+ self,
344
+ node_id: int,
345
+ inclusion_strategy: Union[
346
+ Literal[InclusionStrategy.SECURITY_S0],
347
+ Literal[InclusionStrategy.SECURITY_S2],
348
+ Literal[InclusionStrategy.INSECURE],
349
+ ],
287
350
  ) -> bool:
288
351
  """Send replaceFailedNode command to Controller."""
289
352
  data = await self.client.async_send_command(
290
353
  {
291
354
  "command": "controller.replace_failed_node",
292
355
  "nodeId": node_id,
293
- "includeNonSecure": include_non_secure,
294
- }
356
+ "options": {"inclusionStrategy": inclusion_strategy},
357
+ },
358
+ require_schema=8,
295
359
  )
296
360
  return cast(bool, data["success"])
297
361
 
@@ -438,6 +502,26 @@ class Controller(EventBase):
438
502
  )
439
503
  return cast(List[int], data["neighbors"])
440
504
 
505
+ async def async_grant_security_classes(
506
+ self, inclusion_grant: InclusionGrant
507
+ ) -> None:
508
+ """Send grantSecurityClasses command to Controller."""
509
+ await self.client.async_send_command(
510
+ {
511
+ "command": "controller.grant_security_classes",
512
+ "inclusionGrant": inclusion_grant.to_dict(),
513
+ }
514
+ )
515
+
516
+ async def async_validate_dsk_and_enter_pin(self, pin: str) -> None:
517
+ """Send validateDSKAndEnterPIN command to Controller."""
518
+ await self.client.async_send_command(
519
+ {
520
+ "command": "controller.validate_dsk_and_enter_pin",
521
+ "pin": pin,
522
+ }
523
+ )
524
+
441
525
  def receive_event(self, event: Event) -> None:
442
526
  """Receive an event."""
443
527
  if event.data["source"] == "node":
@@ -502,3 +586,12 @@ class Controller(EventBase):
502
586
  """Process a statistics updated event."""
503
587
  self._statistics.data.update(event.data["statistics"])
504
588
  event.data["statistics_updated"] = self.statistics
589
+
590
+ def handle_grant_security_classes(self, event: Event) -> None:
591
+ """Process a grant security classes event."""
592
+ event.data["requested_grant"] = InclusionGrant.from_dict(
593
+ event.data["requested"]
594
+ )
595
+
596
+ def handle_validate_dsk_and_enter_pin(self, event: Event) -> None:
597
+ """Process a validate dsk and enter pin event."""
@@ -38,10 +38,10 @@ class LogConfig:
38
38
  }
39
39
  return cast(LogConfigDataType, {k: v for k, v in data.items() if v is not None})
40
40
 
41
- @staticmethod
42
- def from_dict(data: LogConfigDataType) -> "LogConfig":
41
+ @classmethod
42
+ def from_dict(cls, data: LogConfigDataType) -> "LogConfig":
43
43
  """Return LogConfig from LogConfigDataType dict."""
44
- return LogConfig(
44
+ return cls(
45
45
  data.get("enabled"),
46
46
  LogLevel(data["level"]) if "level" in data else None,
47
47
  data.get("logToFile"),
@@ -1,7 +1,7 @@
1
1
  """Provide a model for the Z-Wave JS node."""
2
2
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, TypedDict, Union, cast
3
3
 
4
- from ..const import INTERVIEW_FAILED, CommandClass, NodeStatus
4
+ from ..const import INTERVIEW_FAILED, CommandClass, NodeStatus, SecurityClass
5
5
  from ..event import Event
6
6
  from ..exceptions import (
7
7
  FailedCommand,
@@ -42,6 +42,7 @@ if TYPE_CHECKING:
42
42
  class NodeStatisticsDataType(TypedDict):
43
43
  """Represent a node statistics data dict type."""
44
44
 
45
+ # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/node/NodeStatistics.ts#L21-L33
45
46
  commandsTX: int
46
47
  commandsRX: int
47
48
  commandsDroppedTX: int
@@ -511,6 +512,25 @@ class Node(Endpoint):
511
512
  )
512
513
  return cast(bool, data.get("responded", False))
513
514
 
515
+ async def async_has_security_class(self, security_class: SecurityClass) -> bool:
516
+ """Return whether node has the given security class."""
517
+ data = await self.async_send_command(
518
+ "has_security_class",
519
+ securityClass=security_class,
520
+ require_schema=8,
521
+ wait_for_result=True,
522
+ )
523
+ assert data
524
+ return cast(bool, data["hasSecurityClass"])
525
+
526
+ async def async_get_highest_security_class(self) -> SecurityClass:
527
+ """Get the highest security class that a node supports."""
528
+ data = await self.async_send_command(
529
+ "get_highest_security_class", require_schema=8, wait_for_result=True
530
+ )
531
+ assert data
532
+ return SecurityClass(data["highestSecurityClass"])
533
+
514
534
  def handle_wake_up(self, event: Event) -> None:
515
535
  """Process a node wake up event."""
516
536
  # pylint: disable=unused-argument
@@ -0,0 +1,49 @@
1
+ """Command Class specific utility functions for values."""
2
+ from ..const import (
3
+ CC_SPECIFIC_METER_TYPE,
4
+ CC_SPECIFIC_SCALE,
5
+ CC_SPECIFIC_SENSOR_TYPE,
6
+ METER_TYPE_TO_SCALE_ENUM_MAP,
7
+ CommandClass,
8
+ MeterScaleType,
9
+ MeterType,
10
+ MultilevelSensorType,
11
+ )
12
+ from ..exceptions import InvalidCommandClass, UnknownValueData
13
+ from ..model.value import Value
14
+
15
+
16
+ def get_meter_type(value: Value) -> MeterType:
17
+ """Get the MeterType for a given value."""
18
+ if value.command_class != CommandClass.METER:
19
+ raise InvalidCommandClass(value, CommandClass.METER)
20
+ try:
21
+ return MeterType(value.metadata.cc_specific[CC_SPECIFIC_METER_TYPE])
22
+ except ValueError:
23
+ raise UnknownValueData( # pylint: disable=raise-missing-from
24
+ value, f"metadata.cc_specific.{CC_SPECIFIC_METER_TYPE}"
25
+ )
26
+
27
+
28
+ def get_meter_scale_type(value: Value) -> MeterScaleType:
29
+ """Get the ScaleType for a given value."""
30
+ meter_type = get_meter_type(value)
31
+ scale_enum = METER_TYPE_TO_SCALE_ENUM_MAP[meter_type]
32
+ try:
33
+ return scale_enum(value.metadata.cc_specific[CC_SPECIFIC_SCALE])
34
+ except ValueError:
35
+ raise UnknownValueData( # pylint: disable=raise-missing-from
36
+ value, f"metadata.cc_specific.{CC_SPECIFIC_SCALE}"
37
+ )
38
+
39
+
40
+ def get_multilevel_sensor_type(value: Value) -> MultilevelSensorType:
41
+ """Get the MultilevelSensorType for a given value."""
42
+ if value.command_class != CommandClass.SENSOR_MULTILEVEL:
43
+ raise InvalidCommandClass(value, CommandClass.SENSOR_MULTILEVEL)
44
+ try:
45
+ return MultilevelSensorType(value.metadata.cc_specific[CC_SPECIFIC_SENSOR_TYPE])
46
+ except ValueError:
47
+ raise UnknownValueData( # pylint: disable=raise-missing-from
48
+ value, f"metadata.cc_specific.{CC_SPECIFIC_SENSOR_TYPE}"
49
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zwave-js-server-python
3
- Version: 0.28.0
3
+ Version: 0.29.0
4
4
  Summary: Python wrapper for zwave-js-server
5
5
  Home-page: https://github.com/home-assistant-libs/zwave-js-server-python
6
6
  Author: Home Assistant Team
@@ -1,35 +1,36 @@
1
1
  zwave_js_server/__init__.py,sha256=Ey3O4Tha56uU-M92oLJmQHupCJ7B9oZmxlQTo8pGUM8,45
2
2
  zwave_js_server/__main__.py,sha256=1gWC927sa3FwcA47ndmrRWW0HlfYVdiUarjEsccRF2k,3624
3
3
  zwave_js_server/client.py,sha256=kVaxlUfrj2izdwjbzgBZFVi0vZDZflkCtUDDBhugPYk,11946
4
- zwave_js_server/const.py,sha256=6SPXg7Qywi3SIUuRHGT5-CGNKOM3y1DS9bxdlFujzps,11366
4
+ zwave_js_server/const.py,sha256=V-kS0EkzPaqTDM0-Yr2yuNzguPma5uYAjw9AqpU7nQ8,19220
5
5
  zwave_js_server/dump.py,sha256=inv0Q-i0DbzEcbwXeLDiu29U2I248pPMyNUTQDp5wYY,1184
6
6
  zwave_js_server/event.py,sha256=0Aw3NzkzKMnhS7AxBh6BwAVYXRu4zDKeUDib9mHjj8w,1772
7
- zwave_js_server/exceptions.py,sha256=YTu7OTXsTnVxBVfjStPCMXRGgC2QVRD_Rz8Qx9OnUS8,3503
7
+ zwave_js_server/exceptions.py,sha256=mbuiWMwmVjmQrimhZROCguQFYQuFP93pubDJKs1FcVc,4741
8
8
  zwave_js_server/firmware.py,sha256=F5selNpOWWICuLs3L8zBjddnGKrEmip7LriijjL9pDA,878
9
9
  zwave_js_server/version.py,sha256=RDFP2kSU3cCVIQbgMWggdeDnvhtVsZzzZg9pOjH7CC8,364
10
10
  zwave_js_server/model/__init__.py,sha256=XfyKH8lxZ3CB7CbRkOr5sR-eV0GrklZiBtNPmlnCJ_8,123
11
11
  zwave_js_server/model/association.py,sha256=FsgKAha7mw6dpk7u7zdQoNEajTyiL0S5eOYcY4M2Rx4,529
12
12
  zwave_js_server/model/command_class.py,sha256=CPKZOJNr6T3Y-ShfFB0CDZOLmITtXm5p1cOHbnRHMaI,1118
13
- zwave_js_server/model/controller.py,sha256=CpTmJC95N4g4mc8PRNLNNTbNwl6RrO7sEsQzeEDgMlc,17279
13
+ zwave_js_server/model/controller.py,sha256=1eQfRwErjBrOtUYyTtTBSoqeWw6mSxO3XTWV8pi2wzw,20577
14
14
  zwave_js_server/model/device_class.py,sha256=XxiTRVxY-Dc5cVHfJQFc2IkUHEXRiLpL5-V49PkwMxg,1708
15
15
  zwave_js_server/model/device_config.py,sha256=gn3xMGs8efn3B1BQ31VsXOG2u-CdMFJeIKDqIyMPBj8,5882
16
16
  zwave_js_server/model/driver.py,sha256=IEmcukQw7r_Wrls_ZeyZ77RnnTKW9eopeDExOUoFm88,5166
17
17
  zwave_js_server/model/endpoint.py,sha256=EJ_178hg2O4A3oc2xUegLnv6mshv0zo1h6ZwZ1Ws0qo,4110
18
18
  zwave_js_server/model/firmware.py,sha256=ATSCz3fbCipthqfmTbw7C1bfeK4DhrWOiaGdxHNqPds,2315
19
- zwave_js_server/model/log_config.py,sha256=21_roDDPzxKuYl1TNiIJ0vhtPI2Zx6tzzAVwql-cbLM,1571
19
+ zwave_js_server/model/log_config.py,sha256=Qti1p8Y1T1-mXJJydqLAa9JPbglzIzAd34koftG6U0w,1569
20
20
  zwave_js_server/model/log_message.py,sha256=9HQnvVZkwg7V_ENn4YLylEoX6OkmU9EOWMmK9BCQ92k,2607
21
- zwave_js_server/model/node.py,sha256=OgT1BMOgrk_ttGKA9e7uknDjOh7Lk9fDmXBV9EDUxjo,22086
21
+ zwave_js_server/model/node.py,sha256=QlTlRfoJDjuwnq-uODjblXyma584M-0sRfI5pks9Jsk,22991
22
22
  zwave_js_server/model/notification.py,sha256=hflkoNjr6ZrS7D1rpdTmeVfi6h9L87EqGCVupgmBAZY,3689
23
23
  zwave_js_server/model/value.py,sha256=rpJNKYEzxfoZvUS4kRruRseGJCDOzVk04tpZHs34NCI,8450
24
24
  zwave_js_server/model/version.py,sha256=FTdCCyHW69NW4CXeeuNMx339SF0tV2ZipPt24eVcl2o,1000
25
25
  zwave_js_server/util/__init__.py,sha256=ArF1K885aW0GdVd7Lo8fl8Atf70gvsxO5Ra_WoJTG28,42
26
+ zwave_js_server/util/command_class.py,sha256=RQ9iJ1JbhYom1qc-FH25Hrk_Ef-WUyv6hoNBqXaOr6E,1822
26
27
  zwave_js_server/util/helpers.py,sha256=RfIqYFVARlGzvG5oIs1SIvbUkHT5JVjRX3Cs-5SzhdA,1475
27
28
  zwave_js_server/util/lock.py,sha256=m_bRvshaRHV0v_YBaCIrgVDUPLab6xFoXZxXuxknD5E,3806
28
29
  zwave_js_server/util/multicast.py,sha256=iZZ1BcbtH4WqMm6DoHqH_ft0AwdUNSIjU8Yu-QYeKFk,4303
29
30
  zwave_js_server/util/node.py,sha256=tYS9UaPs4UCD6Q0_yiRySOlFH-vjf5bdgozYhSAybTo,12314
30
- zwave_js_server_python-0.28.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
31
- zwave_js_server_python-0.28.0.dist-info/METADATA,sha256=Myet-nCZ-vLPlsfDfHozCdLId-W7SWePd8lI5hC-ZJw,1627
32
- zwave_js_server_python-0.28.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
33
- zwave_js_server_python-0.28.0.dist-info/entry_points.txt,sha256=_8Swg1yhKiBElo6k1fxIEd6E8uPz8PDxQpwQoZ29FZE,74
34
- zwave_js_server_python-0.28.0.dist-info/top_level.txt,sha256=-hwsl-i4Av5Op_yfOHC_OP56KPmzp_iVEkeohRIN5Ng,16
35
- zwave_js_server_python-0.28.0.dist-info/RECORD,,
31
+ zwave_js_server_python-0.29.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
32
+ zwave_js_server_python-0.29.0.dist-info/METADATA,sha256=RL7OTpRpFPtQkvWHGZjCjUaNeIbOVXOUVgfkY3qBhQA,1627
33
+ zwave_js_server_python-0.29.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
34
+ zwave_js_server_python-0.29.0.dist-info/entry_points.txt,sha256=_8Swg1yhKiBElo6k1fxIEd6E8uPz8PDxQpwQoZ29FZE,74
35
+ zwave_js_server_python-0.29.0.dist-info/top_level.txt,sha256=-hwsl-i4Av5Op_yfOHC_OP56KPmzp_iVEkeohRIN5Ng,16
36
+ zwave_js_server_python-0.29.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.36.2)
2
+ Generator: bdist_wheel (0.37.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5