Add Landis+Gyr MWh-readings from ultraheat-api (#89937)

* Use mwh values from ultraheat api when available

Remove manifest cleanup from PR

Remove added device class from this PR

Restore entity registry fixture

Replace filter by attr_entity_registry_enabled_default

* Catchup with #90182 and #90183

* Add comment explaining disabling some entities

* Add parameterisation of test cases
This commit is contained in:
Vincent Knoop Pathuis 2023-03-30 09:07:47 +02:00 committed by GitHub
parent ba32e28fc6
commit a7040a0487
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 369 additions and 12 deletions

View File

@ -54,6 +54,15 @@ class HeatMeterSensorEntityDescription(
HEAT_METER_SENSOR_TYPES = (
HeatMeterSensorEntityDescription(
key="heat_usage_mwh",
icon="mdi:fire",
name="Heat usage MWh",
native_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda res: getattr(res, "heat_usage_mwh", None),
),
HeatMeterSensorEntityDescription(
key="volume_usage_m3",
icon="mdi:fire",
@ -72,6 +81,15 @@ HEAT_METER_SENSOR_TYPES = (
state_class=SensorStateClass.TOTAL,
value_fn=lambda res: getattr(res, "heat_usage_gj", None),
),
HeatMeterSensorEntityDescription(
key="heat_previous_year_mwh",
icon="mdi:fire",
name="Heat previous year MWh",
native_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda res: getattr(res, "heat_previous_year_mwh", None),
),
HeatMeterSensorEntityDescription(
key="heat_previous_year_gj",
icon="mdi:fire",
@ -277,7 +295,6 @@ async def async_setup_entry(
)
sensors = []
for description in HEAT_METER_SENSOR_TYPES:
sensors.append(HeatMeterSensor(coordinator, description, device))
@ -306,6 +323,14 @@ class HeatMeterSensor(
self.entity_description = description
self._attr_device_info = device
if (
description.native_unit_of_measurement
in {UnitOfEnergy.GIGA_JOULE, UnitOfEnergy.MEGA_WATT_HOUR}
and self.native_value is None
):
# Some meters will return MWh, others will return GJ.
self._attr_entity_registry_enabled_default = False
@property
def native_value(self) -> StateType | datetime:
"""Return the state of the sensor."""

View File

@ -1,5 +1,5 @@
# serializer version: 1
# name: test_create_sensors
# name: test_create_sensors[mock_heat_meter_response0]
list([
StateSnapshot({
'attributes': ReadOnlyDict({
@ -276,7 +276,310 @@
'entity_id': 'sensor.heat_meter_meter_date_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '2022-05-20T02:41:17+00:00',
'state': '2022-05-19T19:41:17+00:00',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Measuring range',
'icon': 'mdi:water-outline',
'unit_of_measurement': <UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR: 'm³/h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_measuring_range',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Settings and firmware',
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_settings_and_firmware',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
])
# ---
# name: test_create_sensors[mock_heat_meter_response1]
list([
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Heat Meter Heat usage MWh',
'icon': 'mdi:fire',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.MEGA_WATT_HOUR: 'MWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_heat_usage_mwh',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '123.0',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volume',
'friendly_name': 'Heat Meter Volume usage',
'icon': 'mdi:fire',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfVolume.CUBIC_METERS: 'm³'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_volume_usage',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '456.0',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Heat Meter Heat previous year MWh',
'icon': 'mdi:fire',
'unit_of_measurement': <UnitOfEnergy.MEGA_WATT_HOUR: 'MWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_heat_previous_year_mwh',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '111.0',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volume',
'friendly_name': 'Heat Meter Volume usage previous year',
'icon': 'mdi:fire',
'unit_of_measurement': <UnitOfVolume.CUBIC_METERS: 'm³'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_volume_usage_previous_year',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Ownership number',
'icon': 'mdi:identifier',
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_ownership_number',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Error number',
'icon': 'mdi:home-alert',
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_error_number',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Device number',
'icon': 'mdi:identifier',
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_device_number',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'devicenr_789',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Heat Meter Measurement period minutes',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_measurement_period_minutes',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Heat Meter Power max',
'unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_power_max',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Heat Meter Power max previous year',
'unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_power_max_previous_year',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Flowrate max',
'icon': 'mdi:water-outline',
'unit_of_measurement': <UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR: 'm³/h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_flowrate_max',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Flowrate max previous year',
'icon': 'mdi:water-outline',
'unit_of_measurement': <UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR: 'm³/h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_flowrate_max_previous_year',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Heat Meter Return temperature max',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_return_temperature_max',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Heat Meter Return temperature max previous year',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_return_temperature_max_previous_year',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Heat Meter Flow temperature max',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_flow_temperature_max',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Heat Meter Flow temperature max previous year',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_flow_temperature_max_previous_year',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Heat Meter Operating hours',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_operating_hours',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Heat Meter Flow hours',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_flow_hours',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Heat Meter Fault hours',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_fault_hours',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Heat Meter Fault hours previous year',
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_fault_hours_previous_year',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Yearly set day',
'icon': 'mdi:clock-outline',
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_yearly_set_day',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Heat Meter Monthly set day',
'icon': 'mdi:clock-outline',
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_monthly_set_day',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
}),
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Heat Meter Meter date time',
'icon': 'mdi:clock-outline',
}),
'context': <ANY>,
'entity_id': 'sensor.heat_meter_meter_date_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '2022-05-19T19:41:17+00:00',
}),
StateSnapshot({
'attributes': ReadOnlyDict({

View File

@ -3,6 +3,7 @@ from dataclasses import dataclass
import datetime
from unittest.mock import patch
import pytest
import serial
from syrupy import SnapshotAssertion
@ -25,19 +26,49 @@ API_HEAT_METER_SERVICE = (
class MockHeatMeterResponse:
"""Mock for HeatMeterResponse."""
heat_usage_gj: float
heat_usage_gj: float | None
heat_usage_mwh: float | None
volume_usage_m3: float
heat_previous_year_gj: float
heat_previous_year_gj: float | None
heat_previous_year_mwh: float | None
device_number: str
meter_date_time: datetime.datetime
@pytest.mark.parametrize(
"mock_heat_meter_response",
[
{
"heat_usage_gj": 123.0,
"heat_usage_mwh": None,
"volume_usage_m3": 456.0,
"heat_previous_year_gj": 111.0,
"heat_previous_year_mwh": None,
"device_number": "devicenr_789",
"meter_date_time": dt_util.as_utc(
datetime.datetime(2022, 5, 19, 19, 41, 17)
),
},
{
"heat_usage_gj": None,
"heat_usage_mwh": 123.0,
"volume_usage_m3": 456.0,
"heat_previous_year_gj": None,
"heat_previous_year_mwh": 111.0,
"device_number": "devicenr_789",
"meter_date_time": dt_util.as_utc(
datetime.datetime(2022, 5, 19, 19, 41, 17)
),
},
],
)
@patch(API_HEAT_METER_SERVICE)
async def test_create_sensors(
mock_heat_meter,
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_heat_meter_response,
) -> None:
"""Test sensor."""
entry_data = {
@ -48,13 +79,7 @@ async def test_create_sensors(
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id=DOMAIN, data=entry_data)
mock_entry.add_to_hass(hass)
mock_heat_meter_response = MockHeatMeterResponse(
heat_usage_gj=123.0,
volume_usage_m3=456.0,
heat_previous_year_gj=111.0,
device_number="devicenr_789",
meter_date_time=dt_util.as_utc(datetime.datetime(2022, 5, 19, 19, 41, 17)),
)
mock_heat_meter_response = MockHeatMeterResponse(**mock_heat_meter_response)
mock_heat_meter().read.return_value = mock_heat_meter_response
@ -79,8 +104,10 @@ async def test_exception_on_polling(mock_heat_meter, hass: HomeAssistant) -> Non
# First setup normally
mock_heat_meter_response = MockHeatMeterResponse(
heat_usage_gj=123.0,
heat_usage_mwh=None,
volume_usage_m3=456.0,
heat_previous_year_gj=111.0,
heat_previous_year_mwh=None,
device_number="devicenr_789",
meter_date_time=dt_util.as_utc(datetime.datetime(2022, 5, 19, 19, 41, 17)),
)
@ -106,8 +133,10 @@ async def test_exception_on_polling(mock_heat_meter, hass: HomeAssistant) -> Non
# Now 'enable' and see if next poll succeeds
mock_heat_meter_response = MockHeatMeterResponse(
heat_usage_gj=124.0,
heat_usage_mwh=None,
volume_usage_m3=457.0,
heat_previous_year_gj=112.0,
heat_previous_year_mwh=None,
device_number="devicenr_789",
meter_date_time=dt_util.as_utc(datetime.datetime(2022, 5, 19, 20, 41, 17)),
)