mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 11:47:06 +00:00
Handle Shelly BLE errors during connect and disconnect (#119174)
This commit is contained in:
parent
b937fc0cfe
commit
04222c32b5
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
from aioshelly.block_device import BlockDevice
|
from aioshelly.block_device import BlockDevice
|
||||||
@ -301,13 +300,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ShellyConfigEntry) -> b
|
|||||||
entry, platforms
|
entry, platforms
|
||||||
):
|
):
|
||||||
if shelly_entry_data.rpc:
|
if shelly_entry_data.rpc:
|
||||||
with contextlib.suppress(DeviceConnectionError):
|
await shelly_entry_data.rpc.shutdown()
|
||||||
# If the device is restarting or has gone offline before
|
|
||||||
# the ping/pong timeout happens, the shutdown command
|
|
||||||
# will fail, but we don't care since we are unloading
|
|
||||||
# and if we setup again, we will fix anything that is
|
|
||||||
# in an inconsistent state at that time.
|
|
||||||
await shelly_entry_data.rpc.shutdown()
|
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
@ -627,7 +627,13 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
|
|||||||
if self.connected: # Already connected
|
if self.connected: # Already connected
|
||||||
return
|
return
|
||||||
self.connected = True
|
self.connected = True
|
||||||
await self._async_run_connected_events()
|
try:
|
||||||
|
await self._async_run_connected_events()
|
||||||
|
except DeviceConnectionError as err:
|
||||||
|
LOGGER.error(
|
||||||
|
"Error running connected events for device %s: %s", self.name, err
|
||||||
|
)
|
||||||
|
self.last_update_success = False
|
||||||
|
|
||||||
async def _async_run_connected_events(self) -> None:
|
async def _async_run_connected_events(self) -> None:
|
||||||
"""Run connected events.
|
"""Run connected events.
|
||||||
@ -701,10 +707,18 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
|
|||||||
if self.device.connected:
|
if self.device.connected:
|
||||||
try:
|
try:
|
||||||
await async_stop_scanner(self.device)
|
await async_stop_scanner(self.device)
|
||||||
|
await super().shutdown()
|
||||||
except InvalidAuthError:
|
except InvalidAuthError:
|
||||||
self.entry.async_start_reauth(self.hass)
|
self.entry.async_start_reauth(self.hass)
|
||||||
return
|
return
|
||||||
await super().shutdown()
|
except DeviceConnectionError as err:
|
||||||
|
# If the device is restarting or has gone offline before
|
||||||
|
# the ping/pong timeout happens, the shutdown command
|
||||||
|
# will fail, but we don't care since we are unloading
|
||||||
|
# and if we setup again, we will fix anything that is
|
||||||
|
# in an inconsistent state at that time.
|
||||||
|
LOGGER.debug("Error during shutdown for device %s: %s", self.name, err)
|
||||||
|
return
|
||||||
await self._async_disconnected(False)
|
await self._async_disconnected(False)
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,12 +15,14 @@ from homeassistant.components.shelly.const import (
|
|||||||
ATTR_CLICK_TYPE,
|
ATTR_CLICK_TYPE,
|
||||||
ATTR_DEVICE,
|
ATTR_DEVICE,
|
||||||
ATTR_GENERATION,
|
ATTR_GENERATION,
|
||||||
|
CONF_BLE_SCANNER_MODE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
ENTRY_RELOAD_COOLDOWN,
|
ENTRY_RELOAD_COOLDOWN,
|
||||||
MAX_PUSH_UPDATE_FAILURES,
|
MAX_PUSH_UPDATE_FAILURES,
|
||||||
RPC_RECONNECT_INTERVAL,
|
RPC_RECONNECT_INTERVAL,
|
||||||
SLEEP_PERIOD_MULTIPLIER,
|
SLEEP_PERIOD_MULTIPLIER,
|
||||||
UPDATE_PERIOD_MULTIPLIER,
|
UPDATE_PERIOD_MULTIPLIER,
|
||||||
|
BLEScannerMode,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||||
from homeassistant.const import ATTR_DEVICE_ID, STATE_ON, STATE_UNAVAILABLE
|
from homeassistant.const import ATTR_DEVICE_ID, STATE_ON, STATE_UNAVAILABLE
|
||||||
@ -485,6 +487,25 @@ async def test_rpc_reload_with_invalid_auth(
|
|||||||
assert flow["context"].get("entry_id") == entry.entry_id
|
assert flow["context"].get("entry_id") == entry.entry_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_rpc_connection_error_during_unload(
|
||||||
|
hass: HomeAssistant, mock_rpc_device: Mock, caplog: pytest.LogCaptureFixture
|
||||||
|
) -> None:
|
||||||
|
"""Test RPC DeviceConnectionError suppressed during config entry unload."""
|
||||||
|
entry = await init_integration(hass, 2)
|
||||||
|
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.shelly.coordinator.async_stop_scanner",
|
||||||
|
side_effect=DeviceConnectionError,
|
||||||
|
):
|
||||||
|
await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert "Error during shutdown for device" in caplog.text
|
||||||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
||||||
async def test_rpc_click_event(
|
async def test_rpc_click_event(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_rpc_device: Mock,
|
mock_rpc_device: Mock,
|
||||||
@ -713,6 +734,32 @@ async def test_rpc_reconnect_error(
|
|||||||
assert get_entity_state(hass, "switch.test_switch_0") == STATE_UNAVAILABLE
|
assert get_entity_state(hass, "switch.test_switch_0") == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
|
async def test_rpc_error_running_connected_events(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
mock_rpc_device: Mock,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test RPC error while running connected events."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.shelly.coordinator.async_ensure_ble_enabled",
|
||||||
|
side_effect=DeviceConnectionError,
|
||||||
|
):
|
||||||
|
await init_integration(
|
||||||
|
hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Error running connected events for device" in caplog.text
|
||||||
|
assert get_entity_state(hass, "switch.test_switch_0") == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
# Move time to generate reconnect without error
|
||||||
|
freezer.tick(timedelta(seconds=RPC_RECONNECT_INTERVAL))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
|
||||||
|
assert get_entity_state(hass, "switch.test_switch_0") == STATE_ON
|
||||||
|
|
||||||
|
|
||||||
async def test_rpc_polling_connection_error(
|
async def test_rpc_polling_connection_error(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user