Remove deprecated YAML import in Onkyo (#141600)
parent
d92728e533
commit
4c0d8ce87c
|
@ -14,7 +14,7 @@ from homeassistant.config_entries import (
|
|||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import section
|
||||
from homeassistant.helpers.selector import (
|
||||
|
@ -30,8 +30,6 @@ from homeassistant.helpers.selector import (
|
|||
from homeassistant.helpers.service_info.ssdp import SsdpServiceInfo
|
||||
|
||||
from .const import (
|
||||
CONF_RECEIVER_MAX_VOLUME,
|
||||
CONF_SOURCES,
|
||||
DOMAIN,
|
||||
OPTION_INPUT_SOURCES,
|
||||
OPTION_LISTENING_MODES,
|
||||
|
@ -329,61 +327,6 @@ class OnkyoConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
"""Handle reconfiguration of the receiver."""
|
||||
return await self.async_step_manual()
|
||||
|
||||
async def async_step_import(self, user_input: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Import the yaml config."""
|
||||
_LOGGER.debug("Import flow user input: %s", user_input)
|
||||
|
||||
host: str = user_input[CONF_HOST]
|
||||
name: str | None = user_input.get(CONF_NAME)
|
||||
user_max_volume: int = user_input[OPTION_MAX_VOLUME]
|
||||
user_volume_resolution: int = user_input[CONF_RECEIVER_MAX_VOLUME]
|
||||
user_sources: dict[InputSource, str] = user_input[CONF_SOURCES]
|
||||
|
||||
info: ReceiverInfo | None = user_input.get("info")
|
||||
if info is None:
|
||||
try:
|
||||
info = await async_interview(host)
|
||||
except Exception:
|
||||
_LOGGER.exception("Import flow interview error for host %s", host)
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
||||
if info is None:
|
||||
_LOGGER.error("Import flow interview error for host %s", host)
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
||||
unique_id = info.identifier
|
||||
await self.async_set_unique_id(unique_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
name = name or info.model_name
|
||||
|
||||
volume_resolution = VOLUME_RESOLUTION_ALLOWED[-1]
|
||||
for volume_resolution_allowed in VOLUME_RESOLUTION_ALLOWED:
|
||||
if user_volume_resolution <= volume_resolution_allowed:
|
||||
volume_resolution = volume_resolution_allowed
|
||||
break
|
||||
|
||||
max_volume = min(
|
||||
100, user_max_volume * user_volume_resolution / volume_resolution
|
||||
)
|
||||
|
||||
sources_store: dict[str, str] = {}
|
||||
for source, source_name in user_sources.items():
|
||||
sources_store[source.value] = source_name
|
||||
|
||||
return self.async_create_entry(
|
||||
title=name,
|
||||
data={
|
||||
CONF_HOST: host,
|
||||
},
|
||||
options={
|
||||
OPTION_VOLUME_RESOLUTION: volume_resolution,
|
||||
OPTION_MAX_VOLUME: max_volume,
|
||||
OPTION_INPUT_SOURCES: sources_store,
|
||||
OPTION_LISTENING_MODES: LISTENING_MODES_DEFAULT,
|
||||
},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
|
||||
|
|
|
@ -11,9 +11,6 @@ DOMAIN = "onkyo"
|
|||
DEVICE_INTERVIEW_TIMEOUT = 5
|
||||
DEVICE_DISCOVERY_TIMEOUT = 5
|
||||
|
||||
CONF_SOURCES = "sources"
|
||||
CONF_RECEIVER_MAX_VOLUME = "receiver_max_volume"
|
||||
|
||||
type VolumeResolution = Literal[50, 80, 100, 200]
|
||||
OPTION_VOLUME_RESOLUTION = "volume_resolution"
|
||||
OPTION_VOLUME_RESOLUTION_DEFAULT: VolumeResolution = 50
|
||||
|
|
|
@ -8,32 +8,18 @@ from functools import cache
|
|||
import logging
|
||||
from typing import Any, Literal
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaPlayerState,
|
||||
MediaType,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import (
|
||||
AddConfigEntryEntitiesCallback,
|
||||
AddEntitiesCallback,
|
||||
)
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import OnkyoConfigEntry
|
||||
from .const import (
|
||||
CONF_RECEIVER_MAX_VOLUME,
|
||||
CONF_SOURCES,
|
||||
DOMAIN,
|
||||
OPTION_MAX_VOLUME,
|
||||
OPTION_VOLUME_RESOLUTION,
|
||||
|
@ -43,46 +29,11 @@ from .const import (
|
|||
ListeningMode,
|
||||
VolumeResolution,
|
||||
)
|
||||
from .receiver import Receiver, async_discover
|
||||
from .receiver import Receiver
|
||||
from .services import DATA_MP_ENTITIES
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_MAX_VOLUME_DEFAULT = 100
|
||||
CONF_RECEIVER_MAX_VOLUME_DEFAULT = 80
|
||||
CONF_SOURCES_DEFAULT = {
|
||||
"tv": "TV",
|
||||
"bd": "Bluray",
|
||||
"game": "Game",
|
||||
"aux1": "Aux1",
|
||||
"video1": "Video 1",
|
||||
"video2": "Video 2",
|
||||
"video3": "Video 3",
|
||||
"video4": "Video 4",
|
||||
"video5": "Video 5",
|
||||
"video6": "Video 6",
|
||||
"video7": "Video 7",
|
||||
"fm": "Radio",
|
||||
}
|
||||
|
||||
ISSUE_URL_PLACEHOLDER = "/config/integrations/dashboard/add?domain=onkyo"
|
||||
|
||||
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Optional(OPTION_MAX_VOLUME, default=CONF_MAX_VOLUME_DEFAULT): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1, max=100)
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_RECEIVER_MAX_VOLUME, default=CONF_RECEIVER_MAX_VOLUME_DEFAULT
|
||||
): cv.positive_int,
|
||||
vol.Optional(CONF_SOURCES, default=CONF_SOURCES_DEFAULT): {
|
||||
cv.string: cv.string
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
SUPPORTED_FEATURES_BASE = (
|
||||
MediaPlayerEntityFeature.TURN_ON
|
||||
|
@ -194,122 +145,6 @@ def _rev_listening_mode_lib_mappings(zone: str) -> dict[LibValue, ListeningMode]
|
|||
return {value: key for key, value in _listening_mode_lib_mappings(zone).items()}
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Import config from yaml."""
|
||||
host = config.get(CONF_HOST)
|
||||
|
||||
source_mapping: dict[str, InputSource] = {}
|
||||
for zone in ZONES:
|
||||
for source, source_lib in _input_source_lib_mappings(zone).items():
|
||||
if isinstance(source_lib, str):
|
||||
source_mapping.setdefault(source_lib, source)
|
||||
else:
|
||||
for source_lib_single in source_lib:
|
||||
source_mapping.setdefault(source_lib_single, source)
|
||||
|
||||
sources: dict[InputSource, str] = {}
|
||||
for source_lib_single, source_name in config[CONF_SOURCES].items():
|
||||
user_source = source_mapping.get(source_lib_single.lower())
|
||||
if user_source is not None:
|
||||
sources[user_source] = source_name
|
||||
|
||||
config[CONF_SOURCES] = sources
|
||||
|
||||
results = []
|
||||
if host is not None:
|
||||
_LOGGER.debug("Importing yaml single: %s", host)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=config
|
||||
)
|
||||
results.append((host, result))
|
||||
else:
|
||||
for info in await async_discover():
|
||||
host = info.host
|
||||
|
||||
# Migrate legacy entities.
|
||||
registry = er.async_get(hass)
|
||||
old_unique_id = f"{info.model_name}_{info.identifier}"
|
||||
new_unique_id = f"{info.identifier}_main"
|
||||
entity_id = registry.async_get_entity_id(
|
||||
"media_player", DOMAIN, old_unique_id
|
||||
)
|
||||
if entity_id is not None:
|
||||
_LOGGER.debug(
|
||||
"Migrating unique_id from [%s] to [%s] for entity %s",
|
||||
old_unique_id,
|
||||
new_unique_id,
|
||||
entity_id,
|
||||
)
|
||||
registry.async_update_entity(entity_id, new_unique_id=new_unique_id)
|
||||
|
||||
_LOGGER.debug("Importing yaml discover: %s", info.host)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=config | {CONF_HOST: info.host} | {"info": info},
|
||||
)
|
||||
results.append((host, result))
|
||||
|
||||
_LOGGER.debug("Importing yaml results: %s", results)
|
||||
if not results:
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml_import_issue_no_discover",
|
||||
breaks_in_ha_version="2025.5.0",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml_import_issue_no_discover",
|
||||
translation_placeholders={"url": ISSUE_URL_PLACEHOLDER},
|
||||
)
|
||||
|
||||
all_successful = True
|
||||
for host, result in results:
|
||||
if (
|
||||
result.get("type") == FlowResultType.CREATE_ENTRY
|
||||
or result.get("reason") == "already_configured"
|
||||
):
|
||||
continue
|
||||
if error := result.get("reason"):
|
||||
all_successful = False
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"deprecated_yaml_import_issue_{host}_{error}",
|
||||
breaks_in_ha_version="2025.5.0",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key=f"deprecated_yaml_import_issue_{error}",
|
||||
translation_placeholders={
|
||||
"host": host,
|
||||
"url": ISSUE_URL_PLACEHOLDER,
|
||||
},
|
||||
)
|
||||
|
||||
if all_successful:
|
||||
async_create_issue(
|
||||
hass,
|
||||
HOMEASSISTANT_DOMAIN,
|
||||
f"deprecated_yaml_{DOMAIN}",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
breaks_in_ha_version="2025.5.0",
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
translation_placeholders={
|
||||
"domain": DOMAIN,
|
||||
"integration_title": "onkyo",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: OnkyoConfigEntry,
|
||||
|
|
|
@ -83,16 +83,6 @@
|
|||
"empty_listening_mode_list": "Listening mode list cannot be empty"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecated_yaml_import_issue_no_discover": {
|
||||
"title": "The Onkyo YAML configuration import failed",
|
||||
"description": "Configuring Onkyo using YAML is being removed but no receivers were discovered when importing your YAML configuration.\n\nEnsure the connection to the receiver works and restart Home Assistant to try again or remove the Onkyo YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
|
||||
},
|
||||
"deprecated_yaml_import_issue_cannot_connect": {
|
||||
"title": "The Onkyo YAML configuration import failed",
|
||||
"description": "Configuring Onkyo using YAML is being removed but there was a connection error when importing your YAML configuration for host {host}.\n\nEnsure the connection to the receiver works and restart Home Assistant to try again or remove the Onkyo YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"invalid_sound_mode": {
|
||||
"message": "Cannot select sound mode \"{invalid_sound_mode}\" for entity: {entity_id}."
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
"""Test Onkyo config flow."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.onkyo import InputSource
|
||||
from homeassistant.components.onkyo.config_flow import OnkyoConfigFlow
|
||||
from homeassistant.components.onkyo.const import (
|
||||
DOMAIN,
|
||||
|
@ -536,89 +534,6 @@ async def test_reconfigure_new_device(hass: HomeAssistant) -> None:
|
|||
assert config_entry.unique_id == old_unique_id
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("user_input", "exception", "error"),
|
||||
[
|
||||
(
|
||||
# No host, and thus no host reachable
|
||||
{
|
||||
CONF_HOST: None,
|
||||
"receiver_max_volume": 100,
|
||||
"max_volume": 100,
|
||||
"sources": {},
|
||||
},
|
||||
None,
|
||||
"cannot_connect",
|
||||
),
|
||||
(
|
||||
# No host, and connection exception
|
||||
{
|
||||
CONF_HOST: None,
|
||||
"receiver_max_volume": 100,
|
||||
"max_volume": 100,
|
||||
"sources": {},
|
||||
},
|
||||
Exception(),
|
||||
"cannot_connect",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_import_fail(
|
||||
hass: HomeAssistant,
|
||||
user_input: dict[str, Any],
|
||||
exception: Exception,
|
||||
error: str,
|
||||
) -> None:
|
||||
"""Test import flow failed."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.receiver.pyeiscp.Connection.discover",
|
||||
side_effect=exception,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=user_input
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == error
|
||||
|
||||
|
||||
async def test_import_success(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test import flow succeeded."""
|
||||
info = create_receiver_info(1)
|
||||
|
||||
user_input = {
|
||||
CONF_HOST: info.host,
|
||||
"receiver_max_volume": 80,
|
||||
"max_volume": 110,
|
||||
"sources": {
|
||||
InputSource("00"): "Auxiliary",
|
||||
InputSource("01"): "Video",
|
||||
},
|
||||
"info": info,
|
||||
}
|
||||
|
||||
import_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=user_input
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert import_result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert import_result["data"] == {"host": "host 1"}
|
||||
assert import_result["options"] == {
|
||||
"volume_resolution": 80,
|
||||
"max_volume": 100,
|
||||
"input_sources": {
|
||||
"00": "Auxiliary",
|
||||
"01": "Video",
|
||||
},
|
||||
"listening_modes": {},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ignore_missing_translations",
|
||||
[
|
||||
|
|
Loading…
Reference in New Issue