Skip to content
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

Add options to SelectEntityDescription #78882

Merged
merged 15 commits into from
Oct 10, 2022

Conversation

epenet
Copy link
Contributor

@epenet epenet commented Sep 21, 2022

Breaking change

FOR CUSTOM COMPONENTS ONLY:

As of Home Assistant Core 2022.11, options is available as a standard property of SelectEntityDescription.
This may cause issues in custom components if a custom options property was previously implemented.

Please adjust the custom component by either dropping or renaming the custom options property.

Proposed change

Add options to SelectEntityDescription

I was originally surprised that options was not part of the standard SelectEntityDescription and I thought it was missed in the initial implementation.
Then I bumped into the issue that "we don't want a default" so I backed out.
Then I received the request from @MartinHjelmare in #78873 (comment) so I worked on it again and found a (maybe not pretty) solution.

Looking at the SelectEntityDescription in the core, it looks like more components would also benefit:

  • kostal_plenticore, overkiz, renault and xiaomi_miio use a custom options property that is updated in this PR
  • goodwe, lifx and senseme use a local constant which could be moved to the SelectEntityDescription
  • litterrobot, roku, sensibo, tuya and unifiprotect use a dynamic list, and would not be impacted by this PR
  • plugwise uses options in a weird way, adjusted in Rename property in Plugwise EntityDescription #78935
  • rainmachine uses options in a weird way, adjusted in Rename options key in rainmachine #79249

So 7 out of 14 components would directly benefit from this, if it is accepted.

HACS:

These use a custom options property that replicates this PR

These do not use a custom options property and are not impacted by the change

  • DeebotUniverse/Deebot-4-Home-Assistant
  • MTrab/landroid_cloud
  • dmamontov/hass-ledfx
  • dmamontov/hass-miwifi
  • eifinger/hass-foldingathomecontrol
  • eifinger/hass-weenect
  • mletenay/home-assistant-goodwe-inverter-
  • music-assistant/hass-music-assistant
  • wlcrs/huawei_solar

If it is not accepted, then I am happy to close (and maybe adjust the seven components to use the same code style).

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

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
  • The code has been formatted using Black (black --fast 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.
  • Untested files have been added to .coveragerc.

The integration reached or maintains the following Integration Quality Scale:

  • No score or internal
  • 🥈 Silver
  • 🥇 Gold
  • 🏆 Platinum

To help with the load of incoming pull requests:

@probot-home-assistant
Copy link

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

homeassistant/components/select/__init__.py Outdated Show resolved Hide resolved
@@ -72,6 +72,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class SelectEntityDescription(EntityDescription):
"""A class that describes select entities."""

options: list[str] | UndefinedType = UNDEFINED
Copy link
Member

Choose a reason for hiding this comment

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

Can we skip the UndefinedType to make it easier with type checking in all the platforms that use this description attribute? UNDEFINED is just needed here in the integration to make it optional with a default.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, using type: ignore

@@ -25,7 +25,7 @@ class PlugwiseSelectDescriptionMixin:

command: Callable[[Smile, str, str], Awaitable[Any]]
current_option: str
options: str
options_key: str
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a risk of breaking change for components that have the options key already defined.

Copy link
Member

Choose a reason for hiding this comment

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

We should perhaps make a dev blog, and search HACS for matches?

homeassistant/components/select/__init__.py Outdated Show resolved Hide resolved
@@ -72,6 +72,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class SelectEntityDescription(EntityDescription):
"""A class that describes select entities."""

options: list[str] | UndefinedType = UNDEFINED
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, using type: ignore

Copy link
Member

@MartinHjelmare MartinHjelmare left a comment

Choose a reason for hiding this comment

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

@frenck what do you think?

@@ -72,6 +72,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class SelectEntityDescription(EntityDescription):
"""A class that describes select entities."""

options: list[str] = UNDEFINED # type: ignore[assignment]
Copy link
Member

@frenck frenck Sep 21, 2022

Choose a reason for hiding this comment

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

This feels way too hacky for my taste (messing with the typing that is).

Copy link
Member

Choose a reason for hiding this comment

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

It's convenience. Same reason why we ignore type for hass in Entity.

Copy link
Member

@frenck frenck Oct 10, 2022

Choose a reason for hiding this comment

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

What we do in hass is a very exceptional case.
In this case, options cannot be None, so we don't need to use sentinel values.

As in: We don't need to "hacky" around it or use typing exceptions to make this work nicely.

Suggested change
options: list[str] = UNDEFINED # type: ignore[assignment]
options: list[str] | None = None

EDIT: this comment looks weird, as its shown as part of a review, there is more context above. This was original a response to Martin.

@frenck
Copy link
Member

frenck commented Sep 21, 2022

I'm kinda lost on why we do this? (Motivation? SSIA doesn't help in this case).
As in, EntityDescription are pretty much limited to their basic, descriptive, properties, not to their options/data.

Adding this level of things to the entity descriptions by default, opens up a can of worms we originally avoided. For example, effects in lights, HVAC modes and many others.

It could be fine to implement those as well, but I think it should be consistent if we do that (and not just one off).

@epenet
Copy link
Contributor Author

epenet commented Sep 21, 2022

I'm kinda lost on why we do this? (Motivation? SSIA doesn't help in this case).

I have adjusted the PR description. It originated from #78873 (comment)

