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

Flow rate unit conversions and device class #106077

Merged
merged 8 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion homeassistant/components/number/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
UnitOfTemperature,
UnitOfTime,
UnitOfVolume,
UnitOfVolumeFlowRate,
emontnemery marked this conversation as resolved.
Show resolved Hide resolved
UnitOfVolumetricFlux,
)
from homeassistant.helpers.deprecation import (
Expand All @@ -42,7 +43,11 @@
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
from homeassistant.util.unit_conversion import BaseUnitConverter, TemperatureConverter
from homeassistant.util.unit_conversion import (
BaseUnitConverter,
TemperatureConverter,
VolumeFlowRateConverter,
)

ATTR_VALUE = "value"
ATTR_MIN = "min"
Expand Down Expand Up @@ -372,6 +377,14 @@ class NumberDeviceClass(StrEnum):
USCS/imperial units are currently assumed to be US volumes)
"""

VOLUME_FLOW_RATE = "volume_flow_rate"
"""Generic flow rate

Unit of measurement: UnitOfVolumeFlowRate
- SI / metric: `m³/h`, `L/min`
- USCS / imperial: `ft³/min`, `gal/min`
"""

WATER = "water"
"""Water.

Expand Down Expand Up @@ -464,6 +477,7 @@ class NumberDeviceClass(StrEnum):
NumberDeviceClass.VOLTAGE: set(UnitOfElectricPotential),
NumberDeviceClass.VOLUME: set(UnitOfVolume),
NumberDeviceClass.VOLUME_STORAGE: set(UnitOfVolume),
NumberDeviceClass.VOLUME_FLOW_RATE: set(UnitOfVolumeFlowRate),
NumberDeviceClass.WATER: {
UnitOfVolume.CENTUM_CUBIC_FEET,
UnitOfVolume.CUBIC_FEET,
Expand All @@ -477,6 +491,7 @@ class NumberDeviceClass(StrEnum):

UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = {
NumberDeviceClass.TEMPERATURE: TemperatureConverter,
NumberDeviceClass.VOLUME_FLOW_RATE: VolumeFlowRateConverter,
}

# These can be removed if no deprecated constant are in this module anymore
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/number/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@
"volume_storage": {
"name": "[%key:component::sensor::entity_component::volume_storage::name%]"
},
"volume_flow_rate": {
"name": "[%key:component::sensor::entity_component::volume_flow_rate::name%]"
},
"water": {
"name": "[%key:component::sensor::entity_component::water::name%]"
},
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/recorder/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
TemperatureConverter,
UnitlessRatioConverter,
VolumeConverter,
VolumeFlowRateConverter,
)

from .const import (
Expand Down Expand Up @@ -139,6 +140,7 @@
**{unit: TemperatureConverter for unit in TemperatureConverter.VALID_UNITS},
**{unit: UnitlessRatioConverter for unit in UnitlessRatioConverter.VALID_UNITS},
**{unit: VolumeConverter for unit in VolumeConverter.VALID_UNITS},
**{unit: VolumeFlowRateConverter for unit in VolumeFlowRateConverter.VALID_UNITS},
}

DATA_SHORT_TERM_STATISTICS_RUN_CACHE = "recorder_short_term_statistics_run_cache"
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/recorder/websocket_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
TemperatureConverter,
UnitlessRatioConverter,
VolumeConverter,
VolumeFlowRateConverter,
)

from .models import StatisticPeriod
Expand Down Expand Up @@ -67,6 +68,7 @@
vol.Optional("temperature"): vol.In(TemperatureConverter.VALID_UNITS),
vol.Optional("unitless"): vol.In(UnitlessRatioConverter.VALID_UNITS),
vol.Optional("volume"): vol.In(VolumeConverter.VALID_UNITS),
vol.Optional("volume_flow_rate"): vol.In(VolumeFlowRateConverter.VALID_UNITS),
}
)

Expand Down
13 changes: 13 additions & 0 deletions homeassistant/components/sensor/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
UnitOfTemperature,
UnitOfTime,
UnitOfVolume,
UnitOfVolumeFlowRate,
UnitOfVolumetricFlux,
)
from homeassistant.helpers.deprecation import (
Expand All @@ -57,6 +58,7 @@
TemperatureConverter,
UnitlessRatioConverter,
VolumeConverter,
VolumeFlowRateConverter,
)

DOMAIN: Final = "sensor"
Expand Down Expand Up @@ -394,6 +396,14 @@ class SensorDeviceClass(StrEnum):
USCS/imperial units are currently assumed to be US volumes)
"""

VOLUME_FLOW_RATE = "volume_flow_rate"
"""Generic flow rate

Unit of measurement: UnitOfVolumeFlowRate
- SI / metric: `m³/h`, `L/min`
- USCS / imperial: `ft³/min`, `gal/min`
"""

WATER = "water"
"""Water.

