diff --git a/homeassistant/components/idasen_desk/__init__.py b/homeassistant/components/idasen_desk/__init__.py index 77af68da12e..1ea9b3b2f00 100644 --- a/homeassistant/components/idasen_desk/__init__.py +++ b/homeassistant/components/idasen_desk/__init__.py @@ -2,12 +2,10 @@ from __future__ import annotations -import asyncio import logging from attr import dataclass from bleak.exc import BleakError -from idasen_ha import Desk from idasen_ha.errors import AuthFailedError from homeassistant.components import bluetooth @@ -23,84 +21,15 @@ from homeassistant.core import Event, HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import DOMAIN +from .coordinator import IdasenDeskCoordinator PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.COVER, Platform.SENSOR] _LOGGER = logging.getLogger(__name__) -class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]): # pylint: disable=hass-enforce-coordinator-module - """Class to manage updates for the Idasen Desk.""" - - def __init__( - self, - hass: HomeAssistant, - logger: logging.Logger, - name: str, - address: str, - ) -> None: - """Init IdasenDeskCoordinator.""" - - super().__init__(hass, logger, name=name) - self._address = address - self._expected_connected = False - self._connection_lost = False - self._disconnect_lock = asyncio.Lock() - - self.desk = Desk(self.async_set_updated_data) - - async def async_connect(self) -> bool: - """Connect to desk.""" - _LOGGER.debug("Trying to connect %s", self._address) - ble_device = bluetooth.async_ble_device_from_address( - self.hass, self._address, connectable=True - ) - if ble_device is None: - _LOGGER.debug("No BLEDevice for %s", self._address) - return False - self._expected_connected = True - await self.desk.connect(ble_device) - return True - - async def async_disconnect(self) -> None: - """Disconnect from desk.""" - _LOGGER.debug("Disconnecting from %s", self._address) - self._expected_connected = False - self._connection_lost = False - await self.desk.disconnect() - - async def async_ensure_connection_state(self) -> None: - """Check if the expected connection state matches the current state. - - If the expected and current state don't match, calls connect/disconnect - as needed. - """ - if self._expected_connected: - if not self.desk.is_connected: - _LOGGER.debug("Desk disconnected. Reconnecting") - self._connection_lost = True - await self.async_connect() - elif self._connection_lost: - _LOGGER.info("Reconnected to desk") - self._connection_lost = False - elif self.desk.is_connected: - if self._disconnect_lock.locked(): - _LOGGER.debug("Already disconnecting") - return - async with self._disconnect_lock: - _LOGGER.debug("Desk is connected but should not be. Disconnecting") - await self.desk.disconnect() - - @callback - def async_set_updated_data(self, data: int | None) -> None: - """Handle data update.""" - self.hass.async_create_task(self.async_ensure_connection_state()) - return super().async_set_updated_data(data) - - @dataclass class DeskData: """Data for the Idasen Desk integration.""" diff --git a/homeassistant/components/idasen_desk/coordinator.py b/homeassistant/components/idasen_desk/coordinator.py new file mode 100644 index 00000000000..5bdf1b37331 --- /dev/null +++ b/homeassistant/components/idasen_desk/coordinator.py @@ -0,0 +1,83 @@ +"""Coordinator for the IKEA Idasen Desk integration.""" + +from __future__ import annotations + +import asyncio +import logging + +from idasen_ha import Desk + +from homeassistant.components import bluetooth +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +_LOGGER = logging.getLogger(__name__) + + +class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]): + """Class to manage updates for the Idasen Desk.""" + + def __init__( + self, + hass: HomeAssistant, + logger: logging.Logger, + name: str, + address: str, + ) -> None: + """Init IdasenDeskCoordinator.""" + + super().__init__(hass, logger, name=name) + self._address = address + self._expected_connected = False + self._connection_lost = False + self._disconnect_lock = asyncio.Lock() + + self.desk = Desk(self.async_set_updated_data) + + async def async_connect(self) -> bool: + """Connect to desk.""" + _LOGGER.debug("Trying to connect %s", self._address) + ble_device = bluetooth.async_ble_device_from_address( + self.hass, self._address, connectable=True + ) + if ble_device is None: + _LOGGER.debug("No BLEDevice for %s", self._address) + return False + self._expected_connected = True + await self.desk.connect(ble_device) + return True + + async def async_disconnect(self) -> None: + """Disconnect from desk.""" + _LOGGER.debug("Disconnecting from %s", self._address) + self._expected_connected = False + self._connection_lost = False + await self.desk.disconnect() + + async def async_ensure_connection_state(self) -> None: + """Check if the expected connection state matches the current state. + + If the expected and current state don't match, calls connect/disconnect + as needed. + """ + if self._expected_connected: + if not self.desk.is_connected: + _LOGGER.debug("Desk disconnected. Reconnecting") + self._connection_lost = True + await self.async_connect() + elif self._connection_lost: + _LOGGER.info("Reconnected to desk") + self._connection_lost = False + elif self.desk.is_connected: + if self._disconnect_lock.locked(): + _LOGGER.debug("Already disconnecting") + return + async with self._disconnect_lock: + _LOGGER.debug("Desk is connected but should not be. Disconnecting") + await self.desk.disconnect() + + @callback + def async_set_updated_data(self, data: int | None) -> None: + """Handle data update.""" + self.hass.async_create_task(self.async_ensure_connection_state()) + return super().async_set_updated_data(data) diff --git a/tests/components/idasen_desk/conftest.py b/tests/components/idasen_desk/conftest.py index 8159039aff4..c621a54cd95 100644 --- a/tests/components/idasen_desk/conftest.py +++ b/tests/components/idasen_desk/conftest.py @@ -19,7 +19,9 @@ def mock_bluetooth(enable_bluetooth): @pytest.fixture(autouse=False) def mock_desk_api(): """Set up idasen desk API fixture.""" - with mock.patch("homeassistant.components.idasen_desk.Desk") as desk_patched: + with mock.patch( + "homeassistant.components.idasen_desk.coordinator.Desk" + ) as desk_patched: mock_desk = MagicMock() def mock_init(