Ensure selected entity is pre-selected in homekit options flow (#63628)

* Ensure selected entity is pre-selected in homekit options flow

- We recently adjusted the flow to exclude entities that had
  been deleted from breaking the UI validation. We need to include
  single entities in the set of all supported entities since
  accessory mode has no domain filter

* tweak

* Additional fixes

* small tweak to speed up building the set

* merged fixed version for test branch
This commit is contained in:
J. Nick Koston 2022-01-07 15:46:39 -10:00 committed by GitHub
parent 0b67d7fb28
commit 9dd09f66e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 13 deletions

View file

@ -447,15 +447,25 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
return await self.async_step_advanced() return await self.async_step_advanced()
entity_filter = self.hk_options.get(CONF_FILTER, {}) entity_filter = self.hk_options.get(CONF_FILTER, {})
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, [])
all_supported_entities = _async_get_matching_entities( all_supported_entities = _async_get_matching_entities(
self.hass, self.hass,
domains=self.hk_options[CONF_DOMAINS], domains=self.hk_options[CONF_DOMAINS],
) )
data_schema = {} data_schema = {}
# Strip out entities that no longer exist to prevent error in the UI
valid_entities = [
entity_id for entity_id in entities if entity_id in all_supported_entities
]
if self.hk_options[CONF_HOMEKIT_MODE] == HOMEKIT_MODE_ACCESSORY:
# In accessory mode we can only have one
default_value = valid_entities[0] if valid_entities else None
entity_schema = vol.In entity_schema = vol.In
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, []) entities_schema_required = vol.Required
if self.hk_options[CONF_HOMEKIT_MODE] != HOMEKIT_MODE_ACCESSORY: else:
# Bridge mode
entities_schema_required = vol.Optional
include_exclude_mode = MODE_INCLUDE include_exclude_mode = MODE_INCLUDE
if not entities: if not entities:
include_exclude_mode = MODE_EXCLUDE include_exclude_mode = MODE_EXCLUDE
@ -464,13 +474,10 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
vol.Required(CONF_INCLUDE_EXCLUDE_MODE, default=include_exclude_mode) vol.Required(CONF_INCLUDE_EXCLUDE_MODE, default=include_exclude_mode)
] = vol.In(INCLUDE_EXCLUDE_MODES) ] = vol.In(INCLUDE_EXCLUDE_MODES)
entity_schema = cv.multi_select entity_schema = cv.multi_select
default_value = valid_entities
# Strip out entities that no longer exist to prevent error in the UI
valid_entities = [
entity_id for entity_id in entities if entity_id in all_supported_entities
]
data_schema[ data_schema[
vol.Optional(CONF_ENTITIES, default=valid_entities) entities_schema_required(CONF_ENTITIES, default=default_value)
] = entity_schema(all_supported_entities) ] = entity_schema(all_supported_entities)
return self.async_show_form( return self.async_show_form(

View file

@ -7,6 +7,8 @@ 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.setup import async_setup_component from homeassistant.setup import async_setup_component
from .util import PATH_HOMEKIT, async_init_entry
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -1065,11 +1067,13 @@ async def test_options_flow_blocked_when_from_yaml(hass, mock_get_source_ip):
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
async def test_options_flow_include_mode_basic_accessory(hass, mock_get_source_ip): @patch(f"{PATH_HOMEKIT}.async_port_is_available", return_value=True)
async def test_options_flow_include_mode_basic_accessory(
port_mock, hass, mock_get_source_ip, hk_driver, mock_async_zeroconf
):
"""Test config flow options in include mode with a single accessory.""" """Test config flow options in include mode with a single accessory."""
config_entry = _mock_config_entry_with_options_populated() config_entry = _mock_config_entry_with_options_populated()
config_entry.add_to_hass(hass) await async_init_entry(hass, config_entry)
hass.states.async_set("media_player.tv", "off") hass.states.async_set("media_player.tv", "off")
hass.states.async_set("media_player.sonos", "off") hass.states.async_set("media_player.sonos", "off")
@ -1101,7 +1105,48 @@ async def test_options_flow_include_mode_basic_accessory(hass, mock_get_source_i
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "include_exclude" assert result2["step_id"] == "include_exclude"
assert _get_schema_default(result2["data_schema"].schema, "entities") == [] assert _get_schema_default(result2["data_schema"].schema, "entities") is None
result3 = await hass.config_entries.options.async_configure(
result2["flow_id"],
user_input={"entities": "media_player.tv"},
)
assert result3["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert config_entry.options == {
"mode": "accessory",
"filter": {
"exclude_domains": [],
"exclude_entities": [],
"include_domains": [],
"include_entities": ["media_player.tv"],
},
}
# Now we check again to make sure the single entity is still
# preselected
result = await hass.config_entries.options.async_init(
config_entry.entry_id, context={"show_advanced_options": False}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "init"
assert result["data_schema"]({}) == {
"domains": ["media_player"],
"mode": "accessory",
}
result2 = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"domains": ["media_player"], "mode": "accessory"},
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "include_exclude"
assert (
_get_schema_default(result2["data_schema"].schema, "entities")
== "media_player.tv"
)
result3 = await hass.config_entries.options.async_configure( result3 = await hass.config_entries.options.async_configure(
result2["flow_id"], result2["flow_id"],