mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Limit precision when stringifying float states (#48822)
* Limit precision when stringifying float states * Add test * Fix typing * Move StateType * Update * Move conversion to entity helper * Address review comments * Tweak precision * Tweak * Make _stringify_state an instance method
This commit is contained in:
parent
5e00fdccfd
commit
d2fd504442
@ -7,6 +7,8 @@ from collections.abc import Awaitable, Iterable, Mapping
|
||||
from datetime import datetime, timedelta
|
||||
import functools as ft
|
||||
import logging
|
||||
import math
|
||||
import sys
|
||||
from timeit import default_timer as timer
|
||||
from typing import Any
|
||||
|
||||
@ -43,6 +45,10 @@ DATA_ENTITY_SOURCE = "entity_info"
|
||||
SOURCE_CONFIG_ENTRY = "config_entry"
|
||||
SOURCE_PLATFORM_CONFIG = "platform_config"
|
||||
|
||||
# Used when converting float states to string: limit precision according to machine
|
||||
# epsilon to make the string representation readable
|
||||
FLOAT_PRECISION = abs(int(math.floor(math.log10(abs(sys.float_info.epsilon))))) - 1
|
||||
|
||||
|
||||
@callback
|
||||
@bind_hass
|
||||
@ -327,6 +333,19 @@ class Entity(ABC):
|
||||
|
||||
self._async_write_ha_state()
|
||||
|
||||
def _stringify_state(self) -> str:
|
||||
"""Convert state to string."""
|
||||
if not self.available:
|
||||
return STATE_UNAVAILABLE
|
||||
state = self.state
|
||||
if state is None:
|
||||
return STATE_UNKNOWN
|
||||
if isinstance(state, float):
|
||||
# If the entity's state is a float, limit precision according to machine
|
||||
# epsilon to make the string representation readable
|
||||
return f"{state:.{FLOAT_PRECISION}}"
|
||||
return str(state)
|
||||
|
||||
@callback
|
||||
def _async_write_ha_state(self) -> None:
|
||||
"""Write the state to the state machine."""
|
||||
@ -346,11 +365,8 @@ class Entity(ABC):
|
||||
attr = self.capability_attributes
|
||||
attr = dict(attr) if attr else {}
|
||||
|
||||
if not self.available:
|
||||
state = STATE_UNAVAILABLE
|
||||
else:
|
||||
sstate = self.state
|
||||
state = STATE_UNKNOWN if sstate is None else str(sstate)
|
||||
state = self._stringify_state()
|
||||
if self.available:
|
||||
attr.update(self.state_attributes or {})
|
||||
extra_state_attributes = self.extra_state_attributes
|
||||
# Backwards compatibility for "device_state_attributes" deprecated in 2021.4
|
||||
|
@ -162,6 +162,26 @@ async def test_increment(hass):
|
||||
assert float(state.state) == 51
|
||||
|
||||
|
||||
async def test_rounding(hass):
|
||||
"""Test increment introducing floating point error is rounded."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{DOMAIN: {"test_2": {"initial": 2.4, "min": 0, "max": 51, "step": 1.2}}},
|
||||
)
|
||||
entity_id = "input_number.test_2"
|
||||
assert 2.4 + 1.2 != 3.6
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert float(state.state) == 2.4
|
||||
|
||||
await increment(hass, entity_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert float(state.state) == 3.6
|
||||
|
||||
|
||||
async def test_decrement(hass):
|
||||
"""Test decrement method."""
|
||||
assert await async_setup_component(
|
||||
|
@ -774,3 +774,17 @@ async def test_get_supported_features_raises_on_unknown(hass):
|
||||
"""Test get_supported_features raises on unknown entity_id."""
|
||||
with pytest.raises(HomeAssistantError):
|
||||
entity.get_supported_features(hass, "hello.world")
|
||||
|
||||
|
||||
async def test_float_conversion(hass):
|
||||
"""Test conversion of float state to string rounds."""
|
||||
assert 2.4 + 1.2 != 3.6
|
||||
with patch.object(entity.Entity, "state", PropertyMock(return_value=2.4 + 1.2)):
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.entity_id = "hello.world"
|
||||
ent.async_write_ha_state()
|
||||
|
||||
state = hass.states.get("hello.world")
|
||||
assert state is not None
|
||||
assert state.state == "3.6"
|
||||
|
Loading…
x
Reference in New Issue
Block a user