-
Notifications
You must be signed in to change notification settings - Fork 12
Gate operations on device availability (dead-node check) #889
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -75,7 +75,7 @@ class BaseLock: | |||||
| - Set hard_refresh_interval = None to disable | ||||||
|
|
||||||
| 4. Poll connection state: | ||||||
| - Periodic async_internal_is_connection_up() at connection_check_interval | ||||||
| - Periodic async_internal_is_integration_connected() at connection_check_interval | ||||||
| - Helps detect reconnects for integrations without config entry state signals | ||||||
| - Set connection_check_interval = None to disable | ||||||
|
|
||||||
|
|
@@ -144,11 +144,16 @@ async def _execute_rate_limited( | |||||
| **kwargs: Any, | ||||||
| ) -> Any: | ||||||
| """Execute operation with connection check, serialization, and delay.""" | ||||||
| if not await self.async_internal_is_connection_up(): | ||||||
| if not await self.async_internal_is_integration_connected(): | ||||||
| raise LockDisconnected( | ||||||
| f"Cannot {_OPERATION_MESSAGES[operation_type]} {self.lock.entity_id} - lock not connected" | ||||||
|
||||||
| f"Cannot {_OPERATION_MESSAGES[operation_type]} {self.lock.entity_id} - lock not connected" | |
| f"Cannot {_OPERATION_MESSAGES[operation_type]} {self.lock.entity_id} - integration not connected" |
Copilot
AI
Mar 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR description states that device availability "includes integration check", but BaseLock.is_device_available()/async_is_device_available() do not check integration connectivity (they only call the device hook). Either adjust the PR description or update the implementation/docstrings to match the intended semantics (e.g., make async_is_device_available incorporate async_is_integration_connected, or clarify that the operation gate checks both independently).
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -54,7 +54,7 @@ async def async_unload(self, remove_permanently: bool) -> None: | |||||
| else: | ||||||
| await self._store.async_save(self._data) | ||||||
|
|
||||||
| async def async_is_connection_up(self) -> bool: | ||||||
| async def async_is_integration_connected(self) -> bool: | ||||||
| """Return whether connection to lock is up.""" | ||||||
|
||||||
| """Return whether connection to lock is up.""" | |
| """Return whether the integration is connected.""" |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,7 +10,7 @@ | |||||||||||||||||
| from typing import Any | ||||||||||||||||||
|
|
||||||||||||||||||
| from zwave_js_server.client import Client | ||||||||||||||||||
| from zwave_js_server.const import CommandClass | ||||||||||||||||||
| from zwave_js_server.const import CommandClass, NodeStatus | ||||||||||||||||||
| from zwave_js_server.const.command_class.lock import ( | ||||||||||||||||||
| ATTR_CODE_SLOT, | ||||||||||||||||||
| ATTR_IN_USE, | ||||||||||||||||||
|
|
@@ -470,11 +470,18 @@ async def async_unload(self, remove_permanently: bool) -> None: | |||||||||||||||||
| self._listeners.clear() | ||||||||||||||||||
| await super().async_unload(remove_permanently) | ||||||||||||||||||
|
|
||||||||||||||||||
| async def async_is_connection_up(self) -> bool: | ||||||||||||||||||
| """Return whether connection to lock is up.""" | ||||||||||||||||||
| async def async_is_integration_connected(self) -> bool: | ||||||||||||||||||
| """Return whether the Z-Wave JS client is connected.""" | ||||||||||||||||||
| ready, _reason = self._get_client_state() | ||||||||||||||||||
| return ready | ||||||||||||||||||
|
|
||||||||||||||||||
| async def async_is_device_available(self) -> bool: | ||||||||||||||||||
| """Return whether the Z-Wave node is available for commands.""" | ||||||||||||||||||
| try: | ||||||||||||||||||
| return self.node.status != NodeStatus.DEAD | ||||||||||||||||||
| except Exception: # noqa: BLE001 | ||||||||||||||||||
|
||||||||||||||||||
| except Exception: # noqa: BLE001 | |
| except Exception as err: # noqa: BLE001 | |
| _LOGGER.debug( | |
| "Lock %s: failed to determine node availability: %s", | |
| getattr(self.lock, "entity_id", "unknown"), | |
| err, | |
| exc_info=True, | |
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The provider interface documentation here was updated for async_is_integration_connected, but it doesn't mention the new provider-agnostic device availability hook (is_device_available/async_is_device_available) that now gates operations. Please document the new hook (and that the default is True) so future provider implementations understand when/why to override it.