mirror of
https://github.com/home-assistant/core.git
synced 2025-07-26 06:37:52 +00:00
Fix homekit options being mutated during config_flow/migration (#64003)
This commit is contained in:
parent
f034ea5b4b
commit
1019156899
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from copy import deepcopy
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -348,8 +349,8 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_import_options_from_data_if_missing(hass: HomeAssistant, entry: ConfigEntry):
|
def _async_import_options_from_data_if_missing(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
options = dict(entry.options)
|
options = deepcopy(dict(entry.options))
|
||||||
data = dict(entry.data)
|
data = deepcopy(dict(entry.data))
|
||||||
modified = False
|
modified = False
|
||||||
for importable_option in CONFIG_OPTIONS:
|
for importable_option in CONFIG_OPTIONS:
|
||||||
if importable_option not in entry.options and importable_option in entry.data:
|
if importable_option not in entry.options and importable_option in entry.data:
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from copy import deepcopy
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -117,7 +119,7 @@ DEFAULT_DOMAINS = [
|
|||||||
"water_heater",
|
"water_heater",
|
||||||
]
|
]
|
||||||
|
|
||||||
_EMPTY_ENTITY_FILTER = {
|
_EMPTY_ENTITY_FILTER: Final = {
|
||||||
CONF_INCLUDE_DOMAINS: [],
|
CONF_INCLUDE_DOMAINS: [],
|
||||||
CONF_EXCLUDE_DOMAINS: [],
|
CONF_EXCLUDE_DOMAINS: [],
|
||||||
CONF_INCLUDE_ENTITIES: [],
|
CONF_INCLUDE_ENTITIES: [],
|
||||||
@ -152,7 +154,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
"""Choose specific domains in bridge mode."""
|
"""Choose specific domains in bridge mode."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
entity_filter = _EMPTY_ENTITY_FILTER.copy()
|
entity_filter = deepcopy(_EMPTY_ENTITY_FILTER)
|
||||||
entity_filter[CONF_INCLUDE_DOMAINS] = user_input[CONF_INCLUDE_DOMAINS]
|
entity_filter[CONF_INCLUDE_DOMAINS] = user_input[CONF_INCLUDE_DOMAINS]
|
||||||
self.hk_data[CONF_FILTER] = entity_filter
|
self.hk_data[CONF_FILTER] = entity_filter
|
||||||
return await self.async_step_pairing()
|
return await self.async_step_pairing()
|
||||||
@ -493,7 +495,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self.hk_options.update(user_input)
|
self.hk_options.update(user_input)
|
||||||
return await self.async_step_include_exclude()
|
return await self.async_step_include_exclude()
|
||||||
|
|
||||||
self.hk_options = dict(self.config_entry.options)
|
self.hk_options = deepcopy(dict(self.config_entry.options))
|
||||||
entity_filter = self.hk_options.get(CONF_FILTER, {})
|
entity_filter = self.hk_options.get(CONF_FILTER, {})
|
||||||
homekit_mode = self.hk_options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE)
|
homekit_mode = self.hk_options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE)
|
||||||
domains = entity_filter.get(CONF_INCLUDE_DOMAINS, [])
|
domains = entity_filter.get(CONF_INCLUDE_DOMAINS, [])
|
||||||
|
@ -2,9 +2,14 @@
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant import config_entries, data_entry_flow
|
from homeassistant import config_entries, data_entry_flow
|
||||||
from homeassistant.components.homekit.const import DOMAIN, SHORT_BRIDGE_NAME
|
from homeassistant.components.homekit.const import (
|
||||||
|
CONF_FILTER,
|
||||||
|
DOMAIN,
|
||||||
|
SHORT_BRIDGE_NAME,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import SOURCE_IGNORE, SOURCE_IMPORT
|
from homeassistant.config_entries import SOURCE_IGNORE, SOURCE_IMPORT
|
||||||
from homeassistant.const import CONF_NAME, CONF_PORT
|
from homeassistant.const import CONF_NAME, CONF_PORT
|
||||||
|
from homeassistant.helpers.entityfilter import CONF_INCLUDE_DOMAINS
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from .util import PATH_HOMEKIT, async_init_entry
|
from .util import PATH_HOMEKIT, async_init_entry
|
||||||
@ -347,6 +352,10 @@ async def test_options_flow_exclude_mode_basic(hass, mock_get_source_ip):
|
|||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "include_exclude"
|
assert result["step_id"] == "include_exclude"
|
||||||
|
|
||||||
|
# Inject garbage to ensure the options data
|
||||||
|
# is being deep copied and we cannot mutate it in flight
|
||||||
|
config_entry.options[CONF_FILTER][CONF_INCLUDE_DOMAINS].append("garbage")
|
||||||
|
|
||||||
result2 = await hass.config_entries.options.async_configure(
|
result2 = await hass.config_entries.options.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={"entities": ["climate.old"], "include_exclude_mode": "exclude"},
|
user_input={"entities": ["climate.old"], "include_exclude_mode": "exclude"},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user