mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Tweak Matter discovery to ignore empty lists (#136854)
This commit is contained in:
parent
acbf40c384
commit
72caf9d5a2
@ -19,7 +19,7 @@ from .event import DISCOVERY_SCHEMAS as EVENT_SCHEMAS
|
||||
from .fan import DISCOVERY_SCHEMAS as FAN_SCHEMAS
|
||||
from .light import DISCOVERY_SCHEMAS as LIGHT_SCHEMAS
|
||||
from .lock import DISCOVERY_SCHEMAS as LOCK_SCHEMAS
|
||||
from .models import MatterDiscoverySchema, MatterEntityInfo
|
||||
from .models import UNSET, MatterDiscoverySchema, MatterEntityInfo
|
||||
from .number import DISCOVERY_SCHEMAS as NUMBER_SCHEMAS
|
||||
from .select import DISCOVERY_SCHEMAS as SELECT_SCHEMAS
|
||||
from .sensor import DISCOVERY_SCHEMAS as SENSOR_SCHEMAS
|
||||
@ -67,6 +67,8 @@ def async_discover_entities(
|
||||
if any(x in schema.required_attributes for x in discovered_attributes):
|
||||
continue
|
||||
|
||||
primary_attribute = schema.required_attributes[0]
|
||||
|
||||
# check vendor_id
|
||||
if (
|
||||
schema.vendor_id is not None
|
||||
@ -121,31 +123,6 @@ def async_discover_entities(
|
||||
):
|
||||
continue
|
||||
|
||||
# check if value exists but is none/null
|
||||
if not schema.allow_none_value and any(
|
||||
endpoint.get_attribute_value(None, val_schema) in (None, NullValue)
|
||||
for val_schema in schema.required_attributes
|
||||
):
|
||||
continue
|
||||
|
||||
# check for required value in (primary) attribute
|
||||
primary_attribute = schema.required_attributes[0]
|
||||
primary_value = endpoint.get_attribute_value(None, primary_attribute)
|
||||
if schema.value_contains is not None and (
|
||||
isinstance(primary_value, list)
|
||||
and schema.value_contains not in primary_value
|
||||
):
|
||||
continue
|
||||
|
||||
# check for value that may not be present
|
||||
if schema.value_is_not is not None and (
|
||||
schema.value_is_not == primary_value
|
||||
or (
|
||||
isinstance(primary_value, list) and schema.value_is_not in primary_value
|
||||
)
|
||||
):
|
||||
continue
|
||||
|
||||
# check for required value in cluster featuremap
|
||||
if schema.featuremap_contains is not None and (
|
||||
not bool(
|
||||
@ -159,6 +136,61 @@ def async_discover_entities(
|
||||
):
|
||||
continue
|
||||
|
||||
# BEGIN checks on actual attribute values
|
||||
# these are the least likely to be used and least efficient, so they are checked last
|
||||
|
||||
# check if PRIMARY value exists but is none/null
|
||||
if not schema.allow_none_value and any(
|
||||
endpoint.get_attribute_value(None, val_schema) in (None, NullValue)
|
||||
for val_schema in schema.required_attributes
|
||||
):
|
||||
continue
|
||||
|
||||
# check for required value in PRIMARY attribute
|
||||
primary_value = endpoint.get_attribute_value(None, primary_attribute)
|
||||
if schema.value_contains is not UNSET and (
|
||||
isinstance(primary_value, list)
|
||||
and schema.value_contains not in primary_value
|
||||
):
|
||||
continue
|
||||
|
||||
# check for value that may not be present in PRIMARY attribute
|
||||
if schema.value_is_not is not UNSET and (
|
||||
schema.value_is_not == primary_value
|
||||
or (
|
||||
isinstance(primary_value, list) and schema.value_is_not in primary_value
|
||||
)
|
||||
):
|
||||
continue
|
||||
|
||||
# check for value that may not be present in SECONDARY attribute
|
||||
secondary_attribute = (
|
||||
schema.required_attributes[1]
|
||||
if len(schema.required_attributes) > 1
|
||||
else None
|
||||
)
|
||||
secondary_value = (
|
||||
endpoint.get_attribute_value(None, secondary_attribute)
|
||||
if secondary_attribute
|
||||
else None
|
||||
)
|
||||
if schema.secondary_value_is_not is not UNSET and (
|
||||
(schema.secondary_value_is_not == secondary_value)
|
||||
or (
|
||||
isinstance(secondary_value, list)
|
||||
and schema.secondary_value_is_not in secondary_value
|
||||
)
|
||||
):
|
||||
continue
|
||||
|
||||
# check for required value in SECONDARY attribute
|
||||
if schema.secondary_value_contains is not UNSET and (
|
||||
isinstance(secondary_value, list)
|
||||
and schema.secondary_value_contains not in secondary_value
|
||||
):
|
||||
continue
|
||||
|
||||
# FINISH all validation checks
|
||||
# all checks passed, this value belongs to an entity
|
||||
|
||||
attributes_to_watch = list(schema.required_attributes)
|
||||
|
@ -18,6 +18,14 @@ type SensorValueTypes = type[
|
||||
]
|
||||
|
||||
|
||||
# A sentinel object to detect if a parameter is supplied or not.
|
||||
class _UNSET_TYPE:
|
||||
pass
|
||||
|
||||
|
||||
UNSET = _UNSET_TYPE()
|
||||
|
||||
|
||||
class MatterDeviceInfo(TypedDict):
|
||||
"""Dictionary with Matter Device info.
|
||||
|
||||
@ -111,16 +119,6 @@ class MatterDiscoverySchema:
|
||||
# are not discovered by other entities
|
||||
optional_attributes: tuple[type[ClusterAttributeDescriptor], ...] | None = None
|
||||
|
||||
# [optional] the primary attribute value must contain this value
|
||||
# for example for the AcceptedCommandList
|
||||
# NOTE: only works for list values
|
||||
value_contains: Any | None = None
|
||||
|
||||
# [optional] the primary attribute value must NOT have this value
|
||||
# for example to filter out invalid values (such as empty string instead of null)
|
||||
# in case of a list value, the list may not contain this value
|
||||
value_is_not: Any | None = None
|
||||
|
||||
# [optional] the primary attribute's cluster featuremap must contain this value
|
||||
# for example for the DoorSensor on a DoorLock Cluster
|
||||
featuremap_contains: int | None = None
|
||||
@ -131,3 +129,22 @@ class MatterDiscoverySchema:
|
||||
|
||||
# [optional] the primary attribute value may not be null/None
|
||||
allow_none_value: bool = False
|
||||
|
||||
# [optional] the primary attribute value must contain this value
|
||||
# for example for the AcceptedCommandList
|
||||
# NOTE: only works for list values
|
||||
value_contains: Any = UNSET
|
||||
|
||||
# [optional] the secondary (required) attribute value must contain this value
|
||||
# for example for the AcceptedCommandList
|
||||
# NOTE: only works for list values
|
||||
secondary_value_contains: Any = UNSET
|
||||
|
||||
# [optional] the primary attribute value must NOT have this value
|
||||
# for example to filter out invalid values (such as empty string instead of null)
|
||||
# in case of a list value, the list may not contain this value
|
||||
value_is_not: Any = UNSET
|
||||
|
||||
# [optional] the secondary (required) attribute value must NOT have this value
|
||||
# for example to filter out empty lists in list sensor values
|
||||
secondary_value_is_not: Any = UNSET
|
||||
|
@ -217,6 +217,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.ModeSelect.Attributes.CurrentMode,
|
||||
clusters.ModeSelect.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -229,6 +231,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.OvenMode.Attributes.CurrentMode,
|
||||
clusters.OvenMode.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -241,6 +245,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.LaundryWasherMode.Attributes.CurrentMode,
|
||||
clusters.LaundryWasherMode.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -253,6 +259,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.RefrigeratorAndTemperatureControlledCabinetMode.Attributes.CurrentMode,
|
||||
clusters.RefrigeratorAndTemperatureControlledCabinetMode.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -265,6 +273,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.RvcCleanMode.Attributes.CurrentMode,
|
||||
clusters.RvcCleanMode.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -277,6 +287,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.DishwasherMode.Attributes.CurrentMode,
|
||||
clusters.DishwasherMode.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -289,6 +301,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.EnergyEvseMode.Attributes.CurrentMode,
|
||||
clusters.EnergyEvseMode.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -301,6 +315,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.DeviceEnergyManagementMode.Attributes.CurrentMode,
|
||||
clusters.DeviceEnergyManagementMode.Attributes.SupportedModes,
|
||||
),
|
||||
# don't discover this entry if the supported modes list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -384,6 +400,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.TemperatureControl.Attributes.SelectedTemperatureLevel,
|
||||
clusters.TemperatureControl.Attributes.SupportedTemperatureLevels,
|
||||
),
|
||||
# don't discover this entry if the supported levels list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -397,6 +415,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.LaundryWasherControls.Attributes.SpinSpeedCurrent,
|
||||
clusters.LaundryWasherControls.Attributes.SpinSpeeds,
|
||||
),
|
||||
# don't discover this entry if the spinspeeds list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SELECT,
|
||||
@ -412,5 +432,7 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.LaundryWasherControls.Attributes.NumberOfRinses,
|
||||
clusters.LaundryWasherControls.Attributes.SupportedRinses,
|
||||
),
|
||||
# don't discover this entry if the supported rinses list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
]
|
||||
|
@ -809,6 +809,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.OperationalState.Attributes.OperationalState,
|
||||
clusters.OperationalState.Attributes.OperationalStateList,
|
||||
),
|
||||
# don't discover this entry if the supported state list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SENSOR,
|
||||
@ -822,6 +824,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.OperationalState.Attributes.CurrentPhase,
|
||||
clusters.OperationalState.Attributes.PhaseList,
|
||||
),
|
||||
# don't discover this entry if the supported state list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SENSOR,
|
||||
@ -835,6 +839,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.RvcOperationalState.Attributes.CurrentPhase,
|
||||
clusters.RvcOperationalState.Attributes.PhaseList,
|
||||
),
|
||||
# don't discover this entry if the supported state list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SENSOR,
|
||||
@ -848,6 +854,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.OvenCavityOperationalState.Attributes.CurrentPhase,
|
||||
clusters.OvenCavityOperationalState.Attributes.PhaseList,
|
||||
),
|
||||
# don't discover this entry if the supported state list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SENSOR,
|
||||
@ -877,6 +885,8 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.RvcOperationalState.Attributes.OperationalStateList,
|
||||
),
|
||||
allow_multi=True, # also used for vacuum entity
|
||||
# don't discover this entry if the supported state list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.SENSOR,
|
||||
@ -891,5 +901,7 @@ DISCOVERY_SCHEMAS = [
|
||||
clusters.OvenCavityOperationalState.Attributes.OperationalState,
|
||||
clusters.OvenCavityOperationalState.Attributes.OperationalStateList,
|
||||
),
|
||||
# don't discover this entry if the supported state list is empty
|
||||
secondary_value_is_not=[],
|
||||
),
|
||||
]
|
||||
|
@ -1518,108 +1518,6 @@
|
||||
'state': 'previous',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[silabs_dishwasher][select.dishwasher_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.dishwasher_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Mode',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'mode',
|
||||
'unique_id': '00000000000004D2-0000000000000036-MatterNodeDevice-1-MatterDishwasherMode-89-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[silabs_dishwasher][select.dishwasher_mode-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Dishwasher Mode',
|
||||
'options': list([
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.dishwasher_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[silabs_laundrywasher][select.laundrywasher_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.laundrywasher_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Mode',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'mode',
|
||||
'unique_id': '00000000000004D2-000000000000001D-MatterNodeDevice-1-MatterLaundryWasherMode-81-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[silabs_laundrywasher][select.laundrywasher_mode-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'LaundryWasher Mode',
|
||||
'options': list([
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.laundrywasher_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[silabs_laundrywasher][select.laundrywasher_number_of_rinses-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
Loading…
x
Reference in New Issue
Block a user