Chapter 5

The complete Filter Engine

Learn Include, Exclude, ANY, ALL and every matcher in Stable 2.0.55, confirm the scope in the preview first, and only then let Home Assistant entities into a Matter bridge.

Turn "which entities get bridged" into a predictable rule

The Filter Engine decides whether an entity in the Home Assistant registry is eligible for a bridge to build a Matter endpoint from. It is not the room filter inside a controller, and it does not change the entities in Home Assistant itself; it only controls the candidate set for this one bridge. In a home with lights, diagnostic sensors, scripts and integrations from several brands, including every entity outright tends to push out items you do not need or that are not supported. In a small office you often need to split into several bridges by floor, by area or by label.

The right approach is to write the include conditions first, then the always-exclude conditions, and check the result in the preview last. An empty Include array means the candidates are not restricted; only a non-empty Include is evaluated against includeMode. Exclude always removes an entity as soon as any one rule matches. The final result can be written as: (Include is empty or Include evaluates true) and nothing in Exclude matches.

Warning: a match only means the entity joins the candidate set. It does not guarantee that a particular controller can show its Home Assistant domain, its device class or the Matter device type you override it to. Release channel, feature maturity and controller support are three different things.

ANY, ALL, empty sets and precedence

WhereHow they combineResultWhen it fits
An empty Include arrayNo matcher runsEvery registered entity starts out includedA blocklist strategy driven by Exclude
Include + anyORAny one Include matcher is enoughIncluding several domains, areas or labels
Include + allANDEvery Include matcher has to matchFor example, requiring the light domain and a given area at the same time
ExcludeAlways ANY / ORAny one Exclude matcher removes the entityExcluding diagnostic, test or sensitive-action entities
Include and Exclude both matchExclude winsThe entity is not includedInclude broadly first, then add safety guards

includeMode applies to Include only; when it is omitted, the code defaults to any. Exclude calls the matcher test without a mode, so it defaults to any as well. ALL does not mean the several values of one matcher are ANDed together; each row still carries exactly one type and one value, and ALL requires every row to succeed.

Hidden entities are a second layer of the decision. Even when a matcher matches, an entity that Home Assistant marks as hidden is skipped by default; it comes in only when the bridge's includeHiddenEntities feature flag is on. An entity marked disabled in the mapping likewise should not be treated as a usable endpoint. Do not run the filter, the hidden flag and the Entity Mapping disable together as if they were one switch.

Every Stable 2.0.55 matcher at a glance

typeData matchedExact behavior and caveats
patternentity_idA wildcard expands only * into an arbitrary string, and the whole value is anchored at both ends; every other regular-expression character is escaped. Example: light.demo_*.
regexentity_idBuilds a JavaScript RegExp directly and tests entity_id only; invalid syntax returns no match, and it never falls back to another field. Example: ^(light|switch)\.demo_.*.
domainThe part of entity_id before the dotAn exact, case-sensitive match, for example light; it accepts neither a comma-separated list nor a wildcard.
platformEntity Registry platformAn exact match on the integration / platform string, for example the documented mqtt; the value Filter Reference displays is the authoritative one.
labelThe entity's own labelsDeprecated; it behaves exactly like entity_label. Old configurations still read, but new rules should migrate.
entity_labelEntity labelsIncludes only the entities that carry the label directly; it does not pull in the other entities on the same device. Enter either the display name or the label_id.
device_labelDevice labelsWhen a device label matches, every candidate entity under that device matches. Enter either the display name or the label_id.
entity_label_regexThe slug and display name of every entity labelA match when any assigned label satisfies the RegExp; no labels, or an invalid RegExp, is no match.
device_label_regexThe slug and display name of every device labelAny matching device label makes that device's entities match; good for managing a whole device through a label naming convention.
any_field_regexA single-line key=value haystackCan check entity_id, domain, platform, area, entity_category, device_class, entity / device label slugs and names, device_name, product_name and manufacturer all at once.
areaThe entity area, otherwise the device areaTakes the entity's area_id first and falls back to the device's area_id; an exact match on the area slug.
entity_categoryEntity Registry categoryAn exact match, for example config or diagnostic; usually placed in Exclude.
device_namename_by_user → name → default_nameWithout a * it is a case-insensitive substring; with a * it is a case-insensitive wildcard anchored across the whole value.
product_namemodel → default_modelThe same case-insensitive substring, or an anchored wildcard when it contains *.
manufacturermanufacturer → default_manufacturerThe same case-insensitive substring or wildcard; no match when there is no device registry data.
device_classThe current state's attributes.device_classAn exact match, for example temperature or motion; it is neither the entity domain nor the Matter device type.
Term: the schema calls pattern a wildcard pattern and defines regex as an entity ID regex. The interface has no separate type named entityIdRegex; in v2.0.55, the entityIdRegex the requirements talk about is type: "regex".

Regular expressions, label resolution and any-field

The exact label matchers first look the value up by display name, case-insensitively, and turn it into a label_id when they find one. When they do not, the code normalizes your input: it strips combining diacritics, lowercases, turns every non-alphanumeric character into an underscore and clears leading and trailing underscores. The most reliable route is still to copy the real label_id from Filter Reference in the sidebar (route /labels), so a renamed label cannot change what the rule does.

The haystack for any_field_regex is one line of key=value fields joined by spaces. You can express OR with alternation, and require one entity to satisfy several fields at once with positive lookahead, as the documentation illustrates with (?=.*domain=light)(?=.*area=demo_room). Label arrays are joined with commas, so test any boundary construct against your real slugs. The engine builds the rule with new RegExp(pattern) and adds no case-insensitive flag; cover the casing explicitly in the pattern when you need it, and the most stable option is still the raw value Filter Reference shows.

