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

Allow per-sensor unit conversion on BMW sensors #110272

Merged
merged 20 commits into from
Jun 4, 2024

Conversation

rikroe
Copy link
Contributor

@rikroe rikroe commented Feb 11, 2024

Proposed change

This PR updates the BMW sensors to use the default unit conversion in HomeAssistant via device_class and native_unit_of_measure.

The precision of the conversion to imperial units has been removed from the tests, but works on a per-entity basis if #110270 is merged.

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
  • 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.
  • Untested files have been added to .coveragerc.

To help with the load of incoming pull requests:

@home-assistant
Copy link

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

Code owner commands

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

@rikroe rikroe force-pushed the bmw-update-entity-descriptions branch 2 times, most recently from 8c3c565 to 5c422a9 Compare March 7, 2024 21:54
@SGXander
Copy link

SGXander commented Mar 22, 2024

Is the below review requirement from HA staff or you @rikroe ?

@rikroe
Copy link
Contributor Author

rikroe commented Mar 22, 2024

Not sure If I follow. This PR can only be merged by the HA team and unfortunately they haven't had time to review it (if thats what you asked).

@rikroe rikroe force-pushed the bmw-update-entity-descriptions branch from 5c422a9 to f797be2 Compare April 8, 2024 19:33
@rjenx
Copy link

rjenx commented Apr 9, 2024

Any idea when this will be implemented?

)
if isinstance(state, (ValueWithUnit, Enum)):
state = state.value
self._attr_native_value = cast(StateType, state)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think you need to cast

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, removed it.

@@ -85,6 +88,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by MyBMW',
'device_class': 'distance',
Copy link
Member

Choose a reason for hiding this comment

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

I would expect it would look like this

Suggested change
'device_class': 'distance',
'device_class': '<Sensor device class=distance,>',

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seems to be coming up only when iterating over config entries as done e.g. in the airly tests.

I don't think it makes a difference.

