diff --git a/homeassistant/components/melcloud_home/sensor.py b/homeassistant/components/melcloud_home/sensor.py index 95b8499484ed..48122626b5ac 100644 --- a/homeassistant/components/melcloud_home/sensor.py +++ b/homeassistant/components/melcloud_home/sensor.py @@ -6,7 +6,7 @@ from datetime import datetime from itertools import chain from typing import override -from aiomelcloudhome import ATAUnit, ATWUnit +from aiomelcloudhome import ATAUnit, ATWOperationMode, ATWUnit from homeassistant.components.sensor import ( SensorDeviceClass, @@ -44,6 +44,14 @@ ENERGY_CONSUMED_DESCRIPTION = SensorEntityDescription( suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ) +ATW_OPERATION_STATUS: dict[ATWOperationMode, str] = { + ATWOperationMode.STOP: "idle", + ATWOperationMode.HOT_WATER: "heating_water", + ATWOperationMode.HEAT: "heating_zones", + ATWOperationMode.HEAT_ZONES: "heating_zones", + ATWOperationMode.COOL: "cooling", +} + OUTDOOR_TEMPERATURE_DESCRIPTION = SensorEntityDescription( key="outdoor_temperature", translation_key="outdoor_temperature", @@ -126,6 +134,15 @@ ATW_SENSORS: tuple[MelCloudHomeSensorEntityDescription[ATWUnit], ...] = ( suggested_display_precision=1, value_fn=lambda unit, _: unit.tank_water_temperature, ), + MelCloudHomeSensorEntityDescription( + key="operation_status", + translation_key="operation_status", + device_class=SensorDeviceClass.ENUM, + options=list(dict.fromkeys(ATW_OPERATION_STATUS.values())), + value_fn=lambda unit, _: ( + ATW_OPERATION_STATUS[unit.operation_mode] if unit.operation_mode else None + ), + ), *_common_sensor_descriptions(ATWUnit), ) diff --git a/homeassistant/components/melcloud_home/strings.json b/homeassistant/components/melcloud_home/strings.json index 45b97e499c6c..db244d64d4f5 100644 --- a/homeassistant/components/melcloud_home/strings.json +++ b/homeassistant/components/melcloud_home/strings.json @@ -127,6 +127,15 @@ "energy_consumed": { "name": "Energy consumed (monthly)" }, + "operation_status": { + "name": "Operation status", + "state": { + "cooling": "Cooling", + "heating_water": "Heating water", + "heating_zones": "Heating zones", + "idle": "[%key:common::state::idle%]" + } + }, "outdoor_temperature": { "name": "Outdoor temperature" }, diff --git a/tests/components/melcloud_home/snapshots/test_sensor.ambr b/tests/components/melcloud_home/snapshots/test_sensor.ambr index 31547eb2d23d..372e545b1bc6 100644 --- a/tests/components/melcloud_home/snapshots/test_sensor.ambr +++ b/tests/components/melcloud_home/snapshots/test_sensor.ambr @@ -61,6 +61,70 @@ 'state': '0.4505', }) # --- +# name: test_all_entities[sensor.heat_pump_operation_status-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : list([ + 'idle', + 'heating_water', + 'heating_zones', + 'cooling', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.heat_pump_operation_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Operation status', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Operation status', + 'platform': 'melcloud_home', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'operation_status', + 'unique_id': 'atw-unit-uuid-1_operation_status', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.heat_pump_operation_status-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'enum', + : 'Heat Pump Operation status', + : list([ + 'idle', + 'heating_water', + 'heating_zones', + 'cooling', + ]), + }), + 'context': , + 'entity_id': 'sensor.heat_pump_operation_status', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'heating_zones', + }) +# --- # name: test_all_entities[sensor.heat_pump_signal_strength-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ diff --git a/tests/components/melcloud_home/test_sensor.py b/tests/components/melcloud_home/test_sensor.py index 3b3e1372f944..60320b8861fd 100644 --- a/tests/components/melcloud_home/test_sensor.py +++ b/tests/components/melcloud_home/test_sensor.py @@ -1,17 +1,25 @@ """Tests for the MELCloud Home sensor platform.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch +from aiomelcloudhome import UserContext import pytest from syrupy.assertion import SnapshotAssertion -from homeassistant.const import Platform +from homeassistant.components.melcloud_home.const import DOMAIN +from homeassistant.const import STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import setup_integration -from tests.common import MockConfigEntry, snapshot_platform +from tests.common import ( + MockConfigEntry, + async_load_json_object_fixture, + snapshot_platform, +) + +OPERATION_STATUS_ENTITY_ID = "sensor.heat_pump_operation_status" @pytest.fixture(autouse=True) @@ -36,3 +44,36 @@ async def test_all_entities( await snapshot_platform( hass, entity_registry, snapshot, mock_config_entry.entry_id ) + + +@pytest.mark.parametrize( + ("operation_mode", "expected_state"), + [ + pytest.param("Stop", "idle", id="idle"), + pytest.param("HotWater", "heating_water", id="heating_water"), + pytest.param("Heat", "heating_zones", id="heat"), + pytest.param("HeatZones", "heating_zones", id="heating_zones"), + pytest.param("Cool", "cooling", id="cooling"), + pytest.param("Defrost", STATE_UNKNOWN, id="unsupported_mode"), + ], +) +async def test_operation_status( + hass: HomeAssistant, + mock_melcloud_client: AsyncMock, + mock_config_entry: MockConfigEntry, + operation_mode: str, + expected_state: str, +) -> None: + """Test the operation status follows the unit's operation mode.""" + context = await async_load_json_object_fixture(hass, "context.json", DOMAIN) + settings = { + unit_setting["name"]: unit_setting + for unit_setting in context["buildings"][0]["airToWaterUnits"][0]["settings"] + } + settings["OperationMode"]["value"] = operation_mode + mock_melcloud_client.get_context.return_value = UserContext.model_validate(context) + + await setup_integration(hass, mock_config_entry) + + assert (state := hass.states.get(OPERATION_STATUS_ENTITY_ID)) + assert state.state == expected_state