diff --git a/homeassistant/components/powerfox/diagnostics.py b/homeassistant/components/powerfox/diagnostics.py index 8f6b847fca0..4c6b0f8c6eb 100644 --- a/homeassistant/components/powerfox/diagnostics.py +++ b/homeassistant/components/powerfox/diagnostics.py @@ -5,7 +5,7 @@ from __future__ import annotations from datetime import datetime from typing import Any -from powerfox import PowerMeter, WaterMeter +from powerfox import HeatMeter, PowerMeter, WaterMeter from homeassistant.core import HomeAssistant @@ -52,6 +52,22 @@ async def async_get_config_entry_diagnostics( if isinstance(coordinator.data, WaterMeter) else {} ), + **( + { + "heat_meter": { + "outdated": coordinator.data.outdated, + "timestamp": datetime.strftime( + coordinator.data.timestamp, "%Y-%m-%d %H:%M:%S" + ), + "total_energy": coordinator.data.total_energy, + "delta_energy": coordinator.data.delta_energy, + "total_volume": coordinator.data.total_volume, + "delta_volume": coordinator.data.delta_volume, + } + } + if isinstance(coordinator.data, HeatMeter) + else {} + ), } for coordinator in powerfox_data ], diff --git a/homeassistant/components/powerfox/sensor.py b/homeassistant/components/powerfox/sensor.py index 7771f96dd81..6505139fcd9 100644 --- a/homeassistant/components/powerfox/sensor.py +++ b/homeassistant/components/powerfox/sensor.py @@ -5,7 +5,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from powerfox import Device, PowerMeter, WaterMeter +from powerfox import Device, HeatMeter, PowerMeter, WaterMeter from homeassistant.components.sensor import ( SensorDeviceClass, @@ -23,7 +23,7 @@ from .entity import PowerfoxEntity @dataclass(frozen=True, kw_only=True) -class PowerfoxSensorEntityDescription[T: (PowerMeter, WaterMeter)]( +class PowerfoxSensorEntityDescription[T: (PowerMeter, WaterMeter, HeatMeter)]( SensorEntityDescription ): """Describes Poweropti sensor entity.""" @@ -93,6 +93,40 @@ SENSORS_WATER: tuple[PowerfoxSensorEntityDescription[WaterMeter], ...] = ( ), ) +SENSORS_HEAT: tuple[PowerfoxSensorEntityDescription[HeatMeter], ...] = ( + PowerfoxSensorEntityDescription[HeatMeter]( + key="heat_total_energy", + translation_key="heat_total_energy", + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda meter: meter.total_energy, + ), + PowerfoxSensorEntityDescription[HeatMeter]( + key="heat_delta_energy", + translation_key="heat_delta_energy", + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, + value_fn=lambda meter: meter.delta_energy, + ), + PowerfoxSensorEntityDescription[HeatMeter]( + key="heat_total_volume", + translation_key="heat_total_volume", + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + device_class=SensorDeviceClass.WATER, + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda meter: meter.total_volume, + ), + PowerfoxSensorEntityDescription[HeatMeter]( + key="heat_delta_volume", + translation_key="heat_delta_volume", + suggested_display_precision=2, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + device_class=SensorDeviceClass.WATER, + value_fn=lambda meter: meter.delta_volume, + ), +) + async def async_setup_entry( hass: HomeAssistant, @@ -121,6 +155,15 @@ async def async_setup_entry( ) for description in SENSORS_WATER ) + if isinstance(coordinator.data, HeatMeter): + entities.extend( + PowerfoxSensorEntity( + coordinator=coordinator, + description=description, + device=coordinator.device, + ) + for description in SENSORS_HEAT + ) async_add_entities(entities) diff --git a/homeassistant/components/powerfox/strings.json b/homeassistant/components/powerfox/strings.json index 4a7c8e8fa4d..cb068a212c2 100644 --- a/homeassistant/components/powerfox/strings.json +++ b/homeassistant/components/powerfox/strings.json @@ -64,6 +64,18 @@ }, "warm_water": { "name": "Warm water" + }, + "heat_total_energy": { + "name": "Total energy" + }, + "heat_delta_energy": { + "name": "Delta energy" + }, + "heat_total_volume": { + "name": "Total volume" + }, + "heat_delta_volume": { + "name": "Delta volume" } } } diff --git a/tests/components/powerfox/conftest.py b/tests/components/powerfox/conftest.py index 14ccc5996e5..1d930394254 100644 --- a/tests/components/powerfox/conftest.py +++ b/tests/components/powerfox/conftest.py @@ -4,7 +4,7 @@ from collections.abc import Generator from datetime import UTC, datetime from unittest.mock import AsyncMock, patch -from powerfox import Device, DeviceType, PowerMeter, WaterMeter +from powerfox import Device, DeviceType, HeatMeter, PowerMeter, WaterMeter import pytest from homeassistant.components.powerfox.const import DOMAIN @@ -53,6 +53,14 @@ def mock_powerfox_client() -> Generator[AsyncMock]: type=DeviceType.COLD_WATER_METER, name="Wateropti", ), + Device( + id="9x9x1f12xx5x", + date_added=datetime(2024, 11, 26, 9, 22, 35, tzinfo=UTC), + main_device=False, + bidirectional=False, + type=DeviceType.HEAT_METER, + name="Heatopti", + ), ] client.device.side_effect = [ PowerMeter( @@ -70,6 +78,14 @@ def mock_powerfox_client() -> Generator[AsyncMock]: cold_water=1111.111, warm_water=0.0, ), + HeatMeter( + outdated=False, + timestamp=datetime(2024, 11, 26, 10, 48, 51, tzinfo=UTC), + total_energy=1111.111, + delta_energy=111, + total_volume=1111.111, + delta_volume=0.111, + ), ] yield client diff --git a/tests/components/powerfox/snapshots/test_diagnostics.ambr b/tests/components/powerfox/snapshots/test_diagnostics.ambr index 781e7b8c0d5..d749a1b5b60 100644 --- a/tests/components/powerfox/snapshots/test_diagnostics.ambr +++ b/tests/components/powerfox/snapshots/test_diagnostics.ambr @@ -21,6 +21,16 @@ 'warm_water': 0.0, }), }), + dict({ + 'heat_meter': dict({ + 'delta_energy': 111, + 'delta_volume': 0.111, + 'outdated': False, + 'timestamp': '2024-11-26 10:48:51', + 'total_energy': 1111.111, + 'total_volume': 1111.111, + }), + }), ]), }) # --- diff --git a/tests/components/powerfox/snapshots/test_sensor.ambr b/tests/components/powerfox/snapshots/test_sensor.ambr index dda162d4eeb..a2aa8a9c72c 100644 --- a/tests/components/powerfox/snapshots/test_sensor.ambr +++ b/tests/components/powerfox/snapshots/test_sensor.ambr @@ -1,4 +1,205 @@ # serializer version: 1 +# name: test_all_sensors[sensor.heatopti_delta_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.heatopti_delta_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Delta energy', + 'platform': 'powerfox', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'heat_delta_energy', + 'unique_id': '9x9x1f12xx5x_heat_delta_energy', + 'unit_of_measurement': , + }) +# --- +# name: test_all_sensors[sensor.heatopti_delta_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'Heatopti Delta energy', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.heatopti_delta_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '111', + }) +# --- +# name: test_all_sensors[sensor.heatopti_delta_volume-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.heatopti_delta_volume', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Delta volume', + 'platform': 'powerfox', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'heat_delta_volume', + 'unique_id': '9x9x1f12xx5x_heat_delta_volume', + 'unit_of_measurement': , + }) +# --- +# name: test_all_sensors[sensor.heatopti_delta_volume-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'water', + 'friendly_name': 'Heatopti Delta volume', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.heatopti_delta_volume', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.111', + }) +# --- +# name: test_all_sensors[sensor.heatopti_total_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.heatopti_total_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total energy', + 'platform': 'powerfox', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'heat_total_energy', + 'unique_id': '9x9x1f12xx5x_heat_total_energy', + 'unit_of_measurement': , + }) +# --- +# name: test_all_sensors[sensor.heatopti_total_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'Heatopti Total energy', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.heatopti_total_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1111.111', + }) +# --- +# name: test_all_sensors[sensor.heatopti_total_volume-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.heatopti_total_volume', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total volume', + 'platform': 'powerfox', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'heat_total_volume', + 'unique_id': '9x9x1f12xx5x_heat_total_volume', + 'unit_of_measurement': , + }) +# --- +# name: test_all_sensors[sensor.heatopti_total_volume-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'water', + 'friendly_name': 'Heatopti Total volume', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.heatopti_total_volume', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1111.111', + }) +# --- # name: test_all_sensors[sensor.poweropti_energy_return-entry] EntityRegistryEntrySnapshot({ 'aliases': set({