From 24c04cceee3ea89c2e1a4dfad519090c466cb74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Andr=C3=A9=20Roland?= Date: Sat, 13 Sep 2025 16:41:51 +0200 Subject: [PATCH] Reflect Verisure lock, alarm control panel and switch state immediately without cloud pull (#149479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: AbĂ­lio Costa --- .../verisure/alarm_control_panel.py | 25 ++++++--- homeassistant/components/verisure/lock.py | 55 ++++++++++--------- homeassistant/components/verisure/switch.py | 2 +- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/verisure/alarm_control_panel.py b/homeassistant/components/verisure/alarm_control_panel.py index 7ead1f014c8..db199b180f4 100644 --- a/homeassistant/components/verisure/alarm_control_panel.py +++ b/homeassistant/components/verisure/alarm_control_panel.py @@ -67,8 +67,13 @@ class VerisureAlarm( ) LOGGER.debug("Verisure set arm state %s", state) result = None + attempts = 0 while result is None: - await asyncio.sleep(0.5) + if attempts == 30: + break + if attempts > 1: + await asyncio.sleep(0.5) + attempts += 1 transaction = await self.hass.async_add_executor_job( self.coordinator.verisure.request, self.coordinator.verisure.poll_arm_state( @@ -81,8 +86,10 @@ class VerisureAlarm( .get("armStateChangePollResult", {}) .get("result") ) - - await self.coordinator.async_refresh() + LOGGER.debug("Result is %s", result) + if result == "OK": + self._attr_alarm_state = ALARM_STATE_TO_HA.get(state) + self.async_write_ha_state() async def async_alarm_disarm(self, code: str | None = None) -> None: """Send disarm command.""" @@ -108,16 +115,20 @@ class VerisureAlarm( "ARMED_AWAY", self.coordinator.verisure.arm_away(code) ) - @callback - def _handle_coordinator_update(self) -> None: - """Handle updated data from the coordinator.""" + def _update_alarm_attributes(self) -> None: + """Update alarm state and changed by from coordinator data.""" self._attr_alarm_state = ALARM_STATE_TO_HA.get( self.coordinator.data["alarm"]["statusType"] ) self._attr_changed_by = self.coordinator.data["alarm"].get("name") + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._update_alarm_attributes() super()._handle_coordinator_update() async def async_added_to_hass(self) -> None: """When entity is added to hass.""" await super().async_added_to_hass() - self._handle_coordinator_update() + self._update_alarm_attributes() diff --git a/homeassistant/components/verisure/lock.py b/homeassistant/components/verisure/lock.py index 76aeedd05fa..4d2229967a0 100644 --- a/homeassistant/components/verisure/lock.py +++ b/homeassistant/components/verisure/lock.py @@ -10,7 +10,7 @@ from verisure import Error as VerisureError from homeassistant.components.lock import LockEntity, LockState from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_CODE -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, @@ -70,7 +70,9 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt self._attr_unique_id = serial_number self.serial_number = serial_number - self._state: str | None = None + self._attr_is_locked = None + self._attr_changed_by = None + self._changed_method: str | None = None @property def device_info(self) -> DeviceInfo: @@ -92,20 +94,6 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt super().available and self.serial_number in self.coordinator.data["locks"] ) - @property - def changed_by(self) -> str | None: - """Last change triggered by.""" - return ( - self.coordinator.data["locks"][self.serial_number] - .get("user", {}) - .get("name") - ) - - @property - def changed_method(self) -> str: - """Last change method.""" - return self.coordinator.data["locks"][self.serial_number]["lockMethod"] - @property def code_format(self) -> str: """Return the configured code format.""" @@ -115,16 +103,9 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt return f"^\\d{{{digits}}}$" @property - def is_locked(self) -> bool: - """Return true if lock is locked.""" - return ( - self.coordinator.data["locks"][self.serial_number]["lockStatus"] == "LOCKED" - ) - - @property - def extra_state_attributes(self) -> dict[str, str]: + def extra_state_attributes(self) -> dict[str, str | None]: """Return the state attributes.""" - return {"method": self.changed_method} + return {"method": self._changed_method} async def async_unlock(self, **kwargs: Any) -> None: """Send unlock command.""" @@ -154,7 +135,7 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt target_state = "LOCKED" if state == LockState.LOCKED else "UNLOCKED" lock_status = None attempts = 0 - while lock_status != "OK": + while lock_status is None: if attempts == 30: break if attempts > 1: @@ -172,8 +153,10 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt .get("doorLockStateChangePollResult", {}) .get("result") ) + LOGGER.debug("Lock status is %s", lock_status) if lock_status == "OK": - self._state = state + self._attr_is_locked = state == LockState.LOCKED + self.async_write_ha_state() def disable_autolock(self) -> None: """Disable autolock on a doorlock.""" @@ -196,3 +179,21 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt LOGGER.debug("Enabling autolock on %s", self.serial_number) except VerisureError as ex: LOGGER.error("Could not enable autolock, %s", ex) + + def _update_lock_attributes(self) -> None: + """Update lock state, changed by, and method from coordinator data.""" + lock_data = self.coordinator.data["locks"][self.serial_number] + self._attr_is_locked = lock_data["lockStatus"] == "LOCKED" + self._attr_changed_by = lock_data.get("user", {}).get("name") + self._changed_method = lock_data["lockMethod"] + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._update_lock_attributes() + super()._handle_coordinator_update() + + async def async_added_to_hass(self) -> None: + """When entity is added to hass.""" + await super().async_added_to_hass() + self._update_lock_attributes() diff --git a/homeassistant/components/verisure/switch.py b/homeassistant/components/verisure/switch.py index 0deb1da5e95..bdd933c753b 100644 --- a/homeassistant/components/verisure/switch.py +++ b/homeassistant/components/verisure/switch.py @@ -99,4 +99,4 @@ class VerisureSmartplug(CoordinatorEntity[VerisureDataUpdateCoordinator], Switch ) self._state = state self._change_timestamp = monotonic() - await self.coordinator.async_request_refresh() + self.async_write_ha_state()