mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
Allow multiple Airzone entries with different System IDs (#135397)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
3bbd7daa7f
commit
4e494aa393
@ -86,7 +86,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
|
|||||||
options = ConnectionOptions(
|
options = ConnectionOptions(
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data[CONF_PORT],
|
entry.data[CONF_PORT],
|
||||||
entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID),
|
entry.data[CONF_ID],
|
||||||
)
|
)
|
||||||
|
|
||||||
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
|
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
|
||||||
@ -120,3 +120,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
|
|||||||
async def async_unload_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
||||||
|
"""Migrate an old entry."""
|
||||||
|
if entry.version == 1 and entry.minor_version < 2:
|
||||||
|
# Add missing CONF_ID
|
||||||
|
system_id = entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID)
|
||||||
|
new_data = entry.data.copy()
|
||||||
|
new_data[CONF_ID] = system_id
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry,
|
||||||
|
data=new_data,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER.info(
|
||||||
|
"Migration to configuration version %s.%s successful",
|
||||||
|
entry.version,
|
||||||
|
entry.minor_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
@ -44,6 +44,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
_discovered_ip: str | None = None
|
_discovered_ip: str | None = None
|
||||||
_discovered_mac: str | None = None
|
_discovered_mac: str | None = None
|
||||||
|
MINOR_VERSION = 2
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
@ -53,6 +54,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
if CONF_ID not in user_input:
|
||||||
|
user_input[CONF_ID] = DEFAULT_SYSTEM_ID
|
||||||
|
|
||||||
self._async_abort_entries_match(user_input)
|
self._async_abort_entries_match(user_input)
|
||||||
|
|
||||||
airzone = AirzoneLocalApi(
|
airzone = AirzoneLocalApi(
|
||||||
@ -60,7 +64,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
ConnectionOptions(
|
ConnectionOptions(
|
||||||
user_input[CONF_HOST],
|
user_input[CONF_HOST],
|
||||||
user_input[CONF_PORT],
|
user_input[CONF_PORT],
|
||||||
user_input.get(CONF_ID, DEFAULT_SYSTEM_ID),
|
user_input[CONF_ID],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -84,6 +88,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
|
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
|
||||||
|
if user_input[CONF_ID] != DEFAULT_SYSTEM_ID:
|
||||||
|
title += f" #{user_input[CONF_ID]}"
|
||||||
|
|
||||||
return self.async_create_entry(title=title, data=user_input)
|
return self.async_create_entry(title=title, data=user_input)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
|
@ -275,6 +275,7 @@
|
|||||||
'config_entry': dict({
|
'config_entry': dict({
|
||||||
'data': dict({
|
'data': dict({
|
||||||
'host': '192.168.1.100',
|
'host': '192.168.1.100',
|
||||||
|
'id': 0,
|
||||||
'port': 3000,
|
'port': 3000,
|
||||||
}),
|
}),
|
||||||
'disabled_by': None,
|
'disabled_by': None,
|
||||||
@ -282,7 +283,7 @@
|
|||||||
}),
|
}),
|
||||||
'domain': 'airzone',
|
'domain': 'airzone',
|
||||||
'entry_id': '6e7a0798c1734ba81d26ced0e690eaec',
|
'entry_id': '6e7a0798c1734ba81d26ced0e690eaec',
|
||||||
'minor_version': 1,
|
'minor_version': 2,
|
||||||
'options': dict({
|
'options': dict({
|
||||||
}),
|
}),
|
||||||
'pref_disable_new_entities': False,
|
'pref_disable_new_entities': False,
|
||||||
|
@ -28,6 +28,7 @@ from .util import (
|
|||||||
HVAC_MOCK,
|
HVAC_MOCK,
|
||||||
HVAC_VERSION_MOCK,
|
HVAC_VERSION_MOCK,
|
||||||
HVAC_WEBSERVER_MOCK,
|
HVAC_WEBSERVER_MOCK,
|
||||||
|
USER_INPUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
@ -81,7 +82,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], CONFIG
|
result["flow_id"], USER_INPUT
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -94,7 +95,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||||||
assert result["title"] == f"Airzone {CONFIG[CONF_HOST]}:{CONFIG[CONF_PORT]}"
|
assert result["title"] == f"Airzone {CONFIG[CONF_HOST]}:{CONFIG[CONF_PORT]}"
|
||||||
assert result["data"][CONF_HOST] == CONFIG[CONF_HOST]
|
assert result["data"][CONF_HOST] == CONFIG[CONF_HOST]
|
||||||
assert result["data"][CONF_PORT] == CONFIG[CONF_PORT]
|
assert result["data"][CONF_PORT] == CONFIG[CONF_PORT]
|
||||||
assert CONF_ID not in result["data"]
|
assert result["data"][CONF_ID] == CONFIG[CONF_ID]
|
||||||
|
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ async def test_form_invalid_system_id(hass: HomeAssistant) -> None:
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
@ -154,7 +155,7 @@ async def test_form_invalid_system_id(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert (
|
assert (
|
||||||
result["title"]
|
result["title"]
|
||||||
== f"Airzone {CONFIG_ID1[CONF_HOST]}:{CONFIG_ID1[CONF_PORT]}"
|
== f"Airzone {CONFIG_ID1[CONF_HOST]}:{CONFIG_ID1[CONF_PORT]} #{CONFIG_ID1[CONF_ID]}"
|
||||||
)
|
)
|
||||||
assert result["data"][CONF_HOST] == CONFIG_ID1[CONF_HOST]
|
assert result["data"][CONF_HOST] == CONFIG_ID1[CONF_HOST]
|
||||||
assert result["data"][CONF_PORT] == CONFIG_ID1[CONF_PORT]
|
assert result["data"][CONF_PORT] == CONFIG_ID1[CONF_PORT]
|
||||||
@ -167,6 +168,7 @@ async def test_form_duplicated_id(hass: HomeAssistant) -> None:
|
|||||||
"""Test setting up duplicated entry."""
|
"""Test setting up duplicated entry."""
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
minor_version=2,
|
||||||
data=CONFIG,
|
data=CONFIG,
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="airzone_unique_id",
|
unique_id="airzone_unique_id",
|
||||||
@ -174,7 +176,7 @@ async def test_form_duplicated_id(hass: HomeAssistant) -> None:
|
|||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
@ -189,7 +191,7 @@ async def test_connection_error(hass: HomeAssistant) -> None:
|
|||||||
side_effect=AirzoneError,
|
side_effect=AirzoneError,
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["errors"] == {"base": "cannot_connect"}
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
@ -25,6 +25,7 @@ async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None:
|
|||||||
"""Test ClientConnectorError on coordinator update."""
|
"""Test ClientConnectorError on coordinator update."""
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
minor_version=2,
|
||||||
data=CONFIG,
|
data=CONFIG,
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="airzone_unique_id",
|
unique_id="airzone_unique_id",
|
||||||
@ -74,6 +75,7 @@ async def test_coordinator_new_devices(
|
|||||||
"""Test new devices on coordinator update."""
|
"""Test new devices on coordinator update."""
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
minor_version=2,
|
||||||
data=CONFIG,
|
data=CONFIG,
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="airzone_unique_id",
|
unique_id="airzone_unique_id",
|
||||||
|
@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from aioairzone.const import DEFAULT_SYSTEM_ID
|
||||||
from aioairzone.exceptions import HotWaterNotAvailable, InvalidMethod, SystemOutOfRange
|
from aioairzone.exceptions import HotWaterNotAvailable, InvalidMethod, SystemOutOfRange
|
||||||
|
|
||||||
from homeassistant.components.airzone.const import DOMAIN
|
from homeassistant.components.airzone.const import DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
from homeassistant.const import CONF_ID
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from .util import CONFIG, HVAC_MOCK, HVAC_VERSION_MOCK, HVAC_WEBSERVER_MOCK
|
from .util import CONFIG, HVAC_MOCK, HVAC_VERSION_MOCK, HVAC_WEBSERVER_MOCK, USER_INPUT
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
@ -19,7 +21,11 @@ async def test_unique_id_migrate(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test unique id migration."""
|
"""Test unique id migration."""
|
||||||
|
|
||||||
config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG)
|
config_entry = MockConfigEntry(
|
||||||
|
minor_version=2,
|
||||||
|
domain=DOMAIN,
|
||||||
|
data=CONFIG,
|
||||||
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
@ -89,6 +95,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None:
|
|||||||
"""Test unload."""
|
"""Test unload."""
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
minor_version=2,
|
||||||
data=CONFIG,
|
data=CONFIG,
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="airzone_unique_id",
|
unique_id="airzone_unique_id",
|
||||||
@ -112,3 +119,42 @@ async def test_unload_entry(hass: HomeAssistant) -> None:
|
|||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migrate_entry_v2(hass: HomeAssistant) -> None:
|
||||||
|
"""Test entry migration to v2."""
|
||||||
|
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
minor_version=1,
|
||||||
|
data=USER_INPUT,
|
||||||
|
domain=DOMAIN,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_dhw",
|
||||||
|
side_effect=HotWaterNotAvailable,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac",
|
||||||
|
return_value=HVAC_MOCK,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems",
|
||||||
|
side_effect=SystemOutOfRange,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_version",
|
||||||
|
return_value=HVAC_VERSION_MOCK,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_webserver",
|
||||||
|
side_effect=InvalidMethod,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert config_entry.minor_version == 2
|
||||||
|
assert config_entry.data.get(CONF_ID) == DEFAULT_SYSTEM_ID
|
||||||
|
@ -55,6 +55,7 @@ from aioairzone.const import (
|
|||||||
API_WS_AZ,
|
API_WS_AZ,
|
||||||
API_WS_TYPE,
|
API_WS_TYPE,
|
||||||
API_ZONE_ID,
|
API_ZONE_ID,
|
||||||
|
DEFAULT_SYSTEM_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
from homeassistant.components.airzone.const import DOMAIN
|
from homeassistant.components.airzone.const import DOMAIN
|
||||||
@ -63,13 +64,18 @@ from homeassistant.core import HomeAssistant
|
|||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
CONFIG = {
|
USER_INPUT = {
|
||||||
CONF_HOST: "192.168.1.100",
|
CONF_HOST: "192.168.1.100",
|
||||||
CONF_PORT: 3000,
|
CONF_PORT: 3000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONFIG = {
|
||||||
|
**USER_INPUT,
|
||||||
|
CONF_ID: DEFAULT_SYSTEM_ID,
|
||||||
|
}
|
||||||
|
|
||||||
CONFIG_ID1 = {
|
CONFIG_ID1 = {
|
||||||
**CONFIG,
|
**USER_INPUT,
|
||||||
CONF_ID: 1,
|
CONF_ID: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,6 +365,7 @@ async def async_init_integration(
|
|||||||
"""Set up the Airzone integration in Home Assistant."""
|
"""Set up the Airzone integration in Home Assistant."""
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
minor_version=2,
|
||||||
data=CONFIG,
|
data=CONFIG,
|
||||||
entry_id="6e7a0798c1734ba81d26ced0e690eaec",
|
entry_id="6e7a0798c1734ba81d26ced0e690eaec",
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user