@@ -34,17 +34,17 @@ async def test_entity_state_attrs(
("entity_id", "unit_system", "value", "unit_of_measurement"),
[
("sensor.i3_rex_remaining_range_total", METRIC, "279", "km"),
("sensor.i3_rex_remaining_range_total", IMPERIAL, "173.36", "mi"),
("sensor.i3_rex_remaining_range_total", IMPERIAL, "173", "mi"),
Copy link
Member

Choose a reason for hiding this comment

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

Why remove the rest? Might as well include it now and use suggested_display_precision in the entity descriptions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Set the display precision, but now had to add the full length converted value, as HA will print the full float as string state value.

@home-assistant
Copy link

home-assistant bot commented Apr 9, 2024

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@home-assistant home-assistant bot marked this pull request as draft April 9, 2024 06:25
@rikroe rikroe marked this pull request as ready for review April 10, 2024 18:16
@home-assistant home-assistant bot requested a review from joostlek April 10, 2024 18:16
@rikroe rikroe force-pushed the bmw-update-entity-descriptions branch from fb0c603 to d6b137f Compare April 17, 2024 19:30
@@ -83,83 +73,96 @@ def convert_and_round(
key="charging_status",
translation_key="charging_status",
key_class="fuel_and_battery",
value=lambda x, y: x.value,
device_class=SensorDeviceClass.ENUM,
options=[s.value.lower() for s in ChargingState if s != ChargingState.UNKNOWN],
Copy link
Member

@joostlek joostlek Apr 17, 2024

Choose a reason for hiding this comment

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

@MartinHjelmare Usually we discourage developers to do this because "what if the library changes?", but in this case the options are fixated in the snapshots, so whenever the enum changes and the library is bumped (without regenerating the snapshots) tests would fail. So the only thing left is that the reviewer should know that a string should be added to the strings.

https://github.com/home-assistant/core/pull/110272/files#diff-3cb4a00db27894cb9e17559dd978b695621d9a673c6899905f94f839e97910ebR22-R35

Copy link
Member

Choose a reason for hiding this comment

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

I still don't think it's a good idea to take that approach. The same reasoning still applies. The integration should blow up if an option is missing in the translations.

Copy link
Member

Choose a reason for hiding this comment

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

But that can also happen if someone just adds a value to "a random list in const.py" without adding the transition key? It both comes down to the alertness of the reviewer on this. (And imo, with these snapshots it's even more clear what this change is and that a key should be added)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The integration should blow up if an option is missing in the translations.

I implemented a separate test only for the translations to make it clear after a library update if translations are missing (see latest commit).
Then users would not get error messages it is already part of the testing pipeline.

If a translation key is missing (or malformed), pytest assertion would throw an error similar to this:

>       assert all_sensor_options == all_translation_options
E       AssertionError: assert {'component.b...ot_full', ...} == {'component.b...ot_full', ...}
E         
E         Extra items in the left set:
E         'component.bmw_connected_drive.entity.sensor.charging_status.state.error'
E         Extra items in the right set:
E         'component.bmw_connected_drive.entity.sensor.charging_status.state.error1'
E         Use -v to get more diff

Copy link
Member

Choose a reason for hiding this comment

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

Oh you added a full test for it, my point was more saying that having a way to fixate the options list somewhere would suffice instead of duplicating all enums.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wondering what I can do for this to go through?
Just pinning the enum sensors?

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 have reverted (i.e. pinned) the Enum values so this PR can be (hopefully) merged.

I might create another draft PR with a corresponding architecture discussion as I think that Enum values from a library should be allowed, provided there are tests if all translations are available.

@rikroe rikroe requested a review from joostlek June 3, 2024 18:07
Comment on lines 82 to 93
ChargingState.DEFAULT,
ChargingState.CHARGING,
ChargingState.ERROR,
ChargingState.COMPLETE,
ChargingState.FULLY_CHARGED,
ChargingState.FINISHED_FULLY_CHARGED,
ChargingState.FINISHED_NOT_FULL,
ChargingState.INVALID,
ChargingState.NOT_CHARGING,
ChargingState.PLUGGED_IN,
ChargingState.WAITING_FOR_CHARGING,
ChargingState.TARGET_REACHED,
Copy link
Member

Choose a reason for hiding this comment

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

Which one is missing?

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 if I follow. The only Enum not in this list is ChargingState.UNKNOWN as this is not a valid state and (from previous comments) should be None.

Copy link
Member

Choose a reason for hiding this comment

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

But now we fixate the options in the snapshot, so we can do [x.lower() for x in ChargingState where ChargingState is not CharginState.UNKNOWN]

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'm sorry, I thought the discussion about this with @MartinHjelmare (#110272 (comment)) was the reason this PR was stuck.

Thats why I reverted this in 947c8b8.

Copy link
Member

Choose a reason for hiding this comment

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

Yes that was the original point of the PR, but I still believe this is a non-issue because we got it covered. I spoke Frenck today and he was like "why not"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Many thanks! Added the list comprehensions back in.

Comment on lines 182 to 185
ClimateActivityState.COOLING,
ClimateActivityState.HEATING,
ClimateActivityState.INACTIVE,
ClimateActivityState.STANDBY,
Copy link
Member

Choose a reason for hiding this comment

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

idem

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as ChargingState.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reverted

"finished_not_full": "Finished, not full",
"invalid": "Invalid",
"not_charging": "Not charging",
"plugged_in": "Plugged Ii",
Copy link
Member

Choose a reason for hiding this comment

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

typo?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, fixed

Comment on lines 106 to 107
"fully_charged": "Fully charged",
"finished_fully_charged": "Fully charged",
Copy link
Member

Choose a reason for hiding this comment

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

Having 2 the same states doesn't make a lot of sense imo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah - the issue is we don't really know which of these enums could occur, they are collected over multiple years.
Therefore I decided in this case to combine both, but I can also have different translations if you feel this is needed.

Copy link
Member

Choose a reason for hiding this comment

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

The thing I am worried about is that with this change when you edit an automation and select a possible state, you see "Fully charged" twice and never know which one is triggered when. So imagine created an automation for Fully charged 1, and then the car actually displays 2, that's strange.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

+1, I agree on this.

I'll translate it for now and then have to think later how to deal with this ever growing list.

@rikroe
Copy link
Contributor Author

rikroe commented Jun 3, 2024

As discussed with @joostlek, I have remove (almost) all changes regarding sensor enums for two reasons:

  • Ongoing discussions on translations/enum options
  • (more importantly) previous changes would have lead to a breaking change due to lowercase states (this forces some temporary special handling that will be removed soon)

While I know that the code does not fully adhere to standards, it is not worse than before. I'll create a seperate PR to address the sensor enum issues separately.

@rikroe rikroe mentioned this pull request Jun 3, 2024
20 tasks
@joostlek joostlek added this to the 2024.6.0 milestone Jun 4, 2024
@joostlek joostlek merged commit 7fb2802 into home-assistant:dev Jun 4, 2024
24 checks passed
@joostlek joostlek modified the milestone: 2024.6.0 Jun 4, 2024
@frenck frenck modified the milestone: 2024.6.0 Jun 4, 2024
frenck pushed a commit that referenced this pull request Jun 4, 2024
* Update BMW sensors to use device_class

* Test adjustments

* Trigger CI

* Remove unneeded cast

* Set suggested_display_precision to 0

* Rebase for climate_status

* Change charging_status to ENUM device class

* Add test for Enum translations

* Pin Enum sensor values

* Use snapshot_platform helper

* Remove translation tests

* Formatting

* Remove comment

* Use const.STATE_UNKOWN

* Fix typo

* Update strings

* Loop through Enum sensors

* Revert enum sensor changes

---------

Co-authored-by: Richard <rikroe@users.noreply.github.com>
dgomes pushed a commit to dgomes/home-assistant that referenced this pull request Jun 4, 2024
* Update BMW sensors to use device_class

* Test adjustments

* Trigger CI

* Remove unneeded cast

* Set suggested_display_precision to 0

* Rebase for climate_status

* Change charging_status to ENUM device class

* Add test for Enum translations

* Pin Enum sensor values

* Use snapshot_platform helper

* Remove translation tests

* Formatting

* Remove comment

* Use const.STATE_UNKOWN

* Fix typo

* Update strings

* Loop through Enum sensors

* Revert enum sensor changes

---------

Co-authored-by: Richard <rikroe@users.noreply.github.com>
@github-actions github-actions bot locked and limited conversation to collaborators Jun 5, 2024
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.

BMW Connected - unable to change individual sensor units
6 participants