diff --git a/homeassistant/components/airthings_ble/coordinator.py b/homeassistant/components/airthings_ble/coordinator.py index 7c7284f7e751..487fe635fa07 100644 --- a/homeassistant/components/airthings_ble/coordinator.py +++ b/homeassistant/components/airthings_ble/coordinator.py @@ -102,4 +102,14 @@ class AirthingsBLEDataUpdateCoordinator(DataUpdateCoordinator[AirthingsDevice]): data = await self.airthings.update_device(self.ble_device) except Exception as err: raise UpdateFailed(f"Unable to fetch data: {err}") from err + + if not data.address: + # The device did not report its address, which means the read did not + # complete. Building entities from this would create a duplicate device + # and entities with an empty unique id prefix. + raise UpdateFailed( + translation_domain=DOMAIN, + translation_key="incomplete_read", + ) + return data diff --git a/homeassistant/components/airthings_ble/strings.json b/homeassistant/components/airthings_ble/strings.json index ccc467160c17..7dc45d2880c4 100644 --- a/homeassistant/components/airthings_ble/strings.json +++ b/homeassistant/components/airthings_ble/strings.json @@ -67,6 +67,9 @@ "exceptions": { "device_not_found": { "message": "Could not find Airthings device with address {address}: {reason}" + }, + "incomplete_read": { + "message": "The Airthings device did not return complete data, retrying" } } } diff --git a/tests/components/airthings_ble/test_init.py b/tests/components/airthings_ble/test_init.py index 7cf03940190d..1ee615f5cbf2 100644 --- a/tests/components/airthings_ble/test_init.py +++ b/tests/components/airthings_ble/test_init.py @@ -98,6 +98,33 @@ async def test_setup_retries_when_device_not_found( ) +async def test_setup_retries_on_incomplete_read( + hass: HomeAssistant, +) -> None: + """Test setup is retried when the device returns data without an address.""" + 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) + + incomplete_device_info = deepcopy(WAVE_DEVICE_INFO) + incomplete_device_info.address = "" + + with ( + patch_async_ble_device_from_address(WAVE_SERVICE_INFO.device), + patch_airthings_ble(incomplete_device_info), + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert len(hass.states.async_all()) == 0 + + async def test_no_migration_when_device_model_exists( hass: HomeAssistant, ) -> None: