diff --git a/homeassistant/components/energy/sensor.py b/homeassistant/components/energy/sensor.py index c60c5c11519..540642a89da 100644 --- a/homeassistant/components/energy/sensor.py +++ b/homeassistant/components/energy/sensor.py @@ -17,12 +17,9 @@ from homeassistant.components.sensor import ( from homeassistant.components.sensor.recorder import reset_detected from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, - ENERGY_GIGA_JOULE, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS, + UnitOfEnergy, ) from homeassistant.core import ( HomeAssistant, @@ -46,10 +43,10 @@ SUPPORTED_STATE_CLASSES = [ SensorStateClass.TOTAL_INCREASING, ] VALID_ENERGY_UNITS = [ - ENERGY_WATT_HOUR, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_GIGA_JOULE, + UnitOfEnergy.WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, + UnitOfEnergy.MEGA_WATT_HOUR, + UnitOfEnergy.GIGA_JOULE, ] VALID_ENERGY_UNITS_GAS = [VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS] + VALID_ENERGY_UNITS _LOGGER = logging.getLogger(__name__) @@ -286,17 +283,17 @@ class EnergyCostSensor(SensorEntity): return if energy_price_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, "").endswith( - f"/{ENERGY_WATT_HOUR}" + f"/{UnitOfEnergy.WATT_HOUR}" ): energy_price *= 1000.0 if energy_price_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, "").endswith( - f"/{ENERGY_MEGA_WATT_HOUR}" + f"/{UnitOfEnergy.MEGA_WATT_HOUR}" ): energy_price /= 1000.0 if energy_price_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, "").endswith( - f"/{ENERGY_GIGA_JOULE}" + f"/{UnitOfEnergy.GIGA_JOULE}" ): energy_price /= 1000 / 3.6 @@ -319,11 +316,11 @@ class EnergyCostSensor(SensorEntity): if energy_unit not in VALID_ENERGY_UNITS_GAS: energy_unit = None - if energy_unit == ENERGY_WATT_HOUR: + if energy_unit == UnitOfEnergy.WATT_HOUR: energy_price /= 1000 - elif energy_unit == ENERGY_MEGA_WATT_HOUR: + elif energy_unit == UnitOfEnergy.MEGA_WATT_HOUR: energy_price *= 1000 - elif energy_unit == ENERGY_GIGA_JOULE: + elif energy_unit == UnitOfEnergy.GIGA_JOULE: energy_price *= 1000 / 3.6 if energy_unit is None: diff --git a/homeassistant/components/energy/validate.py b/homeassistant/components/energy/validate.py index 17ac7227bf8..f31eb53fb37 100644 --- a/homeassistant/components/energy/validate.py +++ b/homeassistant/components/energy/validate.py @@ -9,14 +9,11 @@ from typing import Any from homeassistant.components import recorder, sensor from homeassistant.const import ( ATTR_DEVICE_CLASS, - ENERGY_GIGA_JOULE, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, STATE_UNAVAILABLE, STATE_UNKNOWN, VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS, + UnitOfEnergy, ) from homeassistant.core import HomeAssistant, callback, valid_entity_id @@ -26,10 +23,10 @@ from .const import DOMAIN ENERGY_USAGE_DEVICE_CLASSES = (sensor.SensorDeviceClass.ENERGY,) ENERGY_USAGE_UNITS = { sensor.SensorDeviceClass.ENERGY: ( - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, - ENERGY_GIGA_JOULE, + UnitOfEnergy.KILO_WATT_HOUR, + UnitOfEnergy.MEGA_WATT_HOUR, + UnitOfEnergy.WATT_HOUR, + UnitOfEnergy.GIGA_JOULE, ) } ENERGY_PRICE_UNITS = tuple( @@ -43,10 +40,10 @@ GAS_USAGE_DEVICE_CLASSES = ( ) GAS_USAGE_UNITS = { sensor.SensorDeviceClass.ENERGY: ( - ENERGY_WATT_HOUR, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_GIGA_JOULE, + UnitOfEnergy.WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, + UnitOfEnergy.MEGA_WATT_HOUR, + UnitOfEnergy.GIGA_JOULE, ), sensor.SensorDeviceClass.GAS: (VOLUME_CUBIC_METERS, VOLUME_CUBIC_FEET), } diff --git a/homeassistant/components/energy/websocket_api.py b/homeassistant/components/energy/websocket_api.py index 8fa35e607b2..c2b693c0809 100644 --- a/homeassistant/components/energy/websocket_api.py +++ b/homeassistant/components/energy/websocket_api.py @@ -13,7 +13,7 @@ from typing import Any, cast import voluptuous as vol from homeassistant.components import recorder, websocket_api -from homeassistant.const import ENERGY_KILO_WATT_HOUR +from homeassistant.const import UnitOfEnergy from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.integration_platform import ( async_process_integration_platforms, @@ -273,7 +273,7 @@ async def ws_get_fossil_energy_consumption( statistic_ids, "hour", True, - {"energy": ENERGY_KILO_WATT_HOUR}, + {"energy": UnitOfEnergy.KILO_WATT_HOUR}, ) def _combine_sum_statistics( diff --git a/homeassistant/const.py b/homeassistant/const.py index 1bac1c10c5a..096bdf1df99 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -486,11 +486,23 @@ POWER_BTU_PER_HOUR: Final = "BTU/h" # Reactive power units POWER_VOLT_AMPERE_REACTIVE: Final = "var" + # Energy units -ENERGY_GIGA_JOULE: Final = "GJ" +class UnitOfEnergy(StrEnum): + """Energy units.""" + + GIGA_JOULE = "GJ" + KILO_WATT_HOUR = "kWh" + MEGA_WATT_HOUR = "MWh" + WATT_HOUR = "Wh" + + ENERGY_KILO_WATT_HOUR: Final = "kWh" +"""Deprecated: please use UnitOfEnergy.KILO_WATT_HOUR.""" ENERGY_MEGA_WATT_HOUR: Final = "MWh" +"""Deprecated: please use UnitOfEnergy.MEGA_WATT_HOUR.""" ENERGY_WATT_HOUR: Final = "Wh" +"""Deprecated: please use UnitOfEnergy.WATT_HOUR.""" # Electric_current units ELECTRIC_CURRENT_MILLIAMPERE: Final = "mA" diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index cd27f79b045..1cf00230d1c 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -2,10 +2,6 @@ from __future__ import annotations from homeassistant.const import ( - ENERGY_GIGA_JOULE, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, LENGTH_CENTIMETERS, LENGTH_FEET, LENGTH_INCHES, @@ -46,6 +42,7 @@ from homeassistant.const import ( VOLUME_GALLONS, VOLUME_LITERS, VOLUME_MILLILITERS, + UnitOfEnergy, UnitOfVolumetricFlux, ) from homeassistant.exceptions import HomeAssistantError @@ -151,18 +148,18 @@ class EnergyConverter(BaseUnitConverter): """Utility to convert energy values.""" UNIT_CLASS = "energy" - NORMALIZED_UNIT = ENERGY_KILO_WATT_HOUR + NORMALIZED_UNIT = UnitOfEnergy.KILO_WATT_HOUR _UNIT_CONVERSION: dict[str, float] = { - ENERGY_WATT_HOUR: 1 * 1000, - ENERGY_KILO_WATT_HOUR: 1, - ENERGY_MEGA_WATT_HOUR: 1 / 1000, - ENERGY_GIGA_JOULE: 3.6 / 1000, + UnitOfEnergy.WATT_HOUR: 1 * 1000, + UnitOfEnergy.KILO_WATT_HOUR: 1, + UnitOfEnergy.MEGA_WATT_HOUR: 1 / 1000, + UnitOfEnergy.GIGA_JOULE: 3.6 / 1000, } VALID_UNITS = { - ENERGY_WATT_HOUR, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_GIGA_JOULE, + UnitOfEnergy.WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, + UnitOfEnergy.MEGA_WATT_HOUR, + UnitOfEnergy.GIGA_JOULE, } diff --git a/tests/components/energy/test_sensor.py b/tests/components/energy/test_sensor.py index 20dc533e70f..3b954e8d62b 100644 --- a/tests/components/energy/test_sensor.py +++ b/tests/components/energy/test_sensor.py @@ -16,13 +16,10 @@ from homeassistant.components.sensor.recorder import compile_statistics from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT, - ENERGY_GIGA_JOULE, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, STATE_UNKNOWN, VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS, + UnitOfEnergy, ) from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component @@ -143,7 +140,7 @@ async def test_cost_sensor_price_entity_total_increasing( return compile_statistics(hass, now, now + timedelta(seconds=1)).platform_stats energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, ATTR_STATE_CLASS: SensorStateClass.TOTAL_INCREASING, } @@ -347,7 +344,7 @@ async def test_cost_sensor_price_entity_total( return compile_statistics(hass, now, now + timedelta(seconds=1)).platform_stats energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, ATTR_STATE_CLASS: energy_state_class, } @@ -553,7 +550,7 @@ async def test_cost_sensor_price_entity_total_no_reset( return compile_statistics(hass, now, now + timedelta(seconds=1)).platform_stats energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, ATTR_STATE_CLASS: energy_state_class, } @@ -701,10 +698,10 @@ async def test_cost_sensor_price_entity_total_no_reset( @pytest.mark.parametrize( "energy_unit,factor", [ - (ENERGY_WATT_HOUR, 1000), - (ENERGY_KILO_WATT_HOUR, 1), - (ENERGY_MEGA_WATT_HOUR, 0.001), - (ENERGY_GIGA_JOULE, 0.001 * 3.6), + (UnitOfEnergy.WATT_HOUR, 1000), + (UnitOfEnergy.KILO_WATT_HOUR, 1), + (UnitOfEnergy.MEGA_WATT_HOUR, 0.001), + (UnitOfEnergy.GIGA_JOULE, 0.001 * 3.6), ], ) async def test_cost_sensor_handle_energy_units( @@ -767,10 +764,10 @@ async def test_cost_sensor_handle_energy_units( @pytest.mark.parametrize( "price_unit,factor", [ - (f"EUR/{ENERGY_WATT_HOUR}", 0.001), - (f"EUR/{ENERGY_KILO_WATT_HOUR}", 1), - (f"EUR/{ENERGY_MEGA_WATT_HOUR}", 1000), - (f"EUR/{ENERGY_GIGA_JOULE}", 1000 / 3.6), + (f"EUR/{UnitOfEnergy.WATT_HOUR}", 0.001), + (f"EUR/{UnitOfEnergy.KILO_WATT_HOUR}", 1), + (f"EUR/{UnitOfEnergy.MEGA_WATT_HOUR}", 1000), + (f"EUR/{UnitOfEnergy.GIGA_JOULE}", 1000 / 3.6), ], ) async def test_cost_sensor_handle_price_units( @@ -778,7 +775,7 @@ async def test_cost_sensor_handle_price_units( ) -> None: """Test energy cost price from sensor entity.""" energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, ATTR_STATE_CLASS: SensorStateClass.TOTAL_INCREASING, } price_attributes = { @@ -891,7 +888,7 @@ async def test_cost_sensor_handle_gas_kwh( ) -> None: """Test gas cost price from sensor entity.""" energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, ATTR_STATE_CLASS: SensorStateClass.TOTAL_INCREASING, } energy_data = data.EnergyManager.default_preferences() @@ -942,7 +939,7 @@ async def test_cost_sensor_wrong_state_class( ) -> None: """Test energy sensor rejects sensor with wrong state_class.""" energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, ATTR_STATE_CLASS: state_class, } energy_data = data.EnergyManager.default_preferences() @@ -1003,7 +1000,7 @@ async def test_cost_sensor_state_class_measurement_no_reset( ) -> None: """Test energy sensor rejects state_class measurement with no last_reset.""" energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, + ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, ATTR_STATE_CLASS: state_class, } energy_data = data.EnergyManager.default_preferences() diff --git a/tests/components/energy/test_validate.py b/tests/components/energy/test_validate.py index 729e7685ac5..c8aa4299484 100644 --- a/tests/components/energy/test_validate.py +++ b/tests/components/energy/test_validate.py @@ -4,12 +4,7 @@ from unittest.mock import patch import pytest from homeassistant.components.energy import async_get_manager, validate -from homeassistant.const import ( - ENERGY_GIGA_JOULE, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, -) +from homeassistant.const import UnitOfEnergy from homeassistant.helpers.json import JSON_DUMP from homeassistant.setup import async_setup_component @@ -68,13 +63,13 @@ async def test_validation_empty_config(hass): @pytest.mark.parametrize( "state_class, energy_unit, extra", [ - ("total_increasing", ENERGY_KILO_WATT_HOUR, {}), - ("total_increasing", ENERGY_MEGA_WATT_HOUR, {}), - ("total_increasing", ENERGY_WATT_HOUR, {}), - ("total", ENERGY_KILO_WATT_HOUR, {}), - ("total", ENERGY_KILO_WATT_HOUR, {"last_reset": "abc"}), - ("measurement", ENERGY_KILO_WATT_HOUR, {"last_reset": "abc"}), - ("total_increasing", ENERGY_GIGA_JOULE, {}), + ("total_increasing", UnitOfEnergy.KILO_WATT_HOUR, {}), + ("total_increasing", UnitOfEnergy.MEGA_WATT_HOUR, {}), + ("total_increasing", UnitOfEnergy.WATT_HOUR, {}), + ("total", UnitOfEnergy.KILO_WATT_HOUR, {}), + ("total", UnitOfEnergy.KILO_WATT_HOUR, {"last_reset": "abc"}), + ("measurement", UnitOfEnergy.KILO_WATT_HOUR, {"last_reset": "abc"}), + ("total_increasing", UnitOfEnergy.GIGA_JOULE, {}), ], ) async def test_validation( diff --git a/tests/util/test_unit_conversion.py b/tests/util/test_unit_conversion.py index 454f0708aac..1b43af09aec 100644 --- a/tests/util/test_unit_conversion.py +++ b/tests/util/test_unit_conversion.py @@ -2,10 +2,6 @@ import pytest from homeassistant.const import ( - ENERGY_GIGA_JOULE, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, LENGTH_CENTIMETERS, LENGTH_FEET, LENGTH_INCHES, @@ -44,6 +40,7 @@ from homeassistant.const import ( VOLUME_GALLONS, VOLUME_LITERS, VOLUME_MILLILITERS, + UnitOfEnergy, UnitOfVolumetricFlux, ) from homeassistant.exceptions import HomeAssistantError @@ -73,10 +70,10 @@ INVALID_SYMBOL = "bob" (DistanceConverter, LENGTH_YARD), (DistanceConverter, LENGTH_FEET), (DistanceConverter, LENGTH_INCHES), - (EnergyConverter, ENERGY_WATT_HOUR), - (EnergyConverter, ENERGY_KILO_WATT_HOUR), - (EnergyConverter, ENERGY_MEGA_WATT_HOUR), - (EnergyConverter, ENERGY_GIGA_JOULE), + (EnergyConverter, UnitOfEnergy.WATT_HOUR), + (EnergyConverter, UnitOfEnergy.KILO_WATT_HOUR), + (EnergyConverter, UnitOfEnergy.MEGA_WATT_HOUR), + (EnergyConverter, UnitOfEnergy.GIGA_JOULE), (MassConverter, MASS_GRAMS), (MassConverter, MASS_KILOGRAMS), (MassConverter, MASS_MICROGRAMS), @@ -120,7 +117,7 @@ def test_convert_same_unit(converter: type[BaseUnitConverter], valid_unit: str) "converter,valid_unit", [ (DistanceConverter, LENGTH_KILOMETERS), - (EnergyConverter, ENERGY_KILO_WATT_HOUR), + (EnergyConverter, UnitOfEnergy.KILO_WATT_HOUR), (MassConverter, MASS_GRAMS), (PowerConverter, POWER_WATT), (PressureConverter, PRESSURE_PA), @@ -146,7 +143,7 @@ def test_convert_invalid_unit( "converter,from_unit,to_unit", [ (DistanceConverter, LENGTH_KILOMETERS, LENGTH_METERS), - (EnergyConverter, ENERGY_WATT_HOUR, ENERGY_KILO_WATT_HOUR), + (EnergyConverter, UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR), (MassConverter, MASS_GRAMS, MASS_KILOGRAMS), (PowerConverter, POWER_WATT, POWER_KILO_WATT), (PressureConverter, PRESSURE_HPA, PRESSURE_INHG), @@ -167,7 +164,7 @@ def test_convert_nonnumeric_value( "converter,from_unit,to_unit,expected", [ (DistanceConverter, LENGTH_KILOMETERS, LENGTH_METERS, 1 / 1000), - (EnergyConverter, ENERGY_WATT_HOUR, ENERGY_KILO_WATT_HOUR, 1000), + (EnergyConverter, UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR, 1000), (PowerConverter, POWER_WATT, POWER_KILO_WATT, 1000), (PressureConverter, PRESSURE_HPA, PRESSURE_INHG, pytest.approx(33.86389)), ( @@ -261,14 +258,14 @@ def test_distance_convert( @pytest.mark.parametrize( "value,from_unit,expected,to_unit", [ - (10, ENERGY_WATT_HOUR, 0.01, ENERGY_KILO_WATT_HOUR), - (10, ENERGY_WATT_HOUR, 0.00001, ENERGY_MEGA_WATT_HOUR), - (10, ENERGY_KILO_WATT_HOUR, 10000, ENERGY_WATT_HOUR), - (10, ENERGY_KILO_WATT_HOUR, 0.01, ENERGY_MEGA_WATT_HOUR), - (10, ENERGY_MEGA_WATT_HOUR, 10000000, ENERGY_WATT_HOUR), - (10, ENERGY_MEGA_WATT_HOUR, 10000, ENERGY_KILO_WATT_HOUR), - (10, ENERGY_GIGA_JOULE, 10000 / 3.6, ENERGY_KILO_WATT_HOUR), - (10, ENERGY_GIGA_JOULE, 10 / 3.6, ENERGY_MEGA_WATT_HOUR), + (10, UnitOfEnergy.WATT_HOUR, 0.01, UnitOfEnergy.KILO_WATT_HOUR), + (10, UnitOfEnergy.WATT_HOUR, 0.00001, UnitOfEnergy.MEGA_WATT_HOUR), + (10, UnitOfEnergy.KILO_WATT_HOUR, 10000, UnitOfEnergy.WATT_HOUR), + (10, UnitOfEnergy.KILO_WATT_HOUR, 0.01, UnitOfEnergy.MEGA_WATT_HOUR), + (10, UnitOfEnergy.MEGA_WATT_HOUR, 10000000, UnitOfEnergy.WATT_HOUR), + (10, UnitOfEnergy.MEGA_WATT_HOUR, 10000, UnitOfEnergy.KILO_WATT_HOUR), + (10, UnitOfEnergy.GIGA_JOULE, 10000 / 3.6, UnitOfEnergy.KILO_WATT_HOUR), + (10, UnitOfEnergy.GIGA_JOULE, 10 / 3.6, UnitOfEnergy.MEGA_WATT_HOUR), ], ) def test_energy_convert(