mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
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>
This commit is contained in:
parent
6a4f414236
commit
9003dbfdf3
@ -11,10 +11,12 @@ from homeassistant.helpers.httpx_client import get_async_client
|
|||||||
|
|
||||||
from .config_flow import (
|
from .config_flow import (
|
||||||
CONF_SHOW_ALL_SOURCES,
|
CONF_SHOW_ALL_SOURCES,
|
||||||
|
CONF_UPDATE_AUDYSSEY,
|
||||||
CONF_ZONE2,
|
CONF_ZONE2,
|
||||||
CONF_ZONE3,
|
CONF_ZONE3,
|
||||||
DEFAULT_SHOW_SOURCES,
|
DEFAULT_SHOW_SOURCES,
|
||||||
DEFAULT_TIMEOUT,
|
DEFAULT_TIMEOUT,
|
||||||
|
DEFAULT_UPDATE_AUDYSSEY,
|
||||||
DEFAULT_ZONE2,
|
DEFAULT_ZONE2,
|
||||||
DEFAULT_ZONE3,
|
DEFAULT_ZONE3,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -53,6 +55,9 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
hass.data[DOMAIN][entry.entry_id] = {
|
||||||
CONF_RECEIVER: receiver,
|
CONF_RECEIVER: receiver,
|
||||||
|
CONF_UPDATE_AUDYSSEY: entry.options.get(
|
||||||
|
CONF_UPDATE_AUDYSSEY, DEFAULT_UPDATE_AUDYSSEY
|
||||||
|
),
|
||||||
UNDO_UPDATE_LISTENER: undo_listener,
|
UNDO_UPDATE_LISTENER: undo_listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,13 @@ CONF_ZONE3 = "zone3"
|
|||||||
CONF_MODEL = "model"
|
CONF_MODEL = "model"
|
||||||
CONF_MANUFACTURER = "manufacturer"
|
CONF_MANUFACTURER = "manufacturer"
|
||||||
CONF_SERIAL_NUMBER = "serial_number"
|
CONF_SERIAL_NUMBER = "serial_number"
|
||||||
|
CONF_UPDATE_AUDYSSEY = "update_audyssey"
|
||||||
|
|
||||||
DEFAULT_SHOW_SOURCES = False
|
DEFAULT_SHOW_SOURCES = False
|
||||||
DEFAULT_TIMEOUT = 5
|
DEFAULT_TIMEOUT = 5
|
||||||
DEFAULT_ZONE2 = False
|
DEFAULT_ZONE2 = False
|
||||||
DEFAULT_ZONE3 = False
|
DEFAULT_ZONE3 = False
|
||||||
|
DEFAULT_UPDATE_AUDYSSEY = False
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({vol.Optional(CONF_HOST): str})
|
CONFIG_SCHEMA = vol.Schema({vol.Optional(CONF_HOST): str})
|
||||||
|
|
||||||
@ -67,6 +69,12 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
CONF_ZONE3,
|
CONF_ZONE3,
|
||||||
default=self.config_entry.options.get(CONF_ZONE3, DEFAULT_ZONE3),
|
default=self.config_entry.options.get(CONF_ZONE3, DEFAULT_ZONE3),
|
||||||
): bool,
|
): 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_MODEL,
|
||||||
CONF_SERIAL_NUMBER,
|
CONF_SERIAL_NUMBER,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
|
CONF_UPDATE_AUDYSSEY,
|
||||||
|
DEFAULT_UPDATE_AUDYSSEY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTR_SOUND_MODE_RAW = "sound_mode_raw"
|
ATTR_SOUND_MODE_RAW = "sound_mode_raw"
|
||||||
|
ATTR_DYNAMIC_EQ = "dynamic_eq"
|
||||||
|
|
||||||
SUPPORT_DENON = (
|
SUPPORT_DENON = (
|
||||||
SUPPORT_VOLUME_STEP
|
SUPPORT_VOLUME_STEP
|
||||||
@ -75,6 +78,8 @@ PARALLEL_UPDATES = 1
|
|||||||
|
|
||||||
# Services
|
# Services
|
||||||
SERVICE_GET_COMMAND = "get_command"
|
SERVICE_GET_COMMAND = "get_command"
|
||||||
|
SERVICE_SET_DYNAMIC_EQ = "set_dynamic_eq"
|
||||||
|
SERVICE_UPDATE_AUDYSSEY = "update_audyssey"
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -84,14 +89,23 @@ async def async_setup_entry(
|
|||||||
):
|
):
|
||||||
"""Set up the DenonAVR receiver from a config entry."""
|
"""Set up the DenonAVR receiver from a config entry."""
|
||||||
entities = []
|
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():
|
for receiver_zone in receiver.zones.values():
|
||||||
if config_entry.data[CONF_SERIAL_NUMBER] is not None:
|
if config_entry.data[CONF_SERIAL_NUMBER] is not None:
|
||||||
unique_id = f"{config_entry.unique_id}-{receiver_zone.zone}"
|
unique_id = f"{config_entry.unique_id}-{receiver_zone.zone}"
|
||||||
else:
|
else:
|
||||||
unique_id = f"{config_entry.entry_id}-{receiver_zone.zone}"
|
unique_id = f"{config_entry.entry_id}-{receiver_zone.zone}"
|
||||||
await receiver_zone.async_setup()
|
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(
|
_LOGGER.debug(
|
||||||
"%s receiver at host %s initialized", receiver.manufacturer, receiver.host
|
"%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},
|
{vol.Required(ATTR_COMMAND): cv.string},
|
||||||
f"async_{SERVICE_GET_COMMAND}",
|
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)
|
async_add_entities(entities, update_before_add=True)
|
||||||
|
|
||||||
@ -115,11 +139,13 @@ class DenonDevice(MediaPlayerEntity):
|
|||||||
receiver: DenonAVR,
|
receiver: DenonAVR,
|
||||||
unique_id: str,
|
unique_id: str,
|
||||||
config_entry: config_entries.ConfigEntry,
|
config_entry: config_entries.ConfigEntry,
|
||||||
|
update_audyssey: bool,
|
||||||
):
|
):
|
||||||
"""Initialize the device."""
|
"""Initialize the device."""
|
||||||
self._receiver = receiver
|
self._receiver = receiver
|
||||||
self._unique_id = unique_id
|
self._unique_id = unique_id
|
||||||
self._config_entry = config_entry
|
self._config_entry = config_entry
|
||||||
|
self._update_audyssey = update_audyssey
|
||||||
|
|
||||||
self._supported_features_base = SUPPORT_DENON
|
self._supported_features_base = SUPPORT_DENON
|
||||||
self._supported_features_base |= (
|
self._supported_features_base |= (
|
||||||
@ -194,6 +220,8 @@ class DenonDevice(MediaPlayerEntity):
|
|||||||
async def async_update(self) -> None:
|
async def async_update(self) -> None:
|
||||||
"""Get the latest status information from device."""
|
"""Get the latest status information from device."""
|
||||||
await self._receiver.async_update()
|
await self._receiver.async_update()
|
||||||
|
if self._update_audyssey:
|
||||||
|
await self._receiver.async_update_audyssey()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
@ -350,13 +378,22 @@ class DenonDevice(MediaPlayerEntity):
|
|||||||
@property
|
@property
|
||||||
def extra_state_attributes(self):
|
def extra_state_attributes(self):
|
||||||
"""Return device specific state attributes."""
|
"""Return device specific state attributes."""
|
||||||
|
if self._receiver.power != POWER_ON:
|
||||||
|
return {}
|
||||||
|
state_attributes = {}
|
||||||
if (
|
if (
|
||||||
self._receiver.sound_mode_raw is not None
|
self._receiver.sound_mode_raw is not None
|
||||||
and self._receiver.support_sound_mode
|
and self._receiver.support_sound_mode
|
||||||
and self._receiver.power == POWER_ON
|
|
||||||
):
|
):
|
||||||
return {ATTR_SOUND_MODE_RAW: self._receiver.sound_mode_raw}
|
state_attributes[ATTR_SOUND_MODE_RAW] = self._receiver.sound_mode_raw
|
||||||
return {}
|
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_log_errors
|
||||||
async def async_media_play_pause(self):
|
async def async_media_play_pause(self):
|
||||||
@ -436,6 +473,23 @@ class DenonDevice(MediaPlayerEntity):
|
|||||||
"""Send generic command."""
|
"""Send generic command."""
|
||||||
return await self._receiver.async_get_command(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
|
# Decorator defined before is a staticmethod
|
||||||
async_log_errors = staticmethod( # pylint: disable=no-staticmethod-decorator
|
async_log_errors = staticmethod( # pylint: disable=no-staticmethod-decorator
|
||||||
async_log_errors
|
async_log_errors
|
||||||
|
@ -9,3 +9,22 @@ get_command:
|
|||||||
command:
|
command:
|
||||||
description: Endpoint of the command, including associated parameters.
|
description: Endpoint of the command, including associated parameters.
|
||||||
example: "/goform/formiPhoneAppDirect.xml?RCKSK0410370"
|
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": {
|
"data": {
|
||||||
"show_all_sources": "Show all sources",
|
"show_all_sources": "Show all sources",
|
||||||
"zone2": "Set up Zone 2",
|
"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": {
|
"data": {
|
||||||
"show_all_sources": "Show all sources",
|
"show_all_sources": "Show all sources",
|
||||||
"zone2": "Set up Zone 2",
|
"zone2": "Set up Zone 2",
|
||||||
"zone3": "Set up Zone 3"
|
"zone3": "Set up Zone 3",
|
||||||
|
"update_audyssey": "Update Audyssey settings"
|
||||||
},
|
},
|
||||||
"description": "Specify optional settings",
|
"description": "Specify optional settings",
|
||||||
"title": "Denon AVR Network Receivers"
|
"title": "Denon AVR Network Receivers"
|
||||||
|
@ -11,6 +11,7 @@ from homeassistant.components.denonavr.config_flow import (
|
|||||||
CONF_SERIAL_NUMBER,
|
CONF_SERIAL_NUMBER,
|
||||||
CONF_SHOW_ALL_SOURCES,
|
CONF_SHOW_ALL_SOURCES,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
|
CONF_UPDATE_AUDYSSEY,
|
||||||
CONF_ZONE2,
|
CONF_ZONE2,
|
||||||
CONF_ZONE3,
|
CONF_ZONE3,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -28,6 +29,7 @@ TEST_IGNORED_MODEL = "HEOS 7"
|
|||||||
TEST_RECEIVER_TYPE = "avr-x"
|
TEST_RECEIVER_TYPE = "avr-x"
|
||||||
TEST_SERIALNUMBER = "123456789"
|
TEST_SERIALNUMBER = "123456789"
|
||||||
TEST_MANUFACTURER = "Denon"
|
TEST_MANUFACTURER = "Denon"
|
||||||
|
TEST_UPDATE_AUDYSSEY = False
|
||||||
TEST_SSDP_LOCATION = f"http://{TEST_HOST}/"
|
TEST_SSDP_LOCATION = f"http://{TEST_HOST}/"
|
||||||
TEST_UNIQUE_ID = f"{TEST_MODEL}-{TEST_SERIALNUMBER}"
|
TEST_UNIQUE_ID = f"{TEST_MODEL}-{TEST_SERIALNUMBER}"
|
||||||
TEST_DISCOVER_1_RECEIVER = [{CONF_HOST: TEST_HOST}]
|
TEST_DISCOVER_1_RECEIVER = [{CONF_HOST: TEST_HOST}]
|
||||||
@ -397,6 +399,7 @@ async def test_options_flow(hass):
|
|||||||
CONF_TYPE: TEST_RECEIVER_TYPE,
|
CONF_TYPE: TEST_RECEIVER_TYPE,
|
||||||
CONF_MANUFACTURER: TEST_MANUFACTURER,
|
CONF_MANUFACTURER: TEST_MANUFACTURER,
|
||||||
CONF_SERIAL_NUMBER: TEST_SERIALNUMBER,
|
CONF_SERIAL_NUMBER: TEST_SERIALNUMBER,
|
||||||
|
CONF_UPDATE_AUDYSSEY: TEST_UPDATE_AUDYSSEY,
|
||||||
},
|
},
|
||||||
title=TEST_NAME,
|
title=TEST_NAME,
|
||||||
)
|
)
|
||||||
@ -420,6 +423,7 @@ async def test_options_flow(hass):
|
|||||||
CONF_SHOW_ALL_SOURCES: True,
|
CONF_SHOW_ALL_SOURCES: True,
|
||||||
CONF_ZONE2: True,
|
CONF_ZONE2: True,
|
||||||
CONF_ZONE3: 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 (
|
from homeassistant.components.denonavr.media_player import (
|
||||||
ATTR_COMMAND,
|
ATTR_COMMAND,
|
||||||
|
ATTR_DYNAMIC_EQ,
|
||||||
SERVICE_GET_COMMAND,
|
SERVICE_GET_COMMAND,
|
||||||
|
SERVICE_SET_DYNAMIC_EQ,
|
||||||
|
SERVICE_UPDATE_AUDYSSEY,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST
|
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()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
client.async_get_command.assert_awaited_with("test_command")
|
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…
x
Reference in New Issue
Block a user