Add denonavr DynamicEQ and Audyssey service (#48694)
* denonavr: Add DynamicEQ and Audyssey service * Remove debug print * Syntax sugar * Apply suggestions from code review Co-authored-by: J. Nick Koston <nick@koston.org> * Update homeassistant/components/denonavr/services.yaml Co-authored-by: J. Nick Koston <nick@koston.org> * Remove trailing whitespaces Co-authored-by: J. Nick Koston <nick@koston.org>pull/49509/head
parent
6a4f414236
commit
9003dbfdf3
|
@ -11,10 +11,12 @@ from homeassistant.helpers.httpx_client import get_async_client
|
|||
|
||||
from .config_flow import (
|
||||
CONF_SHOW_ALL_SOURCES,
|
||||
CONF_UPDATE_AUDYSSEY,
|
||||
CONF_ZONE2,
|
||||
CONF_ZONE3,
|
||||
DEFAULT_SHOW_SOURCES,
|
||||
DEFAULT_TIMEOUT,
|
||||
DEFAULT_UPDATE_AUDYSSEY,
|
||||
DEFAULT_ZONE2,
|
||||
DEFAULT_ZONE3,
|
||||
DOMAIN,
|
||||
|
@ -53,6 +55,9 @@ async def async_setup_entry(
|
|||
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
CONF_RECEIVER: receiver,
|
||||
CONF_UPDATE_AUDYSSEY: entry.options.get(
|
||||
CONF_UPDATE_AUDYSSEY, DEFAULT_UPDATE_AUDYSSEY
|
||||
),
|
||||
UNDO_UPDATE_LISTENER: undo_listener,
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,13 @@ CONF_ZONE3 = "zone3"
|
|||
CONF_MODEL = "model"
|
||||
CONF_MANUFACTURER = "manufacturer"
|
||||
CONF_SERIAL_NUMBER = "serial_number"
|
||||
CONF_UPDATE_AUDYSSEY = "update_audyssey"
|
||||
|
||||
DEFAULT_SHOW_SOURCES = False
|
||||
DEFAULT_TIMEOUT = 5
|
||||
DEFAULT_ZONE2 = False
|
||||
DEFAULT_ZONE3 = False
|
||||
DEFAULT_UPDATE_AUDYSSEY = False
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({vol.Optional(CONF_HOST): str})
|
||||
|
||||
|
@ -67,6 +69,12 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||
CONF_ZONE3,
|
||||
default=self.config_entry.options.get(CONF_ZONE3, DEFAULT_ZONE3),
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_UPDATE_AUDYSSEY,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_UPDATE_AUDYSSEY, DEFAULT_UPDATE_AUDYSSEY
|
||||
),
|
||||
): bool,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -45,12 +45,15 @@ from .config_flow import (
|
|||
CONF_MODEL,
|
||||
CONF_SERIAL_NUMBER,
|
||||
CONF_TYPE,
|
||||
CONF_UPDATE_AUDYSSEY,
|
||||
DEFAULT_UPDATE_AUDYSSEY,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_SOUND_MODE_RAW = "sound_mode_raw"
|
||||
ATTR_DYNAMIC_EQ = "dynamic_eq"
|
||||
|
||||
SUPPORT_DENON = (
|
||||
SUPPORT_VOLUME_STEP
|
||||
|
@ -75,6 +78,8 @@ PARALLEL_UPDATES = 1
|
|||
|
||||
# Services
|
||||
SERVICE_GET_COMMAND = "get_command"
|
||||
SERVICE_SET_DYNAMIC_EQ = "set_dynamic_eq"
|
||||
SERVICE_UPDATE_AUDYSSEY = "update_audyssey"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -84,14 +89,23 @@ async def async_setup_entry(
|
|||
):
|
||||
"""Set up the DenonAVR receiver from a config entry."""
|
||||
entities = []
|
||||
receiver = hass.data[DOMAIN][config_entry.entry_id][CONF_RECEIVER]
|
||||
data = hass.data[DOMAIN][config_entry.entry_id]
|
||||
receiver = data[CONF_RECEIVER]
|
||||
update_audyssey = data.get(CONF_UPDATE_AUDYSSEY, DEFAULT_UPDATE_AUDYSSEY)
|
||||
for receiver_zone in receiver.zones.values():
|
||||
if config_entry.data[CONF_SERIAL_NUMBER] is not None:
|
||||
unique_id = f"{config_entry.unique_id}-{receiver_zone.zone}"
|
||||
else:
|
||||
unique_id = f"{config_entry.entry_id}-{receiver_zone.zone}"
|
||||
await receiver_zone.async_setup()
|
||||
entities.append(DenonDevice(receiver_zone, unique_id, config_entry))
|
||||
entities.append(
|
||||
DenonDevice(
|
||||
receiver_zone,
|
||||
unique_id,
|
||||
config_entry,
|
||||
update_audyssey,
|
||||
)
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"%s receiver at host %s initialized", receiver.manufacturer, receiver.host
|
||||
)
|
||||
|
@ -103,6 +117,16 @@ async def async_setup_entry(
|
|||
{vol.Required(ATTR_COMMAND): cv.string},
|
||||
f"async_{SERVICE_GET_COMMAND}",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_DYNAMIC_EQ,
|
||||
{vol.Required(ATTR_DYNAMIC_EQ): cv.boolean},
|
||||
f"async_{SERVICE_SET_DYNAMIC_EQ}",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_UPDATE_AUDYSSEY,
|
||||
{},
|
||||
f"async_{SERVICE_UPDATE_AUDYSSEY}",
|
||||
)
|
||||
|
||||
async_add_entities(entities, update_before_add=True)
|
||||
|
||||
|
@ -115,11 +139,13 @@ class DenonDevice(MediaPlayerEntity):
|
|||
receiver: DenonAVR,
|
||||
unique_id: str,
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
update_audyssey: bool,
|
||||
):
|
||||
"""Initialize the device."""
|
||||
self._receiver = receiver
|
||||
self._unique_id = unique_id
|
||||
self._config_entry = config_entry
|
||||
self._update_audyssey = update_audyssey
|
||||
|
||||
self._supported_features_base = SUPPORT_DENON
|
||||
self._supported_features_base |= (
|
||||
|
@ -194,6 +220,8 @@ class DenonDevice(MediaPlayerEntity):
|
|||
async def async_update(self) -> None:
|
||||
"""Get the latest status information from device."""
|
||||
await self._receiver.async_update()
|
||||
if self._update_audyssey:
|
||||
await self._receiver.async_update_audyssey()
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
|
@ -350,13 +378,22 @@ class DenonDevice(MediaPlayerEntity):
|
|||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return device specific state attributes."""
|
||||
if self._receiver.power != POWER_ON:
|
||||
return {}
|
||||
state_attributes = {}
|
||||
if (
|
||||
self._receiver.sound_mode_raw is not None
|
||||
and self._receiver.support_sound_mode
|
||||
and self._receiver.power == POWER_ON
|
||||
):
|
||||
return {ATTR_SOUND_MODE_RAW: self._receiver.sound_mode_raw}
|
||||
return {}
|
||||
state_attributes[ATTR_SOUND_MODE_RAW] = self._receiver.sound_mode_raw
|
||||
if self._receiver.dynamic_eq is not None:
|
||||
state_attributes[ATTR_DYNAMIC_EQ] = self._receiver.dynamic_eq
|
||||
return state_attributes
|
||||
|
||||
@property
|
||||
def dynamic_eq(self):
|
||||
"""Status of DynamicEQ."""
|
||||
return self._receiver.dynamic_eq
|
||||
|
||||
@async_log_errors
|
||||
async def async_media_play_pause(self):
|
||||
|
@ -436,6 +473,23 @@ class DenonDevice(MediaPlayerEntity):
|
|||
"""Send generic command."""
|
||||
return await self._receiver.async_get_command(command)
|
||||
|
||||
@async_log_errors
|
||||
async def async_update_audyssey(self):
|
||||
"""Get the latest audyssey information from device."""
|
||||
await self._receiver.async_update_audyssey()
|
||||
|
||||
@async_log_errors
|
||||
async def async_set_dynamic_eq(self, dynamic_eq: bool):
|
||||
"""Turn DynamicEQ on or off."""
|
||||
if dynamic_eq:
|
||||
result = await self._receiver.async_dynamic_eq_on()
|
||||
else:
|
||||
result = await self._receiver.async_dynamic_eq_off()
|
||||
|
||||
if self._update_audyssey:
|
||||
await self._receiver.async_update_audyssey()
|
||||
return result
|
||||
|
||||
# Decorator defined before is a staticmethod
|
||||
async_log_errors = staticmethod( # pylint: disable=no-staticmethod-decorator
|
||||
async_log_errors
|
||||
|
|
|
@ -9,3 +9,22 @@ get_command:
|
|||
command:
|
||||
description: Endpoint of the command, including associated parameters.
|
||||
example: "/goform/formiPhoneAppDirect.xml?RCKSK0410370"
|
||||
set_dynamic_eq:
|
||||
description: "Enable or disable DynamicEQ."
|
||||
target:
|
||||
entity:
|
||||
integration: denonavr
|
||||
domain: media_player
|
||||
fields:
|
||||
dynamic_eq:
|
||||
description: "True/false for enable/disable."
|
||||
default: true
|
||||
example: true
|
||||
selector:
|
||||
boolean:
|
||||
update_audyssey:
|
||||
description: "Update Audyssey settings."
|
||||
target:
|
||||
entity:
|
||||
integration: denonavr
|
||||
domain: media_player
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
"data": {
|
||||
"show_all_sources": "Show all sources",
|
||||
"zone2": "Set up Zone 2",
|
||||
"zone3": "Set up Zone 3"
|
||||
"zone3": "Set up Zone 3",
|
||||
"update_audyssey": "Update Audyssey settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
"data": {
|
||||
"show_all_sources": "Show all sources",
|
||||
"zone2": "Set up Zone 2",
|
||||
"zone3": "Set up Zone 3"
|
||||
"zone3": "Set up Zone 3",
|
||||
"update_audyssey": "Update Audyssey settings"
|
||||
},
|
||||
"description": "Specify optional settings",
|
||||
"title": "Denon AVR Network Receivers"
|
||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.components.denonavr.config_flow import (
|
|||
CONF_SERIAL_NUMBER,
|
||||
CONF_SHOW_ALL_SOURCES,
|
||||
CONF_TYPE,
|
||||
CONF_UPDATE_AUDYSSEY,
|
||||
CONF_ZONE2,
|
||||
CONF_ZONE3,
|
||||
DOMAIN,
|
||||
|
@ -28,6 +29,7 @@ TEST_IGNORED_MODEL = "HEOS 7"
|
|||
TEST_RECEIVER_TYPE = "avr-x"
|
||||
TEST_SERIALNUMBER = "123456789"
|
||||
TEST_MANUFACTURER = "Denon"
|
||||
TEST_UPDATE_AUDYSSEY = False
|
||||
TEST_SSDP_LOCATION = f"http://{TEST_HOST}/"
|
||||
TEST_UNIQUE_ID = f"{TEST_MODEL}-{TEST_SERIALNUMBER}"
|
||||
TEST_DISCOVER_1_RECEIVER = [{CONF_HOST: TEST_HOST}]
|
||||
|
@ -397,6 +399,7 @@ async def test_options_flow(hass):
|
|||
CONF_TYPE: TEST_RECEIVER_TYPE,
|
||||
CONF_MANUFACTURER: TEST_MANUFACTURER,
|
||||
CONF_SERIAL_NUMBER: TEST_SERIALNUMBER,
|
||||
CONF_UPDATE_AUDYSSEY: TEST_UPDATE_AUDYSSEY,
|
||||
},
|
||||
title=TEST_NAME,
|
||||
)
|
||||
|
@ -420,6 +423,7 @@ async def test_options_flow(hass):
|
|||
CONF_SHOW_ALL_SOURCES: True,
|
||||
CONF_ZONE2: True,
|
||||
CONF_ZONE3: True,
|
||||
CONF_UPDATE_AUDYSSEY: False,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,10 @@ from homeassistant.components.denonavr.config_flow import (
|
|||
)
|
||||
from homeassistant.components.denonavr.media_player import (
|
||||
ATTR_COMMAND,
|
||||
ATTR_DYNAMIC_EQ,
|
||||
SERVICE_GET_COMMAND,
|
||||
SERVICE_SET_DYNAMIC_EQ,
|
||||
SERVICE_UPDATE_AUDYSSEY,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST
|
||||
|
||||
|
@ -94,3 +97,41 @@ async def test_get_command(hass, client):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
client.async_get_command.assert_awaited_with("test_command")
|
||||
|
||||
|
||||
async def test_dynamic_eq(hass, client):
|
||||
"""Test that dynamic eq method works."""
|
||||
await setup_denonavr(hass)
|
||||
|
||||
data = {
|
||||
ATTR_ENTITY_ID: ENTITY_ID,
|
||||
ATTR_DYNAMIC_EQ: True,
|
||||
}
|
||||
# Verify on call
|
||||
await hass.services.async_call(DOMAIN, SERVICE_SET_DYNAMIC_EQ, data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Verify off call
|
||||
data[ATTR_DYNAMIC_EQ] = False
|
||||
await hass.services.async_call(DOMAIN, SERVICE_SET_DYNAMIC_EQ, data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client.async_dynamic_eq_on.assert_called_once()
|
||||
client.async_dynamic_eq_off.assert_called_once()
|
||||
|
||||
|
||||
async def test_update_audyssey(hass, client):
|
||||
"""Test that dynamic eq method works."""
|
||||
await setup_denonavr(hass)
|
||||
|
||||
# Verify call
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_AUDYSSEY,
|
||||
{
|
||||
ATTR_ENTITY_ID: ENTITY_ID,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client.async_update_audyssey.assert_called_once()
|
||||
|
|
Loading…
Reference in New Issue