mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Bump zwave_js lib to 0.43.0 and fix multi-file firmware updates (#79342)
This commit is contained in:
parent
2b27cfdabb
commit
27413cee19
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from functools import partial, wraps
|
from functools import partial, wraps
|
||||||
from typing import Any, Literal, cast
|
from typing import Any, Literal
|
||||||
|
|
||||||
from aiohttp import web, web_exceptions, web_request
|
from aiohttp import web, web_exceptions, web_request
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -27,7 +27,7 @@ from zwave_js_server.exceptions import (
|
|||||||
NotFoundError,
|
NotFoundError,
|
||||||
SetValueFailed,
|
SetValueFailed,
|
||||||
)
|
)
|
||||||
from zwave_js_server.firmware import begin_firmware_update
|
from zwave_js_server.firmware import update_firmware
|
||||||
from zwave_js_server.model.controller import (
|
from zwave_js_server.model.controller import (
|
||||||
ControllerStatistics,
|
ControllerStatistics,
|
||||||
InclusionGrant,
|
InclusionGrant,
|
||||||
@ -36,8 +36,9 @@ from zwave_js_server.model.controller import (
|
|||||||
)
|
)
|
||||||
from zwave_js_server.model.driver import Driver
|
from zwave_js_server.model.driver import Driver
|
||||||
from zwave_js_server.model.firmware import (
|
from zwave_js_server.model.firmware import (
|
||||||
FirmwareUpdateFinished,
|
FirmwareUpdateData,
|
||||||
FirmwareUpdateProgress,
|
FirmwareUpdateProgress,
|
||||||
|
FirmwareUpdateResult,
|
||||||
)
|
)
|
||||||
from zwave_js_server.model.log_config import LogConfig
|
from zwave_js_server.model.log_config import LogConfig
|
||||||
from zwave_js_server.model.log_message import LogMessage
|
from zwave_js_server.model.log_message import LogMessage
|
||||||
@ -1897,11 +1898,14 @@ async def websocket_is_node_firmware_update_in_progress(
|
|||||||
|
|
||||||
def _get_firmware_update_progress_dict(
|
def _get_firmware_update_progress_dict(
|
||||||
progress: FirmwareUpdateProgress,
|
progress: FirmwareUpdateProgress,
|
||||||
) -> dict[str, int]:
|
) -> dict[str, int | float]:
|
||||||
"""Get a dictionary of firmware update progress."""
|
"""Get a dictionary of firmware update progress."""
|
||||||
return {
|
return {
|
||||||
|
"current_file": progress.current_file,
|
||||||
|
"total_files": progress.total_files,
|
||||||
"sent_fragments": progress.sent_fragments,
|
"sent_fragments": progress.sent_fragments,
|
||||||
"total_fragments": progress.total_fragments,
|
"total_fragments": progress.total_fragments,
|
||||||
|
"progress": progress.progress,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1943,14 +1947,16 @@ async def websocket_subscribe_firmware_update_status(
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def forward_finished(event: dict) -> None:
|
def forward_finished(event: dict) -> None:
|
||||||
finished: FirmwareUpdateFinished = event["firmware_update_finished"]
|
finished: FirmwareUpdateResult = event["firmware_update_finished"]
|
||||||
connection.send_message(
|
connection.send_message(
|
||||||
websocket_api.event_message(
|
websocket_api.event_message(
|
||||||
msg[ID],
|
msg[ID],
|
||||||
{
|
{
|
||||||
"event": event["event"],
|
"event": event["event"],
|
||||||
"status": finished.status,
|
"status": finished.status,
|
||||||
|
"success": finished.success,
|
||||||
"wait_time": finished.wait_time,
|
"wait_time": finished.wait_time,
|
||||||
|
"reinterview": finished.reinterview,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -2052,21 +2058,20 @@ class FirmwareUploadView(HomeAssistantView):
|
|||||||
if "file" not in data or not isinstance(data["file"], web_request.FileField):
|
if "file" not in data or not isinstance(data["file"], web_request.FileField):
|
||||||
raise web_exceptions.HTTPBadRequest
|
raise web_exceptions.HTTPBadRequest
|
||||||
|
|
||||||
target = None
|
|
||||||
if "target" in data:
|
|
||||||
target = int(cast(str, data["target"]))
|
|
||||||
|
|
||||||
uploaded_file: web_request.FileField = data["file"]
|
uploaded_file: web_request.FileField = data["file"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await begin_firmware_update(
|
await update_firmware(
|
||||||
node.client.ws_server_url,
|
node.client.ws_server_url,
|
||||||
node,
|
node,
|
||||||
uploaded_file.filename,
|
[
|
||||||
await hass.async_add_executor_job(uploaded_file.file.read),
|
FirmwareUpdateData(
|
||||||
|
uploaded_file.filename,
|
||||||
|
await hass.async_add_executor_job(uploaded_file.file.read),
|
||||||
|
)
|
||||||
|
],
|
||||||
async_get_clientsession(hass),
|
async_get_clientsession(hass),
|
||||||
additional_user_agent_components=USER_AGENT,
|
additional_user_agent_components=USER_AGENT,
|
||||||
target=target,
|
|
||||||
)
|
)
|
||||||
except BaseZwaveJSServerError as err:
|
except BaseZwaveJSServerError as err:
|
||||||
raise web_exceptions.HTTPBadRequest(reason=str(err)) from err
|
raise web_exceptions.HTTPBadRequest(reason=str(err)) from err
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Z-Wave",
|
"name": "Z-Wave",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
||||||
"requirements": ["pyserial==3.5", "zwave-js-server-python==0.42.0"],
|
"requirements": ["pyserial==3.5", "zwave-js-server-python==0.43.0"],
|
||||||
"codeowners": ["@home-assistant/z-wave"],
|
"codeowners": ["@home-assistant/z-wave"],
|
||||||
"dependencies": ["usb", "http", "websocket_api"],
|
"dependencies": ["usb", "http", "websocket_api"],
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from math import floor
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from awesomeversion import AwesomeVersion
|
from awesomeversion import AwesomeVersion
|
||||||
@ -13,10 +12,9 @@ from zwave_js_server.const import NodeStatus
|
|||||||
from zwave_js_server.exceptions import BaseZwaveJSServerError, FailedZWaveCommand
|
from zwave_js_server.exceptions import BaseZwaveJSServerError, FailedZWaveCommand
|
||||||
from zwave_js_server.model.driver import Driver
|
from zwave_js_server.model.driver import Driver
|
||||||
from zwave_js_server.model.firmware import (
|
from zwave_js_server.model.firmware import (
|
||||||
FirmwareUpdateFinished,
|
|
||||||
FirmwareUpdateInfo,
|
FirmwareUpdateInfo,
|
||||||
FirmwareUpdateProgress,
|
FirmwareUpdateProgress,
|
||||||
FirmwareUpdateStatus,
|
FirmwareUpdateResult,
|
||||||
)
|
)
|
||||||
from zwave_js_server.model.node import Node as ZwaveNode
|
from zwave_js_server.model.node import Node as ZwaveNode
|
||||||
|
|
||||||
@ -91,9 +89,8 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
|
|||||||
self._poll_unsub: Callable[[], None] | None = None
|
self._poll_unsub: Callable[[], None] | None = None
|
||||||
self._progress_unsub: Callable[[], None] | None = None
|
self._progress_unsub: Callable[[], None] | None = None
|
||||||
self._finished_unsub: Callable[[], None] | None = None
|
self._finished_unsub: Callable[[], None] | None = None
|
||||||
self._num_files_installed: int = 0
|
|
||||||
self._finished_event = asyncio.Event()
|
self._finished_event = asyncio.Event()
|
||||||
self._finished_status: FirmwareUpdateStatus | None = None
|
self._result: FirmwareUpdateResult | None = None
|
||||||
|
|
||||||
# Entity class attributes
|
# Entity class attributes
|
||||||
self._attr_name = "Firmware"
|
self._attr_name = "Firmware"
|
||||||
@ -115,25 +112,14 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
|
|||||||
progress: FirmwareUpdateProgress = event["firmware_update_progress"]
|
progress: FirmwareUpdateProgress = event["firmware_update_progress"]
|
||||||
if not self._latest_version_firmware:
|
if not self._latest_version_firmware:
|
||||||
return
|
return
|
||||||
# We will assume that each file in the firmware update represents an equal
|
self._attr_in_progress = int(progress.progress)
|
||||||
# percentage of the overall progress. This is likely not true because each file
|
|
||||||
# may be a different size, but it's the best we can do since we don't know the
|
|
||||||
# total number of fragments across all files.
|
|
||||||
self._attr_in_progress = floor(
|
|
||||||
100
|
|
||||||
* (
|
|
||||||
self._num_files_installed
|
|
||||||
+ (progress.sent_fragments / progress.total_fragments)
|
|
||||||
)
|
|
||||||
/ len(self._latest_version_firmware.files)
|
|
||||||
)
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _update_finished(self, event: dict[str, Any]) -> None:
|
def _update_finished(self, event: dict[str, Any]) -> None:
|
||||||
"""Update install progress on event."""
|
"""Update install progress on event."""
|
||||||
finished: FirmwareUpdateFinished = event["firmware_update_finished"]
|
result: FirmwareUpdateResult = event["firmware_update_finished"]
|
||||||
self._finished_status = finished.status
|
self._result = result
|
||||||
self._finished_event.set()
|
self._finished_event.set()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -149,10 +135,9 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
|
|||||||
self._finished_unsub()
|
self._finished_unsub()
|
||||||
self._finished_unsub = None
|
self._finished_unsub = None
|
||||||
|
|
||||||
self._finished_status = None
|
self._result = None
|
||||||
self._finished_event.clear()
|
self._finished_event.clear()
|
||||||
self._num_files_installed = 0
|
self._attr_in_progress = False
|
||||||
self._attr_in_progress = 0
|
|
||||||
if write_state:
|
if write_state:
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -235,41 +220,23 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
|
|||||||
"firmware update finished", self._update_finished
|
"firmware update finished", self._update_finished
|
||||||
)
|
)
|
||||||
|
|
||||||
for file in firmware.files:
|
try:
|
||||||
try:
|
await self.driver.controller.async_firmware_update_ota(
|
||||||
await self.driver.controller.async_begin_ota_firmware_update(
|
self.node, firmware.files
|
||||||
self.node, file
|
|
||||||
)
|
|
||||||
except BaseZwaveJSServerError as err:
|
|
||||||
self._unsub_firmware_events_and_reset_progress()
|
|
||||||
raise HomeAssistantError(err) from err
|
|
||||||
|
|
||||||
# We need to block until we receive the `firmware update finished` event
|
|
||||||
await self._finished_event.wait()
|
|
||||||
# Clear the event so that a second firmware update blocks again
|
|
||||||
self._finished_event.clear()
|
|
||||||
assert self._finished_status is not None
|
|
||||||
|
|
||||||
# If status is not OK, we should throw an error to let the user know
|
|
||||||
if self._finished_status not in (
|
|
||||||
FirmwareUpdateStatus.OK_NO_RESTART,
|
|
||||||
FirmwareUpdateStatus.OK_RESTART_PENDING,
|
|
||||||
FirmwareUpdateStatus.OK_WAITING_FOR_ACTIVATION,
|
|
||||||
):
|
|
||||||
status = self._finished_status
|
|
||||||
self._unsub_firmware_events_and_reset_progress()
|
|
||||||
raise HomeAssistantError(status.name.replace("_", " ").title())
|
|
||||||
|
|
||||||
# If we get here, the firmware installation was successful and we need to
|
|
||||||
# update progress accordingly
|
|
||||||
self._num_files_installed += 1
|
|
||||||
self._attr_in_progress = floor(
|
|
||||||
100 * self._num_files_installed / len(firmware.files)
|
|
||||||
)
|
)
|
||||||
|
except BaseZwaveJSServerError as err:
|
||||||
|
self._unsub_firmware_events_and_reset_progress()
|
||||||
|
raise HomeAssistantError(err) from err
|
||||||
|
|
||||||
# Clear the status so we can get a new one
|
# We need to block until we receive the `firmware update finished` event
|
||||||
self._finished_status = None
|
await self._finished_event.wait()
|
||||||
self.async_write_ha_state()
|
assert self._result is not None
|
||||||
|
|
||||||
|
# If the update was not successful, we should throw an error to let the user know
|
||||||
|
if not self._result.success:
|
||||||
|
error_msg = self._result.status.name.replace("_", " ").title()
|
||||||
|
self._unsub_firmware_events_and_reset_progress()
|
||||||
|
raise HomeAssistantError(error_msg)
|
||||||
|
|
||||||
# If we get here, all files were installed successfully
|
# If we get here, all files were installed successfully
|
||||||
self._attr_installed_version = self._attr_latest_version = firmware.version
|
self._attr_installed_version = self._attr_latest_version = firmware.version
|
||||||
|
@ -2622,7 +2622,7 @@ zigpy==0.51.1
|
|||||||
zm-py==0.5.2
|
zm-py==0.5.2
|
||||||
|
|
||||||
# homeassistant.components.zwave_js
|
# homeassistant.components.zwave_js
|
||||||
zwave-js-server-python==0.42.0
|
zwave-js-server-python==0.43.0
|
||||||
|
|
||||||
# homeassistant.components.zwave_me
|
# homeassistant.components.zwave_me
|
||||||
zwave_me_ws==0.2.6
|
zwave_me_ws==0.2.6
|
||||||
|
@ -1814,7 +1814,7 @@ zigpy-znp==0.9.0
|
|||||||
zigpy==0.51.1
|
zigpy==0.51.1
|
||||||
|
|
||||||
# homeassistant.components.zwave_js
|
# homeassistant.components.zwave_js
|
||||||
zwave-js-server-python==0.42.0
|
zwave-js-server-python==0.43.0
|
||||||
|
|
||||||
# homeassistant.components.zwave_me
|
# homeassistant.components.zwave_me
|
||||||
zwave_me_ws==0.2.6
|
zwave_me_ws==0.2.6
|
||||||
|
@ -28,6 +28,7 @@ from zwave_js_server.model.controller import (
|
|||||||
ProvisioningEntry,
|
ProvisioningEntry,
|
||||||
QRProvisioningInformation,
|
QRProvisioningInformation,
|
||||||
)
|
)
|
||||||
|
from zwave_js_server.model.firmware import FirmwareUpdateData
|
||||||
from zwave_js_server.model.node import Node
|
from zwave_js_server.model.node import Node
|
||||||
|
|
||||||
from homeassistant.components.websocket_api import ERR_INVALID_FORMAT, ERR_NOT_FOUND
|
from homeassistant.components.websocket_api import ERR_INVALID_FORMAT, ERR_NOT_FOUND
|
||||||
@ -2815,18 +2816,20 @@ async def test_firmware_upload_view(
|
|||||||
client = await hass_client()
|
client = await hass_client()
|
||||||
device = get_device(hass, multisensor_6)
|
device = get_device(hass, multisensor_6)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.zwave_js.api.begin_firmware_update",
|
"homeassistant.components.zwave_js.api.update_firmware",
|
||||||
) as mock_cmd, patch.dict(
|
) as mock_cmd, patch.dict(
|
||||||
"homeassistant.components.zwave_js.api.USER_AGENT",
|
"homeassistant.components.zwave_js.api.USER_AGENT",
|
||||||
{"HomeAssistant": "0.0.0"},
|
{"HomeAssistant": "0.0.0"},
|
||||||
):
|
):
|
||||||
resp = await client.post(
|
resp = await client.post(
|
||||||
f"/api/zwave_js/firmware/upload/{device.id}",
|
f"/api/zwave_js/firmware/upload/{device.id}",
|
||||||
data={"file": firmware_file, "target": "15"},
|
data={"file": firmware_file},
|
||||||
|
)
|
||||||
|
assert mock_cmd.call_args[0][1:3] == (
|
||||||
|
multisensor_6,
|
||||||
|
[FirmwareUpdateData("file", bytes(10))],
|
||||||
)
|
)
|
||||||
assert mock_cmd.call_args[0][1:4] == (multisensor_6, "file", bytes(10))
|
|
||||||
assert mock_cmd.call_args[1] == {
|
assert mock_cmd.call_args[1] == {
|
||||||
"target": 15,
|
|
||||||
"additional_user_agent_components": {"HomeAssistant": "0.0.0"},
|
"additional_user_agent_components": {"HomeAssistant": "0.0.0"},
|
||||||
}
|
}
|
||||||
assert json.loads(await resp.text()) is None
|
assert json.loads(await resp.text()) is None
|
||||||
@ -2839,7 +2842,7 @@ async def test_firmware_upload_view_failed_command(
|
|||||||
client = await hass_client()
|
client = await hass_client()
|
||||||
device = get_device(hass, multisensor_6)
|
device = get_device(hass, multisensor_6)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.zwave_js.api.begin_firmware_update",
|
"homeassistant.components.zwave_js.api.update_firmware",
|
||||||
side_effect=FailedCommand("test", "test"),
|
side_effect=FailedCommand("test", "test"),
|
||||||
):
|
):
|
||||||
resp = await client.post(
|
resp = await client.post(
|
||||||
@ -3502,8 +3505,13 @@ async def test_subscribe_firmware_update_status(
|
|||||||
"source": "node",
|
"source": "node",
|
||||||
"event": "firmware update progress",
|
"event": "firmware update progress",
|
||||||
"nodeId": multisensor_6.node_id,
|
"nodeId": multisensor_6.node_id,
|
||||||
"sentFragments": 1,
|
"progress": {
|
||||||
"totalFragments": 10,
|
"currentFile": 1,
|
||||||
|
"totalFiles": 1,
|
||||||
|
"sentFragments": 1,
|
||||||
|
"totalFragments": 10,
|
||||||
|
"progress": 10.0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
multisensor_6.receive_event(event)
|
multisensor_6.receive_event(event)
|
||||||
@ -3511,8 +3519,11 @@ async def test_subscribe_firmware_update_status(
|
|||||||
msg = await ws_client.receive_json()
|
msg = await ws_client.receive_json()
|
||||||
assert msg["event"] == {
|
assert msg["event"] == {
|
||||||
"event": "firmware update progress",
|
"event": "firmware update progress",
|
||||||
|
"current_file": 1,
|
||||||
|
"total_files": 1,
|
||||||
"sent_fragments": 1,
|
"sent_fragments": 1,
|
||||||
"total_fragments": 10,
|
"total_fragments": 10,
|
||||||
|
"progress": 10.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
event = Event(
|
event = Event(
|
||||||
@ -3521,8 +3532,12 @@ async def test_subscribe_firmware_update_status(
|
|||||||
"source": "node",
|
"source": "node",
|
||||||
"event": "firmware update finished",
|
"event": "firmware update finished",
|
||||||
"nodeId": multisensor_6.node_id,
|
"nodeId": multisensor_6.node_id,
|
||||||
"status": 255,
|
"result": {
|
||||||
"waitTime": 10,
|
"status": 255,
|
||||||
|
"success": True,
|
||||||
|
"waitTime": 10,
|
||||||
|
"reInterview": False,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
multisensor_6.receive_event(event)
|
multisensor_6.receive_event(event)
|
||||||
@ -3531,7 +3546,9 @@ async def test_subscribe_firmware_update_status(
|
|||||||
assert msg["event"] == {
|
assert msg["event"] == {
|
||||||
"event": "firmware update finished",
|
"event": "firmware update finished",
|
||||||
"status": 255,
|
"status": 255,
|
||||||
|
"success": True,
|
||||||
"wait_time": 10,
|
"wait_time": 10,
|
||||||
|
"reinterview": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3551,8 +3568,13 @@ async def test_subscribe_firmware_update_status_initial_value(
|
|||||||
"source": "node",
|
"source": "node",
|
||||||
"event": "firmware update progress",
|
"event": "firmware update progress",
|
||||||
"nodeId": multisensor_6.node_id,
|
"nodeId": multisensor_6.node_id,
|
||||||
"sentFragments": 1,
|
"progress": {
|
||||||
"totalFragments": 10,
|
"currentFile": 1,
|
||||||
|
"totalFiles": 1,
|
||||||
|
"sentFragments": 1,
|
||||||
|
"totalFragments": 10,
|
||||||
|
"progress": 10.0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
multisensor_6.receive_event(event)
|
multisensor_6.receive_event(event)
|
||||||
@ -3574,8 +3596,11 @@ async def test_subscribe_firmware_update_status_initial_value(
|
|||||||
msg = await ws_client.receive_json()
|
msg = await ws_client.receive_json()
|
||||||
assert msg["event"] == {
|
assert msg["event"] == {
|
||||||
"event": "firmware update progress",
|
"event": "firmware update progress",
|
||||||
|
"current_file": 1,
|
||||||
|
"total_files": 1,
|
||||||
"sent_fragments": 1,
|
"sent_fragments": 1,
|
||||||
"total_fragments": 10,
|
"total_fragments": 10,
|
||||||
|
"progress": 10.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ async def test_update_entity_progress(
|
|||||||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
client.async_send_command.reset_mock()
|
||||||
client.async_send_command.return_value = None
|
client.async_send_command.return_value = {"success": False}
|
||||||
|
|
||||||
# Test successful install call without a version
|
# Test successful install call without a version
|
||||||
install_task = hass.async_create_task(
|
install_task = hass.async_create_task(
|
||||||
@ -352,8 +352,13 @@ async def test_update_entity_progress(
|
|||||||
"source": "node",
|
"source": "node",
|
||||||
"event": "firmware update progress",
|
"event": "firmware update progress",
|
||||||
"nodeId": node.node_id,
|
"nodeId": node.node_id,
|
||||||
"sentFragments": 1,
|
"progress": {
|
||||||
"totalFragments": 20,
|
"currentFile": 1,
|
||||||
|
"totalFiles": 1,
|
||||||
|
"sentFragments": 1,
|
||||||
|
"totalFragments": 20,
|
||||||
|
"progress": 5.0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
node.receive_event(event)
|
node.receive_event(event)
|
||||||
@ -370,7 +375,11 @@ async def test_update_entity_progress(
|
|||||||
"source": "node",
|
"source": "node",
|
||||||
"event": "firmware update finished",
|
"event": "firmware update finished",
|
||||||
"nodeId": node.node_id,
|
"nodeId": node.node_id,
|
||||||
"status": FirmwareUpdateStatus.OK_NO_RESTART,
|
"result": {
|
||||||
|
"status": FirmwareUpdateStatus.OK_NO_RESTART,
|
||||||
|
"success": True,
|
||||||
|
"reInterview": False,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -381,142 +390,7 @@ async def test_update_entity_progress(
|
|||||||
state = hass.states.get(UPDATE_ENTITY)
|
state = hass.states.get(UPDATE_ENTITY)
|
||||||
assert state
|
assert state
|
||||||
attrs = state.attributes
|
attrs = state.attributes
|
||||||
assert attrs[ATTR_IN_PROGRESS] == 0
|
assert attrs[ATTR_IN_PROGRESS] is False
|
||||||
assert attrs[ATTR_INSTALLED_VERSION] == "11.2.4"
|
|
||||||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
|
||||||
assert state.state == STATE_OFF
|
|
||||||
|
|
||||||
await install_task
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_entity_progress_multiple(
|
|
||||||
hass,
|
|
||||||
client,
|
|
||||||
climate_radio_thermostat_ct100_plus_different_endpoints,
|
|
||||||
integration,
|
|
||||||
):
|
|
||||||
"""Test update entity progress with multiple files."""
|
|
||||||
node = climate_radio_thermostat_ct100_plus_different_endpoints
|
|
||||||
client.async_send_command.return_value = FIRMWARE_UPDATE_MULTIPLE_FILES
|
|
||||||
|
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(days=1))
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get(UPDATE_ENTITY)
|
|
||||||
assert state
|
|
||||||
assert state.state == STATE_ON
|
|
||||||
attrs = state.attributes
|
|
||||||
assert attrs[ATTR_INSTALLED_VERSION] == "10.7"
|
|
||||||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
client.async_send_command.return_value = None
|
|
||||||
|
|
||||||
# Test successful install call without a version
|
|
||||||
install_task = hass.async_create_task(
|
|
||||||
hass.services.async_call(
|
|
||||||
UPDATE_DOMAIN,
|
|
||||||
SERVICE_INSTALL,
|
|
||||||
{
|
|
||||||
ATTR_ENTITY_ID: UPDATE_ENTITY,
|
|
||||||
},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Sleep so that task starts
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
|
|
||||||
state = hass.states.get(UPDATE_ENTITY)
|
|
||||||
assert state
|
|
||||||
attrs = state.attributes
|
|
||||||
assert attrs[ATTR_IN_PROGRESS] is True
|
|
||||||
|
|
||||||
node.receive_event(
|
|
||||||
Event(
|
|
||||||
type="firmware update progress",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "firmware update progress",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"sentFragments": 1,
|
|
||||||
"totalFragments": 20,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Block so HA can do its thing
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
|
|
||||||
# Validate that the progress is updated (two files means progress is 50% of 5)
|
|
||||||
state = hass.states.get(UPDATE_ENTITY)
|
|
||||||
assert state
|
|
||||||
attrs = state.attributes
|
|
||||||
assert attrs[ATTR_IN_PROGRESS] == 2
|
|
||||||
|
|
||||||
node.receive_event(
|
|
||||||
Event(
|
|
||||||
type="firmware update finished",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "firmware update finished",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"status": FirmwareUpdateStatus.OK_NO_RESTART,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Block so HA can do its thing
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
|
|
||||||
# One file done, progress should be 50%
|
|
||||||
state = hass.states.get(UPDATE_ENTITY)
|
|
||||||
assert state
|
|
||||||
attrs = state.attributes
|
|
||||||
assert attrs[ATTR_IN_PROGRESS] == 50
|
|
||||||
|
|
||||||
node.receive_event(
|
|
||||||
Event(
|
|
||||||
type="firmware update progress",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "firmware update progress",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"sentFragments": 1,
|
|
||||||
"totalFragments": 20,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Block so HA can do its thing
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
|
|
||||||
# Validate that the progress is updated (50% + 50% of 5)
|
|
||||||
state = hass.states.get(UPDATE_ENTITY)
|
|
||||||
assert state
|
|
||||||
attrs = state.attributes
|
|
||||||
assert attrs[ATTR_IN_PROGRESS] == 52
|
|
||||||
|
|
||||||
node.receive_event(
|
|
||||||
Event(
|
|
||||||
type="firmware update finished",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "firmware update finished",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"status": FirmwareUpdateStatus.OK_NO_RESTART,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Block so HA can do its thing
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
|
|
||||||
# Validate that progress is reset and entity reflects new version
|
|
||||||
state = hass.states.get(UPDATE_ENTITY)
|
|
||||||
assert state
|
|
||||||
attrs = state.attributes
|
|
||||||
assert attrs[ATTR_IN_PROGRESS] == 0
|
|
||||||
assert attrs[ATTR_INSTALLED_VERSION] == "11.2.4"
|
assert attrs[ATTR_INSTALLED_VERSION] == "11.2.4"
|
||||||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
@ -546,10 +420,11 @@ async def test_update_entity_install_failed(
|
|||||||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
client.async_send_command.reset_mock()
|
||||||
client.async_send_command.return_value = None
|
client.async_send_command.return_value = {"success": False}
|
||||||
|
|
||||||
async def call_install():
|
# Test install call - we expect it to finish fail
|
||||||
await hass.services.async_call(
|
install_task = hass.async_create_task(
|
||||||
|
hass.services.async_call(
|
||||||
UPDATE_DOMAIN,
|
UPDATE_DOMAIN,
|
||||||
SERVICE_INSTALL,
|
SERVICE_INSTALL,
|
||||||
{
|
{
|
||||||
@ -557,9 +432,7 @@ async def test_update_entity_install_failed(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
)
|
||||||
# Test install call - we expect it to raise
|
|
||||||
install_task = hass.async_create_task(call_install())
|
|
||||||
|
|
||||||
# Sleep so that task starts
|
# Sleep so that task starts
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
@ -570,8 +443,13 @@ async def test_update_entity_install_failed(
|
|||||||
"source": "node",
|
"source": "node",
|
||||||
"event": "firmware update progress",
|
"event": "firmware update progress",
|
||||||
"nodeId": node.node_id,
|
"nodeId": node.node_id,
|
||||||
"sentFragments": 1,
|
"progress": {
|
||||||
"totalFragments": 20,
|
"currentFile": 1,
|
||||||
|
"totalFiles": 1,
|
||||||
|
"sentFragments": 1,
|
||||||
|
"totalFragments": 20,
|
||||||
|
"progress": 5.0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
node.receive_event(event)
|
node.receive_event(event)
|
||||||
@ -588,7 +466,11 @@ async def test_update_entity_install_failed(
|
|||||||
"source": "node",
|
"source": "node",
|
||||||
"event": "firmware update finished",
|
"event": "firmware update finished",
|
||||||
"nodeId": node.node_id,
|
"nodeId": node.node_id,
|
||||||
"status": FirmwareUpdateStatus.ERROR_TIMEOUT,
|
"result": {
|
||||||
|
"status": FirmwareUpdateStatus.ERROR_TIMEOUT,
|
||||||
|
"success": False,
|
||||||
|
"reInterview": False,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -599,7 +481,7 @@ async def test_update_entity_install_failed(
|
|||||||
state = hass.states.get(UPDATE_ENTITY)
|
state = hass.states.get(UPDATE_ENTITY)
|
||||||
assert state
|
assert state
|
||||||
attrs = state.attributes
|
attrs = state.attributes
|
||||||
assert attrs[ATTR_IN_PROGRESS] == 0
|
assert attrs[ATTR_IN_PROGRESS] is False
|
||||||
assert attrs[ATTR_INSTALLED_VERSION] == "10.7"
|
assert attrs[ATTR_INSTALLED_VERSION] == "10.7"
|
||||||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
|
Loading…
x
Reference in New Issue
Block a user