Allow smlight device to reboot before updating firmware data coordinator (#127442)

* Add delay before updating firmware coordinator

* fix update tests

* change sleep to 1s

* Timeout incase reboot fails

* update test

* test reboot timeout

* log hostname in warning
This commit is contained in:
TimL 2024-10-30 18:02:30 +11:00 committed by GitHub
parent c7c72231c7
commit 5f4103a4a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 71 additions and 5 deletions

View File

@ -23,6 +23,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import SmConfigEntry from . import SmConfigEntry
from .const import LOGGER
from .coordinator import SmFirmwareUpdateCoordinator, SmFwData from .coordinator import SmFirmwareUpdateCoordinator, SmFwData
from .entity import SmEntity from .entity import SmEntity
@ -159,7 +160,6 @@ class SmUpdateEntity(SmEntity, UpdateEntity):
def _update_done(self) -> None: def _update_done(self) -> None:
"""Handle cleanup for update done.""" """Handle cleanup for update done."""
self._finished_event.set() self._finished_event.set()
self.coordinator.in_progress = False
for remove_cb in self._unload: for remove_cb in self._unload:
remove_cb() remove_cb()
@ -178,7 +178,7 @@ class SmUpdateEntity(SmEntity, UpdateEntity):
@callback @callback
def _update_failed(self, event: MessageEvent) -> None: def _update_failed(self, event: MessageEvent) -> None:
self._update_done() self._update_done()
self.coordinator.in_progress = False
raise HomeAssistantError(f"Update failed for {self.name}") raise HomeAssistantError(f"Update failed for {self.name}")
async def async_install( async def async_install(
@ -197,5 +197,20 @@ class SmUpdateEntity(SmEntity, UpdateEntity):
# block until update finished event received # block until update finished event received
await self._finished_event.wait() await self._finished_event.wait()
# allow time for SLZB-06 to reboot before updating coordinator data
try:
async with asyncio.timeout(180):
while (
self.coordinator.in_progress
and self.installed_version != self._firmware.ver
):
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
await asyncio.sleep(1)
except TimeoutError:
LOGGER.warning(
"Timeout waiting for %s to reboot after update",
self.coordinator.data.info.hostname,
)
self.coordinator.in_progress = False
self._finished_event.clear() self._finished_event.clear()

View File

@ -1,6 +1,7 @@
"""Tests for the SMLIGHT update platform.""" """Tests for the SMLIGHT update platform."""
from unittest.mock import MagicMock from datetime import timedelta
from unittest.mock import MagicMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
from pysmlight import Firmware, Info from pysmlight import Firmware, Info
@ -88,7 +89,9 @@ async def test_update_setup(
await hass.config_entries.async_unload(entry.entry_id) await hass.config_entries.async_unload(entry.entry_id)
@patch("homeassistant.components.smlight.update.asyncio.sleep", return_value=None)
async def test_update_firmware( async def test_update_firmware(
mock_sleep: MagicMock,
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
@ -126,7 +129,7 @@ async def test_update_firmware(
sw_version="v2.5.2", sw_version="v2.5.2",
) )
freezer.tick(SCAN_FIRMWARE_INTERVAL) freezer.tick(timedelta(seconds=5))
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -216,6 +219,54 @@ async def test_update_firmware_failed(
assert state.attributes[ATTR_UPDATE_PERCENTAGE] is None assert state.attributes[ATTR_UPDATE_PERCENTAGE] is None
@patch("homeassistant.components.smlight.const.LOGGER.warning")
async def test_update_reboot_timeout(
mock_warning: MagicMock,
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_config_entry: MockConfigEntry,
mock_smlight_client: MagicMock,
) -> None:
"""Test firmware updates."""
await setup_integration(hass, mock_config_entry)
entity_id = "update.mock_title_core_firmware"
state = hass.states.get(entity_id)
assert state.state == STATE_ON
assert state.attributes[ATTR_INSTALLED_VERSION] == "v2.3.6"
assert state.attributes[ATTR_LATEST_VERSION] == "v2.5.2"
with (
patch(
"homeassistant.components.smlight.update.asyncio.timeout",
side_effect=TimeoutError,
),
patch(
"homeassistant.components.smlight.update.asyncio.sleep",
return_value=None,
),
):
await hass.services.async_call(
PLATFORM,
SERVICE_INSTALL,
{ATTR_ENTITY_ID: entity_id},
blocking=False,
)
assert len(mock_smlight_client.fw_update.mock_calls) == 1
event_function = get_mock_event_function(
mock_smlight_client, SmEvents.FW_UPD_done
)
event_function(MOCK_FIRMWARE_DONE)
freezer.tick(timedelta(seconds=5))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_warning.assert_called_once()
async def test_update_release_notes( async def test_update_release_notes(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,