Skip to content

Commit

Permalink
Migrate energy units to an enum (#80998)
Browse files Browse the repository at this point in the history
  • Loading branch information
epenet committed Oct 26, 2022
1 parent 9d34420 commit 842cb18
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 93 deletions.
25 changes: 11 additions & 14 deletions homeassistant/components/energy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@
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,
Expand All @@ -46,10 +43,10 @@
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__)
Expand Down Expand Up @@ -286,17 +283,17 @@ def _update_cost(self) -> None: # noqa: C901
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

Expand All @@ -319,11 +316,11 @@ def _update_cost(self) -> None: # noqa: C901
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:
Expand Down
21 changes: 9 additions & 12 deletions homeassistant/components/energy/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@
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

Expand All @@ -26,10 +23,10 @@
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(
Expand All @@ -43,10 +40,10 @@
)
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),
}
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/energy/websocket_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
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,
Expand Down Expand Up @@ -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(
Expand Down
14 changes: 13 additions & 1 deletion homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,11 +486,23 @@ class Platform(StrEnum):
# 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"
Expand Down
23 changes: 10 additions & 13 deletions homeassistant/util/unit_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -46,6 +42,7 @@
VOLUME_GALLONS,
VOLUME_LITERS,
VOLUME_MILLILITERS,
UnitOfEnergy,
UnitOfVolumetricFlux,
)
from homeassistant.exceptions import HomeAssistantError
Expand Down Expand Up @@ -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,
}


Expand Down
35 changes: 16 additions & 19 deletions tests/components/energy/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@
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
Expand Down Expand Up @@ -143,7 +140,7 @@ def _compile_statistics(_):
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,
}

Expand Down Expand Up @@ -347,7 +344,7 @@ def _compile_statistics(_):
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,
}

Expand Down Expand Up @@ -553,7 +550,7 @@ def _compile_statistics(_):
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,
}

Expand Down Expand Up @@ -701,10 +698,10 @@ def _compile_statistics(_):
@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(
Expand Down Expand Up @@ -767,18 +764,18 @@ 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(
setup_integration, hass, hass_storage, price_unit, factor
) -> 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 = {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
21 changes: 8 additions & 13 deletions tests/components/energy/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
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

Expand Down Expand Up @@ -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(
Expand Down
Loading

0 comments on commit 842cb18

Please sign in to comment.