mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Add significant Change support for number (#105863)
This commit is contained in:
parent
a6d8a82f3e
commit
117ff21c48
@ -78,7 +78,7 @@ __dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
|
||||
class NumberDeviceClass(StrEnum):
|
||||
"""Device class for numbers."""
|
||||
|
||||
# NumberDeviceClass should be aligned with NumberDeviceClass
|
||||
# NumberDeviceClass should be aligned with SensorDeviceClass
|
||||
|
||||
APPARENT_POWER = "apparent_power"
|
||||
"""Apparent power.
|
||||
|
92
homeassistant/components/number/significant_change.py
Normal file
92
homeassistant/components/number/significant_change.py
Normal file
@ -0,0 +1,92 @@
|
||||
"""Helper to test significant Number state changes."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
PERCENTAGE,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.significant_change import (
|
||||
check_absolute_change,
|
||||
check_percentage_change,
|
||||
check_valid_float,
|
||||
)
|
||||
|
||||
from .const import NumberDeviceClass
|
||||
|
||||
|
||||
def _absolute_and_relative_change(
|
||||
old_state: int | float | None,
|
||||
new_state: int | float | None,
|
||||
absolute_change: int | float,
|
||||
percentage_change: int | float,
|
||||
) -> bool:
|
||||
return check_absolute_change(
|
||||
old_state, new_state, absolute_change
|
||||
) and check_percentage_change(old_state, new_state, percentage_change)
|
||||
|
||||
|
||||
@callback
|
||||
def async_check_significant_change(
|
||||
hass: HomeAssistant,
|
||||
old_state: str,
|
||||
old_attrs: dict,
|
||||
new_state: str,
|
||||
new_attrs: dict,
|
||||
**kwargs: Any,
|
||||
) -> bool | None:
|
||||
"""Test if state significantly changed."""
|
||||
if (device_class := new_attrs.get(ATTR_DEVICE_CLASS)) is None:
|
||||
return None
|
||||
|
||||
absolute_change: float | None = None
|
||||
percentage_change: float | None = None
|
||||
|
||||
# special for temperature
|
||||
if device_class == NumberDeviceClass.TEMPERATURE:
|
||||
if new_attrs.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.FAHRENHEIT:
|
||||
absolute_change = 1.0
|
||||
else:
|
||||
absolute_change = 0.5
|
||||
|
||||
# special for percentage
|
||||
elif device_class in (
|
||||
NumberDeviceClass.BATTERY,
|
||||
NumberDeviceClass.HUMIDITY,
|
||||
NumberDeviceClass.MOISTURE,
|
||||
):
|
||||
absolute_change = 1.0
|
||||
|
||||
# special for power factor
|
||||
elif device_class == NumberDeviceClass.POWER_FACTOR:
|
||||
if new_attrs.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE:
|
||||
absolute_change = 1.0
|
||||
else:
|
||||
absolute_change = 0.1
|
||||
percentage_change = 2.0
|
||||
|
||||
# default for all other classified
|
||||
else:
|
||||
absolute_change = 1.0
|
||||
percentage_change = 2.0
|
||||
|
||||
if not check_valid_float(new_state):
|
||||
# New state is invalid, don't report it
|
||||
return False
|
||||
|
||||
if not check_valid_float(old_state):
|
||||
# Old state was invalid, we should report again
|
||||
return True
|
||||
|
||||
if absolute_change is not None and percentage_change is not None:
|
||||
return _absolute_and_relative_change(
|
||||
float(old_state), float(new_state), absolute_change, percentage_change
|
||||
)
|
||||
if absolute_change is not None:
|
||||
return check_absolute_change(
|
||||
float(old_state), float(new_state), absolute_change
|
||||
)
|
94
tests/components/number/test_significant_change.py
Normal file
94
tests/components/number/test_significant_change.py
Normal file
@ -0,0 +1,94 @@
|
||||
"""Test the Number significant change platform."""
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.number import NumberDeviceClass
|
||||
from homeassistant.components.number.significant_change import (
|
||||
async_check_significant_change,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
PERCENTAGE,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
|
||||
AQI_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.AQI}
|
||||
BATTERY_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.BATTERY}
|
||||
CO_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.CO}
|
||||
CO2_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.CO2}
|
||||
HUMIDITY_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.HUMIDITY}
|
||||
MOISTURE_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.MOISTURE}
|
||||
PM1_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.PM1}
|
||||
PM10_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.PM10}
|
||||
PM25_ATTRS = {ATTR_DEVICE_CLASS: NumberDeviceClass.PM25}
|
||||
POWER_FACTOR_ATTRS = {
|
||||
ATTR_DEVICE_CLASS: NumberDeviceClass.POWER_FACTOR,
|
||||
}
|
||||
POWER_FACTOR_ATTRS_PERCENTAGE = {
|
||||
ATTR_DEVICE_CLASS: NumberDeviceClass.POWER_FACTOR,
|
||||
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
|
||||
}
|
||||
TEMP_CELSIUS_ATTRS = {
|
||||
ATTR_DEVICE_CLASS: NumberDeviceClass.TEMPERATURE,
|
||||
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS,
|
||||
}
|
||||
TEMP_FREEDOM_ATTRS = {
|
||||
ATTR_DEVICE_CLASS: NumberDeviceClass.TEMPERATURE,
|
||||
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.FAHRENHEIT,
|
||||
}
|
||||
VOLATILE_ORGANIC_COMPOUNDS_ATTRS = {
|
||||
ATTR_DEVICE_CLASS: NumberDeviceClass.VOLATILE_ORGANIC_COMPOUNDS
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("old_state", "new_state", "attrs", "result"),
|
||||
[
|
||||
("0", "0.9", {}, None),
|
||||
("0", "1", AQI_ATTRS, True),
|
||||
("1", "0", AQI_ATTRS, True),
|
||||
("0.1", "0.5", AQI_ATTRS, False),
|
||||
("0.5", "0.1", AQI_ATTRS, False),
|
||||
("99", "100", AQI_ATTRS, False),
|
||||
("100", "99", AQI_ATTRS, False),
|
||||
("101", "99", AQI_ATTRS, False),
|
||||
("99", "101", AQI_ATTRS, True),
|
||||
("100", "100", BATTERY_ATTRS, False),
|
||||
("100", "99", BATTERY_ATTRS, True),
|
||||
("0", "1", CO_ATTRS, True),
|
||||
("0.1", "0.5", CO_ATTRS, False),
|
||||
("0", "1", CO2_ATTRS, True),
|
||||
("0.1", "0.5", CO2_ATTRS, False),
|
||||
("100", "100", HUMIDITY_ATTRS, False),
|
||||
("100", "99", HUMIDITY_ATTRS, True),
|
||||
("100", "100", MOISTURE_ATTRS, False),
|
||||
("100", "99", MOISTURE_ATTRS, True),
|
||||
("0", "1", PM1_ATTRS, True),
|
||||
("0.1", "0.5", PM1_ATTRS, False),
|
||||
("0", "1", PM10_ATTRS, True),
|
||||
("0.1", "0.5", PM10_ATTRS, False),
|
||||
("0", "1", PM25_ATTRS, True),
|
||||
("0.1", "0.5", PM25_ATTRS, False),
|
||||
("0.1", "0.2", POWER_FACTOR_ATTRS, True),
|
||||
("0.1", "0.19", POWER_FACTOR_ATTRS, False),
|
||||
("1", "2", POWER_FACTOR_ATTRS_PERCENTAGE, True),
|
||||
("1", "1.9", POWER_FACTOR_ATTRS_PERCENTAGE, False),
|
||||
("12", "12", TEMP_CELSIUS_ATTRS, False),
|
||||
("12", "13", TEMP_CELSIUS_ATTRS, True),
|
||||
("12.1", "12.2", TEMP_CELSIUS_ATTRS, False),
|
||||
("70", "71", TEMP_FREEDOM_ATTRS, True),
|
||||
("70", "70.5", TEMP_FREEDOM_ATTRS, False),
|
||||
("fail", "70", TEMP_FREEDOM_ATTRS, True),
|
||||
("70", "fail", TEMP_FREEDOM_ATTRS, False),
|
||||
("0", "1", VOLATILE_ORGANIC_COMPOUNDS_ATTRS, True),
|
||||
("0.1", "0.5", VOLATILE_ORGANIC_COMPOUNDS_ATTRS, False),
|
||||
],
|
||||
)
|
||||
async def test_significant_change_temperature(
|
||||
old_state, new_state, attrs, result
|
||||
) -> None:
|
||||
"""Detect temperature significant changes."""
|
||||
assert (
|
||||
async_check_significant_change(None, old_state, attrs, new_state, attrs)
|
||||
is result
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user