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 deterministic units #70

Merged
merged 5 commits into from
Jul 31, 2022
Merged

Conversation

VadimKraus
Copy link
Contributor

possible solution for #62

After this PR:
Units need to be defined using the enum Units.
The classes Measurement and Total specify if the values are measurements with arbitrary content or if the continuously summing up (Total).

A sensor_mapping tuple can have multiple variations:

'Network Voltage Phase 1':   1, # == (1, Measurement(Units.UNKNOWN))
'Network Voltage Phase 2':    (2,Units.V), # ==(2,Measurement(Units.V))
'Total Energy':                          (3,Total(Units.KWH)),
'Total Energy Consumption':              (4,Total(Units.KWH), some_post_processing),

If only an index is provided, will assume that this is a measurement with an unknown unit.
If only an unit enum value is provided, will assume that that this is a measurement with the provided unit.
Using the Measurement or Total classes it is possible to define the behavior explicitly.

Consequences for home assistant

this change is NOT backwards compatible!

See VadimKraus/core@1ba0304 for how the changes would need to be reflected in the integration.
Basically, by type checking for the Total type, it is clear with sensor is of type total_increasing

solax/units.py Outdated
C = "°C"
HZ = "Hz"
PERCENT = "%"
UNKNOWN = ""
Copy link
Owner

Choose a reason for hiding this comment

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

We probably want to call this some variant of "none", since it's not undefined- we just know it doesn't have a unit.

'Today\'s Feed-in Energy': (90, 'kWh', div100),
'Total Feed-in Energy': (86, Total(Units.KWH),
feedin_energy),
'Total Feed-in Energy Resets': (87),
Copy link
Owner

Choose a reason for hiding this comment

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

did you miss some 'none' / 'unknown' units here?

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 mapping is able to handle a variation of fallbacks/defaults
'Total Feed-in Energy Resets': (87), is equivalent to
'Total Feed-in Energy Resets': 87, and
'Total Feed-in Energy Resets': (87,Units.UNKNOWN), or
'Total Feed-in Energy Resets': (87,Measurment(Units.UNKNOWN)),

I did not remove the brackets, so that is somewhat visually consistent

@squishykid
Copy link
Owner

Also, thank you for adding a commend about backwards compatibility.

@squishykid
Copy link
Owner

I should be able to get this merged on the weekend

@squishykid
Copy link
Owner

I might try and follow up this PR with one containing a test to ensure these units are used for new inverters and not constants

@VadimKraus
Copy link
Contributor Author

I might try and follow up this PR with one containing a test to ensure these units are used for new inverters and not constants

tests/test_smoke.py contains a test, which should ensure the use of units in all inverters.

@squishykid
Copy link
Owner

squishykid commented Jul 31, 2022

Going to rebase this with the black/mypy changes

@squishykid squishykid merged commit 4c4bc83 into squishykid:master Jul 31, 2022
@VadimKraus VadimKraus deleted the feature/units branch July 31, 2022 11:34
C = "°C"
HZ = "Hz"
PERCENT = "%"
NONE = ""
Copy link
Contributor

Choose a reason for hiding this comment

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

As a physicist, I would say that the correct name for units of a pure number quantity is "dimensionless"


if isinstance(mapping, tuple):
(idx, unit_or_measurement, *_) = mapping
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

This else clause is never hit - coverage is complaining at me in #77 about this line never executing. A grep finds no inverter subclasses that don't give units for values.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK - this code does use this for the "resets" values, which I've removed in 77. Sorry for false alarm

if processor:
sensors[name] = processor[0]
for name, mapping in cls.response_decoder().items():
if isinstance(mapping, tuple):
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above - cov complains about not all branches taken

@@ -18,8 +20,9 @@ class Inverter:
"""Base wrapper around Inverter HTTP API"""

ResponseDecoderType = Union[
Dict[str, Tuple[int, str]],
Dict[str, Tuple[int, str, Callable[[Any, Any, Any], Any]]],
Dict[str, int],
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this not have the union in the value part? i.e.

ResponseDecoderType = Dict[str, Union[int, Tuple[int, SensorUnit]. Tuple[int, SensorUnit, Callable]]]

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

Successfully merging this pull request may close these issues.

3 participants