Skip to content
Snippets Groups Projects
Unverified Commit e2c050ed authored by J. Nick Koston's avatar J. Nick Koston Committed by GitHub
Browse files

Cache sensor precision calculation (#140019)

parent 02e90024
No related branches found
No related tags found
No related merge requests found
......@@ -675,22 +675,13 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
):
# Deduce the precision by finding the decimal point, if any
value_s = str(value)
precision = (
len(value_s) - value_s.index(".") - 1 if "." in value_s else 0
)
# Scale the precision when converting to a larger unit
# For example 1.1 Wh should be rendered as 0.0011 kWh, not 0.0 kWh
ratio_log = max(
0,
log10(
converter.get_unit_ratio(
native_unit_of_measurement, unit_of_measurement
)
),
precision = (
len(value_s) - value_s.index(".") - 1 if "." in value_s else 0
) + converter.get_unit_floored_log_ratio(
native_unit_of_measurement, unit_of_measurement
)
precision = precision + floor(ratio_log)
value = f"{converted_numerical_value:z.{precision}f}"
else:
value = converted_numerical_value
......
......@@ -4,6 +4,7 @@ from __future__ import annotations
from collections.abc import Callable
from functools import lru_cache
from math import floor, log10
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
......@@ -144,6 +145,15 @@ class BaseUnitConverter:
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
return from_ratio / to_ratio
@classmethod
@lru_cache
def get_unit_floored_log_ratio(
cls, from_unit: str | None, to_unit: str | None
) -> float:
"""Get floored base10 log ratio between units of measurement."""
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
return floor(max(0, log10(from_ratio / to_ratio)))
@classmethod
@lru_cache
def _are_unit_inverses(cls, from_unit: str | None, to_unit: str | None) -> bool:
......
......@@ -902,8 +902,8 @@ def test_convert_nonnumeric_value(
("converter", "from_unit", "to_unit", "expected"),
[
# Process all items in _GET_UNIT_RATIO
(converter, item[0], item[1], item[2])
for converter, item in _GET_UNIT_RATIO.items()
(converter, from_unit, to_unit, expected)
for converter, (from_unit, to_unit, expected) in _GET_UNIT_RATIO.items()
],
)
def test_get_unit_ratio(
......@@ -915,13 +915,34 @@ def test_get_unit_ratio(
assert converter.get_unit_ratio(to_unit, from_unit) == pytest.approx(1 / ratio)
@pytest.mark.parametrize(
("converter", "from_unit", "to_unit", "expected"),
[
# Process all items in _GET_UNIT_RATIO
(converter, from_unit, to_unit, expected)
for converter, (from_unit, to_unit, expected) in _GET_UNIT_RATIO.items()
],
)
def get_unit_floored_log_ratio(
converter: type[BaseUnitConverter], from_unit: str, to_unit: str, expected: float
) -> None:
"""Test floored log unit ratio.
Should not use pytest.approx since we are checking these
values are exact.
"""
ratio = converter.get_unit_floored_log_ratio(from_unit, to_unit)
assert ratio == expected
assert converter.get_unit_floored_log_ratio(to_unit, from_unit) == 1 / ratio
@pytest.mark.parametrize(
("converter", "value", "from_unit", "expected", "to_unit"),
[
# Process all items in _CONVERTED_VALUE
(converter, list_item[0], list_item[1], list_item[2], list_item[3])
(converter, value, from_unit, expected, to_unit)
for converter, item in _CONVERTED_VALUE.items()
for list_item in item
for value, from_unit, expected, to_unit in item
],
)
def test_unit_conversion(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment