commit 8ffc1aec111a74d7e29919058380415d0315db4f parent 73d3ff4d595a1b23d627bc0a55b4f88a7bdc4738 Author: Stéphan Kochen <git@stephank.nl> Date: Wed, 30 Sep 2026 19:32:19 +0200 Bind to address, not just name
diff --git a/custom_components/inlite/__init__.py b/custom_components/inlite/__init__.py index 5dbc86b3c5c0615b50157b1f4578b9fd35b329a6..c0755f9aad04c74611e1cc66e372a8b7ece09809 100644 --- a/custom_components/inlite/__init__.py +++ b/custom_components/inlite/__init__.py @@ -25,7 +25,7 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.event import async_track_time_change from homeassistant.helpers.update_coordinator import UpdateFailed -from .const import BLE_LOCAL_NAME, CONF_CONFIRM, DOMAIN +from .const import CONF_CONFIRM, CONF_GARDEN_ID, DOMAIN from .coordinator import InliteCoordinator # Preload the config flow with the integration package. Home Assistant imports # package-level modules in its import executor; loading this later for options @@ -47,9 +47,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: InliteConfigEntry) -> bool: """Set up in-lite from a config entry.""" coordinator = InliteCoordinator(hass, entry) - # Register a BLE callback that: - # 1. Suppresses future discovery notifications for this device - # 2. Keeps the BLE device reference fresh (critical for ESPHome proxies) @callback def _async_update_ble( service_info: bluetooth.BluetoothServiceInfoBleak, @@ -62,8 +59,8 @@ entry.async_on_unload( bluetooth.async_register_callback( hass, _async_update_ble, - BluetoothCallbackMatcher(local_name=BLE_LOCAL_NAME), - BluetoothScanningMode.PASSIVE, + BluetoothCallbackMatcher(address=entry.data[CONF_GARDEN_ID]), + BluetoothScanningMode.ACTIVE, ) ) diff --git a/custom_components/inlite/coordinator.py b/custom_components/inlite/coordinator.py index eceba53a0b41c0ff9206f03a01dca9d7aa8959bd..90dfe307214d7165eebf226b087a40c73b5d2ed9 100644 --- a/custom_components/inlite/coordinator.py +++ b/custom_components/inlite/coordinator.py @@ -22,8 +22,8 @@ from inlite_ble.hub import InliteHub, ZoneState from .const import ( - BLE_LOCAL_NAME, CONF_IDLE_DISCONNECT, + CONF_GARDEN_ID, CONF_NETWORK_KEY, CONF_PASSWORD, CONF_SCAN_INTERVAL, @@ -65,6 +65,7 @@ name=DOMAIN, update_interval=timedelta(seconds=scan_interval), ) self.entry = entry + self._gateway_address: str = entry.data[CONF_GARDEN_ID] self._hubs: dict[int, InliteHub] = {} self._available = False self._ble_lock = asyncio.Lock() @@ -146,13 +147,12 @@ }, ) def _find_ble_device(self) -> bluetooth.BluetoothServiceInfoBleak | None: - """Find the in-lite hub, preferring the cached reference.""" + """Find this entry's gateway, preferring the freshest callback data.""" if self._ble_service_info is not None: return self._ble_service_info - for info in bluetooth.async_discovered_service_info(self.hass, connectable=True): - if info.name and info.name.lower() == BLE_LOCAL_NAME: - return info - return None + return bluetooth.async_last_service_info( + self.hass, self._gateway_address, connectable=True + ) async def _ensure_connected(self, hub: InliteHub) -> None: """Ensure the hub has an active BLE connection, reconnecting if needed.""" @@ -227,6 +227,18 @@ """ async with self._ble_lock: self._cancel_idle_disconnect() all_states: dict[int, dict[int, ZoneState]] = {} + + # A missing advertisement cannot be recovered by reconnecting the + # logical mesh hub. Fail the whole refresh once and let HA retry + # setup/polling when the address becomes visible again. + if ( + (self._client is None or not self._client.is_connected) + and self._find_ble_device() is None + ): + self._available = False + raise UpdateFailed( + f"in-lite gateway {self._gateway_address} is not available" + ) for device_id, hub in self._hubs.items(): for attempt in range(MAX_POLL_ATTEMPTS):