mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Separate energy endpoint MELCloud Home (#179124)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Coordinator for MELCloud Home."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import override
|
||||
|
||||
@@ -104,44 +104,24 @@ class MelCloudHomeCoordinator(DataUpdateCoordinator[UserContext]):
|
||||
_LOGGER.debug("Removing stale device: %s", device.identifiers)
|
||||
registry.async_remove_device(device.id)
|
||||
|
||||
async def _async_get_energy(
|
||||
self, unit_id: str, start_of_month: datetime, now: datetime
|
||||
) -> float | None:
|
||||
"""Fetch energy telemetry for a unit without failing the whole update."""
|
||||
try:
|
||||
energy = await self.client.get_energy_telemetry(
|
||||
unit_id, from_dt=start_of_month, to_dt=now
|
||||
)
|
||||
except MelCloudHomeConnectionError, MelCloudHomeTimeoutError:
|
||||
_LOGGER.warning("Failed to fetch energy telemetry for %s:", unit_id)
|
||||
return None
|
||||
return sum(float(e.value) for e in energy)
|
||||
|
||||
@override
|
||||
async def _async_update_data(self) -> UserContext:
|
||||
"""Fetch data from the MELCloud Home API."""
|
||||
try:
|
||||
data = await self.client.get_context()
|
||||
|
||||
start_of_month = utcnow().replace(
|
||||
day=1, hour=0, minute=0, second=0, microsecond=0
|
||||
)
|
||||
for building in data.buildings:
|
||||
for ata_unit in building.air_to_air_units:
|
||||
self.ata_units[ata_unit.id] = ata_unit
|
||||
if (
|
||||
ata_unit.capabilities
|
||||
and ata_unit.capabilities.has_energy_consumed_meter
|
||||
):
|
||||
energy = await self.client.get_energy_telemetry(
|
||||
ata_unit.id,
|
||||
from_dt=start_of_month,
|
||||
to_dt=utcnow(),
|
||||
)
|
||||
self.ata_energy[ata_unit.id] = sum(
|
||||
float(e.value) for e in energy
|
||||
)
|
||||
for atw_unit in building.air_to_water_units:
|
||||
self.atw_units[atw_unit.id] = atw_unit
|
||||
if (
|
||||
atw_unit.capabilities
|
||||
and atw_unit.capabilities.has_energy_consumed_meter
|
||||
):
|
||||
energy = await self.client.get_energy_telemetry(
|
||||
atw_unit.id,
|
||||
from_dt=start_of_month,
|
||||
to_dt=utcnow(),
|
||||
)
|
||||
self.atw_energy[atw_unit.id] = sum(
|
||||
float(e.value) for e in energy
|
||||
)
|
||||
except MelCloudHomeAuthenticationError as err:
|
||||
raise ConfigEntryAuthFailed(
|
||||
translation_domain=DOMAIN,
|
||||
@@ -157,8 +137,32 @@ class MelCloudHomeCoordinator(DataUpdateCoordinator[UserContext]):
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="timeout_connect",
|
||||
) from err
|
||||
else:
|
||||
return data
|
||||
|
||||
start_of_month = utcnow().replace(
|
||||
day=1, hour=0, minute=0, second=0, microsecond=0
|
||||
)
|
||||
now = utcnow()
|
||||
for building in data.buildings:
|
||||
for ata_unit in building.air_to_air_units:
|
||||
self.ata_units[ata_unit.id] = ata_unit
|
||||
if (
|
||||
ata_unit.capabilities
|
||||
and ata_unit.capabilities.has_energy_consumed_meter
|
||||
):
|
||||
self.ata_energy[ata_unit.id] = await self._async_get_energy(
|
||||
ata_unit.id, start_of_month, now
|
||||
)
|
||||
for atw_unit in building.air_to_water_units:
|
||||
self.atw_units[atw_unit.id] = atw_unit
|
||||
if (
|
||||
atw_unit.capabilities
|
||||
and atw_unit.capabilities.has_energy_consumed_meter
|
||||
):
|
||||
self.atw_energy[atw_unit.id] = await self._async_get_energy(
|
||||
atw_unit.id, start_of_month, now
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
@callback
|
||||
@override
|
||||
|
||||
@@ -193,3 +193,42 @@ async def test_new_atw_unit_callback(
|
||||
if "heat_pump" in entity.entity_id
|
||||
]
|
||||
assert atw_entities
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exception",
|
||||
[
|
||||
pytest.param(MelCloudHomeConnectionError("cannot connect"), id="connection"),
|
||||
pytest.param(MelCloudHomeTimeoutError("timeout"), id="timeout"),
|
||||
],
|
||||
)
|
||||
async def test_energy_update_cycle_fails(
|
||||
hass: HomeAssistant,
|
||||
mock_melcloud_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
"""Test that a failing energy fetch clears the value without unloading the entry."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
coordinator = mock_config_entry.runtime_data
|
||||
|
||||
assert coordinator.ata_energy["ata-unit-uuid-1"] is not None
|
||||
assert coordinator.atw_energy["atw-unit-uuid-1"] is not None
|
||||
|
||||
mock_melcloud_client.get_energy_telemetry.side_effect = exception
|
||||
freezer.tick(UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert coordinator.ata_energy["ata-unit-uuid-1"] is None
|
||||
assert coordinator.atw_energy["atw-unit-uuid-1"] is None
|
||||
|
||||
# Demonstrate a recovery
|
||||
mock_melcloud_client.get_energy_telemetry.side_effect = None
|
||||
freezer.tick(UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert coordinator.ata_energy["ata-unit-uuid-1"] is not None
|
||||
assert coordinator.atw_energy["atw-unit-uuid-1"] is not None
|
||||
|
||||
Reference in New Issue
Block a user