parent
3a5309e9a0
commit
8d239d368b
9 changed files with 39 additions and 31 deletions
|
@ -3,7 +3,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aiorussound import Russound
|
from aiorussound import RussoundClient, RussoundTcpConnectionHandler
|
||||||
|
|
||||||
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
|
||||||
|
@ -16,7 +16,7 @@ PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
type RussoundConfigEntry = ConfigEntry[Russound]
|
type RussoundConfigEntry = ConfigEntry[RussoundClient]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: RussoundConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: RussoundConfigEntry) -> bool:
|
||||||
|
@ -24,7 +24,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: RussoundConfigEntry) ->
|
||||||
|
|
||||||
host = entry.data[CONF_HOST]
|
host = entry.data[CONF_HOST]
|
||||||
port = entry.data[CONF_PORT]
|
port = entry.data[CONF_PORT]
|
||||||
russ = Russound(hass.loop, host, port)
|
russ = RussoundClient(RussoundTcpConnectionHandler(hass.loop, host, port))
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def is_connected_updated(connected: bool) -> None:
|
def is_connected_updated(connected: bool) -> None:
|
||||||
|
@ -37,14 +37,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: RussoundConfigEntry) ->
|
||||||
port,
|
port,
|
||||||
)
|
)
|
||||||
|
|
||||||
russ.add_connection_callback(is_connected_updated)
|
russ.connection_handler.add_connection_callback(is_connected_updated)
|
||||||
|
|
||||||
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 ConfigEntryNotReady(f"Error while connecting to {host}:{port}") from err
|
raise ConfigEntryNotReady(f"Error while connecting to {host}:{port}") from err
|
||||||
|
|
||||||
entry.runtime_data = russ
|
entry.runtime_data = russ
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiorussound import Controller, Russound
|
from aiorussound import Controller, RussoundClient, RussoundTcpConnectionHandler
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
|
@ -54,8 +54,9 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
host = user_input[CONF_HOST]
|
host = user_input[CONF_HOST]
|
||||||
port = user_input[CONF_PORT]
|
port = user_input[CONF_PORT]
|
||||||
|
|
||||||
controllers = None
|
russ = RussoundClient(
|
||||||
russ = Russound(self.hass.loop, host, port)
|
RussoundTcpConnectionHandler(self.hass.loop, host, port)
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
async with asyncio.timeout(CONNECT_TIMEOUT):
|
async with asyncio.timeout(CONNECT_TIMEOUT):
|
||||||
await russ.connect()
|
await russ.connect()
|
||||||
|
@ -87,7 +88,7 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
port = import_data.get(CONF_PORT, 9621)
|
port = import_data.get(CONF_PORT, 9621)
|
||||||
|
|
||||||
# Connection logic is repeated here since this method will be removed in future releases
|
# Connection logic is repeated here since this method will be removed in future releases
|
||||||
russ = Russound(self.hass.loop, host, port)
|
russ = RussoundClient(RussoundTcpConnectionHandler(self.hass.loop, host, port))
|
||||||
try:
|
try:
|
||||||
async with asyncio.timeout(CONNECT_TIMEOUT):
|
async with asyncio.timeout(CONNECT_TIMEOUT):
|
||||||
await russ.connect()
|
await russ.connect()
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from aiorussound import CommandException
|
from aiorussound import CommandError
|
||||||
from aiorussound.const import FeatureFlag
|
from aiorussound.const import FeatureFlag
|
||||||
|
|
||||||
from homeassistant.components.media_player import MediaPlayerEntityFeature
|
from homeassistant.components.media_player import MediaPlayerEntityFeature
|
||||||
|
@ -10,7 +10,7 @@ from homeassistant.components.media_player import MediaPlayerEntityFeature
|
||||||
DOMAIN = "russound_rio"
|
DOMAIN = "russound_rio"
|
||||||
|
|
||||||
RUSSOUND_RIO_EXCEPTIONS = (
|
RUSSOUND_RIO_EXCEPTIONS = (
|
||||||
CommandException,
|
CommandError,
|
||||||
ConnectionRefusedError,
|
ConnectionRefusedError,
|
||||||
TimeoutError,
|
TimeoutError,
|
||||||
asyncio.CancelledError,
|
asyncio.CancelledError,
|
||||||
|
|
|
@ -4,7 +4,7 @@ from collections.abc import Awaitable, Callable, Coroutine
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, Concatenate
|
from typing import Any, Concatenate
|
||||||
|
|
||||||
from aiorussound import Controller
|
from aiorussound import Controller, RussoundTcpConnectionHandler
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
@ -53,7 +53,6 @@ class RussoundBaseEntity(Entity):
|
||||||
or f"{self._primary_mac_address}-{self._controller.controller_id}"
|
or f"{self._primary_mac_address}-{self._controller.controller_id}"
|
||||||
)
|
)
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
configuration_url=f"http://{self._instance.host}",
|
|
||||||
# Use MAC address of Russound device as identifier
|
# Use MAC address of Russound device as identifier
|
||||||
identifiers={(DOMAIN, self._device_identifier)},
|
identifiers={(DOMAIN, self._device_identifier)},
|
||||||
manufacturer="Russound",
|
manufacturer="Russound",
|
||||||
|
@ -61,6 +60,10 @@ class RussoundBaseEntity(Entity):
|
||||||
model=controller.controller_type,
|
model=controller.controller_type,
|
||||||
sw_version=controller.firmware_version,
|
sw_version=controller.firmware_version,
|
||||||
)
|
)
|
||||||
|
if isinstance(self._instance.connection_handler, RussoundTcpConnectionHandler):
|
||||||
|
self._attr_device_info["configuration_url"] = (
|
||||||
|
f"http://{self._instance.connection_handler.host}"
|
||||||
|
)
|
||||||
if controller.parent_controller:
|
if controller.parent_controller:
|
||||||
self._attr_device_info["via_device"] = (
|
self._attr_device_info["via_device"] = (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
@ -79,8 +82,12 @@ class RussoundBaseEntity(Entity):
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
self._instance.add_connection_callback(self._is_connected_updated)
|
self._instance.connection_handler.add_connection_callback(
|
||||||
|
self._is_connected_updated
|
||||||
|
)
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Remove callbacks."""
|
"""Remove callbacks."""
|
||||||
self._instance.remove_connection_callback(self._is_connected_updated)
|
self._instance.connection_handler.remove_connection_callback(
|
||||||
|
self._is_connected_updated
|
||||||
|
)
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aiorussound"],
|
"loggers": ["aiorussound"],
|
||||||
"quality_scale": "silver",
|
"quality_scale": "silver",
|
||||||
"requirements": ["aiorussound==2.3.2"]
|
"requirements": ["aiorussound==3.0.4"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,14 +84,16 @@ async def async_setup_entry(
|
||||||
"""Set up the Russound RIO platform."""
|
"""Set up the Russound RIO platform."""
|
||||||
russ = entry.runtime_data
|
russ = entry.runtime_data
|
||||||
|
|
||||||
|
await russ.init_sources()
|
||||||
|
sources = russ.sources
|
||||||
|
for source in sources.values():
|
||||||
|
await source.watch()
|
||||||
|
|
||||||
# Discover controllers
|
# Discover controllers
|
||||||
controllers = await russ.enumerate_controllers()
|
controllers = await russ.enumerate_controllers()
|
||||||
|
|
||||||
entities = []
|
entities = []
|
||||||
for controller in controllers.values():
|
for controller in controllers.values():
|
||||||
sources = controller.sources
|
|
||||||
for source in sources.values():
|
|
||||||
await source.watch()
|
|
||||||
for zone in controller.zones.values():
|
for zone in controller.zones.values():
|
||||||
await zone.watch()
|
await zone.watch()
|
||||||
mp = RussoundZoneDevice(zone, sources)
|
mp = RussoundZoneDevice(zone, sources)
|
||||||
|
@ -154,7 +156,7 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
|
||||||
@property
|
@property
|
||||||
def state(self) -> MediaPlayerState | None:
|
def state(self) -> MediaPlayerState | None:
|
||||||
"""Return the state of the device."""
|
"""Return the state of the device."""
|
||||||
status = self._zone.status
|
status = self._zone.properties.status
|
||||||
if status == "ON":
|
if status == "ON":
|
||||||
return MediaPlayerState.ON
|
return MediaPlayerState.ON
|
||||||
if status == "OFF":
|
if status == "OFF":
|
||||||
|
@ -174,22 +176,22 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
|
||||||
@property
|
@property
|
||||||
def media_title(self):
|
def media_title(self):
|
||||||
"""Title of current playing media."""
|
"""Title of current playing media."""
|
||||||
return self._current_source().song_name
|
return self._current_source().properties.song_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_artist(self):
|
def media_artist(self):
|
||||||
"""Artist of current playing media, music track only."""
|
"""Artist of current playing media, music track only."""
|
||||||
return self._current_source().artist_name
|
return self._current_source().properties.artist_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_album_name(self):
|
def media_album_name(self):
|
||||||
"""Album name of current playing media, music track only."""
|
"""Album name of current playing media, music track only."""
|
||||||
return self._current_source().album_name
|
return self._current_source().properties.album_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_image_url(self):
|
def media_image_url(self):
|
||||||
"""Image url of current playing media."""
|
"""Image url of current playing media."""
|
||||||
return self._current_source().cover_art_url
|
return self._current_source().properties.cover_art_url
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volume_level(self):
|
def volume_level(self):
|
||||||
|
@ -198,7 +200,7 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
|
||||||
Value is returned based on a range (0..50).
|
Value is returned based on a range (0..50).
|
||||||
Therefore float divide by 50 to get to the required range.
|
Therefore float divide by 50 to get to the required range.
|
||||||
"""
|
"""
|
||||||
return float(self._zone.volume or "0") / 50.0
|
return float(self._zone.properties.volume or "0") / 50.0
|
||||||
|
|
||||||
@command
|
@command
|
||||||
async def async_turn_off(self) -> None:
|
async def async_turn_off(self) -> None:
|
||||||
|
@ -214,7 +216,7 @@ class RussoundZoneDevice(RussoundBaseEntity, MediaPlayerEntity):
|
||||||
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(str(rvol))
|
||||||
|
|
||||||
@command
|
@command
|
||||||
async def async_select_source(self, source: str) -> None:
|
async def async_select_source(self, source: str) -> None:
|
||||||
|
|
|
@ -350,7 +350,7 @@ aioridwell==2024.01.0
|
||||||
aioruckus==0.41
|
aioruckus==0.41
|
||||||
|
|
||||||
# homeassistant.components.russound_rio
|
# homeassistant.components.russound_rio
|
||||||
aiorussound==2.3.2
|
aiorussound==3.0.4
|
||||||
|
|
||||||
# homeassistant.components.ruuvi_gateway
|
# homeassistant.components.ruuvi_gateway
|
||||||
aioruuvigateway==0.1.0
|
aioruuvigateway==0.1.0
|
||||||
|
|
|
@ -332,7 +332,7 @@ aioridwell==2024.01.0
|
||||||
aioruckus==0.41
|
aioruckus==0.41
|
||||||
|
|
||||||
# homeassistant.components.russound_rio
|
# homeassistant.components.russound_rio
|
||||||
aiorussound==2.3.2
|
aiorussound==3.0.4
|
||||||
|
|
||||||
# homeassistant.components.ruuvi_gateway
|
# homeassistant.components.ruuvi_gateway
|
||||||
aioruuvigateway==0.1.0
|
aioruuvigateway==0.1.0
|
||||||
|
|
|
@ -37,10 +37,10 @@ def mock_russound() -> Generator[AsyncMock]:
|
||||||
"""Mock the Russound RIO client."""
|
"""Mock the Russound RIO client."""
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.russound_rio.Russound", autospec=True
|
"homeassistant.components.russound_rio.RussoundClient", autospec=True
|
||||||
) as mock_client,
|
) as mock_client,
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.russound_rio.config_flow.Russound",
|
"homeassistant.components.russound_rio.config_flow.RussoundClient",
|
||||||
return_value=mock_client,
|
return_value=mock_client,
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue