From 46951bf2230792aed4c17ed1825ce746741fd2aa Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 25 May 2025 16:16:55 +0200 Subject: [PATCH] Add `returned energy` sensor for Shelly RPC switch component (#145490) * Add returned energy sensor for switch component * Add test * More tests * Make returned energy sensor disabled by default --- homeassistant/components/shelly/sensor.py | 15 +++ .../shelly/snapshots/test_sensor.ambr | 116 ++++++++++++++++++ tests/components/shelly/test_sensor.py | 54 ++++++++ 3 files changed, 185 insertions(+) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 986127b5836..78eff171daf 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -836,6 +836,21 @@ RPC_SENSORS: Final = { device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), + "ret_energy": RpcSensorDescription( + key="switch", + sub_key="ret_aenergy", + name="Returned energy", + 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, + entity_registry_enabled_default=False, + removal_condition=lambda _config, status, key: ( + status[key].get("ret_aenergy") is None + ), + ), "energy_light": RpcSensorDescription( key="light", sub_key="aenergy", diff --git a/tests/components/shelly/snapshots/test_sensor.ambr b/tests/components/shelly/snapshots/test_sensor.ambr index cb39b148c8a..c5c1427e3dc 100644 --- a/tests/components/shelly/snapshots/test_sensor.ambr +++ b/tests/components/shelly/snapshots/test_sensor.ambr @@ -154,3 +154,119 @@ 'state': '0', }) # --- +# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_switch_0_energy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'test switch_0 energy', + 'platform': 'shelly', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '123456789ABC-switch:0-energy', + 'unit_of_measurement': , + }) +# --- +# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'test switch_0 energy', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_switch_0_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1234.56789', + }) +# --- +# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_returned_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_switch_0_returned_energy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'test switch_0 returned energy', + 'platform': 'shelly', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '123456789ABC-switch:0-ret_energy', + 'unit_of_measurement': , + }) +# --- +# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_returned_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'test switch_0 returned energy', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_switch_0_returned_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '98.76543', + }) +# --- diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index 3bf63546419..a3d0a0f59c9 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -1519,3 +1519,57 @@ async def test_rpc_device_virtual_number_sensor_with_device_class( assert state.state == "34" assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.HUMIDITY + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_rpc_switch_energy_sensors( + hass: HomeAssistant, + mock_rpc_device: Mock, + entity_registry: EntityRegistry, + monkeypatch: pytest.MonkeyPatch, + snapshot: SnapshotAssertion, +) -> None: + """Test energy sensors for switch component.""" + status = { + "sys": {}, + "switch:0": { + "id": 0, + "output": True, + "apower": 85.3, + "aenergy": {"total": 1234567.89}, + "ret_aenergy": {"total": 98765.43}, + }, + } + monkeypatch.setattr(mock_rpc_device, "status", status) + await init_integration(hass, 3) + + for entity in ("energy", "returned_energy"): + entity_id = f"{SENSOR_DOMAIN}.test_switch_0_{entity}" + + state = hass.states.get(entity_id) + assert state == snapshot(name=f"{entity_id}-state") + + entry = entity_registry.async_get(entity_id) + assert entry == snapshot(name=f"{entity_id}-entry") + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_rpc_switch_no_returned_energy_sensor( + hass: HomeAssistant, + mock_rpc_device: Mock, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test switch component without returned energy sensor.""" + status = { + "sys": {}, + "switch:0": { + "id": 0, + "output": True, + "apower": 85.3, + "aenergy": {"total": 1234567.89}, + }, + } + monkeypatch.setattr(mock_rpc_device, "status", status) + await init_integration(hass, 3) + + assert hass.states.get("sensor.test_switch_0_returned_energy") is None