RequirementRecommended matcherWhy
A known set of entity_id prefixespatternEasier to read than a regex, and anchored end to end.
The entity_id structure of two domainsregexAlternation on entity_id only, so the scope is explicit.
One entity labelentity_labelIt will not drag in a whole device by accident.
A whole device and all its entitiesdevice_labelMore stable in meaning than several entity_id rules.
domain and area both trueTwo rows under Include ALLEasier to maintain than an any-field lookahead.
Complex OR / AND across fieldsany_field_regexOnly it sees the whole haystack inside a single matcher.
Warning: an invalid regex silently turns into "no match". In ALL mode that fails the whole Include; in Exclude it can cost you a guard you believed was there. Look at the Preview every time you change a regex.

Build and preview a filter in the field editor

  1. Open the bridge edit page

    Select the target bridge on Bridges and open Edit. Write down the current Include, Exclude and includeMode first, and do not change feature flags at the same time.

  2. Choose the Include Mode

    Under Include or exclude entities, set Include Mode to any or all. ANY suits "any one of these categories"; ALL suits "all of these conditions at once".

  3. Add Include rows one at a time

    Use the Include add control, then pick a Type and fill in a Value for each row. Copy label, area and platform values from Filter Reference in the sidebar rather than guessing them from the displayed text.

  4. Add Exclude guards

    Put the categories you should not expose into Exclude, such as a category of diagnostic or an explicit test entity pattern. A single Exclude match beats Include.

  5. Check Preview Matching Entities

    Wait about 800 ms for the automatic refresh, or press Preview Matching Entities. Check the count, the domain chips, the names and the entity_ids; the preview lists at most the first 100 rows, and total is the real number of matches.

  6. Save and verify

    Press Save only when the preview matches what you expect and the form is valid. Then go back to Bridge / Devices to check the actual endpoints and the failed entities; do not make "the controller shows it immediately" your only success criterion.

Filter Preview runs the same matcher logic against the current Home Assistant entity, device, state and label registries, and sorts the result by entity_id. It also flags a large entity count, an unsupported domain or a vacuum situation; those hints are planning information and never rewrite the configuration on their own.

Maintainable rules for homes and offices

A room-specific bridge: set Include Mode to ALL, use area in the first row with the area slug the documentation uses, and domain in the second row limited to light. That is not the same thing as "area or light". If you want switches too, express light / switch with a single entity ID regex and put it under ALL together with the area, or reorganize the categories with entity or device labels.

A voice-device allowlist: in Home Assistant, put an entity label directly on the entities you are willing to hand to a voice controller, and use entity_label in Include. Use device_label only when you need every entity on a device. Do not build new configurations on the deprecated label.

Include broadly, exclude precisely: leave Include empty and add entity_category=config, entity_category=diagnostic and a test-naming pattern to Exclude. This strategy widens on its own as Home Assistant gains entities; look at the Preview again after every integration you add.

Splitting by brand or model: use manufacturer, product_name or device_name. All three do a case-insensitive substring match, and only switch to a whole-value match when they contain a wildcard. An entity whose device registry data is missing will not match, so use the Preview to find the gaps instead of assuming platform is the same as manufacturer.

Maintenance principle: reach first for the ones whose meaning is clear — domain, area, entity or device label — and only then for pattern; use an any-field regex only where several ANY / ALL rows cannot express what you need. The shorter the rule, the easier it is to read after a rename or a change of integration.

Back out step by step when the filter result is wrong

  1. Include ALL returns zero rows

    Temporarily keep only one row at a time and look at the Preview, confirming that each row can match the same entity on its own. The most common causes are setting "area A or area B" to ALL by mistake, and invalid regex syntax.

  2. A label brings in only one entity

    Check that you are using entity_label. If you want a whole device, assign the label to the device in Home Assistant and switch to device_label; do not expect an entity label to propagate on its own.

  3. The area rule does not match

    Copy the area slug from Filter Reference. The engine looks at the entity area first and only then at the device area; an entity that has been placed in another area overrides the device area.

  4. It is in the Preview but not on the bridge

    Check the hidden state, includeHiddenEntities, the disabled flag in Entity Mapping, whether the domain and type are supported, and the bridge's failed entities. Filter Preview only proves the filter decision.

  5. Exclude did not block an item

    Exclude is always ANY, so check first that the type points at the right field. regex looks at entity_id only; to check manufacturer or labels, switch to the dedicated matcher or to an any-field regex.

  6. The preview shows only 100 rows

    That is the interface truncating, not a filter limit. Read total, narrow the rule or split the bridge, then preview again, so a healthy-looking first 100 rows does not hide what is at the end.

Filter Engine FAQ

Does leaving Include empty mean no entity is included?
No. In v2.0.55 the decision treats an empty Include array as everything included, and then applies Exclude. For an allowlist, add at least one Include row.
Can Exclude be set to ALL as well?
No. The schema has includeMode only, and the backend uses the default ANY for Exclude. Any single Exclude match excludes the entity.
Which of pattern and regex is case-insensitive?
Neither is case-insensitive on its own. pattern turns * into a wildcard and anchors the value; regex is a JavaScript RegExp. Only device_name, product_name and manufacturer lowercase the values before comparing.
Does Filter Reference change labels or areas?
No. It is a lookup page. It lists labels, areas, domains, platforms, entity categories, device classes, device names and product names, and gives you a way to copy each value.
If the controller does not show it, does that mean the matcher is wrong?
Not necessarily. Confirm the candidates and the endpoints in the Preview and on Devices first, then check controller support for the Matter device type separately. Whether a controller can display something is not a field the Filter Engine decides on.

Stable 2.0.55 pinned-version sources

All of the above are pinned to the v2.0.55 commit; this chapter describes the Stable channel. The Filter Engine itself is a settled feature, and controller support plays no part in the matcher decision.