@epenet epenet marked this pull request as draft September 21, 2022 13:04
@MartinHjelmare
Copy link
Member

We already use the descriptions for many different kind of attributes in the implementing integrations. I don't think the can of worms is closed.

@frenck
Copy link
Member

frenck commented Sep 21, 2022

We already use the descriptions for many different kind of attributes in the implementing integrations.

Yeah sure, they can extend it they way they like.

@epenet epenet marked this pull request as ready for review September 21, 2022 15:05
@epenet
Copy link
Contributor Author

epenet commented Sep 21, 2022

I was surprised that options was not part of the standard SelectEntityDescription and I thought it was missed in the initial implementation.
Then I bumped into the issue that "we don't want a default" so I backed out.
Then I received the request from @MartinHjelmare so I worked on it again and found a (maybe not pretty) solution.

Looking at the SelectEntityDescription in the core, it looks like more components would also benefit:

  • kostal_plenticore, overkiz, renault and xiaomi_miio use a custom options property that it updated in this PR
  • goodwe, lifx and senseme use a local constant which could be moved to the SelectEntityDescription
  • litterrobot, roku, sensibo, tuya and unifiprotect use a dynamic list, and would not be impacted by this PR
  • plugwise uses options in a weird way

So 7 out of 13 components would directly benefit from this, if it is accepted.

If it is not accepted, then I am happy to close (and maybe adjust the seven components to use the same code style).

@bouwew
Copy link
Contributor

bouwew commented Sep 22, 2022

@epenet for plugwise, I would suggest changing current_option: str to current_option_key: str as well. And the occurrences further down as well.
current_option is used as a key, like the updated option_key.

In plugwise number.py the use of the addition _key was already implemented. Select.py was on the todo-list, but since you're already updating things, let's update all :)

@epenet
Copy link
Contributor Author

epenet commented Sep 22, 2022

@epenet for plugwise, I would suggest changing current_option: str to current_option_key: str as well. And the occurrences further down as well. current_option is used as a key, like the updated option_key.

In plugwise number.py the use of the addition _key was already implemented. Select.py was on the todo-list, but since you're already updating things, let's update all :)

I am not sure if this PR will be accepted or not. I think plugwise should be adjusted regardless to avoid confusion and I have opened the corresponding PR: #78935

@frenck frenck self-requested a review September 26, 2022 08:54
@epenet
Copy link
Contributor Author

epenet commented Sep 28, 2022

There was a conflict... solved now.

@frenck
Copy link
Member

frenck commented Sep 28, 2022

👍 Will be getting back to this after the beta stuff is out.

@epenet
Copy link
Contributor Author

epenet commented Sep 28, 2022

rainmachine has crept in - I'll have to look at that tomorrow

@epenet
Copy link
Contributor Author

epenet commented Oct 10, 2022

I have rebased over dev to pick-up lametric which also benefits from this change.

@frenck
Copy link
Member

frenck commented Oct 10, 2022

That is the reason why I didn't implement current_option which gets adjusted throughout the life of the entity.

Sure, but it can provide a default. When setting the entity description options, it still leaves room for adjusting it during the lifetime, for example, by setting self._attr_options during an update.

@epenet
Copy link
Contributor Author

epenet commented Oct 10, 2022

That is the reason why I didn't implement current_option which gets adjusted throughout the life of the entity.

Sure, but it can provide a default. When setting the entity description options, it still leaves room for adjusting it during the lifetime, for example, by setting self._attr_options during an update.

I have added it because it was requested, but I couldn't find a use case for it in the core.

@frenck
Copy link
Member

frenck commented Oct 10, 2022

I've seen you've applied current_option already, but I would love to hear the opinion of @MartinHjelmare on this as well.

I have added it because it was requested, but I couldn't find a use case for it in the core.

Well, that might be a sign not to do it 🤷
That said, select isn't a widely used platform. The default state of, e.g., switch or binary_sensors are more common maybe.

@MartinHjelmare
Copy link
Member

I think it's good to be consistent and have attributes for all properties in the descriptions but if we don't see a use case in the current code we could wait with adding it.

@epenet
Copy link
Contributor Author

epenet commented Oct 10, 2022

I think it's good to be consistent and have attributes for all properties in the descriptions but if we don't see a use case in the current code we could wait with adding it.

I agree that all properties in the description should have corresponding attributes, and I think that's already the case.
Conversely however, I think only static attributes should have corresponding properties in the description.

@frenck
Copy link
Member

frenck commented Oct 10, 2022

Conversely however, I think only static attributes should have corresponding properties in the description.

In that case, this PR is invalid, as options doesn't have to be static...

@epenet
Copy link
Contributor Author

epenet commented Oct 10, 2022

Point taken. I should have phrased it as static or usually unchanged after initialisation

Copy link
Member

@frenck frenck left a comment

Choose a reason for hiding this comment

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

LGTM

@MartinHjelmare Do you agree on the typing change that I suggested (and has been applied by @epenet)?

Copy link
Member

@MartinHjelmare MartinHjelmare left a comment

Choose a reason for hiding this comment

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

Looks good!

@epenet
Copy link
Contributor Author

epenet commented Oct 10, 2022

I have added a breaking change section (for custom component developers) and a blog post PR.
Once this is merged I will check again all the HACS components.

@frenck
Copy link
Member

frenck commented Oct 10, 2022

Thanks, @epenet 👍

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

Successfully merging this pull request may close these issues.

5 participants