Skip to content

Commit 7233dcf

Browse files
Refactor firmware models (#1262)
1 parent 13ed254 commit 7233dcf

File tree

3 files changed

+293
-179
lines changed

3 files changed

+293
-179
lines changed

zwave_js_server/model/driver/firmware.py

Lines changed: 15 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,25 @@
44

55
from dataclasses import dataclass, field
66
from enum import IntEnum
7-
from typing import TypedDict
87

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+
)
1016

1117

12-
class DriverFirmwareUpdateDataDataType(TypedDict, total=False):
18+
class DriverFirmwareUpdateDataDataType(FirmwareUpdateDataDataType):
1319
"""Represent a driver firmware update data dict type."""
1420

15-
filename: str # required
16-
file: str # required
17-
fileFormat: str
18-
1921

2022
@dataclass
21-
class DriverFirmwareUpdateData:
23+
class DriverFirmwareUpdateData(FirmwareUpdateData):
2224
"""Driver firmware update data."""
2325

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-
3826

3927
class DriverFirmwareUpdateStatus(IntEnum):
4028
"""Enum with all driver firmware update status values.
@@ -52,46 +40,26 @@ class DriverFirmwareUpdateStatus(IntEnum):
5240
OK = 255
5341

5442

55-
class DriverFirmwareUpdateProgressDataType(TypedDict):
43+
class DriverFirmwareUpdateProgressDataType(FirmwareUpdateProgressDataType):
5644
"""Represent a driver firmware update progress dict type."""
5745

58-
sentFragments: int
59-
totalFragments: int
60-
progress: float
61-
6246

6347
@dataclass
64-
class DriverFirmwareUpdateProgress:
48+
class DriverFirmwareUpdateProgress(FirmwareUpdateProgress):
6549
"""Model for a driver firmware update progress data."""
6650

6751
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)
7152

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"])
7753

78-
79-
class DriverFirmwareUpdateResultDataType(TypedDict):
54+
class DriverFirmwareUpdateResultDataType(FirmwareUpdateResultDataType):
8055
"""Represent a driver firmware update result dict type."""
8156

82-
status: int
83-
success: bool
84-
8557

8658
@dataclass
87-
class DriverFirmwareUpdateResult:
59+
class DriverFirmwareUpdateResult(FirmwareUpdateResult):
8860
"""Model for driver firmware update result data."""
8961

9062
data: DriverFirmwareUpdateResultDataType = field(repr=False)
9163
status: DriverFirmwareUpdateStatus = field(init=False)
9264
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

zwave_js_server/model/firmware.py

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

0 commit comments

Comments
 (0)