-
-
Notifications
You must be signed in to change notification settings - Fork 35.8k
Improve scan interval for Airthings Corentium Home 2 #155694
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: dev
Are you sure you want to change the base?
Changes from all commits
e024fa7
3564eb7
7133275
4afac76
cc6e552
9573a82
c0f2bb3
011c0a7
2bac611
8fa4fac
bd76df9
2d70980
f115848
bdd741c
04b786e
64c041a
3b1a873
46ea184
7db8444
d59ac29
af07f3d
c0627d6
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 |
|---|---|---|
|
|
@@ -5,7 +5,11 @@ | |
| from datetime import timedelta | ||
| import logging | ||
|
|
||
| from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice | ||
| from airthings_ble import ( | ||
| AirthingsBluetoothDeviceData, | ||
| AirthingsDevice, | ||
| AirthingsDeviceType, | ||
| ) | ||
| from bleak.backends.device import BLEDevice | ||
| from bleak_retry_connector import close_stale_connections_by_address | ||
|
|
||
|
|
@@ -16,7 +20,7 @@ | |
| from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed | ||
| from homeassistant.util.unit_system import METRIC_SYSTEM | ||
|
|
||
| from .const import DEFAULT_SCAN_INTERVAL, DOMAIN | ||
| from .const import DEFAULT_SCAN_INTERVAL, DEVICE_MODEL, DOMAIN, RADON_SCAN_INTERVAL | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
|
|
@@ -34,12 +38,19 @@ def __init__(self, hass: HomeAssistant, entry: AirthingsBLEConfigEntry) -> None: | |
| self.airthings = AirthingsBluetoothDeviceData( | ||
| _LOGGER, hass.config.units is METRIC_SYSTEM | ||
| ) | ||
|
|
||
| device_model = entry.data.get(DEVICE_MODEL) | ||
| if device_model == AirthingsDeviceType.CORENTIUM_HOME_2.value: | ||
| interval = RADON_SCAN_INTERVAL | ||
| else: | ||
| interval = DEFAULT_SCAN_INTERVAL | ||
|
|
||
| super().__init__( | ||
| hass, | ||
| _LOGGER, | ||
| config_entry=entry, | ||
| name=DOMAIN, | ||
| update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL), | ||
| update_interval=timedelta(seconds=interval), | ||
| ) | ||
|
|
||
| async def _async_setup(self) -> None: | ||
|
|
@@ -58,11 +69,26 @@ async def _async_setup(self) -> None: | |
| ) | ||
| self.ble_device = ble_device | ||
|
|
||
| if DEVICE_MODEL not in self.config_entry.data: | ||
| _LOGGER.debug("Fetching device info for migration") | ||
| try: | ||
| data = await self.airthings.update_device(self.ble_device) | ||
| except Exception as err: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be more specific here, ideally we should also update the one in async_update_data but that is outside of the scope There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Noted. Will try to handle this (+ the other one) in a separate PR. |
||
| raise UpdateFailed( | ||
| f"Unable to fetch data for migration: {err}" | ||
| ) from err | ||
|
|
||
| self.hass.config_entries.async_update_entry( | ||
| self.config_entry, | ||
| data={**self.config_entry.data, DEVICE_MODEL: data.model.value}, | ||
| ) | ||
| if data.model == AirthingsDeviceType.CORENTIUM_HOME_2: | ||
| self.update_interval = timedelta(seconds=RADON_SCAN_INTERVAL) | ||
|
|
||
| async def _async_update_data(self) -> AirthingsDevice: | ||
| """Get data from Airthings BLE.""" | ||
| try: | ||
| data = await self.airthings.update_device(self.ble_device) | ||
| except Exception as err: | ||
| raise UpdateFailed(f"Unable to fetch data: {err}") from err | ||
|
|
||
| return data | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| """Test the Airthings BLE integration init.""" | ||
|
|
||
| from datetime import timedelta | ||
|
|
||
| import pytest | ||
|
|
||
| from homeassistant.components.airthings_ble.const import ( | ||
| DEFAULT_SCAN_INTERVAL, | ||
| DOMAIN, | ||
| RADON_SCAN_INTERVAL, | ||
| ) | ||
| from homeassistant.core import HomeAssistant | ||
|
|
||
| from . import ( | ||
| CORENTIUM_HOME_2_DEVICE_INFO, | ||
| CORENTIUM_HOME_2_SERVICE_INFO, | ||
| WAVE_DEVICE_INFO, | ||
| WAVE_ENHANCE_DEVICE_INFO, | ||
| WAVE_ENHANCE_SERVICE_INFO, | ||
| WAVE_SERVICE_INFO, | ||
| patch_airthings_ble, | ||
| patch_async_ble_device_from_address, | ||
| ) | ||
|
|
||
| from tests.common import MockConfigEntry | ||
| from tests.components.bluetooth import inject_bluetooth_service_info | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| ("service_info", "device_info"), | ||
| [ | ||
| (WAVE_SERVICE_INFO, WAVE_DEVICE_INFO), | ||
| (WAVE_ENHANCE_SERVICE_INFO, WAVE_ENHANCE_DEVICE_INFO), | ||
| (CORENTIUM_HOME_2_SERVICE_INFO, CORENTIUM_HOME_2_DEVICE_INFO), | ||
| ], | ||
| ) | ||
| async def test_migration_existing_entries( | ||
| hass: HomeAssistant, | ||
| service_info, | ||
| device_info, | ||
| ) -> None: | ||
| """Test migration of existing config entry without device model.""" | ||
| entry = MockConfigEntry( | ||
| domain=DOMAIN, | ||
| unique_id=service_info.address, | ||
| data={}, | ||
| ) | ||
| entry.add_to_hass(hass) | ||
|
|
||
| inject_bluetooth_service_info(hass, service_info) | ||
|
|
||
| assert "device_model" not in entry.data | ||
|
|
||
| with ( | ||
| patch_async_ble_device_from_address(service_info.device), | ||
| patch_airthings_ble(device_info), | ||
| ): | ||
| await hass.config_entries.async_setup(entry.entry_id) | ||
| await hass.async_block_till_done() | ||
|
|
||
| # Migration should have added device_model to entry data | ||
| assert "device_model" in entry.data | ||
| assert entry.data["device_model"] == device_info.model.value | ||
|
|
||
|
|
||
| async def test_no_migration_when_device_model_exists( | ||
| hass: HomeAssistant, | ||
| ) -> None: | ||
| """Test that migration does not run when device_model already exists.""" | ||
| entry = MockConfigEntry( | ||
| domain=DOMAIN, | ||
| unique_id=WAVE_SERVICE_INFO.address, | ||
| data={"device_model": WAVE_DEVICE_INFO.model.value}, | ||
| ) | ||
| entry.add_to_hass(hass) | ||
|
|
||
| inject_bluetooth_service_info(hass, WAVE_SERVICE_INFO) | ||
|
|
||
| with ( | ||
| patch_async_ble_device_from_address(WAVE_SERVICE_INFO.device), | ||
| patch_airthings_ble(WAVE_DEVICE_INFO) as mock_update, | ||
| ): | ||
| await hass.config_entries.async_setup(entry.entry_id) | ||
| await hass.async_block_till_done() | ||
|
|
||
| # Should have only 1 call for initial refresh (no migration call) | ||
| assert mock_update.call_count == 1 | ||
| assert entry.data["device_model"] == WAVE_DEVICE_INFO.model.value | ||
|
|
||
|
|
||
| async def test_scan_interval_corentium_home_2( | ||
| hass: HomeAssistant, | ||
| ) -> None: | ||
| """Test that coordinator uses radon scan interval for Corentium Home 2.""" | ||
| entry = MockConfigEntry( | ||
| domain=DOMAIN, | ||
| unique_id=WAVE_SERVICE_INFO.address, | ||
| data={"device_model": CORENTIUM_HOME_2_DEVICE_INFO.model.value}, | ||
| ) | ||
| entry.add_to_hass(hass) | ||
|
|
||
| inject_bluetooth_service_info(hass, WAVE_SERVICE_INFO) | ||
|
|
||
| with ( | ||
| patch_async_ble_device_from_address(WAVE_SERVICE_INFO.device), | ||
| patch_airthings_ble(CORENTIUM_HOME_2_DEVICE_INFO), | ||
| ): | ||
| await hass.config_entries.async_setup(entry.entry_id) | ||
| await hass.async_block_till_done() | ||
|
|
||
| # Coordinator should have radon scan interval | ||
| coordinator = entry.runtime_data | ||
| assert coordinator.update_interval == timedelta(seconds=RADON_SCAN_INTERVAL) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| ("service_info", "device_info"), | ||
| [ | ||
| (WAVE_SERVICE_INFO, WAVE_DEVICE_INFO), | ||
| (WAVE_ENHANCE_SERVICE_INFO, WAVE_ENHANCE_DEVICE_INFO), | ||
| ], | ||
| ) | ||
| async def test_coordinator_default_scan_interval( | ||
| hass: HomeAssistant, | ||
| service_info, | ||
| device_info, | ||
| ) -> None: | ||
| """Test that coordinator uses default scan interval.""" | ||
| entry = MockConfigEntry( | ||
| domain=DOMAIN, | ||
| unique_id=service_info.address, | ||
| data={"device_model": device_info.model.value}, | ||
| ) | ||
| entry.add_to_hass(hass) | ||
|
|
||
| inject_bluetooth_service_info(hass, service_info) | ||
|
|
||
| with ( | ||
| patch_async_ble_device_from_address(service_info.device), | ||
| patch_airthings_ble(device_info), | ||
| ): | ||
| await hass.config_entries.async_setup(entry.entry_id) | ||
| await hass.async_block_till_done() | ||
|
|
||
| # Coordinator should have default scan interval | ||
| coordinator = entry.runtime_data | ||
| assert coordinator.update_interval == timedelta(seconds=DEFAULT_SCAN_INTERVAL) |
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.
I would assume these changes also have an effect to the test_config_flow.py