mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Reduce complexity in the homekit config flow filters (#109850)
* Add typing to entity filters * Add typing to entity filters * Add typing to entity filters * Add typing to entity filters * tweaks * tweaks * tweaks * tweaks * tweaks
This commit is contained in:
parent
8e4714c563
commit
206aaac700
@ -7,7 +7,7 @@ from operator import itemgetter
|
|||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
from typing import Any
|
from typing import Any, Final, TypedDict
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -34,12 +34,6 @@ from homeassistant.helpers import (
|
|||||||
device_registry as dr,
|
device_registry as dr,
|
||||||
entity_registry as er,
|
entity_registry as er,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entityfilter import (
|
|
||||||
CONF_EXCLUDE_DOMAINS,
|
|
||||||
CONF_EXCLUDE_ENTITIES,
|
|
||||||
CONF_INCLUDE_DOMAINS,
|
|
||||||
CONF_INCLUDE_ENTITIES,
|
|
||||||
)
|
|
||||||
from homeassistant.loader import async_get_integrations
|
from homeassistant.loader import async_get_integrations
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -69,13 +63,13 @@ MODE_EXCLUDE = "exclude"
|
|||||||
|
|
||||||
INCLUDE_EXCLUDE_MODES = [MODE_EXCLUDE, MODE_INCLUDE]
|
INCLUDE_EXCLUDE_MODES = [MODE_EXCLUDE, MODE_INCLUDE]
|
||||||
|
|
||||||
DOMAINS_NEED_ACCESSORY_MODE = [
|
DOMAINS_NEED_ACCESSORY_MODE = {
|
||||||
CAMERA_DOMAIN,
|
CAMERA_DOMAIN,
|
||||||
LOCK_DOMAIN,
|
LOCK_DOMAIN,
|
||||||
MEDIA_PLAYER_DOMAIN,
|
MEDIA_PLAYER_DOMAIN,
|
||||||
REMOTE_DOMAIN,
|
REMOTE_DOMAIN,
|
||||||
]
|
}
|
||||||
NEVER_BRIDGED_DOMAINS = [CAMERA_DOMAIN]
|
NEVER_BRIDGED_DOMAINS = {CAMERA_DOMAIN}
|
||||||
|
|
||||||
CAMERA_ENTITY_PREFIX = f"{CAMERA_DOMAIN}."
|
CAMERA_ENTITY_PREFIX = f"{CAMERA_DOMAIN}."
|
||||||
|
|
||||||
@ -124,12 +118,34 @@ DEFAULT_DOMAINS = [
|
|||||||
"water_heater",
|
"water_heater",
|
||||||
]
|
]
|
||||||
|
|
||||||
_EMPTY_ENTITY_FILTER: dict[str, list[str]] = {
|
CONF_INCLUDE_DOMAINS: Final = "include_domains"
|
||||||
CONF_INCLUDE_DOMAINS: [],
|
CONF_INCLUDE_ENTITIES: Final = "include_entities"
|
||||||
CONF_EXCLUDE_DOMAINS: [],
|
CONF_EXCLUDE_DOMAINS: Final = "exclude_domains"
|
||||||
CONF_INCLUDE_ENTITIES: [],
|
CONF_EXCLUDE_ENTITIES: Final = "exclude_entities"
|
||||||
CONF_EXCLUDE_ENTITIES: [],
|
|
||||||
}
|
|
||||||
|
class EntityFilterDict(TypedDict, total=False):
|
||||||
|
"""Entity filter dict."""
|
||||||
|
|
||||||
|
include_domains: list[str]
|
||||||
|
include_entities: list[str]
|
||||||
|
exclude_domains: list[str]
|
||||||
|
exclude_entities: list[str]
|
||||||
|
|
||||||
|
|
||||||
|
def _make_entity_filter(
|
||||||
|
include_domains: list[str] | None = None,
|
||||||
|
include_entities: list[str] | None = None,
|
||||||
|
exclude_domains: list[str] | None = None,
|
||||||
|
exclude_entities: list[str] | None = None,
|
||||||
|
) -> EntityFilterDict:
|
||||||
|
"""Create a filter dict."""
|
||||||
|
return EntityFilterDict(
|
||||||
|
include_domains=include_domains or [],
|
||||||
|
include_entities=include_entities or [],
|
||||||
|
exclude_domains=exclude_domains or [],
|
||||||
|
exclude_entities=exclude_entities or [],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _async_domain_names(hass: HomeAssistant, domains: list[str]) -> str:
|
async def _async_domain_names(hass: HomeAssistant, domains: list[str]) -> str:
|
||||||
@ -141,19 +157,18 @@ async def _async_domain_names(hass: HomeAssistant, domains: list[str]) -> str:
|
|||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_build_entites_filter(
|
def _async_build_entities_filter(
|
||||||
domains: list[str], entities: list[str]
|
domains: list[str], entities: list[str]
|
||||||
) -> dict[str, Any]:
|
) -> EntityFilterDict:
|
||||||
"""Build an entities filter from domains and entities."""
|
"""Build an entities filter from domains and entities."""
|
||||||
entity_filter = deepcopy(_EMPTY_ENTITY_FILTER)
|
|
||||||
entity_filter[CONF_INCLUDE_ENTITIES] = entities
|
|
||||||
# Include all of the domain if there are no entities
|
# Include all of the domain if there are no entities
|
||||||
# explicitly included as the user selected the domain
|
# explicitly included as the user selected the domain
|
||||||
domains_with_entities_selected = _domains_set_from_entities(entities)
|
return _make_entity_filter(
|
||||||
entity_filter[CONF_INCLUDE_DOMAINS] = [
|
include_domains=sorted(
|
||||||
domain for domain in domains if domain not in domains_with_entities_selected
|
set(domains).difference(_domains_set_from_entities(entities))
|
||||||
]
|
),
|
||||||
return entity_filter
|
include_entities=entities,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _async_cameras_from_entities(entities: list[str]) -> dict[str, str]:
|
def _async_cameras_from_entities(entities: list[str]) -> dict[str, str]:
|
||||||
@ -190,13 +205,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""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 = deepcopy(_EMPTY_ENTITY_FILTER)
|
self.hk_data[CONF_FILTER] = _make_entity_filter(
|
||||||
entity_filter[CONF_INCLUDE_DOMAINS] = user_input[CONF_INCLUDE_DOMAINS]
|
include_domains=user_input[CONF_INCLUDE_DOMAINS]
|
||||||
self.hk_data[CONF_FILTER] = entity_filter
|
)
|
||||||
return await self.async_step_pairing()
|
return await self.async_step_pairing()
|
||||||
|
|
||||||
self.hk_data[CONF_HOMEKIT_MODE] = HOMEKIT_MODE_BRIDGE
|
self.hk_data[CONF_HOMEKIT_MODE] = HOMEKIT_MODE_BRIDGE
|
||||||
default_domains = [] if self._async_current_names() else DEFAULT_DOMAINS
|
default_domains = (
|
||||||
|
[] if self._async_current_entries(include_ignore=False) else DEFAULT_DOMAINS
|
||||||
|
)
|
||||||
name_to_type_map = await _async_name_to_type_map(self.hass)
|
name_to_type_map = await _async_name_to_type_map(self.hass)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
@ -213,24 +230,28 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Pairing instructions."""
|
"""Pairing instructions."""
|
||||||
|
hk_data = self.hk_data
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
port = async_find_next_available_port(self.hass, DEFAULT_CONFIG_FLOW_PORT)
|
port = async_find_next_available_port(self.hass, DEFAULT_CONFIG_FLOW_PORT)
|
||||||
await self._async_add_entries_for_accessory_mode_entities(port)
|
await self._async_add_entries_for_accessory_mode_entities(port)
|
||||||
self.hk_data[CONF_PORT] = port
|
hk_data[CONF_PORT] = port
|
||||||
include_domains_filter = self.hk_data[CONF_FILTER][CONF_INCLUDE_DOMAINS]
|
conf_filter: EntityFilterDict = hk_data[CONF_FILTER]
|
||||||
for domain in NEVER_BRIDGED_DOMAINS:
|
conf_filter[CONF_INCLUDE_DOMAINS] = [
|
||||||
if domain in include_domains_filter:
|
domain
|
||||||
include_domains_filter.remove(domain)
|
for domain in conf_filter[CONF_INCLUDE_DOMAINS]
|
||||||
|
if domain not in NEVER_BRIDGED_DOMAINS
|
||||||
|
]
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=f"{self.hk_data[CONF_NAME]}:{self.hk_data[CONF_PORT]}",
|
title=f"{hk_data[CONF_NAME]}:{hk_data[CONF_PORT]}",
|
||||||
data=self.hk_data,
|
data=hk_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.hk_data[CONF_NAME] = self._async_available_name(SHORT_BRIDGE_NAME)
|
hk_data[CONF_NAME] = self._async_available_name(SHORT_BRIDGE_NAME)
|
||||||
self.hk_data[CONF_EXCLUDE_ACCESSORY_MODE] = True
|
hk_data[CONF_EXCLUDE_ACCESSORY_MODE] = True
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="pairing",
|
step_id="pairing",
|
||||||
description_placeholders={CONF_NAME: self.hk_data[CONF_NAME]},
|
description_placeholders={CONF_NAME: hk_data[CONF_NAME]},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_add_entries_for_accessory_mode_entities(
|
async def _async_add_entries_for_accessory_mode_entities(
|
||||||
@ -265,14 +286,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
state = self.hass.states.get(entity_id)
|
state = self.hass.states.get(entity_id)
|
||||||
assert state is not None
|
assert state is not None
|
||||||
name = state.attributes.get(ATTR_FRIENDLY_NAME) or state.entity_id
|
name = state.attributes.get(ATTR_FRIENDLY_NAME) or state.entity_id
|
||||||
entity_filter = _EMPTY_ENTITY_FILTER.copy()
|
|
||||||
entity_filter[CONF_INCLUDE_ENTITIES] = [entity_id]
|
|
||||||
|
|
||||||
entry_data = {
|
entry_data = {
|
||||||
CONF_PORT: port,
|
CONF_PORT: port,
|
||||||
CONF_NAME: self._async_available_name(name),
|
CONF_NAME: self._async_available_name(name),
|
||||||
CONF_HOMEKIT_MODE: HOMEKIT_MODE_ACCESSORY,
|
CONF_HOMEKIT_MODE: HOMEKIT_MODE_ACCESSORY,
|
||||||
CONF_FILTER: entity_filter,
|
CONF_FILTER: _make_entity_filter(include_entities=[entity_id]),
|
||||||
}
|
}
|
||||||
if entity_id.startswith(CAMERA_ENTITY_PREFIX):
|
if entity_id.startswith(CAMERA_ENTITY_PREFIX):
|
||||||
entry_data[CONF_ENTITY_CONFIG] = {
|
entry_data[CONF_ENTITY_CONFIG] = {
|
||||||
@ -360,26 +379,19 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Choose advanced options."""
|
"""Choose advanced options."""
|
||||||
if (
|
hk_options = self.hk_options
|
||||||
not self.show_advanced_options
|
show_advanced_options = self.show_advanced_options
|
||||||
or user_input is not None
|
bridge_mode = hk_options[CONF_HOMEKIT_MODE] == HOMEKIT_MODE_BRIDGE
|
||||||
or self.hk_options[CONF_HOMEKIT_MODE] != HOMEKIT_MODE_BRIDGE
|
|
||||||
):
|
if not show_advanced_options or user_input is not None or not bridge_mode:
|
||||||
if user_input:
|
if user_input:
|
||||||
self.hk_options.update(user_input)
|
hk_options.update(user_input)
|
||||||
if (
|
if show_advanced_options and bridge_mode:
|
||||||
self.show_advanced_options
|
hk_options[CONF_DEVICES] = user_input[CONF_DEVICES]
|
||||||
and self.hk_options[CONF_HOMEKIT_MODE] == HOMEKIT_MODE_BRIDGE
|
|
||||||
):
|
|
||||||
self.hk_options[CONF_DEVICES] = user_input[CONF_DEVICES]
|
|
||||||
|
|
||||||
for key in (CONF_DOMAINS, CONF_ENTITIES):
|
|
||||||
if key in self.hk_options:
|
|
||||||
del self.hk_options[key]
|
|
||||||
|
|
||||||
if CONF_INCLUDE_EXCLUDE_MODE in self.hk_options:
|
|
||||||
del self.hk_options[CONF_INCLUDE_EXCLUDE_MODE]
|
|
||||||
|
|
||||||
|
hk_options.pop(CONF_DOMAINS, None)
|
||||||
|
hk_options.pop(CONF_ENTITIES, None)
|
||||||
|
hk_options.pop(CONF_INCLUDE_EXCLUDE_MODE, None)
|
||||||
return self.async_create_entry(title="", data=self.hk_options)
|
return self.async_create_entry(title="", data=self.hk_options)
|
||||||
|
|
||||||
all_supported_devices = await _async_get_supported_devices(self.hass)
|
all_supported_devices = await _async_get_supported_devices(self.hass)
|
||||||
@ -404,35 +416,37 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Choose camera config."""
|
"""Choose camera config."""
|
||||||
|
hk_options = self.hk_options
|
||||||
|
all_entity_config: dict[str, dict[str, Any]]
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
entity_config = self.hk_options[CONF_ENTITY_CONFIG]
|
all_entity_config = hk_options[CONF_ENTITY_CONFIG]
|
||||||
for entity_id in self.included_cameras:
|
for entity_id in self.included_cameras:
|
||||||
|
entity_config = all_entity_config.setdefault(entity_id, {})
|
||||||
|
|
||||||
if entity_id in user_input[CONF_CAMERA_COPY]:
|
if entity_id in user_input[CONF_CAMERA_COPY]:
|
||||||
entity_config.setdefault(entity_id, {})[
|
entity_config[CONF_VIDEO_CODEC] = VIDEO_CODEC_COPY
|
||||||
CONF_VIDEO_CODEC
|
elif CONF_VIDEO_CODEC in entity_config:
|
||||||
] = VIDEO_CODEC_COPY
|
del entity_config[CONF_VIDEO_CODEC]
|
||||||
elif (
|
|
||||||
entity_id in entity_config
|
|
||||||
and CONF_VIDEO_CODEC in entity_config[entity_id]
|
|
||||||
):
|
|
||||||
del entity_config[entity_id][CONF_VIDEO_CODEC]
|
|
||||||
if entity_id in user_input[CONF_CAMERA_AUDIO]:
|
if entity_id in user_input[CONF_CAMERA_AUDIO]:
|
||||||
entity_config.setdefault(entity_id, {})[CONF_SUPPORT_AUDIO] = True
|
entity_config[CONF_SUPPORT_AUDIO] = True
|
||||||
elif (
|
elif CONF_SUPPORT_AUDIO in entity_config:
|
||||||
entity_id in entity_config
|
del entity_config[CONF_SUPPORT_AUDIO]
|
||||||
and CONF_SUPPORT_AUDIO in entity_config[entity_id]
|
|
||||||
):
|
if not entity_config:
|
||||||
del entity_config[entity_id][CONF_SUPPORT_AUDIO]
|
all_entity_config.pop(entity_id)
|
||||||
|
|
||||||
return await self.async_step_advanced()
|
return await self.async_step_advanced()
|
||||||
|
|
||||||
cameras_with_audio = []
|
cameras_with_audio = []
|
||||||
cameras_with_copy = []
|
cameras_with_copy = []
|
||||||
entity_config = self.hk_options.setdefault(CONF_ENTITY_CONFIG, {})
|
all_entity_config = hk_options.setdefault(CONF_ENTITY_CONFIG, {})
|
||||||
for entity in self.included_cameras:
|
for entity in self.included_cameras:
|
||||||
hk_entity_config = entity_config.get(entity, {})
|
entity_config = all_entity_config.get(entity, {})
|
||||||
if hk_entity_config.get(CONF_VIDEO_CODEC) == VIDEO_CODEC_COPY:
|
if entity_config.get(CONF_VIDEO_CODEC) == VIDEO_CODEC_COPY:
|
||||||
cameras_with_copy.append(entity)
|
cameras_with_copy.append(entity)
|
||||||
if hk_entity_config.get(CONF_SUPPORT_AUDIO):
|
if entity_config.get(CONF_SUPPORT_AUDIO):
|
||||||
cameras_with_audio.append(entity)
|
cameras_with_audio.append(entity)
|
||||||
|
|
||||||
data_schema = vol.Schema(
|
data_schema = vol.Schema(
|
||||||
@ -453,18 +467,20 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Choose entity for the accessory."""
|
"""Choose entity for the accessory."""
|
||||||
domains = self.hk_options[CONF_DOMAINS]
|
hk_options = self.hk_options
|
||||||
|
domains = hk_options[CONF_DOMAINS]
|
||||||
|
entity_filter: EntityFilterDict
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
entities = cv.ensure_list(user_input[CONF_ENTITIES])
|
entities = cv.ensure_list(user_input[CONF_ENTITIES])
|
||||||
entity_filter = _async_build_entites_filter(domains, entities)
|
entity_filter = _async_build_entities_filter(domains, entities)
|
||||||
self.included_cameras = _async_cameras_from_entities(entities)
|
self.included_cameras = _async_cameras_from_entities(entities)
|
||||||
self.hk_options[CONF_FILTER] = entity_filter
|
hk_options[CONF_FILTER] = entity_filter
|
||||||
if self.included_cameras:
|
if self.included_cameras:
|
||||||
return await self.async_step_cameras()
|
return await self.async_step_cameras()
|
||||||
return await self.async_step_advanced()
|
return await self.async_step_advanced()
|
||||||
|
|
||||||
entity_filter = self.hk_options.get(CONF_FILTER, {})
|
entity_filter = hk_options.get(CONF_FILTER, {})
|
||||||
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, [])
|
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, [])
|
||||||
all_supported_entities = _async_get_matching_entities(
|
all_supported_entities = _async_get_matching_entities(
|
||||||
self.hass, domains, include_entity_category=True, include_hidden=True
|
self.hass, domains, include_entity_category=True, include_hidden=True
|
||||||
@ -494,24 +510,21 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Choose entities to include from the domain on the bridge."""
|
"""Choose entities to include from the domain on the bridge."""
|
||||||
domains = self.hk_options[CONF_DOMAINS]
|
hk_options = self.hk_options
|
||||||
|
domains = hk_options[CONF_DOMAINS]
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
entities = cv.ensure_list(user_input[CONF_ENTITIES])
|
entities = cv.ensure_list(user_input[CONF_ENTITIES])
|
||||||
entity_filter = _async_build_entites_filter(domains, entities)
|
|
||||||
self.included_cameras = _async_cameras_from_entities(entities)
|
self.included_cameras = _async_cameras_from_entities(entities)
|
||||||
self.hk_options[CONF_FILTER] = entity_filter
|
hk_options[CONF_FILTER] = _async_build_entities_filter(domains, entities)
|
||||||
if self.included_cameras:
|
if self.included_cameras:
|
||||||
return await self.async_step_cameras()
|
return await self.async_step_cameras()
|
||||||
return await self.async_step_advanced()
|
return await self.async_step_advanced()
|
||||||
|
|
||||||
entity_filter = self.hk_options.get(CONF_FILTER, {})
|
entity_filter: EntityFilterDict = hk_options.get(CONF_FILTER, {})
|
||||||
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, [])
|
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, [])
|
||||||
|
|
||||||
all_supported_entities = _async_get_matching_entities(
|
all_supported_entities = _async_get_matching_entities(
|
||||||
self.hass, domains, include_entity_category=True, include_hidden=True
|
self.hass, domains, include_entity_category=True, include_hidden=True
|
||||||
)
|
)
|
||||||
if not entities:
|
|
||||||
entities = entity_filter.get(CONF_EXCLUDE_ENTITIES, [])
|
|
||||||
# Strip out entities that no longer exist to prevent error in the UI
|
# Strip out entities that no longer exist to prevent error in the UI
|
||||||
default_value = [
|
default_value = [
|
||||||
entity_id for entity_id in entities if entity_id in all_supported_entities
|
entity_id for entity_id in entities if entity_id in all_supported_entities
|
||||||
@ -535,15 +548,13 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Choose entities to exclude from the domain on the bridge."""
|
"""Choose entities to exclude from the domain on the bridge."""
|
||||||
domains = self.hk_options[CONF_DOMAINS]
|
hk_options = self.hk_options
|
||||||
|
domains = hk_options[CONF_DOMAINS]
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
entity_filter = deepcopy(_EMPTY_ENTITY_FILTER)
|
|
||||||
entities = cv.ensure_list(user_input[CONF_ENTITIES])
|
|
||||||
entity_filter[CONF_INCLUDE_DOMAINS] = domains
|
|
||||||
entity_filter[CONF_EXCLUDE_ENTITIES] = entities
|
|
||||||
self.included_cameras = {}
|
self.included_cameras = {}
|
||||||
if CAMERA_DOMAIN in entity_filter[CONF_INCLUDE_DOMAINS]:
|
entities = cv.ensure_list(user_input[CONF_ENTITIES])
|
||||||
|
if CAMERA_DOMAIN in domains:
|
||||||
camera_entities = _async_get_matching_entities(
|
camera_entities = _async_get_matching_entities(
|
||||||
self.hass, [CAMERA_DOMAIN]
|
self.hass, [CAMERA_DOMAIN]
|
||||||
)
|
)
|
||||||
@ -552,7 +563,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
for entity_id in camera_entities
|
for entity_id in camera_entities
|
||||||
if entity_id not in entities
|
if entity_id not in entities
|
||||||
}
|
}
|
||||||
self.hk_options[CONF_FILTER] = entity_filter
|
hk_options[CONF_FILTER] = _make_entity_filter(
|
||||||
|
include_domains=domains, exclude_entities=entities
|
||||||
|
)
|
||||||
if self.included_cameras:
|
if self.included_cameras:
|
||||||
return await self.async_step_cameras()
|
return await self.async_step_cameras()
|
||||||
return await self.async_step_advanced()
|
return await self.async_step_advanced()
|
||||||
@ -600,14 +613,13 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
|
|
||||||
self.hk_options = deepcopy(dict(self.config_entry.options))
|
self.hk_options = deepcopy(dict(self.config_entry.options))
|
||||||
homekit_mode = self.hk_options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE)
|
homekit_mode = self.hk_options.get(CONF_HOMEKIT_MODE, DEFAULT_HOMEKIT_MODE)
|
||||||
entity_filter = self.hk_options.get(CONF_FILTER, {})
|
entity_filter: EntityFilterDict = self.hk_options.get(CONF_FILTER, {})
|
||||||
include_exclude_mode = MODE_INCLUDE
|
include_exclude_mode = MODE_INCLUDE
|
||||||
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, [])
|
entities = entity_filter.get(CONF_INCLUDE_ENTITIES, [])
|
||||||
if homekit_mode != HOMEKIT_MODE_ACCESSORY:
|
if homekit_mode != HOMEKIT_MODE_ACCESSORY:
|
||||||
include_exclude_mode = MODE_INCLUDE if entities else MODE_EXCLUDE
|
include_exclude_mode = MODE_INCLUDE if entities else MODE_EXCLUDE
|
||||||
domains = entity_filter.get(CONF_INCLUDE_DOMAINS, [])
|
domains = entity_filter.get(CONF_INCLUDE_DOMAINS, [])
|
||||||
include_entities = entity_filter.get(CONF_INCLUDE_ENTITIES)
|
if include_entities := entity_filter.get(CONF_INCLUDE_ENTITIES):
|
||||||
if include_entities:
|
|
||||||
domains.extend(_domains_set_from_entities(include_entities))
|
domains.extend(_domains_set_from_entities(include_entities))
|
||||||
name_to_type_map = await _async_name_to_type_map(self.hass)
|
name_to_type_map = await _async_name_to_type_map(self.hass)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@ -708,7 +720,7 @@ def _async_get_entity_ids_for_accessory_mode(
|
|||||||
def _async_entity_ids_with_accessory_mode(hass: HomeAssistant) -> set[str]:
|
def _async_entity_ids_with_accessory_mode(hass: HomeAssistant) -> set[str]:
|
||||||
"""Return a set of entity ids that have config entries in accessory mode."""
|
"""Return a set of entity ids that have config entries in accessory mode."""
|
||||||
|
|
||||||
entity_ids = set()
|
entity_ids: set[str] = set()
|
||||||
|
|
||||||
current_entries = hass.config_entries.async_entries(DOMAIN)
|
current_entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
for entry in current_entries:
|
for entry in current_entries:
|
||||||
|
@ -889,7 +889,7 @@ async def test_options_flow_include_mode_with_cameras(
|
|||||||
"filter": {
|
"filter": {
|
||||||
"exclude_domains": [],
|
"exclude_domains": [],
|
||||||
"exclude_entities": [],
|
"exclude_entities": [],
|
||||||
"include_domains": ["fan", "vacuum", "climate"],
|
"include_domains": ["climate", "fan", "vacuum"],
|
||||||
"include_entities": ["camera.native_h264", "camera.transcode_h264"],
|
"include_entities": ["camera.native_h264", "camera.transcode_h264"],
|
||||||
},
|
},
|
||||||
"entity_config": {"camera.native_h264": {"video_codec": "copy"}},
|
"entity_config": {"camera.native_h264": {"video_codec": "copy"}},
|
||||||
@ -904,15 +904,15 @@ async def test_options_flow_include_mode_with_cameras(
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["step_id"] == "init"
|
assert result["step_id"] == "init"
|
||||||
assert result["data_schema"]({}) == {
|
assert result["data_schema"]({}) == {
|
||||||
"domains": ["fan", "vacuum", "climate", "camera"],
|
"domains": ["climate", "fan", "vacuum", "camera"],
|
||||||
"mode": "bridge",
|
"mode": "bridge",
|
||||||
"include_exclude_mode": "include",
|
"include_exclude_mode": "include",
|
||||||
}
|
}
|
||||||
schema = result["data_schema"].schema
|
schema = result["data_schema"].schema
|
||||||
assert _get_schema_default(schema, "domains") == [
|
assert _get_schema_default(schema, "domains") == [
|
||||||
|
"climate",
|
||||||
"fan",
|
"fan",
|
||||||
"vacuum",
|
"vacuum",
|
||||||
"climate",
|
|
||||||
"camera",
|
"camera",
|
||||||
]
|
]
|
||||||
assert _get_schema_default(schema, "mode") == "bridge"
|
assert _get_schema_default(schema, "mode") == "bridge"
|
||||||
@ -921,7 +921,7 @@ async def test_options_flow_include_mode_with_cameras(
|
|||||||
result = await hass.config_entries.options.async_configure(
|
result = await hass.config_entries.options.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={
|
user_input={
|
||||||
"domains": ["fan", "vacuum", "climate", "camera"],
|
"domains": ["climate", "fan", "vacuum", "camera"],
|
||||||
"include_exclude_mode": "exclude",
|
"include_exclude_mode": "exclude",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -959,11 +959,11 @@ async def test_options_flow_include_mode_with_cameras(
|
|||||||
|
|
||||||
assert result3["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
assert result3["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||||
assert config_entry.options == {
|
assert config_entry.options == {
|
||||||
"entity_config": {"camera.native_h264": {}},
|
"entity_config": {},
|
||||||
"filter": {
|
"filter": {
|
||||||
"exclude_domains": [],
|
"exclude_domains": [],
|
||||||
"exclude_entities": ["climate.old", "camera.excluded"],
|
"exclude_entities": ["climate.old", "camera.excluded"],
|
||||||
"include_domains": ["fan", "vacuum", "climate", "camera"],
|
"include_domains": ["climate", "fan", "vacuum", "camera"],
|
||||||
"include_entities": [],
|
"include_entities": [],
|
||||||
},
|
},
|
||||||
"mode": "bridge",
|
"mode": "bridge",
|
||||||
@ -1025,7 +1025,7 @@ async def test_options_flow_with_camera_audio(
|
|||||||
"filter": {
|
"filter": {
|
||||||
"exclude_domains": [],
|
"exclude_domains": [],
|
||||||
"exclude_entities": [],
|
"exclude_entities": [],
|
||||||
"include_domains": ["fan", "vacuum", "climate"],
|
"include_domains": ["climate", "fan", "vacuum"],
|
||||||
"include_entities": ["camera.audio", "camera.no_audio"],
|
"include_entities": ["camera.audio", "camera.no_audio"],
|
||||||
},
|
},
|
||||||
"entity_config": {"camera.audio": {"support_audio": True}},
|
"entity_config": {"camera.audio": {"support_audio": True}},
|
||||||
@ -1040,15 +1040,15 @@ async def test_options_flow_with_camera_audio(
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["step_id"] == "init"
|
assert result["step_id"] == "init"
|
||||||
assert result["data_schema"]({}) == {
|
assert result["data_schema"]({}) == {
|
||||||
"domains": ["fan", "vacuum", "climate", "camera"],
|
"domains": ["climate", "fan", "vacuum", "camera"],
|
||||||
"mode": "bridge",
|
"mode": "bridge",
|
||||||
"include_exclude_mode": "include",
|
"include_exclude_mode": "include",
|
||||||
}
|
}
|
||||||
schema = result["data_schema"].schema
|
schema = result["data_schema"].schema
|
||||||
assert _get_schema_default(schema, "domains") == [
|
assert _get_schema_default(schema, "domains") == [
|
||||||
|
"climate",
|
||||||
"fan",
|
"fan",
|
||||||
"vacuum",
|
"vacuum",
|
||||||
"climate",
|
|
||||||
"camera",
|
"camera",
|
||||||
]
|
]
|
||||||
assert _get_schema_default(schema, "mode") == "bridge"
|
assert _get_schema_default(schema, "mode") == "bridge"
|
||||||
@ -1058,7 +1058,7 @@ async def test_options_flow_with_camera_audio(
|
|||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={
|
user_input={
|
||||||
"include_exclude_mode": "exclude",
|
"include_exclude_mode": "exclude",
|
||||||
"domains": ["fan", "vacuum", "climate", "camera"],
|
"domains": ["climate", "fan", "vacuum", "camera"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1095,11 +1095,11 @@ async def test_options_flow_with_camera_audio(
|
|||||||
|
|
||||||
assert result3["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
assert result3["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||||
assert config_entry.options == {
|
assert config_entry.options == {
|
||||||
"entity_config": {"camera.audio": {}},
|
"entity_config": {},
|
||||||
"filter": {
|
"filter": {
|
||||||
"exclude_domains": [],
|
"exclude_domains": [],
|
||||||
"exclude_entities": ["climate.old", "camera.excluded"],
|
"exclude_entities": ["climate.old", "camera.excluded"],
|
||||||
"include_domains": ["fan", "vacuum", "climate", "camera"],
|
"include_domains": ["climate", "fan", "vacuum", "camera"],
|
||||||
"include_entities": [],
|
"include_entities": [],
|
||||||
},
|
},
|
||||||
"mode": "bridge",
|
"mode": "bridge",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user