From fe8bb7efa3003aef2457febbc39c1afffdac3d6c Mon Sep 17 00:00:00 2001 From: Anthony <210036686+Herbertmt978@users.noreply.github.com> Date: Mon, 13 Jul 2026 20:50:56 +0100 Subject: [PATCH 1/2] fix: allow partial Home Assistant forecast config --- .../forecast/providers/home_assistant_api.py | 8 +---- .../forecast/test_home_assistant_api.py | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py diff --git a/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py b/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py index fdcedf27..3070e570 100644 --- a/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py +++ b/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py @@ -66,8 +66,7 @@ def create( forecast_provider_config: ForecastProviderHomeAssistantConfig = config service_home_assistant_api = cast(ServiceHomeAssistantAPI, external_service) - # Use the builder to configure the provider, in this way we can - # ensure that all required entities are set. + # Use the builder to configure only the entities supplied by the user. builder = HomeAssistantForecastProviderBuilder(home_assistant=service_home_assistant_api, logger=logger) # Configure the builder with the entities and units @@ -205,11 +204,6 @@ def set_remaining_today_energy_entity( def build(self) -> "HomeAssistantForecastProvider": """Builds the HomeAssistantForecastProvider instance.""" - if not self.entity_forecast_power_actual_h: - raise ValueError("Entity ID for actual solar power forecast is required.") - if not self.entity_forecast_energy_actual_h: - raise ValueError("Entity ID for actual solar energy forecast is required.") - forecast_provider = HomeAssistantForecastProvider( home_assistant=self.home_assistant, entity_forecast_power_actual_h=self.entity_forecast_power_actual_h, diff --git a/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py b/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py new file mode 100644 index 00000000..74f4e2e3 --- /dev/null +++ b/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py @@ -0,0 +1,35 @@ +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from edge_mining.adapters.domain.forecast.providers.home_assistant_api import ( + HomeAssistantForecastProviderFactory, +) +from edge_mining.shared.adapter_configs.forecast import ForecastProviderHomeAssistantConfig +from edge_mining.shared.external_services.common import ExternalServiceAdapter + + +@pytest.mark.asyncio +async def test_factory_accepts_partial_entity_configuration(): + home_assistant = MagicMock() + home_assistant.external_service_type = ExternalServiceAdapter.HOME_ASSISTANT_API + home_assistant.get_entity_state = AsyncMock(return_value=("1.5", {})) + home_assistant.parse_energy.return_value = 1.5 + config = ForecastProviderHomeAssistantConfig( + entity_forecast_energy_today="sensor.solar_forecast_today", + ) + + provider = HomeAssistantForecastProviderFactory().create( + config=config, + logger=None, + external_service=home_assistant, + ) + + assert provider.entity_forecast_energy_today == "sensor.solar_forecast_today" + assert provider.entity_forecast_power_actual_h is None + assert provider.entity_forecast_energy_actual_h is None + + forecast = await provider.get_forecast() + + assert len(forecast.intervals) == 1 + home_assistant.get_entity_state.assert_awaited_once_with("sensor.solar_forecast_today") From 4e9c05a97c8264c76456f267e6b8bd149d3e3642 Mon Sep 17 00:00:00 2001 From: Herbertmt978 <210036686+Herbertmt978@users.noreply.github.com> Date: Tue, 28 Jul 2026 00:31:33 +0100 Subject: [PATCH 2/2] fix: reject empty Home Assistant forecast config --- .../forecast/providers/home_assistant_api.py | 15 +++++++++++++++ .../domain/forecast/test_home_assistant_api.py | 13 +++++++++++++ 2 files changed, 28 insertions(+) diff --git a/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py b/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py index 3070e570..87d5a1cf 100644 --- a/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py +++ b/core/edge_mining/adapters/domain/forecast/providers/home_assistant_api.py @@ -204,6 +204,21 @@ def set_remaining_today_energy_entity( def build(self) -> "HomeAssistantForecastProvider": """Builds the HomeAssistantForecastProvider instance.""" + if not any( + ( + self.entity_forecast_power_actual_h, + self.entity_forecast_power_next_1h, + self.entity_forecast_power_next_12h, + self.entity_forecast_power_next_24h, + self.entity_forecast_energy_actual_h, + self.entity_forecast_energy_next_1h, + self.entity_forecast_energy_today, + self.entity_forecast_energy_tomorrow, + self.entity_forecast_energy_remaining_today, + ) + ): + raise ForecastError("At least one Home Assistant forecast entity is required.") + forecast_provider = HomeAssistantForecastProvider( home_assistant=self.home_assistant, entity_forecast_power_actual_h=self.entity_forecast_power_actual_h, diff --git a/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py b/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py index 74f4e2e3..361c9c6f 100644 --- a/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py +++ b/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py @@ -5,6 +5,7 @@ from edge_mining.adapters.domain.forecast.providers.home_assistant_api import ( HomeAssistantForecastProviderFactory, ) +from edge_mining.domain.forecast.exceptions import ForecastError from edge_mining.shared.adapter_configs.forecast import ForecastProviderHomeAssistantConfig from edge_mining.shared.external_services.common import ExternalServiceAdapter @@ -33,3 +34,15 @@ async def test_factory_accepts_partial_entity_configuration(): assert len(forecast.intervals) == 1 home_assistant.get_entity_state.assert_awaited_once_with("sensor.solar_forecast_today") + + +def test_factory_rejects_empty_entity_configuration(): + home_assistant = MagicMock() + home_assistant.external_service_type = ExternalServiceAdapter.HOME_ASSISTANT_API + + with pytest.raises(ForecastError, match="At least one Home Assistant forecast entity is required"): + HomeAssistantForecastProviderFactory().create( + config=ForecastProviderHomeAssistantConfig(), + logger=None, + external_service=home_assistant, + )