Expand Down Expand Up @@ -489,6 +499,7 @@ class SensorStateClass(StrEnum):
SensorDeviceClass.VOLTAGE: ElectricPotentialConverter,
SensorDeviceClass.VOLUME: VolumeConverter,
SensorDeviceClass.VOLUME_STORAGE: VolumeConverter,
SensorDeviceClass.VOLUME_FLOW_RATE: VolumeFlowRateConverter,
SensorDeviceClass.WATER: VolumeConverter,
SensorDeviceClass.WEIGHT: MassConverter,
SensorDeviceClass.WIND_SPEED: SpeedConverter,
Expand Down Expand Up @@ -555,6 +566,7 @@ class SensorStateClass(StrEnum):
},
SensorDeviceClass.VOLTAGE: set(UnitOfElectricPotential),
SensorDeviceClass.VOLUME: set(UnitOfVolume),
SensorDeviceClass.VOLUME_FLOW_RATE: set(UnitOfVolumeFlowRate),
SensorDeviceClass.VOLUME_STORAGE: set(UnitOfVolume),
SensorDeviceClass.WATER: {
UnitOfVolume.CENTUM_CUBIC_FEET,
Expand Down Expand Up @@ -621,6 +633,7 @@ class SensorStateClass(StrEnum):
SensorStateClass.TOTAL_INCREASING,
},
SensorDeviceClass.VOLUME_STORAGE: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.VOLUME_FLOW_RATE: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.WATER: {
SensorStateClass.TOTAL,
SensorStateClass.TOTAL_INCREASING,
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/sensor/device_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
CONF_IS_VOLATILE_ORGANIC_COMPOUNDS_PARTS = "is_volatile_organic_compounds_parts"
CONF_IS_VOLTAGE = "is_voltage"
CONF_IS_VOLUME = "is_volume"
CONF_IS_VOLUME_FLOW_RATE = "is_volume_flow_rate"
CONF_IS_WATER = "is_water"
CONF_IS_WEIGHT = "is_weight"
CONF_IS_WIND_SPEED = "is_wind_speed"
Expand Down Expand Up @@ -132,6 +133,7 @@
SensorDeviceClass.VOLTAGE: [{CONF_TYPE: CONF_IS_VOLTAGE}],
SensorDeviceClass.VOLUME: [{CONF_TYPE: CONF_IS_VOLUME}],
SensorDeviceClass.VOLUME_STORAGE: [{CONF_TYPE: CONF_IS_VOLUME}],
SensorDeviceClass.VOLUME_FLOW_RATE: [{CONF_TYPE: CONF_IS_VOLUME_FLOW_RATE}],
SensorDeviceClass.WATER: [{CONF_TYPE: CONF_IS_WATER}],
SensorDeviceClass.WEIGHT: [{CONF_TYPE: CONF_IS_WEIGHT}],
SensorDeviceClass.WIND_SPEED: [{CONF_TYPE: CONF_IS_WIND_SPEED}],
Expand Down Expand Up @@ -186,6 +188,7 @@
CONF_IS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
CONF_IS_VOLTAGE,
CONF_IS_VOLUME,
CONF_IS_VOLUME_FLOW_RATE,
CONF_IS_WATER,
CONF_IS_WEIGHT,
CONF_IS_WIND_SPEED,
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/sensor/device_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
CONF_VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts"
CONF_VOLTAGE = "voltage"
CONF_VOLUME = "volume"
CONF_VOLUME_FLOW_RATE = "volume_flow_rate"
CONF_WATER = "water"
CONF_WEIGHT = "weight"
CONF_WIND_SPEED = "wind_speed"
Expand Down Expand Up @@ -131,6 +132,7 @@
SensorDeviceClass.VOLTAGE: [{CONF_TYPE: CONF_VOLTAGE}],
SensorDeviceClass.VOLUME: [{CONF_TYPE: CONF_VOLUME}],
SensorDeviceClass.VOLUME_STORAGE: [{CONF_TYPE: CONF_VOLUME}],
SensorDeviceClass.VOLUME_FLOW_RATE: [{CONF_TYPE: CONF_VOLUME_FLOW_RATE}],
SensorDeviceClass.WATER: [{CONF_TYPE: CONF_WATER}],
SensorDeviceClass.WEIGHT: [{CONF_TYPE: CONF_WEIGHT}],
SensorDeviceClass.WIND_SPEED: [{CONF_TYPE: CONF_WIND_SPEED}],
Expand Down Expand Up @@ -186,6 +188,7 @@
CONF_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
CONF_VOLTAGE,
CONF_VOLUME,
CONF_VOLUME_FLOW_RATE,
CONF_WATER,
CONF_WEIGHT,
CONF_WIND_SPEED,
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/sensor/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"is_volatile_organic_compounds_parts": "[%key:component::sensor::device_automation::condition_type::is_volatile_organic_compounds%]",
"is_voltage": "Current {entity_name} voltage",
"is_volume": "Current {entity_name} volume",
"is_volume_flow_rate": "Current {entity_name} volume flow rate",
"is_water": "Current {entity_name} water",
"is_weight": "Current {entity_name} weight",
"is_wind_speed": "Current {entity_name} wind speed"
Expand Down Expand Up @@ -93,6 +94,7 @@
"volatile_organic_compounds_parts": "[%key:component::sensor::device_automation::trigger_type::volatile_organic_compounds%]",
"voltage": "{entity_name} voltage changes",
"volume": "{entity_name} volume changes",
"volume_flow_rate": "{entity_name} volume flow rate changes",
"water": "{entity_name} water changes",
"weight": "{entity_name} weight changes",
"wind_speed": "{entity_name} wind speed changes"
Expand Down Expand Up @@ -260,6 +262,9 @@
"volume": {
"name": "Volume"
},
"volume_flow_rate": {
"name": "Volume flow rate"
},
"volume_storage": {
"name": "Stored volume"
},
Expand Down
15 changes: 13 additions & 2 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,9 @@ class UnitOfVolumeFlowRate(StrEnum):
"""Volume flow rate units."""

CUBIC_METERS_PER_HOUR = "m³/h"
CUBIC_FEET_PER_MINUTE = "ft³/m"
CUBIC_FEET_PER_MINUTE = "ft³/min"
elupus marked this conversation as resolved.
Show resolved Hide resolved
LITERS_PER_MINUTE = "L/min"
GALLONS_PER_MINUTE = "gal/min"


_DEPRECATED_VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: Final = DeprecatedConstantEnum(
Expand All @@ -1053,7 +1055,16 @@ class UnitOfVolumeFlowRate(StrEnum):
"2025.1",
)
"""Deprecated: please use UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE"""

_DEPRECATED_VOLUME_FLOW_RATE_LITERS_PER_MINUTE: Final = DeprecatedConstantEnum(
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
"2025.1",
)
"""Deprecated: please use UnitOfVolumeFlowRate.LITERS_PER_MINUTE"""
_DEPRECATED_VOLUME_FLOW_RATE_GALLONS_PER_MINUTE: Final = DeprecatedConstantEnum(
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
"2025.1",
)
"""Deprecated: please use UnitOfVolumeFlowRate.GALLONS_PER_MINUTE"""
elupus marked this conversation as resolved.
Show resolved Hide resolved
# Area units
AREA_SQUARE_METERS: Final = "m²"

Expand Down
25 changes: 25 additions & 0 deletions homeassistant/util/unit_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolume,
UnitOfVolumeFlowRate,
UnitOfVolumetricFlux,
)
from homeassistant.exceptions import HomeAssistantError
Expand All @@ -39,6 +40,7 @@

