Skip to content

Conversation

@epenet
Copy link
Contributor

@epenet epenet commented Sep 12, 2025

Proposed change

SSIA

If we want to use the name of the device class, then it is simple to remove the fallback_name from the quirk

Note I am aware of zigpy/zha#525 and zigpy/zha#533, but I think this change makes sense regardless.

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 @dmulcahey, @Adminiuga, @puddly, @TheJulianJES, mind taking a look at this pull request as it has been labeled with an integration (zha) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of zha 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 zha 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.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR modifies the ZHA entity naming logic to prioritize fallback names from quirks over device class names. The change ensures that when a device quirk explicitly sets a fallback_name, it takes precedence over auto-generated names from device classes.

  • Updates the name property to check translation key availability before applying fallback names
  • Removes unused import UNDEFINED from typing module
  • Adds logic to only use fallback_name when the translation_key is not found in platform translations

self._attr_name = meta.fallback_name
# The fallback name takes priority over the device class name.
if (
(translation_key := self._name_translation_key)
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

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

Potential AttributeError if self.platform_data is None or doesn't have a platform_translations attribute. Consider adding a guard to ensure these attributes exist before accessing them.

Suggested change
(translation_key := self._name_translation_key)
(translation_key := self._name_translation_key)
and self.platform_data is not None
and hasattr(self.platform_data, "platform_translations")

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think this applies to ZHA

Copy link
Member

@TheJulianJES TheJulianJES left a comment

Choose a reason for hiding this comment

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

If we want to use the name of the device class, then it is simple to remove the fallback_name from the quirk

The fallback name is required to exist for all v2 quirks to identify what the entity does when just looking at the quirks v2 definition. And it may always be used by other projects that don’t have a project of device classes with names.
We also use it to generate the default US English translations when bumping the ZHA library in HA (if a translation key is provided only).
So, the fallback name should only ever directly be used for custom quirks.

The behavior we have at the moment works fine for built-in quirks and is like this:

  1. translation key value (if translation key is set)
  2. device class name
  3. fallback name, should never happen with built-in quirks, since we enforce setting a translation_key or device_class in v2 quirks. So, this only applies to custom quirks that don’t set a device class. But that’s also where the issue (you’re trying to fix here) lies, since the device class will unexpectedly take precedence over the fallback name, even if a translation key is set (meaning a non-device class name should be used), only for custom quirks.

So, if we want a quirks v2 entity to assume the name of the device class, no translation key should be set. The fallback name will still be available internally, but should not be used (if no translation key is set).

To fix this and keep the current structure in quirks, we‘d need to just use the fallback name if a translation key is provided in the first place AND if no translation was found before. If translation key is provided, the device class should be used instead.

There’s some more explanation on the behavior here (and other stuff): zigpy/zha#527

We should ideally also get some tests in HA that verify the correct behavior for this. I should be able to do that soon.

@TheJulianJES TheJulianJES marked this pull request as draft September 12, 2025 16:50
@TheJulianJES
Copy link
Member

TheJulianJES commented Sep 12, 2025

I have't checked this at all and it's just quickly thrown together, but something along these lines might work:

    @cached_property
    def name(self) -> str | UndefinedType | None:
        """Return the name of the entity."""
        meta = self.entity_data.entity.info_object
        if meta.primary:
            self._attr_name = None
            return super().name

        if meta.translation_key is not None or super().name in (UNDEFINED, None):  # revision: changed condition
            # if we have a translation for this key, use it
            if (
                (translation_key := self._name_translation_key) is not None
                and translation_key in self.platform_data.platform_translations
            ):
                return super().name
            # a translation won't be available for custom quirks, so use fallback_name
            if meta.fallback_name is not None:
                self._attr_name = meta.fallback_name
                return super().name

        # we should only reach this for the device class name at this point
        return super().name

I should be able to get back to to this in a few hours or tomorrow the next few days. Feel free to play around or ask questions.

@epenet
Copy link
Contributor Author

epenet commented Sep 15, 2025

I should be able to get back to to this in a few hours or tomorrow the next few days. Feel free to play around or ask questions.

Your version breaks the friendly_name test in test_number.py
It doesn't have a translation_key nor a device_class, but it has a fallback_name from the endpoint/cluster description

FAILED tests/components/zha/test_number.py::test_number - AssertionError: assert 'FakeManufact...akeModel None' == 'FakeManufact...akeModel PWM1'

@TheJulianJES
Copy link
Member

TheJulianJES commented Sep 15, 2025

It doesn't have a translation_key nor a device_class, but it has a fallback_name from the endpoint/cluster description

Ah, yeah. Thanks for testing. I did not think of that case for this. The linked zigpy issue for the "forced name" separation from fallback name does have a purpose after all. 😅

When we do that, we can also introduce a bool (or enum?) attr on the ZHA lib entity side that HA can directly use to see which name type has to be used (translation/fallback name OR device class name).

As a workaround, until that's all in place and done cleanly on the ZHA side, we might be able to change the condition for the translation/fallback name block to if meta.translation_key is not None or super().name in (UNDEFINED, None): in the code snippet if above.
I've adjusted the snippet in my comment above to include that. (Should likely just put the super name output into a variable to avoid calling it twice if it's a str/device class, but it's just for testing for now. If you can get to test that already, please let me know the results as well.)

@epenet
Copy link
Contributor Author

epenet commented Sep 15, 2025

It doesn't have a translation_key nor a device_class, but it has a fallback_name from the endpoint/cluster description

Ah, yeah. Thanks for testing. I did not think of that case for this. The linked zigpy issue for the "forced name" separation from fallback name does have a purpose after all. 😅

When we do that, we can also introduce a bool (or enum?) attr on the ZHA lib entity side that HA can directly use to see which name type has to be used (translation/fallback name OR device class name).

As a workaround, until that's all in place and done cleanly on the ZHA side, we might be able to change the condition for the translation/fallback name block to if meta.translation_key is not None or super().name in (UNDEFINED, None): in the code snippet if above. I've adjusted the snippet in my comment above to include that. (Should likely just put the super name output into a variable to avoid calling it twice if it's a str/device class, but it's just for testing for now. If you can get to test that already, please let me know the results as well.)

I think your condition is wrong, as super().name will return the name from the device class, and so will never use the fallback name.

AFAICT, that still doesn't allow us to set both a custom name AND a device class

@TheJulianJES
Copy link
Member

I think your condition is wrong, as super().name will return the name from the device class, and so will never use the fallback name.

It was added in response to the test failure you reported (listed below) to make sure we don't get None as the name for entities that neither have a translation_key nor a device_class.

Previously, a translation key had to be present for the entity to have the fallback_name assigned (if the translation isn't present due to it being loaded as a custom quirk).
Now, we'll also return the fallback_name in that block if super().name is None, which it should be, since no device class seems to be defined in the test. Since no translation (key) is present, it should fall back to fallback_name, which is the description attribute for those devices.

Your version breaks the friendly_name test in test_number.py
It doesn't have a translation_key nor a device_class, but it has a fallback_name from the endpoint/cluster description

FAILED tests/components/zha/test_number.py::test_number - AssertionError: assert 'FakeManufact...akeModel None' == 'FakeManufact...akeModel PWM1'

AFAICT, that still doesn't allow us to set both a custom name AND a device class

As long as a translation_key is set, the custom fallback name should now be used (even if the translation isn't present in HA) , from what I can see at least. The device class name should not be used in that case.

I think it should work, but I'll definitely take an actual look in the coming days or weeks at the latest.

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.

6 participants