Handle additional bluetooth start exceptions (#76096)

This commit is contained in:
J. Nick Koston 2022-08-02 13:46:43 -10:00 committed by GitHub
parent fbf3c1a5d4
commit bf931f1225
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 142 additions and 1 deletions

View File

@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Final
import async_timeout
from bleak import BleakError
from dbus_next import InvalidMessageError
from homeassistant import config_entries
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
@ -26,6 +27,7 @@ from homeassistant.helpers import discovery_flow
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
from homeassistant.loader import async_get_bluetooth
from homeassistant.util.package import is_docker_env
from . import models
from .const import CONF_ADAPTER, DEFAULT_ADAPTERS, DOMAIN
@ -341,13 +343,42 @@ class BluetoothManager:
try:
async with async_timeout.timeout(START_TIMEOUT):
await self.scanner.start() # type: ignore[no-untyped-call]
except InvalidMessageError as ex:
self._cancel_device_detected()
_LOGGER.debug("Invalid DBus message received: %s", ex, exc_info=True)
raise ConfigEntryNotReady(
f"Invalid DBus message received: {ex}; try restarting `dbus`"
) from ex
except BrokenPipeError as ex:
self._cancel_device_detected()
_LOGGER.debug("DBus connection broken: %s", ex, exc_info=True)
if is_docker_env():
raise ConfigEntryNotReady(
f"DBus connection broken: {ex}; try restarting `bluetooth`, `dbus`, and finally the docker container"
) from ex
raise ConfigEntryNotReady(
f"DBus connection broken: {ex}; try restarting `bluetooth` and `dbus`"
) from ex
except FileNotFoundError as ex:
self._cancel_device_detected()
_LOGGER.debug(
"FileNotFoundError while starting bluetooth: %s", ex, exc_info=True
)
if is_docker_env():
raise ConfigEntryNotReady(
f"DBus service not found; docker config may be missing `-v /run/dbus:/run/dbus:ro`: {ex}"
) from ex
raise ConfigEntryNotReady(
f"DBus service not found; make sure the DBus socket is available to Home Assistant: {ex}"
) from ex
except asyncio.TimeoutError as ex:
self._cancel_device_detected()
raise ConfigEntryNotReady(
f"Timed out starting Bluetooth after {START_TIMEOUT} seconds"
) from ex
except (FileNotFoundError, BleakError) as ex:
except BleakError as ex:
self._cancel_device_detected()
_LOGGER.debug("BleakError while starting bluetooth: %s", ex, exc_info=True)
raise ConfigEntryNotReady(f"Failed to start Bluetooth: {ex}") from ex
self.async_setup_unavailable_tracking()
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.async_stop)

View File

@ -5,6 +5,7 @@ from unittest.mock import MagicMock, patch
from bleak import BleakError
from bleak.backends.scanner import AdvertisementData, BLEDevice
from dbus_next import InvalidMessageError
import pytest
from homeassistant.components import bluetooth
@ -1409,3 +1410,112 @@ async def test_changing_the_adapter_at_runtime(hass):
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
async def test_dbus_socket_missing_in_container(hass, caplog):
"""Test we handle dbus being missing in the container."""
with patch(
"homeassistant.components.bluetooth.is_docker_env", return_value=True
), patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch(
"homeassistant.components.bluetooth.HaBleakScanner.start",
side_effect=FileNotFoundError,
):
assert await async_setup_component(
hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}}
)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
assert "/run/dbus" in caplog.text
assert "docker" in caplog.text
async def test_dbus_socket_missing(hass, caplog):
"""Test we handle dbus being missing."""
with patch(
"homeassistant.components.bluetooth.is_docker_env", return_value=False
), patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch(
"homeassistant.components.bluetooth.HaBleakScanner.start",
side_effect=FileNotFoundError,
):
assert await async_setup_component(
hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}}
)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
assert "DBus" in caplog.text
assert "docker" not in caplog.text
async def test_dbus_broken_pipe_in_container(hass, caplog):
"""Test we handle dbus broken pipe in the container."""
with patch(
"homeassistant.components.bluetooth.is_docker_env", return_value=True
), patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch(
"homeassistant.components.bluetooth.HaBleakScanner.start",
side_effect=BrokenPipeError,
):
assert await async_setup_component(
hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}}
)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
assert "dbus" in caplog.text
assert "restarting" in caplog.text
assert "container" in caplog.text
async def test_dbus_broken_pipe(hass, caplog):
"""Test we handle dbus broken pipe."""
with patch(
"homeassistant.components.bluetooth.is_docker_env", return_value=False
), patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch(
"homeassistant.components.bluetooth.HaBleakScanner.start",
side_effect=BrokenPipeError,
):
assert await async_setup_component(
hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}}
)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
assert "DBus" in caplog.text
assert "restarting" in caplog.text
assert "container" not in caplog.text
async def test_invalid_dbus_message(hass, caplog):
"""Test we handle invalid dbus message."""
with patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch(
"homeassistant.components.bluetooth.HaBleakScanner.start",
side_effect=InvalidMessageError,
):
assert await async_setup_component(
hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}}
)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
assert "dbus" in caplog.text