1 """Helper class to implement include/exclude of entities and domains."""
3 from __future__
import annotations
5 from collections.abc
import Callable
7 from functools
import lru_cache, partial
11 import voluptuous
as vol
18 MAX_EXPECTED_ENTITY_IDS,
22 from .
import config_validation
as cv
24 CONF_INCLUDE_DOMAINS =
"include_domains"
25 CONF_INCLUDE_ENTITY_GLOBS =
"include_entity_globs"
26 CONF_INCLUDE_ENTITIES =
"include_entities"
27 CONF_EXCLUDE_DOMAINS =
"exclude_domains"
28 CONF_EXCLUDE_ENTITY_GLOBS =
"exclude_entity_globs"
29 CONF_EXCLUDE_ENTITIES =
"exclude_entities"
31 CONF_ENTITY_GLOBS =
"entity_globs"
35 """A entity filter."""
37 def __init__(self, config: dict[str, list[str]]) ->
None:
38 """Init the filter."""
39 self.empty_filter: bool = sum(len(val)
for val
in config.values()) == 0
41 self.
_include_e_include_e = set(config[CONF_INCLUDE_ENTITIES])
42 self.
_exclude_e_exclude_e = set(config[CONF_EXCLUDE_ENTITIES])
43 self.
_include_d_include_d = set(config[CONF_INCLUDE_DOMAINS])
44 self.
_exclude_d_exclude_d = set(config[CONF_EXCLUDE_DOMAINS])
57 """Check if an entity is explicitly included."""
58 return entity_id
in self.
_include_e_include_e
or (
63 """Check if an entity is explicitly excluded."""
64 return entity_id
in self.
_exclude_e_exclude_e
or (
69 """Return the filter function."""
74 return self.
_filter_filter(entity_id)
78 """Convert the filter schema into a filter."""
82 BASE_FILTER_SCHEMA = vol.Schema(
84 vol.Optional(CONF_EXCLUDE_DOMAINS, default=[]): vol.All(
85 cv.ensure_list, [cv.string]
87 vol.Optional(CONF_EXCLUDE_ENTITY_GLOBS, default=[]): vol.All(
88 cv.ensure_list, [cv.string]
90 vol.Optional(CONF_EXCLUDE_ENTITIES, default=[]): cv.entity_ids,
91 vol.Optional(CONF_INCLUDE_DOMAINS, default=[]): vol.All(
92 cv.ensure_list, [cv.string]
94 vol.Optional(CONF_INCLUDE_ENTITY_GLOBS, default=[]): vol.All(
95 cv.ensure_list, [cv.string]
97 vol.Optional(CONF_INCLUDE_ENTITIES, default=[]): cv.entity_ids,
101 FILTER_SCHEMA = vol.All(BASE_FILTER_SCHEMA, convert_filter)
105 config: dict[str, dict[str, list[str]]],
107 """Convert the include exclude filter schema into a filter."""
108 include = config[CONF_INCLUDE]
109 exclude = config[CONF_EXCLUDE]
112 CONF_INCLUDE_DOMAINS: include[CONF_DOMAINS],
113 CONF_INCLUDE_ENTITY_GLOBS: include[CONF_ENTITY_GLOBS],
114 CONF_INCLUDE_ENTITIES: include[CONF_ENTITIES],
115 CONF_EXCLUDE_DOMAINS: exclude[CONF_DOMAINS],
116 CONF_EXCLUDE_ENTITY_GLOBS: exclude[CONF_ENTITY_GLOBS],
117 CONF_EXCLUDE_ENTITIES: exclude[CONF_ENTITIES],
122 INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER = vol.Schema(
124 vol.Optional(CONF_DOMAINS, default=[]): vol.All(cv.ensure_list, [cv.string]),
125 vol.Optional(CONF_ENTITY_GLOBS, default=[]): vol.All(
126 cv.ensure_list, [cv.string]
128 vol.Optional(CONF_ENTITIES, default=[]): cv.entity_ids,
132 INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA = vol.Schema(
136 ): INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER,
139 ): INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER,
143 INCLUDE_EXCLUDE_FILTER_SCHEMA = vol.All(
144 INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, convert_include_exclude_filter
149 """Convert a list of globs to a re pattern list."""
153 translated_patterns: list[str] = [
154 pattern
for glob
in set(globs)
if (pattern := fnmatch.translate(glob))
157 if not translated_patterns:
160 inner =
"|".join(translated_patterns)
161 combined = f
"(?:{inner})"
162 return re.compile(combined)
166 include_domains: list[str],
167 include_entities: list[str],
168 exclude_domains: list[str],
169 exclude_entities: list[str],
170 include_entity_globs: list[str] |
None =
None,
171 exclude_entity_globs: list[str] |
None =
None,
172 ) -> Callable[[str], bool]:
173 """Return a function that will filter entities based on the args."""
175 set(include_domains),
176 set(include_entities),
177 set(exclude_domains),
178 set(exclude_entities),
189 include_eg: re.Pattern[str] |
None,
190 exclude_eg: re.Pattern[str] |
None,
191 ) -> Callable[[str], bool]:
192 """Generate a filter from pre-comuted sets and pattern lists."""
193 have_exclude = bool(exclude_e
or exclude_d
or exclude_eg)
194 have_include = bool(include_e
or include_d
or include_eg)
198 if not have_include
and not have_exclude:
206 if have_include
and not have_exclude:
208 @lru_cache(maxsize=MAX_EXPECTED_ENTITY_IDS)
209 def entity_included(entity_id: str) -> bool:
210 """Return true if entity matches inclusion filters."""
212 entity_id
in include_e
214 or (bool(include_eg
and include_eg.match(entity_id)))
218 return entity_included
225 if not have_include
and have_exclude:
227 @lru_cache(maxsize=MAX_EXPECTED_ENTITY_IDS)
228 def entity_not_excluded(entity_id: str) -> bool:
229 """Return true if entity matches exclusion filters."""
231 entity_id
in exclude_e
233 or (exclude_eg
and exclude_eg.match(entity_id))
236 return entity_not_excluded
245 if include_d
or include_eg:
247 @lru_cache(maxsize=MAX_EXPECTED_ENTITY_IDS)
248 def entity_filter_4a(entity_id: str) -> bool:
249 """Return filter function for case 4a."""
250 return entity_id
in include_e
or (
251 entity_id
not in exclude_e
253 bool(include_eg
and include_eg.match(entity_id))
256 and not (exclude_eg
and exclude_eg.match(entity_id))
261 return entity_filter_4a
269 if exclude_d
or exclude_eg:
271 @lru_cache(maxsize=MAX_EXPECTED_ENTITY_IDS)
272 def entity_filter_4b(entity_id: str) -> bool:
273 """Return filter function for case 4b."""
275 if domain
in exclude_d
or bool(exclude_eg
and exclude_eg.match(entity_id)):
276 return entity_id
in include_e
277 return entity_id
not in exclude_e
279 return entity_filter_4b
284 return partial(operator.contains, include_e)
bool explicitly_included(self, str entity_id)
Callable[[str], bool] get_filter(self)
bool __call__(self, str entity_id)
None __init__(self, dict[str, list[str]] config)
bool explicitly_excluded(self, str entity_id)
list[_T] match(self, BluetoothServiceInfoBleak service_info)
tuple[str, str] split_entity_id(str entity_id)
re.Pattern[str]|None _convert_globs_to_pattern(list[str]|None globs)
INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER
Callable[[str], bool] generate_filter(list[str] include_domains, list[str] include_entities, list[str] exclude_domains, list[str] exclude_entities, list[str]|None include_entity_globs=None, list[str]|None exclude_entity_globs=None)
Callable[[str], bool] _generate_filter_from_sets_and_pattern_lists(set[str] include_d, set[str] include_e, set[str] exclude_d, set[str] exclude_e, re.Pattern[str]|None include_eg, re.Pattern[str]|None exclude_eg)
EntityFilter convert_filter(dict[str, list[str]] config)
EntityFilter convert_include_exclude_filter(dict[str, dict[str, list[str]]] config)