Add error handling for Russound RIO async calls (#123756)
Add better error handling to Russound RIOpull/123819/head
parent
679baddd3d
commit
995ed77849
|
@ -8,7 +8,7 @@ from aiorussound import Russound
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryError
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
|
||||||
from .const import CONNECT_TIMEOUT, RUSSOUND_RIO_EXCEPTIONS
|
from .const import CONNECT_TIMEOUT, RUSSOUND_RIO_EXCEPTIONS
|
||||||
|
|
||||||
|
@ -22,13 +22,15 @@ type RussoundConfigEntry = ConfigEntry[Russound]
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: RussoundConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: RussoundConfigEntry) -> bool:
|
||||||
"""Set up a config entry."""
|
"""Set up a config entry."""
|
||||||
|
|
||||||
russ = Russound(hass.loop, entry.data[CONF_HOST], entry.data[CONF_PORT])
|
host = entry.data[CONF_HOST]
|
||||||
|
port = entry.data[CONF_PORT]
|
||||||
|
russ = Russound(hass.loop, host, port)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with asyncio.timeout(CONNECT_TIMEOUT):
|
async with asyncio.timeout(CONNECT_TIMEOUT):
|
||||||
await russ.connect()
|
await russ.connect()
|
||||||
except RUSSOUND_RIO_EXCEPTIONS as err:
|
except RUSSOUND_RIO_EXCEPTIONS as err:
|
||||||
raise ConfigEntryError(err) from err
|
raise ConfigEntryNotReady(f"Error while connecting to {host}:{port}") from err
|
||||||
|
|
||||||
entry.runtime_data = russ
|
entry.runtime_data = russ
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Awaitable, Callable, Coroutine
|
||||||
|
from functools import wraps
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any, Concatenate
|
||||||
|
|
||||||
from aiorussound import Source, Zone
|
from aiorussound import Source, Zone
|
||||||
|
|
||||||
|
@ -17,12 +20,13 @@ from homeassistant.config_entries import SOURCE_IMPORT
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, callback
|
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, callback
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from . import RussoundConfigEntry
|
from . import RUSSOUND_RIO_EXCEPTIONS, RussoundConfigEntry
|
||||||
from .const import DOMAIN, MP_FEATURES_BY_FLAG
|
from .const import DOMAIN, MP_FEATURES_BY_FLAG
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -107,6 +111,24 @@ async def async_setup_entry(
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
|
def command[_T: RussoundZoneDevice, **_P](
|
||||||
|
func: Callable[Concatenate[_T, _P], Awaitable[None]],
|
||||||
|
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]:
|
||||||
|
"""Wrap async calls to raise on request error."""
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
async def decorator(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||||
|
"""Wrap all command methods."""
|
||||||
|
try:
|
||||||
|
await func(self, *args, **kwargs)
|
||||||
|
except RUSSOUND_RIO_EXCEPTIONS as exc:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
f"Error executing {func.__name__} on entity {self.entity_id},"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
class RussoundZoneDevice(MediaPlayerEntity):
|
class RussoundZoneDevice(MediaPlayerEntity):
|
||||||
"""Representation of a Russound Zone."""
|
"""Representation of a Russound Zone."""
|
||||||
|
|
||||||
|
@ -221,19 +243,23 @@ class RussoundZoneDevice(MediaPlayerEntity):
|
||||||
"""
|
"""
|
||||||
return float(self._zone.volume or "0") / 50.0
|
return float(self._zone.volume or "0") / 50.0
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_turn_off(self) -> None:
|
async def async_turn_off(self) -> None:
|
||||||
"""Turn off the zone."""
|
"""Turn off the zone."""
|
||||||
await self._zone.zone_off()
|
await self._zone.zone_off()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_turn_on(self) -> None:
|
async def async_turn_on(self) -> None:
|
||||||
"""Turn on the zone."""
|
"""Turn on the zone."""
|
||||||
await self._zone.zone_on()
|
await self._zone.zone_on()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_set_volume_level(self, volume: float) -> None:
|
async def async_set_volume_level(self, volume: float) -> None:
|
||||||
"""Set the volume level."""
|
"""Set the volume level."""
|
||||||
rvol = int(volume * 50.0)
|
rvol = int(volume * 50.0)
|
||||||
await self._zone.set_volume(rvol)
|
await self._zone.set_volume(rvol)
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_select_source(self, source: str) -> None:
|
async def async_select_source(self, source: str) -> None:
|
||||||
"""Select the source input for this zone."""
|
"""Select the source input for this zone."""
|
||||||
for source_id, src in self._sources.items():
|
for source_id, src in self._sources.items():
|
||||||
|
@ -242,10 +268,12 @@ class RussoundZoneDevice(MediaPlayerEntity):
|
||||||
await self._zone.select_source(source_id)
|
await self._zone.select_source(source_id)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_volume_up(self) -> None:
|
async def async_volume_up(self) -> None:
|
||||||
"""Step the volume up."""
|
"""Step the volume up."""
|
||||||
await self._zone.volume_up()
|
await self._zone.volume_up()
|
||||||
|
|
||||||
|
@command
|
||||||
async def async_volume_down(self) -> None:
|
async def async_volume_down(self) -> None:
|
||||||
"""Step the volume down."""
|
"""Step the volume down."""
|
||||||
await self._zone.volume_down()
|
await self._zone.volume_down()
|
||||||
|
|
Loading…
Reference in New Issue