mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Update HEOS host from discovery (#138950)
This commit is contained in:
parent
1d43cb3f29
commit
b73c6ed768
@ -102,6 +102,18 @@ async def _validate_auth(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _get_current_hosts(entry: HeosConfigEntry) -> set[str]:
|
||||||
|
"""Get a set of current hosts from the entry."""
|
||||||
|
hosts = set(entry.data[CONF_HOST])
|
||||||
|
if hasattr(entry, "runtime_data"):
|
||||||
|
hosts.update(
|
||||||
|
player.ip_address
|
||||||
|
for player in entry.runtime_data.heos.players.values()
|
||||||
|
if player.ip_address is not None
|
||||||
|
)
|
||||||
|
return hosts
|
||||||
|
|
||||||
|
|
||||||
class HeosFlowHandler(ConfigFlow, domain=DOMAIN):
|
class HeosFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
"""Define a flow for HEOS."""
|
"""Define a flow for HEOS."""
|
||||||
|
|
||||||
@ -125,10 +137,15 @@ class HeosFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert discovery_info.ssdp_location
|
assert discovery_info.ssdp_location
|
||||||
|
|
||||||
await self.async_set_unique_id(DOMAIN)
|
entry: HeosConfigEntry | None = await self.async_set_unique_id(DOMAIN)
|
||||||
# Connect to discovered host and get system information
|
|
||||||
hostname = urlparse(discovery_info.ssdp_location).hostname
|
hostname = urlparse(discovery_info.ssdp_location).hostname
|
||||||
assert hostname is not None
|
assert hostname is not None
|
||||||
|
|
||||||
|
# Abort early when discovered host is part of the current system
|
||||||
|
if entry and hostname in _get_current_hosts(entry):
|
||||||
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
|
# Connect to discovered host and get system information
|
||||||
heos = Heos(HeosOptions(hostname, events=False, heart_beat=False))
|
heos = Heos(HeosOptions(hostname, events=False, heart_beat=False))
|
||||||
try:
|
try:
|
||||||
await heos.connect()
|
await heos.connect()
|
||||||
@ -146,8 +163,23 @@ class HeosFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
# Select the preferred host, if available
|
# Select the preferred host, if available
|
||||||
if system_info.preferred_hosts:
|
if system_info.preferred_hosts:
|
||||||
hostname = system_info.preferred_hosts[0].ip_address
|
hostname = system_info.preferred_hosts[0].ip_address
|
||||||
self._discovered_host = hostname
|
|
||||||
return await self.async_step_confirm_discovery()
|
# Move to confirmation when not configured
|
||||||
|
if entry is None:
|
||||||
|
self._discovered_host = hostname
|
||||||
|
return await self.async_step_confirm_discovery()
|
||||||
|
|
||||||
|
# Only update if the configured host isn't part of the discovered hosts to ensure new players that come online don't trigger a reload
|
||||||
|
if entry.data[CONF_HOST] not in [host.ip_address for host in system_info.hosts]:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Updated host %s to discovered host %s", entry.data[CONF_HOST], hostname
|
||||||
|
)
|
||||||
|
return self.async_update_reload_and_abort(
|
||||||
|
entry,
|
||||||
|
data_updates={CONF_HOST: hostname},
|
||||||
|
reason="reconfigure_successful",
|
||||||
|
)
|
||||||
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
async def async_step_confirm_discovery(
|
async def async_step_confirm_discovery(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
@ -167,6 +199,7 @@ class HeosFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Obtain host and validate connection."""
|
"""Obtain host and validate connection."""
|
||||||
await self.async_set_unique_id(DOMAIN)
|
await self.async_set_unique_id(DOMAIN)
|
||||||
|
self._abort_if_unique_id_configured(error="single_instance_allowed")
|
||||||
# Try connecting to host if provided
|
# Try connecting to host if provided
|
||||||
errors: dict[str, str] = {}
|
errors: dict[str, str] = {}
|
||||||
host = None
|
host = None
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
"loggers": ["pyheos"],
|
"loggers": ["pyheos"],
|
||||||
"quality_scale": "silver",
|
"quality_scale": "silver",
|
||||||
"requirements": ["pyheos==1.0.2"],
|
"requirements": ["pyheos==1.0.2"],
|
||||||
"single_config_entry": true,
|
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"st": "urn:schemas-denon-com:device:ACT-Denon:1"
|
"st": "urn:schemas-denon-com:device:ACT-Denon:1"
|
||||||
|
@ -38,9 +38,7 @@ rules:
|
|||||||
# Gold
|
# Gold
|
||||||
devices: done
|
devices: done
|
||||||
diagnostics: done
|
diagnostics: done
|
||||||
discovery-update-info:
|
discovery-update-info: done
|
||||||
status: todo
|
|
||||||
comment: Explore if this is possible.
|
|
||||||
discovery: done
|
discovery: done
|
||||||
docs-data-update: done
|
docs-data-update: done
|
||||||
docs-examples: done
|
docs-examples: done
|
||||||
|
@ -7,7 +7,9 @@ from pyheos import (
|
|||||||
CommandFailedError,
|
CommandFailedError,
|
||||||
ConnectionState,
|
ConnectionState,
|
||||||
HeosError,
|
HeosError,
|
||||||
|
HeosHost,
|
||||||
HeosSystem,
|
HeosSystem,
|
||||||
|
NetworkType,
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -118,17 +120,44 @@ async def test_discovery(
|
|||||||
|
|
||||||
|
|
||||||
async def test_discovery_flow_aborts_already_setup(
|
async def test_discovery_flow_aborts_already_setup(
|
||||||
hass: HomeAssistant, discovery_data: SsdpServiceInfo, config_entry: MockConfigEntry
|
hass: HomeAssistant,
|
||||||
|
discovery_data_bedroom: SsdpServiceInfo,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
controller: MockHeos,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test discovery flow aborts when entry already setup."""
|
"""Test discovery flow aborts when entry already setup and hosts didn't change."""
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
assert config_entry.data[CONF_HOST] == "127.0.0.1"
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data
|
DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data_bedroom
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result["reason"] == "single_instance_allowed"
|
assert result["reason"] == "single_instance_allowed"
|
||||||
|
assert controller.get_system_info.call_count == 0
|
||||||
|
assert config_entry.data[CONF_HOST] == "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_discovery_aborts_same_system(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
discovery_data_bedroom: SsdpServiceInfo,
|
||||||
|
controller: MockHeos,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
system: HeosSystem,
|
||||||
|
) -> None:
|
||||||
|
"""Test discovery does not update when current host is part of discovered's system."""
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert config_entry.data[CONF_HOST] == "127.0.0.1"
|
||||||
|
|
||||||
|
controller.get_system_info.return_value = system
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data_bedroom
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "single_instance_allowed"
|
||||||
|
assert controller.get_system_info.call_count == 1
|
||||||
|
assert config_entry.data[CONF_HOST] == "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
async def test_discovery_fails_to_connect_aborts(
|
async def test_discovery_fails_to_connect_aborts(
|
||||||
@ -145,6 +174,26 @@ async def test_discovery_fails_to_connect_aborts(
|
|||||||
assert controller.disconnect.call_count == 1
|
assert controller.disconnect.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_discovery_updates(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
discovery_data_bedroom: SsdpServiceInfo,
|
||||||
|
controller: MockHeos,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test discovery updates existing entry."""
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert config_entry.data[CONF_HOST] == "127.0.0.1"
|
||||||
|
|
||||||
|
host = HeosHost("Player", "Model", None, None, "127.0.0.2", NetworkType.WIRED, True)
|
||||||
|
controller.get_system_info.return_value = HeosSystem(None, host, [host])
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data_bedroom
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "reconfigure_successful"
|
||||||
|
assert config_entry.data[CONF_HOST] == "127.0.0.2"
|
||||||
|
|
||||||
|
|
||||||
async def test_reconfigure_validates_and_updates_config(
|
async def test_reconfigure_validates_and_updates_config(
|
||||||
hass: HomeAssistant, config_entry: MockConfigEntry, controller: MockHeos
|
hass: HomeAssistant, config_entry: MockConfigEntry, controller: MockHeos
|
||||||
) -> None:
|
) -> None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user