1 """Support for Russound multizone controllers using RIO Protocol."""
3 from __future__
import annotations
7 from aiorussound
import Controller
8 from aiorussound.models
import PlayStatus, Source
9 from aiorussound.rio
import ZoneControlSurface
12 MediaPlayerDeviceClass,
14 MediaPlayerEntityFeature,
25 from .
import RussoundConfigEntry
26 from .const
import DOMAIN, MP_FEATURES_BY_FLAG
27 from .entity
import RussoundBaseEntity, command
29 _LOGGER = logging.getLogger(__name__)
35 async_add_entities: AddEntitiesCallback,
36 discovery_info: DiscoveryInfoType |
None =
None,
38 """Set up the Russound RIO platform."""
40 result = await hass.config_entries.flow.async_init(
42 context={
"source": SOURCE_IMPORT},
46 result[
"type"]
is FlowResultType.CREATE_ENTRY
47 or result[
"reason"] ==
"single_instance_allowed"
52 f
"deprecated_yaml_{DOMAIN}",
53 breaks_in_ha_version=
"2025.2.0",
56 severity=IssueSeverity.WARNING,
57 translation_key=
"deprecated_yaml",
58 translation_placeholders={
60 "integration_title":
"Russound RIO",
67 f
"deprecated_yaml_import_issue_{result['reason']}",
68 breaks_in_ha_version=
"2025.2.0",
71 severity=IssueSeverity.WARNING,
72 translation_key=f
"deprecated_yaml_import_issue_{result['reason']}",
73 translation_placeholders={
75 "integration_title":
"Russound RIO",
82 entry: RussoundConfigEntry,
83 async_add_entities: AddEntitiesCallback,
85 """Set up the Russound RIO platform."""
86 client = entry.runtime_data
87 sources = client.sources
91 for controller
in client.controllers.values()
92 for zone_id
in controller.zones
97 """Representation of a Russound Zone."""
99 _attr_device_class = MediaPlayerDeviceClass.SPEAKER
100 _attr_media_content_type = MediaType.MUSIC
101 _attr_supported_features = (
102 MediaPlayerEntityFeature.VOLUME_SET
103 | MediaPlayerEntityFeature.VOLUME_STEP
104 | MediaPlayerEntityFeature.TURN_ON
105 | MediaPlayerEntityFeature.TURN_OFF
106 | MediaPlayerEntityFeature.SELECT_SOURCE
110 self, controller: Controller, zone_id: int, sources: dict[int, Source]
112 """Initialize the zone device."""
115 _zone = self.
_zone_zone
118 self.
_attr_unique_id_attr_unique_id = f
"{self._primary_mac_address}-{_zone.device_str}"
119 for flag, feature
in MP_FEATURES_BY_FLAG.items():
120 if flag
in self.
_client_client.supported_features:
124 def _zone(self) -> ZoneControlSurface:
129 return self.
_zone_zone.fetch_current_source()
132 def state(self) -> MediaPlayerState | None:
133 """Return the state of the device."""
134 status = self.
_zone_zone.status
135 play_status = self.
_source_source.play_status
137 return MediaPlayerState.OFF
138 if play_status == PlayStatus.PLAYING:
139 return MediaPlayerState.PLAYING
140 if play_status == PlayStatus.PAUSED:
141 return MediaPlayerState.PAUSED
142 if play_status == PlayStatus.TRANSITIONING:
143 return MediaPlayerState.BUFFERING
144 if play_status == PlayStatus.STOPPED:
145 return MediaPlayerState.IDLE
146 return MediaPlayerState.ON
150 """Get the currently selected source."""
151 return self.
_source_source.name
155 """Return a list of available input sources."""
156 return [x.name
for x
in self.
_sources_sources.values()]
160 """Title of current playing media."""
161 return self.
_source_source.song_name
165 """Artist of current playing media, music track only."""
166 return self.
_source_source.artist_name
170 """Album name of current playing media, music track only."""
171 return self.
_source_source.album_name
175 """Image url of current playing media."""
176 return self.
_source_source.cover_art_url
180 """Volume level of the media player (0..1).
182 Value is returned based on a range (0..50).
183 Therefore float divide by 50 to get to the required range.
185 return self.
_zone_zone.volume / 50.0
189 """Turn off the zone."""
190 await self.
_zone_zone.zone_off()
194 """Turn on the zone."""
195 await self.
_zone_zone.zone_on()
199 """Set the volume level."""
200 rvol =
int(volume * 50.0)
201 await self.
_zone_zone.set_volume(
str(rvol))
205 """Select the source input for this zone."""
206 for source_id, src
in self.
_sources_sources.items():
207 if src.name.lower() != source.lower():
214 """Step the volume up."""
215 await self.
_zone_zone.volume_up()
219 """Step the volume down."""
220 await self.
_zone_zone.volume_down()
None async_create_issue(HomeAssistant hass, str entry_id)