Skip to content

Fix use of Matter labels #145652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from

Conversation

jvmahon
Copy link
Contributor

@jvmahon jvmahon commented May 26, 2025

Breaking change

Proposed change

This pull request fixes several interrelated issues regarding the naming of Matter entities, particularly for multi-endpoint devices.

1. Prevent unintended overwriting of an attribute name using the FixedLabel

  • Previously, if a device type endpoint included a FixedLabel, that label would be used to name the control entities (a correct behavior), but it would also overwrite the attribute names, for example, the Onlevel, OnTransitionTime, and OffTransition time attributes of the LevelControl cluster would be overwritten by the FixedLabel.
  • With this update, whether or not the FixedLabel is used is explicitly specified in the entity's discovery schema allowing much greater specificity in the use of labels.
  • A new attribute "name_using_matter_labels" has been added to the MatterEntityDescription. If an entity is allowed to be named using a Matter FixedLabel or UserLabel, then in the discovery schema for that entity, you set name_using_matter_labels to the list of permitted labels.
  • The image below shows this issue, which has now been fixed.

image

2. Improved Association of Attributes for Multi-Endpoint Devices

  • Previously, if a node had multiple endpoints, the endpoint number was appended to the entity name (e.g., "Light (1)" or "Light (6)") and to its related attributes. This allowed a user to associate the attribute with the particular entity.
  • However, if the endpoints had different primary attributes, then the endpoint number was not appended even if the same attribute appeared on another endpoint, so you have instances where it was not possible to associate an attribute with a particular endpoint.
  • This is fixed by appending the endpoint number to the entity name if the same cluster is present on another endpoint (rather than just looking at the primary attribute).

"Real World" devices that have this problem include Inovelli VTM31 Dimmer, their Fan Switch VTM36 and Inovelli's upcoming Matter Switch (VTM30) which I'm beta testing. Samples of these devices can be sent if needed.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:
  • Link to developer documentation pull request:
  • Link to frontend pull request:

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

@home-assistant
Copy link

Hey there @home-assistant/matter, mind taking a look at this pull request as it has been labeled with an integration (matter) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of matter can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign matter Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component) on the pull request.

@jvmahon
Copy link
Contributor Author

jvmahon commented May 26, 2025

@marcelveldt This is a full re-write of my prior attempt to fix the labeling issue. On my last attempt, the code made some guesses about whether to rename using the FixedLabel / UserLabel. You suggested it would be better to be more explicit about when the FixedLabel is used for a entity name. That is done here.

This PR builds on your suggest and I do think it is a better approach. Hopefully, you'll agree!.

@MartinHjelmare MartinHjelmare changed the title Fixed for Use of Matter labels Fix use of Matter labels May 27, 2025
Comment on lines +122 to +123
clusters.FixedLabel.Attributes.LabelList,
clusters.UserLabel.Attributes.LabelList,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we perhaps swap userlabel and fixedlabel so we prefer a userlabel over a fixedlabel ?

Copy link
Contributor Author

@jvmahon jvmahon May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think FixedLabel shoudl get priority as the primary reason for the label is to give the end-user the manufacturer specified "hint" as to what the endpoint does. The user can then rename it as they please.

The "UserLabel" is secondary. If anything, I was inclined to eliminate the "UserLabel" since it provides no manufacturer "hint" as to purpose of the endpoint, but rather merely contains whatever the user put there (no more useful than renaming by the user), but I kept UserLabel there as it was there in the prior implementation, so I was assuming it was thought to be useful.

For now, its probably irrelevant. I haven't seen any system that allows setting UserLabel yet (I've checked Home Assistant, Apple, Google, SmartThings, Hubitat), so it wouldn't get picked up (and I have not seen the attribute in any device).

@@ -62,6 +62,8 @@ class MatterEntityDescription(EntityDescription):
measurement_to_ha: Callable[[Any], Any] | None = None
ha_to_native_value: Callable[[Any], Any] | None = None
command_timeout: int | None = None
# List of Matter FixedLabel or UserLabels used for name
name_using_matter_labels: list | None = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a list and if it needs to be a list, the typing is not complete

