Skip to content

Commit

Permalink
Add sound mode support
Browse files Browse the repository at this point in the history
  • Loading branch information
arturpragacz committed Jan 24, 2025
1 parent a56c37a commit 62c2193
Show file tree
Hide file tree
Showing 7 changed files with 469 additions and 133 deletions.
40 changes: 38 additions & 2 deletions homeassistant/components/onkyo/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""The onkyo component."""

from dataclasses import dataclass
import logging

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, Platform
Expand All @@ -9,10 +10,19 @@
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType

from .const import DOMAIN, OPTION_INPUT_SOURCES, InputSource
from .const import (
DOMAIN,
OPTION_INPUT_SOURCES,
OPTION_LISTENING_MODES,
OPTION_LISTENING_MODES_DEFAULT,
InputSource,
ListeningMode,
)
from .receiver import Receiver, async_interview
from .services import DATA_MP_ENTITIES, async_register_services

_LOGGER = logging.getLogger(__name__)

PLATFORMS = [Platform.MEDIA_PLAYER]

CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
Expand All @@ -24,6 +34,7 @@ class OnkyoData:

receiver: Receiver
sources: dict[InputSource, str]
sound_modes: dict[ListeningMode, str]


type OnkyoConfigEntry = ConfigEntry[OnkyoData]
Expand All @@ -50,7 +61,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: OnkyoConfigEntry) -> boo
sources_store: dict[str, str] = entry.options[OPTION_INPUT_SOURCES]
sources = {InputSource(k): v for k, v in sources_store.items()}

entry.runtime_data = OnkyoData(receiver, sources)
sound_modes_store: dict[str, str] = entry.options[OPTION_LISTENING_MODES]
sound_modes = {ListeningMode(k): v for k, v in sound_modes_store.items()}

entry.runtime_data = OnkyoData(receiver, sources, sound_modes)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

Expand All @@ -74,3 +88,25 @@ async def async_unload_entry(hass: HomeAssistant, entry: OnkyoConfigEntry) -> bo
async def update_listener(hass: HomeAssistant, entry: OnkyoConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)


async def async_migrate_entry(hass: HomeAssistant, entry: OnkyoConfigEntry) -> bool:
"""Migrate entry."""
_LOGGER.debug("Migrating from version %s.%s", entry.version, entry.minor_version)

if entry.version > 1:
# This means the user has downgraded from a future version
return False

Check warning on line 99 in homeassistant/components/onkyo/__init__.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/onkyo/__init__.py#L99

Added line #L99 was not covered by tests

if entry.version == 1:
new_options = {**entry.options}
if entry.minor_version < 2:
new_options[OPTION_LISTENING_MODES] = OPTION_LISTENING_MODES_DEFAULT

hass.config_entries.async_update_entry(
entry, options=new_options, version=1, minor_version=2
)

_LOGGER.debug("Migrated to version %s.%s", entry.version, entry.minor_version)

return True
134 changes: 104 additions & 30 deletions homeassistant/components/onkyo/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,29 @@
CONF_SOURCES,
DOMAIN,
OPTION_INPUT_SOURCES,
OPTION_INPUT_SOURCES_DEFAULT,
OPTION_LISTENING_MODES,
OPTION_LISTENING_MODES_DEFAULT,
OPTION_MAX_VOLUME,
OPTION_MAX_VOLUME_DEFAULT,
OPTION_VOLUME_RESOLUTION,
OPTION_VOLUME_RESOLUTION_DEFAULT,
VOLUME_RESOLUTION_ALLOWED,
InputSource,
ListeningMode,
)
from .receiver import ReceiverInfo, async_discover, async_interview

_LOGGER = logging.getLogger(__name__)

CONF_DEVICE = "device"

INPUT_SOURCES_ALL_MEANINGS = [
input_source.value_meaning for input_source in InputSource
]
INPUT_SOURCES_ALL_MEANINGS = {
input_source.value_meaning: input_source for input_source in InputSource
}
LISTENING_MODES_ALL_MEANINGS = {
listening_mode.value_meaning: listening_mode for listening_mode in ListeningMode
}
STEP_MANUAL_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str})
STEP_RECONFIGURE_SCHEMA = vol.Schema(
{
Expand All @@ -59,7 +66,14 @@
{
vol.Required(OPTION_INPUT_SOURCES): SelectSelector(
SelectSelectorConfig(
options=INPUT_SOURCES_ALL_MEANINGS,
options=list(INPUT_SOURCES_ALL_MEANINGS),
multiple=True,
mode=SelectSelectorMode.DROPDOWN,
)
),
vol.Required(OPTION_LISTENING_MODES): SelectSelector(
SelectSelectorConfig(
options=list(LISTENING_MODES_ALL_MEANINGS),
multiple=True,
mode=SelectSelectorMode.DROPDOWN,
)
Expand All @@ -71,6 +85,8 @@
class OnkyoConfigFlow(ConfigFlow, domain=DOMAIN):
"""Onkyo config flow."""

MINOR_VERSION = 2

_receiver_info: ReceiverInfo
_discovered_infos: dict[str, ReceiverInfo]

Expand Down Expand Up @@ -238,9 +254,8 @@ async def async_step_configure_receiver(
CONF_HOST: self._receiver_info.host,
},
options={
**entry_options,
OPTION_VOLUME_RESOLUTION: volume_resolution,
OPTION_MAX_VOLUME: entry_options[OPTION_MAX_VOLUME],
OPTION_INPUT_SOURCES: entry_options[OPTION_INPUT_SOURCES],
},
)

Expand All @@ -250,12 +265,24 @@ async def async_step_configure_receiver(
input_source_meanings: list[str] = user_input[OPTION_INPUT_SOURCES]
if not input_source_meanings:
errors[OPTION_INPUT_SOURCES] = "empty_input_source_list"
else:

listening_modes: list[str] = user_input[OPTION_LISTENING_MODES]
if not listening_modes:
errors[OPTION_LISTENING_MODES] = "empty_listening_mode_list"

if not errors:
input_sources_store: dict[str, str] = {}
for input_source_meaning in input_source_meanings:
input_source = InputSource.from_meaning(input_source_meaning)
input_source = INPUT_SOURCES_ALL_MEANINGS[input_source_meaning]
input_sources_store[input_source.value] = input_source_meaning

listening_modes_store: dict[str, str] = {}
for listening_mode_meaning in listening_modes:
listening_mode = LISTENING_MODES_ALL_MEANINGS[
listening_mode_meaning
]
listening_modes_store[listening_mode.value] = listening_mode_meaning

result = self.async_create_entry(
title=self._receiver_info.model_name,
data={
Expand All @@ -265,6 +292,7 @@ async def async_step_configure_receiver(
OPTION_VOLUME_RESOLUTION: volume_resolution,
OPTION_MAX_VOLUME: OPTION_MAX_VOLUME_DEFAULT,
OPTION_INPUT_SOURCES: input_sources_store,
OPTION_LISTENING_MODES: listening_modes_store,
},
)

Expand All @@ -278,16 +306,13 @@ async def async_step_configure_receiver(
if reconfigure_entry is None:
suggested_values = {
OPTION_VOLUME_RESOLUTION: OPTION_VOLUME_RESOLUTION_DEFAULT,
OPTION_INPUT_SOURCES: [],
OPTION_INPUT_SOURCES: OPTION_INPUT_SOURCES_DEFAULT,
OPTION_LISTENING_MODES: OPTION_LISTENING_MODES_DEFAULT,
}
else:
entry_options = reconfigure_entry.options
suggested_values = {
OPTION_VOLUME_RESOLUTION: entry_options[OPTION_VOLUME_RESOLUTION],
OPTION_INPUT_SOURCES: [
InputSource(input_source).value_meaning
for input_source in entry_options[OPTION_INPUT_SOURCES]
],
}

return self.async_show_form(
Expand Down Expand Up @@ -356,6 +381,7 @@ async def async_step_import(self, user_input: dict[str, Any]) -> ConfigFlowResul
OPTION_VOLUME_RESOLUTION: volume_resolution,
OPTION_MAX_VOLUME: max_volume,
OPTION_INPUT_SOURCES: sources_store,
OPTION_LISTENING_MODES: OPTION_LISTENING_MODES_DEFAULT,
},
)

Expand All @@ -373,7 +399,14 @@ def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
),
vol.Required(OPTION_INPUT_SOURCES): SelectSelector(
SelectSelectorConfig(
options=INPUT_SOURCES_ALL_MEANINGS,
options=list(INPUT_SOURCES_ALL_MEANINGS),
multiple=True,
mode=SelectSelectorMode.DROPDOWN,
)
),
vol.Required(OPTION_LISTENING_MODES): SelectSelector(
SelectSelectorConfig(
options=list(LISTENING_MODES_ALL_MEANINGS),
multiple=True,
mode=SelectSelectorMode.DROPDOWN,
)
Expand All @@ -387,6 +420,7 @@ class OnkyoOptionsFlowHandler(OptionsFlow):

_data: dict[str, Any]
_input_sources: dict[InputSource, str]
_listening_modes: dict[ListeningMode, str]

async def async_step_init(
self, user_input: dict[str, Any] | None = None
Expand All @@ -397,17 +431,33 @@ async def async_step_init(
entry_options = self.config_entry.options

if user_input is not None:
self._input_sources = {}
for input_source_meaning in user_input[OPTION_INPUT_SOURCES]:
input_source = InputSource.from_meaning(input_source_meaning)
input_source_name = entry_options[OPTION_INPUT_SOURCES].get(
input_source.value, input_source_meaning
)
self._input_sources[input_source] = input_source_name

if not self._input_sources:
input_source_meanings: list[str] = user_input[OPTION_INPUT_SOURCES]
if not input_source_meanings:
errors[OPTION_INPUT_SOURCES] = "empty_input_source_list"
else:

listening_mode_meanings: list[str] = user_input[OPTION_LISTENING_MODES]
if not listening_mode_meanings:
errors[OPTION_LISTENING_MODES] = "empty_listening_mode_list"

if not errors:
self._input_sources = {}
for input_source_meaning in input_source_meanings:
input_source = INPUT_SOURCES_ALL_MEANINGS[input_source_meaning]
input_source_name = entry_options[OPTION_INPUT_SOURCES].get(
input_source.value, input_source_meaning
)
self._input_sources[input_source] = input_source_name

self._listening_modes = {}
for listening_mode_meaning in listening_mode_meanings:
listening_mode = LISTENING_MODES_ALL_MEANINGS[
listening_mode_meaning
]
listening_mode_name = entry_options[OPTION_LISTENING_MODES].get(
listening_mode.value, listening_mode_meaning
)
self._listening_modes[listening_mode] = listening_mode_name

self._data = {
OPTION_VOLUME_RESOLUTION: entry_options[OPTION_VOLUME_RESOLUTION],
OPTION_MAX_VOLUME: user_input[OPTION_MAX_VOLUME],
Expand All @@ -423,6 +473,10 @@ async def async_step_init(
InputSource(input_source).value_meaning
for input_source in entry_options[OPTION_INPUT_SOURCES]
],
OPTION_LISTENING_MODES: [
ListeningMode(listening_mode).value_meaning
for listening_mode in entry_options[OPTION_LISTENING_MODES]
],
}

return self.async_show_form(
Expand All @@ -440,28 +494,48 @@ async def async_step_names(
if user_input is not None:
input_sources_store: dict[str, str] = {}
for input_source_meaning, input_source_name in user_input[
"input_sources"
OPTION_INPUT_SOURCES
].items():
input_source = InputSource.from_meaning(input_source_meaning)
input_source = INPUT_SOURCES_ALL_MEANINGS[input_source_meaning]
input_sources_store[input_source.value] = input_source_name

listening_modes_store: dict[str, str] = {}
for listening_mode_meaning, listening_mode_name in user_input[
OPTION_LISTENING_MODES
].items():
listening_mode = LISTENING_MODES_ALL_MEANINGS[listening_mode_meaning]
listening_modes_store[listening_mode.value] = listening_mode_name

return self.async_create_entry(
data={
**self._data,
OPTION_INPUT_SOURCES: input_sources_store,
OPTION_LISTENING_MODES: listening_modes_store,
}
)

schema_dict: dict[Any, Selector] = {}

input_sources_schema_dict: dict[Any, Selector] = {}
for input_source, input_source_name in self._input_sources.items():
schema_dict[
input_sources_schema_dict[
vol.Required(input_source.value_meaning, default=input_source_name)
] = TextSelector()

listening_modes_schema_dict: dict[Any, Selector] = {}
for listening_mode, listening_mode_name in self._listening_modes.items():
listening_modes_schema_dict[
vol.Required(listening_mode.value_meaning, default=listening_mode_name)
] = TextSelector()

return self.async_show_form(
step_id="names",
data_schema=vol.Schema(
{vol.Required("input_sources"): section(vol.Schema(schema_dict))}
{
vol.Required(OPTION_INPUT_SOURCES): section(
vol.Schema(input_sources_schema_dict)
),
vol.Required(OPTION_LISTENING_MODES): section(
vol.Schema(listening_modes_schema_dict)
),
}
),
)
Loading

0 comments on commit 62c2193

Please sign in to comment.