fix(update): kein Downgrade und keine parallelen Installs pro Lampe
This commit is contained in:
@@ -74,6 +74,7 @@ class NinlightUpdateEntity(NinlightEntity, UpdateEntity):
|
||||
self._offer: FirmwareImage | None = None
|
||||
self._evaluated_for: tuple[str, str, str] | None = None
|
||||
self._eval_lock = asyncio.Lock()
|
||||
self._install_lock = asyncio.Lock()
|
||||
self._attr_in_progress = False
|
||||
|
||||
@property
|
||||
@@ -168,6 +169,16 @@ class NinlightUpdateEntity(NinlightEntity, UpdateEntity):
|
||||
)
|
||||
|
||||
async def async_install(self, version: str | None, backup: bool, **kwargs: Any) -> None:
|
||||
# Only the call that owns this lock may touch in_progress/ota_in_progress below: without
|
||||
# this check, a second concurrent call would sail through HA's own in-progress guard (it
|
||||
# only rejects installed == latest) and its `finally` would clear the flags out from under
|
||||
# the first call while that one is still flashing the lamp.
|
||||
if self._install_lock.locked():
|
||||
raise _error("update_busy")
|
||||
async with self._install_lock:
|
||||
await self._async_install_locked()
|
||||
|
||||
async def _async_install_locked(self) -> None:
|
||||
shared = self.hass.data[DATA_SHARED]
|
||||
image = self._offer
|
||||
if image is None:
|
||||
@@ -176,8 +187,12 @@ class NinlightUpdateEntity(NinlightEntity, UpdateEntity):
|
||||
if not await self.hass.async_add_executor_job(shared.catalog.verify, image):
|
||||
raise _error("image_invalid")
|
||||
|
||||
# Re-check right before sending: settings may have changed since the offer.
|
||||
# Re-check right before sending: the lamp's firmware may have changed since the offer was
|
||||
# computed (e.g. a stale offer surviving a failed evaluation - see _async_evaluate) and
|
||||
# must never be flashed if it is no longer actually newer than what is running now.
|
||||
info = await self.coordinator.async_call(lambda c: c.get_info())
|
||||
if not _firmware_is_newer(image.version, str(info.get("fw_version"))):
|
||||
raise _error("no_update")
|
||||
settings = await self.coordinator.async_call(lambda c: c.get_settings())
|
||||
if info.get("device_type") != image.device_type:
|
||||
raise _error("image_invalid")
|
||||
|
||||
Reference in New Issue
Block a user