zwave-js-server-python 0.44.0__py3-none-any.whl → 0.45.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/client.py +1 -1
- zwave_js_server/const/__init__.py +2 -2
- zwave_js_server/firmware.py +36 -1
- zwave_js_server/model/controller/__init__.py +34 -4
- zwave_js_server/model/controller/data_model.py +1 -0
- zwave_js_server/model/controller/event_model.py +17 -0
- zwave_js_server/model/controller/firmware.py +73 -0
- zwave_js_server/model/driver.py +12 -0
- zwave_js_server/model/firmware.py +3 -226
- zwave_js_server/model/node/__init__.py +20 -20
- zwave_js_server/model/node/event_model.py +6 -3
- zwave_js_server/model/node/firmware.py +235 -0
- zwave_js_server/model/utils.py +12 -0
- {zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/METADATA +1 -1
- {zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/RECORD +19 -17
- {zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/LICENSE +0 -0
- {zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/WHEEL +0 -0
- {zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/entry_points.txt +0 -0
- {zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/top_level.txt +0 -0
zwave_js_server/client.py
CHANGED
|
@@ -7,9 +7,9 @@ PACKAGE_NAME = "zwave-js-server-python"
|
|
|
7
7
|
__version__ = metadata.version(PACKAGE_NAME)
|
|
8
8
|
|
|
9
9
|
# minimal server schema version we can handle
|
|
10
|
-
MIN_SERVER_SCHEMA_VERSION =
|
|
10
|
+
MIN_SERVER_SCHEMA_VERSION = 25
|
|
11
11
|
# max server schema version we can handle (and our code is compatible with)
|
|
12
|
-
MAX_SERVER_SCHEMA_VERSION =
|
|
12
|
+
MAX_SERVER_SCHEMA_VERSION = 25
|
|
13
13
|
|
|
14
14
|
TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED = sys.version_info < (3, 9, 2)
|
|
15
15
|
|
zwave_js_server/firmware.py
CHANGED
|
@@ -16,7 +16,7 @@ async def update_firmware(
|
|
|
16
16
|
session: aiohttp.ClientSession,
|
|
17
17
|
additional_user_agent_components: Optional[Dict[str, str]] = None,
|
|
18
18
|
) -> bool:
|
|
19
|
-
"""Send
|
|
19
|
+
"""Send updateFirmware command to Node."""
|
|
20
20
|
client = Client(
|
|
21
21
|
url, session, additional_user_agent_components=additional_user_agent_components
|
|
22
22
|
)
|
|
@@ -37,3 +37,38 @@ async def update_firmware(
|
|
|
37
37
|
receive_task.cancel()
|
|
38
38
|
|
|
39
39
|
return cast(bool, data["success"])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def controller_firmware_update_otw(
|
|
43
|
+
url: str,
|
|
44
|
+
firmware_file: FirmwareUpdateData,
|
|
45
|
+
session: aiohttp.ClientSession,
|
|
46
|
+
additional_user_agent_components: Optional[Dict[str, str]] = None,
|
|
47
|
+
) -> bool:
|
|
48
|
+
"""
|
|
49
|
+
Send firmwareUpdateOTW command to Controller.
|
|
50
|
+
|
|
51
|
+
Sending the wrong firmware to a controller can brick it and make it unrecoverable.
|
|
52
|
+
Consumers of this library should build mechanisms to ensure that users understand
|
|
53
|
+
the risks.
|
|
54
|
+
"""
|
|
55
|
+
client = Client(
|
|
56
|
+
url, session, additional_user_agent_components=additional_user_agent_components
|
|
57
|
+
)
|
|
58
|
+
await client.connect()
|
|
59
|
+
await client.initialize()
|
|
60
|
+
|
|
61
|
+
receive_task = asyncio.get_running_loop().create_task(client.receive_until_closed())
|
|
62
|
+
|
|
63
|
+
data = await client.async_send_command(
|
|
64
|
+
{
|
|
65
|
+
"command": "controller.firmware_update_otw",
|
|
66
|
+
**firmware_file.to_dict(),
|
|
67
|
+
},
|
|
68
|
+
require_schema=25,
|
|
69
|
+
)
|
|
70
|
+
await client.disconnect()
|
|
71
|
+
if not receive_task.done():
|
|
72
|
+
receive_task.cancel()
|
|
73
|
+
|
|
74
|
+
return cast(bool, data["success"])
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast
|
|
4
4
|
|
|
5
|
-
from zwave_js_server.model.firmware import
|
|
5
|
+
from zwave_js_server.model.node.firmware import (
|
|
6
|
+
NodeFirmwareUpdateFileInfo,
|
|
7
|
+
NodeFirmwareUpdateInfo,
|
|
8
|
+
)
|
|
6
9
|
|
|
7
10
|
from ...const import (
|
|
8
11
|
MINIMUM_QR_STRING_LENGTH,
|
|
@@ -20,6 +23,7 @@ from ..association import AssociationAddress, AssociationGroup
|
|
|
20
23
|
from ..node import Node
|
|
21
24
|
from .data_model import ControllerDataType
|
|
22
25
|
from .event_model import CONTROLLER_EVENT_MODEL_MAP
|
|
26
|
+
from .firmware import ControllerFirmwareUpdateProgress, ControllerFirmwareUpdateResult
|
|
23
27
|
from .inclusion_and_provisioning import (
|
|
24
28
|
InclusionGrant,
|
|
25
29
|
ProvisioningEntry,
|
|
@@ -88,6 +92,13 @@ class Controller(EventBase):
|
|
|
88
92
|
"""Return own_node_id."""
|
|
89
93
|
return self.data.get("ownNodeId")
|
|
90
94
|
|
|
95
|
+
@property
|
|
96
|
+
def own_node(self) -> Optional[Node]:
|
|
97
|
+
"""Return own_node."""
|
|
98
|
+
if self.own_node_id is None:
|
|
99
|
+
return None
|
|
100
|
+
return self.nodes.get(self.own_node_id)
|
|
101
|
+
|
|
91
102
|
@property
|
|
92
103
|
def is_primary(self) -> Optional[bool]:
|
|
93
104
|
"""Return is_primary."""
|
|
@@ -175,6 +186,13 @@ class Controller(EventBase):
|
|
|
175
186
|
"""Return inclusion state."""
|
|
176
187
|
return InclusionState(self.data["inclusionState"])
|
|
177
188
|
|
|
189
|
+
@property
|
|
190
|
+
def rf_region(self) -> Optional[RFRegion]:
|
|
191
|
+
"""Return RF region of controller."""
|
|
192
|
+
if (rf_region := self.data.get("rfRegion")) is None:
|
|
193
|
+
return None
|
|
194
|
+
return RFRegion(rf_region)
|
|
195
|
+
|
|
178
196
|
def update(self, data: ControllerDataType) -> None:
|
|
179
197
|
"""Update controller data."""
|
|
180
198
|
self.data = data
|
|
@@ -714,7 +732,7 @@ class Controller(EventBase):
|
|
|
714
732
|
|
|
715
733
|
async def async_get_available_firmware_updates(
|
|
716
734
|
self, node: Node, api_key: str, include_prereleases: bool = False
|
|
717
|
-
) -> List[
|
|
735
|
+
) -> List[NodeFirmwareUpdateInfo]:
|
|
718
736
|
"""Send getAvailableFirmwareUpdates command to Controller."""
|
|
719
737
|
data = await self.client.async_send_command(
|
|
720
738
|
{
|
|
@@ -726,10 +744,10 @@ class Controller(EventBase):
|
|
|
726
744
|
require_schema=24,
|
|
727
745
|
)
|
|
728
746
|
assert data
|
|
729
|
-
return [
|
|
747
|
+
return [NodeFirmwareUpdateInfo.from_dict(update) for update in data["updates"]]
|
|
730
748
|
|
|
731
749
|
async def async_firmware_update_ota(
|
|
732
|
-
self, node: Node, updates: List[
|
|
750
|
+
self, node: Node, updates: List[NodeFirmwareUpdateFileInfo]
|
|
733
751
|
) -> bool:
|
|
734
752
|
"""Send firmwareUpdateOTA command to Controller."""
|
|
735
753
|
data = await self.client.async_send_command(
|
|
@@ -766,6 +784,18 @@ class Controller(EventBase):
|
|
|
766
784
|
event.data["controller"] = self
|
|
767
785
|
self.emit(event.type, event.data)
|
|
768
786
|
|
|
787
|
+
def handle_firmware_update_progress(self, event: Event) -> None:
|
|
788
|
+
"""Process a firmware update progress event."""
|
|
789
|
+
event.data["firmware_update_progress"] = ControllerFirmwareUpdateProgress(
|
|
790
|
+
event.data["progress"]
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
def handle_firmware_update_finished(self, event: Event) -> None:
|
|
794
|
+
"""Process a firmware update finished event."""
|
|
795
|
+
event.data["firmware_update_finished"] = ControllerFirmwareUpdateResult(
|
|
796
|
+
event.data["result"]
|
|
797
|
+
)
|
|
798
|
+
|
|
769
799
|
def handle_inclusion_failed(self, event: Event) -> None:
|
|
770
800
|
"""Process an inclusion failed event."""
|
|
771
801
|
|
|
@@ -4,6 +4,7 @@ from typing import Dict, Literal, Type
|
|
|
4
4
|
from ...const import TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED
|
|
5
5
|
from ...event import BaseEventModel
|
|
6
6
|
from ..node.data_model import FoundNodeDataType, NodeDataType
|
|
7
|
+
from .firmware import ControllerFirmwareUpdateProgressDataType
|
|
7
8
|
from .inclusion_and_provisioning import InclusionGrantDataType
|
|
8
9
|
from .statistics import ControllerStatisticsDataType
|
|
9
10
|
|
|
@@ -43,6 +44,20 @@ class ExclusionStoppedEventModel(BaseControllerEventModel):
|
|
|
43
44
|
event: Literal["exclusion stopped"]
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
class FirmwareUpdateFinishedEventModel(BaseControllerEventModel):
|
|
48
|
+
"""Model for `firmware update finished` event data."""
|
|
49
|
+
|
|
50
|
+
event: Literal["firmware update finished"]
|
|
51
|
+
result: int
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class FirmwareUpdateProgressEventModel(BaseControllerEventModel):
|
|
55
|
+
"""Model for `firmware update progress` event data."""
|
|
56
|
+
|
|
57
|
+
event: Literal["firmware update progress"]
|
|
58
|
+
progress: ControllerFirmwareUpdateProgressDataType
|
|
59
|
+
|
|
60
|
+
|
|
46
61
|
class GrantSecurityClassesEventModel(BaseControllerEventModel):
|
|
47
62
|
"""Model for `grant security classes` event data."""
|
|
48
63
|
|
|
@@ -157,6 +172,8 @@ CONTROLLER_EVENT_MODEL_MAP: Dict[str, Type["BaseControllerEventModel"]] = {
|
|
|
157
172
|
"exclusion failed": ExclusionFailedEventModel,
|
|
158
173
|
"exclusion started": ExclusionStartedEventModel,
|
|
159
174
|
"exclusion stopped": ExclusionStoppedEventModel,
|
|
175
|
+
"firmware update finished": FirmwareUpdateFinishedEventModel,
|
|
176
|
+
"firmware update progress": FirmwareUpdateProgressEventModel,
|
|
160
177
|
"grant security classes": GrantSecurityClassesEventModel,
|
|
161
178
|
"heal network done": HealNetworkDoneEventModel,
|
|
162
179
|
"heal network progress": HealNetworkProgressEventModel,
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Provide a model for Z-Wave controller firmware."""
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ControllerFirmwareUpdateStatus(IntEnum):
|
|
7
|
+
"""Enum with all controller firmware update status values.
|
|
8
|
+
|
|
9
|
+
https://zwave-js.github.io/node-zwave-js/#/api/node?id=status
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
ERROR_TIMEOUT = 0
|
|
13
|
+
# The maximum number of retry attempts for a firmware fragments were reached
|
|
14
|
+
ERROR_RETRY_LIMIT_REACHED = 1
|
|
15
|
+
# The update was aborted by the bootloader
|
|
16
|
+
ERROR_ABORTED = 2
|
|
17
|
+
OK = 255
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ControllerFirmwareUpdateProgressDataType(TypedDict):
|
|
21
|
+
"""Represent a controller firmware update progress dict type."""
|
|
22
|
+
|
|
23
|
+
sentFragments: int
|
|
24
|
+
totalFragments: int
|
|
25
|
+
progress: float
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ControllerFirmwareUpdateProgress:
|
|
29
|
+
"""Model for a controller firmware update progress data."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, data: ControllerFirmwareUpdateProgressDataType) -> None:
|
|
32
|
+
"""Initialize."""
|
|
33
|
+
self.data = data
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def sent_fragments(self) -> int:
|
|
37
|
+
"""Return the number of fragments sent to the device so far."""
|
|
38
|
+
return self.data["sentFragments"]
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def total_fragments(self) -> int:
|
|
42
|
+
"""Return the total number of fragments that need to be sent to the device."""
|
|
43
|
+
return self.data["totalFragments"]
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def progress(self) -> float:
|
|
47
|
+
"""Return progress."""
|
|
48
|
+
return float(self.data["progress"])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ControllerFirmwareUpdateResultDataType(TypedDict):
|
|
52
|
+
"""Represent a controller firmware update result dict type."""
|
|
53
|
+
|
|
54
|
+
status: int
|
|
55
|
+
success: bool
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ControllerFirmwareUpdateResult:
|
|
59
|
+
"""Model for controller firmware update result data."""
|
|
60
|
+
|
|
61
|
+
def __init__(self, data: ControllerFirmwareUpdateResultDataType) -> None:
|
|
62
|
+
"""Initialize."""
|
|
63
|
+
self.data = data
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def status(self) -> ControllerFirmwareUpdateStatus:
|
|
67
|
+
"""Return the firmware update status."""
|
|
68
|
+
return ControllerFirmwareUpdateStatus(self.data["status"])
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def success(self) -> bool:
|
|
72
|
+
"""Return whether the firmware update was successful."""
|
|
73
|
+
return self.data["success"]
|
zwave_js_server/model/driver.py
CHANGED
|
@@ -166,6 +166,18 @@ class Driver(EventBase):
|
|
|
166
166
|
"""Send command to enable Sentry error reporting."""
|
|
167
167
|
await self._async_send_command("enable_error_reporting", require_schema=16)
|
|
168
168
|
|
|
169
|
+
async def async_hard_reset(self) -> None:
|
|
170
|
+
"""Send command to hard reset controller."""
|
|
171
|
+
await self._async_send_command("hard_reset", require_schema=25)
|
|
172
|
+
|
|
173
|
+
async def async_try_soft_reset(self) -> None:
|
|
174
|
+
"""Send command to try to soft reset controller."""
|
|
175
|
+
await self._async_send_command("try_soft_reset", require_schema=25)
|
|
176
|
+
|
|
177
|
+
async def async_soft_reset(self) -> None:
|
|
178
|
+
"""Send command to soft reset controller."""
|
|
179
|
+
await self._async_send_command("soft_reset", require_schema=25)
|
|
180
|
+
|
|
169
181
|
def handle_logging(self, event: Event) -> None:
|
|
170
182
|
"""Process a driver logging event."""
|
|
171
183
|
event.data["log_message"] = LogMessage(cast(LogMessageDataType, event.data))
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""Provide a model for Z-Wave firmware."""
|
|
2
|
-
from dataclasses import
|
|
3
|
-
from
|
|
4
|
-
from typing import TYPE_CHECKING, List, Optional, Union, cast
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional
|
|
5
4
|
|
|
6
|
-
from ..const import TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED
|
|
5
|
+
from ..const import TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED
|
|
7
6
|
from ..util.helpers import convert_bytes_to_base64
|
|
8
7
|
|
|
9
8
|
if TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED:
|
|
@@ -11,9 +10,6 @@ if TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED:
|
|
|
11
10
|
else:
|
|
12
11
|
from typing import TypedDict
|
|
13
12
|
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from .node import Node
|
|
16
|
-
|
|
17
13
|
|
|
18
14
|
class FirmwareUpdateDataDataType(TypedDict, total=False):
|
|
19
15
|
"""Represent a firmware update data dict type."""
|
|
@@ -40,222 +36,3 @@ class FirmwareUpdateData:
|
|
|
40
36
|
if self.file_format is not None:
|
|
41
37
|
data["fileFormat"] = self.file_format
|
|
42
38
|
return data
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class FirmwareUpdateCapabilitiesDataType(TypedDict, total=False):
|
|
46
|
-
"""Represent a firmware update capabilities dict type."""
|
|
47
|
-
|
|
48
|
-
firmwareUpgradable: bool # required
|
|
49
|
-
firmwareTargets: List[int]
|
|
50
|
-
continuesToFunction: Union[bool, str]
|
|
51
|
-
supportsActivation: Union[bool, str]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class FirmwareUpdateCapabilitiesDict(TypedDict, total=False):
|
|
55
|
-
"""Represent a dict from FirmwareUpdateCapabilities."""
|
|
56
|
-
|
|
57
|
-
firmware_upgradable: bool # required
|
|
58
|
-
firmware_targets: List[int]
|
|
59
|
-
continues_to_function: Optional[bool]
|
|
60
|
-
supports_activation: Optional[bool]
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class FirmwareUpdateCapabilities:
|
|
64
|
-
"""Model for firmware update capabilities."""
|
|
65
|
-
|
|
66
|
-
def __init__(self, data: FirmwareUpdateCapabilitiesDataType) -> None:
|
|
67
|
-
"""Initialize class."""
|
|
68
|
-
self.data = data
|
|
69
|
-
|
|
70
|
-
@property
|
|
71
|
-
def firmware_upgradable(self) -> bool:
|
|
72
|
-
"""Return whether firmware is upgradable."""
|
|
73
|
-
return self.data["firmwareUpgradable"]
|
|
74
|
-
|
|
75
|
-
@property
|
|
76
|
-
def firmware_targets(self) -> List[int]:
|
|
77
|
-
"""Return firmware targets."""
|
|
78
|
-
if not self.firmware_upgradable:
|
|
79
|
-
raise TypeError("Firmware is not upgradeable.")
|
|
80
|
-
return self.data["firmwareTargets"]
|
|
81
|
-
|
|
82
|
-
@property
|
|
83
|
-
def continues_to_function(self) -> Optional[bool]:
|
|
84
|
-
"""Return whether node continues to function during update."""
|
|
85
|
-
if not self.firmware_upgradable:
|
|
86
|
-
raise TypeError("Firmware is not upgradeable.")
|
|
87
|
-
if (val := self.data["continuesToFunction"]) == VALUE_UNKNOWN:
|
|
88
|
-
return None
|
|
89
|
-
assert isinstance(val, bool)
|
|
90
|
-
return val
|
|
91
|
-
|
|
92
|
-
@property
|
|
93
|
-
def supports_activation(self) -> Optional[bool]:
|
|
94
|
-
"""Return whether node supports delayed activation of the new firmware."""
|
|
95
|
-
if not self.firmware_upgradable:
|
|
96
|
-
raise TypeError("Firmware is not upgradeable.")
|
|
97
|
-
if (val := self.data["supportsActivation"]) == VALUE_UNKNOWN:
|
|
98
|
-
return None
|
|
99
|
-
assert isinstance(val, bool)
|
|
100
|
-
return val
|
|
101
|
-
|
|
102
|
-
def to_dict(self) -> FirmwareUpdateCapabilitiesDict:
|
|
103
|
-
"""Return dict representation of the object."""
|
|
104
|
-
if not self.firmware_upgradable:
|
|
105
|
-
return {"firmware_upgradable": self.firmware_upgradable}
|
|
106
|
-
return {
|
|
107
|
-
"firmware_upgradable": self.firmware_upgradable,
|
|
108
|
-
"firmware_targets": self.firmware_targets,
|
|
109
|
-
"continues_to_function": self.continues_to_function,
|
|
110
|
-
"supports_activation": self.supports_activation,
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class FirmwareUpdateStatus(IntEnum):
|
|
115
|
-
"""Enum with all Firmware update status values.
|
|
116
|
-
|
|
117
|
-
https://zwave-js.github.io/node-zwave-js/#/api/node?id=status
|
|
118
|
-
"""
|
|
119
|
-
|
|
120
|
-
ERROR_TIMEOUT = -1
|
|
121
|
-
ERROR_CHECKSUM = 0
|
|
122
|
-
ERROR_TRANSMISSION_FAILED = 1
|
|
123
|
-
ERROR_INVALID_MANUFACTURER_ID = 2
|
|
124
|
-
ERROR_INVALID_FIRMWARE_ID = 3
|
|
125
|
-
ERROR_INVALID_FIRMWARE_TARGET = 4
|
|
126
|
-
ERROR_INVALID_HEADER_INFORMATION = 5
|
|
127
|
-
ERROR_INVALID_HEADER_FORMAT = 6
|
|
128
|
-
ERROR_INSUFFICIENT_MEMORY = 7
|
|
129
|
-
ERROR_INVALID_HARDWARE_VERSION = 8
|
|
130
|
-
OK_WAITING_FOR_ACTIVATION = 253
|
|
131
|
-
OK_NO_RESTART = 254
|
|
132
|
-
OK_RESTART_PENDING = 255
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class FirmwareUpdateProgressDataType(TypedDict):
|
|
136
|
-
"""Represent a firmware update progress dict type."""
|
|
137
|
-
|
|
138
|
-
currentFile: int
|
|
139
|
-
totalFiles: int
|
|
140
|
-
sentFragments: int
|
|
141
|
-
totalFragments: int
|
|
142
|
-
progress: float
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
class FirmwareUpdateProgress:
|
|
146
|
-
"""Model for firmware update progress data."""
|
|
147
|
-
|
|
148
|
-
def __init__(self, node: "Node", data: FirmwareUpdateProgressDataType) -> None:
|
|
149
|
-
"""Initialize."""
|
|
150
|
-
self.data = data
|
|
151
|
-
self.node = node
|
|
152
|
-
|
|
153
|
-
@property
|
|
154
|
-
def current_file(self) -> int:
|
|
155
|
-
"""Return current file."""
|
|
156
|
-
return self.data["currentFile"]
|
|
157
|
-
|
|
158
|
-
@property
|
|
159
|
-
def total_files(self) -> int:
|
|
160
|
-
"""Return total files."""
|
|
161
|
-
return self.data["totalFiles"]
|
|
162
|
-
|
|
163
|
-
@property
|
|
164
|
-
def sent_fragments(self) -> int:
|
|
165
|
-
"""Return the number of fragments sent to the device so far."""
|
|
166
|
-
return self.data["sentFragments"]
|
|
167
|
-
|
|
168
|
-
@property
|
|
169
|
-
def total_fragments(self) -> int:
|
|
170
|
-
"""Return the total number of fragments that need to be sent to the device."""
|
|
171
|
-
return self.data["totalFragments"]
|
|
172
|
-
|
|
173
|
-
@property
|
|
174
|
-
def progress(self) -> float:
|
|
175
|
-
"""Return progress."""
|
|
176
|
-
return float(self.data["progress"])
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
class FirmwareUpdateResultDataType(TypedDict, total=False):
|
|
180
|
-
"""Represent a firmware update result dict type."""
|
|
181
|
-
|
|
182
|
-
status: int # required
|
|
183
|
-
success: bool # required
|
|
184
|
-
waitTime: int
|
|
185
|
-
reInterview: bool # required
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
class FirmwareUpdateResult:
|
|
189
|
-
"""Model for firmware update result data."""
|
|
190
|
-
|
|
191
|
-
def __init__(self, node: "Node", data: FirmwareUpdateResultDataType) -> None:
|
|
192
|
-
"""Initialize."""
|
|
193
|
-
self.data = data
|
|
194
|
-
self.node = node
|
|
195
|
-
|
|
196
|
-
@property
|
|
197
|
-
def status(self) -> FirmwareUpdateStatus:
|
|
198
|
-
"""Return the firmware update status."""
|
|
199
|
-
return FirmwareUpdateStatus(self.data["status"])
|
|
200
|
-
|
|
201
|
-
@property
|
|
202
|
-
def success(self) -> bool:
|
|
203
|
-
"""Return whether the firmware update was successful."""
|
|
204
|
-
return self.data["success"]
|
|
205
|
-
|
|
206
|
-
@property
|
|
207
|
-
def wait_time(self) -> Optional[int]:
|
|
208
|
-
"""Return the wait time in seconds before the device is functional again."""
|
|
209
|
-
return self.data.get("waitTime")
|
|
210
|
-
|
|
211
|
-
@property
|
|
212
|
-
def reinterview(self) -> bool:
|
|
213
|
-
"""Return whether the node will be re-interviewed."""
|
|
214
|
-
return self.data["reInterview"]
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
class FirmwareUpdateFileInfoDataType(TypedDict):
|
|
218
|
-
"""Represent a firmware update file info data dict type."""
|
|
219
|
-
|
|
220
|
-
target: int
|
|
221
|
-
url: str
|
|
222
|
-
integrity: str # sha256
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
@dataclass
|
|
226
|
-
class FirmwareUpdateFileInfo:
|
|
227
|
-
"""Represent a firmware update file info."""
|
|
228
|
-
|
|
229
|
-
target: int
|
|
230
|
-
url: str
|
|
231
|
-
integrity: str
|
|
232
|
-
|
|
233
|
-
def to_dict(self) -> FirmwareUpdateFileInfoDataType:
|
|
234
|
-
"""Return dict representation of the object."""
|
|
235
|
-
return cast(FirmwareUpdateFileInfoDataType, asdict(self))
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
class FirmwareUpdateInfoDataType(TypedDict):
|
|
239
|
-
"""Represent a firmware update info data dict type."""
|
|
240
|
-
|
|
241
|
-
version: str
|
|
242
|
-
changelog: str
|
|
243
|
-
files: List[FirmwareUpdateFileInfoDataType]
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
@dataclass
|
|
247
|
-
class FirmwareUpdateInfo:
|
|
248
|
-
"""Represent a firmware update info."""
|
|
249
|
-
|
|
250
|
-
version: str
|
|
251
|
-
changelog: str
|
|
252
|
-
files: List[FirmwareUpdateFileInfo]
|
|
253
|
-
|
|
254
|
-
@classmethod
|
|
255
|
-
def from_dict(cls, data: FirmwareUpdateInfoDataType) -> "FirmwareUpdateInfo":
|
|
256
|
-
"""Initialize from dict."""
|
|
257
|
-
return cls(
|
|
258
|
-
version=data["version"],
|
|
259
|
-
changelog=data["changelog"],
|
|
260
|
-
files=[FirmwareUpdateFileInfo(**file) for file in data["files"]],
|
|
261
|
-
)
|
|
@@ -21,14 +21,6 @@ from ..command_class import CommandClassInfo
|
|
|
21
21
|
from ..device_class import DeviceClass
|
|
22
22
|
from ..device_config import DeviceConfig
|
|
23
23
|
from ..endpoint import Endpoint
|
|
24
|
-
from ..firmware import (
|
|
25
|
-
FirmwareUpdateCapabilities,
|
|
26
|
-
FirmwareUpdateCapabilitiesDataType,
|
|
27
|
-
FirmwareUpdateProgress,
|
|
28
|
-
FirmwareUpdateProgressDataType,
|
|
29
|
-
FirmwareUpdateResult,
|
|
30
|
-
FirmwareUpdateResultDataType,
|
|
31
|
-
)
|
|
32
24
|
from ..notification import (
|
|
33
25
|
EntryControlNotification,
|
|
34
26
|
EntryControlNotificationDataType,
|
|
@@ -51,6 +43,14 @@ from ..value import (
|
|
|
51
43
|
)
|
|
52
44
|
from .data_model import NodeDataType
|
|
53
45
|
from .event_model import NODE_EVENT_MODEL_MAP
|
|
46
|
+
from .firmware import (
|
|
47
|
+
NodeFirmwareUpdateCapabilities,
|
|
48
|
+
NodeFirmwareUpdateCapabilitiesDataType,
|
|
49
|
+
NodeFirmwareUpdateProgress,
|
|
50
|
+
NodeFirmwareUpdateProgressDataType,
|
|
51
|
+
NodeFirmwareUpdateResult,
|
|
52
|
+
NodeFirmwareUpdateResultDataType,
|
|
53
|
+
)
|
|
54
54
|
from .health_check import (
|
|
55
55
|
CheckHealthProgress,
|
|
56
56
|
LifelineHealthCheckSummary,
|
|
@@ -91,7 +91,7 @@ class Node(EventBase):
|
|
|
91
91
|
self.data: NodeDataType = {}
|
|
92
92
|
self._device_config = DeviceConfig({})
|
|
93
93
|
self._statistics = NodeStatistics(client, data.get("statistics"))
|
|
94
|
-
self._firmware_update_progress: Optional[
|
|
94
|
+
self._firmware_update_progress: Optional[NodeFirmwareUpdateProgress] = None
|
|
95
95
|
self.values: Dict[str, Union[ConfigurationValue, Value]] = {}
|
|
96
96
|
self.endpoints: Dict[int, Endpoint] = {}
|
|
97
97
|
self.update(data)
|
|
@@ -309,7 +309,7 @@ class Node(EventBase):
|
|
|
309
309
|
return self._statistics
|
|
310
310
|
|
|
311
311
|
@property
|
|
312
|
-
def firmware_update_progress(self) -> Optional[
|
|
312
|
+
def firmware_update_progress(self) -> Optional[NodeFirmwareUpdateProgress]:
|
|
313
313
|
"""Return firmware update progress."""
|
|
314
314
|
return self._firmware_update_progress
|
|
315
315
|
|
|
@@ -527,7 +527,7 @@ class Node(EventBase):
|
|
|
527
527
|
|
|
528
528
|
async def async_get_firmware_update_capabilities(
|
|
529
529
|
self,
|
|
530
|
-
) ->
|
|
530
|
+
) -> NodeFirmwareUpdateCapabilities:
|
|
531
531
|
"""Send getFirmwareUpdateCapabilities command to Node."""
|
|
532
532
|
data = await self.async_send_command(
|
|
533
533
|
"get_firmware_update_capabilities",
|
|
@@ -535,13 +535,13 @@ class Node(EventBase):
|
|
|
535
535
|
wait_for_result=True,
|
|
536
536
|
)
|
|
537
537
|
assert data
|
|
538
|
-
return
|
|
539
|
-
cast(
|
|
538
|
+
return NodeFirmwareUpdateCapabilities(
|
|
539
|
+
cast(NodeFirmwareUpdateCapabilitiesDataType, data["capabilities"])
|
|
540
540
|
)
|
|
541
541
|
|
|
542
542
|
async def async_get_firmware_update_capabilities_cached(
|
|
543
543
|
self,
|
|
544
|
-
) ->
|
|
544
|
+
) -> NodeFirmwareUpdateCapabilities:
|
|
545
545
|
"""Send getFirmwareUpdateCapabilitiesCached command to Node."""
|
|
546
546
|
data = await self.async_send_command(
|
|
547
547
|
"get_firmware_update_capabilities_cached",
|
|
@@ -549,8 +549,8 @@ class Node(EventBase):
|
|
|
549
549
|
wait_for_result=True,
|
|
550
550
|
)
|
|
551
551
|
assert data
|
|
552
|
-
return
|
|
553
|
-
cast(
|
|
552
|
+
return NodeFirmwareUpdateCapabilities(
|
|
553
|
+
cast(NodeFirmwareUpdateCapabilitiesDataType, data["capabilities"])
|
|
554
554
|
)
|
|
555
555
|
|
|
556
556
|
async def async_abort_firmware_update(self) -> None:
|
|
@@ -905,15 +905,15 @@ class Node(EventBase):
|
|
|
905
905
|
"""Process a node firmware update progress event."""
|
|
906
906
|
self._firmware_update_progress = event.data[
|
|
907
907
|
"firmware_update_progress"
|
|
908
|
-
] =
|
|
909
|
-
self, cast(
|
|
908
|
+
] = NodeFirmwareUpdateProgress(
|
|
909
|
+
self, cast(NodeFirmwareUpdateProgressDataType, event.data["progress"])
|
|
910
910
|
)
|
|
911
911
|
|
|
912
912
|
def handle_firmware_update_finished(self, event: Event) -> None:
|
|
913
913
|
"""Process a node firmware update finished event."""
|
|
914
914
|
self._firmware_update_progress = None
|
|
915
|
-
event.data["firmware_update_finished"] =
|
|
916
|
-
self, cast(
|
|
915
|
+
event.data["firmware_update_finished"] = NodeFirmwareUpdateResult(
|
|
916
|
+
self, cast(NodeFirmwareUpdateResultDataType, event.data["result"])
|
|
917
917
|
)
|
|
918
918
|
|
|
919
919
|
def handle_statistics_updated(self, event: Event) -> None:
|
|
@@ -6,7 +6,6 @@ from pydantic import BaseModel
|
|
|
6
6
|
from zwave_js_server.const import CommandClass
|
|
7
7
|
|
|
8
8
|
from ...event import BaseEventModel
|
|
9
|
-
from ..firmware import FirmwareUpdateProgressDataType, FirmwareUpdateResultDataType
|
|
10
9
|
from ..notification import (
|
|
11
10
|
EntryControlNotificationArgsDataType,
|
|
12
11
|
NotificationNotificationArgsDataType,
|
|
@@ -14,6 +13,10 @@ from ..notification import (
|
|
|
14
13
|
)
|
|
15
14
|
from ..value import ValueDataType
|
|
16
15
|
from .data_model import NodeDataType
|
|
16
|
+
from .firmware import (
|
|
17
|
+
NodeFirmwareUpdateProgressDataType,
|
|
18
|
+
NodeFirmwareUpdateResultDataType,
|
|
19
|
+
)
|
|
17
20
|
from .statistics import NodeStatisticsDataType
|
|
18
21
|
|
|
19
22
|
|
|
@@ -186,14 +189,14 @@ class FirmwareUpdateFinishedEventModel(BaseNodeEventModel):
|
|
|
186
189
|
"""Model for `firmware update finished` event data."""
|
|
187
190
|
|
|
188
191
|
event: Literal["firmware update finished"]
|
|
189
|
-
result:
|
|
192
|
+
result: NodeFirmwareUpdateResultDataType
|
|
190
193
|
|
|
191
194
|
|
|
192
195
|
class FirmwareUpdateProgressEventModel(BaseNodeEventModel):
|
|
193
196
|
"""Model for `firmware update progress` event data."""
|
|
194
197
|
|
|
195
198
|
event: Literal["firmware update progress"]
|
|
196
|
-
progress:
|
|
199
|
+
progress: NodeFirmwareUpdateProgressDataType
|
|
197
200
|
|
|
198
201
|
|
|
199
202
|
NODE_EVENT_MODEL_MAP: Dict[str, Type["BaseNodeEventModel"]] = {
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Provide a model for Z-Wave firmware."""
|
|
2
|
+
from dataclasses import asdict, dataclass
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
from typing import TYPE_CHECKING, List, Optional, Union, cast
|
|
5
|
+
|
|
6
|
+
from ...const import TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED, VALUE_UNKNOWN
|
|
7
|
+
|
|
8
|
+
if TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED:
|
|
9
|
+
from typing_extensions import TypedDict
|
|
10
|
+
else:
|
|
11
|
+
from typing import TypedDict
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from . import Node
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NodeFirmwareUpdateCapabilitiesDataType(TypedDict, total=False):
|
|
18
|
+
"""Represent a firmware update capabilities dict type."""
|
|
19
|
+
|
|
20
|
+
firmwareUpgradable: bool # required
|
|
21
|
+
firmwareTargets: List[int]
|
|
22
|
+
continuesToFunction: Union[bool, str]
|
|
23
|
+
supportsActivation: Union[bool, str]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class NodeFirmwareUpdateCapabilitiesDict(TypedDict, total=False):
|
|
27
|
+
"""Represent a dict from FirmwareUpdateCapabilities."""
|
|
28
|
+
|
|
29
|
+
firmware_upgradable: bool # required
|
|
30
|
+
firmware_targets: List[int]
|
|
31
|
+
continues_to_function: Optional[bool]
|
|
32
|
+
supports_activation: Optional[bool]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NodeFirmwareUpdateCapabilities:
|
|
36
|
+
"""Model for firmware update capabilities."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, data: NodeFirmwareUpdateCapabilitiesDataType) -> None:
|
|
39
|
+
"""Initialize class."""
|
|
40
|
+
self.data = data
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def firmware_upgradable(self) -> bool:
|
|
44
|
+
"""Return whether firmware is upgradable."""
|
|
45
|
+
return self.data["firmwareUpgradable"]
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def firmware_targets(self) -> List[int]:
|
|
49
|
+
"""Return firmware targets."""
|
|
50
|
+
if not self.firmware_upgradable:
|
|
51
|
+
raise TypeError("Firmware is not upgradeable.")
|
|
52
|
+
return self.data["firmwareTargets"]
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def continues_to_function(self) -> Optional[bool]:
|
|
56
|
+
"""Return whether node continues to function during update."""
|
|
57
|
+
if not self.firmware_upgradable:
|
|
58
|
+
raise TypeError("Firmware is not upgradeable.")
|
|
59
|
+
if (val := self.data["continuesToFunction"]) == VALUE_UNKNOWN:
|
|
60
|
+
return None
|
|
61
|
+
assert isinstance(val, bool)
|
|
62
|
+
return val
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def supports_activation(self) -> Optional[bool]:
|
|
66
|
+
"""Return whether node supports delayed activation of the new firmware."""
|
|
67
|
+
if not self.firmware_upgradable:
|
|
68
|
+
raise TypeError("Firmware is not upgradeable.")
|
|
69
|
+
if (val := self.data["supportsActivation"]) == VALUE_UNKNOWN:
|
|
70
|
+
return None
|
|
71
|
+
assert isinstance(val, bool)
|
|
72
|
+
return val
|
|
73
|
+
|
|
74
|
+
def to_dict(self) -> NodeFirmwareUpdateCapabilitiesDict:
|
|
75
|
+
"""Return dict representation of the object."""
|
|
76
|
+
if not self.firmware_upgradable:
|
|
77
|
+
return {"firmware_upgradable": self.firmware_upgradable}
|
|
78
|
+
return {
|
|
79
|
+
"firmware_upgradable": self.firmware_upgradable,
|
|
80
|
+
"firmware_targets": self.firmware_targets,
|
|
81
|
+
"continues_to_function": self.continues_to_function,
|
|
82
|
+
"supports_activation": self.supports_activation,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class NodeFirmwareUpdateStatus(IntEnum):
|
|
87
|
+
"""Enum with all node firmware update status values.
|
|
88
|
+
|
|
89
|
+
https://zwave-js.github.io/node-zwave-js/#/api/node?id=status
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
ERROR_TIMEOUT = -1
|
|
93
|
+
ERROR_CHECKSUM = 0
|
|
94
|
+
ERROR_TRANSMISSION_FAILED = 1
|
|
95
|
+
ERROR_INVALID_MANUFACTURER_ID = 2
|
|
96
|
+
ERROR_INVALID_FIRMWARE_ID = 3
|
|
97
|
+
ERROR_INVALID_FIRMWARE_TARGET = 4
|
|
98
|
+
ERROR_INVALID_HEADER_INFORMATION = 5
|
|
99
|
+
ERROR_INVALID_HEADER_FORMAT = 6
|
|
100
|
+
ERROR_INSUFFICIENT_MEMORY = 7
|
|
101
|
+
ERROR_INVALID_HARDWARE_VERSION = 8
|
|
102
|
+
OK_WAITING_FOR_ACTIVATION = 253
|
|
103
|
+
OK_NO_RESTART = 254
|
|
104
|
+
OK_RESTART_PENDING = 255
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class NodeFirmwareUpdateProgressDataType(TypedDict):
|
|
108
|
+
"""Represent a node firmware update progress dict type."""
|
|
109
|
+
|
|
110
|
+
currentFile: int
|
|
111
|
+
totalFiles: int
|
|
112
|
+
sentFragments: int
|
|
113
|
+
totalFragments: int
|
|
114
|
+
progress: float
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class NodeFirmwareUpdateProgress:
|
|
118
|
+
"""Model for a node firmware update progress data."""
|
|
119
|
+
|
|
120
|
+
def __init__(self, node: "Node", data: NodeFirmwareUpdateProgressDataType) -> None:
|
|
121
|
+
"""Initialize."""
|
|
122
|
+
self.data = data
|
|
123
|
+
self.node = node
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def current_file(self) -> int:
|
|
127
|
+
"""Return current file."""
|
|
128
|
+
return self.data["currentFile"]
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def total_files(self) -> int:
|
|
132
|
+
"""Return total files."""
|
|
133
|
+
return self.data["totalFiles"]
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def sent_fragments(self) -> int:
|
|
137
|
+
"""Return the number of fragments sent to the device so far."""
|
|
138
|
+
return self.data["sentFragments"]
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def total_fragments(self) -> int:
|
|
142
|
+
"""Return the total number of fragments that need to be sent to the device."""
|
|
143
|
+
return self.data["totalFragments"]
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def progress(self) -> float:
|
|
147
|
+
"""Return progress."""
|
|
148
|
+
return float(self.data["progress"])
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class NodeFirmwareUpdateResultDataType(TypedDict, total=False):
|
|
152
|
+
"""Represent a node firmware update result dict type."""
|
|
153
|
+
|
|
154
|
+
status: int # required
|
|
155
|
+
success: bool # required
|
|
156
|
+
waitTime: int
|
|
157
|
+
reInterview: bool # required
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class NodeFirmwareUpdateResult:
|
|
161
|
+
"""Model for node firmware update result data."""
|
|
162
|
+
|
|
163
|
+
def __init__(self, node: "Node", data: NodeFirmwareUpdateResultDataType) -> None:
|
|
164
|
+
"""Initialize."""
|
|
165
|
+
self.data = data
|
|
166
|
+
self.node = node
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def status(self) -> NodeFirmwareUpdateStatus:
|
|
170
|
+
"""Return the firmware update status."""
|
|
171
|
+
return NodeFirmwareUpdateStatus(self.data["status"])
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def success(self) -> bool:
|
|
175
|
+
"""Return whether the firmware update was successful."""
|
|
176
|
+
return self.data["success"]
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def wait_time(self) -> Optional[int]:
|
|
180
|
+
"""Return the wait time in seconds before the device is functional again."""
|
|
181
|
+
return self.data.get("waitTime")
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def reinterview(self) -> bool:
|
|
185
|
+
"""Return whether the node will be re-interviewed."""
|
|
186
|
+
return self.data["reInterview"]
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class NodeFirmwareUpdateFileInfoDataType(TypedDict):
|
|
190
|
+
"""Represent a firmware update file info data dict type."""
|
|
191
|
+
|
|
192
|
+
target: int
|
|
193
|
+
url: str
|
|
194
|
+
integrity: str # sha256
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass
|
|
198
|
+
class NodeFirmwareUpdateFileInfo:
|
|
199
|
+
"""Represent a firmware update file info."""
|
|
200
|
+
|
|
201
|
+
target: int
|
|
202
|
+
url: str
|
|
203
|
+
integrity: str
|
|
204
|
+
|
|
205
|
+
def to_dict(self) -> NodeFirmwareUpdateFileInfoDataType:
|
|
206
|
+
"""Return dict representation of the object."""
|
|
207
|
+
return cast(NodeFirmwareUpdateFileInfoDataType, asdict(self))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class NodeFirmwareUpdateInfoDataType(TypedDict):
|
|
211
|
+
"""Represent a firmware update info data dict type."""
|
|
212
|
+
|
|
213
|
+
version: str
|
|
214
|
+
changelog: str
|
|
215
|
+
files: List[NodeFirmwareUpdateFileInfoDataType]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass
|
|
219
|
+
class NodeFirmwareUpdateInfo:
|
|
220
|
+
"""Represent a firmware update info."""
|
|
221
|
+
|
|
222
|
+
version: str
|
|
223
|
+
changelog: str
|
|
224
|
+
files: List[NodeFirmwareUpdateFileInfo]
|
|
225
|
+
|
|
226
|
+
@classmethod
|
|
227
|
+
def from_dict(
|
|
228
|
+
cls, data: NodeFirmwareUpdateInfoDataType
|
|
229
|
+
) -> "NodeFirmwareUpdateInfo":
|
|
230
|
+
"""Initialize from dict."""
|
|
231
|
+
return cls(
|
|
232
|
+
version=data["version"],
|
|
233
|
+
changelog=data["changelog"],
|
|
234
|
+
files=[NodeFirmwareUpdateFileInfo(**file) for file in data["files"]],
|
|
235
|
+
)
|
zwave_js_server/model/utils.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"""Model for utils commands."""
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
2
4
|
from ..client import Client
|
|
3
5
|
from ..const import MINIMUM_QR_STRING_LENGTH
|
|
4
6
|
from .controller import QRProvisioningInformation
|
|
@@ -19,3 +21,13 @@ async def async_parse_qr_code_string(
|
|
|
19
21
|
{"command": "utils.parse_qr_code_string", "qr": qr_code_string}
|
|
20
22
|
)
|
|
21
23
|
return QRProvisioningInformation.from_dict(data["qrProvisioningInformation"])
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def async_try_parse_dsk_from_qr_code_string(
|
|
27
|
+
client: Client, qr_code_string: str
|
|
28
|
+
) -> Optional[str]:
|
|
29
|
+
"""Try to get DSK QR code."""
|
|
30
|
+
data = await client.async_send_command(
|
|
31
|
+
{"command": "utils.try_parse_dsk_from_qr_code_string", "qr": qr_code_string}
|
|
32
|
+
)
|
|
33
|
+
return data.get("dsk")
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
zwave_js_server/__init__.py,sha256=Ey3O4Tha56uU-M92oLJmQHupCJ7B9oZmxlQTo8pGUM8,45
|
|
2
2
|
zwave_js_server/__main__.py,sha256=1gWC927sa3FwcA47ndmrRWW0HlfYVdiUarjEsccRF2k,3624
|
|
3
|
-
zwave_js_server/client.py,sha256=
|
|
3
|
+
zwave_js_server/client.py,sha256=J1JFbCOnDurnH-g1NeCuMSdaUPYFrz818NEtKGUvmUk,15702
|
|
4
4
|
zwave_js_server/dump.py,sha256=ntHBOFS4tca2CHiS8kmd4Eo58-DEOV617-QNXvhPWpw,1532
|
|
5
5
|
zwave_js_server/event.py,sha256=E2bIEb635al9MsCB5rkk3ZklU4-YkmqmXtifzlbxXaQ,1958
|
|
6
6
|
zwave_js_server/exceptions.py,sha256=s-a--Stnp8wOIsIb8GfagrrRFyD8cus0iBHe2-rekEw,6097
|
|
7
|
-
zwave_js_server/firmware.py,sha256=
|
|
7
|
+
zwave_js_server/firmware.py,sha256=pLp2YkZN_yI2Lb187e7Y5h6tsiQcCR--8Kk1E8gUg0Q,2150
|
|
8
8
|
zwave_js_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
zwave_js_server/version.py,sha256=RDFP2kSU3cCVIQbgMWggdeDnvhtVsZzzZg9pOjH7CC8,364
|
|
10
|
-
zwave_js_server/const/__init__.py,sha256=
|
|
10
|
+
zwave_js_server/const/__init__.py,sha256=Du-7Q-8_enu4roX5xaUKhMW0fOnhLrxMOCogiFZtE2M,9131
|
|
11
11
|
zwave_js_server/const/command_class/__init__.py,sha256=WilOppnr9CXahDjEEkAXyh_j7iCq_qZ1GtBkjWLoQUg,37
|
|
12
12
|
zwave_js_server/const/command_class/barrier_operator.py,sha256=NBhTp5T-6T31glpIKrEOLgbKTndkB5gqdQmCJlXFFUA,783
|
|
13
13
|
zwave_js_server/const/command_class/color_switch.py,sha256=9jIDdmz8gJ5d1RdINHiK7Yh0NXg1I0U1xVzkrQJICk8,918
|
|
@@ -28,24 +28,26 @@ zwave_js_server/model/association.py,sha256=GIrgo-3uxQxFnCH8PJ6TtGRoBW9Ak3iKWTJb
|
|
|
28
28
|
zwave_js_server/model/command_class.py,sha256=pb_aXIl1_r9iHzdEyRr300y5xu62uE6GzmF6540CQd4,1256
|
|
29
29
|
zwave_js_server/model/device_class.py,sha256=KpSM101bGa_3Mig4C-_rkE4CzqImK2JPIUtlG-TFcvU,1886
|
|
30
30
|
zwave_js_server/model/device_config.py,sha256=7NaMa4BZ5tGe2p0YtOQFIUjBwRVF7GYWVExLwAgIUtE,6659
|
|
31
|
-
zwave_js_server/model/driver.py,sha256=
|
|
31
|
+
zwave_js_server/model/driver.py,sha256=fxQQMNGB1SyS-Y6GFQOs-pKsmn_msK63nPB8WXExGIU,6830
|
|
32
32
|
zwave_js_server/model/endpoint.py,sha256=KRh4aeXKC4PNpTZbT3NuF5x_fDlbeOvoOSJ4u16lrLc,7972
|
|
33
|
-
zwave_js_server/model/firmware.py,sha256=
|
|
33
|
+
zwave_js_server/model/firmware.py,sha256=llAEyTMocofgaGL1TVgt9CktE3BKQJXe934hZ0prFnA,1047
|
|
34
34
|
zwave_js_server/model/log_config.py,sha256=F-QM7oHUXAfAxbdkrvmi6k6woM4VOwxB3qRrGmTQiQA,1722
|
|
35
35
|
zwave_js_server/model/log_message.py,sha256=IxZ4USpXTeHsuRNS61NDPYMSIflv1KDO_EjUy4_vavE,5859
|
|
36
36
|
zwave_js_server/model/notification.py,sha256=vrkKOdNV2bj1G4gP-1n1o43HaFkGoMw060VVLBlM6wU,7269
|
|
37
37
|
zwave_js_server/model/statistics.py,sha256=uG87o-iNGKHxJc4Kfr2ZJ0VY7NjCbdxwgWOqu-D-JNE,3321
|
|
38
|
-
zwave_js_server/model/utils.py,sha256=
|
|
38
|
+
zwave_js_server/model/utils.py,sha256=YTlfb3ceRnH-d7lO3jWSPrlixxqzonwa0M7Nbw7MhZY,1152
|
|
39
39
|
zwave_js_server/model/value.py,sha256=4xl7E6eCOgcpGFQc6GKkYvvmnX-BhzUIFSoLVB9hPh0,8786
|
|
40
40
|
zwave_js_server/model/version.py,sha256=W1H-UbhKdgMW5KdnY9MUfRFibfMZSOD3nljXOv1ZVVM,1398
|
|
41
|
-
zwave_js_server/model/controller/__init__.py,sha256=
|
|
42
|
-
zwave_js_server/model/controller/data_model.py,sha256=
|
|
43
|
-
zwave_js_server/model/controller/event_model.py,sha256=
|
|
41
|
+
zwave_js_server/model/controller/__init__.py,sha256=BVmHUD6YWgWrHt8dV4MVc91hoZsbw1kJixPp0YCCaPE,32660
|
|
42
|
+
zwave_js_server/model/controller/data_model.py,sha256=OotRUphLDQxZJ_Ik2Piwbrhz82_JgE_uw0VaxRE3TdM,967
|
|
43
|
+
zwave_js_server/model/controller/event_model.py,sha256=2F6iLMudRfYgDcc1wym363wVAu2y9Zt1xGhElgrD9AY,5777
|
|
44
|
+
zwave_js_server/model/controller/firmware.py,sha256=6emM0SHi_8CWDmEJsiCMvxSdnZ50qb10EjBm6QuDQqk,2148
|
|
44
45
|
zwave_js_server/model/controller/inclusion_and_provisioning.py,sha256=01YUq4YBhQe3gmJAIsN6ctMM3pcLEaGMSyIySBI9v2c,7497
|
|
45
46
|
zwave_js_server/model/controller/statistics.py,sha256=7aeFjKq_-TvcHBWc-a4xQP6wvO3GoGjr9P-m8uyOco8,4031
|
|
46
|
-
zwave_js_server/model/node/__init__.py,sha256=
|
|
47
|
+
zwave_js_server/model/node/__init__.py,sha256=t_huosfTraNJChqKpFC327gQKYfIqy1ltQrtO8az_qI,33080
|
|
47
48
|
zwave_js_server/model/node/data_model.py,sha256=qaeK77paI3mXFDusi8BWMRAl42tXiNgQTUz0jHLEsC8,1941
|
|
48
|
-
zwave_js_server/model/node/event_model.py,sha256=
|
|
49
|
+
zwave_js_server/model/node/event_model.py,sha256=OQzbbs1LW2I9-VjwIXgVFtZ1w7dI3dmiEjVuJV7nuJ8,6096
|
|
50
|
+
zwave_js_server/model/node/firmware.py,sha256=CgcZSJLkFO8T7fG2LQlThMqdDow-UxloKVvrLzq_MKo,7096
|
|
49
51
|
zwave_js_server/model/node/health_check.py,sha256=EAOMBkMKd5q--fhax2GvJhWdhuuv5dX_-2Hf_mczODs,6054
|
|
50
52
|
zwave_js_server/model/node/statistics.py,sha256=SgAdnXnvC-Hs2GdBvENz1mkbtnXOUf5E0s2OU0iUARE,3634
|
|
51
53
|
zwave_js_server/util/__init__.py,sha256=ArF1K885aW0GdVd7Lo8fl8Atf70gvsxO5Ra_WoJTG28,42
|
|
@@ -56,9 +58,9 @@ zwave_js_server/util/node.py,sha256=9cUky9gssP6psiascmOtdRCqU2MOkZ6tym6VBKsgyqs,
|
|
|
56
58
|
zwave_js_server/util/command_class/__init__.py,sha256=sRxti47ekLTzfk8B609CMQumIbcD6mon2ZS0zwh9omY,59
|
|
57
59
|
zwave_js_server/util/command_class/meter.py,sha256=Qkif1ul4KpV_k6Dc7yNXlSjlJLLTH8mCitgQk9J9W0A,1277
|
|
58
60
|
zwave_js_server/util/command_class/multilevel_sensor.py,sha256=p92YLNRze5z3UWhfIDJO95F-j5tqx1PKUG9oamo9Dng,1446
|
|
59
|
-
zwave_js_server_python-0.
|
|
60
|
-
zwave_js_server_python-0.
|
|
61
|
-
zwave_js_server_python-0.
|
|
62
|
-
zwave_js_server_python-0.
|
|
63
|
-
zwave_js_server_python-0.
|
|
64
|
-
zwave_js_server_python-0.
|
|
61
|
+
zwave_js_server_python-0.45.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
62
|
+
zwave_js_server_python-0.45.0.dist-info/METADATA,sha256=NQdmntgI38wyEk25PeAl8Sgd_MVQUjSYR7mzjXg7-cU,1684
|
|
63
|
+
zwave_js_server_python-0.45.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
64
|
+
zwave_js_server_python-0.45.0.dist-info/entry_points.txt,sha256=_8Swg1yhKiBElo6k1fxIEd6E8uPz8PDxQpwQoZ29FZE,74
|
|
65
|
+
zwave_js_server_python-0.45.0.dist-info/top_level.txt,sha256=-hwsl-i4Av5Op_yfOHC_OP56KPmzp_iVEkeohRIN5Ng,16
|
|
66
|
+
zwave_js_server_python-0.45.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{zwave_js_server_python-0.44.0.dist-info → zwave_js_server_python-0.45.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|