Add energy meter sensors for Shelly Pro EM (#99747)

* Add support for Pro EM

* Improve get_rpc_channel_name()

* Revert an unintended change

* Add tests
This commit is contained in:
Maciej Bieniek 2023-09-07 04:59:04 +00:00 committed by GitHub
parent 0c7e0f5cd9
commit e1f4a3fa9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 0 deletions

View file

@ -363,6 +363,14 @@ RPC_SENSORS: Final = {
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
"power_em1": RpcSensorDescription(
key="em1",
sub_key="act_power",
name="Power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
"power_pm1": RpcSensorDescription(
key="pm1",
sub_key="apower",
@ -427,6 +435,14 @@ RPC_SENSORS: Final = {
device_class=SensorDeviceClass.APPARENT_POWER,
state_class=SensorStateClass.MEASUREMENT,
),
"aprt_power_em1": RpcSensorDescription(
key="em1",
sub_key="aprt_power",
name="Apparent power",
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
device_class=SensorDeviceClass.APPARENT_POWER,
state_class=SensorStateClass.MEASUREMENT,
),
"total_aprt_power": RpcSensorDescription(
key="em",
sub_key="total_aprt_power",
@ -435,6 +451,13 @@ RPC_SENSORS: Final = {
device_class=SensorDeviceClass.APPARENT_POWER,
state_class=SensorStateClass.MEASUREMENT,
),
"pf_em1": RpcSensorDescription(
key="em1",
sub_key="pf",
name="Power factor",
device_class=SensorDeviceClass.POWER_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
),
"a_pf": RpcSensorDescription(
key="em",
sub_key="a_pf",
@ -467,6 +490,17 @@ RPC_SENSORS: Final = {
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"voltage_em1": RpcSensorDescription(
key="em1",
sub_key="voltage",
name="Voltage",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
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,
),
"voltage_pm1": RpcSensorDescription(
key="pm1",
sub_key="voltage",
@ -515,6 +549,16 @@ RPC_SENSORS: Final = {
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"current_em1": RpcSensorDescription(
key="em1",
sub_key="current",
name="Current",
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
value=lambda status, _: None if status is None else float(status),
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"current_pm1": RpcSensorDescription(
key="pm1",
sub_key="current",
@ -605,6 +649,18 @@ RPC_SENSORS: Final = {
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
"total_act_energy": RpcSensorDescription(
key="em1data",
sub_key="total_act_energy",
name="Total active energy",
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,
),
"a_total_act_energy": RpcSensorDescription(
key="emdata",
sub_key="a_total_act_energy",
@ -652,6 +708,18 @@ RPC_SENSORS: Final = {
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
"total_act_ret_energy": RpcSensorDescription(
key="em1data",
sub_key="total_act_ret_energy",
name="Total active returned energy",
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,
),
"a_total_act_ret_energy": RpcSensorDescription(
key="emdata",
sub_key="a_total_act_ret_energy",
@ -698,6 +766,16 @@ RPC_SENSORS: Final = {
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"freq_em1": RpcSensorDescription(
key="em1",
sub_key="freq",
name="Frequency",
native_unit_of_measurement=UnitOfFrequency.HERTZ,
suggested_display_precision=0,
device_class=SensorDeviceClass.FREQUENCY,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"freq_pm1": RpcSensorDescription(
key="pm1",
sub_key="freq",

View file

@ -288,6 +288,7 @@ def get_model_name(info: dict[str, Any]) -> str:
def get_rpc_channel_name(device: RpcDevice, key: str) -> str:
"""Get name based on device and channel name."""
key = key.replace("emdata", "em")
key = key.replace("em1data", "em1")
if device.config.get("switch:0"):
key = key.replace("input", "switch")
device_name = device.name
@ -298,6 +299,8 @@ def get_rpc_channel_name(device: RpcDevice, key: str) -> str:
if entity_name is None:
if key.startswith(("input:", "light:", "switch:")):
return f"{device_name} {key.replace(':', '_')}"
if key.startswith("em1"):
return f"{device_name} EM{key.split(':')[-1]}"
return device_name
return entity_name

View file

@ -202,6 +202,10 @@ MOCK_STATUS_RPC = {
"devicepower:0": {"external": {"present": True}},
"temperature:0": {"tC": 22.9},
"illuminance:0": {"lux": 345},
"em1:0": {"act_power": 85.3},
"em1:1": {"act_power": 123.3},
"em1data:0": {"total_act_energy": 123456.4},
"em1data:1": {"total_act_energy": 987654.3},
"sys": {
"available_updates": {
"beta": {"version": "some_beta_version"},

View file

@ -408,3 +408,43 @@ async def test_rpc_restored_sleeping_sensor_no_last_state(
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "22.9"
async def test_rpc_em1_sensors(
hass: HomeAssistant, mock_rpc_device, entity_registry_enabled_by_default: None
) -> None:
"""Test RPC sensors for EM1 component."""
registry = async_get(hass)
await init_integration(hass, 2)
state = hass.states.get("sensor.test_name_em0_power")
assert state
assert state.state == "85.3"
entry = registry.async_get("sensor.test_name_em0_power")
assert entry
assert entry.unique_id == "123456789ABC-em1:0-power_em1"
state = hass.states.get("sensor.test_name_em1_power")
assert state
assert state.state == "123.3"
entry = registry.async_get("sensor.test_name_em1_power")
assert entry
assert entry.unique_id == "123456789ABC-em1:1-power_em1"
state = hass.states.get("sensor.test_name_em0_total_active_energy")
assert state
assert state.state == "123.4564"
entry = registry.async_get("sensor.test_name_em0_total_active_energy")
assert entry
assert entry.unique_id == "123456789ABC-em1data:0-total_act_energy"
state = hass.states.get("sensor.test_name_em1_total_active_energy")
assert state
assert state.state == "987.6543"
entry = registry.async_get("sensor.test_name_em1_total_active_energy")
assert entry
assert entry.unique_id == "123456789ABC-em1data:1-total_act_energy"