Add mWh as unit of measurement for Matter energy sensors (#133005)

This commit is contained in:
Stefan Agner 2024-12-13 09:33:58 +01:00 committed by GitHub
parent a0e49ebc97
commit 9ab69aa41c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 28 additions and 9 deletions

View File

@ -612,11 +612,12 @@ DISCOVERY_SCHEMAS = [
key="ElectricalEnergyMeasurementCumulativeEnergyImported",
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
native_unit_of_measurement=UnitOfEnergy.MILLIWATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=3,
state_class=SensorStateClass.TOTAL_INCREASING,
# id 0 of the EnergyMeasurementStruct is the cumulative energy (in mWh)
measurement_to_ha=lambda x: x.energy / 1000000,
measurement_to_ha=lambda x: x.energy,
),
entity_class=MatterSensor,
required_attributes=(

View File

@ -163,7 +163,7 @@ class NumberDeviceClass(StrEnum):
ENERGY = "energy"
"""Energy.
Unit of measurement: `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `MJ`, `GJ`
Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal`
"""
ENERGY_STORAGE = "energy_storage"
@ -172,7 +172,7 @@ class NumberDeviceClass(StrEnum):
Use this device class for sensors measuring stored energy, for example the amount
of electric energy currently stored in a battery or the capacity of a battery.
Unit of measurement: `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `MJ`, `GJ`
Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal`
"""
FREQUENCY = "frequency"

View File

@ -106,8 +106,12 @@ def _validate_unit(options: dict[str, Any]) -> None:
and (units := DEVICE_CLASS_UNITS.get(device_class))
and (unit := options.get(CONF_UNIT_OF_MEASUREMENT)) not in units
):
# Sort twice to make sure strings with same case-insensitive order of
# letters are sorted consistently still (sorted() is guaranteed stable).
sorted_units = sorted(
[f"'{unit!s}'" if unit else "no unit of measurement" for unit in units],
sorted(
[f"'{unit!s}'" if unit else "no unit of measurement" for unit in units],
),
key=str.casefold,
)
if len(sorted_units) == 1:

View File

@ -191,7 +191,7 @@ class SensorDeviceClass(StrEnum):
Use this device class for sensors measuring energy consumption, for example
electric energy consumption.
Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal`
Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal`
"""
ENERGY_STORAGE = "energy_storage"
@ -200,7 +200,7 @@ class SensorDeviceClass(StrEnum):
Use this device class for sensors measuring stored energy, for example the amount
of electric energy currently stored in a battery or the capacity of a battery.
Unit of measurement: `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `MJ`, `GJ`
Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal`
"""
FREQUENCY = "frequency"

View File

@ -235,8 +235,12 @@ def _validate_unit(options: dict[str, Any]) -> None:
and (units := DEVICE_CLASS_UNITS.get(device_class)) is not None
and (unit := options.get(CONF_UNIT_OF_MEASUREMENT)) not in units
):
# Sort twice to make sure strings with same case-insensitive order of
# letters are sorted consistently still.
sorted_units = sorted(
[f"'{unit!s}'" if unit else "no unit of measurement" for unit in units],
sorted(
[f"'{unit!s}'" if unit else "no unit of measurement" for unit in units],
),
key=str.casefold,
)
if len(sorted_units) == 1:

View File

@ -619,6 +619,7 @@ class UnitOfEnergy(StrEnum):
KILO_JOULE = "kJ"
MEGA_JOULE = "MJ"
GIGA_JOULE = "GJ"
MILLIWATT_HOUR = "mWh"
WATT_HOUR = "Wh"
KILO_WATT_HOUR = "kWh"
MEGA_WATT_HOUR = "MWh"

View File

@ -266,6 +266,7 @@ class EnergyConverter(BaseUnitConverter):
UnitOfEnergy.KILO_JOULE: _WH_TO_J,
UnitOfEnergy.MEGA_JOULE: _WH_TO_J / 1e3,
UnitOfEnergy.GIGA_JOULE: _WH_TO_J / 1e6,
UnitOfEnergy.MILLIWATT_HOUR: 1e6,
UnitOfEnergy.WATT_HOUR: 1e3,
UnitOfEnergy.KILO_WATT_HOUR: 1,
UnitOfEnergy.MEGA_WATT_HOUR: 1 / 1e3,

View File

@ -1543,6 +1543,9 @@
'sensor': dict({
'suggested_display_precision': 3,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
@ -2480,6 +2483,9 @@
'sensor': dict({
'suggested_display_precision': 3,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,

View File

@ -804,7 +804,7 @@ EARLY_END_ERROR = "invalid template (TemplateSyntaxError: unexpected 'end of tem
),
"unit_of_measurement": (
"'None' is not a valid unit for device class 'energy'; "
"expected one of 'cal', 'Gcal', 'GJ', 'GWh', 'J', 'kcal', 'kJ', 'kWh', 'Mcal', 'MJ', 'MWh', 'TWh', 'Wh'"
"expected one of 'cal', 'Gcal', 'GJ', 'GWh', 'J', 'kcal', 'kJ', 'kWh', 'Mcal', 'MJ', 'MWh', 'mWh', 'TWh', 'Wh'"
),
},
),

View File

@ -441,6 +441,8 @@ _CONVERTED_VALUE: dict[
(5, UnitOfElectricPotential.MICROVOLT, 5e-6, UnitOfElectricPotential.VOLT),
],
EnergyConverter: [
(10, UnitOfEnergy.MILLIWATT_HOUR, 0.00001, UnitOfEnergy.KILO_WATT_HOUR),
(10, UnitOfEnergy.WATT_HOUR, 10000, UnitOfEnergy.MILLIWATT_HOUR),
(10, UnitOfEnergy.WATT_HOUR, 0.01, UnitOfEnergy.KILO_WATT_HOUR),
(10, UnitOfEnergy.WATT_HOUR, 0.00001, UnitOfEnergy.MEGA_WATT_HOUR),
(10, UnitOfEnergy.WATT_HOUR, 0.00000001, UnitOfEnergy.GIGA_WATT_HOUR),