diff --git a/homeassistant/components/yalexs_ble/__init__.py b/homeassistant/components/yalexs_ble/__init__.py index 26231a8d58ec..539db81dc054 100644 --- a/homeassistant/components/yalexs_ble/__init__.py +++ b/homeassistant/components/yalexs_ble/__init__.py @@ -12,6 +12,7 @@ from yalexs_ble import ( ) from homeassistant.components import bluetooth +from homeassistant.components.bluetooth import BluetoothReachabilityIntent from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ADDRESS, EVENT_HOMEASSISTANT_STOP, Platform from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback @@ -24,6 +25,7 @@ from .const import ( CONF_LOCAL_NAME, CONF_SLOT, DEVICE_TIMEOUT, + DOMAIN, ) from .models import YaleXSBLEData from .util import async_find_existing_service_info, bluetooth_callback_matcher @@ -83,7 +85,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: YALEXSBLEConfigEntry) -> # If we are starting and the advertisement is not found, do not delay # the setup. We will wait for the advertisement to be found and then # discovery will trigger setup retry. - raise ConfigEntryNotReady("{local_name} ({address}) not advertising yet") + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="device_not_advertising", + translation_placeholders={ + "local_name": local_name, + "address": address, + "reason": bluetooth.async_address_reachability_diagnostics( + hass, + address.upper(), + BluetoothReachabilityIntent.CONNECTION, + ), + }, + ) entry.async_on_unload( bluetooth.async_register_callback( diff --git a/homeassistant/components/yalexs_ble/strings.json b/homeassistant/components/yalexs_ble/strings.json index c4e4210a0b64..595130107599 100644 --- a/homeassistant/components/yalexs_ble/strings.json +++ b/homeassistant/components/yalexs_ble/strings.json @@ -53,6 +53,11 @@ } } }, + "exceptions": { + "device_not_advertising": { + "message": "{local_name} ({address}) is not advertising yet: {reason}" + } + }, "options": { "step": { "device_options": { diff --git a/tests/components/yalexs_ble/test_init.py b/tests/components/yalexs_ble/test_init.py new file mode 100644 index 000000000000..1ad6f260c2bf --- /dev/null +++ b/tests/components/yalexs_ble/test_init.py @@ -0,0 +1,61 @@ +"""Test the Yale Access Bluetooth init.""" + +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from homeassistant.components.yalexs_ble.const import ( + CONF_KEY, + CONF_LOCAL_NAME, + CONF_SLOT, + DOMAIN, +) +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import CONF_ADDRESS +from homeassistant.core import CoreState, HomeAssistant + +from . import YALE_ACCESS_LOCK_DISCOVERY_INFO + +from tests.common import MockConfigEntry + + +async def test_setup_retries_when_not_advertising_at_startup( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test setup is retried with a diagnostic reason when not advertising at startup.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_LOCAL_NAME: YALE_ACCESS_LOCK_DISCOVERY_INFO.name, + CONF_ADDRESS: YALE_ACCESS_LOCK_DISCOVERY_INFO.address, + CONF_KEY: "2fd51b8621c6a139eaffbedcb846b60f", + CONF_SLOT: 66, + }, + unique_id=YALE_ACCESS_LOCK_DISCOVERY_INFO.address, + ) + entry.add_to_hass(hass) + + hass.set_state(CoreState.starting) + + push_lock = MagicMock() + push_lock.start = AsyncMock(return_value=MagicMock()) + + with ( + patch("homeassistant.components.yalexs_ble.close_stale_connections_by_address"), + patch("homeassistant.components.yalexs_ble.PushLock", return_value=push_lock), + patch( + "homeassistant.components.yalexs_ble.bluetooth." + "async_address_reachability_diagnostics", + return_value="mock reachability reason", + ), + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert ( + f"{YALE_ACCESS_LOCK_DISCOVERY_INFO.name} " + f"({YALE_ACCESS_LOCK_DISCOVERY_INFO.address}) is not advertising yet: " + "mock reachability reason" in caplog.text + )