zwave-js-server-python 0.65.0__py3-none-any.whl → 0.67.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.
@@ -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.65.0"
11
+ __version__ = "0.67.0"
12
12
 
13
13
  # minimal server schema version we can handle
14
- MIN_SERVER_SCHEMA_VERSION = 41
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 = 43
16
+ MAX_SERVER_SCHEMA_VERSION = 44
17
17
 
18
18
  VALUE_UNKNOWN = "unknown"
19
19
 
zwave_js_server/event.py CHANGED
@@ -7,10 +7,7 @@ from dataclasses import dataclass, field
7
7
  import logging
8
8
  from typing import Literal
9
9
 
10
- try:
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, Optional
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: Optional[str] = None,
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
- if not (channel_2 := self.data.get("channel2")):
104
- self.channel_2 = None
105
- return
106
- self.channel_2 = ChannelRSSI(channel_2)
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: str
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) -> str | None: # TODO: In the dump this is an int.
168
- """Return manufacturer id (as defined in specs) as a 4-digit hex string."""
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 = create_model_from_typeddict(
57
- LogMessageDataType, __base__=BaseDriverEventModel
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:
@@ -4,37 +4,25 @@ from __future__ import annotations
4
4
 
5
5
  from dataclasses import dataclass, field
6
6
  from enum import IntEnum
7
- from typing import TypedDict
8
7
 
9
- from ...util.helpers import convert_bytes_to_base64
8
+ from zwave_js_server.model.firmware import (
9
+ FirmwareUpdateData,
10
+ FirmwareUpdateDataDataType,
11
+ FirmwareUpdateProgress,
12
+ FirmwareUpdateProgressDataType,
13
+ FirmwareUpdateResult,
14
+ FirmwareUpdateResultDataType,
15
+ )
10
16
 
11
17
 
12
- class DriverFirmwareUpdateDataDataType(TypedDict, total=False):
18
+ class DriverFirmwareUpdateDataDataType(FirmwareUpdateDataDataType):
13
19
  """Represent a driver firmware update data dict type."""
14
20
 
15
- filename: str # required
16
- file: str # required
17
- fileFormat: str
18
-
19
21
 
20
22
  @dataclass
21
- class DriverFirmwareUpdateData:
23
+ class DriverFirmwareUpdateData(FirmwareUpdateData):
22
24
  """Driver firmware update data."""
23
25
 
24
- filename: str
25
- file: bytes
26
- file_format: str | None = None
27
-
28
- def to_dict(self) -> DriverFirmwareUpdateDataDataType:
29
- """Convert firmware update data to dict."""
30
- data: DriverFirmwareUpdateDataDataType = {
31
- "filename": self.filename,
32
- "file": convert_bytes_to_base64(self.file),
33
- }
34
- if self.file_format is not None:
35
- data["fileFormat"] = self.file_format
36
- return data
37
-
38
26
 
39
27
  class DriverFirmwareUpdateStatus(IntEnum):
40
28
  """Enum with all driver firmware update status values.
@@ -52,46 +40,26 @@ class DriverFirmwareUpdateStatus(IntEnum):
52
40
  OK = 255
53
41
 
54
42
 
55
- class DriverFirmwareUpdateProgressDataType(TypedDict):
43
+ class DriverFirmwareUpdateProgressDataType(FirmwareUpdateProgressDataType):
56
44
  """Represent a driver firmware update progress dict type."""
57
45
 
58
- sentFragments: int
59
- totalFragments: int
60
- progress: float
61
-
62
46
 
63
47
  @dataclass
64
- class DriverFirmwareUpdateProgress:
48
+ class DriverFirmwareUpdateProgress(FirmwareUpdateProgress):
65
49
  """Model for a driver firmware update progress data."""
66
50
 
67
51
  data: DriverFirmwareUpdateProgressDataType = field(repr=False)
68
- sent_fragments: int = field(init=False)
69
- total_fragments: int = field(init=False)
70
- progress: float = field(init=False)
71
52
 
72
- def __post_init__(self) -> None:
73
- """Post initialize."""
74
- self.sent_fragments = self.data["sentFragments"]
75
- self.total_fragments = self.data["totalFragments"]
76
- self.progress = float(self.data["progress"])
77
53
 
78
-
79
- class DriverFirmwareUpdateResultDataType(TypedDict):
54
+ class DriverFirmwareUpdateResultDataType(FirmwareUpdateResultDataType):
80
55
  """Represent a driver firmware update result dict type."""
81
56
 
82
- status: int
83
- success: bool
84
-
85
57
 
86
58
  @dataclass
87
- class DriverFirmwareUpdateResult:
59
+ class DriverFirmwareUpdateResult(FirmwareUpdateResult):
88
60
  """Model for driver firmware update result data."""
89
61
 
90
62
  data: DriverFirmwareUpdateResultDataType = field(repr=False)
91
63
  status: DriverFirmwareUpdateStatus = field(init=False)
92
64
  success: bool = field(init=False)
93
-
94
- def __post_init__(self) -> None:
95
- """Post initialize."""
96
- self.status = DriverFirmwareUpdateStatus(self.data["status"])
97
- self.success = self.data["success"]
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:
@@ -2,52 +2,56 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import asdict, dataclass, field
5
+ from dataclasses import dataclass, field
6
6
  from enum import IntEnum
7
- from typing import TYPE_CHECKING, Literal, TypedDict, cast
8
-
9
- from ...const import VALUE_UNKNOWN, RFRegion
10
- from ...util.helpers import convert_bytes_to_base64
7
+ from typing import TYPE_CHECKING, Required, TypedDict, cast
8
+
9
+ from zwave_js_server.model.firmware import (
10
+ FirmwareUpdateData,
11
+ FirmwareUpdateDataDataType,
12
+ FirmwareUpdateDeviceID,
13
+ FirmwareUpdateDeviceIDDataType,
14
+ FirmwareUpdateFileInfo,
15
+ FirmwareUpdateFileInfoDataType,
16
+ FirmwareUpdateInfo,
17
+ FirmwareUpdateInfoDataType,
18
+ FirmwareUpdateProgress,
19
+ FirmwareUpdateProgressDataType,
20
+ FirmwareUpdateResult,
21
+ FirmwareUpdateResultDataType,
22
+ )
23
+
24
+ from ...const import VALUE_UNKNOWN
11
25
 
12
26
  if TYPE_CHECKING:
13
27
  from . import Node
14
28
 
15
29
 
16
- class NodeFirmwareUpdateDataDataType(TypedDict, total=False):
30
+ class NodeFirmwareUpdateDataDataType(FirmwareUpdateDataDataType):
17
31
  """Represent a firmware update data dict type."""
18
32
 
19
- filename: str # required
20
- file: str # required
21
- fileFormat: str
22
33
  firmwareTarget: int
23
34
 
24
35
 
25
36
  @dataclass
26
- class NodeFirmwareUpdateData:
37
+ class NodeFirmwareUpdateData(FirmwareUpdateData):
27
38
  """Firmware update data."""
28
39
 
29
- filename: str
30
- file: bytes
31
- file_format: str | None = None
32
40
  firmware_target: int | None = None
33
41
 
34
42
  def to_dict(self) -> NodeFirmwareUpdateDataDataType:
35
43
  """Convert firmware update data to dict."""
36
- data: NodeFirmwareUpdateDataDataType = {
37
- "filename": self.filename,
38
- "file": convert_bytes_to_base64(self.file),
39
- }
40
- if self.file_format is not None:
41
- data["fileFormat"] = self.file_format
44
+ data = super().to_dict()
45
+ node_data = cast(NodeFirmwareUpdateDataDataType, data)
42
46
  if self.firmware_target is not None:
43
- data["firmwareTarget"] = self.firmware_target
44
- return data
47
+ node_data["firmwareTarget"] = self.firmware_target
48
+ return node_data
45
49
 
46
50
 
47
51
  class NodeFirmwareUpdateCapabilitiesDataType(TypedDict, total=False):
48
52
  """Represent a firmware update capabilities dict type."""
49
53
 
50
- firmwareUpgradable: bool # required
54
+ firmwareUpgradable: Required[bool]
51
55
  firmwareTargets: list[int]
52
56
  continuesToFunction: bool | str
53
57
  supportsActivation: bool | str
@@ -56,7 +60,7 @@ class NodeFirmwareUpdateCapabilitiesDataType(TypedDict, total=False):
56
60
  class NodeFirmwareUpdateCapabilitiesDict(TypedDict, total=False):
57
61
  """Represent a dict from FirmwareUpdateCapabilities."""
58
62
 
59
- firmware_upgradable: bool # required
63
+ firmware_upgradable: Required[bool]
60
64
  firmware_targets: list[int]
61
65
  continues_to_function: bool | None
62
66
  supports_activation: bool | None
@@ -133,193 +137,125 @@ class NodeFirmwareUpdateStatus(IntEnum):
133
137
  OK_RESTART_PENDING = 255
134
138
 
135
139
 
136
- class NodeFirmwareUpdateProgressDataType(TypedDict):
140
+ class NodeFirmwareUpdateProgressDataType(FirmwareUpdateProgressDataType):
137
141
  """Represent a node firmware update progress dict type."""
138
142
 
139
143
  currentFile: int
140
144
  totalFiles: int
141
- sentFragments: int
142
- totalFragments: int
143
- progress: float
144
145
 
145
146
 
146
147
  @dataclass
147
- class NodeFirmwareUpdateProgress:
148
+ class NodeFirmwareUpdateProgress(FirmwareUpdateProgress):
148
149
  """Model for a node firmware update progress data."""
149
150
 
150
151
  node: Node
151
152
  data: NodeFirmwareUpdateProgressDataType = field(repr=False)
152
153
  current_file: int = field(init=False)
153
154
  total_files: int = field(init=False)
154
- sent_fragments: int = field(init=False)
155
- total_fragments: int = field(init=False)
156
- progress: float = field(init=False)
155
+
156
+ def __init__(self, node: Node, data: NodeFirmwareUpdateProgressDataType) -> None:
157
+ """Initialize the node firmware update progress.
158
+
159
+ Explicit init method to retain backwards compatibility
160
+ that requires the node as first parameter.
161
+ """
162
+ super().__init__(data)
163
+ self.node = node
157
164
 
158
165
  def __post_init__(self) -> None:
159
166
  """Post initialize."""
167
+ super().__post_init__()
160
168
  self.current_file = self.data["currentFile"]
161
169
  self.total_files = self.data["totalFiles"]
162
- self.sent_fragments = self.data["sentFragments"]
163
- self.total_fragments = self.data["totalFragments"]
164
- self.progress = float(self.data["progress"])
165
170
 
166
171
 
167
- class NodeFirmwareUpdateResultDataType(TypedDict, total=False):
172
+ class NodeFirmwareUpdateResultDataType(FirmwareUpdateResultDataType, total=False):
168
173
  """Represent a node firmware update result dict type."""
169
174
 
170
- status: int # required
171
- success: bool # required
172
175
  waitTime: int
173
- reInterview: bool # required
176
+ reInterview: Required[bool]
174
177
 
175
178
 
176
179
  @dataclass
177
- class NodeFirmwareUpdateResult:
180
+ class NodeFirmwareUpdateResult(FirmwareUpdateResult):
178
181
  """Model for node firmware update result data."""
179
182
 
180
183
  node: Node
181
184
  data: NodeFirmwareUpdateResultDataType = field(repr=False)
182
185
  status: NodeFirmwareUpdateStatus = field(init=False)
183
- success: bool = field(init=False)
184
186
  wait_time: int | None = field(init=False)
185
187
  reinterview: bool = field(init=False)
188
+ _status_class = NodeFirmwareUpdateStatus
189
+
190
+ def __init__(self, node: Node, data: NodeFirmwareUpdateResultDataType) -> None:
191
+ """Initialize the node firmware update result.
192
+
193
+ Explicit init method to retain backwards compatibility
194
+ that requires the node as first parameter.
195
+ """
196
+ super().__init__(data)
197
+ self.node = node
186
198
 
187
199
  def __post_init__(self) -> None:
188
200
  """Post initialize."""
189
- self.status = NodeFirmwareUpdateStatus(self.data["status"])
190
- self.success = self.data["success"]
201
+ super().__post_init__()
191
202
  self.wait_time = self.data.get("waitTime")
192
203
  self.reinterview = self.data["reInterview"]
193
204
 
194
205
 
195
- class NodeFirmwareUpdateFileInfoDataType(TypedDict):
196
- """Represent a firmware update file info data dict type."""
197
-
198
- target: int
199
- url: str
200
- integrity: str # sha256
206
+ class NodeFirmwareUpdateFileInfoDataType(FirmwareUpdateFileInfoDataType):
207
+ """Represent a node firmware update file info data dict type."""
201
208
 
202
209
 
203
210
  @dataclass
204
- class NodeFirmwareUpdateFileInfo:
211
+ class NodeFirmwareUpdateFileInfo(FirmwareUpdateFileInfo):
205
212
  """Represent a firmware update file info."""
206
213
 
207
- target: int
208
- url: str
209
- integrity: str
210
-
211
214
  @classmethod
212
215
  def from_dict(
213
216
  cls, data: NodeFirmwareUpdateFileInfoDataType
214
217
  ) -> NodeFirmwareUpdateFileInfo:
215
218
  """Initialize from dict."""
216
- return cls(
217
- target=data["target"],
218
- url=data["url"],
219
- integrity=data["integrity"],
220
- )
219
+ return cast(NodeFirmwareUpdateFileInfo, super().from_dict(data))
221
220
 
222
221
  def to_dict(self) -> NodeFirmwareUpdateFileInfoDataType:
223
222
  """Return dict representation of the object."""
224
- return cast(NodeFirmwareUpdateFileInfoDataType, asdict(self))
225
-
223
+ return super().to_dict()
226
224
 
227
- class NodeFirmwareUpdateDeviceIDDataType(TypedDict, total=False):
228
- """Represent a firmware update device ID dict type."""
229
225
 
230
- manufacturerId: int # required
231
- productType: int # required
232
- productId: int # required
233
- firmwareVersion: str # required
234
- rfRegion: int
226
+ class NodeFirmwareUpdateDeviceIDDataType(FirmwareUpdateDeviceIDDataType):
227
+ """Represent a node firmware update device ID data dict type."""
235
228
 
236
229
 
237
230
  @dataclass
238
- class NodeFirmwareUpdateDeviceID:
231
+ class NodeFirmwareUpdateDeviceID(FirmwareUpdateDeviceID):
239
232
  """Represent a firmware update device ID."""
240
233
 
241
- manufacturer_id: int
242
- product_type: int
243
- product_id: int
244
- firmware_version: str
245
- rf_region: RFRegion | None
246
-
247
234
  @classmethod
248
235
  def from_dict(
249
236
  cls, data: NodeFirmwareUpdateDeviceIDDataType
250
237
  ) -> NodeFirmwareUpdateDeviceID:
251
238
  """Initialize from dict."""
252
- return cls(
253
- manufacturer_id=data["manufacturerId"],
254
- product_type=data["productType"],
255
- product_id=data["productId"],
256
- firmware_version=data["firmwareVersion"],
257
- rf_region=RFRegion(data["rfRegion"]) if "rfRegion" in data else None,
258
- )
239
+ return cast(NodeFirmwareUpdateDeviceID, super().from_dict(data))
259
240
 
260
241
  def to_dict(self) -> NodeFirmwareUpdateDeviceIDDataType:
261
242
  """Return dict representation of the object."""
262
- data = {
263
- "manufacturerId": self.manufacturer_id,
264
- "productType": self.product_type,
265
- "productId": self.product_id,
266
- "firmwareVersion": self.firmware_version,
267
- }
268
- if self.rf_region is not None:
269
- data["rfRegion"] = self.rf_region
270
- return cast(NodeFirmwareUpdateDeviceIDDataType, data)
271
-
243
+ return super().to_dict()
272
244
 
273
- class NodeFirmwareUpdateInfoDataType(TypedDict, total=False):
274
- """Represent a firmware update info data dict type."""
275
245
 
276
- version: str
277
- changelog: str
278
- channel: Literal["stable", "beta"]
279
- files: list[NodeFirmwareUpdateFileInfoDataType]
280
- downgrade: bool
281
- normalizedVersion: str
282
- device: NodeFirmwareUpdateDeviceIDDataType
246
+ class NodeFirmwareUpdateInfoDataType(FirmwareUpdateInfoDataType):
247
+ """Represent a node firmware update info data dict type."""
283
248
 
284
249
 
285
250
  @dataclass
286
- class NodeFirmwareUpdateInfo:
251
+ class NodeFirmwareUpdateInfo(FirmwareUpdateInfo):
287
252
  """Represent a firmware update info."""
288
253
 
289
- version: str
290
- changelog: str
291
- channel: Literal["stable", "beta"]
292
- files: list[NodeFirmwareUpdateFileInfo]
293
- downgrade: bool
294
- normalized_version: str
295
- device: NodeFirmwareUpdateDeviceID
296
-
297
254
  @classmethod
298
255
  def from_dict(cls, data: NodeFirmwareUpdateInfoDataType) -> NodeFirmwareUpdateInfo:
299
256
  """Initialize from dict."""
300
- return cls(
301
- version=data["version"],
302
- changelog=data["changelog"],
303
- channel=data["channel"],
304
- files=[
305
- NodeFirmwareUpdateFileInfo.from_dict(file) for file in data["files"]
306
- ],
307
- downgrade=data["downgrade"],
308
- normalized_version=data["normalizedVersion"],
309
- device=NodeFirmwareUpdateDeviceID.from_dict(data["device"]),
310
- )
257
+ return cast(NodeFirmwareUpdateInfo, super().from_dict(data))
311
258
 
312
259
  def to_dict(self) -> NodeFirmwareUpdateInfoDataType:
313
260
  """Return dict representation of the object."""
314
- return cast(
315
- NodeFirmwareUpdateInfoDataType,
316
- {
317
- "version": self.version,
318
- "changelog": self.changelog,
319
- "channel": self.channel,
320
- "files": [file.to_dict() for file in self.files],
321
- "downgrade": self.downgrade,
322
- "normalizedVersion": self.normalized_version,
323
- "device": self.device.to_dict(),
324
- },
325
- )
261
+ return super().to_dict()
@@ -25,7 +25,7 @@ class NodeStatisticsDataType(TypedDict, total=False):
25
25
  commandsDroppedTX: int # required
26
26
  commandsDroppedRX: int # required
27
27
  timeoutResponse: int # required
28
- rtt: int
28
+ rtt: float
29
29
  rssi: int
30
30
  lwr: RouteStatisticsDataType
31
31
  nlwr: RouteStatisticsDataType
@@ -43,7 +43,7 @@ class NodeStatistics:
43
43
  commands_dropped_rx: int = field(init=False)
44
44
  commands_dropped_tx: int = field(init=False)
45
45
  timeout_response: int = field(init=False)
46
- rtt: int | None = field(init=False)
46
+ rtt: float | None = field(init=False)
47
47
  lwr: RouteStatistics | None = field(init=False, default=None)
48
48
  nlwr: RouteStatistics | None = field(init=False, default=None)
49
49
  last_seen: datetime | None = field(init=False, default=None)
@@ -40,7 +40,7 @@ class MetaDataType(TypedDict, total=False):
40
40
  label: str
41
41
  min: int | None
42
42
  max: int | None
43
- unit: str
43
+ unit: str | None
44
44
  states: dict[str, str]
45
45
  ccSpecific: dict[str, Any]
46
46
  valueChangeOptions: list[str]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zwave-js-server-python
3
- Version: 0.65.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>=1.10.0
21
+ Requires-Dist: pydantic>=2.0.0
22
22
  Dynamic: license-file
23
23
 
24
24
  # zwave-js-server-python
@@ -2,12 +2,12 @@ zwave_js_server/__init__.py,sha256=Ey3O4Tha56uU-M92oLJmQHupCJ7B9oZmxlQTo8pGUM8,4
2
2
  zwave_js_server/__main__.py,sha256=X6Zdbtv6QmYjsWeQ5OvtIRhtEyT1o3o0XvAmXtRrgoY,3659
3
3
  zwave_js_server/client.py,sha256=1kwujigWJU84-vT61vvYED2EeB-4JS_7gMqu4ipjlEI,19589
4
4
  zwave_js_server/dump.py,sha256=ZBhkC07ndrFd784zpogfjRJtAdEh8-y8ZycvrGf34sE,1523
5
- zwave_js_server/event.py,sha256=zz6hzhommpsqWOZul9JtI_RlX5hyn2KVNuAqH_0Cgsg,2435
5
+ zwave_js_server/event.py,sha256=FBZ86ns4bEi95UAMrcinNs2WKHUbR7plAErIc1Ed0lg,2368
6
6
  zwave_js_server/exceptions.py,sha256=8SY6FA8NiTEQgtauLR83F7m69gBGQviJ6O2obirH2po,6148
7
7
  zwave_js_server/firmware.py,sha256=57L7olhmtJ0FosEi3GtinNjfayudbQxYRYhpgrzsj90,2328
8
8
  zwave_js_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  zwave_js_server/version.py,sha256=PUwxOVcUMk5pOguQP9zAjiPjs1Nnmiwj3fcA4HJLiBg,401
10
- zwave_js_server/const/__init__.py,sha256=eD2a2MTdU9x5VH2GLsJQbZLASqphFn-pmAJE6x6lPs4,14264
10
+ zwave_js_server/const/__init__.py,sha256=oxX2Jq12Q2TKPUaNgYvTGxW_AwIfULadxw0SyhFtGVU,14264
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=IJ195hGKEi1FLLqKWMtC2ZhY9Jt5PACr7GYTyVPjkKM,820
13
13
  zwave_js_server/const/command_class/basic.py,sha256=cs0k7p5qxrwpR8-NvXQLtnfEsC6IlBAYiW79DaM_6Eg,96
@@ -32,31 +32,32 @@ zwave_js_server/model/__init__.py,sha256=XfyKH8lxZ3CB7CbRkOr5sR-eV0GrklZiBtNPmln
32
32
  zwave_js_server/model/association.py,sha256=4rpf8PbafCYCfNJGISAmnpfcvJZTo_jPDGm2M-BuiPk,878
33
33
  zwave_js_server/model/command_class.py,sha256=SDTyGKlz5tDUoBb4RgKwPGwPuJvZ77a_q2EllN4bnzU,1394
34
34
  zwave_js_server/model/device_class.py,sha256=WTeYuR9PoIMzfjNhE21XIlHoTA9yDNEuRPkFS1W6nts,1609
35
- zwave_js_server/model/device_config.py,sha256=HpM-DchbhcyjhkDrp-6b8YaX2Qqo7KZbWk60xcMIhFY,6562
35
+ zwave_js_server/model/device_config.py,sha256=ANKQLGpXYAk6ibZTr6zRQEj6i70sby7ctsRN1XwYjc4,6525
36
36
  zwave_js_server/model/duration.py,sha256=OW2OqReexL6GXxxLs__i5Vs3JCVoODgLpwPHU48yynU,1153
37
37
  zwave_js_server/model/endpoint.py,sha256=V_OWg4PLwExOSdY4cAiY5IkiJ2FH5MHF3tKG8O0X1Lo,11911
38
+ zwave_js_server/model/firmware.py,sha256=kSGtfvwurDi3fekcipl3658FZ5hIHgDBq46LSUDFn3c,6219
38
39
  zwave_js_server/model/log_config.py,sha256=uF7gJlr2dSBP2mBMYzOyFiV_T45PwGU98AM5c7qs2g0,1574
39
40
  zwave_js_server/model/log_message.py,sha256=SNicnss7LUZd6woMecNQ0ZKwghC9OmxNdA1vYnR-YAE,4450
40
41
  zwave_js_server/model/notification.py,sha256=-M9MZ1RUWmUCHuIU_So6h5sYhMILKxntGx3zBkKaqeo,6165
41
42
  zwave_js_server/model/statistics.py,sha256=in7S8oxWlG38tCEz1k3lxLjEsfY5yfsnD0bolcEeuHg,3233
42
43
  zwave_js_server/model/utils.py,sha256=VMoJQ99QRkpJleJhXR4eojbgObdZZeEfse5gonmK7Ic,1157
43
- zwave_js_server/model/value.py,sha256=Ku_VnX79WL82SD6Up01qQUNRldnCvi37HeEuTrTdBPU,13266
44
+ zwave_js_server/model/value.py,sha256=aHiWfmFym8t5aok2uxQoVHw375wbv-frNjAMAs6MSMg,13273
44
45
  zwave_js_server/model/version.py,sha256=EnyL6O8r2gSv7qLIuZ-sWnybG5JnJWw-IEE5UtIUjiM,1272
45
- zwave_js_server/model/config_manager/__init__.py,sha256=g1qEnadcfG7P8AJrUjxTeke9OBaKFiLVelgTQQJRMx4,1246
46
+ zwave_js_server/model/config_manager/__init__.py,sha256=zLcC-G4-skg7CDK_458T5ny-MGcUCzBxHDrb-e_ig1s,1233
46
47
  zwave_js_server/model/controller/__init__.py,sha256=d2htuMJhnwF9CdcaIK7sRExDJ1ljH6ypsKOk5mu6k4M,37378
47
48
  zwave_js_server/model/controller/data_model.py,sha256=5e6M0t7RkSJytovmDz1tULMFFoCQ1TsGHe56scrZRqI,851
48
49
  zwave_js_server/model/controller/event_model.py,sha256=BtacPbezXVByd9kuMSHysywLP3eCz5VgTdWs2NmA65M,9518
49
50
  zwave_js_server/model/controller/inclusion_and_provisioning.py,sha256=PAO5HQE_yyTMxoVN3Woth1qC_oL6EzJNJ7NEvMtpBEA,7667
50
51
  zwave_js_server/model/controller/rebuild_routes.py,sha256=ElAZdSLiDaQLbGgfJQMnuilTpsSbCz3qag3lFBt2JoM,1073
51
- zwave_js_server/model/controller/statistics.py,sha256=iU2wiRvWvHzYGwydOqs8Y7CBdKA3DgL6tI1NW1zBTfU,4672
52
- zwave_js_server/model/driver/__init__.py,sha256=VMqZAdbIq4h-udwTvLfQR1zyxA3QEoAojfkgMNwz6Lg,9275
53
- zwave_js_server/model/driver/firmware.py,sha256=pWSI6QYt4mJ-ff3Wd4Hyc3r-MeiJPkaAz24lFcz40Vs,2815
54
- zwave_js_server/model/node/__init__.py,sha256=OeHGrrjzsviGXWRv6HrLTlw70pmAagP6NwiRQACNxrE,41489
52
+ zwave_js_server/model/controller/statistics.py,sha256=s1wg-npEtXp-2yprych8BuQfG1NHq0fAZypqFz2T8hI,4954
53
+ zwave_js_server/model/driver/__init__.py,sha256=wXzUX0W8B3o4_RgOMFtAZ2s_xavSf8PwCk9_fTcOiAo,11870
54
+ zwave_js_server/model/driver/firmware.py,sha256=y5qcCpD2O6vCPl-eEGZiH4SHnaqegBlF2o6q0i1ygkY,1939
55
+ zwave_js_server/model/node/__init__.py,sha256=Qqav-yXwaRkeTnug2rn2ZPPSw7X6jCAEIudVnH27BbY,41438
55
56
  zwave_js_server/model/node/data_model.py,sha256=lhCUcOkJ-PYAJ6ItnDwX4WkrBCMHZTDtSerf3YAdbEE,1872
56
- zwave_js_server/model/node/event_model.py,sha256=mSRqCIBt6LXvCmMaOGmWz_cLrfPqot_ScZLmVr_z2w4,10055
57
- zwave_js_server/model/node/firmware.py,sha256=jF1CrI6hnETj8oZsXuwQVnIkTJ9qQ8uJEdY7JAthpC0,10214
57
+ zwave_js_server/model/node/event_model.py,sha256=Xx_4U6HVsw1j4Hpt63CrxX4JtiVSVf9e-opaP0grZDk,10002
58
+ zwave_js_server/model/node/firmware.py,sha256=hloB17eUCL1Rge9hh359bve3O5_Lp80MNap5X6zVj5E,8411
58
59
  zwave_js_server/model/node/health_check.py,sha256=GuDFPfk5BL7aK4q5aEtGcpyZz_F-aLEIGeVw_4shq5E,5341
59
- zwave_js_server/model/node/statistics.py,sha256=f8JElpXEtT7y9cLhH6UhOGVwrGprtfL5GQX1VIKAGcQ,2871
60
+ zwave_js_server/model/node/statistics.py,sha256=PkD_vsOcT_6Iwwlqy5ISBZRXFQ-alTOZwIpatRg0PFQ,2875
60
61
  zwave_js_server/util/__init__.py,sha256=ArF1K885aW0GdVd7Lo8fl8Atf70gvsxO5Ra_WoJTG28,42
61
62
  zwave_js_server/util/helpers.py,sha256=BNEaa5xSGlFwJrIIWt-Ne8BdHFOdcUnZOhZ-nsfDk2c,1638
62
63
  zwave_js_server/util/lock.py,sha256=QKl6BuzDhIP25HBB0Ba9Fl6drARXnyqV3u5ywQl0Te8,7422
@@ -66,9 +67,9 @@ zwave_js_server/util/command_class/__init__.py,sha256=sRxti47ekLTzfk8B609CMQumIb
66
67
  zwave_js_server/util/command_class/energy_production.py,sha256=K1VmGDlqXmKDfQRpTu5o99sjnDShBMV_crEb49o-O_4,1489
67
68
  zwave_js_server/util/command_class/meter.py,sha256=tJ7rbwWUZbJCS7xEJWS_KnqUUGR8RN0f2S8iLkufae0,1258
68
69
  zwave_js_server/util/command_class/multilevel_sensor.py,sha256=wG4GQ0kjrP6d3x5DpEkUHrZd8-0LbvXoYdIxZAf6bso,1427
69
- zwave_js_server_python-0.65.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
70
- zwave_js_server_python-0.65.0.dist-info/METADATA,sha256=BhcNcKhkENka1CwbOlJxBJWHvVz4iBpH0W-tnB6tG0c,1927
71
- zwave_js_server_python-0.65.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
72
- zwave_js_server_python-0.65.0.dist-info/entry_points.txt,sha256=lvzma7Rd_3FW_k-_xGuTfpvcvA2MR_22DOz5f1t7-xg,73
73
- zwave_js_server_python-0.65.0.dist-info/top_level.txt,sha256=-hwsl-i4Av5Op_yfOHC_OP56KPmzp_iVEkeohRIN5Ng,16
74
- zwave_js_server_python-0.65.0.dist-info/RECORD,,
70
+ zwave_js_server_python-0.67.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
71
+ zwave_js_server_python-0.67.0.dist-info/METADATA,sha256=H6r6_Efz63gNZIppDeHql5lSF5RNNwMMXUXyqLwmbcg,1926
72
+ zwave_js_server_python-0.67.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
73
+ zwave_js_server_python-0.67.0.dist-info/entry_points.txt,sha256=lvzma7Rd_3FW_k-_xGuTfpvcvA2MR_22DOz5f1t7-xg,73
74
+ zwave_js_server_python-0.67.0.dist-info/top_level.txt,sha256=-hwsl-i4Av5Op_yfOHC_OP56KPmzp_iVEkeohRIN5Ng,16
75
+ zwave_js_server_python-0.67.0.dist-info/RECORD,,