Skip to content

Commit 3b11119

Browse files
Add driver firmware v15 commands (#1260)
* Add driver firmware v15 commands * Add parameters to async_firmware_update_otw * Bump min and max schema version to 44 * Put async_is_firmware_update_in_progress back * Fix updateInfo parameter * Require schema 44 for async_firmware_update_otw
1 parent 7233dcf commit 3b11119

File tree

9 files changed

+164
-27
lines changed

9 files changed

+164
-27
lines changed

test/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def version_data_fixture() -> dict[str, Any]:
246246
"serverVersion": "test_server_version",
247247
"homeId": "test_home_id",
248248
"minSchemaVersion": 0,
249-
"maxSchemaVersion": 43,
249+
"maxSchemaVersion": 44,
250250
}
251251

252252

test/model/common.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""Provide common tools for testing Z-Wave JS server models."""
2+
3+
from zwave_js_server.model.firmware import FirmwareUpdateInfoDataType
4+
5+
FIRMWARE_UPDATE_INFO = FirmwareUpdateInfoDataType(
6+
**{
7+
"version": "1.0.0",
8+
"changelog": "changelog",
9+
"channel": "stable",
10+
"files": [{"target": 0, "url": "http://example.com", "integrity": "test"}],
11+
"downgrade": True,
12+
"normalizedVersion": "1.0.0",
13+
"device": {
14+
"manufacturerId": 1,
15+
"productType": 2,
16+
"productId": 3,
17+
"firmwareVersion": "0.4.4",
18+
"rfRegion": 1,
19+
},
20+
}
21+
)

test/model/test_controller.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,7 @@
3737
from zwave_js_server.model.node.firmware import NodeFirmwareUpdateInfo
3838

3939
from .. import load_fixture
40-
41-
FIRMWARE_UPDATE_INFO = {
42-
"version": "1.0.0",
43-
"changelog": "changelog",
44-
"channel": "stable",
45-
"files": [{"target": 0, "url": "http://example.com", "integrity": "test"}],
46-
"downgrade": True,
47-
"normalizedVersion": "1.0.0",
48-
"device": {
49-
"manufacturerId": 1,
50-
"productType": 2,
51-
"productId": 3,
52-
"firmwareVersion": "0.4.4",
53-
"rfRegion": 1,
54-
},
55-
}
40+
from .common import FIRMWARE_UPDATE_INFO
5641

5742

5843
def test_from_state():

test/model/test_driver.py

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
"""Test the driver model."""
22

33
import json
4+
from typing import Any
45

56
import pytest
67

78
from zwave_js_server.const import LogLevel
89
from zwave_js_server.event import Event
910
from zwave_js_server.model import (
10-
driver as driver_pkg,
1111
log_config as log_config_pkg,
1212
log_message as log_message_pkg,
1313
)
14+
from zwave_js_server.model.driver import Driver
1415
from zwave_js_server.model.driver.firmware import DriverFirmwareUpdateStatus
16+
from zwave_js_server.model.firmware import FirmwareUpdateData, FirmwareUpdateInfo
1517

1618
from .. import load_fixture
19+
from ..common import MockCommandProtocol
20+
from .common import FIRMWARE_UPDATE_INFO
1721

1822

1923
def test_from_state(client, log_config):
2024
"""Test from_state method."""
2125
ws_msgs = load_fixture("basic_dump.txt").strip().split("\n")
2226

23-
driver = driver_pkg.Driver(
24-
client, json.loads(ws_msgs[0])["result"]["state"], log_config
25-
)
26-
assert driver == driver_pkg.Driver(
27+
driver = Driver(client, json.loads(ws_msgs[0])["result"]["state"], log_config)
28+
assert driver == Driver(
2729
client, json.loads(ws_msgs[0])["result"]["state"], log_config
2830
)
2931
assert driver != driver.controller.home_id
@@ -452,3 +454,93 @@ async def test_firmware_events(driver):
452454
assert result.status == DriverFirmwareUpdateStatus.OK
453455
assert result.success
454456
assert driver.firmware_update_progress is None
457+
458+
459+
@pytest.mark.parametrize("progress", [True, False])
460+
async def test_is_otw_firmware_update_in_progress(
461+
driver: Driver,
462+
uuid4: str,
463+
mock_command: MockCommandProtocol,
464+
progress: bool,
465+
) -> None:
466+
"""Test is_otw_firmware_update_in_progress command."""
467+
ack_commands = mock_command(
468+
{"command": "driver.is_otw_firmware_update_in_progress"},
469+
{"progress": progress},
470+
)
471+
assert await driver.async_is_otw_firmware_update_in_progress() is progress
472+
473+
assert len(ack_commands) == 1
474+
assert ack_commands[0] == {
475+
"command": "driver.is_otw_firmware_update_in_progress",
476+
"messageId": uuid4,
477+
}
478+
479+
480+
@pytest.mark.parametrize(
481+
("firmware_update_param", "expected_params"),
482+
[
483+
(
484+
{
485+
"update_data": FirmwareUpdateData(
486+
filename="test-file", file=b"test", file_format=None
487+
)
488+
},
489+
FirmwareUpdateData(
490+
filename="test-file", file=b"test", file_format=None
491+
).to_dict(),
492+
),
493+
(
494+
{"update_info": FirmwareUpdateInfo.from_dict(FIRMWARE_UPDATE_INFO)},
495+
{"updateInfo": FIRMWARE_UPDATE_INFO},
496+
),
497+
],
498+
)
499+
@pytest.mark.parametrize(("status", "success"), [(1, True), (0, False)])
500+
async def test_firmware_update_otw(
501+
driver: Driver,
502+
uuid4: str,
503+
mock_command: MockCommandProtocol,
504+
status: int,
505+
success: bool,
506+
firmware_update_param: dict[str, Any],
507+
expected_params: dict[str, Any],
508+
) -> None:
509+
"""Test firmware_update_otw command."""
510+
ack_commands = mock_command(
511+
{"command": "driver.firmware_update_otw", **expected_params},
512+
{"result": {"status": status, "success": success}},
513+
)
514+
result = await driver.async_firmware_update_otw(**firmware_update_param)
515+
516+
assert result.status == status
517+
assert result.success is success
518+
519+
assert len(ack_commands) == 1
520+
assert ack_commands[0] == {
521+
"command": "driver.firmware_update_otw",
522+
"messageId": uuid4,
523+
**expected_params,
524+
}
525+
526+
527+
@pytest.mark.parametrize(
528+
"firmware_update_param",
529+
[
530+
{}, # No parameters
531+
{
532+
"update_data": FirmwareUpdateData(
533+
filename="test-file", file=b"test", file_format=None
534+
),
535+
"update_info": FirmwareUpdateInfo.from_dict(FIRMWARE_UPDATE_INFO),
536+
}, # Invalid parameters
537+
],
538+
)
539+
async def test_firmware_update_otw_invalid_params(
540+
driver: Driver,
541+
firmware_update_param: dict[str, FirmwareUpdateData | FirmwareUpdateInfo],
542+
) -> None:
543+
"""Test firmware_update_otw command invalid parameters."""
544+
with pytest.raises(ValueError):
545+
# Should raise ValueError if no parameters are provided or both are provided
546+
await driver.async_firmware_update_otw(**firmware_update_param)

test/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ async def test_additional_user_agent_components(client_session, url):
463463
{
464464
"command": "initialize",
465465
"messageId": "initialize",
466-
"schemaVersion": 43,
466+
"schemaVersion": 44,
467467
"additionalUserAgentComponents": {
468468
"zwave-js-server-python": __version__,
469469
"foo": "bar",

test/test_dump.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ async def test_dump_additional_user_agent_components(
106106
{
107107
"command": "initialize",
108108
"messageId": "initialize",
109-
"schemaVersion": 43,
109+
"schemaVersion": 44,
110110
"additionalUserAgentComponents": {
111111
"zwave-js-server-python": __version__,
112112
"foo": "bar",

test/test_main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def test_dump_state(
5858
assert captured.out == (
5959
"{'type': 'version', 'driverVersion': 'test_driver_version', "
6060
"'serverVersion': 'test_server_version', 'homeId': 'test_home_id', "
61-
"'minSchemaVersion': 0, 'maxSchemaVersion': 43}\n"
61+
"'minSchemaVersion': 0, 'maxSchemaVersion': 44}\n"
6262
"{'type': 'result', 'success': True, 'result': {}, 'messageId': 'initialize'}\n"
6363
"test_result\n"
6464
)

zwave_js_server/const/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
__version__ = "0.66.0"
1212

1313
# minimal server schema version we can handle
14-
MIN_SERVER_SCHEMA_VERSION = 41
14+
MIN_SERVER_SCHEMA_VERSION = 44
1515
# 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
1717

1818
VALUE_UNKNOWN = "unknown"
1919

zwave_js_server/model/driver/__init__.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
from typing import TYPE_CHECKING, Any, Literal, cast
66

7+
from zwave_js_server.model.firmware import (
8+
FirmwareUpdateData,
9+
FirmwareUpdateDataDataType,
10+
FirmwareUpdateInfo,
11+
FirmwareUpdateInfoDataType,
12+
)
13+
714
from ...event import BaseEventModel, Event, EventBase
815
from ..config_manager import ConfigManager
916
from ..controller import Controller
@@ -242,6 +249,38 @@ async def async_install_config_update(self) -> bool:
242249
)
243250
return cast(bool, result["success"])
244251

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+
245284
async def async_set_preferred_scales(
246285
self, scales: dict[str | int, str | int]
247286
) -> None:

0 commit comments

Comments
 (0)