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 fdcedf2..87d5a1c 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,10 +204,20 @@ 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.") + 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, 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 0000000..361c9c6 --- /dev/null +++ b/core/tests/unit/adapters/domain/forecast/test_home_assistant_api.py @@ -0,0 +1,48 @@ +from unittest.mock import AsyncMock, MagicMock + +import pytest + +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 + + +@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") + + +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, + )