mirror of
https://github.com/home-assistant/core.git
synced 2025-08-02 20:25:07 +02:00
Add morning and evening damping to Forecast solar (#98721)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
committed by
GitHub
parent
a1d554d1cb
commit
30d3df2d96
@@ -5,12 +5,38 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import (
|
||||||
|
CONF_DAMPING,
|
||||||
|
CONF_DAMPING_EVENING,
|
||||||
|
CONF_DAMPING_MORNING,
|
||||||
|
CONF_MODULES_POWER,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from .coordinator import ForecastSolarDataUpdateCoordinator
|
from .coordinator import ForecastSolarDataUpdateCoordinator
|
||||||
|
|
||||||
PLATFORMS = [Platform.SENSOR]
|
PLATFORMS = [Platform.SENSOR]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Migrate old config entry."""
|
||||||
|
|
||||||
|
if entry.version == 1:
|
||||||
|
new_options = entry.options.copy()
|
||||||
|
new_options |= {
|
||||||
|
CONF_MODULES_POWER: new_options.pop("modules power"),
|
||||||
|
CONF_DAMPING_MORNING: new_options.get(CONF_DAMPING, 0.0),
|
||||||
|
CONF_DAMPING_EVENING: new_options.pop(CONF_DAMPING, 0.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.version = 2
|
||||||
|
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry, data=entry.data, options=new_options
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Forecast.Solar from a config entry."""
|
"""Set up Forecast.Solar from a config entry."""
|
||||||
coordinator = ForecastSolarDataUpdateCoordinator(hass, entry)
|
coordinator = ForecastSolarDataUpdateCoordinator(hass, entry)
|
||||||
|
@@ -14,7 +14,8 @@ from homeassistant.helpers import config_validation as cv
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_AZIMUTH,
|
CONF_AZIMUTH,
|
||||||
CONF_DAMPING,
|
CONF_DAMPING_EVENING,
|
||||||
|
CONF_DAMPING_MORNING,
|
||||||
CONF_DECLINATION,
|
CONF_DECLINATION,
|
||||||
CONF_INVERTER_SIZE,
|
CONF_INVERTER_SIZE,
|
||||||
CONF_MODULES_POWER,
|
CONF_MODULES_POWER,
|
||||||
@@ -27,7 +28,7 @@ RE_API_KEY = re.compile(r"^[a-zA-Z0-9]{16}$")
|
|||||||
class ForecastSolarFlowHandler(ConfigFlow, domain=DOMAIN):
|
class ForecastSolarFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a config flow for Forecast.Solar."""
|
"""Handle a config flow for Forecast.Solar."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 2
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@callback
|
@callback
|
||||||
@@ -127,8 +128,16 @@ class ForecastSolarOptionFlowHandler(OptionsFlow):
|
|||||||
default=self.config_entry.options[CONF_MODULES_POWER],
|
default=self.config_entry.options[CONF_MODULES_POWER],
|
||||||
): vol.Coerce(int),
|
): vol.Coerce(int),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_DAMPING,
|
CONF_DAMPING_MORNING,
|
||||||
default=self.config_entry.options.get(CONF_DAMPING, 0.0),
|
default=self.config_entry.options.get(
|
||||||
|
CONF_DAMPING_MORNING, 0.0
|
||||||
|
),
|
||||||
|
): vol.Coerce(float),
|
||||||
|
vol.Optional(
|
||||||
|
CONF_DAMPING_EVENING,
|
||||||
|
default=self.config_entry.options.get(
|
||||||
|
CONF_DAMPING_EVENING, 0.0
|
||||||
|
),
|
||||||
): vol.Coerce(float),
|
): vol.Coerce(float),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_INVERTER_SIZE,
|
CONF_INVERTER_SIZE,
|
||||||
|
@@ -8,6 +8,8 @@ LOGGER = logging.getLogger(__package__)
|
|||||||
|
|
||||||
CONF_DECLINATION = "declination"
|
CONF_DECLINATION = "declination"
|
||||||
CONF_AZIMUTH = "azimuth"
|
CONF_AZIMUTH = "azimuth"
|
||||||
CONF_MODULES_POWER = "modules power"
|
CONF_MODULES_POWER = "modules_power"
|
||||||
CONF_DAMPING = "damping"
|
CONF_DAMPING = "damping"
|
||||||
|
CONF_DAMPING_MORNING = "damping_morning"
|
||||||
|
CONF_DAMPING_EVENING = "damping_evening"
|
||||||
CONF_INVERTER_SIZE = "inverter_size"
|
CONF_INVERTER_SIZE = "inverter_size"
|
||||||
|
@@ -13,7 +13,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_AZIMUTH,
|
CONF_AZIMUTH,
|
||||||
CONF_DAMPING,
|
CONF_DAMPING_EVENING,
|
||||||
|
CONF_DAMPING_MORNING,
|
||||||
CONF_DECLINATION,
|
CONF_DECLINATION,
|
||||||
CONF_INVERTER_SIZE,
|
CONF_INVERTER_SIZE,
|
||||||
CONF_MODULES_POWER,
|
CONF_MODULES_POWER,
|
||||||
@@ -48,7 +49,8 @@ class ForecastSolarDataUpdateCoordinator(DataUpdateCoordinator[Estimate]):
|
|||||||
declination=entry.options[CONF_DECLINATION],
|
declination=entry.options[CONF_DECLINATION],
|
||||||
azimuth=(entry.options[CONF_AZIMUTH] - 180),
|
azimuth=(entry.options[CONF_AZIMUTH] - 180),
|
||||||
kwp=(entry.options[CONF_MODULES_POWER] / 1000),
|
kwp=(entry.options[CONF_MODULES_POWER] / 1000),
|
||||||
damping=entry.options.get(CONF_DAMPING, 0),
|
damping_morning=entry.options.get(CONF_DAMPING_MORNING, 0.0),
|
||||||
|
damping_evening=entry.options.get(CONF_DAMPING_EVENING, 0.0),
|
||||||
inverter=inverter_size,
|
inverter=inverter_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -24,10 +24,11 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"api_key": "Forecast.Solar API Key (optional)",
|
"api_key": "Forecast.Solar API Key (optional)",
|
||||||
"azimuth": "[%key:component::forecast_solar::config::step::user::data::azimuth%]",
|
"azimuth": "[%key:component::forecast_solar::config::step::user::data::azimuth%]",
|
||||||
"damping": "Damping factor: adjusts the results in the morning and evening",
|
"damping_morning": "Damping factor: adjusts the results in the morning",
|
||||||
|
"damping_evening": "Damping factor: adjusts the results in the evening",
|
||||||
"inverter_size": "Inverter size (Watt)",
|
"inverter_size": "Inverter size (Watt)",
|
||||||
"declination": "[%key:component::forecast_solar::config::step::user::data::declination%]",
|
"declination": "[%key:component::forecast_solar::config::step::user::data::declination%]",
|
||||||
"modules power": "[%key:component::forecast_solar::config::step::user::data::modules_power%]"
|
"modules_power": "[%key:component::forecast_solar::config::step::user::data::modules_power%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,8 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components.forecast_solar.const import (
|
from homeassistant.components.forecast_solar.const import (
|
||||||
CONF_AZIMUTH,
|
CONF_AZIMUTH,
|
||||||
CONF_DAMPING,
|
CONF_DAMPING_EVENING,
|
||||||
|
CONF_DAMPING_MORNING,
|
||||||
CONF_DECLINATION,
|
CONF_DECLINATION,
|
||||||
CONF_INVERTER_SIZE,
|
CONF_INVERTER_SIZE,
|
||||||
CONF_MODULES_POWER,
|
CONF_MODULES_POWER,
|
||||||
@@ -37,6 +38,7 @@ def mock_config_entry() -> MockConfigEntry:
|
|||||||
return MockConfigEntry(
|
return MockConfigEntry(
|
||||||
title="Green House",
|
title="Green House",
|
||||||
unique_id="unique",
|
unique_id="unique",
|
||||||
|
version=2,
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_LATITUDE: 52.42,
|
CONF_LATITUDE: 52.42,
|
||||||
@@ -47,7 +49,8 @@ def mock_config_entry() -> MockConfigEntry:
|
|||||||
CONF_DECLINATION: 30,
|
CONF_DECLINATION: 30,
|
||||||
CONF_AZIMUTH: 190,
|
CONF_AZIMUTH: 190,
|
||||||
CONF_MODULES_POWER: 5100,
|
CONF_MODULES_POWER: 5100,
|
||||||
CONF_DAMPING: 0.5,
|
CONF_DAMPING_MORNING: 0.5,
|
||||||
|
CONF_DAMPING_EVENING: 0.5,
|
||||||
CONF_INVERTER_SIZE: 2000,
|
CONF_INVERTER_SIZE: 2000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@@ -33,10 +33,11 @@
|
|||||||
'options': dict({
|
'options': dict({
|
||||||
'api_key': '**REDACTED**',
|
'api_key': '**REDACTED**',
|
||||||
'azimuth': 190,
|
'azimuth': 190,
|
||||||
'damping': 0.5,
|
'damping_evening': 0.5,
|
||||||
|
'damping_morning': 0.5,
|
||||||
'declination': 30,
|
'declination': 30,
|
||||||
'inverter_size': 2000,
|
'inverter_size': 2000,
|
||||||
'modules power': 5100,
|
'modules_power': 5100,
|
||||||
}),
|
}),
|
||||||
'title': 'Green House',
|
'title': 'Green House',
|
||||||
}),
|
}),
|
||||||
|
27
tests/components/forecast_solar/snapshots/test_init.ambr
Normal file
27
tests/components/forecast_solar/snapshots/test_init.ambr
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_migration
|
||||||
|
ConfigEntrySnapshot({
|
||||||
|
'data': dict({
|
||||||
|
'latitude': 52.42,
|
||||||
|
'longitude': 4.42,
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'forecast_solar',
|
||||||
|
'entry_id': <ANY>,
|
||||||
|
'options': dict({
|
||||||
|
'api_key': 'abcdef12345',
|
||||||
|
'azimuth': 190,
|
||||||
|
'damping_evening': 0.5,
|
||||||
|
'damping_morning': 0.5,
|
||||||
|
'declination': 30,
|
||||||
|
'inverter_size': 2000,
|
||||||
|
'modules_power': 5100,
|
||||||
|
}),
|
||||||
|
'pref_disable_new_entities': False,
|
||||||
|
'pref_disable_polling': False,
|
||||||
|
'source': 'user',
|
||||||
|
'title': 'Green House',
|
||||||
|
'unique_id': 'unique',
|
||||||
|
'version': 2,
|
||||||
|
})
|
||||||
|
# ---
|
@@ -3,7 +3,8 @@ from unittest.mock import AsyncMock
|
|||||||
|
|
||||||
from homeassistant.components.forecast_solar.const import (
|
from homeassistant.components.forecast_solar.const import (
|
||||||
CONF_AZIMUTH,
|
CONF_AZIMUTH,
|
||||||
CONF_DAMPING,
|
CONF_DAMPING_EVENING,
|
||||||
|
CONF_DAMPING_MORNING,
|
||||||
CONF_DECLINATION,
|
CONF_DECLINATION,
|
||||||
CONF_INVERTER_SIZE,
|
CONF_INVERTER_SIZE,
|
||||||
CONF_MODULES_POWER,
|
CONF_MODULES_POWER,
|
||||||
@@ -75,7 +76,8 @@ async def test_options_flow_invalid_api(
|
|||||||
CONF_DECLINATION: 21,
|
CONF_DECLINATION: 21,
|
||||||
CONF_AZIMUTH: 22,
|
CONF_AZIMUTH: 22,
|
||||||
CONF_MODULES_POWER: 2122,
|
CONF_MODULES_POWER: 2122,
|
||||||
CONF_DAMPING: 0.25,
|
CONF_DAMPING_MORNING: 0.25,
|
||||||
|
CONF_DAMPING_EVENING: 0.25,
|
||||||
CONF_INVERTER_SIZE: 2000,
|
CONF_INVERTER_SIZE: 2000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -108,7 +110,8 @@ async def test_options_flow(
|
|||||||
CONF_DECLINATION: 21,
|
CONF_DECLINATION: 21,
|
||||||
CONF_AZIMUTH: 22,
|
CONF_AZIMUTH: 22,
|
||||||
CONF_MODULES_POWER: 2122,
|
CONF_MODULES_POWER: 2122,
|
||||||
CONF_DAMPING: 0.25,
|
CONF_DAMPING_MORNING: 0.25,
|
||||||
|
CONF_DAMPING_EVENING: 0.25,
|
||||||
CONF_INVERTER_SIZE: 2000,
|
CONF_INVERTER_SIZE: 2000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -120,7 +123,8 @@ async def test_options_flow(
|
|||||||
CONF_DECLINATION: 21,
|
CONF_DECLINATION: 21,
|
||||||
CONF_AZIMUTH: 22,
|
CONF_AZIMUTH: 22,
|
||||||
CONF_MODULES_POWER: 2122,
|
CONF_MODULES_POWER: 2122,
|
||||||
CONF_DAMPING: 0.25,
|
CONF_DAMPING_MORNING: 0.25,
|
||||||
|
CONF_DAMPING_EVENING: 0.25,
|
||||||
CONF_INVERTER_SIZE: 2000,
|
CONF_INVERTER_SIZE: 2000,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +151,8 @@ async def test_options_flow_without_key(
|
|||||||
CONF_DECLINATION: 21,
|
CONF_DECLINATION: 21,
|
||||||
CONF_AZIMUTH: 22,
|
CONF_AZIMUTH: 22,
|
||||||
CONF_MODULES_POWER: 2122,
|
CONF_MODULES_POWER: 2122,
|
||||||
CONF_DAMPING: 0.25,
|
CONF_DAMPING_MORNING: 0.25,
|
||||||
|
CONF_DAMPING_EVENING: 0.25,
|
||||||
CONF_INVERTER_SIZE: 2000,
|
CONF_INVERTER_SIZE: 2000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -159,6 +164,7 @@ async def test_options_flow_without_key(
|
|||||||
CONF_DECLINATION: 21,
|
CONF_DECLINATION: 21,
|
||||||
CONF_AZIMUTH: 22,
|
CONF_AZIMUTH: 22,
|
||||||
CONF_MODULES_POWER: 2122,
|
CONF_MODULES_POWER: 2122,
|
||||||
CONF_DAMPING: 0.25,
|
CONF_DAMPING_MORNING: 0.25,
|
||||||
|
CONF_DAMPING_EVENING: 0.25,
|
||||||
CONF_INVERTER_SIZE: 2000,
|
CONF_INVERTER_SIZE: 2000,
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,17 @@
|
|||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from forecast_solar import ForecastSolarConnectionError
|
from forecast_solar import ForecastSolarConnectionError
|
||||||
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.forecast_solar.const import DOMAIN
|
from homeassistant.components.forecast_solar.const import (
|
||||||
|
CONF_AZIMUTH,
|
||||||
|
CONF_DAMPING,
|
||||||
|
CONF_DECLINATION,
|
||||||
|
CONF_INVERTER_SIZE,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
@@ -44,3 +52,29 @@ async def test_config_entry_not_ready(
|
|||||||
|
|
||||||
assert mock_request.call_count == 1
|
assert mock_request.call_count == 1
|
||||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_migration(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
|
||||||
|
"""Test config entry version 1 -> 2 migration."""
|
||||||
|
mock_config_entry = MockConfigEntry(
|
||||||
|
title="Green House",
|
||||||
|
unique_id="unique",
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_LATITUDE: 52.42,
|
||||||
|
CONF_LONGITUDE: 4.42,
|
||||||
|
},
|
||||||
|
options={
|
||||||
|
CONF_API_KEY: "abcdef12345",
|
||||||
|
CONF_DECLINATION: 30,
|
||||||
|
CONF_AZIMUTH: 190,
|
||||||
|
"modules power": 5100,
|
||||||
|
CONF_DAMPING: 0.5,
|
||||||
|
CONF_INVERTER_SIZE: 2000,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.config_entries.async_get_entry(mock_config_entry.entry_id) == snapshot
|
||||||
|
Reference in New Issue
Block a user