zwave-js-server-python 0.35.0__tar.gz → 0.35.3__tar.gz
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-python-0.35.0 → zwave-js-server-python-0.35.3}/PKG-INFO +1 -1
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/setup.py +1 -1
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/event.py +1 -1
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/exceptions.py +0 -14
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/association.py +1 -1
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/controller/__init__.py +97 -53
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/device_config.py +2 -2
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/endpoint.py +18 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/log_message.py +2 -2
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/node/__init__.py +8 -3
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/node/data_model.py +2 -2
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server_python.egg-info/PKG-INFO +1 -1
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server_python.egg-info/SOURCES.txt +0 -3
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server_python.egg-info/top_level.txt +0 -1
- zwave-js-server-python-0.35.0/scripts/__init__.py +0 -1
- zwave-js-server-python-0.35.0/scripts/generate_multilevel_sensor_constants.py +0 -245
- zwave-js-server-python-0.35.0/scripts/run_mock_server.py +0 -385
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/LICENSE +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/README.md +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/setup.cfg +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/test/test_client.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/test/test_dump.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/test/test_event.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/test/test_firmware.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/test/test_main.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/test/test_version.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/__init__.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/__main__.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/client.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/__init__.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/__init__.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/barrier_operator.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/color_switch.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/entry_control.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/humidity_control.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/lock.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/meter.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/multilevel_sensor.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/multilevel_switch.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/notification.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/power_level.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/protection.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/sound_switch.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/thermostat.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/const/command_class/wake_up.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/dump.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/firmware.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/__init__.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/command_class.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/controller/data_model.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/controller/event_model.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/controller/inclusion_and_provisioning.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/controller/statistics.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/device_class.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/driver.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/firmware.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/log_config.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/node/event_model.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/node/health_check.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/node/statistics.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/notification.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/utils.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/value.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/version.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/__init__.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/command_class/__init__.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/command_class/meter.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/command_class/multilevel_sensor.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/helpers.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/lock.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/multicast.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/util/node.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/version.py +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server_python.egg-info/dependency_links.txt +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server_python.egg-info/entry_points.txt +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server_python.egg-info/not-zip-safe +0 -0
- {zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server_python.egg-info/requires.txt +0 -0
|
@@ -57,7 +57,7 @@ class EventBase:
|
|
|
57
57
|
|
|
58
58
|
def emit(self, event_name: str, data: dict) -> None:
|
|
59
59
|
"""Run all callbacks for an event."""
|
|
60
|
-
for listener in self._listeners.get(event_name, []):
|
|
60
|
+
for listener in self._listeners.get(event_name, []).copy():
|
|
61
61
|
listener(data)
|
|
62
62
|
|
|
63
63
|
def _handle_event_protocol(self, event: Event) -> None:
|
{zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/exceptions.py
RENAMED
|
@@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, Optional
|
|
|
4
4
|
from .const import CommandClass
|
|
5
5
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
|
-
from .event import Event
|
|
8
7
|
from .model.value import Value
|
|
9
8
|
|
|
10
9
|
|
|
@@ -157,16 +156,3 @@ class UnknownValueData(BaseZwaveJSServerError):
|
|
|
157
156
|
"upstream issue with the driver or missing support for this data in the "
|
|
158
157
|
"library"
|
|
159
158
|
)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
class NotificationHasUnsupportedCommandClass(BaseZwaveJSServerError):
|
|
163
|
-
"""Exception raised when notification is received for an unsupported CC."""
|
|
164
|
-
|
|
165
|
-
def __init__(self, event: "Event", command_class: CommandClass) -> None:
|
|
166
|
-
"""Initialize an invalid Command Class error."""
|
|
167
|
-
self.event_data = event.data
|
|
168
|
-
self.command_class = command_class
|
|
169
|
-
super().__init__(
|
|
170
|
-
"Notification received with unsupported command class "
|
|
171
|
-
f"{command_class.name}: {event.data}"
|
|
172
|
-
)
|
|
@@ -12,7 +12,7 @@ from ...const import (
|
|
|
12
12
|
)
|
|
13
13
|
from ...event import Event, EventBase
|
|
14
14
|
from ...util.helpers import convert_base64_to_bytes, convert_bytes_to_base64
|
|
15
|
-
from ..association import
|
|
15
|
+
from ..association import AssociationAddress, AssociationGroup
|
|
16
16
|
from ..node import Node
|
|
17
17
|
from .data_model import ControllerDataType
|
|
18
18
|
from .event_model import CONTROLLER_EVENT_MODEL_MAP
|
|
@@ -420,18 +420,21 @@ class Controller(EventBase):
|
|
|
420
420
|
return cast(bool, data["failed"])
|
|
421
421
|
|
|
422
422
|
async def async_get_association_groups(
|
|
423
|
-
self,
|
|
423
|
+
self, source: AssociationAddress
|
|
424
424
|
) -> Dict[int, AssociationGroup]:
|
|
425
425
|
"""Send getAssociationGroups command to Controller."""
|
|
426
|
+
source_data = {"nodeId": source.node_id}
|
|
427
|
+
if source.endpoint is not None:
|
|
428
|
+
source_data["endpoint"] = source.endpoint
|
|
426
429
|
data = await self.client.async_send_command(
|
|
427
430
|
{
|
|
428
431
|
"command": "controller.get_association_groups",
|
|
429
|
-
|
|
432
|
+
**source_data,
|
|
430
433
|
}
|
|
431
434
|
)
|
|
432
435
|
groups = {}
|
|
433
436
|
for key, group in data["groups"].items():
|
|
434
|
-
groups[key] = AssociationGroup(
|
|
437
|
+
groups[int(key)] = AssociationGroup(
|
|
435
438
|
max_nodes=group["maxNodes"],
|
|
436
439
|
is_lifeline=group["isLifeline"],
|
|
437
440
|
multi_channel=group["multiChannel"],
|
|
@@ -441,84 +444,125 @@ class Controller(EventBase):
|
|
|
441
444
|
)
|
|
442
445
|
return groups
|
|
443
446
|
|
|
444
|
-
async def async_get_associations(
|
|
447
|
+
async def async_get_associations(
|
|
448
|
+
self, source: AssociationAddress
|
|
449
|
+
) -> Dict[int, List[AssociationAddress]]:
|
|
445
450
|
"""Send getAssociations command to Controller."""
|
|
451
|
+
source_data = {"nodeId": source.node_id}
|
|
452
|
+
if source.endpoint is not None:
|
|
453
|
+
source_data["endpoint"] = source.endpoint
|
|
446
454
|
data = await self.client.async_send_command(
|
|
447
455
|
{
|
|
448
456
|
"command": "controller.get_associations",
|
|
449
|
-
|
|
457
|
+
**source_data,
|
|
450
458
|
}
|
|
451
459
|
)
|
|
452
460
|
associations = {}
|
|
453
|
-
for key,
|
|
454
|
-
associations[key] =
|
|
455
|
-
|
|
456
|
-
|
|
461
|
+
for key, association_addresses in data["associations"].items():
|
|
462
|
+
associations[int(key)] = [
|
|
463
|
+
AssociationAddress(
|
|
464
|
+
node_id=association_address["nodeId"],
|
|
465
|
+
endpoint=association_address.get("endpoint"),
|
|
466
|
+
)
|
|
467
|
+
for association_address in association_addresses
|
|
468
|
+
]
|
|
457
469
|
return associations
|
|
458
470
|
|
|
459
471
|
async def async_is_association_allowed(
|
|
460
|
-
self,
|
|
472
|
+
self, source: AssociationAddress, group: int, association: AssociationAddress
|
|
461
473
|
) -> bool:
|
|
462
474
|
"""Send isAssociationAllowed command to Controller."""
|
|
475
|
+
source_data = {"nodeId": source.node_id}
|
|
476
|
+
if source.endpoint is not None:
|
|
477
|
+
source_data["endpoint"] = source.endpoint
|
|
478
|
+
|
|
479
|
+
association_data = {"nodeId": association.node_id}
|
|
480
|
+
if association.endpoint is not None:
|
|
481
|
+
association_data["endpoint"] = association.endpoint
|
|
463
482
|
data = await self.client.async_send_command(
|
|
464
483
|
{
|
|
465
484
|
"command": "controller.is_association_allowed",
|
|
466
|
-
|
|
485
|
+
**source_data,
|
|
467
486
|
"group": group,
|
|
468
|
-
"association":
|
|
469
|
-
"nodeId": association.node_id,
|
|
470
|
-
"endpoint": association.endpoint,
|
|
471
|
-
},
|
|
487
|
+
"association": association_data,
|
|
472
488
|
}
|
|
473
489
|
)
|
|
474
490
|
return cast(bool, data["allowed"])
|
|
475
491
|
|
|
476
492
|
async def async_add_associations(
|
|
477
|
-
self,
|
|
493
|
+
self,
|
|
494
|
+
source: AssociationAddress,
|
|
495
|
+
group: int,
|
|
496
|
+
associations: List[AssociationAddress],
|
|
497
|
+
wait_for_result: bool = False,
|
|
478
498
|
) -> None:
|
|
479
499
|
"""Send addAssociations command to Controller."""
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
500
|
+
source_data = {"nodeId": source.node_id}
|
|
501
|
+
if source.endpoint is not None:
|
|
502
|
+
source_data["endpoint"] = source.endpoint
|
|
503
|
+
|
|
504
|
+
associations_data = []
|
|
505
|
+
for association in associations:
|
|
506
|
+
association_data = {"nodeId": association.node_id}
|
|
507
|
+
if association.endpoint is not None:
|
|
508
|
+
association_data["endpoint"] = association.endpoint
|
|
509
|
+
associations_data.append(association_data)
|
|
510
|
+
|
|
511
|
+
cmd = {
|
|
512
|
+
"command": "controller.add_associations",
|
|
513
|
+
**source_data,
|
|
514
|
+
"group": group,
|
|
515
|
+
"associations": associations_data,
|
|
516
|
+
}
|
|
517
|
+
if wait_for_result:
|
|
518
|
+
await self.client.async_send_command(cmd)
|
|
519
|
+
else:
|
|
520
|
+
await self.client.async_send_command_no_wait(cmd)
|
|
494
521
|
|
|
495
522
|
async def async_remove_associations(
|
|
496
|
-
self,
|
|
523
|
+
self,
|
|
524
|
+
source: AssociationAddress,
|
|
525
|
+
group: int,
|
|
526
|
+
associations: List[AssociationAddress],
|
|
527
|
+
wait_for_result: bool = False,
|
|
497
528
|
) -> None:
|
|
498
529
|
"""Send removeAssociations command to Controller."""
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
530
|
+
source_data = {"nodeId": source.node_id}
|
|
531
|
+
if source.endpoint is not None:
|
|
532
|
+
source_data["endpoint"] = source.endpoint
|
|
533
|
+
|
|
534
|
+
associations_data = []
|
|
535
|
+
for association in associations:
|
|
536
|
+
association_data = {"nodeId": association.node_id}
|
|
537
|
+
if association.endpoint is not None:
|
|
538
|
+
association_data["endpoint"] = association.endpoint
|
|
539
|
+
associations_data.append(association_data)
|
|
540
|
+
|
|
541
|
+
cmd = {
|
|
542
|
+
"command": "controller.remove_associations",
|
|
543
|
+
**source_data,
|
|
544
|
+
"group": group,
|
|
545
|
+
"associations": associations_data,
|
|
546
|
+
}
|
|
547
|
+
if wait_for_result:
|
|
548
|
+
await self.client.async_send_command(cmd)
|
|
549
|
+
else:
|
|
550
|
+
await self.client.async_send_command_no_wait(cmd)
|
|
513
551
|
|
|
514
|
-
async def async_remove_node_from_all_associations(
|
|
552
|
+
async def async_remove_node_from_all_associations(
|
|
553
|
+
self,
|
|
554
|
+
node_id: int,
|
|
555
|
+
wait_for_result: bool = False,
|
|
556
|
+
) -> None:
|
|
515
557
|
"""Send removeNodeFromAllAssociations command to Controller."""
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
558
|
+
cmd = {
|
|
559
|
+
"command": "controller.remove_node_from_all_associations",
|
|
560
|
+
"nodeId": node_id,
|
|
561
|
+
}
|
|
562
|
+
if wait_for_result:
|
|
563
|
+
await self.client.async_send_command(cmd)
|
|
564
|
+
else:
|
|
565
|
+
await self.client.async_send_command_no_wait(cmd)
|
|
522
566
|
|
|
523
567
|
async def async_get_node_neighbors(self, node_id: int) -> List[int]:
|
|
524
568
|
"""Send getNodeNeighbors command to Controller to get node's neighbors."""
|
|
@@ -3,7 +3,7 @@ Model for a Zwave Node's device config.
|
|
|
3
3
|
|
|
4
4
|
https://zwave-js.github.io/node-zwave-js/#/api/node?id=deviceconfig
|
|
5
5
|
"""
|
|
6
|
-
from typing import Dict, List, Literal, Optional, Union
|
|
6
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
7
7
|
|
|
8
8
|
from ..const import TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED
|
|
9
9
|
|
|
@@ -138,7 +138,7 @@ class DeviceConfigDataType(TypedDict, total=False):
|
|
|
138
138
|
paramInformation: Dict[str, dict]
|
|
139
139
|
supportsZWavePlus: bool
|
|
140
140
|
proprietary: dict
|
|
141
|
-
compat: Dict[str,
|
|
141
|
+
compat: Dict[str, Any]
|
|
142
142
|
metadata: DeviceMetadataDataType
|
|
143
143
|
isEmbedded: bool
|
|
144
144
|
|
{zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/endpoint.py
RENAMED
|
@@ -49,6 +49,24 @@ class Endpoint(EventBase):
|
|
|
49
49
|
self.values: Dict[str, Union[ConfigurationValue, Value]] = {}
|
|
50
50
|
self.update(data, values)
|
|
51
51
|
|
|
52
|
+
def __repr__(self) -> str:
|
|
53
|
+
"""Return the representation."""
|
|
54
|
+
return f"{type(self).__name__}(node_id={self.node_id}, index={self.index})"
|
|
55
|
+
|
|
56
|
+
def __hash__(self) -> int:
|
|
57
|
+
"""Return the hash."""
|
|
58
|
+
return hash((self.client.driver, self.node_id, self.index))
|
|
59
|
+
|
|
60
|
+
def __eq__(self, other: object) -> bool:
|
|
61
|
+
"""Return whether this instance equals another."""
|
|
62
|
+
if not isinstance(other, Endpoint):
|
|
63
|
+
return False
|
|
64
|
+
return (
|
|
65
|
+
self.client.driver == other.client.driver
|
|
66
|
+
and self.node_id == other.node_id
|
|
67
|
+
and self.index == other.index
|
|
68
|
+
)
|
|
69
|
+
|
|
52
70
|
@property
|
|
53
71
|
def node_id(self) -> int:
|
|
54
72
|
"""Return node ID property."""
|
{zwave-js-server-python-0.35.0 → zwave-js-server-python-0.35.3}/zwave_js_server/model/log_message.py
RENAMED
|
@@ -13,7 +13,7 @@ class LogMessageContextDataType(TypedDict, total=False):
|
|
|
13
13
|
"""Represent a log message context data dict type."""
|
|
14
14
|
|
|
15
15
|
source: Literal["config", "serial", "controller", "driver"] # required
|
|
16
|
-
type: Literal["value", "node"]
|
|
16
|
+
type: Literal["controller", "value", "node"]
|
|
17
17
|
nodeId: int
|
|
18
18
|
header: str
|
|
19
19
|
direction: Literal["inbound", "outbound", "none"]
|
|
@@ -38,7 +38,7 @@ class LogMessageContext:
|
|
|
38
38
|
return self.data["source"]
|
|
39
39
|
|
|
40
40
|
@property
|
|
41
|
-
def type(self) -> Optional[Literal["value", "node"]]:
|
|
41
|
+
def type(self) -> Optional[Literal["controller", "value", "node"]]:
|
|
42
42
|
"""Return the object type for the log message if applicable."""
|
|
43
43
|
return self.data.get("type")
|
|
44
44
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Provide a model for the Z-Wave JS node."""
|
|
2
|
+
import logging
|
|
2
3
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
|
|
3
4
|
|
|
4
5
|
from ...const import (
|
|
@@ -12,7 +13,6 @@ from ...event import Event, EventBase
|
|
|
12
13
|
from ...exceptions import (
|
|
13
14
|
FailedCommand,
|
|
14
15
|
NotFoundError,
|
|
15
|
-
NotificationHasUnsupportedCommandClass,
|
|
16
16
|
UnparseableValue,
|
|
17
17
|
UnwriteableValue,
|
|
18
18
|
)
|
|
@@ -58,6 +58,9 @@ if TYPE_CHECKING:
|
|
|
58
58
|
from ...client import Client
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
_LOGGER = logging.getLogger(__package__)
|
|
62
|
+
|
|
63
|
+
|
|
61
64
|
class Node(EventBase):
|
|
62
65
|
"""Represent a Z-Wave JS node."""
|
|
63
66
|
|
|
@@ -152,7 +155,9 @@ class Node(EventBase):
|
|
|
152
155
|
@property
|
|
153
156
|
def is_secure(self) -> Optional[bool]:
|
|
154
157
|
"""Return the is_secure."""
|
|
155
|
-
|
|
158
|
+
if (is_secure := self.data.get("isSecure")) == "unknown":
|
|
159
|
+
return None
|
|
160
|
+
return is_secure
|
|
156
161
|
|
|
157
162
|
@property
|
|
158
163
|
def protocol_version(self) -> Optional[int]:
|
|
@@ -787,7 +792,7 @@ class Node(EventBase):
|
|
|
787
792
|
self, cast(PowerLevelNotificationDataType, event.data)
|
|
788
793
|
)
|
|
789
794
|
else:
|
|
790
|
-
|
|
795
|
+
_LOGGER.info("Unhandled notification command class: %s", command_class.name)
|
|
791
796
|
|
|
792
797
|
def handle_firmware_update_progress(self, event: Event) -> None:
|
|
793
798
|
"""Process a node firmware update progress event."""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""Data model for a Z-Wave JS node."""
|
|
2
|
-
from typing import List, Optional, Union
|
|
2
|
+
from typing import List, Literal, Optional, Union
|
|
3
3
|
|
|
4
4
|
from ...const import TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED
|
|
5
5
|
from ..device_class import DeviceClassDataType
|
|
@@ -33,7 +33,7 @@ class NodeDataType(TypedDict, total=False):
|
|
|
33
33
|
isRouting: bool
|
|
34
34
|
maxDataRate: int
|
|
35
35
|
supportedDataRates: List[int]
|
|
36
|
-
isSecure: bool
|
|
36
|
+
isSecure: Union[bool, Literal["unknown"]]
|
|
37
37
|
supportsBeaming: bool
|
|
38
38
|
supportsSecurity: bool
|
|
39
39
|
protocolVersion: int
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Scripts module."""
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
"""Script to generate Multilevel Sensor CC constants."""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import json
|
|
5
|
-
import pathlib
|
|
6
|
-
import re
|
|
7
|
-
import subprocess
|
|
8
|
-
from collections import defaultdict
|
|
9
|
-
from typing import Callable, List
|
|
10
|
-
|
|
11
|
-
import requests
|
|
12
|
-
from slugify import slugify
|
|
13
|
-
|
|
14
|
-
GITHUB_PROJECT = "zwave-js/node-zwave-js"
|
|
15
|
-
BRANCH_NAME = "master"
|
|
16
|
-
SENSOR_TYPES_FILE_PATH = "packages/config/config/sensorTypes.json"
|
|
17
|
-
DEFAULT_SCALES_FILE_PATH = "packages/config/config/scales.json"
|
|
18
|
-
|
|
19
|
-
CONST_FILE_PATH = pathlib.Path(__file__).parent.joinpath(
|
|
20
|
-
"../zwave_js_server/const/command_class/multilevel_sensor.py"
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def remove_comments(text: str) -> str:
|
|
25
|
-
"""Remove comments from a JSON string."""
|
|
26
|
-
return "\n".join(
|
|
27
|
-
line for line in text.split("\n") if not line.strip().startswith("//")
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def remove_paranthesis(text: str) -> str:
|
|
32
|
-
"""Remove text in paranethesis from a string."""
|
|
33
|
-
return re.sub(r"\([^)]*\)", "", text)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def enum_name_format(name: str, should_remove_paranthesis: bool) -> str:
|
|
37
|
-
"""Convert sensor/scale name to enum format."""
|
|
38
|
-
if should_remove_paranthesis:
|
|
39
|
-
name = remove_paranthesis(name)
|
|
40
|
-
return slugify(name, separator="_").upper()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def normalize_name(name: str) -> str:
|
|
44
|
-
"""Convert a sensor/scale name into a normalized name."""
|
|
45
|
-
return enum_name_format(name, True).replace("_", " ").title()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def format_for_class_name(name: str) -> str:
|
|
49
|
-
"""Convert sensor/scale name to class name format."""
|
|
50
|
-
return f"{normalize_name(name).replace(' ', '')}Scale"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def normalize_scale_definition(scale_definitions: dict[str, dict]) -> dict[str, int]:
|
|
54
|
-
"""Convert a scales definition dictionary into a normalized dictionary."""
|
|
55
|
-
scale_def_ = {}
|
|
56
|
-
for scale_id, scale_props in scale_definitions.items():
|
|
57
|
-
scale_id = int(scale_id, 16)
|
|
58
|
-
scale_name_ = enum_name_format(scale_props["label"], True)
|
|
59
|
-
scale_def_[scale_name_] = scale_id
|
|
60
|
-
|
|
61
|
-
return dict(sorted(scale_def_.items(), key=lambda kv: kv[0]))
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
sensor_types = json.loads(
|
|
65
|
-
remove_comments(
|
|
66
|
-
requests.get(
|
|
67
|
-
(
|
|
68
|
-
f"https://raw.githubusercontent.com/{GITHUB_PROJECT}/{BRANCH_NAME}/"
|
|
69
|
-
f"{SENSOR_TYPES_FILE_PATH}"
|
|
70
|
-
)
|
|
71
|
-
).text
|
|
72
|
-
)
|
|
73
|
-
)
|
|
74
|
-
default_scales = json.loads(
|
|
75
|
-
remove_comments(
|
|
76
|
-
requests.get(
|
|
77
|
-
(
|
|
78
|
-
f"https://raw.githubusercontent.com/{GITHUB_PROJECT}/{BRANCH_NAME}/"
|
|
79
|
-
f"{DEFAULT_SCALES_FILE_PATH}"
|
|
80
|
-
)
|
|
81
|
-
).text
|
|
82
|
-
)
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
scales = {
|
|
86
|
-
normalize_name(scale_name): normalize_scale_definition(scale_def)
|
|
87
|
-
for scale_name, scale_def in default_scales.items()
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
sensors = {}
|
|
91
|
-
for sensor_id, sensor_props in sensor_types.items():
|
|
92
|
-
sensor_id = int(sensor_id, 16)
|
|
93
|
-
scale_def = sensor_props["scales"]
|
|
94
|
-
remove_paranthesis_ = True
|
|
95
|
-
if sensor_id in (87, 88):
|
|
96
|
-
remove_paranthesis_ = False
|
|
97
|
-
sensor_name = enum_name_format(sensor_props["label"], remove_paranthesis_)
|
|
98
|
-
sensors[sensor_name] = {"id": sensor_id}
|
|
99
|
-
if isinstance(scale_def, str):
|
|
100
|
-
sensors[sensor_name]["scale"] = normalize_name(
|
|
101
|
-
scale_def.replace("$SCALES:", "")
|
|
102
|
-
)
|
|
103
|
-
else:
|
|
104
|
-
scales[sensor_name] = normalize_scale_definition(scale_def)
|
|
105
|
-
sensors[sensor_name]["scale"] = normalize_name(sensor_name)
|
|
106
|
-
|
|
107
|
-
scales = dict(sorted(scales.items(), key=lambda kv: kv[0]))
|
|
108
|
-
sensors = dict(sorted(sensors.items(), key=lambda kv: kv[0]))
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def generate_int_enum_class_definition(
|
|
112
|
-
class_name: str,
|
|
113
|
-
enum_dict: dict[str, str | dict],
|
|
114
|
-
enum_ref_url: str | None = None,
|
|
115
|
-
get_id_func: Callable | None = None,
|
|
116
|
-
docstring_info: str = "",
|
|
117
|
-
) -> List[str]:
|
|
118
|
-
"""Generate an IntEnum class definition as an array of lines of string."""
|
|
119
|
-
class_def = []
|
|
120
|
-
class_def.append(f"class {class_name}(IntEnum):")
|
|
121
|
-
docstring = (
|
|
122
|
-
f'"""Enum for known {docstring_info} multilevel sensor types."""'.replace(
|
|
123
|
-
" ", " "
|
|
124
|
-
)
|
|
125
|
-
)
|
|
126
|
-
class_def.append(f" {docstring}")
|
|
127
|
-
if enum_ref_url:
|
|
128
|
-
class_def.append(f" # {enum_ref_url}")
|
|
129
|
-
for enum_name, enum_id in enum_dict.items():
|
|
130
|
-
if get_id_func:
|
|
131
|
-
enum_id = get_id_func(enum_id)
|
|
132
|
-
class_def.append(f" {enum_name} = {enum_id}")
|
|
133
|
-
return class_def
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
SENSOR_TYPE_URL = (
|
|
137
|
-
f"https://github.com/{GITHUB_PROJECT}/blob/{BRANCH_NAME}/{SENSOR_TYPES_FILE_PATH}"
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
lines = [
|
|
141
|
-
'"""Constants for the Multilevel Sensor CC."""',
|
|
142
|
-
"",
|
|
143
|
-
"# ----------------------------------------------------------------------------------- #",
|
|
144
|
-
"# **BEGINNING OF AUTOGENERATED CONTENT** (TO ADD ADDITIONAL MANUAL CONTENT, LOOK FOR #",
|
|
145
|
-
'# THE "END OF AUTOGENERATED CONTENT" COMMENT BLOCK AND ADD YOUR CODE BELOW IT) #',
|
|
146
|
-
"# ----------------------------------------------------------------------------------- #",
|
|
147
|
-
"",
|
|
148
|
-
"from enum import IntEnum",
|
|
149
|
-
"from typing import Dict, Set, Type, Union",
|
|
150
|
-
'CC_SPECIFIC_SCALE = "scale"',
|
|
151
|
-
'CC_SPECIFIC_SENSOR_TYPE = "sensorType"',
|
|
152
|
-
]
|
|
153
|
-
|
|
154
|
-
lines.extend(
|
|
155
|
-
generate_int_enum_class_definition(
|
|
156
|
-
"MultilevelSensorType",
|
|
157
|
-
sensors,
|
|
158
|
-
SENSOR_TYPE_URL,
|
|
159
|
-
get_id_func=lambda x: x["id"],
|
|
160
|
-
)
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
unit_name_to_enum_map = defaultdict(list)
|
|
164
|
-
for scale_name, scale_dict in scales.items():
|
|
165
|
-
lines.extend(
|
|
166
|
-
generate_int_enum_class_definition(
|
|
167
|
-
format_for_class_name(scale_name),
|
|
168
|
-
scale_dict,
|
|
169
|
-
SENSOR_TYPE_URL,
|
|
170
|
-
docstring_info=f"scales for {scale_name}",
|
|
171
|
-
)
|
|
172
|
-
)
|
|
173
|
-
for unit_name in scale_dict.keys():
|
|
174
|
-
unit_name_to_enum_map[unit_name].append(
|
|
175
|
-
f"{format_for_class_name(scale_name)}.{unit_name}"
|
|
176
|
-
)
|
|
177
|
-
unit_name_to_enum_map = dict(
|
|
178
|
-
sorted(unit_name_to_enum_map.items(), key=lambda kv: kv[0])
|
|
179
|
-
)
|
|
180
|
-
for unit_name, enum_list in unit_name_to_enum_map.items():
|
|
181
|
-
unit_name_to_enum_map[unit_name] = sorted(enum_list)
|
|
182
|
-
|
|
183
|
-
scale_class_names = [format_for_class_name(scale_name) for scale_name in scales]
|
|
184
|
-
lines.extend(
|
|
185
|
-
[f"MultilevelSensorScaleType = Union[{', '.join(sorted(scale_class_names))}]", ""]
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
multilevel_sensor_type_to_scale_map_line = (
|
|
189
|
-
"MULTILEVEL_SENSOR_TYPE_TO_SCALE_MAP: Dict[MultilevelSensorType, "
|
|
190
|
-
"Type[MultilevelSensorScaleType]] = {"
|
|
191
|
-
)
|
|
192
|
-
for sensor_name, sensor_def in sensors.items():
|
|
193
|
-
multilevel_sensor_type_to_scale_map_line += (
|
|
194
|
-
f" MultilevelSensorType.{sensor_name}: "
|
|
195
|
-
f"{format_for_class_name(sensor_def['scale'])},"
|
|
196
|
-
)
|
|
197
|
-
multilevel_sensor_type_to_scale_map_line += "}"
|
|
198
|
-
lines.append(multilevel_sensor_type_to_scale_map_line)
|
|
199
|
-
lines.append("")
|
|
200
|
-
|
|
201
|
-
for unit_name, unit_enums in unit_name_to_enum_map.items():
|
|
202
|
-
lines.append(
|
|
203
|
-
f"UNIT_{unit_name}: Set[MultilevelSensorScaleType] = {{{','.join(sorted(unit_enums))}}}"
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
lines.extend(
|
|
207
|
-
[
|
|
208
|
-
"",
|
|
209
|
-
"# ----------------------------------------------------------------------------------- #",
|
|
210
|
-
"# **END OF AUTOGENERATED CONTENT** (DO NOT EDIT/REMOVE THIS COMMENT BLOCK AND DO NOT #",
|
|
211
|
-
"# EDIT ANYTHING ABOVE IT. IF A NEW IMPORT IS NEEDED, UPDATE THE LINES AROUND 135 #",
|
|
212
|
-
"# IN scripts/generate_multilevel_sensor_constants.py THEN RE-RUN THE SCRIPT. ALL #",
|
|
213
|
-
"# LINES WRITTEN BELOW THIS BLOCK WILL BE PRESERVED AS LONG AS THIS BLOCK REMAINS) #",
|
|
214
|
-
"# ----------------------------------------------------------------------------------- #",
|
|
215
|
-
"",
|
|
216
|
-
]
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
existing_const_file = CONST_FILE_PATH.read_text(encoding="utf-8").splitlines()
|
|
220
|
-
|
|
221
|
-
manually_written_code_start_idx = (
|
|
222
|
-
next(
|
|
223
|
-
i
|
|
224
|
-
for i, line in enumerate(existing_const_file)
|
|
225
|
-
if "**END OF AUTOGENERATED CONTENT**" in line
|
|
226
|
-
)
|
|
227
|
-
+ 6
|
|
228
|
-
)
|
|
229
|
-
if len(existing_const_file) > manually_written_code_start_idx:
|
|
230
|
-
lines.extend(
|
|
231
|
-
[
|
|
232
|
-
line.strip("\n")
|
|
233
|
-
for line in existing_const_file[manually_written_code_start_idx:]
|
|
234
|
-
]
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
CONST_FILE_PATH.write_text("\n".join(lines), encoding="utf-8")
|
|
238
|
-
|
|
239
|
-
if subprocess.run(["which", "black"], capture_output=True, check=True).stdout:
|
|
240
|
-
subprocess.run(
|
|
241
|
-
["black", CONST_FILE_PATH],
|
|
242
|
-
check=True,
|
|
243
|
-
)
|
|
244
|
-
else:
|
|
245
|
-
print("Could not run black on new file, please run it to properly format it.")
|