Remove connection state handling from Idasen Desk (#120242)

This commit is contained in:
Abílio Costa 2024-06-23 18:56:10 +01:00 committed by GitHub
parent 29da88d8f6
commit 480ffeda2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 20 additions and 90 deletions

View File

@ -68,7 +68,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
) -> None: ) -> None:
"""Update from a Bluetooth callback to ensure that a new BLEDevice is fetched.""" """Update from a Bluetooth callback to ensure that a new BLEDevice is fetched."""
_LOGGER.debug("Bluetooth callback triggered") _LOGGER.debug("Bluetooth callback triggered")
hass.async_create_task(coordinator.async_ensure_connection_state()) hass.async_create_task(coordinator.async_connect_if_expected())
entry.async_on_unload( entry.async_on_unload(
bluetooth.async_register_callback( bluetooth.async_register_callback(

View File

@ -2,13 +2,12 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import logging import logging
from idasen_ha import Desk from idasen_ha import Desk
from homeassistant.components import bluetooth from homeassistant.components import bluetooth
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -29,55 +28,29 @@ class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]):
super().__init__(hass, logger, name=name) super().__init__(hass, logger, name=name)
self._address = address self._address = address
self._expected_connected = False self._expected_connected = False
self._connection_lost = False
self._disconnect_lock = asyncio.Lock()
self.desk = Desk(self.async_set_updated_data) self.desk = Desk(self.async_set_updated_data)
async def async_connect(self) -> bool: async def async_connect(self) -> bool:
"""Connect to desk.""" """Connect to desk."""
_LOGGER.debug("Trying to connect %s", self._address) _LOGGER.debug("Trying to connect %s", self._address)
self._expected_connected = True
ble_device = bluetooth.async_ble_device_from_address( ble_device = bluetooth.async_ble_device_from_address(
self.hass, self._address, connectable=True self.hass, self._address, connectable=True
) )
if ble_device is None: if ble_device is None:
_LOGGER.debug("No BLEDevice for %s", self._address) _LOGGER.debug("No BLEDevice for %s", self._address)
return False return False
self._expected_connected = True
await self.desk.connect(ble_device) await self.desk.connect(ble_device)
return True return True
async def async_disconnect(self) -> None: async def async_disconnect(self) -> None:
"""Disconnect from desk.""" """Disconnect from desk."""
_LOGGER.debug("Disconnecting from %s", self._address)
self._expected_connected = False self._expected_connected = False
self._connection_lost = False _LOGGER.debug("Disconnecting from %s", self._address)
await self.desk.disconnect() await self.desk.disconnect()
async def async_ensure_connection_state(self) -> None: async def async_connect_if_expected(self) -> None:
"""Check if the expected connection state matches the current state. """Ensure that the desk is connected if that is the expected state."""
If the expected and current state don't match, calls connect/disconnect
as needed.
"""
if self._expected_connected: if self._expected_connected:
if not self.desk.is_connected:
_LOGGER.debug("Desk disconnected. Reconnecting")
self._connection_lost = True
await self.async_connect() 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)

View File

@ -12,5 +12,5 @@
"documentation": "https://www.home-assistant.io/integrations/idasen_desk", "documentation": "https://www.home-assistant.io/integrations/idasen_desk",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "silver", "quality_scale": "silver",
"requirements": ["idasen-ha==2.5.3"] "requirements": ["idasen-ha==2.6.1"]
} }

View File

@ -1140,7 +1140,7 @@ ical==8.0.1
icmplib==3.0 icmplib==3.0
# homeassistant.components.idasen_desk # homeassistant.components.idasen_desk
idasen-ha==2.5.3 idasen-ha==2.6.1
# homeassistant.components.network # homeassistant.components.network
ifaddr==0.2.0 ifaddr==0.2.0

View File

@ -936,7 +936,7 @@ ical==8.0.1
icmplib==3.0 icmplib==3.0
# homeassistant.components.idasen_desk # homeassistant.components.idasen_desk
idasen-ha==2.5.3 idasen-ha==2.6.1
# homeassistant.components.network # homeassistant.components.network
ifaddr==0.2.0 ifaddr==0.2.0

View File

@ -1,6 +1,5 @@
"""Test the IKEA Idasen Desk init.""" """Test the IKEA Idasen Desk init."""
import asyncio
from unittest import mock from unittest import mock
from unittest.mock import AsyncMock, MagicMock from unittest.mock import AsyncMock, MagicMock
@ -66,63 +65,21 @@ async def test_reconnect_on_bluetooth_callback(
mock_desk_api.connect.assert_called_once() mock_desk_api.connect.assert_called_once()
mock_register_callback.assert_called_once() mock_register_callback.assert_called_once()
mock_desk_api.is_connected = False
_, register_callback_args, _ = mock_register_callback.mock_calls[0] _, register_callback_args, _ = mock_register_callback.mock_calls[0]
bt_callback = register_callback_args[1] bt_callback = register_callback_args[1]
bt_callback(None, None)
await hass.async_block_till_done()
assert mock_desk_api.connect.call_count == 2
async def test_duplicated_disconnect_is_no_op(
hass: HomeAssistant, mock_desk_api: MagicMock
) -> None:
"""Test that calling disconnect while disconnecting is a no-op."""
await init_integration(hass)
await hass.services.async_call(
"button", "press", {"entity_id": "button.test_disconnect"}, blocking=True
)
await hass.async_block_till_done()
async def mock_disconnect():
await asyncio.sleep(0)
mock_desk_api.disconnect.reset_mock()
mock_desk_api.disconnect.side_effect = mock_disconnect
# Since the disconnect button was pressed but the desk indicates "connected",
# any update event will call disconnect()
mock_desk_api.is_connected = True
mock_desk_api.trigger_update_callback(None)
mock_desk_api.trigger_update_callback(None)
mock_desk_api.trigger_update_callback(None)
await hass.async_block_till_done()
mock_desk_api.disconnect.assert_called_once()
async def test_ensure_connection_state(
hass: HomeAssistant, mock_desk_api: MagicMock
) -> None:
"""Test that the connection state is ensured."""
await init_integration(hass)
mock_desk_api.connect.reset_mock() mock_desk_api.connect.reset_mock()
mock_desk_api.is_connected = False bt_callback(None, None)
mock_desk_api.trigger_update_callback(None)
await hass.async_block_till_done() await hass.async_block_till_done()
mock_desk_api.connect.assert_called_once() mock_desk_api.connect.assert_called_once()
mock_desk_api.connect.reset_mock()
await hass.services.async_call( await hass.services.async_call(
"button", "press", {"entity_id": "button.test_disconnect"}, blocking=True "button", "press", {"entity_id": "button.test_disconnect"}, blocking=True
) )
bt_callback(None, None)
await hass.async_block_till_done() await hass.async_block_till_done()
assert mock_desk_api.connect.call_count == 0
mock_desk_api.disconnect.reset_mock()
mock_desk_api.is_connected = True
mock_desk_api.trigger_update_callback(None)
await hass.async_block_till_done()
mock_desk_api.disconnect.assert_called_once()
async def test_unload_entry(hass: HomeAssistant, mock_desk_api: MagicMock) -> None: async def test_unload_entry(hass: HomeAssistant, mock_desk_api: MagicMock) -> None: