mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Cache sensor precision calculation (#140019)
This commit is contained in:
parent
02e9002466
commit
e2c050ed40
@ -675,22 +675,13 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
):
|
):
|
||||||
# Deduce the precision by finding the decimal point, if any
|
# Deduce the precision by finding the decimal point, if any
|
||||||
value_s = str(value)
|
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
|
# 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
|
# For example 1.1 Wh should be rendered as 0.0011 kWh, not 0.0 kWh
|
||||||
ratio_log = max(
|
precision = (
|
||||||
0,
|
len(value_s) - value_s.index(".") - 1 if "." in value_s else 0
|
||||||
log10(
|
) + converter.get_unit_floored_log_ratio(
|
||||||
converter.get_unit_ratio(
|
|
||||||
native_unit_of_measurement, unit_of_measurement
|
native_unit_of_measurement, unit_of_measurement
|
||||||
)
|
)
|
||||||
),
|
|
||||||
)
|
|
||||||
precision = precision + floor(ratio_log)
|
|
||||||
|
|
||||||
value = f"{converted_numerical_value:z.{precision}f}"
|
value = f"{converted_numerical_value:z.{precision}f}"
|
||||||
else:
|
else:
|
||||||
value = converted_numerical_value
|
value = converted_numerical_value
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
from math import floor, log10
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONCENTRATION_PARTS_PER_BILLION,
|
CONCENTRATION_PARTS_PER_BILLION,
|
||||||
@ -144,6 +145,15 @@ class BaseUnitConverter:
|
|||||||
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
|
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
|
||||||
return from_ratio / to_ratio
|
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
|
@classmethod
|
||||||
@lru_cache
|
@lru_cache
|
||||||
def _are_unit_inverses(cls, from_unit: str | None, to_unit: str | None) -> bool:
|
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"),
|
("converter", "from_unit", "to_unit", "expected"),
|
||||||
[
|
[
|
||||||
# Process all items in _GET_UNIT_RATIO
|
# Process all items in _GET_UNIT_RATIO
|
||||||
(converter, item[0], item[1], item[2])
|
(converter, from_unit, to_unit, expected)
|
||||||
for converter, item in _GET_UNIT_RATIO.items()
|
for converter, (from_unit, to_unit, expected) in _GET_UNIT_RATIO.items()
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_get_unit_ratio(
|
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)
|
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(
|
@pytest.mark.parametrize(
|
||||||
("converter", "value", "from_unit", "expected", "to_unit"),
|
("converter", "value", "from_unit", "expected", "to_unit"),
|
||||||
[
|
[
|
||||||
# Process all items in _CONVERTED_VALUE
|
# 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 converter, item in _CONVERTED_VALUE.items()
|
||||||
for list_item in item
|
for value, from_unit, expected, to_unit in item
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_unit_conversion(
|
def test_unit_conversion(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user