Copy link
Contributor Author

@jvmahon jvmahon May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I corrected this to be:

name_using_matter_labels: list[str]  | None = None

I think it should be a list. Previously, there was a hard-coded list of two labels ("Label" and "Button"), but there are no standards for how manufacturers should label things. In this iteration, I wanted to have a more adaptable approach, and using a list of the labels allows adapting the list of labels to the device by specifying a list in the discovery schema (which seems less error-prone when it gets changed than changing the main code).

key="MatterLight",
name=None,
name_using_matter_labels=["Label"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this on purpose not lowercase ?

Copy link
Contributor Author

@jvmahon jvmahon May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for appearance purposes only, but as explained further in the next comment response, the case doesn't matter as label matching is now case insensitive.

key="GenericSwitch",
device_class=EventDeviceClass.BUTTON,
translation_key="button",
name_using_matter_labels=["label", "button"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this on purpose all lowercase?

Copy link
Contributor Author

@jvmahon jvmahon May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now changed this to "Label" and "Button" for consistency of how I presented them elsewhere in the code, but as a practical matter, it doesn't matter because ...

The code that does label matching is now case insensitive - both the labels in that name_using_matter_labels list, as well as the FixedLabel / UserLabel values get converted to lower case for the comparison. I did this because the standards don't really give much guidance on how a manufacturer should specify a label - some may be in lower case, upper, mixed, whatever. So I thought it made sense just to be case insensitive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI - Lines 128-131 of entity.py now implement a case-insensitive match of the labels in "name_using_matter_labels" to the labels in FixedLabel or UserLabel

@marcelveldt
Copy link
Member

I think the default should be to always prefer the matter labels, because that is exactly where they are intended for.
Also wondering why we need to differentiate between the label, I'd like to see some examples.

@jvmahon
Copy link
Contributor Author

jvmahon commented May 27, 2025

I think the default should be to always prefer the matter labels, because that is exactly where they are intended for.

I looked at this pretty closely and, it turns out that, more often than not, you don't want to use the label.

The reason is that for every cluster, there is generally 1 "controllable" item that you want to label - for example, the main "switch" or "light" control, but then there are several attributes on that same cluster that you don't want to overwrite with the label (e.g., the OnOffTransitionTime, OnTransitionTime, OffTransitionTime, etc.) Since the number of attributes that you don't want to overwrite is larger than the number of control items where you want to use the label, defaulting to not using labels works right the majority of the time.

As a side note ... The "original" labeling code was implemented around the Inovelli VTM31 - that device didn't have labels on the RGB endpoint or Dimmer endpoint, and only used labels on the three Generic Switch endpoints. For this device, the fact that the labels were always didn't show up as an issue - the labels were only on the Generic switches and overwriting attribute values didn't happen on those endpoints since the Generic Switch attributes were not used to create Home Assistant entities. In newer products, Inovelli started using labels on the primary control endpoints which made this problem of overwriting attributes for, e.g., level control, fan, and on/Off clusters, more pronounced. Thus, the need to make these changes in labeling.

Also wondering why we need to differentiate between the label, I'd like to see some examples.

Here, again, I was finding that due to how different manufacturers use labels, you sometimes don't want to use the label even if the manufacturer gave one, or you only want them on specific entities. As examples, Inovelli uses "Labels" and "Button" and generally those labels have relevant information. Eve uses "orientation" but only on their dual-outlet product. Again, for Eve, the label contains useful information ("top" and "bottom" designation). TP-LINK, on the other hand, used "Position" for their dual outlet plug, but the information there wasn't very useful (just a 1 or a 2), so i that case, I wanted to ignore the label. Gong forward, there is no way to know what will be used by other manufacturers - so as I was doing this rewrite, I wanted to do something flexible and adaptable, and therefore included a means for different discovery schema to differentiate which labels may be used.

Thus, the ability to specify a list of label for the entity to be created seemed a way to adapt to particular quirks of the manufacturers implementation. Additionally, in the future, if there was some device that had a really unusual use of labels, you could do a vendor / product specific discovery schema block to handle a particular product with its labeling quirks.

@jvmahon
Copy link
Contributor Author

jvmahon commented May 27, 2025

I think the default should be to always prefer the matter labels

If you don't agree with my reasons not to always use labels, I think the fix is pretty simple

At line 66 of entity.py, change from "None" to have a set of preferred defaults, like:

    name_using_matter_labels: list[str] | None = ["labels", "button"]

Then in the discovery schema for all of the attributes, mode selects, etc. where you don't want to use labels, include:

name_using_matter_labels = None

Or where you want to override the default list, specifying a different list.

Changing the default to "always use" with a default list of labels will result in many more changes throughout the discovery schemas as we'll want to indicate that, for all cluster attributes where the attribute name matters (e.g., like for the "onTransition", "offTransition", "onOffTransition" examples) , the code should not use the labels.

@lboue
Copy link
Contributor

lboue commented May 28, 2025

There are errors during tests:

FAILED tests/components/matter/test_binary_sensor.py::test_binary_sensors[door_lock] - AssertionError
FAILED tests/components/matter/test_binary_sensor.py::test_binary_sensors[door_lock_with_unbolt] - AssertionError
FAILED tests/components/matter/test_binary_sensor.py::test_battery_sensor[door_lock] - assert None
FAILED tests/components/matter/test_button.py::test_buttons[multi_endpoint_light] - AssertionError
FAILED tests/components/matter/test_button.py::test_buttons[temperature_sensor] - AssertionError
FAILED tests/components/matter/test_event.py::test_events[generic_switch_multi] - AssertionError
FAILED tests/components/matter/test_event.py::test_events[multi_endpoint_light] - AssertionError
FAILED tests/components/matter/test_event.py::test_generic_switch_multi_node[generic_switch_multi] - assert None
FAILED tests/components/matter/test_number.py::test_numbers[multi_endpoint_light] - AssertionError
FAILED tests/components/matter/test_select.py::test_selects[oven] - AssertionError
FAILED tests/components/matter/test_sensor.py::test_sensors[generic_switch_multi] - AssertionError
FAILED tests/components/matter/test_sensor.py::test_sensors[multi_endpoint_light] - AssertionError

You should update snapshots files with commands like this and fix tests if needed:

source .venv/bin/activate
pytest tests/components/matter/test_event.py --snapshot-update

New entities names:

2025-05-28 16:38:05.724 INFO     MainThread homeassistant.helpers.entity_registry:entity_registry.py:967 Registered new event.matter entity: event.mock_generic_switch_button_1
2025-05-28 16:38:05.726 INFO     MainThread homeassistant.helpers.entity_registry:entity_registry.py:967 Registered new sensor.matter entity: sensor.mock_generic_switch_current_switch_position_1
2025-05-28 16:38:05.727 INFO     MainThread homeassistant.helpers.entity_registry:entity_registry.py:967 Registered new event.matter entity: event.mock_generic_switch_fancy_button_2
2025-05-28 16:38:05.729 INFO     MainThread homeassistant.helpers.entity_registry:entity_registry.py:967 Registered new sensor.matter entity: sensor.mock_generic_switch_current_switch_position_2

@jvmahon
Copy link
Contributor Author

jvmahon commented May 28, 2025

You should update snapshots files with commands like this and fix tests if needed:

I can try to go back and figure this part out (will be at least a week until I can get the time), but I really don't understand how to setup test, so any help here would be appreciated.

@lboue
Copy link
Contributor

lboue commented May 28, 2025

You should update snapshots files with commands like this and fix tests if needed:

I can try to go back and figure this part out (will be at least a week until I can get the time), but I really don't understand how to setup test, so any help here would be appreciated.

Check this:
https://developers.home-assistant.io/docs/development_testing/

@jvmahon
Copy link
Contributor Author

jvmahon commented May 29, 2025

Check this:
https://developers.home-assistant.io/docs/development_testing/

I tried, but I don't really understand it.
How about we first see if @marcel and others accept this PR first, then figure out what to do about correcting the test. Most likely, that's an area where I'll need assistance from others.

@jvmahon
Copy link
Contributor Author

jvmahon commented Jun 12, 2025

@marcelveldt
This fix looks like its stalled for a bit. I assume the HA team is just busy with other priorities and will get back to this when they can, but if you're waiting for something from me, please let me know. thanks.

@jvmahon jvmahon mentioned this pull request Jun 22, 2025
19 tasks
@lboue
Copy link
Contributor

lboue commented Jun 22, 2025

@marcelveldt This fix looks like its stalled for a bit. I assume the HA team is just busy with other priorities and will get back to this when they can, but if you're waiting for something from me, please let me know. thanks.

There is a tutorial here: Testing your code. You should update snapshots to fix errors:

--------------------------- snapshot report summary ----------------------------
10 snapshots failed. 558 snapshots passed. 42 snapshots unused.

Unused test_binary_sensors[door_lock][binary_sensor.mock_door_lock_battery-entry], test_binary_sensors[door_lock][binary_sensor.mock_door_lock_battery-state], test_binary_sensors[door_lock_with_unbolt][binary_sensor.mock_door_lock_battery-entry], test_binary_sensors[door_lock_with_unbolt][binary_sensor.mock_door_lock_battery-state], test_binary_sensors[door_lock_with_unbolt][binary_sensor.mock_door_lock_door-entry], test_binary_sensors[door_lock_with_unbolt][binary_sensor.mock_door_lock_door-state] (tests/components/matter/snapshots/test_binary_sensor.ambr)
Unused test_numbers[multi_endpoint_light][number.inovelli_off_transition_time-entry], test_numbers[multi_endpoint_light][number.inovelli_off_transition_time-state], test_numbers[multi_endpoint_light][number.inovelli_on_level_6-entry], test_numbers[multi_endpoint_light][number.inovelli_on_level_6-state], test_numbers[multi_endpoint_light][number.inovelli_on_off_transition_time-entry], test_numbers[multi_endpoint_light][number.inovelli_on_off_transition_time-state], test_numbers[multi_endpoint_light][number.inovelli_on_transition_time-entry], test_numbers[multi_endpoint_light][number.inovelli_on_transition_time-state] (tests/components/matter/snapshots/test_number.ambr)
Unused test_sensors[generic_switch_multi][sensor.mock_generic_switch_fancy_button-entry], test_sensors[generic_switch_multi][sensor.mock_generic_switch_fancy_button-state], test_sensors[multi_endpoint_light][sensor.inovelli_config-entry], test_sensors[multi_endpoint_light][sensor.inovelli_config-state], test_sensors[multi_endpoint_light][sensor.inovelli_down-entry], test_sensors[multi_endpoint_light][sensor.inovelli_down-state], test_sensors[multi_endpoint_light][sensor.inovelli_up-entry], test_sensors[multi_endpoint_light][sensor.inovelli_up-state] (tests/components/matter/snapshots/test_sensor.ambr)
Unused test_selects[oven][select.mock_oven_temperature_level-entry], test_selects[oven][select.mock_oven_temperature_level-state] (tests/components/matter/snapshots/test_select.ambr)
Unused test_buttons[multi_endpoint_light][button.inovelli_config-entry], test_buttons[multi_endpoint_light][button.inovelli_config-state], test_buttons[multi_endpoint_light][button.inovelli_down-entry], test_buttons[multi_endpoint_light][button.inovelli_down-state], test_buttons[multi_endpoint_light][button.inovelli_identify_6-entry], test_buttons[multi_endpoint_light][button.inovelli_identify_6-state], test_buttons[multi_endpoint_light][button.inovelli_up-entry], test_buttons[multi_endpoint_light][button.inovelli_up-state], test_buttons[temperature_sensor][button.mock_temperature_sensor_identify-entry], test_buttons[temperature_sensor][button.mock_temperature_sensor_identify-state] (tests/components/matter/snapshots/test_button.ambr)
Unused test_events[generic_switch_multi][event.mock_generic_switch_fancy_button-entry], test_events[generic_switch_multi][event.mock_generic_switch_fancy_button-state], test_events[multi_endpoint_light][event.inovelli_config-entry], test_events[multi_endpoint_light][event.inovelli_config-state], test_events[multi_endpoint_light][event.inovelli_down-entry], test_events[multi_endpoint_light][event.inovelli_down-state], test_events[multi_endpoint_light][event.inovelli_up-entry], test_events[multi_endpoint_light][event.inovelli_up-state] (tests/components/matter/snapshots/test_event.ambr)
Re-run pytest with --snapshot-update to delete unused snapshots.

snapshots update

cd core
source .venv/bin/activate
pytest tests/components/matter/test_button.py --snapshot-update
pytest tests/components/matter/test_binary_sensor.py  --snapshot-update
pytest tests/components/matter/test_event.py  --snapshot-update
pytest tests/components/matter/test_number.py --snapshot-update
pytest tests/components/matter/test_select.py --snapshot-update
pytest tests/components/matter/test_sensor.py --snapshot-update

@lboue
Copy link
Contributor

lboue commented Jun 22, 2025

@jvmahon
There is something wrong with tests/conftest.py file in your branch. You should update your branch from orgin dev branch.
Do this I was able to update snapshots:

pytest tests/components/matter/test_button.py --snapshot-update
Test session starts (platform: linux, Python 3.13.3, pytest 8.4.0, pytest-sugar 1.0.0)
rootdir: /home/ludovic/ha_core
configfile: pyproject.toml
plugins: aiohttp-1.1.0, respx-0.22.0, anyio-4.9.0, syrupy-4.9.1, xdist-3.7.0, timeout-2.4.0, requests-mock-1.12.1, unordered-0.7.0, socket-0.7.0, cov-6.1.1, pytest_freezer-0.4.9, github-actions-annotate-failures-0.3.0, picked-0.5.1, sugar-1.0.0, asyncio-1.0.0
asyncio: mode=Mode.AUTO, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function
collected 55 items

 tests/components/matter/test_button.py ✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓                                                                               100% ██████████
---------------------------------------------------------------------------------- snapshot report summary ----------------------------------------------------------------------------------
82 snapshots passed. 8 snapshots generated. 8 unused snapshots deleted.

Deleted test_buttons[multi_endpoint_light][button.inovelli_config-entry], test_buttons[multi_endpoint_light][button.inovelli_config-state], test_buttons[multi_endpoint_light][button.inovelli_down-entry], test_buttons[multi_endpoint_light][button.inovelli_down-state], test_buttons[multi_endpoint_light][button.inovelli_up-entry], test_buttons[multi_endpoint_light][button.inovelli_up-state], test_buttons[temperature_sensor][button.mock_temperature_sensor_identify-entry], test_buttons[temperature_sensor][button.mock_temperature_sensor_identify-state] (tests/components/matter/snapshots/test_button.ambr)

Results (19.66s):
      55 passed

@jvmahon
Copy link
Contributor Author

jvmahon commented Jun 22, 2025

There is something wrong with tests/conftest.py file in your branch. You should update your branch from orgin dev branch.

Are you saying that, if I go to my branch: https://github.com/jvmahon/HomeAssistantCore/tree/Matter-labeling-fixes and do a "sync fork" that will fix the issue?

@lboue
Copy link
Contributor

lboue commented Jun 22, 2025

Are you saying that, if I go to my branch: https://github.com/jvmahon/HomeAssistantCore/tree/Matter-labeling-fixes and do a "sync fork" that will fix the issue?

Alternatively, you can create a new branch and copy the changes you've made. This gives you a clean start.
I tested the snapshot update with this commit:
lboue@ec78d32

Update with main branch
@jvmahon
Copy link
Contributor Author

jvmahon commented Jun 25, 2025

I tested the snapshot update with this commit:
I did the update to sync my branch. Hopefully it is OK now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants