From b4730f4ffed94720716e1eb4b1ea88f04f27312e Mon Sep 17 00:00:00 2001 From: micha91 Date: Sun, 28 Nov 2021 18:52:46 +0100 Subject: [PATCH] Add Yamaha MusicCast number entities (#60093) --- .coveragerc | 1 + .../components/yamaha_musiccast/__init__.py | 46 +++++++++++++- .../components/yamaha_musiccast/const.py | 14 +++++ .../components/yamaha_musiccast/manifest.json | 2 +- .../components/yamaha_musiccast/number.py | 62 +++++++++++++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 7 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/yamaha_musiccast/number.py diff --git a/.coveragerc b/.coveragerc index b0f3baf33cc..e7863427d63 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1288,6 +1288,7 @@ omit = homeassistant/components/yale_smart_alarm/coordinator.py homeassistant/components/yamaha_musiccast/__init__.py homeassistant/components/yamaha_musiccast/media_player.py + homeassistant/components/yamaha_musiccast/number.py homeassistant/components/yandex_transport/* homeassistant/components/yeelightsunflower/light.py homeassistant/components/yi/camera.py diff --git a/homeassistant/components/yamaha_musiccast/__init__.py b/homeassistant/components/yamaha_musiccast/__init__.py index 2245dd952a9..06a242c6070 100644 --- a/homeassistant/components/yamaha_musiccast/__init__.py +++ b/homeassistant/components/yamaha_musiccast/__init__.py @@ -5,6 +5,7 @@ from datetime import timedelta import logging from aiomusiccast import MusicCastConnectionException +from aiomusiccast.capabilities import Capability from aiomusiccast.musiccast_device import MusicCastData, MusicCastDevice from homeassistant.components import ssdp @@ -20,9 +21,16 @@ from homeassistant.helpers.update_coordinator import ( UpdateFailed, ) -from .const import BRAND, CONF_SERIAL, CONF_UPNP_DESC, DEFAULT_ZONE, DOMAIN +from .const import ( + BRAND, + CONF_SERIAL, + CONF_UPNP_DESC, + DEFAULT_ZONE, + DOMAIN, + ENTITY_CATEGORY_MAPPING, +) -PLATFORMS = ["media_player"] +PLATFORMS = ["media_player", "number"] _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=60) @@ -66,6 +74,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) coordinator = MusicCastDataUpdateCoordinator(hass, client=client) await coordinator.async_config_entry_first_refresh() + coordinator.musiccast.build_capabilities() hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator @@ -190,3 +199,36 @@ class MusicCastDeviceEntity(MusicCastEntity): device_info["via_device"] = (DOMAIN, self.coordinator.data.device_id) return device_info + + +class MusicCastCapabilityEntity(MusicCastDeviceEntity): + """Base Entity type for all capabilities.""" + + def __init__( + self, + coordinator: MusicCastDataUpdateCoordinator, + capability: Capability, + zone_id: str = None, + ) -> None: + """Initialize a capability based entity.""" + if zone_id is not None: + self._zone_id = zone_id + self.capability = capability + super().__init__(name=capability.name, icon="", coordinator=coordinator) + self._attr_entity_category = ENTITY_CATEGORY_MAPPING.get(capability.entity_type) + + async def async_added_to_hass(self): + """Run when this Entity has been added to HA.""" + await super().async_added_to_hass() + # All capability based entities should register callbacks to update HA when their state changes + self.coordinator.musiccast.register_callback(self.async_write_ha_state) + + async def async_will_remove_from_hass(self): + """Entity being removed from hass.""" + await super().async_added_to_hass() + self.coordinator.musiccast.remove_callback(self.async_write_ha_state) + + @property + def unique_id(self) -> str: + """Return the unique ID for this entity.""" + return f"{self.device_id}_{self.capability.id}" diff --git a/homeassistant/components/yamaha_musiccast/const.py b/homeassistant/components/yamaha_musiccast/const.py index 55ce3920fa1..5384cc56694 100644 --- a/homeassistant/components/yamaha_musiccast/const.py +++ b/homeassistant/components/yamaha_musiccast/const.py @@ -1,5 +1,7 @@ """Constants for the MusicCast integration.""" +from aiomusiccast.capabilities import EntityType + from homeassistant.components.media_player.const import ( MEDIA_CLASS_DIRECTORY, MEDIA_CLASS_TRACK, @@ -7,6 +9,11 @@ from homeassistant.components.media_player.const import ( REPEAT_MODE_OFF, REPEAT_MODE_ONE, ) +from homeassistant.const import ( + ENTITY_CATEGORY_CONFIG, + ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORY_SYSTEM, +) DOMAIN = "yamaha_musiccast" @@ -42,3 +49,10 @@ MEDIA_CLASS_MAPPING = { "directory": MEDIA_CLASS_DIRECTORY, "categories": MEDIA_CLASS_DIRECTORY, } + +ENTITY_CATEGORY_MAPPING = { + EntityType.CONFIG: ENTITY_CATEGORY_CONFIG, + EntityType.REGULAR: None, + EntityType.DIAGNOSTIC: ENTITY_CATEGORY_DIAGNOSTIC, + EntityType.SYSTEM: ENTITY_CATEGORY_SYSTEM, +} diff --git a/homeassistant/components/yamaha_musiccast/manifest.json b/homeassistant/components/yamaha_musiccast/manifest.json index 8a7e9cf2c79..329fa2354d5 100644 --- a/homeassistant/components/yamaha_musiccast/manifest.json +++ b/homeassistant/components/yamaha_musiccast/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yamaha_musiccast", "requirements": [ - "aiomusiccast==0.13.1" + "aiomusiccast==0.14.2" ], "ssdp": [ { diff --git a/homeassistant/components/yamaha_musiccast/number.py b/homeassistant/components/yamaha_musiccast/number.py new file mode 100644 index 00000000000..daef8bacd12 --- /dev/null +++ b/homeassistant/components/yamaha_musiccast/number.py @@ -0,0 +1,62 @@ +"""Number entities for musiccast.""" + +from aiomusiccast.capabilities import NumberSetter + +from homeassistant.components.number import NumberEntity +from homeassistant.components.yamaha_musiccast import ( + DOMAIN, + MusicCastCapabilityEntity, + MusicCastDataUpdateCoordinator, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up MusicCast number entities based on a config entry.""" + coordinator: MusicCastDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + + number_entities = [] + + for capability in coordinator.data.capabilities: + if isinstance(capability, NumberSetter): + number_entities.append(NumberCapability(coordinator, capability)) + + for zone, data in coordinator.data.zones.items(): + for capability in data.capabilities: + if isinstance(capability, NumberSetter): + number_entities.append(NumberCapability(coordinator, capability, zone)) + + async_add_entities(number_entities) + + +class NumberCapability(MusicCastCapabilityEntity, NumberEntity): + """Representation of a MusicCast Number entity.""" + + capability: NumberSetter + + def __init__( + self, + coordinator: MusicCastDataUpdateCoordinator, + capability: NumberSetter, + zone_id: str = None, + ) -> None: + """Initialize the number entity.""" + super().__init__(coordinator, capability, zone_id) + self._attr_min_value = capability.value_range.minimum + self._attr_max_value = capability.value_range.maximum + self._attr_step = capability.value_range.step + + @property + def value(self): + """Return the current value.""" + return self.capability.current + + async def async_set_value(self, value: float): + """Set a new value.""" + await self.capability.set(value) diff --git a/requirements_all.txt b/requirements_all.txt index 3d47a00fe2e..5e6ab6c13b4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -216,7 +216,7 @@ aiolyric==1.0.8 aiomodernforms==0.1.8 # homeassistant.components.yamaha_musiccast -aiomusiccast==0.13.1 +aiomusiccast==0.14.2 # homeassistant.components.nanoleaf aionanoleaf==0.0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 46e7f97a7f2..d8e91756a36 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -149,7 +149,7 @@ aiolyric==1.0.8 aiomodernforms==0.1.8 # homeassistant.components.yamaha_musiccast -aiomusiccast==0.13.1 +aiomusiccast==0.14.2 # homeassistant.components.nanoleaf aionanoleaf==0.0.4