Energy distance units (#136933)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>pull/122818/head^2
parent
21ffcf853b
commit
84ae476b67
|
@ -23,6 +23,7 @@ from homeassistant.const import (
|
|||
UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfEnergy,
|
||||
UnitOfEnergyDistance,
|
||||
UnitOfFrequency,
|
||||
UnitOfInformation,
|
||||
UnitOfIrradiance,
|
||||
|
@ -166,6 +167,15 @@ class NumberDeviceClass(StrEnum):
|
|||
Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal`
|
||||
"""
|
||||
|
||||
ENERGY_DISTANCE = "energy_distance"
|
||||
"""Energy distance.
|
||||
|
||||
Use this device class for sensors measuring energy by distance, for example the amount
|
||||
of electric energy consumed by an electric car.
|
||||
|
||||
Unit of measurement: `kWh/100km`, `mi/kWh`, `km/kWh`
|
||||
"""
|
||||
|
||||
ENERGY_STORAGE = "energy_storage"
|
||||
"""Stored energy.
|
||||
|
||||
|
@ -447,6 +457,7 @@ DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
|
|||
UnitOfTime.MILLISECONDS,
|
||||
},
|
||||
NumberDeviceClass.ENERGY: set(UnitOfEnergy),
|
||||
NumberDeviceClass.ENERGY_DISTANCE: set(UnitOfEnergyDistance),
|
||||
NumberDeviceClass.ENERGY_STORAGE: set(UnitOfEnergy),
|
||||
NumberDeviceClass.FREQUENCY: set(UnitOfFrequency),
|
||||
NumberDeviceClass.GAS: {
|
||||
|
|
|
@ -38,6 +38,7 @@ from homeassistant.util.unit_conversion import (
|
|||
ElectricCurrentConverter,
|
||||
ElectricPotentialConverter,
|
||||
EnergyConverter,
|
||||
EnergyDistanceConverter,
|
||||
InformationConverter,
|
||||
MassConverter,
|
||||
PowerConverter,
|
||||
|
@ -147,6 +148,7 @@ STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = {
|
|||
for unit in ElectricPotentialConverter.VALID_UNITS
|
||||
},
|
||||
**{unit: EnergyConverter for unit in EnergyConverter.VALID_UNITS},
|
||||
**{unit: EnergyDistanceConverter for unit in EnergyDistanceConverter.VALID_UNITS},
|
||||
**{unit: InformationConverter for unit in InformationConverter.VALID_UNITS},
|
||||
**{unit: MassConverter for unit in MassConverter.VALID_UNITS},
|
||||
**{unit: PowerConverter for unit in PowerConverter.VALID_UNITS},
|
||||
|
|
|
@ -25,6 +25,7 @@ from homeassistant.util.unit_conversion import (
|
|||
ElectricCurrentConverter,
|
||||
ElectricPotentialConverter,
|
||||
EnergyConverter,
|
||||
EnergyDistanceConverter,
|
||||
InformationConverter,
|
||||
MassConverter,
|
||||
PowerConverter,
|
||||
|
@ -67,6 +68,7 @@ UNIT_SCHEMA = vol.Schema(
|
|||
vol.Optional("electric_current"): vol.In(ElectricCurrentConverter.VALID_UNITS),
|
||||
vol.Optional("voltage"): vol.In(ElectricPotentialConverter.VALID_UNITS),
|
||||
vol.Optional("energy"): vol.In(EnergyConverter.VALID_UNITS),
|
||||
vol.Optional("energy_distance"): vol.In(EnergyDistanceConverter.VALID_UNITS),
|
||||
vol.Optional("information"): vol.In(InformationConverter.VALID_UNITS),
|
||||
vol.Optional("mass"): vol.In(MassConverter.VALID_UNITS),
|
||||
vol.Optional("power"): vol.In(PowerConverter.VALID_UNITS),
|
||||
|
|
|
@ -23,6 +23,7 @@ from homeassistant.const import (
|
|||
UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfEnergy,
|
||||
UnitOfEnergyDistance,
|
||||
UnitOfFrequency,
|
||||
UnitOfInformation,
|
||||
UnitOfIrradiance,
|
||||
|
@ -51,6 +52,7 @@ from homeassistant.util.unit_conversion import (
|
|||
ElectricCurrentConverter,
|
||||
ElectricPotentialConverter,
|
||||
EnergyConverter,
|
||||
EnergyDistanceConverter,
|
||||
InformationConverter,
|
||||
MassConverter,
|
||||
PowerConverter,
|
||||
|
@ -194,6 +196,15 @@ class SensorDeviceClass(StrEnum):
|
|||
Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal`
|
||||
"""
|
||||
|
||||
ENERGY_DISTANCE = "energy_distance"
|
||||
"""Energy distance.
|
||||
|
||||
Use this device class for sensors measuring energy by distance, for example the amount
|
||||
of electric energy consumed by an electric car.
|
||||
|
||||
Unit of measurement: `kWh/100km`, `mi/kWh`, `km/kWh`
|
||||
"""
|
||||
|
||||
ENERGY_STORAGE = "energy_storage"
|
||||
"""Stored energy.
|
||||
|
||||
|
@ -500,6 +511,7 @@ UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] =
|
|||
SensorDeviceClass.DISTANCE: DistanceConverter,
|
||||
SensorDeviceClass.DURATION: DurationConverter,
|
||||
SensorDeviceClass.ENERGY: EnergyConverter,
|
||||
SensorDeviceClass.ENERGY_DISTANCE: EnergyDistanceConverter,
|
||||
SensorDeviceClass.ENERGY_STORAGE: EnergyConverter,
|
||||
SensorDeviceClass.GAS: VolumeConverter,
|
||||
SensorDeviceClass.POWER: PowerConverter,
|
||||
|
@ -541,6 +553,7 @@ DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
|
|||
UnitOfTime.MILLISECONDS,
|
||||
},
|
||||
SensorDeviceClass.ENERGY: set(UnitOfEnergy),
|
||||
SensorDeviceClass.ENERGY_DISTANCE: set(UnitOfEnergyDistance),
|
||||
SensorDeviceClass.ENERGY_STORAGE: set(UnitOfEnergy),
|
||||
SensorDeviceClass.FREQUENCY: set(UnitOfFrequency),
|
||||
SensorDeviceClass.GAS: {
|
||||
|
@ -622,6 +635,7 @@ DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
|
|||
SensorStateClass.TOTAL,
|
||||
SensorStateClass.TOTAL_INCREASING,
|
||||
},
|
||||
SensorDeviceClass.ENERGY_DISTANCE: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.ENERGY_STORAGE: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.ENUM: set(),
|
||||
SensorDeviceClass.FREQUENCY: {SensorStateClass.MEASUREMENT},
|
||||
|
|
|
@ -48,6 +48,7 @@ CONF_IS_DATA_SIZE = "is_data_size"
|
|||
CONF_IS_DISTANCE = "is_distance"
|
||||
CONF_IS_DURATION = "is_duration"
|
||||
CONF_IS_ENERGY = "is_energy"
|
||||
CONF_IS_ENERGY_DISTANCE = "is_energy_distance"
|
||||
CONF_IS_FREQUENCY = "is_frequency"
|
||||
CONF_IS_HUMIDITY = "is_humidity"
|
||||
CONF_IS_GAS = "is_gas"
|
||||
|
@ -102,6 +103,7 @@ ENTITY_CONDITIONS = {
|
|||
SensorDeviceClass.DISTANCE: [{CONF_TYPE: CONF_IS_DISTANCE}],
|
||||
SensorDeviceClass.DURATION: [{CONF_TYPE: CONF_IS_DURATION}],
|
||||
SensorDeviceClass.ENERGY: [{CONF_TYPE: CONF_IS_ENERGY}],
|
||||
SensorDeviceClass.ENERGY_DISTANCE: [{CONF_TYPE: CONF_IS_ENERGY_DISTANCE}],
|
||||
SensorDeviceClass.ENERGY_STORAGE: [{CONF_TYPE: CONF_IS_ENERGY}],
|
||||
SensorDeviceClass.FREQUENCY: [{CONF_TYPE: CONF_IS_FREQUENCY}],
|
||||
SensorDeviceClass.GAS: [{CONF_TYPE: CONF_IS_GAS}],
|
||||
|
@ -168,6 +170,7 @@ CONDITION_SCHEMA = vol.All(
|
|||
CONF_IS_DISTANCE,
|
||||
CONF_IS_DURATION,
|
||||
CONF_IS_ENERGY,
|
||||
CONF_IS_ENERGY_DISTANCE,
|
||||
CONF_IS_FREQUENCY,
|
||||
CONF_IS_GAS,
|
||||
CONF_IS_HUMIDITY,
|
||||
|
|
|
@ -47,6 +47,7 @@ CONF_DATA_SIZE = "data_size"
|
|||
CONF_DISTANCE = "distance"
|
||||
CONF_DURATION = "duration"
|
||||
CONF_ENERGY = "energy"
|
||||
CONF_ENERGY_DISTANCE = "energy_distance"
|
||||
CONF_FREQUENCY = "frequency"
|
||||
CONF_GAS = "gas"
|
||||
CONF_HUMIDITY = "humidity"
|
||||
|
@ -101,6 +102,7 @@ ENTITY_TRIGGERS = {
|
|||
SensorDeviceClass.DISTANCE: [{CONF_TYPE: CONF_DISTANCE}],
|
||||
SensorDeviceClass.DURATION: [{CONF_TYPE: CONF_DURATION}],
|
||||
SensorDeviceClass.ENERGY: [{CONF_TYPE: CONF_ENERGY}],
|
||||
SensorDeviceClass.ENERGY_DISTANCE: [{CONF_TYPE: CONF_ENERGY_DISTANCE}],
|
||||
SensorDeviceClass.ENERGY_STORAGE: [{CONF_TYPE: CONF_ENERGY}],
|
||||
SensorDeviceClass.FREQUENCY: [{CONF_TYPE: CONF_FREQUENCY}],
|
||||
SensorDeviceClass.GAS: [{CONF_TYPE: CONF_GAS}],
|
||||
|
@ -168,6 +170,7 @@ TRIGGER_SCHEMA = vol.All(
|
|||
CONF_DISTANCE,
|
||||
CONF_DURATION,
|
||||
CONF_ENERGY,
|
||||
CONF_ENERGY_DISTANCE,
|
||||
CONF_FREQUENCY,
|
||||
CONF_GAS,
|
||||
CONF_HUMIDITY,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"is_distance": "Current {entity_name} distance",
|
||||
"is_duration": "Current {entity_name} duration",
|
||||
"is_energy": "Current {entity_name} energy",
|
||||
"is_energy_distance": "Current {entity_name} energy per distance",
|
||||
"is_frequency": "Current {entity_name} frequency",
|
||||
"is_gas": "Current {entity_name} gas",
|
||||
"is_humidity": "Current {entity_name} humidity",
|
||||
|
@ -69,6 +70,7 @@
|
|||
"distance": "{entity_name} distance changes",
|
||||
"duration": "{entity_name} duration changes",
|
||||
"energy": "{entity_name} energy changes",
|
||||
"energy_distance": "{entity_name} energy per distance changes",
|
||||
"frequency": "{entity_name} frequency changes",
|
||||
"gas": "{entity_name} gas changes",
|
||||
"humidity": "{entity_name} humidity changes",
|
||||
|
@ -183,6 +185,9 @@
|
|||
"energy": {
|
||||
"name": "Energy"
|
||||
},
|
||||
"energy_distance": {
|
||||
"name": "Energy per distance"
|
||||
},
|
||||
"energy_storage": {
|
||||
"name": "Stored energy"
|
||||
},
|
||||
|
|
|
@ -632,6 +632,15 @@ class UnitOfEnergy(StrEnum):
|
|||
GIGA_CALORIE = "Gcal"
|
||||
|
||||
|
||||
# Energy Distance units
|
||||
class UnitOfEnergyDistance(StrEnum):
|
||||
"""Energy Distance units."""
|
||||
|
||||
KILO_WATT_HOUR_PER_100_KM = "kWh/100km"
|
||||
MILES_PER_KILO_WATT_HOUR = "mi/kWh"
|
||||
KM_PER_KILO_WATT_HOUR = "km/kWh"
|
||||
|
||||
|
||||
# Electric_current units
|
||||
class UnitOfElectricCurrent(StrEnum):
|
||||
"""Electric current units."""
|
||||
|
|
|
@ -17,6 +17,7 @@ from homeassistant.const import (
|
|||
UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfEnergy,
|
||||
UnitOfEnergyDistance,
|
||||
UnitOfInformation,
|
||||
UnitOfLength,
|
||||
UnitOfMass,
|
||||
|
@ -90,6 +91,7 @@ class BaseUnitConverter:
|
|||
VALID_UNITS: set[str | None]
|
||||
|
||||
_UNIT_CONVERSION: dict[str | None, float]
|
||||
_UNIT_INVERSES: set[str] = set()
|
||||
|
||||
@classmethod
|
||||
def convert(cls, value: float, from_unit: str | None, to_unit: str | None) -> float:
|
||||
|
@ -105,6 +107,8 @@ class BaseUnitConverter:
|
|||
if from_unit == to_unit:
|
||||
return lambda value: value
|
||||
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
|
||||
if cls._are_unit_inverses(from_unit, to_unit):
|
||||
return lambda val: to_ratio / (val / from_ratio)
|
||||
return lambda val: (val / from_ratio) * to_ratio
|
||||
|
||||
@classmethod
|
||||
|
@ -129,6 +133,8 @@ class BaseUnitConverter:
|
|||
if from_unit == to_unit:
|
||||
return lambda value: value
|
||||
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
|
||||
if cls._are_unit_inverses(from_unit, to_unit):
|
||||
return lambda val: None if val is None else to_ratio / (val / from_ratio)
|
||||
return lambda val: None if val is None else (val / from_ratio) * to_ratio
|
||||
|
||||
@classmethod
|
||||
|
@ -138,6 +144,12 @@ class BaseUnitConverter:
|
|||
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
|
||||
return from_ratio / to_ratio
|
||||
|
||||
@classmethod
|
||||
@lru_cache
|
||||
def _are_unit_inverses(cls, from_unit: str | None, to_unit: str | None) -> bool:
|
||||
"""Return true if one unit is an inverse but not the other."""
|
||||
return (from_unit in cls._UNIT_INVERSES) != (to_unit in cls._UNIT_INVERSES)
|
||||
|
||||
|
||||
class DataRateConverter(BaseUnitConverter):
|
||||
"""Utility to convert data rate values."""
|
||||
|
@ -284,6 +296,22 @@ class EnergyConverter(BaseUnitConverter):
|
|||
VALID_UNITS = set(UnitOfEnergy)
|
||||
|
||||
|
||||
class EnergyDistanceConverter(BaseUnitConverter):
|
||||
"""Utility to convert vehicle energy consumption values."""
|
||||
|
||||
UNIT_CLASS = "energy_distance"
|
||||
_UNIT_CONVERSION: dict[str | None, float] = {
|
||||
UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM: 1,
|
||||
UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR: 100 * _KM_TO_M / _MILE_TO_M,
|
||||
UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR: 100,
|
||||
}
|
||||
_UNIT_INVERSES: set[str] = {
|
||||
UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR,
|
||||
UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR,
|
||||
}
|
||||
VALID_UNITS = set(UnitOfEnergyDistance)
|
||||
|
||||
|
||||
class InformationConverter(BaseUnitConverter):
|
||||
"""Utility to convert information values."""
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ from homeassistant.const import (
|
|||
UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfEnergy,
|
||||
UnitOfEnergyDistance,
|
||||
UnitOfInformation,
|
||||
UnitOfLength,
|
||||
UnitOfMass,
|
||||
|
@ -43,6 +44,7 @@ from homeassistant.util.unit_conversion import (
|
|||
ElectricCurrentConverter,
|
||||
ElectricPotentialConverter,
|
||||
EnergyConverter,
|
||||
EnergyDistanceConverter,
|
||||
InformationConverter,
|
||||
MassConverter,
|
||||
PowerConverter,
|
||||
|
@ -79,6 +81,7 @@ _ALL_CONVERTERS: dict[type[BaseUnitConverter], list[str | None]] = {
|
|||
SpeedConverter,
|
||||
TemperatureConverter,
|
||||
UnitlessRatioConverter,
|
||||
EnergyDistanceConverter,
|
||||
VolumeConverter,
|
||||
VolumeFlowRateConverter,
|
||||
)
|
||||
|
@ -115,6 +118,11 @@ _GET_UNIT_RATIO: dict[type[BaseUnitConverter], tuple[str | None, str | None, flo
|
|||
1000,
|
||||
),
|
||||
EnergyConverter: (UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR, 1000),
|
||||
EnergyDistanceConverter: (
|
||||
UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR,
|
||||
UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR,
|
||||
0.621371,
|
||||
),
|
||||
InformationConverter: (UnitOfInformation.BITS, UnitOfInformation.BYTES, 8),
|
||||
MassConverter: (UnitOfMass.STONES, UnitOfMass.KILOGRAMS, 0.157473),
|
||||
PowerConverter: (UnitOfPower.WATT, UnitOfPower.KILO_WATT, 1000),
|
||||
|
@ -486,6 +494,38 @@ _CONVERTED_VALUE: dict[
|
|||
(10, UnitOfEnergy.GIGA_CALORIE, 10000, UnitOfEnergy.MEGA_CALORIE),
|
||||
(10, UnitOfEnergy.GIGA_CALORIE, 11.622222, UnitOfEnergy.MEGA_WATT_HOUR),
|
||||
],
|
||||
EnergyDistanceConverter: [
|
||||
(
|
||||
10,
|
||||
UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM,
|
||||
6.213712,
|
||||
UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR,
|
||||
),
|
||||
(
|
||||
25,
|
||||
UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM,
|
||||
4,
|
||||
UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR,
|
||||
),
|
||||
(
|
||||
20,
|
||||
UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR,
|
||||
3.106856,
|
||||
UnitOfEnergyDistance.KILO_WATT_HOUR_PER_100_KM,
|
||||
),
|
||||
(
|
||||
10,
|
||||
UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR,
|
||||
16.09344,
|
||||
UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR,
|
||||
),
|
||||
(
|
||||
16.09344,
|
||||
UnitOfEnergyDistance.KM_PER_KILO_WATT_HOUR,
|
||||
10,
|
||||
UnitOfEnergyDistance.MILES_PER_KILO_WATT_HOUR,
|
||||
),
|
||||
],
|
||||
InformationConverter: [
|
||||
(8e3, UnitOfInformation.BITS, 8, UnitOfInformation.KILOBITS),
|
||||
(8e6, UnitOfInformation.BITS, 8, UnitOfInformation.MEGABITS),
|
||||
|
|
Loading…
Reference in New Issue