diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index c3d99ab1e94..753a66926ad 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -48,10 +48,14 @@ STATISTICS_META_BAKERY = "recorder_statistics_bakery" UNIT_CONVERSIONS = { PRESSURE_PA: lambda x, units: pressure_util.convert( x, PRESSURE_PA, units.pressure_unit - ), + ) + if x is not None + else None, TEMP_CELSIUS: lambda x, units: temperature_util.convert( x, TEMP_CELSIUS, units.temperature_unit - ), + ) + if x is not None + else None, } _LOGGER = logging.getLogger(__name__) @@ -133,8 +137,17 @@ def _get_meta_data(hass, session, statistic_ids): return {id: _meta(result, id) for id in statistic_ids} +def _unit_system_unit(unit: str, units) -> str: + if unit == PRESSURE_PA: + return units.pressure_unit + if unit == TEMP_CELSIUS: + return units.temperature_unit + return unit + + def list_statistic_ids(hass, statistic_type=None): """Return statistic_ids.""" + units = hass.config.units with session_scope(hass=hass) as session: baked_query = hass.data[STATISTICS_BAKERY]( lambda session: session.query(*QUERY_STATISTIC_IDS).distinct() @@ -149,8 +162,12 @@ def list_statistic_ids(hass, statistic_type=None): result = execute(baked_query(session)) statistic_ids_list = [statistic_id[0] for statistic_id in result] + statistic_ids = _get_meta_data(hass, session, statistic_ids_list) + for statistic_id in statistic_ids.values(): + unit = _unit_system_unit(statistic_id["unit_of_measurement"], units) + statistic_id["unit_of_measurement"] = unit - return list(_get_meta_data(hass, session, statistic_ids_list).values()) + return list(statistic_ids.values()) def statistics_during_period(hass, start_time, end_time=None, statistic_ids=None): @@ -227,7 +244,7 @@ def _sorted_statistics_to_dict( # Called in a tight loop so cache the function here _process_timestamp_to_utc_isoformat = process_timestamp_to_utc_isoformat - # Append all statistic entries + # Append all statistic entries, and do unit conversion for ent_id, group in groupby(stats, lambda state: state.statistic_id): unit = meta_data[ent_id]["unit_of_measurement"] convert = UNIT_CONVERSIONS.get(unit, lambda x, units: x) diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index ac57069acf0..c4f85717cac 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -14,6 +14,7 @@ import homeassistant.core as ha from homeassistant.helpers.json import JSONEncoder from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util +from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM from tests.common import init_recorder_component from tests.components.recorder.common import trigger_db_commit, wait_recording_done @@ -829,23 +830,46 @@ async def test_entity_ids_limit_via_api_with_skip_initial_state(hass, hass_clien assert response_json[1][0]["entity_id"] == "light.cow" -async def test_statistics_during_period(hass, hass_ws_client): +POWER_SENSOR_ATTRIBUTES = { + "device_class": "power", + "state_class": "measurement", + "unit_of_measurement": "kW", +} +PRESSURE_SENSOR_ATTRIBUTES = { + "device_class": "pressure", + "state_class": "measurement", + "unit_of_measurement": "hPa", +} +TEMPERATURE_SENSOR_ATTRIBUTES = { + "device_class": "temperature", + "state_class": "measurement", + "unit_of_measurement": "°C", +} + + +@pytest.mark.parametrize( + "units, attributes, state, value", + [ + (IMPERIAL_SYSTEM, POWER_SENSOR_ATTRIBUTES, 10, 10000), + (METRIC_SYSTEM, POWER_SENSOR_ATTRIBUTES, 10, 10000), + (IMPERIAL_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, 10, 50), + (METRIC_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, 10, 10), + (IMPERIAL_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, 1000, 14.503774389728312), + (METRIC_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, 1000, 100000), + ], +) +async def test_statistics_during_period( + hass, hass_ws_client, units, attributes, state, value +): """Test statistics_during_period.""" now = dt_util.utcnow() + hass.config.units = units await hass.async_add_executor_job(init_recorder_component, hass) await async_setup_component(hass, "history", {}) await async_setup_component(hass, "sensor", {}) await hass.async_add_executor_job(hass.data[recorder.DATA_INSTANCE].block_till_done) - hass.states.async_set( - "sensor.test", - 10, - attributes={ - "device_class": "temperature", - "state_class": "measurement", - "unit_of_measurement": "°C", - }, - ) + hass.states.async_set("sensor.test", state, attributes=attributes) await hass.async_block_till_done() await hass.async_add_executor_job(trigger_db_commit, hass) @@ -884,9 +908,9 @@ async def test_statistics_during_period(hass, hass_ws_client): { "statistic_id": "sensor.test", "start": now.isoformat(), - "mean": approx(10.0), - "min": approx(10.0), - "max": approx(10.0), + "mean": approx(value), + "min": approx(value), + "max": approx(value), "last_reset": None, "state": None, "sum": None, @@ -944,23 +968,27 @@ async def test_statistics_during_period_bad_end_time(hass, hass_ws_client): assert response["error"]["code"] == "invalid_end_time" -async def test_list_statistic_ids(hass, hass_ws_client): +@pytest.mark.parametrize( + "units, attributes, unit", + [ + (IMPERIAL_SYSTEM, POWER_SENSOR_ATTRIBUTES, "W"), + (METRIC_SYSTEM, POWER_SENSOR_ATTRIBUTES, "W"), + (IMPERIAL_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, "°F"), + (METRIC_SYSTEM, TEMPERATURE_SENSOR_ATTRIBUTES, "°C"), + (IMPERIAL_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, "psi"), + (METRIC_SYSTEM, PRESSURE_SENSOR_ATTRIBUTES, "Pa"), + ], +) +async def test_list_statistic_ids(hass, hass_ws_client, units, attributes, unit): """Test list_statistic_ids.""" now = dt_util.utcnow() + hass.config.units = units await hass.async_add_executor_job(init_recorder_component, hass) await async_setup_component(hass, "history", {"history": {}}) await async_setup_component(hass, "sensor", {}) await hass.async_add_executor_job(hass.data[recorder.DATA_INSTANCE].block_till_done) - hass.states.async_set( - "sensor.test", - 10, - attributes={ - "device_class": "temperature", - "state_class": "measurement", - "unit_of_measurement": "°C", - }, - ) + hass.states.async_set("sensor.test", 10, attributes=attributes) await hass.async_block_till_done() await hass.async_add_executor_job(trigger_db_commit, hass) @@ -979,7 +1007,7 @@ async def test_list_statistic_ids(hass, hass_ws_client): response = await client.receive_json() assert response["success"] assert response["result"] == [ - {"statistic_id": "sensor.test", "unit_of_measurement": "°C"} + {"statistic_id": "sensor.test", "unit_of_measurement": unit} ] await client.send_json( @@ -988,7 +1016,7 @@ async def test_list_statistic_ids(hass, hass_ws_client): response = await client.receive_json() assert response["success"] assert response["result"] == [ - {"statistic_id": "sensor.test", "unit_of_measurement": "°C"} + {"statistic_id": "sensor.test", "unit_of_measurement": unit} ] await client.send_json( @@ -997,7 +1025,7 @@ async def test_list_statistic_ids(hass, hass_ws_client): response = await client.receive_json() assert response["success"] assert response["result"] == [ - {"statistic_id": "sensor.test", "unit_of_measurement": "°C"} + {"statistic_id": "sensor.test", "unit_of_measurement": unit} ] await client.send_json(