diff --git a/.coveragerc b/.coveragerc index ebec3974cbb..4aa8ee4949e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1067,6 +1067,7 @@ omit = homeassistant/components/roomba/sensor.py homeassistant/components/roomba/vacuum.py homeassistant/components/roon/__init__.py + homeassistant/components/roon/event.py homeassistant/components/roon/media_browser.py homeassistant/components/roon/media_player.py homeassistant/components/roon/server.py diff --git a/homeassistant/components/roon/__init__.py b/homeassistant/components/roon/__init__.py index 9969b694895..f721f0bac40 100644 --- a/homeassistant/components/roon/__init__.py +++ b/homeassistant/components/roon/__init__.py @@ -7,7 +7,7 @@ from homeassistant.helpers import device_registry as dr from .const import CONF_ROON_NAME, DOMAIN from .server import RoonServer -PLATFORMS = [Platform.MEDIA_PLAYER] +PLATFORMS = [Platform.EVENT, Platform.MEDIA_PLAYER] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/roon/const.py b/homeassistant/components/roon/const.py index 74cf6a38160..01e2aab9685 100644 --- a/homeassistant/components/roon/const.py +++ b/homeassistant/components/roon/const.py @@ -13,8 +13,8 @@ DEFAULT_NAME = "Roon Labs Music Player" ROON_APPINFO = { "extension_id": "home_assistant", - "display_name": "Roon Integration for Home Assistant", - "display_version": "1.0.0", + "display_name": "Home Assistant", + "display_version": "1.0.1", "publisher": "home_assistant", "email": "home_assistant@users.noreply.github.com", "website": "https://www.home-assistant.io/", diff --git a/homeassistant/components/roon/event.py b/homeassistant/components/roon/event.py new file mode 100644 index 00000000000..fc1bb339cd7 --- /dev/null +++ b/homeassistant/components/roon/event.py @@ -0,0 +1,109 @@ +"""Roon event entities.""" +import logging +from typing import cast + +from homeassistant.components.event import EventDeviceClass, EventEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Roon Event from Config Entry.""" + roon_server = hass.data[DOMAIN][config_entry.entry_id] + event_entities = set() + + @callback + def async_add_roon_volume_entity(player_data): + """Add or update Roon event Entity.""" + dev_id = player_data["dev_id"] + if dev_id in event_entities: + return + # new player! + event_entity = RoonEventEntity(roon_server, player_data) + event_entities.add(dev_id) + async_add_entities([event_entity]) + + # start listening for players to be added from the server component + config_entry.async_on_unload( + async_dispatcher_connect( + hass, "roon_media_player", async_add_roon_volume_entity + ) + ) + + +class RoonEventEntity(EventEntity): + """Representation of a Roon Event entity.""" + + _attr_device_class = EventDeviceClass.BUTTON + _attr_event_types = ["volume_up", "volume_down"] + _attr_translation_key = "volume" + + def __init__(self, server, player_data): + """Initialize the entity.""" + self._server = server + self._player_data = player_data + player_name = player_data["display_name"] + self._attr_name = f"{player_name} roon volume" + self._attr_unique_id = self._player_data["dev_id"] + + if self._player_data.get("source_controls"): + dev_model = self._player_data["source_controls"][0].get("display_name") + + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, self.unique_id)}, + # Instead of setting the device name to the entity name, roon + # should be updated to set has_entity_name = True, and set the entity + # name to None + name=cast(str | None, self.name), + manufacturer="RoonLabs", + model=dev_model, + via_device=(DOMAIN, self._server.roon_id), + ) + + @callback + def _roonapi_volume_callback( + self, control_key: str, event: str, value: int + ) -> None: + """Callbacks from the roon api with volume request.""" + + if event != "set_volume": + _LOGGER.debug("Received unsupported roon volume event %s", event) + return + + if value > 0: + event = "volume_up" + else: + event = "volume_down" + + self._trigger_event(event) + self.async_write_ha_state() + + async def async_added_to_hass(self) -> None: + """Register volume hooks with the roon api.""" + + self._server.roonapi.register_volume_control( + self.unique_id, + self.name, + self._roonapi_volume_callback, + 0, + "incremental", + 0, + 0, + 0, + False, + ) + + async def async_will_remove_from_hass(self) -> None: + """Unregister volume hooks from the roon api.""" + self._server.roonapi.unregister_volume_control(self.unique_id) diff --git a/homeassistant/components/roon/manifest.json b/homeassistant/components/roon/manifest.json index 4fa527d0769..2598d9e8de1 100644 --- a/homeassistant/components/roon/manifest.json +++ b/homeassistant/components/roon/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/roon", "iot_class": "local_push", "loggers": ["roonapi"], - "requirements": ["roonapi==0.1.4"] + "requirements": ["roonapi==0.1.5"] } diff --git a/homeassistant/components/roon/server.py b/homeassistant/components/roon/server.py index 32d909ff00f..488fe18aae4 100644 --- a/homeassistant/components/roon/server.py +++ b/homeassistant/components/roon/server.py @@ -105,7 +105,7 @@ class RoonServer: self._exit = True def roonapi_state_callback(self, event, changed_zones): - """Callbacks from the roon api websockets.""" + """Callbacks from the roon api websocket with state change.""" self.hass.add_job(self.async_update_changed_players(changed_zones)) async def async_do_loop(self): diff --git a/homeassistant/components/roon/strings.json b/homeassistant/components/roon/strings.json index f67779e9eaa..a95c6908312 100644 --- a/homeassistant/components/roon/strings.json +++ b/homeassistant/components/roon/strings.json @@ -22,6 +22,20 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" } }, + "entity": { + "event": { + "volume": { + "state_attributes": { + "event_type": { + "state": { + "volume_up": "Volume up", + "volume_down": "Volume down" + } + } + } + } + } + }, "services": { "transfer": { "name": "Transfer", diff --git a/requirements_all.txt b/requirements_all.txt index 039740f7ba7..6d2d8b1ee21 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2361,7 +2361,7 @@ rokuecp==0.18.1 roombapy==1.6.8 # homeassistant.components.roon -roonapi==0.1.4 +roonapi==0.1.5 # homeassistant.components.rova rova==0.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fbdbf86fc94..7f244425a17 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1757,7 +1757,7 @@ rokuecp==0.18.1 roombapy==1.6.8 # homeassistant.components.roon -roonapi==0.1.4 +roonapi==0.1.5 # homeassistant.components.rpi_power rpi-bad-power==0.1.0