Remove deprecated YAML import in Onkyo (#141600)

pull/141606/head
Artur Pragacz 2025-03-27 21:13:23 +01:00 committed by GitHub
parent d92728e533
commit 4c0d8ce87c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 4 additions and 324 deletions

View File

@ -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:

View File

@ -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

View File

@ -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,

View File

@ -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}."

View File

@ -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",
[