Use `suggested_display_precision` instead of rounding in Shelly sensor platform (#87084)

pull/87663/head^2
Maciej Bieniek 2023-02-20 17:20:52 +01:00 committed by GitHub
parent 4f6a25b470
commit 7119a0f811
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 152 additions and 54 deletions

View File

@ -82,7 +82,7 @@ SENSORS: Final = {
key="device|deviceTemp",
name="Device temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
@ -92,7 +92,6 @@ SENSORS: Final = {
key="emeter|current",
name="Current",
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
value=lambda value: value,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
@ -100,7 +99,7 @@ SENSORS: Final = {
key="light|power",
name="Power",
native_unit_of_measurement=UnitOfPower.WATT,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
@ -109,7 +108,7 @@ SENSORS: Final = {
key="device|power",
name="Power",
native_unit_of_measurement=UnitOfPower.WATT,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
@ -117,7 +116,7 @@ SENSORS: Final = {
key="emeter|power",
name="Power",
native_unit_of_measurement=UnitOfPower.WATT,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
@ -125,7 +124,7 @@ SENSORS: Final = {
key="device|voltage",
name="Voltage",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
@ -134,15 +133,14 @@ SENSORS: Final = {
key="emeter|voltage",
name="Voltage",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
("emeter", "powerFactor"): BlockSensorDescription(
key="emeter|powerFactor",
name="Power factor",
native_unit_of_measurement=PERCENTAGE,
value=lambda value: round(value * 100, 1),
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
),
@ -150,7 +148,7 @@ SENSORS: Final = {
key="relay|power",
name="Power",
native_unit_of_measurement=UnitOfPower.WATT,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
@ -158,23 +156,26 @@ SENSORS: Final = {
key="roller|rollerPower",
name="Power",
native_unit_of_measurement=UnitOfPower.WATT,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
("device", "energy"): BlockSensorDescription(
key="device|energy",
name="Energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: round(value / 60 / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: value / 60,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
("emeter", "energy"): BlockSensorDescription(
key="emeter|energy",
name="Energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: round(value / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
available=lambda block: cast(int, block.energy) != -1,
@ -182,8 +183,9 @@ SENSORS: Final = {
("emeter", "energyReturned"): BlockSensorDescription(
key="emeter|energyReturned",
name="Energy returned",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: round(value / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
available=lambda block: cast(int, block.energyReturned) != -1,
@ -191,8 +193,10 @@ SENSORS: Final = {
("light", "energy"): BlockSensorDescription(
key="light|energy",
name="Energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: round(value / 60 / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: value / 60,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@ -200,16 +204,20 @@ SENSORS: Final = {
("relay", "energy"): BlockSensorDescription(
key="relay|energy",
name="Energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: round(value / 60 / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: value / 60,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
("roller", "rollerEnergy"): BlockSensorDescription(
key="roller|rollerEnergy",
name="Energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: round(value / 60 / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda value: value / 60,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
@ -224,7 +232,7 @@ SENSORS: Final = {
key="sensor|temp",
name="Temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
@ -233,7 +241,7 @@ SENSORS: Final = {
key="sensor|extTemp",
name="Temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
available=lambda block: cast(int, block.extTemp) != 999
@ -243,7 +251,7 @@ SENSORS: Final = {
key="sensor|humidity",
name="Humidity",
native_unit_of_measurement=PERCENTAGE,
value=lambda value: round(value, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
available=lambda block: cast(int, block.humidity) != 999
@ -269,7 +277,8 @@ SENSORS: Final = {
name="Lamp life",
native_unit_of_measurement=PERCENTAGE,
icon="mdi:progress-wrench",
value=lambda value: round(100 - (value / 3600 / SHAIR_MAX_WORK_HOURS), 1),
value=lambda value: 100 - (value / 3600 / SHAIR_MAX_WORK_HOURS),
suggested_display_precision=1,
extra_state_attributes=lambda block: {
"Operational hours": round(cast(int, block.totalWorkTime) / 3600, 1)
},
@ -279,7 +288,7 @@ SENSORS: Final = {
key="adc|adc",
name="ADC",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
value=lambda value: round(value, 2),
suggested_display_precision=2,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
@ -397,7 +406,8 @@ RPC_SENSORS: Final = {
sub_key="voltage",
name="Voltage",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
value=lambda status, _: None if status is None else round(float(status), 1),
value=lambda status, _: None if status is None else float(status),
suggested_display_precision=1,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
@ -460,8 +470,10 @@ RPC_SENSORS: Final = {
key="switch",
sub_key="aenergy",
name="Energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(status["total"] / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: status["total"],
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
@ -469,8 +481,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="total_act",
name="Total active energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
@ -478,8 +492,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="a_total_act_energy",
name="Phase A total active energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@ -488,8 +504,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="b_total_act_energy",
name="Phase B total active energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@ -498,8 +516,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="c_total_act_energy",
name="Phase C total active energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@ -508,8 +528,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="total_act_ret",
name="Total active returned energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
@ -517,8 +539,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="a_total_act_ret_energy",
name="Phase A total active returned energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@ -527,8 +551,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="b_total_act_ret_energy",
name="Phase B total active returned energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@ -537,8 +563,10 @@ RPC_SENSORS: Final = {
key="emdata",
sub_key="c_total_act_ret_energy",
name="Phase C total active returned energy",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: round(float(status) / 1000, 2),
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
@ -548,7 +576,8 @@ RPC_SENSORS: Final = {
sub_key="temperature",
name="Device temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value=lambda status, _: round(status["tC"], 1),
value=lambda status, _: status["tC"],
suggested_display_precision=1,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
@ -560,7 +589,7 @@ RPC_SENSORS: Final = {
sub_key="tC",
name="Temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value=lambda status, _: round(status, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
@ -590,7 +619,7 @@ RPC_SENSORS: Final = {
sub_key="rh",
name="Humidity",
native_unit_of_measurement=PERCENTAGE,
value=lambda status, _: round(status, 1),
suggested_display_precision=1,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@ -609,7 +638,8 @@ RPC_SENSORS: Final = {
sub_key="voltage",
name="Voltmeter",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
value=lambda status, _: round(float(status), 2),
value=lambda status, _: float(status),
suggested_display_precision=2,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
available=lambda status: status is not None,

View File

@ -70,11 +70,13 @@ MOCK_BLOCKS = [
"inputEventCnt": 2,
"overpower": 0,
"power": 53.4,
"energy": 1234567.89,
},
channel="0",
type="relay",
overpower=0,
power=53.4,
energy=1234567.89,
description="relay_0",
set_state=AsyncMock(side_effect=lambda turn: {"ison": turn == "on"}),
),
@ -120,6 +122,15 @@ MOCK_BLOCKS = [
description="device_0",
type="device",
),
Mock(
sensor_ids={"powerFactor": 0.98},
channel="0",
powerFactor=0.98,
targetTemp=4,
temp=22.1,
description="emeter_0",
type="emeter",
),
]
MOCK_CONFIG = {
@ -185,7 +196,7 @@ MOCK_STATUS_RPC = {
"stable": {"version": "some_beta_version"},
}
},
"voltmeter": {"voltage": 4.3},
"voltmeter": {"voltage": 4.321},
"wifi": {"rssi": -63},
}

View File

@ -29,6 +29,7 @@ from tests.common import mock_restore_cache, mock_restore_cache_with_extra_data
SENSOR_BLOCK_ID = 3
DEVICE_BLOCK_ID = 4
EMETER_BLOCK_ID = 5
ENTITY_ID = f"{CLIMATE_DOMAIN}.test_name"
@ -43,6 +44,7 @@ async def test_climate_hvac_mode(
{"battery": 98, "valvePos": 50, "targetTemp": 21.0},
)
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0)
monkeypatch.delattr(mock_block_device.blocks[EMETER_BLOCK_ID], "targetTemp")
await init_integration(hass, 1, sleep_period=1000, model="SHTRV-01")
# Make device online
@ -195,6 +197,7 @@ async def test_block_restored_climate(
"""Test block restored climate."""
monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp")
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0)
monkeypatch.delattr(mock_block_device.blocks[EMETER_BLOCK_ID], "targetTemp")
entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True)
register_device(device_reg, entry)
entity_id = register_entity(
@ -257,6 +260,7 @@ async def test_block_restored_climate_us_customery(
hass.config.units = US_CUSTOMARY_SYSTEM
monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp")
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0)
monkeypatch.delattr(mock_block_device.blocks[EMETER_BLOCK_ID], "targetTemp")
entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True)
register_device(device_reg, entry)
entity_id = register_entity(

View File

@ -1,6 +1,13 @@
"""Tests for Shelly sensor platform."""
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.components.shelly.const import DOMAIN
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
PERCENTAGE,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
UnitOfEnergy,
)
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers.entity_registry import async_get
@ -35,6 +42,52 @@ async def test_block_sensor(
assert hass.states.get(entity_id).state == "60.1"
async def test_energy_sensor(hass: HomeAssistant, mock_block_device) -> None:
"""Test energy sensor."""
entity_id = f"{SENSOR_DOMAIN}.test_name_channel_1_energy"
await init_integration(hass, 1)
state = hass.states.get(entity_id)
# 1234567.89 Wmin / 60 / 1000 = 20.5761315 kWh
assert state.state == "20.5761315"
# suggested unit is KWh
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfEnergy.KILO_WATT_HOUR
async def test_power_factory_unit_migration(
hass: HomeAssistant, mock_block_device
) -> None:
"""Test migration unit of the power factory sensor."""
registry = async_get(hass)
registry.async_get_or_create(
SENSOR_DOMAIN,
DOMAIN,
"123456789ABC-emeter_0-powerFactor",
suggested_object_id="test_name_power_factor",
unit_of_measurement="%",
)
entity_id = f"{SENSOR_DOMAIN}.test_name_power_factor"
await init_integration(hass, 1)
state = hass.states.get(entity_id)
# Value of 0.98 is converted to 98.0%
assert state.state == "98.0"
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
async def test_power_factory_without_unit_migration(
hass: HomeAssistant, mock_block_device
) -> None:
"""Test unit and value of the power factory sensor without unit migration."""
entity_id = f"{SENSOR_DOMAIN}.test_name_power_factor"
await init_integration(hass, 1)
state = hass.states.get(entity_id)
assert state.state == "0.98"
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
async def test_block_rest_sensor(
hass: HomeAssistant, mock_block_device, monkeypatch
) -> None:
@ -228,7 +281,7 @@ async def test_rpc_sensor_error(
entity_id = f"{SENSOR_DOMAIN}.test_name_voltmeter"
await init_integration(hass, 2)
assert hass.states.get(entity_id).state == "4.3"
assert hass.states.get(entity_id).state == "4.321"
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "voltmeter", "voltage", None)
mock_rpc_device.mock_update()