# Duration conversion constants
_HRS_TO_SECS = 60 * 60 # 1 hr = 3600 seconds
_HRS_TO_MINUTES = 60 # 1 hr = 60 minutes
_DAYS_TO_SECS = 24 * _HRS_TO_SECS # 1 day = 24 hours = 86400 seconds

# Mass conversion constants
Expand Down Expand Up @@ -516,3 +518,26 @@ class VolumeConverter(BaseUnitConverter):
UnitOfVolume.CUBIC_FEET,
UnitOfVolume.CENTUM_CUBIC_FEET,
}


class VolumeFlowRateConverter(BaseUnitConverter):
"""Utility to convert volume values."""

UNIT_CLASS = "volume_flow_rate"
NORMALIZED_UNIT = UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR
# Units in terms of m³/h
_UNIT_CONVERSION: dict[str | None, float] = {
UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR: 1,
UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE: 1
/ (_HRS_TO_MINUTES * _CUBIC_FOOT_TO_CUBIC_METER),
UnitOfVolumeFlowRate.LITERS_PER_MINUTE: 1
/ (_HRS_TO_MINUTES * _L_TO_CUBIC_METER),
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE: 1
/ (_HRS_TO_MINUTES * _GALLON_TO_CUBIC_METER),
}
VALID_UNITS = {
UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE,
UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
}
17 changes: 17 additions & 0 deletions tests/components/number/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
ATTR_UNIT_OF_MEASUREMENT,
CONF_PLATFORM,
UnitOfTemperature,
UnitOfVolumeFlowRate,
)
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry as er
Expand Down Expand Up @@ -686,6 +687,22 @@ async def test_restore_number_restore_state(
100,
38.0,
),
(
SensorDeviceClass.VOLUME_FLOW_RATE,
elupus marked this conversation as resolved.
Show resolved Hide resolved
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
50.0,
"13.2",
),
(
SensorDeviceClass.VOLUME_FLOW_RATE,
elupus marked this conversation as resolved.
Show resolved Hide resolved
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
13.0,
"49.2",
),
],
)
async def test_custom_unit(
Expand Down
17 changes: 17 additions & 0 deletions tests/components/sensor/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolume,
UnitOfVolumeFlowRate,
UnitOfVolumetricFlux,
)
from homeassistant.core import HomeAssistant, State
Expand Down Expand Up @@ -581,6 +582,22 @@ async def test_restore_sensor_restore_state(
-0.00001,
"0",
),
(
SensorDeviceClass.VOLUME_FLOW_RATE,
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
50.0,
"13.2",
),
(
SensorDeviceClass.VOLUME_FLOW_RATE,
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
13.0,
"49.2",
),
],
)
async def test_custom_unit(
Expand Down
Loading