zwave-js-server-python 0.65.0__tar.gz → 0.67.0__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.65.0/zwave_js_server_python.egg-info → zwave_js_server_python-0.67.0}/PKG-INFO +2 -2
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/pyproject.toml +4 -3
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/__init__.py +3 -3
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/event.py +1 -4
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/config_manager/__init__.py +2 -2
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/controller/statistics.py +9 -4
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/device_config.py +7 -7
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/driver/__init__.py +74 -9
- zwave_js_server_python-0.67.0/zwave_js_server/model/driver/firmware.py +65 -0
- zwave_js_server_python-0.67.0/zwave_js_server/model/firmware.py +210 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/node/__init__.py +1 -4
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/node/event_model.py +4 -7
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/node/firmware.py +68 -132
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/node/statistics.py +2 -2
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/value.py +1 -1
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0/zwave_js_server_python.egg-info}/PKG-INFO +2 -2
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server_python.egg-info/SOURCES.txt +1 -0
- zwave_js_server_python-0.67.0/zwave_js_server_python.egg-info/requires.txt +2 -0
- zwave_js_server_python-0.65.0/zwave_js_server/model/driver/firmware.py +0 -97
- zwave_js_server_python-0.65.0/zwave_js_server_python.egg-info/requires.txt +0 -2
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/LICENSE +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/MANIFEST.in +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/README.md +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/setup.cfg +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/__init__.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/__main__.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/client.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/__init__.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/barrier_operator.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/basic.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/central_scene.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/color_switch.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/energy_production.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/entry_control.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/humidity_control.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/lock.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/meter.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/multilevel_sensor.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/multilevel_switch.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/notification.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/power_level.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/protection.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/scene_activation.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/sound_switch.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/thermostat.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/wake_up.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/command_class/window_covering.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/dump.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/exceptions.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/firmware.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/__init__.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/association.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/command_class.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/controller/__init__.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/controller/data_model.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/controller/event_model.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/controller/inclusion_and_provisioning.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/controller/rebuild_routes.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/device_class.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/duration.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/endpoint.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/log_config.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/log_message.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/node/data_model.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/node/health_check.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/notification.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/statistics.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/utils.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/model/version.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/py.typed +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/__init__.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/command_class/__init__.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/command_class/energy_production.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/command_class/meter.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/command_class/multilevel_sensor.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/helpers.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/lock.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/multicast.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/util/node.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/version.py +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server_python.egg-info/dependency_links.txt +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server_python.egg-info/entry_points.txt +0 -0
- {zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: zwave-js-server-python
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.67.0
|
4
4
|
Summary: Python wrapper for zwave-js-server
|
5
5
|
Author-email: Home Assistant Team <hello@home-assistant.io>
|
6
6
|
License-Expression: Apache-2.0
|
@@ -18,7 +18,7 @@ Requires-Python: >=3.12
|
|
18
18
|
Description-Content-Type: text/markdown
|
19
19
|
License-File: LICENSE
|
20
20
|
Requires-Dist: aiohttp>3
|
21
|
-
Requires-Dist: pydantic>=
|
21
|
+
Requires-Dist: pydantic>=2.0.0
|
22
22
|
Dynamic: license-file
|
23
23
|
|
24
24
|
# zwave-js-server-python
|
@@ -19,7 +19,10 @@ classifiers = [
|
|
19
19
|
"Programming Language :: Python :: 3.13",
|
20
20
|
"Topic :: Home Automation",
|
21
21
|
]
|
22
|
-
dependencies = [
|
22
|
+
dependencies = [
|
23
|
+
"aiohttp>3",
|
24
|
+
"pydantic>=2.0.0",
|
25
|
+
]
|
23
26
|
dynamic = ["version"]
|
24
27
|
|
25
28
|
[project.urls]
|
@@ -40,8 +43,6 @@ zwave_js_server = ["py.typed"]
|
|
40
43
|
|
41
44
|
[tool.mypy]
|
42
45
|
plugins = ["pydantic.mypy"]
|
43
|
-
follow_imports = "skip"
|
44
|
-
ignore_missing_imports = true
|
45
46
|
check_untyped_defs = true
|
46
47
|
disallow_incomplete_defs = true
|
47
48
|
disallow_untyped_calls = true
|
{zwave_js_server_python-0.65.0 → zwave_js_server_python-0.67.0}/zwave_js_server/const/__init__.py
RENAMED
@@ -8,12 +8,12 @@ import logging
|
|
8
8
|
from typing import TypedDict
|
9
9
|
|
10
10
|
PACKAGE_NAME = "zwave-js-server-python"
|
11
|
-
__version__ = "0.
|
11
|
+
__version__ = "0.67.0"
|
12
12
|
|
13
13
|
# minimal server schema version we can handle
|
14
|
-
MIN_SERVER_SCHEMA_VERSION =
|
14
|
+
MIN_SERVER_SCHEMA_VERSION = 44
|
15
15
|
# max server schema version we can handle (and our code is compatible with)
|
16
|
-
MAX_SERVER_SCHEMA_VERSION =
|
16
|
+
MAX_SERVER_SCHEMA_VERSION = 44
|
17
17
|
|
18
18
|
VALUE_UNKNOWN = "unknown"
|
19
19
|
|
@@ -7,10 +7,7 @@ from dataclasses import dataclass, field
|
|
7
7
|
import logging
|
8
8
|
from typing import Literal
|
9
9
|
|
10
|
-
|
11
|
-
from pydantic.v1 import BaseModel
|
12
|
-
except ImportError:
|
13
|
-
from pydantic import BaseModel
|
10
|
+
from pydantic import BaseModel
|
14
11
|
|
15
12
|
LOGGER = logging.getLogger(__package__)
|
16
13
|
|
@@ -6,7 +6,7 @@ https://zwave-js.github.io/node-zwave-js/#/api/config-manager
|
|
6
6
|
|
7
7
|
from __future__ import annotations
|
8
8
|
|
9
|
-
from typing import TYPE_CHECKING, Any
|
9
|
+
from typing import TYPE_CHECKING, Any
|
10
10
|
|
11
11
|
from ..device_config import DeviceConfig
|
12
12
|
|
@@ -26,7 +26,7 @@ class ConfigManager:
|
|
26
26
|
manufacturer_id: int,
|
27
27
|
product_type: int,
|
28
28
|
product_id: int,
|
29
|
-
firmware_version:
|
29
|
+
firmware_version: str | None = None,
|
30
30
|
) -> DeviceConfig | None:
|
31
31
|
"""Look up the definition of a given device in the configuration DB."""
|
32
32
|
cmd: dict[str, Any] = {
|
@@ -53,6 +53,7 @@ class BackgroundRSSIDataType(TypedDict, total=False):
|
|
53
53
|
channel0: ChannelRSSIDataType # required
|
54
54
|
channel1: ChannelRSSIDataType # required
|
55
55
|
channel2: ChannelRSSIDataType
|
56
|
+
channel3: ChannelRSSIDataType
|
56
57
|
|
57
58
|
|
58
59
|
class ControllerStatisticsDataType(TypedDict, total=False):
|
@@ -94,16 +95,20 @@ class BackgroundRSSI:
|
|
94
95
|
channel_0: ChannelRSSI = field(init=False)
|
95
96
|
channel_1: ChannelRSSI = field(init=False)
|
96
97
|
channel_2: ChannelRSSI | None = field(init=False)
|
98
|
+
channel_3: ChannelRSSI | None = field(init=False)
|
97
99
|
|
98
100
|
def __post_init__(self) -> None:
|
99
101
|
"""Post initialize."""
|
100
102
|
self.timestamp = self.data["timestamp"]
|
101
103
|
self.channel_0 = ChannelRSSI(self.data["channel0"])
|
102
104
|
self.channel_1 = ChannelRSSI(self.data["channel1"])
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
# Channels 2 and 3 may not be present, but 3 requires 2 to be present
|
106
|
+
self.channel_2 = None
|
107
|
+
self.channel_3 = None
|
108
|
+
if channel_2 := self.data.get("channel2"):
|
109
|
+
self.channel_2 = ChannelRSSI(channel_2)
|
110
|
+
if channel_3 := self.data.get("channel3"):
|
111
|
+
self.channel_3 = ChannelRSSI(channel_3)
|
107
112
|
|
108
113
|
|
109
114
|
@dataclass
|
@@ -12,8 +12,8 @@ from typing import Any, Literal, TypedDict
|
|
12
12
|
class DeviceDeviceDataType(TypedDict, total=False):
|
13
13
|
"""Represent a device device data dict type."""
|
14
14
|
|
15
|
-
productType: str
|
16
|
-
productId: str
|
15
|
+
productType: str | int
|
16
|
+
productId: str | int
|
17
17
|
|
18
18
|
|
19
19
|
class DeviceDevice:
|
@@ -24,12 +24,12 @@ class DeviceDevice:
|
|
24
24
|
self.data = data
|
25
25
|
|
26
26
|
@property
|
27
|
-
def product_type(self) -> str | None:
|
27
|
+
def product_type(self) -> str | int | None:
|
28
28
|
"""Return product type."""
|
29
29
|
return self.data.get("productType")
|
30
30
|
|
31
31
|
@property
|
32
|
-
def product_id(self) -> str | None:
|
32
|
+
def product_id(self) -> str | int | None:
|
33
33
|
"""Return product id."""
|
34
34
|
return self.data.get("productId")
|
35
35
|
|
@@ -125,7 +125,7 @@ class DeviceConfigDataType(TypedDict, total=False):
|
|
125
125
|
|
126
126
|
filename: str
|
127
127
|
manufacturer: str
|
128
|
-
manufacturerId:
|
128
|
+
manufacturerId: int
|
129
129
|
label: str
|
130
130
|
description: str
|
131
131
|
devices: list[DeviceDeviceDataType]
|
@@ -164,8 +164,8 @@ class DeviceConfig:
|
|
164
164
|
return self.data.get("manufacturer")
|
165
165
|
|
166
166
|
@property
|
167
|
-
def manufacturer_id(self) ->
|
168
|
-
"""Return manufacturer id (as defined in specs)
|
167
|
+
def manufacturer_id(self) -> int | None:
|
168
|
+
"""Return manufacturer id (as defined in specs)."""
|
169
169
|
return self.data.get("manufacturerId")
|
170
170
|
|
171
171
|
@property
|
@@ -4,11 +4,18 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
from typing import TYPE_CHECKING, Any, Literal, cast
|
6
6
|
|
7
|
+
from zwave_js_server.model.firmware import (
|
8
|
+
FirmwareUpdateData,
|
9
|
+
FirmwareUpdateDataDataType,
|
10
|
+
FirmwareUpdateInfo,
|
11
|
+
FirmwareUpdateInfoDataType,
|
12
|
+
)
|
13
|
+
|
7
14
|
from ...event import BaseEventModel, Event, EventBase
|
8
15
|
from ..config_manager import ConfigManager
|
9
16
|
from ..controller import Controller
|
10
17
|
from ..log_config import LogConfig, LogConfigDataType
|
11
|
-
from ..log_message import LogMessage, LogMessageDataType
|
18
|
+
from ..log_message import LogMessage, LogMessageContextDataType, LogMessageDataType
|
12
19
|
from .firmware import (
|
13
20
|
DriverFirmwareUpdateProgress,
|
14
21
|
DriverFirmwareUpdateProgressDataType,
|
@@ -16,11 +23,6 @@ from .firmware import (
|
|
16
23
|
DriverFirmwareUpdateResultDataType,
|
17
24
|
)
|
18
25
|
|
19
|
-
try:
|
20
|
-
from pydantic.v1 import create_model_from_typeddict
|
21
|
-
except ImportError:
|
22
|
-
from pydantic import create_model_from_typeddict
|
23
|
-
|
24
26
|
if TYPE_CHECKING:
|
25
27
|
from ...client import Client
|
26
28
|
|
@@ -53,9 +55,40 @@ class AllNodesReadyEventModel(BaseDriverEventModel):
|
|
53
55
|
event: Literal["all nodes ready"]
|
54
56
|
|
55
57
|
|
56
|
-
LoggingEventModel
|
57
|
-
|
58
|
-
|
58
|
+
class LoggingEventModel(BaseDriverEventModel):
|
59
|
+
"""Model for `logging` event data."""
|
60
|
+
|
61
|
+
event: Literal["logging"]
|
62
|
+
message: str | list[str] # required
|
63
|
+
formattedMessage: str | list[str] # required
|
64
|
+
direction: str # required
|
65
|
+
level: str # required
|
66
|
+
context: LogMessageContextDataType # required
|
67
|
+
primaryTags: str | None = None
|
68
|
+
secondaryTags: str | None = None
|
69
|
+
secondaryTagPadding: int | None = None
|
70
|
+
multiline: bool | None = None
|
71
|
+
timestamp: str | None = None
|
72
|
+
label: str | None = None
|
73
|
+
|
74
|
+
@classmethod
|
75
|
+
def from_dict(cls, data: dict) -> LoggingEventModel:
|
76
|
+
"""Initialize from dict."""
|
77
|
+
return cls(
|
78
|
+
source=data["source"],
|
79
|
+
event=data["event"],
|
80
|
+
message=data["message"],
|
81
|
+
formattedMessage=data["formattedMessage"],
|
82
|
+
direction=data["direction"],
|
83
|
+
level=data["level"],
|
84
|
+
context=data["context"],
|
85
|
+
primaryTags=data.get("primaryTags"),
|
86
|
+
secondaryTags=data.get("secondaryTags"),
|
87
|
+
secondaryTagPadding=data.get("secondaryTagPadding"),
|
88
|
+
multiline=data.get("multiline"),
|
89
|
+
timestamp=data.get("timestamp"),
|
90
|
+
label=data.get("label"),
|
91
|
+
)
|
59
92
|
|
60
93
|
|
61
94
|
class DriverReadyEventModel(BaseDriverEventModel):
|
@@ -216,6 +249,38 @@ class Driver(EventBase):
|
|
216
249
|
)
|
217
250
|
return cast(bool, result["success"])
|
218
251
|
|
252
|
+
async def async_firmware_update_otw(
|
253
|
+
self,
|
254
|
+
*,
|
255
|
+
update_data: FirmwareUpdateData | None = None,
|
256
|
+
update_info: FirmwareUpdateInfo | None = None,
|
257
|
+
) -> DriverFirmwareUpdateResult:
|
258
|
+
"""Send firmwareUpdateOTW command to Driver."""
|
259
|
+
if update_data is None and update_info is None:
|
260
|
+
raise ValueError(
|
261
|
+
"Either update_data or update_info must be provided for firmware update."
|
262
|
+
)
|
263
|
+
if update_data is not None and update_info is not None:
|
264
|
+
raise ValueError(
|
265
|
+
"Only one of update_data or update_info can be provided for firmware update."
|
266
|
+
)
|
267
|
+
params: FirmwareUpdateDataDataType | dict[str, FirmwareUpdateInfoDataType]
|
268
|
+
if update_data is not None:
|
269
|
+
params = update_data.to_dict()
|
270
|
+
elif update_info is not None:
|
271
|
+
params = {"updateInfo": update_info.to_dict()}
|
272
|
+
data = await self._async_send_command(
|
273
|
+
"firmware_update_otw", require_schema=44, **params
|
274
|
+
)
|
275
|
+
return DriverFirmwareUpdateResult(data["result"])
|
276
|
+
|
277
|
+
async def async_is_otw_firmware_update_in_progress(self) -> bool:
|
278
|
+
"""Send isOTWFirmwareUpdateInProgress command to Driver."""
|
279
|
+
result = await self._async_send_command(
|
280
|
+
"is_otw_firmware_update_in_progress", require_schema=41
|
281
|
+
)
|
282
|
+
return cast(bool, result["progress"])
|
283
|
+
|
219
284
|
async def async_set_preferred_scales(
|
220
285
|
self, scales: dict[str | int, str | int]
|
221
286
|
) -> None:
|
@@ -0,0 +1,65 @@
|
|
1
|
+
"""Provide a model for Z-Wave driver firmware."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from dataclasses import dataclass, field
|
6
|
+
from enum import IntEnum
|
7
|
+
|
8
|
+
from zwave_js_server.model.firmware import (
|
9
|
+
FirmwareUpdateData,
|
10
|
+
FirmwareUpdateDataDataType,
|
11
|
+
FirmwareUpdateProgress,
|
12
|
+
FirmwareUpdateProgressDataType,
|
13
|
+
FirmwareUpdateResult,
|
14
|
+
FirmwareUpdateResultDataType,
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
class DriverFirmwareUpdateDataDataType(FirmwareUpdateDataDataType):
|
19
|
+
"""Represent a driver firmware update data dict type."""
|
20
|
+
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class DriverFirmwareUpdateData(FirmwareUpdateData):
|
24
|
+
"""Driver firmware update data."""
|
25
|
+
|
26
|
+
|
27
|
+
class DriverFirmwareUpdateStatus(IntEnum):
|
28
|
+
"""Enum with all driver firmware update status values.
|
29
|
+
|
30
|
+
https://zwave-js.github.io/node-zwave-js/#/api/driver?id=quotfirmware-update-finishedquot
|
31
|
+
"""
|
32
|
+
|
33
|
+
ERROR_TIMEOUT = 0
|
34
|
+
# The maximum number of retry attempts for a firmware fragments were reached
|
35
|
+
ERROR_RETRY_LIMIT_REACHED = 1
|
36
|
+
# The update was aborted by the bootloader
|
37
|
+
ERROR_ABORTED = 2
|
38
|
+
# This driver does not support firmware updates
|
39
|
+
ERROR_NOT_SUPPORTED = 3
|
40
|
+
OK = 255
|
41
|
+
|
42
|
+
|
43
|
+
class DriverFirmwareUpdateProgressDataType(FirmwareUpdateProgressDataType):
|
44
|
+
"""Represent a driver firmware update progress dict type."""
|
45
|
+
|
46
|
+
|
47
|
+
@dataclass
|
48
|
+
class DriverFirmwareUpdateProgress(FirmwareUpdateProgress):
|
49
|
+
"""Model for a driver firmware update progress data."""
|
50
|
+
|
51
|
+
data: DriverFirmwareUpdateProgressDataType = field(repr=False)
|
52
|
+
|
53
|
+
|
54
|
+
class DriverFirmwareUpdateResultDataType(FirmwareUpdateResultDataType):
|
55
|
+
"""Represent a driver firmware update result dict type."""
|
56
|
+
|
57
|
+
|
58
|
+
@dataclass
|
59
|
+
class DriverFirmwareUpdateResult(FirmwareUpdateResult):
|
60
|
+
"""Model for driver firmware update result data."""
|
61
|
+
|
62
|
+
data: DriverFirmwareUpdateResultDataType = field(repr=False)
|
63
|
+
status: DriverFirmwareUpdateStatus = field(init=False)
|
64
|
+
success: bool = field(init=False)
|
65
|
+
_status_class = DriverFirmwareUpdateStatus
|
@@ -0,0 +1,210 @@
|
|
1
|
+
"""Provide models for Z-Wave JS firmware."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from dataclasses import asdict, dataclass, field
|
6
|
+
from enum import IntEnum
|
7
|
+
from typing import Literal, Required, TypedDict, cast
|
8
|
+
|
9
|
+
from zwave_js_server.const import RFRegion
|
10
|
+
from zwave_js_server.util.helpers import convert_bytes_to_base64
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass
|
14
|
+
class FirmwareUpdateData:
|
15
|
+
"""Firmware update data."""
|
16
|
+
|
17
|
+
filename: str
|
18
|
+
file: bytes
|
19
|
+
file_format: str | None = None
|
20
|
+
|
21
|
+
def to_dict(self) -> FirmwareUpdateDataDataType:
|
22
|
+
"""Convert firmware update data to dict."""
|
23
|
+
data: FirmwareUpdateDataDataType = {
|
24
|
+
"filename": self.filename,
|
25
|
+
"file": convert_bytes_to_base64(self.file),
|
26
|
+
}
|
27
|
+
if self.file_format is not None:
|
28
|
+
data["fileFormat"] = self.file_format
|
29
|
+
return data
|
30
|
+
|
31
|
+
|
32
|
+
class FirmwareUpdateDataDataType(TypedDict, total=False):
|
33
|
+
"""Represent a firmware update data dict type."""
|
34
|
+
|
35
|
+
filename: Required[str]
|
36
|
+
file: Required[str]
|
37
|
+
fileFormat: str
|
38
|
+
|
39
|
+
|
40
|
+
@dataclass
|
41
|
+
class FirmwareUpdateInfo:
|
42
|
+
"""Represent a firmware update info."""
|
43
|
+
|
44
|
+
version: str
|
45
|
+
changelog: str
|
46
|
+
channel: Literal["stable", "beta"]
|
47
|
+
files: list[FirmwareUpdateFileInfo]
|
48
|
+
downgrade: bool
|
49
|
+
normalized_version: str
|
50
|
+
device: FirmwareUpdateDeviceID
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def from_dict(cls, data: FirmwareUpdateInfoDataType) -> FirmwareUpdateInfo:
|
54
|
+
"""Initialize from dict."""
|
55
|
+
return cls(
|
56
|
+
version=data["version"],
|
57
|
+
changelog=data["changelog"],
|
58
|
+
channel=data["channel"],
|
59
|
+
files=[FirmwareUpdateFileInfo.from_dict(file) for file in data["files"]],
|
60
|
+
downgrade=data["downgrade"],
|
61
|
+
normalized_version=data["normalizedVersion"],
|
62
|
+
device=FirmwareUpdateDeviceID.from_dict(data["device"]),
|
63
|
+
)
|
64
|
+
|
65
|
+
def to_dict(self) -> FirmwareUpdateInfoDataType:
|
66
|
+
"""Return dict representation of the object."""
|
67
|
+
return cast(
|
68
|
+
FirmwareUpdateInfoDataType,
|
69
|
+
{
|
70
|
+
"version": self.version,
|
71
|
+
"changelog": self.changelog,
|
72
|
+
"channel": self.channel,
|
73
|
+
"files": [file.to_dict() for file in self.files],
|
74
|
+
"downgrade": self.downgrade,
|
75
|
+
"normalizedVersion": self.normalized_version,
|
76
|
+
"device": self.device.to_dict(),
|
77
|
+
},
|
78
|
+
)
|
79
|
+
|
80
|
+
|
81
|
+
class FirmwareUpdateInfoDataType(TypedDict, total=False):
|
82
|
+
"""Represent a firmware update info data dict type."""
|
83
|
+
|
84
|
+
version: str
|
85
|
+
changelog: str
|
86
|
+
channel: Literal["stable", "beta"]
|
87
|
+
files: list[FirmwareUpdateFileInfoDataType]
|
88
|
+
downgrade: bool
|
89
|
+
normalizedVersion: str
|
90
|
+
device: FirmwareUpdateDeviceIDDataType
|
91
|
+
|
92
|
+
|
93
|
+
@dataclass
|
94
|
+
class FirmwareUpdateDeviceID:
|
95
|
+
"""Represent a firmware update device ID."""
|
96
|
+
|
97
|
+
manufacturer_id: int
|
98
|
+
product_type: int
|
99
|
+
product_id: int
|
100
|
+
firmware_version: str
|
101
|
+
rf_region: RFRegion | None
|
102
|
+
|
103
|
+
@classmethod
|
104
|
+
def from_dict(cls, data: FirmwareUpdateDeviceIDDataType) -> FirmwareUpdateDeviceID:
|
105
|
+
"""Initialize from dict."""
|
106
|
+
return cls(
|
107
|
+
manufacturer_id=data["manufacturerId"],
|
108
|
+
product_type=data["productType"],
|
109
|
+
product_id=data["productId"],
|
110
|
+
firmware_version=data["firmwareVersion"],
|
111
|
+
rf_region=RFRegion(data["rfRegion"]) if "rfRegion" in data else None,
|
112
|
+
)
|
113
|
+
|
114
|
+
def to_dict(self) -> FirmwareUpdateDeviceIDDataType:
|
115
|
+
"""Return dict representation of the object."""
|
116
|
+
data = {
|
117
|
+
"manufacturerId": self.manufacturer_id,
|
118
|
+
"productType": self.product_type,
|
119
|
+
"productId": self.product_id,
|
120
|
+
"firmwareVersion": self.firmware_version,
|
121
|
+
}
|
122
|
+
if self.rf_region is not None:
|
123
|
+
data["rfRegion"] = self.rf_region
|
124
|
+
return cast(FirmwareUpdateDeviceIDDataType, data)
|
125
|
+
|
126
|
+
|
127
|
+
class FirmwareUpdateDeviceIDDataType(TypedDict, total=False):
|
128
|
+
"""Represent a firmware update device ID dict type."""
|
129
|
+
|
130
|
+
manufacturerId: Required[int]
|
131
|
+
productType: Required[int]
|
132
|
+
productId: Required[int]
|
133
|
+
firmwareVersion: Required[str]
|
134
|
+
rfRegion: int
|
135
|
+
|
136
|
+
|
137
|
+
@dataclass
|
138
|
+
class FirmwareUpdateFileInfo:
|
139
|
+
"""Represent a firmware update file info."""
|
140
|
+
|
141
|
+
target: int
|
142
|
+
url: str
|
143
|
+
integrity: str
|
144
|
+
|
145
|
+
@classmethod
|
146
|
+
def from_dict(cls, data: FirmwareUpdateFileInfoDataType) -> FirmwareUpdateFileInfo:
|
147
|
+
"""Initialize from dict."""
|
148
|
+
return cls(
|
149
|
+
target=data["target"],
|
150
|
+
url=data["url"],
|
151
|
+
integrity=data["integrity"],
|
152
|
+
)
|
153
|
+
|
154
|
+
def to_dict(self) -> FirmwareUpdateFileInfoDataType:
|
155
|
+
"""Return dict representation of the object."""
|
156
|
+
return cast(FirmwareUpdateFileInfoDataType, asdict(self))
|
157
|
+
|
158
|
+
|
159
|
+
class FirmwareUpdateFileInfoDataType(TypedDict):
|
160
|
+
"""Represent a firmware update file info data dict type."""
|
161
|
+
|
162
|
+
target: int
|
163
|
+
url: str
|
164
|
+
integrity: str # sha256
|
165
|
+
|
166
|
+
|
167
|
+
@dataclass
|
168
|
+
class FirmwareUpdateProgress:
|
169
|
+
"""Model for a firmware update progress."""
|
170
|
+
|
171
|
+
data: FirmwareUpdateProgressDataType = field(repr=False)
|
172
|
+
sent_fragments: int = field(init=False)
|
173
|
+
total_fragments: int = field(init=False)
|
174
|
+
progress: float = field(init=False)
|
175
|
+
|
176
|
+
def __post_init__(self) -> None:
|
177
|
+
"""Post initialize."""
|
178
|
+
self.sent_fragments = self.data["sentFragments"]
|
179
|
+
self.total_fragments = self.data["totalFragments"]
|
180
|
+
self.progress = float(self.data["progress"])
|
181
|
+
|
182
|
+
|
183
|
+
class FirmwareUpdateProgressDataType(TypedDict):
|
184
|
+
"""Represent a firmware update progress dict type."""
|
185
|
+
|
186
|
+
sentFragments: int
|
187
|
+
totalFragments: int
|
188
|
+
progress: float
|
189
|
+
|
190
|
+
|
191
|
+
@dataclass
|
192
|
+
class FirmwareUpdateResult:
|
193
|
+
"""Model for firmware update result data."""
|
194
|
+
|
195
|
+
data: FirmwareUpdateResultDataType = field(repr=False)
|
196
|
+
status: IntEnum = field(init=False)
|
197
|
+
success: bool = field(init=False)
|
198
|
+
_status_class: type[IntEnum] = field(init=False, repr=False)
|
199
|
+
|
200
|
+
def __post_init__(self) -> None:
|
201
|
+
"""Post initialize."""
|
202
|
+
self.status = self._status_class(self.data["status"])
|
203
|
+
self.success = self.data["success"]
|
204
|
+
|
205
|
+
|
206
|
+
class FirmwareUpdateResultDataType(TypedDict):
|
207
|
+
"""Represent a driver firmware update result dict type."""
|
208
|
+
|
209
|
+
status: int
|
210
|
+
success: bool
|
@@ -503,10 +503,7 @@ class Node(EventBase):
|
|
503
503
|
if wait_for_result:
|
504
504
|
result = await self.client.async_send_command(message, **kwargs)
|
505
505
|
return result
|
506
|
-
if wait_for_result is None and self.status not in (
|
507
|
-
NodeStatus.ASLEEP,
|
508
|
-
NodeStatus.DEAD,
|
509
|
-
):
|
506
|
+
if wait_for_result is None and self.status not in (NodeStatus.ASLEEP,):
|
510
507
|
result_task = asyncio.create_task(
|
511
508
|
self.client.async_send_command(message, **kwargs)
|
512
509
|
)
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
from typing import Literal
|
6
6
|
|
7
|
+
from pydantic import BaseModel
|
8
|
+
|
7
9
|
from ...const import CommandClass
|
8
10
|
from ...event import BaseEventModel
|
9
11
|
from ..notification import (
|
@@ -20,11 +22,6 @@ from .firmware import (
|
|
20
22
|
)
|
21
23
|
from .statistics import NodeStatisticsDataType
|
22
24
|
|
23
|
-
try:
|
24
|
-
from pydantic.v1 import BaseModel
|
25
|
-
except ImportError:
|
26
|
-
from pydantic import BaseModel
|
27
|
-
|
28
25
|
|
29
26
|
class BaseNodeEventModel(BaseEventModel):
|
30
27
|
"""Base model for a node event."""
|
@@ -101,8 +98,8 @@ class InterviewFailedEventArgsModel(BaseModel):
|
|
101
98
|
|
102
99
|
errorMessage: str
|
103
100
|
isFinal: bool
|
104
|
-
attempt: int | None
|
105
|
-
maxAttempts: int | None
|
101
|
+
attempt: int | None = None
|
102
|
+
maxAttempts: int | None = None
|
106
103
|
|
107
104
|
@classmethod
|
108
105
|
def from_dict(cls, data: dict) -> InterviewFailedEventArgsModel:
|