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

Use DataUpdateCoordinator in Vallox #56966

Merged
merged 6 commits into from
Oct 24, 2021
Merged

Use DataUpdateCoordinator in Vallox #56966

merged 6 commits into from
Oct 24, 2021

Conversation

andre-richter
Copy link
Contributor

Breaking change

Proposed change

This PR replaces the State Proxy with a DataUpdateCoordinator, which allows to reduce the integration's code size by ~90 lines.

By staying true to the intended design of the DataUpdateCoordinator, we get a slight change in how a certain error case is handled:
If a state query to the Vallox device was successful, but one of the many metrics, or the fan profile (aka preset), isn't present at all or in an unexpected form, the integration now returns None for the respective entity's value or attribute, instead of setting the whole of the entity.available to false.

However, I haven't seen this corner case happening yet once. Usually, the Vallox device either responds to websocket requests, which means we get updates for all the metrics and the profile, or it doesn't respond. In the latter case, even with the DataUpdateCoordinator, we are still setting any dependent entity's availability to false.

Therefore, I didn't declare this PR as breaking, since I am not sure if this corner case of an error ever happens in reality. Even if it does, I don't think any existing installation will suffer from this change.

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)
  • 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:

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:

@andre-richter andre-richter requested a review from bdraco October 12, 2021 13:29
@andre-richter andre-richter mentioned this pull request Oct 15, 2021
22 tasks
@andre-richter
Copy link
Contributor Author

Hi @bdraco, are you planning to look at this PR or was your comment contained to the startup issue only?

Just trying to understand if I should request somebody else for review of the whole thing 👍

@bdraco
Copy link
Member

bdraco commented Oct 22, 2021

I have ~45 review requests ahead of this one.

@andre-richter
Copy link
Contributor Author

Failing in unrelated test.

Copy link
Member

@bdraco bdraco left a comment

Choose a reason for hiding this comment

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

Please see comments above

@andre-richter
Copy link
Contributor Author

Hi @bdraco,

thank you for your review, really appreciated!

  1. I refactored the Fan's extra state attribute code, it was a bit ugly and verbose anyways. Let me know what you think.
  2. Spammy log messages are downgraded to debug now.
  3. Regarding type annotations for DataUpdateCoordinator: Without them, we get mypy errors.
homeassistant/components/vallox/sensor.py:60: error: Returning Any from function declared to return "Union[None, str, int, float]"  [no-any-return]
homeassistant/components/vallox/fan.py:102: error: Returning Any from function declared to return "bool"  [no-any-return]

These are in native_value() and is_on(), respectively.

@bdraco
Copy link
Member

bdraco commented Oct 24, 2021

3. Regarding type annotations for DataUpdateCoordinator: Without them, we get mypy errors.

homeassistant/components/vallox/sensor.py:60: error: Returning Any from function declared to return "Union[None, str, int, float]"  [no-any-return]
homeassistant/components/vallox/fan.py:102: error: Returning Any from function declared to return "bool"  [no-any-return]

Maybe something like this will work (untested) bdraco@7c1c05f

Comment on lines 123 to 125
def check_and_convert(value: StateType) -> int | None:
if isinstance(value, (int, float)):
return int(value)
Copy link
Member

Choose a reason for hiding this comment

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

Please pull this out into a utility function. We don't want to embed functions if we can avoid it as it makes them more expensive to execute.

Also please name it to show intent

@bdraco
Copy link
Member

bdraco commented Oct 24, 2021

  • I refactored the Fan's extra state attribute code, it was a bit ugly and verbose anyways. Let me know what you think.

Looks a lot better. Small comment above to reduce the overhead and avoid the embedded function.

@bdraco
Copy link
Member

bdraco commented Oct 24, 2021

  • Spammy log messages are downgraded to debug now.

👍

@andre-richter
Copy link
Contributor Author

andre-richter commented Oct 24, 2021

Addressed both 👍

Out of curiosity: Why did you opt for the type annotation with the subclassing, instead of using the generic type parameter?

@bdraco
Copy link
Member

bdraco commented Oct 24, 2021

Out of curiosity: Why did you opt for the type annotation with the subclassing, instead of using the generic type parameter?

For consistency: We don't use the original method anywhere else in the codebase for the coordinator, and I think its better to be more explicit that the type is for the data

# Hence, wait for the started event before doing a first data refresh and loading the platforms,
# because it usually means the system is less busy after the event and can now meet the
# websocket timing requirements.
hass.bus.async_listen_once(
Copy link
Member

Choose a reason for hiding this comment

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

In the future by converting this to a config entry, you can do await coordinator.async_config_entry_first_refresh() which will automatically retry later if the device isn't ready

@bdraco bdraco merged commit 6c01ed8 into home-assistant:dev Oct 24, 2021
@andre-richter andre-richter deleted the vallox_data_update_coordinator branch October 24, 2021 21:22
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.

Please address the comments in a new PR. Thanks!

"""Set up the client and boot the platforms."""
conf = config[DOMAIN]
host = conf.get(CONF_HOST)
name = conf.get(CONF_NAME)
Copy link
Member

Choose a reason for hiding this comment

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

Host is required and name has a default value. Please use dict[key] when we know the key is in the dict.

if metric_key not in vlxDevConstants.__dict__:
raise KeyError(f"Unknown metric key: {metric_key}")
_LOGGER.debug("Metric key invalid: %s", metric_key)
Copy link
Member

Choose a reason for hiding this comment

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

We don't want side effects like logging in entity state properties. get_metric is called from is_on eg.

description="fan_speed_away", metric_key=METRIC_KEY_PROFILE_FAN_SPEED_AWAY
),
ExtraStateAttributeDetails(
description="fan_speed_boost", metric_key=METRIC_KEY_PROFILE_FAN_SPEED_BOOST
Copy link
Member

Choose a reason for hiding this comment

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

What's the set value of these extra attributes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure I understand the question. This Tulpe is iterated over down in the extra state attributes member function.

Copy link
Member

Choose a reason for hiding this comment

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

Yes. What's the measurement type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Integer, representing percent speed.

def native_value(self) -> StateType:
"""Return the value reported by the sensor."""
if (metric_key := self.entity_description.metric_key) is None:
_LOGGER.debug("Error updating sensor. Empty metric key")
Copy link
Member

Choose a reason for hiding this comment

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

Please move or remove the logging.

@github-actions github-actions bot locked and limited conversation to collaborators Oct 26, 2021
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