diff --git a/homeassistant/components/weheat/icons.json b/homeassistant/components/weheat/icons.json index 9606cbdf6fba..0b20377a9ed9 100644 --- a/homeassistant/components/weheat/icons.json +++ b/homeassistant/components/weheat/icons.json @@ -33,6 +33,9 @@ "cop": { "default": "mdi:speedometer" }, + "dhw_control_method": { + "default": "mdi:tune" + }, "dhw_flow_volume": { "default": "mdi:pump" }, @@ -51,6 +54,9 @@ "electricity_used_heating": { "default": "mdi:flash" }, + "electricity_used_indoor_unit": { + "default": "mdi:flash" + }, "energy_output": { "default": "mdi:flash" }, diff --git a/homeassistant/components/weheat/quality_scale.yaml b/homeassistant/components/weheat/quality_scale.yaml index ad4bfee02cbd..0f0710576533 100644 --- a/homeassistant/components/weheat/quality_scale.yaml +++ b/homeassistant/components/weheat/quality_scale.yaml @@ -74,7 +74,7 @@ rules: status: todo comment: | While unlikely to happen. Check if it is easily integrated. - entity-category: todo + entity-category: done entity-device-class: done entity-disabled-by-default: todo entity-translations: done diff --git a/homeassistant/components/weheat/sensor.py b/homeassistant/components/weheat/sensor.py index 421de67e658d..c56ab44b0688 100644 --- a/homeassistant/components/weheat/sensor.py +++ b/homeassistant/components/weheat/sensor.py @@ -15,6 +15,7 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( PERCENTAGE, REVOLUTIONS_PER_MINUTE, + EntityCategory, UnitOfEnergy, UnitOfPower, UnitOfTemperature, @@ -111,6 +112,15 @@ SENSORS = [ suggested_display_precision=DISPLAY_PRECISION_WATER_TEMP, value_fn=lambda status: status.air_inlet_temperature, ), + WeHeatSensorEntityDescription( + translation_key="air_outlet_temperature", + key="air_outlet_temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=DISPLAY_PRECISION_WATER_TEMP, + value_fn=lambda status: status.air_outlet_temperature, + ), WeHeatSensorEntityDescription( translation_key="thermostat_water_setpoint", key="thermostat_water_setpoint", @@ -203,6 +213,30 @@ DHW_SENSORS = [ native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, value_fn=lambda status: status.dhw_flow_volume, ), + WeHeatSensorEntityDescription( + translation_key="dhw_target_temperature", + key="dhw_target_temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=DISPLAY_PRECISION_WATER_TEMP, + # A target of zero is how the heat pump says DHW control is off. + value_fn=lambda status: status.dhw_target_temperature or None, + ), + WeHeatSensorEntityDescription( + translation_key="dhw_control_method", + key="dhw_control_method", + device_class=SensorDeviceClass.ENUM, + entity_category=EntityCategory.DIAGNOSTIC, + options=[method.name.lower() for method in HeatPump.DhwControlMethod], + value_fn=( + lambda status: ( + status.dhw_control_method.name.lower() + if status.dhw_control_method is not None + else None + ) + ), + ), ] ENERGY_SENSORS = [ @@ -214,6 +248,14 @@ ENERGY_SENSORS = [ state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda status: status.energy_total, ), + WeHeatSensorEntityDescription( + translation_key="electricity_used_indoor_unit", + key="electricity_used_indoor_unit", + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda status: status.energy_in_indoor_unit, + ), WeHeatSensorEntityDescription( translation_key="energy_output", key="energy_output", @@ -326,8 +368,6 @@ async def async_setup_entry( entity_description, ) for entity_description in DHW_SENSORS - if entity_description.value_fn(weheatdata.data_coordinator.data) - is not None ) entities.extend( WeheatHeatPumpSensor( @@ -336,8 +376,6 @@ async def async_setup_entry( entity_description, ) for entity_description in DHW_ENERGY_SENSORS - if entity_description.value_fn(weheatdata.energy_coordinator.data) - is not None ) entities.extend( WeheatHeatPumpSensor( diff --git a/homeassistant/components/weheat/strings.json b/homeassistant/components/weheat/strings.json index b84c19ea58bb..2f1fd03cca79 100644 --- a/homeassistant/components/weheat/strings.json +++ b/homeassistant/components/weheat/strings.json @@ -48,6 +48,9 @@ } }, "sensor": { + "air_outlet_temperature": { + "name": "Air outlet temperature" + }, "central_heating_flow_volume": { "name": "Central heating pump flow" }, @@ -66,9 +69,22 @@ "dhw_bottom_temperature": { "name": "DHW bottom temperature" }, + "dhw_control_method": { + "name": "DHW control method", + "state": { + "boost": "Boost", + "fixed": "Fixed setpoint", + "none": "None", + "schedule": "Schedule", + "weheat_intelligence": "Weheat Intelligence" + } + }, "dhw_flow_volume": { "name": "DHW pump flow" }, + "dhw_target_temperature": { + "name": "DHW target temperature" + }, "dhw_top_temperature": { "name": "DHW top temperature" }, @@ -87,6 +103,9 @@ "electricity_used_heating": { "name": "Electricity used heating" }, + "electricity_used_indoor_unit": { + "name": "Electricity used indoor unit" + }, "electricity_used_standby": { "name": "Electricity used standby" }, diff --git a/tests/components/weheat/conftest.py b/tests/components/weheat/conftest.py index 93aea72c3e5a..cd57febf5a7e 100644 --- a/tests/components/weheat/conftest.py +++ b/tests/components/weheat/conftest.py @@ -111,6 +111,7 @@ def mock_weheat_heat_pump_instance() -> MagicMock: mock_heat_pump_instance.water_outlet_temperature = 22 mock_heat_pump_instance.water_house_in_temperature = 33 mock_heat_pump_instance.air_inlet_temperature = 44 + mock_heat_pump_instance.air_outlet_temperature = 50 mock_heat_pump_instance.power_input = 55 mock_heat_pump_instance.power_output = 66 mock_heat_pump_instance.dhw_top_temperature = 77 @@ -126,6 +127,7 @@ def mock_weheat_heat_pump_instance() -> MagicMock: mock_heat_pump_instance.energy_in_cooling = 9000 mock_heat_pump_instance.energy_in_standby = 684 mock_heat_pump_instance.energy_total = 28689 + mock_heat_pump_instance.energy_in_indoor_unit = 1042 mock_heat_pump_instance.energy_out_heating = 10000 mock_heat_pump_instance.energy_out_dhw = 6677 mock_heat_pump_instance.energy_out_defrost = -1200 @@ -134,6 +136,9 @@ def mock_weheat_heat_pump_instance() -> MagicMock: mock_heat_pump_instance.compressor_rpm = 4500 mock_heat_pump_instance.compressor_percentage = 100 mock_heat_pump_instance.dhw_flow_volume = 1.12 + mock_heat_pump_instance.dhw_target_temperature = 55 + mock_heat_pump_instance.dhw_control_method = HeatPump.DhwControlMethod.FIXED + mock_heat_pump_instance.dhw_control_method_code = 1 mock_heat_pump_instance.central_heating_flow_volume = 1.23 mock_heat_pump_instance.indoor_unit_water_pump_state = False mock_heat_pump_instance.indoor_unit_auxiliary_pump_state = False diff --git a/tests/components/weheat/snapshots/test_sensor.ambr b/tests/components/weheat/snapshots/test_sensor.ambr index 61890870abfc..201fe11c7db0 100644 --- a/tests/components/weheat/snapshots/test_sensor.ambr +++ b/tests/components/weheat/snapshots/test_sensor.ambr @@ -77,6 +77,64 @@ 'state': 'heating', }) # --- +# name: test_all_entities[sensor.test_model_air_outlet_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_model_air_outlet_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Air outlet temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Air outlet temperature', + 'platform': 'weheat', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'air_outlet_temperature', + 'unique_id': '0000-1111-2222-3333_air_outlet_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.test_model_air_outlet_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'temperature', + : 'Test Model Air outlet temperature', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.test_model_air_outlet_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '50', + }) +# --- # name: test_all_entities[sensor.test_model_central_heating_inlet_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -473,6 +531,72 @@ 'state': '88', }) # --- +# name: test_all_entities[sensor.test_model_dhw_control_method-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : list([ + 'none', + 'fixed', + 'schedule', + 'weheat_intelligence', + 'boost', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_model_dhw_control_method', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DHW control method', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DHW control method', + 'platform': 'weheat', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dhw_control_method', + 'unique_id': '0000-1111-2222-3333_dhw_control_method', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.test_model_dhw_control_method-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'enum', + : 'Test Model DHW control method', + : list([ + 'none', + 'fixed', + 'schedule', + 'weheat_intelligence', + 'boost', + ]), + }), + 'context': , + 'entity_id': 'sensor.test_model_dhw_control_method', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'fixed', + }) +# --- # name: test_all_entities[sensor.test_model_dhw_pump_flow-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -531,6 +655,64 @@ 'state': '1.12', }) # --- +# name: test_all_entities[sensor.test_model_dhw_target_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_model_dhw_target_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'DHW target temperature', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DHW target temperature', + 'platform': 'weheat', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dhw_target_temperature', + 'unique_id': '0000-1111-2222-3333_dhw_target_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.test_model_dhw_target_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'temperature', + : 'Test Model DHW target temperature', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.test_model_dhw_target_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '55', + }) +# --- # name: test_all_entities[sensor.test_model_dhw_top_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ @@ -879,6 +1061,64 @@ 'state': '12345', }) # --- +# name: test_all_entities[sensor.test_model_electricity_used_indoor_unit-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_model_electricity_used_indoor_unit', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Electricity used indoor unit', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Electricity used indoor unit', + 'platform': 'weheat', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'electricity_used_indoor_unit', + 'unique_id': '0000-1111-2222-3333_electricity_used_indoor_unit', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.test_model_electricity_used_indoor_unit-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'energy', + : 'Test Model Electricity used indoor unit', + : , + : , + }), + 'context': , + 'entity_id': 'sensor.test_model_electricity_used_indoor_unit', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1042', + }) +# --- # name: test_all_entities[sensor.test_model_electricity_used_standby-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ diff --git a/tests/components/weheat/test_sensor.py b/tests/components/weheat/test_sensor.py index cdcfdb2746da..48c089761180 100644 --- a/tests/components/weheat/test_sensor.py +++ b/tests/components/weheat/test_sensor.py @@ -6,7 +6,7 @@ import pytest from syrupy.assertion import SnapshotAssertion from weheat.abstractions.discovery import HeatPumpDiscovery -from homeassistant.const import Platform +from homeassistant.const import STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -33,7 +33,7 @@ async def test_all_entities( await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) -@pytest.mark.parametrize(("has_dhw", "nr_of_entities"), [(False, 23), (True, 28)]) +@pytest.mark.parametrize(("has_dhw", "nr_of_entities"), [(False, 25), (True, 32)]) async def test_create_entities( hass: HomeAssistant, mock_weheat_discover: AsyncMock, @@ -52,3 +52,46 @@ async def test_create_entities( await hass.async_block_till_done() assert len(hass.states.async_all()) == nr_of_entities + + +@pytest.mark.parametrize( + ("target", "expected"), + [ + pytest.param(55, "55", id="a_target_it_aims_for"), + # DHW control off reports a target of zero, which is no target at all + pytest.param(0, STATE_UNKNOWN, id="dhw_control_off"), + ], +) +@pytest.mark.usefixtures("mock_weheat_discover") +async def test_dhw_target_temperature( + hass: HomeAssistant, + mock_weheat_heat_pump: AsyncMock, + mock_config_entry: MockConfigEntry, + target: int, + expected: str, +) -> None: + """Test the DHW target is only reported when the heat pump has one.""" + mock_weheat_heat_pump.dhw_target_temperature = target + + with patch("homeassistant.components.weheat.PLATFORMS", [Platform.SENSOR]): + await setup_integration(hass, mock_config_entry) + + assert hass.states.get("sensor.test_model_dhw_target_temperature").state == expected + + +@pytest.mark.usefixtures("mock_weheat_discover") +async def test_an_unknown_dhw_control_method_keeps_the_sensor( + hass: HomeAssistant, + mock_weheat_heat_pump: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test a control method this library cannot name still gets a sensor.""" + mock_weheat_heat_pump.dhw_control_method = None + mock_weheat_heat_pump.dhw_control_method_code = 99 + + with patch("homeassistant.components.weheat.PLATFORMS", [Platform.SENSOR]): + await setup_integration(hass, mock_config_entry) + + assert ( + hass.states.get("sensor.test_model_dhw_control_method").state == STATE_UNKNOWN + )