2021-12-10 18:59:27 +01:00
|
|
|
"""The test for sensor entity."""
|
2024-03-08 14:47:22 +01:00
|
|
|
|
2023-01-16 11:00:07 +01:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2023-08-21 20:14:07 +03:00
|
|
|
from datetime import UTC, date, datetime
|
2022-04-19 08:01:52 +01:00
|
|
|
from decimal import Decimal
|
2024-01-30 18:55:59 +01:00
|
|
|
import logging
|
2023-12-21 00:02:20 +01:00
|
|
|
from types import ModuleType
|
2023-01-16 11:00:07 +01:00
|
|
|
from typing import Any
|
2021-11-18 14:11:44 +01:00
|
|
|
|
2021-11-11 19:36:53 +01:00
|
|
|
import pytest
|
2024-06-06 17:41:37 +02:00
|
|
|
from typing_extensions import Generator
|
2021-11-11 19:36:53 +01:00
|
|
|
|
2023-12-21 00:02:20 +01:00
|
|
|
from homeassistant.components import sensor
|
2022-11-17 14:00:28 +01:00
|
|
|
from homeassistant.components.number import NumberDeviceClass
|
2023-01-25 20:45:50 +01:00
|
|
|
from homeassistant.components.sensor import (
|
2023-02-04 11:49:24 +01:00
|
|
|
DEVICE_CLASS_STATE_CLASSES,
|
2023-01-25 20:45:50 +01:00
|
|
|
DEVICE_CLASS_UNITS,
|
2023-06-20 23:09:24 +02:00
|
|
|
DOMAIN as SENSOR_DOMAIN,
|
2023-10-02 13:01:26 +02:00
|
|
|
NON_NUMERIC_DEVICE_CLASSES,
|
2023-01-25 20:45:50 +01:00
|
|
|
SensorDeviceClass,
|
2023-06-20 23:09:24 +02:00
|
|
|
SensorEntity,
|
|
|
|
SensorEntityDescription,
|
2023-01-25 20:45:50 +01:00
|
|
|
SensorStateClass,
|
2023-07-02 20:53:50 -05:00
|
|
|
async_rounded_state,
|
2023-02-03 16:30:50 +01:00
|
|
|
async_update_suggested_units,
|
2023-01-25 20:45:50 +01:00
|
|
|
)
|
2023-06-20 23:09:24 +02:00
|
|
|
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
2021-11-11 19:36:53 +01:00
|
|
|
from homeassistant.const import (
|
|
|
|
ATTR_UNIT_OF_MEASUREMENT,
|
2023-01-12 09:20:00 +01:00
|
|
|
PERCENTAGE,
|
2021-11-18 14:11:44 +01:00
|
|
|
STATE_UNKNOWN,
|
2023-10-09 13:37:52 +02:00
|
|
|
EntityCategory,
|
2024-01-30 18:55:59 +01:00
|
|
|
UnitOfDataRate,
|
2023-01-09 14:26:52 +01:00
|
|
|
UnitOfEnergy,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength,
|
|
|
|
UnitOfMass,
|
|
|
|
UnitOfPressure,
|
|
|
|
UnitOfSpeed,
|
2022-12-02 09:11:15 +01:00
|
|
|
UnitOfTemperature,
|
2024-01-30 23:08:12 +01:00
|
|
|
UnitOfTime,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfVolume,
|
2024-01-30 15:01:08 +01:00
|
|
|
UnitOfVolumeFlowRate,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfVolumetricFlux,
|
2021-11-11 19:36:53 +01:00
|
|
|
)
|
2022-12-02 09:11:15 +01:00
|
|
|
from homeassistant.core import HomeAssistant, State
|
2022-03-30 15:43:04 +02:00
|
|
|
from homeassistant.helpers import entity_registry as er
|
2023-06-20 23:09:24 +02:00
|
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
2022-02-08 23:00:26 +01:00
|
|
|
from homeassistant.helpers.restore_state import STORAGE_KEY as RESTORE_STATE_KEY
|
2021-08-11 10:45:05 +02:00
|
|
|
from homeassistant.setup import async_setup_component
|
2021-08-13 12:35:23 +02:00
|
|
|
from homeassistant.util import dt as dt_util
|
2022-10-20 15:42:23 +02:00
|
|
|
from homeassistant.util.unit_system import METRIC_SYSTEM, US_CUSTOMARY_SYSTEM
|
2021-11-11 19:36:53 +01:00
|
|
|
|
2024-06-10 13:33:15 +02:00
|
|
|
from .common import MockRestoreSensor, MockSensor
|
|
|
|
|
2023-05-30 20:48:17 -05:00
|
|
|
from tests.common import (
|
2023-06-20 23:09:24 +02:00
|
|
|
MockConfigEntry,
|
2023-09-23 13:28:14 +02:00
|
|
|
MockEntityPlatform,
|
2023-06-20 23:09:24 +02:00
|
|
|
MockModule,
|
|
|
|
MockPlatform,
|
2023-05-30 20:48:17 -05:00
|
|
|
async_mock_restore_state_shutdown_restart,
|
2024-01-05 11:46:45 +01:00
|
|
|
help_test_all,
|
2023-12-21 00:02:20 +01:00
|
|
|
import_and_test_deprecated_constant_enum,
|
2023-06-20 23:09:24 +02:00
|
|
|
mock_config_flow,
|
|
|
|
mock_integration,
|
|
|
|
mock_platform,
|
2023-05-30 20:48:17 -05:00
|
|
|
mock_restore_cache_with_extra_data,
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform,
|
2023-05-30 20:48:17 -05:00
|
|
|
)
|
2022-02-08 23:00:26 +01:00
|
|
|
|
2023-06-20 23:09:24 +02:00
|
|
|
TEST_DOMAIN = "test"
|
|
|
|
|
2021-11-11 19:36:53 +01:00
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
("unit_system", "native_unit", "state_unit", "native_value", "state_value"),
|
2021-11-11 19:36:53 +01:00
|
|
|
[
|
2023-01-16 09:00:27 +01:00
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
2023-01-16 09:00:27 +01:00
|
|
|
),
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
|
|
|
UnitOfTemperature.CELSIUS,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
38,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
2023-01-16 09:00:27 +01:00
|
|
|
),
|
|
|
|
(
|
|
|
|
METRIC_SYSTEM,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
UnitOfTemperature.CELSIUS,
|
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"38",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
METRIC_SYSTEM,
|
|
|
|
UnitOfTemperature.CELSIUS,
|
|
|
|
UnitOfTemperature.CELSIUS,
|
2023-01-16 09:00:27 +01:00
|
|
|
38,
|
2023-01-25 08:55:46 +01:00
|
|
|
"38",
|
2023-01-16 09:00:27 +01:00
|
|
|
),
|
2021-11-11 19:36:53 +01:00
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_temperature_conversion(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2021-11-11 19:36:53 +01:00
|
|
|
unit_system,
|
|
|
|
native_unit,
|
|
|
|
state_unit,
|
|
|
|
native_value,
|
|
|
|
state_value,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2021-11-11 19:36:53 +01:00
|
|
|
"""Test temperature conversion."""
|
|
|
|
hass.config.units = unit_system
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2021-11-11 19:36:53 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=str(native_value),
|
|
|
|
native_unit_of_measurement=native_unit,
|
2021-12-17 05:07:18 -05:00
|
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
2021-11-11 19:36:53 +01:00
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2021-11-11 19:36:53 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == state_value
|
2021-11-11 19:36:53 +01:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == state_unit
|
2021-08-11 10:45:05 +02:00
|
|
|
|
|
|
|
|
2024-03-19 09:01:07 +01:00
|
|
|
@pytest.mark.parametrize("device_class", [None, SensorDeviceClass.PRESSURE])
|
2022-04-04 20:02:40 +02:00
|
|
|
async def test_temperature_conversion_wrong_device_class(
|
2024-03-28 12:07:55 +01:00
|
|
|
hass: HomeAssistant, device_class
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-04-04 20:02:40 +02:00
|
|
|
"""Test temperatures are not converted if the sensor has wrong device class."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-04-04 20:02:40 +02:00
|
|
|
name="Test",
|
|
|
|
native_value="0.0",
|
2023-01-16 09:00:27 +01:00
|
|
|
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
|
2022-04-04 20:02:40 +02:00
|
|
|
device_class=device_class,
|
2021-08-11 10:45:05 +02:00
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2021-08-11 10:45:05 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2022-04-04 20:02:40 +02:00
|
|
|
# Check temperature is not converted
|
2021-08-11 10:45:05 +02:00
|
|
|
state = hass.states.get(entity0.entity_id)
|
2022-04-04 20:02:40 +02:00
|
|
|
assert state.state == "0.0"
|
2023-01-16 09:00:27 +01:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.FAHRENHEIT
|
2021-08-13 12:35:23 +02:00
|
|
|
|
|
|
|
|
2024-03-19 09:01:07 +01:00
|
|
|
@pytest.mark.parametrize("state_class", ["measurement", "total_increasing"])
|
2022-01-11 13:58:35 +01:00
|
|
|
async def test_deprecated_last_reset(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
state_class,
|
|
|
|
) -> None:
|
2021-08-13 12:35:23 +02:00
|
|
|
"""Test warning on deprecated last reset."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-01-11 13:58:35 +01:00
|
|
|
name="Test", state_class=state_class, last_reset=dt_util.utc_from_timestamp(0)
|
2021-08-13 12:35:23 +02:00
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2021-08-13 12:35:23 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
2024-03-28 12:07:55 +01:00
|
|
|
"Entity sensor.test (<class 'tests.components.sensor.common.MockSensor'>) "
|
2022-01-11 13:58:35 +01:00
|
|
|
f"with state_class {state_class} has set last_reset. Setting last_reset for "
|
|
|
|
"entities with state_class other than 'total' is not supported. Please update "
|
2024-01-29 20:21:35 +01:00
|
|
|
"your configuration if state_class is manually configured."
|
2021-08-13 12:35:23 +02:00
|
|
|
) in caplog.text
|
2021-08-20 15:54:57 +02:00
|
|
|
|
2022-01-11 13:58:35 +01:00
|
|
|
state = hass.states.get("sensor.test")
|
2024-01-29 20:21:35 +01:00
|
|
|
assert state is None
|
2022-01-11 13:58:35 +01:00
|
|
|
|
2021-08-20 15:54:57 +02:00
|
|
|
|
2023-02-15 18:07:40 +01:00
|
|
|
async def test_datetime_conversion(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
) -> None:
|
2021-11-18 14:11:44 +01:00
|
|
|
"""Test conversion of datetime."""
|
2023-08-21 20:14:07 +03:00
|
|
|
test_timestamp = datetime(2017, 12, 19, 18, 29, 42, tzinfo=UTC)
|
2021-11-22 17:04:06 +01:00
|
|
|
test_local_timestamp = test_timestamp.astimezone(
|
|
|
|
dt_util.get_time_zone("Europe/Amsterdam")
|
|
|
|
)
|
2021-11-18 14:11:44 +01:00
|
|
|
test_date = date(2017, 12, 19)
|
2024-03-28 12:07:55 +01:00
|
|
|
entities = [
|
|
|
|
MockSensor(
|
|
|
|
name="Test",
|
|
|
|
native_value=test_timestamp,
|
|
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
|
|
),
|
|
|
|
MockSensor(
|
|
|
|
name="Test", native_value=test_date, device_class=SensorDeviceClass.DATE
|
|
|
|
),
|
|
|
|
MockSensor(
|
|
|
|
name="Test", native_value=None, device_class=SensorDeviceClass.TIMESTAMP
|
|
|
|
),
|
|
|
|
MockSensor(name="Test", native_value=None, device_class=SensorDeviceClass.DATE),
|
|
|
|
MockSensor(
|
|
|
|
name="Test",
|
|
|
|
native_value=test_local_timestamp,
|
|
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, entities)
|
2021-11-18 14:11:44 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
state = hass.states.get(entities[0].entity_id)
|
2021-11-18 14:11:44 +01:00
|
|
|
assert state.state == test_timestamp.isoformat()
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
state = hass.states.get(entities[1].entity_id)
|
2021-11-18 14:11:44 +01:00
|
|
|
assert state.state == test_date.isoformat()
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
state = hass.states.get(entities[2].entity_id)
|
2021-11-18 14:11:44 +01:00
|
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
state = hass.states.get(entities[3].entity_id)
|
2021-11-18 14:11:44 +01:00
|
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
state = hass.states.get(entities[4].entity_id)
|
2021-11-22 17:04:06 +01:00
|
|
|
assert state.state == test_timestamp.isoformat()
|
|
|
|
|
2021-11-18 14:11:44 +01:00
|
|
|
|
2023-03-02 20:20:26 +01:00
|
|
|
async def test_a_sensor_with_a_non_numeric_device_class(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
) -> None:
|
|
|
|
"""Test that a sensor with a non numeric device class will be non numeric.
|
|
|
|
|
|
|
|
A non numeric sensor with a valid device class should never be
|
|
|
|
handled as numeric because it has a device class.
|
|
|
|
"""
|
2023-08-21 20:14:07 +03:00
|
|
|
test_timestamp = datetime(2017, 12, 19, 18, 29, 42, tzinfo=UTC)
|
2023-03-02 20:20:26 +01:00
|
|
|
test_local_timestamp = test_timestamp.astimezone(
|
|
|
|
dt_util.get_time_zone("Europe/Amsterdam")
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entities = [
|
|
|
|
MockSensor(
|
|
|
|
name="Test",
|
|
|
|
native_value=test_local_timestamp,
|
|
|
|
native_unit_of_measurement="",
|
|
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
|
|
),
|
|
|
|
MockSensor(
|
|
|
|
name="Test",
|
|
|
|
native_value=test_local_timestamp,
|
|
|
|
state_class="",
|
|
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, entities)
|
2023-03-02 20:20:26 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
state = hass.states.get(entities[0].entity_id)
|
2023-03-02 20:20:26 +01:00
|
|
|
assert state.state == test_timestamp.isoformat()
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
state = hass.states.get(entities[1].entity_id)
|
2023-03-02 20:20:26 +01:00
|
|
|
assert state.state == test_timestamp.isoformat()
|
|
|
|
|
|
|
|
|
2021-11-18 14:11:44 +01:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
("device_class", "state_value", "provides"),
|
2021-11-18 14:11:44 +01:00
|
|
|
[
|
2022-02-05 08:42:29 +01:00
|
|
|
(SensorDeviceClass.DATE, "2021-01-09", "date"),
|
|
|
|
(SensorDeviceClass.TIMESTAMP, "2021-01-09T12:00:00+00:00", "datetime"),
|
2021-11-18 14:11:44 +01:00
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_deprecated_datetime_str(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
device_class,
|
|
|
|
state_value,
|
|
|
|
provides,
|
|
|
|
) -> None:
|
2021-11-18 14:11:44 +01:00
|
|
|
"""Test warning on deprecated str for a date(time) value."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-02-05 08:42:29 +01:00
|
|
|
name="Test", native_value=state_value, device_class=device_class
|
2021-11-18 14:11:44 +01:00
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2021-11-18 14:11:44 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
2022-06-17 12:17:13 +02:00
|
|
|
f"Invalid {provides}: sensor.test has {device_class} device class "
|
|
|
|
f"but provides state {state_value}:{type(state_value)}"
|
2021-11-18 14:11:44 +01:00
|
|
|
) in caplog.text
|
2021-11-22 17:04:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
async def test_reject_timezoneless_datetime_str(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
) -> None:
|
2021-11-22 17:04:06 +01:00
|
|
|
"""Test rejection of timezone-less datetime objects as timestamp."""
|
|
|
|
test_timestamp = datetime(2017, 12, 19, 18, 29, 42, tzinfo=None)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2021-12-17 05:07:18 -05:00
|
|
|
name="Test",
|
|
|
|
native_value=test_timestamp,
|
|
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
2021-11-22 17:04:06 +01:00
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2021-11-22 17:04:06 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"Invalid datetime: sensor.test provides state '2017-12-19 18:29:42', "
|
|
|
|
"which is missing timezone information"
|
|
|
|
) in caplog.text
|
2022-02-08 23:00:26 +01:00
|
|
|
|
|
|
|
|
|
|
|
RESTORE_DATA = {
|
2023-01-10 11:52:29 +01:00
|
|
|
"str": {"native_unit_of_measurement": None, "native_value": "abc123"},
|
2022-02-08 23:00:26 +01:00
|
|
|
"int": {"native_unit_of_measurement": "°F", "native_value": 123},
|
|
|
|
"float": {"native_unit_of_measurement": "°F", "native_value": 123.0},
|
|
|
|
"date": {
|
2023-01-10 11:52:29 +01:00
|
|
|
"native_unit_of_measurement": None,
|
2022-02-08 23:00:26 +01:00
|
|
|
"native_value": {
|
|
|
|
"__type": "<class 'datetime.date'>",
|
|
|
|
"isoformat": date(2020, 2, 8).isoformat(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"datetime": {
|
2023-01-10 11:52:29 +01:00
|
|
|
"native_unit_of_measurement": None,
|
2022-02-08 23:00:26 +01:00
|
|
|
"native_value": {
|
|
|
|
"__type": "<class 'datetime.datetime'>",
|
2023-08-21 20:14:07 +03:00
|
|
|
"isoformat": datetime(2020, 2, 8, 15, tzinfo=UTC).isoformat(),
|
2022-02-08 23:00:26 +01:00
|
|
|
},
|
|
|
|
},
|
2022-04-19 08:01:52 +01:00
|
|
|
"Decimal": {
|
2023-01-10 11:52:29 +01:00
|
|
|
"native_unit_of_measurement": "kWh",
|
2022-04-19 08:01:52 +01:00
|
|
|
"native_value": {
|
|
|
|
"__type": "<class 'decimal.Decimal'>",
|
|
|
|
"decimal_str": "123.4",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"BadDecimal": {
|
|
|
|
"native_unit_of_measurement": "°F",
|
|
|
|
"native_value": {
|
|
|
|
"__type": "<class 'decimal.Decimal'>",
|
|
|
|
"decimal_str": "123f",
|
|
|
|
},
|
|
|
|
},
|
2022-02-08 23:00:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-04-19 08:01:52 +01:00
|
|
|
# None | str | int | float | date | datetime | Decimal:
|
2022-02-08 23:00:26 +01:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
("native_value", "native_value_type", "expected_extra_data", "device_class", "uom"),
|
2022-02-08 23:00:26 +01:00
|
|
|
[
|
2023-01-10 11:52:29 +01:00
|
|
|
("abc123", str, RESTORE_DATA["str"], None, None),
|
|
|
|
(
|
|
|
|
123,
|
|
|
|
int,
|
|
|
|
RESTORE_DATA["int"],
|
|
|
|
SensorDeviceClass.TEMPERATURE,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
123.0,
|
|
|
|
float,
|
|
|
|
RESTORE_DATA["float"],
|
|
|
|
SensorDeviceClass.TEMPERATURE,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
),
|
|
|
|
(date(2020, 2, 8), dict, RESTORE_DATA["date"], SensorDeviceClass.DATE, None),
|
2022-02-08 23:00:26 +01:00
|
|
|
(
|
2023-08-21 20:14:07 +03:00
|
|
|
datetime(2020, 2, 8, 15, tzinfo=UTC),
|
2022-02-08 23:00:26 +01:00
|
|
|
dict,
|
|
|
|
RESTORE_DATA["datetime"],
|
|
|
|
SensorDeviceClass.TIMESTAMP,
|
2023-01-10 11:52:29 +01:00
|
|
|
None,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Decimal("123.4"),
|
|
|
|
dict,
|
|
|
|
RESTORE_DATA["Decimal"],
|
|
|
|
SensorDeviceClass.ENERGY,
|
|
|
|
UnitOfEnergy.KILO_WATT_HOUR,
|
2022-02-08 23:00:26 +01:00
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_restore_sensor_save_state(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
|
|
|
hass_storage: dict[str, Any],
|
2022-02-08 23:00:26 +01:00
|
|
|
native_value,
|
|
|
|
native_value_type,
|
|
|
|
expected_extra_data,
|
|
|
|
device_class,
|
2023-01-10 11:52:29 +01:00
|
|
|
uom,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-02-08 23:00:26 +01:00
|
|
|
"""Test RestoreSensor."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockRestoreSensor(
|
2022-02-08 23:00:26 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=native_value,
|
2023-01-10 11:52:29 +01:00
|
|
|
native_unit_of_measurement=uom,
|
2022-02-08 23:00:26 +01:00
|
|
|
device_class=device_class,
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-02-08 23:00:26 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Trigger saving state
|
2023-05-30 20:48:17 -05:00
|
|
|
await async_mock_restore_state_shutdown_restart(hass)
|
2022-02-08 23:00:26 +01:00
|
|
|
|
|
|
|
assert len(hass_storage[RESTORE_STATE_KEY]["data"]) == 1
|
|
|
|
state = hass_storage[RESTORE_STATE_KEY]["data"][0]["state"]
|
|
|
|
assert state["entity_id"] == entity0.entity_id
|
|
|
|
extra_data = hass_storage[RESTORE_STATE_KEY]["data"][0]["extra_data"]
|
|
|
|
assert extra_data == expected_extra_data
|
|
|
|
assert type(extra_data["native_value"]) == native_value_type
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
("native_value", "native_value_type", "extra_data", "device_class", "uom"),
|
2022-02-08 23:00:26 +01:00
|
|
|
[
|
2023-01-10 11:52:29 +01:00
|
|
|
("abc123", str, RESTORE_DATA["str"], None, None),
|
2022-02-08 23:00:26 +01:00
|
|
|
(123, int, RESTORE_DATA["int"], SensorDeviceClass.TEMPERATURE, "°F"),
|
|
|
|
(123.0, float, RESTORE_DATA["float"], SensorDeviceClass.TEMPERATURE, "°F"),
|
2023-01-10 11:52:29 +01:00
|
|
|
(date(2020, 2, 8), date, RESTORE_DATA["date"], SensorDeviceClass.DATE, None),
|
2022-02-08 23:00:26 +01:00
|
|
|
(
|
2023-08-21 20:14:07 +03:00
|
|
|
datetime(2020, 2, 8, 15, tzinfo=UTC),
|
2022-02-08 23:00:26 +01:00
|
|
|
datetime,
|
|
|
|
RESTORE_DATA["datetime"],
|
|
|
|
SensorDeviceClass.TIMESTAMP,
|
2023-01-10 11:52:29 +01:00
|
|
|
None,
|
2022-02-08 23:00:26 +01:00
|
|
|
),
|
2022-04-19 08:01:52 +01:00
|
|
|
(
|
|
|
|
Decimal("123.4"),
|
|
|
|
Decimal,
|
|
|
|
RESTORE_DATA["Decimal"],
|
|
|
|
SensorDeviceClass.ENERGY,
|
2023-01-10 11:52:29 +01:00
|
|
|
"kWh",
|
2022-04-19 08:01:52 +01:00
|
|
|
),
|
2022-02-08 23:00:26 +01:00
|
|
|
(None, type(None), None, None, None),
|
|
|
|
(None, type(None), {}, None, None),
|
|
|
|
(None, type(None), {"beer": 123}, None, None),
|
|
|
|
(
|
|
|
|
None,
|
|
|
|
type(None),
|
|
|
|
{"native_unit_of_measurement": "°F", "native_value": {}},
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
),
|
2022-04-19 08:01:52 +01:00
|
|
|
(None, type(None), RESTORE_DATA["BadDecimal"], SensorDeviceClass.ENERGY, None),
|
2022-02-08 23:00:26 +01:00
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_restore_sensor_restore_state(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
|
|
|
hass_storage: dict[str, Any],
|
2022-02-08 23:00:26 +01:00
|
|
|
native_value,
|
|
|
|
native_value_type,
|
|
|
|
extra_data,
|
|
|
|
device_class,
|
|
|
|
uom,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-02-08 23:00:26 +01:00
|
|
|
"""Test RestoreSensor."""
|
|
|
|
mock_restore_cache_with_extra_data(hass, ((State("sensor.test", ""), extra_data),))
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockRestoreSensor(
|
2022-02-08 23:00:26 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-02-08 23:00:26 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert hass.states.get(entity0.entity_id)
|
|
|
|
|
|
|
|
assert entity0.native_value == native_value
|
|
|
|
assert type(entity0.native_value) == native_value_type
|
|
|
|
assert entity0.native_unit_of_measurement == uom
|
2022-03-30 15:43:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"device_class",
|
|
|
|
"native_unit",
|
|
|
|
"custom_unit",
|
|
|
|
"state_unit",
|
|
|
|
"native_value",
|
|
|
|
"custom_state",
|
|
|
|
),
|
2022-03-30 15:43:04 +02:00
|
|
|
[
|
|
|
|
# Smaller to larger unit, InHg is ~33x larger than hPa -> 1 more decimal
|
2022-03-30 19:15:00 +02:00
|
|
|
(
|
|
|
|
SensorDeviceClass.PRESSURE,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.INHG,
|
|
|
|
UnitOfPressure.INHG,
|
2022-03-30 19:15:00 +02:00
|
|
|
1000.0,
|
2023-01-25 08:55:46 +01:00
|
|
|
"29.53",
|
2022-03-30 19:15:00 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.PRESSURE,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.KPA,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.HPA,
|
2022-03-30 19:15:00 +02:00
|
|
|
1.234,
|
2023-01-25 08:55:46 +01:00
|
|
|
"12.340",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.MMHG,
|
|
|
|
UnitOfPressure.MMHG,
|
|
|
|
1000,
|
|
|
|
"750",
|
2022-03-30 19:15:00 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.PRESSURE,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.MMHG,
|
|
|
|
UnitOfPressure.MMHG,
|
2022-03-30 19:15:00 +02:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"750",
|
2022-03-30 19:15:00 +02:00
|
|
|
),
|
2022-03-30 15:43:04 +02:00
|
|
|
# Not a supported pressure unit
|
2022-03-30 19:15:00 +02:00
|
|
|
(
|
|
|
|
SensorDeviceClass.PRESSURE,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
2022-03-30 19:15:00 +02:00
|
|
|
"peer_pressure",
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
2022-03-30 19:15:00 +02:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
2022-03-30 19:15:00 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.TEMPERATURE,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfTemperature.CELSIUS,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
2022-03-30 19:15:00 +02:00
|
|
|
37.5,
|
2023-01-25 08:55:46 +01:00
|
|
|
"99.5",
|
2022-03-30 19:15:00 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.TEMPERATURE,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
|
|
UnitOfTemperature.CELSIUS,
|
|
|
|
UnitOfTemperature.CELSIUS,
|
2022-03-30 19:15:00 +02:00
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"38",
|
2022-03-30 19:15:00 +02:00
|
|
|
),
|
2023-07-02 20:53:50 -05:00
|
|
|
(
|
|
|
|
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
|
|
|
UnitOfPressure.INHG,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
-0.00,
|
|
|
|
"0.0",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
|
|
|
UnitOfPressure.INHG,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
-0.00001,
|
|
|
|
"0",
|
|
|
|
),
|
2024-01-30 15:01:08 +01:00
|
|
|
(
|
|
|
|
SensorDeviceClass.VOLUME_FLOW_RATE,
|
|
|
|
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
|
|
|
|
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
|
|
|
|
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
|
|
|
|
50.0,
|
|
|
|
"13.2",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.VOLUME_FLOW_RATE,
|
|
|
|
UnitOfVolumeFlowRate.GALLONS_PER_MINUTE,
|
|
|
|
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
|
|
|
|
UnitOfVolumeFlowRate.LITERS_PER_MINUTE,
|
|
|
|
13.0,
|
|
|
|
"49.2",
|
|
|
|
),
|
2024-01-30 23:08:12 +01:00
|
|
|
(
|
|
|
|
SensorDeviceClass.DURATION,
|
|
|
|
UnitOfTime.SECONDS,
|
|
|
|
UnitOfTime.HOURS,
|
|
|
|
UnitOfTime.HOURS,
|
|
|
|
5400.0,
|
|
|
|
"1.5000",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.DURATION,
|
|
|
|
UnitOfTime.DAYS,
|
|
|
|
UnitOfTime.MINUTES,
|
|
|
|
UnitOfTime.MINUTES,
|
|
|
|
0.5,
|
|
|
|
"720.0",
|
|
|
|
),
|
2022-03-30 15:43:04 +02:00
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_custom_unit(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2022-03-30 19:15:00 +02:00
|
|
|
device_class,
|
2022-03-30 15:43:04 +02:00
|
|
|
native_unit,
|
|
|
|
custom_unit,
|
|
|
|
state_unit,
|
|
|
|
native_value,
|
2023-01-25 08:55:46 +01:00
|
|
|
custom_state,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-03-30 15:43:04 +02:00
|
|
|
"""Test custom unit."""
|
|
|
|
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entry.entity_id, "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-03-30 15:43:04 +02:00
|
|
|
name="Test",
|
|
|
|
native_value=str(native_value),
|
|
|
|
native_unit_of_measurement=native_unit,
|
2022-03-30 19:15:00 +02:00
|
|
|
device_class=device_class,
|
2022-03-30 15:43:04 +02:00
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-03-30 15:43:04 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2023-07-02 20:53:50 -05:00
|
|
|
entity_id = entity0.entity_id
|
|
|
|
state = hass.states.get(entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == custom_state
|
2022-03-30 15:43:04 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == state_unit
|
|
|
|
|
2023-07-02 20:53:50 -05:00
|
|
|
assert (
|
|
|
|
async_rounded_state(hass, entity_id, hass.states.get(entity_id)) == custom_state
|
|
|
|
)
|
|
|
|
|
2022-03-30 15:43:04 +02:00
|
|
|
|
2023-01-25 08:55:46 +01:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"native_unit",
|
|
|
|
"custom_unit",
|
|
|
|
"state_unit",
|
|
|
|
"native_value",
|
|
|
|
"native_state",
|
|
|
|
"custom_state",
|
|
|
|
"device_class",
|
|
|
|
),
|
2022-03-30 15:43:04 +02:00
|
|
|
[
|
2022-09-27 15:34:00 +01:00
|
|
|
# Distance
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.MILES,
|
|
|
|
UnitOfLength.MILES,
|
2022-09-27 15:34:00 +01:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"621",
|
2022-09-27 15:34:00 +01:00
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.CENTIMETERS,
|
|
|
|
UnitOfLength.INCHES,
|
|
|
|
UnitOfLength.INCHES,
|
2022-09-27 15:34:00 +01:00
|
|
|
7.24,
|
2023-01-25 08:55:46 +01:00
|
|
|
"7.24",
|
|
|
|
"2.85",
|
2022-09-27 15:34:00 +01:00
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
2022-09-27 15:34:00 +01:00
|
|
|
"peer_distance",
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
2022-09-27 15:34:00 +01:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"1000",
|
2022-09-27 15:34:00 +01:00
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
2023-01-09 14:26:52 +01:00
|
|
|
# Energy
|
|
|
|
(
|
|
|
|
UnitOfEnergy.KILO_WATT_HOUR,
|
|
|
|
UnitOfEnergy.MEGA_WATT_HOUR,
|
|
|
|
UnitOfEnergy.MEGA_WATT_HOUR,
|
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"1.000",
|
2023-01-09 14:26:52 +01:00
|
|
|
SensorDeviceClass.ENERGY,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
UnitOfEnergy.GIGA_JOULE,
|
|
|
|
UnitOfEnergy.MEGA_WATT_HOUR,
|
|
|
|
UnitOfEnergy.MEGA_WATT_HOUR,
|
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"278",
|
2023-01-09 14:26:52 +01:00
|
|
|
SensorDeviceClass.ENERGY,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
UnitOfEnergy.KILO_WATT_HOUR,
|
|
|
|
"BTU",
|
|
|
|
UnitOfEnergy.KILO_WATT_HOUR,
|
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"1000",
|
2023-01-09 14:26:52 +01:00
|
|
|
SensorDeviceClass.ENERGY,
|
|
|
|
),
|
2023-01-12 09:20:00 +01:00
|
|
|
# Power factor
|
|
|
|
(
|
|
|
|
None,
|
|
|
|
PERCENTAGE,
|
|
|
|
PERCENTAGE,
|
|
|
|
1.0,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1.0",
|
|
|
|
"100.0",
|
2023-01-12 09:20:00 +01:00
|
|
|
SensorDeviceClass.POWER_FACTOR,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
PERCENTAGE,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
|
|
|
"1.00",
|
2023-01-12 09:20:00 +01:00
|
|
|
SensorDeviceClass.POWER_FACTOR,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"Cos φ",
|
|
|
|
None,
|
|
|
|
"Cos φ",
|
|
|
|
1.0,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1.0",
|
|
|
|
"1.0",
|
2023-01-12 09:20:00 +01:00
|
|
|
SensorDeviceClass.POWER_FACTOR,
|
|
|
|
),
|
2023-01-09 14:26:52 +01:00
|
|
|
# Pressure
|
2022-03-30 15:43:04 +02:00
|
|
|
# Smaller to larger unit, InHg is ~33x larger than hPa -> 1 more decimal
|
2022-09-27 15:34:00 +01:00
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.INHG,
|
|
|
|
UnitOfPressure.INHG,
|
2022-09-27 15:34:00 +01:00
|
|
|
1000.0,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000.0",
|
|
|
|
"29.53",
|
2022-09-27 15:34:00 +01:00
|
|
|
SensorDeviceClass.PRESSURE,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.KPA,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.HPA,
|
2022-09-27 15:34:00 +01:00
|
|
|
1.234,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1.234",
|
|
|
|
"12.340",
|
2022-09-27 15:34:00 +01:00
|
|
|
SensorDeviceClass.PRESSURE,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.MMHG,
|
|
|
|
UnitOfPressure.MMHG,
|
2022-09-27 15:34:00 +01:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"750",
|
2022-09-27 15:34:00 +01:00
|
|
|
SensorDeviceClass.PRESSURE,
|
|
|
|
),
|
2022-03-30 15:43:04 +02:00
|
|
|
# Not a supported pressure unit
|
2022-09-27 15:34:00 +01:00
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
2022-09-27 15:34:00 +01:00
|
|
|
"peer_pressure",
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfPressure.HPA,
|
2022-09-27 15:34:00 +01:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"1000",
|
2022-09-27 15:34:00 +01:00
|
|
|
SensorDeviceClass.PRESSURE,
|
|
|
|
),
|
2022-09-27 17:19:34 +01:00
|
|
|
# Speed
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfSpeed.KILOMETERS_PER_HOUR,
|
|
|
|
UnitOfSpeed.MILES_PER_HOUR,
|
|
|
|
UnitOfSpeed.MILES_PER_HOUR,
|
2022-09-27 17:19:34 +01:00
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
|
|
|
"62",
|
2022-09-27 17:19:34 +01:00
|
|
|
SensorDeviceClass.SPEED,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
|
|
|
|
UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
|
|
UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
2022-09-27 17:19:34 +01:00
|
|
|
78,
|
2023-01-25 08:55:46 +01:00
|
|
|
"78",
|
|
|
|
"0.13",
|
2022-09-27 17:19:34 +01:00
|
|
|
SensorDeviceClass.SPEED,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfSpeed.KILOMETERS_PER_HOUR,
|
2022-09-27 17:19:34 +01:00
|
|
|
"peer_distance",
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfSpeed.KILOMETERS_PER_HOUR,
|
2022-09-27 17:19:34 +01:00
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
|
|
|
"100",
|
2022-09-27 17:19:34 +01:00
|
|
|
SensorDeviceClass.SPEED,
|
|
|
|
),
|
2022-09-27 18:37:52 +01:00
|
|
|
# Volume
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfVolume.CUBIC_METERS,
|
|
|
|
UnitOfVolume.CUBIC_FEET,
|
|
|
|
UnitOfVolume.CUBIC_FEET,
|
2022-09-27 18:37:52 +01:00
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
|
|
|
"3531",
|
2022-09-27 18:37:52 +01:00
|
|
|
SensorDeviceClass.VOLUME,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfVolume.LITERS,
|
|
|
|
UnitOfVolume.FLUID_OUNCES,
|
|
|
|
UnitOfVolume.FLUID_OUNCES,
|
2022-09-27 18:37:52 +01:00
|
|
|
2.3,
|
2023-01-25 08:55:46 +01:00
|
|
|
"2.3",
|
|
|
|
"77.8",
|
2022-09-27 18:37:52 +01:00
|
|
|
SensorDeviceClass.VOLUME,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfVolume.CUBIC_METERS,
|
2022-09-27 18:37:52 +01:00
|
|
|
"peer_distance",
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfVolume.CUBIC_METERS,
|
2022-09-27 18:37:52 +01:00
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
|
|
|
"100",
|
2022-09-27 18:37:52 +01:00
|
|
|
SensorDeviceClass.VOLUME,
|
|
|
|
),
|
2022-09-28 12:13:49 +02:00
|
|
|
# Weight
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfMass.GRAMS,
|
|
|
|
UnitOfMass.OUNCES,
|
|
|
|
UnitOfMass.OUNCES,
|
2022-09-28 12:13:49 +02:00
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
|
|
|
"3.5",
|
2022-09-28 12:13:49 +02:00
|
|
|
SensorDeviceClass.WEIGHT,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfMass.OUNCES,
|
|
|
|
UnitOfMass.GRAMS,
|
|
|
|
UnitOfMass.GRAMS,
|
2022-09-28 12:13:49 +02:00
|
|
|
78,
|
2023-01-25 08:55:46 +01:00
|
|
|
"78",
|
|
|
|
"2211",
|
2022-09-28 12:13:49 +02:00
|
|
|
SensorDeviceClass.WEIGHT,
|
|
|
|
),
|
|
|
|
(
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfMass.GRAMS,
|
2022-09-28 12:13:49 +02:00
|
|
|
"peer_distance",
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfMass.GRAMS,
|
2022-09-28 12:13:49 +02:00
|
|
|
100,
|
2023-01-25 08:55:46 +01:00
|
|
|
"100",
|
|
|
|
"100",
|
2022-09-28 12:13:49 +02:00
|
|
|
SensorDeviceClass.WEIGHT,
|
|
|
|
),
|
2022-03-30 15:43:04 +02:00
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_custom_unit_change(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2022-03-30 15:43:04 +02:00
|
|
|
native_unit,
|
|
|
|
custom_unit,
|
|
|
|
state_unit,
|
|
|
|
native_value,
|
2023-01-25 08:55:46 +01:00
|
|
|
native_state,
|
|
|
|
custom_state,
|
2022-09-27 15:34:00 +01:00
|
|
|
device_class,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-03-30 15:43:04 +02:00
|
|
|
"""Test custom unit changes are picked up."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-03-30 15:43:04 +02:00
|
|
|
name="Test",
|
|
|
|
native_value=str(native_value),
|
|
|
|
native_unit_of_measurement=native_unit,
|
2022-09-27 15:34:00 +01:00
|
|
|
device_class=device_class,
|
2022-03-30 15:43:04 +02:00
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-03-30 15:43:04 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == native_state
|
2023-01-12 09:20:00 +01:00
|
|
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == native_unit
|
2022-03-30 15:43:04 +02:00
|
|
|
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
"sensor.test", "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == custom_state
|
2023-01-12 09:20:00 +01:00
|
|
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == state_unit
|
2022-03-30 15:43:04 +02:00
|
|
|
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
"sensor.test", "sensor", {"unit_of_measurement": native_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == native_state
|
2023-01-12 09:20:00 +01:00
|
|
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == native_unit
|
2022-03-30 15:43:04 +02:00
|
|
|
|
|
|
|
entity_registry.async_update_entity_options("sensor.test", "sensor", None)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == native_state
|
2023-01-12 09:20:00 +01:00
|
|
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == native_unit
|
2022-10-24 16:08:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"unit_system",
|
|
|
|
"native_unit",
|
|
|
|
"automatic_unit",
|
|
|
|
"suggested_unit",
|
|
|
|
"custom_unit",
|
|
|
|
"native_value",
|
|
|
|
"native_state",
|
|
|
|
"automatic_state",
|
|
|
|
"suggested_state",
|
|
|
|
"custom_state",
|
|
|
|
"device_class",
|
|
|
|
),
|
2022-10-24 16:08:02 +02:00
|
|
|
[
|
|
|
|
# Distance
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.MILES,
|
|
|
|
UnitOfLength.METERS,
|
|
|
|
UnitOfLength.YARDS,
|
2022-10-24 16:08:02 +02:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
"1000",
|
|
|
|
"621",
|
|
|
|
"1000000",
|
|
|
|
"1093613",
|
2022-10-24 16:08:02 +02:00
|
|
|
SensorDeviceClass.DISTANCE,
|
2023-07-02 20:53:50 -05:00
|
|
|
)
|
2022-10-24 16:08:02 +02:00
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_unit_conversion_priority(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2022-10-24 16:08:02 +02:00
|
|
|
unit_system,
|
|
|
|
native_unit,
|
|
|
|
automatic_unit,
|
|
|
|
suggested_unit,
|
|
|
|
custom_unit,
|
|
|
|
native_value,
|
2023-01-25 08:55:46 +01:00
|
|
|
native_state,
|
|
|
|
automatic_state,
|
|
|
|
suggested_state,
|
|
|
|
custom_state,
|
2022-10-24 16:08:02 +02:00
|
|
|
device_class,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-10-24 16:08:02 +02:00
|
|
|
"""Test priority of unit conversion."""
|
|
|
|
|
|
|
|
hass.config.units = unit_system
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-10-24 16:08:02 +02:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity1 = MockSensor(
|
2022-10-24 16:08:02 +02:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity2 = MockSensor(
|
2022-10-24 16:08:02 +02:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
unique_id="very_unique_2",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity3 = MockSensor(
|
2022-10-24 16:08:02 +02:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(
|
|
|
|
hass,
|
|
|
|
sensor.DOMAIN,
|
|
|
|
[
|
|
|
|
entity0,
|
|
|
|
entity1,
|
|
|
|
entity2,
|
|
|
|
entity3,
|
|
|
|
],
|
|
|
|
)
|
2022-10-24 16:08:02 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Registered entity -> Follow automatic unit conversion
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == automatic_state
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == automatic_unit
|
|
|
|
# Assert the automatic unit conversion is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
2023-02-27 11:46:55 +01:00
|
|
|
assert entry.unit_of_measurement == automatic_unit
|
2022-10-24 16:08:02 +02:00
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": automatic_unit}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Unregistered entity -> Follow native unit
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == native_state
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit
|
|
|
|
|
|
|
|
# Registered entity with suggested unit
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == suggested_state
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity2.entity_id)
|
2023-02-27 11:46:55 +01:00
|
|
|
assert entry.unit_of_measurement == suggested_unit
|
2022-10-24 16:08:02 +02:00
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": suggested_unit}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Unregistered entity with suggested unit
|
|
|
|
state = hass.states.get(entity3.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == suggested_state
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
|
|
|
|
# Set a custom unit, this should have priority over the automatic unit conversion
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity0.entity_id, "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == custom_state
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity2.entity_id, "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
2023-01-25 08:55:46 +01:00
|
|
|
assert state.state == custom_state
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
|
2023-02-13 10:08:07 +01:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"unit_system",
|
|
|
|
"native_unit",
|
|
|
|
"automatic_unit",
|
|
|
|
"suggested_unit",
|
|
|
|
"custom_unit",
|
|
|
|
"suggested_precision",
|
|
|
|
"native_value",
|
|
|
|
"native_state",
|
|
|
|
"automatic_state",
|
|
|
|
"suggested_state",
|
|
|
|
"custom_state",
|
|
|
|
"device_class",
|
|
|
|
),
|
2023-02-13 10:08:07 +01:00
|
|
|
[
|
|
|
|
# Distance
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.MILES,
|
|
|
|
UnitOfLength.METERS,
|
|
|
|
UnitOfLength.YARDS,
|
|
|
|
2,
|
|
|
|
1000,
|
|
|
|
"1000",
|
|
|
|
621.371,
|
|
|
|
1000000,
|
|
|
|
1093613,
|
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_unit_conversion_priority_precision(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2023-02-13 10:08:07 +01:00
|
|
|
unit_system,
|
|
|
|
native_unit,
|
|
|
|
automatic_unit,
|
|
|
|
suggested_unit,
|
|
|
|
custom_unit,
|
|
|
|
suggested_precision,
|
|
|
|
native_value,
|
|
|
|
native_state,
|
|
|
|
automatic_state,
|
|
|
|
suggested_state,
|
|
|
|
custom_state,
|
|
|
|
device_class,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2023-02-13 10:08:07 +01:00
|
|
|
"""Test priority of unit conversion for sensors with suggested_display_precision."""
|
|
|
|
|
|
|
|
hass.config.units = unit_system
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-02-13 10:08:07 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_display_precision=suggested_precision,
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity1 = MockSensor(
|
2023-02-13 10:08:07 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_display_precision=suggested_precision,
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity2 = MockSensor(
|
2023-02-13 10:08:07 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_display_precision=suggested_precision,
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
unique_id="very_unique_2",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity3 = MockSensor(
|
2023-02-13 10:08:07 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_display_precision=suggested_precision,
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
)
|
2024-04-24 09:46:55 +02:00
|
|
|
entity4 = MockSensor(
|
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_display_precision=None,
|
|
|
|
unique_id="very_unique_4",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(
|
|
|
|
hass,
|
|
|
|
sensor.DOMAIN,
|
|
|
|
[
|
|
|
|
entity0,
|
|
|
|
entity1,
|
|
|
|
entity2,
|
|
|
|
entity3,
|
2024-04-24 09:46:55 +02:00
|
|
|
entity4,
|
2024-03-28 12:07:55 +01:00
|
|
|
],
|
|
|
|
)
|
2023-02-13 10:08:07 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Registered entity -> Follow automatic unit conversion
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert float(state.state) == pytest.approx(automatic_state)
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == automatic_unit
|
|
|
|
# Assert the automatic unit conversion is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
2023-02-27 11:46:55 +01:00
|
|
|
assert entry.unit_of_measurement == automatic_unit
|
2023-02-13 10:08:07 +01:00
|
|
|
assert entry.options == {
|
|
|
|
"sensor": {"suggested_display_precision": 2},
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": automatic_unit},
|
|
|
|
}
|
2023-07-02 20:53:50 -05:00
|
|
|
assert float(async_rounded_state(hass, entity0.entity_id, state)) == pytest.approx(
|
|
|
|
round(automatic_state, 2)
|
|
|
|
)
|
2023-02-13 10:08:07 +01:00
|
|
|
|
|
|
|
# Unregistered entity -> Follow native unit
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
|
|
|
assert state.state == native_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit
|
|
|
|
|
|
|
|
# Registered entity with suggested unit
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
|
|
|
assert float(state.state) == pytest.approx(suggested_state)
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity2.entity_id)
|
2023-02-27 11:46:55 +01:00
|
|
|
assert entry.unit_of_measurement == suggested_unit
|
2023-02-13 10:08:07 +01:00
|
|
|
assert entry.options == {
|
|
|
|
"sensor": {"suggested_display_precision": 2},
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": suggested_unit},
|
|
|
|
}
|
|
|
|
|
|
|
|
# Unregistered entity with suggested unit
|
|
|
|
state = hass.states.get(entity3.entity_id)
|
|
|
|
assert float(state.state) == pytest.approx(suggested_state)
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
|
|
|
|
# Set a custom unit, this should have priority over the automatic unit conversion
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity0.entity_id, "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert float(state.state) == pytest.approx(custom_state)
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity2.entity_id, "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
|
|
|
assert float(state.state) == pytest.approx(custom_state)
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
2023-07-02 20:53:50 -05:00
|
|
|
# Set a display_precision, this should have priority over suggested_display_precision
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity0.entity_id,
|
|
|
|
"sensor",
|
|
|
|
{"suggested_display_precision": 2, "display_precision": 4},
|
|
|
|
)
|
|
|
|
entry0 = entity_registry.async_get(entity0.entity_id)
|
|
|
|
assert entry0.options["sensor"]["suggested_display_precision"] == 2
|
|
|
|
assert entry0.options["sensor"]["display_precision"] == 4
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert float(async_rounded_state(hass, entity0.entity_id, state)) == pytest.approx(
|
|
|
|
round(custom_state, 4)
|
|
|
|
)
|
|
|
|
|
2024-04-24 09:46:55 +02:00
|
|
|
# Set a display_precision without having suggested_display_precision
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity4.entity_id,
|
|
|
|
"sensor",
|
|
|
|
{"display_precision": 4},
|
|
|
|
)
|
|
|
|
entry4 = entity_registry.async_get(entity4.entity_id)
|
|
|
|
assert "suggested_display_precision" not in entry4.options["sensor"]
|
|
|
|
assert entry4.options["sensor"]["display_precision"] == 4
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(entity4.entity_id)
|
|
|
|
assert float(async_rounded_state(hass, entity4.entity_id, state)) == pytest.approx(
|
|
|
|
round(automatic_state, 4)
|
|
|
|
)
|
|
|
|
|
2023-02-13 10:08:07 +01:00
|
|
|
|
2022-10-24 16:08:02 +02:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"unit_system",
|
|
|
|
"native_unit",
|
|
|
|
"original_unit",
|
|
|
|
"suggested_unit",
|
|
|
|
"native_value",
|
|
|
|
"original_value",
|
|
|
|
"device_class",
|
|
|
|
),
|
2022-10-24 16:08:02 +02:00
|
|
|
[
|
|
|
|
# Distance
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.YARDS,
|
|
|
|
UnitOfLength.METERS,
|
2022-10-24 16:08:02 +02:00
|
|
|
1000,
|
|
|
|
1093613,
|
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_unit_conversion_priority_suggested_unit_change(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2022-10-24 16:08:02 +02:00
|
|
|
unit_system,
|
|
|
|
native_unit,
|
|
|
|
original_unit,
|
|
|
|
suggested_unit,
|
|
|
|
native_value,
|
|
|
|
original_value,
|
|
|
|
device_class,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-10-24 16:08:02 +02:00
|
|
|
"""Test priority of unit conversion."""
|
|
|
|
|
|
|
|
hass.config.units = unit_system
|
|
|
|
|
|
|
|
# Pre-register entities
|
2023-02-27 11:46:55 +01:00
|
|
|
entry = entity_registry.async_get_or_create(
|
|
|
|
"sensor", "test", "very_unique", unit_of_measurement=original_unit
|
|
|
|
)
|
2022-10-24 16:08:02 +02:00
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entry.entity_id,
|
|
|
|
"sensor.private",
|
|
|
|
{"suggested_unit_of_measurement": original_unit},
|
|
|
|
)
|
2023-02-27 11:46:55 +01:00
|
|
|
entry = entity_registry.async_get_or_create(
|
|
|
|
"sensor", "test", "very_unique_2", unit_of_measurement=original_unit
|
|
|
|
)
|
2022-10-24 16:08:02 +02:00
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entry.entity_id,
|
|
|
|
"sensor.private",
|
|
|
|
{"suggested_unit_of_measurement": original_unit},
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-10-24 16:08:02 +02:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity1 = MockSensor(
|
2022-10-24 16:08:02 +02:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
unique_id="very_unique_2",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0, entity1])
|
2022-10-24 16:08:02 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Registered entity -> Follow automatic unit conversion the first time the entity was seen
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-27 09:09:46 +01:00
|
|
|
assert float(state.state) == pytest.approx(float(original_value))
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == original_unit
|
2023-02-27 11:46:55 +01:00
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
|
|
|
assert entry.unit_of_measurement == original_unit
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": original_unit},
|
|
|
|
}
|
2022-10-24 16:08:02 +02:00
|
|
|
|
|
|
|
# Registered entity -> Follow suggested unit the first time the entity was seen
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
2023-01-27 09:09:46 +01:00
|
|
|
assert float(state.state) == pytest.approx(float(original_value))
|
2022-10-24 16:08:02 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == original_unit
|
2023-02-27 11:46:55 +01:00
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity1.entity_id)
|
|
|
|
assert entry.unit_of_measurement == original_unit
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": original_unit},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
(
|
|
|
|
"native_unit_1",
|
|
|
|
"native_unit_2",
|
|
|
|
"suggested_unit",
|
|
|
|
"native_value",
|
|
|
|
"original_value",
|
|
|
|
"device_class",
|
|
|
|
),
|
|
|
|
[
|
|
|
|
# Distance
|
|
|
|
(
|
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.METERS,
|
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
1000000,
|
|
|
|
1000,
|
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
|
|
|
# Energy
|
|
|
|
(
|
|
|
|
UnitOfEnergy.KILO_WATT_HOUR,
|
|
|
|
UnitOfEnergy.WATT_HOUR,
|
|
|
|
UnitOfEnergy.KILO_WATT_HOUR,
|
|
|
|
1000000,
|
|
|
|
1000,
|
|
|
|
SensorDeviceClass.ENERGY,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_unit_conversion_priority_suggested_unit_change_2(
|
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2023-02-27 11:46:55 +01:00
|
|
|
native_unit_1,
|
|
|
|
native_unit_2,
|
|
|
|
suggested_unit,
|
|
|
|
native_value,
|
|
|
|
original_value,
|
|
|
|
device_class,
|
|
|
|
) -> None:
|
|
|
|
"""Test priority of unit conversion."""
|
|
|
|
|
|
|
|
hass.config.units = METRIC_SYSTEM
|
|
|
|
|
|
|
|
# Pre-register entities
|
|
|
|
entity_registry.async_get_or_create(
|
|
|
|
"sensor", "test", "very_unique", unit_of_measurement=native_unit_1
|
|
|
|
)
|
|
|
|
entity_registry.async_get_or_create(
|
|
|
|
"sensor", "test", "very_unique_2", unit_of_measurement=native_unit_1
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-02-27 11:46:55 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit_2,
|
|
|
|
native_value=str(native_value),
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
entity1 = MockSensor(
|
2023-02-27 11:46:55 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit_2,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
unique_id="very_unique_2",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0, entity1])
|
2023-02-27 11:46:55 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Registered entity -> Follow unit in entity registry
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert float(state.state) == pytest.approx(float(original_value))
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit_1
|
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
|
|
|
assert entry.unit_of_measurement == native_unit_1
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": native_unit_1},
|
|
|
|
}
|
|
|
|
|
|
|
|
# Registered entity -> Follow unit in entity registry
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
|
|
|
assert float(state.state) == pytest.approx(float(original_value))
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit_1
|
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
|
|
|
assert entry.unit_of_measurement == native_unit_1
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": native_unit_1},
|
|
|
|
}
|
2022-10-26 21:11:28 +02:00
|
|
|
|
|
|
|
|
2023-02-04 20:54:36 +01:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"unit_system",
|
|
|
|
"native_unit",
|
|
|
|
"integration_suggested_precision",
|
|
|
|
"options_suggested_precision",
|
|
|
|
"native_value",
|
|
|
|
"device_class",
|
|
|
|
"extra_options",
|
|
|
|
),
|
2023-02-04 20:54:36 +01:00
|
|
|
[
|
|
|
|
# Distance
|
|
|
|
(
|
2023-02-13 13:55:12 +01:00
|
|
|
METRIC_SYSTEM,
|
2023-02-04 20:54:36 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
4,
|
2023-02-13 13:55:12 +01:00
|
|
|
4,
|
2023-02-04 20:54:36 +01:00
|
|
|
1000,
|
|
|
|
SensorDeviceClass.DISTANCE,
|
2023-02-13 13:55:12 +01:00
|
|
|
{},
|
|
|
|
),
|
|
|
|
# Air pressure
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
1000,
|
|
|
|
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
|
|
|
{"sensor.private": {"suggested_unit_of_measurement": "inHg"}},
|
2023-02-04 20:54:36 +01:00
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_suggested_precision_option(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2023-02-13 13:55:12 +01:00
|
|
|
unit_system,
|
2023-02-04 20:54:36 +01:00
|
|
|
native_unit,
|
2023-02-13 13:55:12 +01:00
|
|
|
integration_suggested_precision,
|
|
|
|
options_suggested_precision,
|
2023-02-04 20:54:36 +01:00
|
|
|
native_value,
|
|
|
|
device_class,
|
2023-02-13 13:55:12 +01:00
|
|
|
extra_options,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2023-02-04 20:54:36 +01:00
|
|
|
"""Test suggested precision is stored in the registry."""
|
|
|
|
|
2023-02-13 13:55:12 +01:00
|
|
|
hass.config.units = unit_system
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-02-04 20:54:36 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
2023-02-13 13:55:12 +01:00
|
|
|
suggested_display_precision=integration_suggested_precision,
|
2023-02-04 20:54:36 +01:00
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-02-04 20:54:36 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Assert the suggested precision is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
2023-02-13 13:55:12 +01:00
|
|
|
assert entry.options == extra_options | {
|
|
|
|
"sensor": {"suggested_display_precision": options_suggested_precision}
|
2023-02-04 20:54:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"unit_system",
|
|
|
|
"native_unit",
|
|
|
|
"suggested_unit",
|
|
|
|
"old_precision",
|
|
|
|
"new_precision",
|
|
|
|
"opt_precision",
|
|
|
|
"native_value",
|
|
|
|
"device_class",
|
|
|
|
"extra_options",
|
|
|
|
),
|
2023-02-04 20:54:36 +01:00
|
|
|
[
|
2023-02-13 13:55:12 +01:00
|
|
|
# Distance
|
2023-02-04 20:54:36 +01:00
|
|
|
(
|
2023-02-13 13:55:12 +01:00
|
|
|
METRIC_SYSTEM,
|
|
|
|
UnitOfLength.KILOMETERS,
|
2023-02-04 20:54:36 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
4,
|
|
|
|
1,
|
2023-02-13 13:55:12 +01:00
|
|
|
1,
|
2023-02-04 20:54:36 +01:00
|
|
|
1000,
|
|
|
|
SensorDeviceClass.DISTANCE,
|
2023-02-13 13:55:12 +01:00
|
|
|
{},
|
|
|
|
),
|
|
|
|
# Air pressure
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
|
|
|
UnitOfPressure.HPA,
|
|
|
|
UnitOfPressure.INHG,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
1000,
|
|
|
|
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
|
|
|
{"sensor.private": {"suggested_unit_of_measurement": "inHg"}},
|
2023-02-04 20:54:36 +01:00
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_suggested_precision_option_update(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2023-02-13 13:55:12 +01:00
|
|
|
unit_system,
|
2023-02-04 20:54:36 +01:00
|
|
|
native_unit,
|
2023-02-13 13:55:12 +01:00
|
|
|
suggested_unit,
|
2023-02-04 20:54:36 +01:00
|
|
|
old_precision,
|
|
|
|
new_precision,
|
2023-02-13 13:55:12 +01:00
|
|
|
opt_precision,
|
2023-02-04 20:54:36 +01:00
|
|
|
native_value,
|
|
|
|
device_class,
|
2023-02-13 13:55:12 +01:00
|
|
|
extra_options,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2023-02-04 20:54:36 +01:00
|
|
|
"""Test suggested precision stored in the registry is updated."""
|
|
|
|
|
2023-02-13 13:55:12 +01:00
|
|
|
hass.config.units = unit_system
|
|
|
|
|
2023-02-04 20:54:36 +01:00
|
|
|
# Pre-register entities
|
|
|
|
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entry.entity_id,
|
|
|
|
"sensor",
|
|
|
|
{
|
|
|
|
"suggested_display_precision": old_precision,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entry.entity_id,
|
|
|
|
"sensor.private",
|
|
|
|
{
|
2023-02-13 13:55:12 +01:00
|
|
|
"suggested_unit_of_measurement": suggested_unit,
|
2023-02-04 20:54:36 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-02-04 20:54:36 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_display_precision=new_precision,
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-02-04 20:54:36 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Assert the suggested precision is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor": {
|
2023-02-13 13:55:12 +01:00
|
|
|
"suggested_display_precision": opt_precision,
|
2023-02-04 20:54:36 +01:00
|
|
|
},
|
|
|
|
"sensor.private": {
|
2023-02-13 13:55:12 +01:00
|
|
|
"suggested_unit_of_measurement": suggested_unit,
|
2023-02-04 20:54:36 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-04-24 09:47:03 -05:00
|
|
|
async def test_suggested_precision_option_removal(
|
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2024-04-24 09:47:03 -05:00
|
|
|
) -> None:
|
|
|
|
"""Test suggested precision stored in the registry is removed."""
|
|
|
|
# Pre-register entities
|
|
|
|
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entry.entity_id,
|
|
|
|
"sensor",
|
|
|
|
{
|
|
|
|
"suggested_display_precision": 1,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
entity0 = MockSensor(
|
|
|
|
name="Test",
|
|
|
|
device_class=SensorDeviceClass.DURATION,
|
|
|
|
native_unit_of_measurement=UnitOfTime.HOURS,
|
|
|
|
native_value="1.5",
|
|
|
|
suggested_display_precision=None,
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Assert the suggested precision is no longer stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
|
|
|
assert entry.options.get("sensor", {}).get("suggested_display_precision") is None
|
|
|
|
|
|
|
|
|
2022-10-26 21:11:28 +02:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"unit_system",
|
|
|
|
"native_unit",
|
|
|
|
"original_unit",
|
|
|
|
"native_value",
|
|
|
|
"original_value",
|
|
|
|
"device_class",
|
|
|
|
),
|
2022-10-26 21:11:28 +02:00
|
|
|
[
|
|
|
|
# Distance
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.MILES,
|
2022-10-26 21:11:28 +02:00
|
|
|
1000,
|
2023-01-25 08:55:46 +01:00
|
|
|
621.0,
|
2022-10-26 21:11:28 +02:00
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
2022-12-05 16:12:37 +01:00
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
2023-01-16 09:00:27 +01:00
|
|
|
UnitOfLength.METERS,
|
|
|
|
UnitOfLength.MILES,
|
2022-12-05 16:12:37 +01:00
|
|
|
1000000,
|
|
|
|
621.371,
|
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
2022-10-26 21:11:28 +02:00
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_unit_conversion_priority_legacy_conversion_removed(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2022-10-26 21:11:28 +02:00
|
|
|
unit_system,
|
|
|
|
native_unit,
|
|
|
|
original_unit,
|
|
|
|
native_value,
|
|
|
|
original_value,
|
|
|
|
device_class,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-10-26 21:11:28 +02:00
|
|
|
"""Test priority of unit conversion."""
|
|
|
|
|
|
|
|
hass.config.units = unit_system
|
|
|
|
|
|
|
|
# Pre-register entities
|
|
|
|
entity_registry.async_get_or_create(
|
|
|
|
"sensor", "test", "very_unique", unit_of_measurement=original_unit
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-10-26 21:11:28 +02:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-10-26 21:11:28 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-01-27 09:09:46 +01:00
|
|
|
assert float(state.state) == pytest.approx(float(original_value))
|
2022-10-26 21:11:28 +02:00
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == original_unit
|
2022-11-17 14:00:28 +01:00
|
|
|
|
|
|
|
|
2023-02-07 14:20:06 +01:00
|
|
|
def test_device_classes_aligned() -> None:
|
2022-11-17 14:00:28 +01:00
|
|
|
"""Make sure all number device classes are also available in SensorDeviceClass."""
|
|
|
|
|
|
|
|
for device_class in NumberDeviceClass:
|
|
|
|
assert hasattr(SensorDeviceClass, device_class.name)
|
|
|
|
assert getattr(SensorDeviceClass, device_class.name).value == device_class.value
|
2022-12-02 09:11:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
async def test_value_unknown_in_enumeration(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-12-02 09:11:15 +01:00
|
|
|
"""Test warning on invalid enum value."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-12-02 09:11:15 +01:00
|
|
|
name="Test",
|
|
|
|
native_value="invalid_option",
|
|
|
|
device_class=SensorDeviceClass.ENUM,
|
|
|
|
options=["option1", "option2"],
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-12-02 09:11:15 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"Sensor sensor.test provides state value 'invalid_option', "
|
|
|
|
"which is not in the list of options provided"
|
|
|
|
) in caplog.text
|
|
|
|
|
|
|
|
|
|
|
|
async def test_invalid_enumeration_entity_with_device_class(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-12-02 09:11:15 +01:00
|
|
|
"""Test warning on entities that provide an enum with a device class."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-12-02 09:11:15 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=21,
|
|
|
|
device_class=SensorDeviceClass.POWER,
|
|
|
|
options=["option1", "option2"],
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-12-02 09:11:15 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"Sensor sensor.test is providing enum options, but has device class 'power' "
|
|
|
|
"instead of 'enum'"
|
|
|
|
) in caplog.text
|
|
|
|
|
|
|
|
|
|
|
|
async def test_invalid_enumeration_entity_without_device_class(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-12-02 09:11:15 +01:00
|
|
|
"""Test warning on entities that provide an enum without a device class."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-12-02 09:11:15 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=21,
|
|
|
|
options=["option1", "option2"],
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-12-02 09:11:15 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"Sensor sensor.test is providing enum options, but is missing "
|
|
|
|
"the enum device class"
|
|
|
|
) in caplog.text
|
|
|
|
|
|
|
|
|
2022-12-06 00:07:02 +01:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"device_class",
|
2024-03-19 09:01:07 +01:00
|
|
|
[
|
2022-12-06 00:07:02 +01:00
|
|
|
SensorDeviceClass.DATE,
|
|
|
|
SensorDeviceClass.ENUM,
|
|
|
|
SensorDeviceClass.TIMESTAMP,
|
2024-03-19 09:01:07 +01:00
|
|
|
],
|
2022-12-06 00:07:02 +01:00
|
|
|
)
|
|
|
|
async def test_non_numeric_device_class_with_unit_of_measurement(
|
2022-12-02 09:11:15 +01:00
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
2022-12-06 00:07:02 +01:00
|
|
|
device_class: SensorDeviceClass,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-12-06 00:07:02 +01:00
|
|
|
"""Test error on numeric entities that provide an unit of measurement."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-12-02 09:11:15 +01:00
|
|
|
name="Test",
|
2022-12-06 00:07:02 +01:00
|
|
|
native_value=None,
|
|
|
|
device_class=device_class,
|
2022-12-02 09:11:15 +01:00
|
|
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
|
|
|
options=["option1", "option2"],
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2022-12-02 09:11:15 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
2022-12-06 00:07:02 +01:00
|
|
|
"Sensor sensor.test has a unit of measurement and thus indicating it has "
|
|
|
|
f"a numeric value; however, it has the non-numeric device class: {device_class}"
|
2022-12-02 09:11:15 +01:00
|
|
|
) in caplog.text
|
2022-12-21 16:14:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"device_class",
|
2024-03-19 09:01:07 +01:00
|
|
|
[
|
2022-12-21 16:14:11 +01:00
|
|
|
SensorDeviceClass.APPARENT_POWER,
|
|
|
|
SensorDeviceClass.AQI,
|
|
|
|
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
|
|
|
SensorDeviceClass.BATTERY,
|
|
|
|
SensorDeviceClass.CO,
|
|
|
|
SensorDeviceClass.CO2,
|
|
|
|
SensorDeviceClass.CURRENT,
|
|
|
|
SensorDeviceClass.DATA_RATE,
|
|
|
|
SensorDeviceClass.DATA_SIZE,
|
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
SensorDeviceClass.DURATION,
|
|
|
|
SensorDeviceClass.ENERGY,
|
|
|
|
SensorDeviceClass.FREQUENCY,
|
|
|
|
SensorDeviceClass.GAS,
|
|
|
|
SensorDeviceClass.HUMIDITY,
|
|
|
|
SensorDeviceClass.ILLUMINANCE,
|
|
|
|
SensorDeviceClass.IRRADIANCE,
|
|
|
|
SensorDeviceClass.MOISTURE,
|
|
|
|
SensorDeviceClass.NITROGEN_DIOXIDE,
|
|
|
|
SensorDeviceClass.NITROGEN_MONOXIDE,
|
|
|
|
SensorDeviceClass.NITROUS_OXIDE,
|
|
|
|
SensorDeviceClass.OZONE,
|
2023-07-23 18:49:10 +02:00
|
|
|
SensorDeviceClass.PH,
|
2022-12-21 16:14:11 +01:00
|
|
|
SensorDeviceClass.PM1,
|
|
|
|
SensorDeviceClass.PM10,
|
|
|
|
SensorDeviceClass.PM25,
|
|
|
|
SensorDeviceClass.POWER_FACTOR,
|
|
|
|
SensorDeviceClass.POWER,
|
|
|
|
SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
|
|
SensorDeviceClass.PRECIPITATION,
|
|
|
|
SensorDeviceClass.PRESSURE,
|
|
|
|
SensorDeviceClass.REACTIVE_POWER,
|
|
|
|
SensorDeviceClass.SIGNAL_STRENGTH,
|
|
|
|
SensorDeviceClass.SOUND_PRESSURE,
|
|
|
|
SensorDeviceClass.SPEED,
|
|
|
|
SensorDeviceClass.SULPHUR_DIOXIDE,
|
|
|
|
SensorDeviceClass.TEMPERATURE,
|
|
|
|
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
|
2023-05-12 14:37:54 +02:00
|
|
|
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
2022-12-21 16:14:11 +01:00
|
|
|
SensorDeviceClass.VOLTAGE,
|
|
|
|
SensorDeviceClass.VOLUME,
|
|
|
|
SensorDeviceClass.WATER,
|
|
|
|
SensorDeviceClass.WEIGHT,
|
|
|
|
SensorDeviceClass.WIND_SPEED,
|
2024-03-19 09:01:07 +01:00
|
|
|
],
|
2022-12-21 16:14:11 +01:00
|
|
|
)
|
|
|
|
async def test_device_classes_with_invalid_unit_of_measurement(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
device_class: SensorDeviceClass,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2022-12-21 16:14:11 +01:00
|
|
|
"""Test error when unit of measurement is not valid for used device class."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2022-12-21 16:14:11 +01:00
|
|
|
name="Test",
|
2022-12-30 19:07:49 +01:00
|
|
|
native_value="1.0",
|
2022-12-21 16:14:11 +01:00
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement="INVALID!",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-01-25 20:45:50 +01:00
|
|
|
units = [
|
|
|
|
str(unit) if unit else "no unit of measurement"
|
|
|
|
for unit in DEVICE_CLASS_UNITS.get(device_class, set())
|
|
|
|
]
|
2022-12-21 16:14:11 +01:00
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"is using native unit of measurement 'INVALID!' which is not a valid "
|
2023-01-25 20:45:50 +01:00
|
|
|
f"unit for the device class ('{device_class}') it is using; "
|
|
|
|
f"expected one of {units}"
|
2022-12-21 16:14:11 +01:00
|
|
|
) in caplog.text
|
2023-01-16 11:00:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
("device_class", "state_class", "unit"),
|
2023-01-16 11:00:07 +01:00
|
|
|
[
|
|
|
|
(SensorDeviceClass.AQI, None, None),
|
|
|
|
(None, SensorStateClass.MEASUREMENT, None),
|
|
|
|
(None, None, UnitOfTemperature.CELSIUS),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
2023-08-23 14:16:40 +02:00
|
|
|
("native_value", "problem"),
|
2023-01-16 11:00:07 +01:00
|
|
|
[
|
2023-08-23 14:16:40 +02:00
|
|
|
("", "non-numeric"),
|
|
|
|
("abc", "non-numeric"),
|
|
|
|
("13.7.1", "non-numeric"),
|
|
|
|
(datetime(2012, 11, 10, 7, 35, 1), "non-numeric"),
|
|
|
|
(date(2012, 11, 10), "non-numeric"),
|
|
|
|
("inf", "non-finite"),
|
|
|
|
(float("inf"), "non-finite"),
|
|
|
|
("nan", "non-finite"),
|
|
|
|
(float("nan"), "non-finite"),
|
2023-01-16 11:00:07 +01:00
|
|
|
],
|
|
|
|
)
|
2023-03-31 14:12:51 +02:00
|
|
|
async def test_non_numeric_validation_error(
|
2023-01-16 11:00:07 +01:00
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
native_value: Any,
|
2023-08-23 14:16:40 +02:00
|
|
|
problem: str,
|
2023-01-16 11:00:07 +01:00
|
|
|
device_class: SensorDeviceClass | None,
|
|
|
|
state_class: SensorStateClass | None,
|
|
|
|
unit: str | None,
|
|
|
|
) -> None:
|
|
|
|
"""Test error on expected numeric entities."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-01-16 11:00:07 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=native_value,
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=unit,
|
|
|
|
state_class=state_class,
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-01-16 11:00:07 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
2023-03-31 14:12:51 +02:00
|
|
|
assert state is None
|
2023-01-16 11:00:07 +01:00
|
|
|
|
|
|
|
assert (
|
|
|
|
"thus indicating it has a numeric value; "
|
2023-08-23 14:16:40 +02:00
|
|
|
f"however, it has the {problem} value: '{native_value}'"
|
2023-01-16 11:00:07 +01:00
|
|
|
) in caplog.text
|
|
|
|
|
|
|
|
|
2023-01-25 08:55:46 +01:00
|
|
|
@pytest.mark.parametrize(
|
2024-03-19 09:01:07 +01:00
|
|
|
("device_class", "state_class", "unit", "precision"), [(None, None, None, 1)]
|
2023-01-25 08:55:46 +01:00
|
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 22:46:03 +01:00
|
|
|
("native_value", "expected"),
|
2023-01-25 08:55:46 +01:00
|
|
|
[
|
|
|
|
("abc", "abc"),
|
|
|
|
("13.7.1", "13.7.1"),
|
|
|
|
(datetime(2012, 11, 10, 7, 35, 1), "2012-11-10 07:35:01"),
|
|
|
|
(date(2012, 11, 10), "2012-11-10"),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_non_numeric_validation_raise(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
native_value: Any,
|
|
|
|
expected: str,
|
|
|
|
device_class: SensorDeviceClass | None,
|
|
|
|
state_class: SensorStateClass | None,
|
|
|
|
unit: str | None,
|
|
|
|
precision,
|
|
|
|
) -> None:
|
|
|
|
"""Test error on expected numeric entities."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-01-25 08:55:46 +01:00
|
|
|
name="Test",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=unit,
|
|
|
|
native_value=native_value,
|
|
|
|
state_class=state_class,
|
2023-02-04 20:54:36 +01:00
|
|
|
suggested_display_precision=precision,
|
2023-01-25 08:55:46 +01:00
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-01-25 08:55:46 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state is None
|
|
|
|
|
2024-02-23 10:49:26 -10:00
|
|
|
assert ("for domain sensor with platform test") in caplog.text
|
2023-01-25 08:55:46 +01:00
|
|
|
|
|
|
|
|
2023-01-16 11:00:07 +01:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
("device_class", "state_class", "unit"),
|
2023-01-16 11:00:07 +01:00
|
|
|
[
|
|
|
|
(SensorDeviceClass.AQI, None, None),
|
|
|
|
(None, SensorStateClass.MEASUREMENT, None),
|
|
|
|
(None, None, UnitOfTemperature.CELSIUS),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 22:46:03 +01:00
|
|
|
("native_value", "expected"),
|
2023-01-16 11:00:07 +01:00
|
|
|
[
|
|
|
|
(13, "13"),
|
|
|
|
(17.50, "17.5"),
|
2023-02-15 15:53:44 +01:00
|
|
|
("1e-05", "1e-05"),
|
2023-01-16 11:00:07 +01:00
|
|
|
(Decimal(18.50), "18.5"),
|
|
|
|
("19.70", "19.70"),
|
|
|
|
(None, STATE_UNKNOWN),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_numeric_validation(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
native_value: Any,
|
|
|
|
expected: str,
|
|
|
|
device_class: SensorDeviceClass | None,
|
|
|
|
state_class: SensorStateClass | None,
|
|
|
|
unit: str | None,
|
|
|
|
) -> None:
|
|
|
|
"""Test does not error on expected numeric entities."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-01-16 11:00:07 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=native_value,
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=unit,
|
|
|
|
state_class=state_class,
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-01-16 11:00:07 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state.state == expected
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"thus indicating it has a numeric value; "
|
|
|
|
f"however, it has the non-numeric value: {native_value}"
|
|
|
|
) not in caplog.text
|
|
|
|
|
|
|
|
|
|
|
|
async def test_numeric_validation_ignores_custom_device_class(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
) -> None:
|
|
|
|
"""Test does not error on expected numeric entities."""
|
|
|
|
native_value = "Three elephants"
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-01-16 11:00:07 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=native_value,
|
|
|
|
device_class="custom__deviceclass",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-01-16 11:00:07 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state.state == "Three elephants"
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"thus indicating it has a numeric value; "
|
|
|
|
f"however, it has the non-numeric value: {native_value}"
|
|
|
|
) not in caplog.text
|
2023-01-16 14:31:24 +01:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"device_class",
|
|
|
|
list(SensorDeviceClass),
|
|
|
|
)
|
|
|
|
async def test_device_classes_with_invalid_state_class(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
device_class: SensorDeviceClass,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2023-01-16 14:31:24 +01:00
|
|
|
"""Test error when unit of measurement is not valid for used device class."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-01-16 14:31:24 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=None,
|
|
|
|
state_class="INVALID!",
|
|
|
|
device_class=device_class,
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-01-16 14:31:24 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2023-02-04 11:49:24 +01:00
|
|
|
classes = DEVICE_CLASS_STATE_CLASSES.get(device_class, set())
|
|
|
|
one_of = ", ".join(f"'{value.value}'" for value in classes)
|
|
|
|
expected = f"None or one of {one_of}" if classes else "None"
|
|
|
|
|
2023-01-16 14:31:24 +01:00
|
|
|
assert (
|
|
|
|
"is using state class 'INVALID!' which is impossible considering device "
|
2023-02-04 11:49:24 +01:00
|
|
|
f"class ('{device_class}') it is using; "
|
|
|
|
f"expected {expected}"
|
2023-01-16 14:31:24 +01:00
|
|
|
) in caplog.text
|
2023-02-01 18:45:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"device_class",
|
|
|
|
"state_class",
|
|
|
|
"native_unit_of_measurement",
|
|
|
|
"suggested_precision",
|
|
|
|
"is_numeric",
|
|
|
|
),
|
2023-02-01 18:45:13 +01:00
|
|
|
[
|
|
|
|
(SensorDeviceClass.ENUM, None, None, None, False),
|
|
|
|
(SensorDeviceClass.DATE, None, None, None, False),
|
|
|
|
(SensorDeviceClass.TIMESTAMP, None, None, None, False),
|
|
|
|
("custom", None, None, None, False),
|
|
|
|
(SensorDeviceClass.POWER, None, "V", None, True),
|
|
|
|
(None, SensorStateClass.MEASUREMENT, None, None, True),
|
|
|
|
(None, None, PERCENTAGE, None, True),
|
|
|
|
(None, None, None, None, False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_numeric_state_expected_helper(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
device_class: SensorDeviceClass | None,
|
|
|
|
state_class: SensorStateClass | None,
|
|
|
|
native_unit_of_measurement: str | None,
|
2023-02-04 20:54:36 +01:00
|
|
|
suggested_precision: int | None,
|
2023-02-01 18:45:13 +01:00
|
|
|
is_numeric: bool,
|
|
|
|
) -> None:
|
|
|
|
"""Test numeric_state_expected helper."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-02-01 18:45:13 +01:00
|
|
|
name="Test",
|
|
|
|
native_value=None,
|
|
|
|
device_class=device_class,
|
|
|
|
state_class=state_class,
|
|
|
|
native_unit_of_measurement=native_unit_of_measurement,
|
2023-02-04 20:54:36 +01:00
|
|
|
suggested_display_precision=suggested_precision,
|
2023-02-01 18:45:13 +01:00
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-02-01 18:45:13 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state is not None
|
|
|
|
|
|
|
|
assert entity0._numeric_state_expected == is_numeric
|
2023-02-03 16:30:50 +01:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 14:09:50 +01:00
|
|
|
(
|
|
|
|
"unit_system_1",
|
|
|
|
"unit_system_2",
|
|
|
|
"native_unit",
|
|
|
|
"automatic_unit_1",
|
|
|
|
"automatic_unit_2",
|
|
|
|
"suggested_unit",
|
|
|
|
"custom_unit",
|
|
|
|
"native_value",
|
|
|
|
"automatic_state_1",
|
|
|
|
"automatic_state_2",
|
|
|
|
"suggested_state",
|
|
|
|
"custom_state",
|
|
|
|
"device_class",
|
|
|
|
),
|
2023-02-03 16:30:50 +01:00
|
|
|
[
|
|
|
|
# Distance
|
|
|
|
(
|
|
|
|
US_CUSTOMARY_SYSTEM,
|
|
|
|
METRIC_SYSTEM,
|
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.MILES,
|
|
|
|
UnitOfLength.KILOMETERS,
|
|
|
|
UnitOfLength.METERS,
|
|
|
|
UnitOfLength.YARDS,
|
|
|
|
1000,
|
|
|
|
"621",
|
|
|
|
"1000",
|
|
|
|
"1000000",
|
|
|
|
"1093613",
|
|
|
|
SensorDeviceClass.DISTANCE,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_unit_conversion_update(
|
2023-02-15 18:07:40 +01:00
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2023-02-03 16:30:50 +01:00
|
|
|
unit_system_1,
|
|
|
|
unit_system_2,
|
|
|
|
native_unit,
|
|
|
|
automatic_unit_1,
|
|
|
|
automatic_unit_2,
|
|
|
|
suggested_unit,
|
|
|
|
custom_unit,
|
|
|
|
native_value,
|
|
|
|
automatic_state_1,
|
|
|
|
automatic_state_2,
|
|
|
|
suggested_state,
|
|
|
|
custom_state,
|
|
|
|
device_class,
|
2023-02-15 18:07:40 +01:00
|
|
|
) -> None:
|
2023-02-03 16:30:50 +01:00
|
|
|
"""Test suggested unit can be updated."""
|
|
|
|
|
|
|
|
hass.config.units = unit_system_1
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(
|
2023-02-03 16:30:50 +01:00
|
|
|
name="Test 0",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
unique_id="very_unique",
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity1 = MockSensor(
|
2023-02-03 16:30:50 +01:00
|
|
|
name="Test 1",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
unique_id="very_unique_1",
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity2 = MockSensor(
|
2023-02-03 16:30:50 +01:00
|
|
|
name="Test 2",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
unique_id="very_unique_2",
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity3 = MockSensor(
|
2023-02-03 16:30:50 +01:00
|
|
|
name="Test 3",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
unique_id="very_unique_3",
|
|
|
|
)
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity4 = MockSensor(
|
2023-09-23 13:28:14 +02:00
|
|
|
name="Test 4",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
unique_id="very_unique_4",
|
|
|
|
)
|
|
|
|
|
|
|
|
entity_platform = MockEntityPlatform(
|
|
|
|
hass, domain="sensor", platform_name="test", platform=None
|
|
|
|
)
|
|
|
|
await entity_platform.async_add_entities((entity0, entity1, entity2, entity3))
|
|
|
|
|
|
|
|
# Pre-register entity4
|
|
|
|
entry = entity_registry.async_get_or_create(
|
|
|
|
"sensor", "test", entity4.unique_id, unit_of_measurement=automatic_unit_1
|
|
|
|
)
|
|
|
|
entity4_entity_id = entry.entity_id
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity4_entity_id,
|
|
|
|
"sensor.private",
|
|
|
|
{
|
|
|
|
"suggested_unit_of_measurement": automatic_unit_1,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2023-02-03 16:30:50 +01:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Registered entity -> Follow automatic unit conversion
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state.state == automatic_state_1
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == automatic_unit_1
|
|
|
|
# Assert the automatic unit conversion is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity0.entity_id)
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": automatic_unit_1}
|
|
|
|
}
|
|
|
|
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
|
|
|
assert state.state == automatic_state_1
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == automatic_unit_1
|
|
|
|
# Assert the automatic unit conversion is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity1.entity_id)
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": automatic_unit_1}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Registered entity with suggested unit
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
|
|
|
assert state.state == suggested_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity2.entity_id)
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": suggested_unit}
|
|
|
|
}
|
|
|
|
|
|
|
|
state = hass.states.get(entity3.entity_id)
|
|
|
|
assert state.state == suggested_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
# Assert the suggested unit is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity3.entity_id)
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": suggested_unit}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Set a custom unit, this should have priority over the automatic unit conversion
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity0.entity_id, "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state.state == custom_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entity2.entity_id, "sensor", {"unit_of_measurement": custom_unit}
|
|
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
|
|
|
assert state.state == custom_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
# Change unit system, states and units should be unchanged
|
|
|
|
hass.config.units = unit_system_2
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state.state == custom_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
|
|
|
assert state.state == automatic_state_1
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == automatic_unit_1
|
|
|
|
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
|
|
|
assert state.state == custom_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
state = hass.states.get(entity3.entity_id)
|
|
|
|
assert state.state == suggested_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
|
|
|
|
# Update suggested unit
|
|
|
|
async_update_suggested_units(hass)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity0.entity_id)
|
|
|
|
assert state.state == custom_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
|
|
|
assert state.state == automatic_state_2
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == automatic_unit_2
|
|
|
|
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
|
|
|
assert state.state == custom_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == custom_unit
|
|
|
|
|
|
|
|
state = hass.states.get(entity3.entity_id)
|
|
|
|
assert state.state == suggested_state
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
2023-06-20 23:09:24 +02:00
|
|
|
|
2023-09-23 13:28:14 +02:00
|
|
|
# Entity 4 still has a pending request to refresh entity options
|
|
|
|
entry = entity_registry.async_get(entity4_entity_id)
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {
|
|
|
|
"refresh_initial_entity_options": True,
|
|
|
|
"suggested_unit_of_measurement": automatic_unit_1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add entity 4, the pending request to refresh entity options should be handled
|
|
|
|
await entity_platform.async_add_entities((entity4,))
|
|
|
|
|
|
|
|
state = hass.states.get(entity4_entity_id)
|
|
|
|
assert state.state == automatic_state_2
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == automatic_unit_2
|
|
|
|
|
|
|
|
entry = entity_registry.async_get(entity4_entity_id)
|
|
|
|
assert entry.options == {}
|
|
|
|
|
2023-06-20 23:09:24 +02:00
|
|
|
|
|
|
|
class MockFlow(ConfigFlow):
|
|
|
|
"""Test flow."""
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
2024-06-06 17:41:37 +02:00
|
|
|
def config_flow_fixture(hass: HomeAssistant) -> Generator[None]:
|
2023-06-20 23:09:24 +02:00
|
|
|
"""Mock config flow."""
|
|
|
|
mock_platform(hass, f"{TEST_DOMAIN}.config_flow")
|
|
|
|
|
|
|
|
with mock_config_flow(TEST_DOMAIN, MockFlow):
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
|
|
|
async def test_name(hass: HomeAssistant) -> None:
|
|
|
|
"""Test sensor name."""
|
|
|
|
|
|
|
|
async def async_setup_entry_init(
|
|
|
|
hass: HomeAssistant, config_entry: ConfigEntry
|
|
|
|
) -> bool:
|
|
|
|
"""Set up test config entry."""
|
Ensure config entries are not unloaded while their platforms are setting up (#118767)
* Report non-awaited/non-locked config entry platform forwards
Its currently possible for config entries to be reloaded while their platforms
are being forwarded if platform forwards are not awaited or done after the
config entry is setup since the lock will not be held in this case.
In https://developers.home-assistant.io/blog/2022/07/08/config_entry_forwards
we advised to await platform forwards to ensure this does not happen, however
for sleeping devices and late discovered devices, platform forwards may happen
later.
If config platform forwards are happening during setup, they should be awaited
If config entry platform forwards are not happening during setup, instead
async_late_forward_entry_setups should be used which will hold the lock to
prevent the config entry from being unloaded while its platforms are being
setup
* Report non-awaited/non-locked config entry platform forwards
Its currently possible for config entries to be reloaded while their platforms
are being forwarded if platform forwards are not awaited or done after the
config entry is setup since the lock will not be held in this case.
In https://developers.home-assistant.io/blog/2022/07/08/config_entry_forwards
we advised to await platform forwards to ensure this does not happen, however
for sleeping devices and late discovered devices, platform forwards may happen
later.
If config platform forwards are happening during setup, they should be awaited
If config entry platform forwards are not happening during setup, instead
async_late_forward_entry_setups should be used which will hold the lock to
prevent the config entry from being unloaded while its platforms are being
setup
* run with error on to find them
* cert_exp, hold lock
* cert_exp, hold lock
* shelly async_late_forward_entry_setups
* compact
* compact
* found another
* patch up mobileapp
* patch up hue tests
* patch up smartthings
* fix mqtt
* fix esphome
* zwave_js
* mqtt
* rework
* fixes
* fix mocking
* fix mocking
* do not call async_forward_entry_setup directly
* docstrings
* docstrings
* docstrings
* add comments
* doc strings
* fixed all in core, turn off strict
* coverage
* coverage
* missing
* coverage
2024-06-04 20:34:39 -05:00
|
|
|
await hass.config_entries.async_forward_entry_setups(
|
|
|
|
config_entry, [SENSOR_DOMAIN]
|
|
|
|
)
|
2023-06-20 23:09:24 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
mock_platform(hass, f"{TEST_DOMAIN}.config_flow")
|
|
|
|
mock_integration(
|
|
|
|
hass,
|
|
|
|
MockModule(
|
|
|
|
TEST_DOMAIN,
|
|
|
|
async_setup_entry=async_setup_entry_init,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
# Unnamed sensor without device class -> no name
|
|
|
|
entity1 = SensorEntity()
|
|
|
|
entity1.entity_id = "sensor.test1"
|
|
|
|
|
|
|
|
# Unnamed sensor with device class but has_entity_name False -> no name
|
|
|
|
entity2 = SensorEntity()
|
|
|
|
entity2.entity_id = "sensor.test2"
|
|
|
|
entity2._attr_device_class = SensorDeviceClass.BATTERY
|
|
|
|
|
|
|
|
# Unnamed sensor with device class and has_entity_name True -> named
|
|
|
|
entity3 = SensorEntity()
|
|
|
|
entity3.entity_id = "sensor.test3"
|
|
|
|
entity3._attr_device_class = SensorDeviceClass.BATTERY
|
|
|
|
entity3._attr_has_entity_name = True
|
|
|
|
|
|
|
|
# Unnamed sensor with device class and has_entity_name True -> named
|
|
|
|
entity4 = SensorEntity()
|
|
|
|
entity4.entity_id = "sensor.test4"
|
|
|
|
entity4.entity_description = SensorEntityDescription(
|
|
|
|
"test",
|
|
|
|
SensorDeviceClass.BATTERY,
|
|
|
|
has_entity_name=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
async def async_setup_entry_platform(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
config_entry: ConfigEntry,
|
|
|
|
async_add_entities: AddEntitiesCallback,
|
|
|
|
) -> None:
|
2023-12-12 08:28:08 +01:00
|
|
|
"""Set up test sensor platform via config entry."""
|
2023-06-20 23:09:24 +02:00
|
|
|
async_add_entities([entity1, entity2, entity3, entity4])
|
|
|
|
|
|
|
|
mock_platform(
|
|
|
|
hass,
|
|
|
|
f"{TEST_DOMAIN}.{SENSOR_DOMAIN}",
|
|
|
|
MockPlatform(async_setup_entry=async_setup_entry_platform),
|
|
|
|
)
|
|
|
|
|
|
|
|
config_entry = MockConfigEntry(domain=TEST_DOMAIN)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
state = hass.states.get(entity1.entity_id)
|
|
|
|
assert state.attributes == {}
|
|
|
|
|
|
|
|
state = hass.states.get(entity2.entity_id)
|
|
|
|
assert state.attributes == {"device_class": "battery"}
|
|
|
|
|
|
|
|
state = hass.states.get(entity3.entity_id)
|
|
|
|
assert state.attributes == {"device_class": "battery", "friendly_name": "Battery"}
|
|
|
|
|
|
|
|
state = hass.states.get(entity4.entity_id)
|
|
|
|
assert state.attributes == {"device_class": "battery", "friendly_name": "Battery"}
|
2023-07-02 20:53:50 -05:00
|
|
|
|
|
|
|
|
|
|
|
def test_async_rounded_state_unregistered_entity_is_passthrough(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
) -> None:
|
|
|
|
"""Test async_rounded_state on unregistered entity is passthrough."""
|
|
|
|
hass.states.async_set("sensor.test", "1.004")
|
|
|
|
state = hass.states.get("sensor.test")
|
|
|
|
assert async_rounded_state(hass, "sensor.test", state) == "1.004"
|
|
|
|
hass.states.async_set("sensor.test", "-0.0")
|
|
|
|
state = hass.states.get("sensor.test")
|
|
|
|
assert async_rounded_state(hass, "sensor.test", state) == "-0.0"
|
|
|
|
|
|
|
|
|
|
|
|
def test_async_rounded_state_registered_entity_with_display_precision(
|
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2023-07-02 20:53:50 -05:00
|
|
|
) -> None:
|
|
|
|
"""Test async_rounded_state on registered with display precision.
|
|
|
|
|
|
|
|
The -0 should be dropped.
|
|
|
|
"""
|
|
|
|
entry = entity_registry.async_get_or_create("sensor", "test", "very_unique")
|
|
|
|
entity_registry.async_update_entity_options(
|
|
|
|
entry.entity_id,
|
|
|
|
"sensor",
|
|
|
|
{"suggested_display_precision": 2, "display_precision": 4},
|
|
|
|
)
|
|
|
|
entity_id = entry.entity_id
|
|
|
|
hass.states.async_set(entity_id, "1.004")
|
|
|
|
state = hass.states.get(entity_id)
|
|
|
|
assert async_rounded_state(hass, entity_id, state) == "1.0040"
|
|
|
|
hass.states.async_set(entity_id, "-0.0")
|
|
|
|
state = hass.states.get(entity_id)
|
|
|
|
assert async_rounded_state(hass, entity_id, state) == "0.0000"
|
2023-10-02 13:01:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_device_class_units_state_classes(hass: HomeAssistant) -> None:
|
|
|
|
"""Test all numeric device classes have unit and state class."""
|
|
|
|
# DEVICE_CLASS_UNITS should include all device classes except:
|
|
|
|
# - SensorDeviceClass.MONETARY
|
|
|
|
# - Device classes enumerated in NON_NUMERIC_DEVICE_CLASSES
|
|
|
|
assert set(DEVICE_CLASS_UNITS) == set(
|
|
|
|
SensorDeviceClass
|
|
|
|
) - NON_NUMERIC_DEVICE_CLASSES - {SensorDeviceClass.MONETARY}
|
|
|
|
# DEVICE_CLASS_STATE_CLASSES should include all device classes
|
|
|
|
assert set(DEVICE_CLASS_STATE_CLASSES) == set(SensorDeviceClass)
|
2023-10-09 13:37:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
async def test_entity_category_config_raises_error(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
) -> None:
|
|
|
|
"""Test error is raised when entity category is set to config."""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity0 = MockSensor(name="Test", entity_category=EntityCategory.CONFIG)
|
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
2023-10-09 13:37:52 +02:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"Entity sensor.test cannot be added as the entity category is set to config"
|
|
|
|
in caplog.text
|
|
|
|
)
|
|
|
|
|
|
|
|
assert not hass.states.get("sensor.test")
|
2023-12-21 00:02:20 +01:00
|
|
|
|
|
|
|
|
2024-01-05 11:46:45 +01:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"module",
|
|
|
|
[sensor, sensor.const],
|
|
|
|
)
|
|
|
|
def test_all(module: ModuleType) -> None:
|
|
|
|
"""Test module.__all__ is correctly set."""
|
|
|
|
help_test_all(module)
|
|
|
|
|
|
|
|
|
2023-12-21 00:02:20 +01:00
|
|
|
@pytest.mark.parametrize(("enum"), list(sensor.SensorStateClass))
|
|
|
|
@pytest.mark.parametrize(("module"), [sensor, sensor.const])
|
|
|
|
def test_deprecated_constants(
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
enum: sensor.SensorStateClass,
|
|
|
|
module: ModuleType,
|
|
|
|
) -> None:
|
|
|
|
"""Test deprecated constants."""
|
|
|
|
import_and_test_deprecated_constant_enum(
|
|
|
|
caplog, module, enum, "STATE_CLASS_", "2025.1"
|
|
|
|
)
|
2023-12-23 20:18:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
("enum"),
|
|
|
|
[
|
|
|
|
sensor.SensorDeviceClass.AQI,
|
|
|
|
sensor.SensorDeviceClass.BATTERY,
|
|
|
|
sensor.SensorDeviceClass.CO,
|
|
|
|
sensor.SensorDeviceClass.CO2,
|
|
|
|
sensor.SensorDeviceClass.CURRENT,
|
|
|
|
sensor.SensorDeviceClass.DATE,
|
|
|
|
sensor.SensorDeviceClass.ENERGY,
|
|
|
|
sensor.SensorDeviceClass.FREQUENCY,
|
|
|
|
sensor.SensorDeviceClass.GAS,
|
|
|
|
sensor.SensorDeviceClass.HUMIDITY,
|
|
|
|
sensor.SensorDeviceClass.ILLUMINANCE,
|
|
|
|
sensor.SensorDeviceClass.MONETARY,
|
|
|
|
sensor.SensorDeviceClass.NITROGEN_DIOXIDE,
|
|
|
|
sensor.SensorDeviceClass.NITROGEN_MONOXIDE,
|
|
|
|
sensor.SensorDeviceClass.NITROUS_OXIDE,
|
|
|
|
sensor.SensorDeviceClass.OZONE,
|
|
|
|
sensor.SensorDeviceClass.PM1,
|
|
|
|
sensor.SensorDeviceClass.PM10,
|
|
|
|
sensor.SensorDeviceClass.PM25,
|
|
|
|
sensor.SensorDeviceClass.POWER_FACTOR,
|
|
|
|
sensor.SensorDeviceClass.POWER,
|
|
|
|
sensor.SensorDeviceClass.PRESSURE,
|
|
|
|
sensor.SensorDeviceClass.SIGNAL_STRENGTH,
|
|
|
|
sensor.SensorDeviceClass.SULPHUR_DIOXIDE,
|
|
|
|
sensor.SensorDeviceClass.TEMPERATURE,
|
|
|
|
sensor.SensorDeviceClass.TIMESTAMP,
|
|
|
|
sensor.SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
|
|
|
|
sensor.SensorDeviceClass.VOLTAGE,
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_deprecated_constants_sensor_device_class(
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
enum: sensor.SensorStateClass,
|
|
|
|
) -> None:
|
|
|
|
"""Test deprecated constants."""
|
|
|
|
import_and_test_deprecated_constant_enum(
|
|
|
|
caplog, sensor, enum, "DEVICE_CLASS_", "2025.1"
|
|
|
|
)
|
2024-01-30 18:55:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
("device_class", "native_unit"),
|
|
|
|
[
|
|
|
|
(SensorDeviceClass.TEMPERATURE, UnitOfTemperature.CELSIUS),
|
|
|
|
(SensorDeviceClass.DATA_RATE, UnitOfDataRate.KILOBITS_PER_SECOND),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_suggested_unit_guard_invalid_unit(
|
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2024-01-30 18:55:59 +01:00
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
device_class: SensorDeviceClass,
|
|
|
|
native_unit: str,
|
|
|
|
) -> None:
|
|
|
|
"""Test suggested_unit_of_measurement guard.
|
|
|
|
|
|
|
|
An invalid suggested unit creates a log entry and the suggested unit will be ignored.
|
|
|
|
"""
|
|
|
|
state_value = 10
|
|
|
|
invalid_suggested_unit = "invalid_unit"
|
|
|
|
|
2024-03-28 12:07:55 +01:00
|
|
|
entity = MockSensor(
|
2024-01-30 18:55:59 +01:00
|
|
|
name="Invalid",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
suggested_unit_of_measurement=invalid_suggested_unit,
|
|
|
|
native_value=str(state_value),
|
|
|
|
unique_id="invalid",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity])
|
2024-01-30 18:55:59 +01:00
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Unit of measurement should be native one
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert int(state.state) == state_value
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == native_unit
|
|
|
|
|
|
|
|
# Assert the suggested unit is ignored and not stored in the entity registry
|
|
|
|
entry = entity_registry.async_get(entity.entity_id)
|
|
|
|
assert entry.unit_of_measurement == native_unit
|
|
|
|
assert entry.options == {}
|
|
|
|
assert (
|
|
|
|
"homeassistant.components.sensor",
|
|
|
|
logging.WARNING,
|
|
|
|
(
|
2024-03-28 12:07:55 +01:00
|
|
|
"<class 'tests.components.sensor.common.MockSensor'> sets an"
|
|
|
|
" invalid suggested_unit_of_measurement. Please create a bug report at "
|
|
|
|
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test%22."
|
|
|
|
" This warning will become an error in Home Assistant Core 2024.5"
|
2024-01-30 18:55:59 +01:00
|
|
|
),
|
|
|
|
) in caplog.record_tuples
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
("device_class", "native_unit", "native_value", "suggested_unit", "expect_value"),
|
|
|
|
[
|
|
|
|
(
|
|
|
|
SensorDeviceClass.TEMPERATURE,
|
|
|
|
UnitOfTemperature.CELSIUS,
|
|
|
|
10,
|
|
|
|
UnitOfTemperature.KELVIN,
|
|
|
|
283,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
SensorDeviceClass.DATA_RATE,
|
|
|
|
UnitOfDataRate.KILOBITS_PER_SECOND,
|
|
|
|
10,
|
|
|
|
UnitOfDataRate.BITS_PER_SECOND,
|
|
|
|
10000,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_suggested_unit_guard_valid_unit(
|
|
|
|
hass: HomeAssistant,
|
2024-05-28 15:41:03 +02:00
|
|
|
entity_registry: er.EntityRegistry,
|
2024-01-30 18:55:59 +01:00
|
|
|
device_class: SensorDeviceClass,
|
|
|
|
native_unit: str,
|
|
|
|
native_value: int,
|
|
|
|
suggested_unit: str,
|
2024-04-08 21:29:05 +02:00
|
|
|
expect_value: float,
|
2024-01-30 18:55:59 +01:00
|
|
|
) -> None:
|
|
|
|
"""Test suggested_unit_of_measurement guard.
|
|
|
|
|
|
|
|
Suggested unit is valid and therefore should be used for unit conversion and stored
|
|
|
|
in the entity registry.
|
|
|
|
"""
|
2024-03-28 12:07:55 +01:00
|
|
|
entity = MockSensor(
|
2024-01-30 18:55:59 +01:00
|
|
|
name="Valid",
|
|
|
|
device_class=device_class,
|
|
|
|
native_unit_of_measurement=native_unit,
|
|
|
|
native_value=str(native_value),
|
|
|
|
suggested_unit_of_measurement=suggested_unit,
|
|
|
|
unique_id="valid",
|
|
|
|
)
|
2024-03-28 12:07:55 +01:00
|
|
|
setup_test_component_platform(hass, sensor.DOMAIN, [entity])
|
2024-01-30 18:55:59 +01:00
|
|
|
|
|
|
|
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Unit of measurement should set to the suggested unit of measurement
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert float(state.state) == expect_value
|
|
|
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == suggested_unit
|
|
|
|
|
|
|
|
# Assert the suggested unit of measurement is stored in the registry
|
|
|
|
entry = entity_registry.async_get(entity.entity_id)
|
|
|
|
assert entry.unit_of_measurement == suggested_unit
|
|
|
|
assert entry.options == {
|
|
|
|
"sensor.private": {"suggested_unit_of_measurement": suggested_unit},
|
|
|
|
}
|