Skip to content

Commit 1e1eb1c

Browse files
Merge pull request #829 from OptimalNothing90/push/issue-818-ignore-pv-feedback-wiring
fix(utils): wire ignore_pv_feedback_during_curtailment runtime flag (#818)
2 parents 6537c47 + 6ba7e2a commit 1e1eb1c

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/emhass/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,17 @@ def _get_ml_param(name, params, runtimeparams, default=None, cast=None):
12331233
weather_forecast_cache_only = runtimeparams["weather_forecast_cache_only"]
12341234
params["passed_data"]["weather_forecast_cache_only"] = weather_forecast_cache_only
12351235

1236+
# Param to bypass PV-feedback mixing during curtailment events (#818)
1237+
if "ignore_pv_feedback_during_curtailment" not in runtimeparams.keys():
1238+
ignore_pv_feedback_during_curtailment = False
1239+
else:
1240+
ignore_pv_feedback_during_curtailment = bool(
1241+
runtimeparams["ignore_pv_feedback_during_curtailment"]
1242+
)
1243+
params["passed_data"]["ignore_pv_feedback_during_curtailment"] = (
1244+
ignore_pv_feedback_during_curtailment
1245+
)
1246+
12361247
# A condition to manually save entity data under data_path/entities after optimization
12371248
if "entity_save" not in runtimeparams.keys():
12381249
entity_save = ""

tests/test_utils.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,52 @@ async def test_treat_runtimeparams_preserves_high_out_of_band_soc_init(self):
12811281
self.assertEqual(params_out["passed_data"]["soc_init"], 0.95)
12821282
self.assertEqual(params_out["passed_data"]["soc_final"], 0.6)
12831283

1284+
async def test_treat_runtimeparams_ignore_pv_feedback_during_curtailment(self):
1285+
"""Wiring for ignore_pv_feedback_during_curtailment runtime flag (#818).
1286+
1287+
The read site in forecast.py reads from params["passed_data"]; this
1288+
test pins the runtime → passed_data path for the four realistic input
1289+
shapes: missing key (default False), JSON bool true, string "true",
1290+
string "false". The string cases document the bool() coerce behaviour.
1291+
"""
1292+
params = await TestUtils.get_test_params()
1293+
params_json = orjson.dumps(params).decode("utf-8")
1294+
retrieve_hass_conf, optim_conf, plant_conf = utils.get_yaml_parse(params_json, logger)
1295+
1296+
async def run(runtimeparams_dict):
1297+
runtimeparams_json = orjson.dumps(runtimeparams_dict).decode("utf-8")
1298+
params_out, _, _, _ = await treat_runtimeparams(
1299+
runtimeparams_json,
1300+
params_json,
1301+
retrieve_hass_conf,
1302+
optim_conf,
1303+
plant_conf,
1304+
"naive-mpc-optim",
1305+
logger,
1306+
emhass_conf,
1307+
)
1308+
return orjson.loads(params_out)
1309+
1310+
# Case 1: key absent -> default False
1311+
out = await run({"prediction_horizon": 10})
1312+
self.assertIs(out["passed_data"]["ignore_pv_feedback_during_curtailment"], False)
1313+
1314+
# Case 2: JSON bool true -> True
1315+
out = await run({"prediction_horizon": 10, "ignore_pv_feedback_during_curtailment": True})
1316+
self.assertIs(out["passed_data"]["ignore_pv_feedback_during_curtailment"], True)
1317+
1318+
# Case 3: string "true" -> bool() coerce -> True
1319+
out = await run({"prediction_horizon": 10, "ignore_pv_feedback_during_curtailment": "true"})
1320+
self.assertIs(out["passed_data"]["ignore_pv_feedback_during_curtailment"], True)
1321+
1322+
# Case 4: string "false" -> bool() coerce -> True (Python bool("false") is True)
1323+
# Documents the known limitation of bool() on non-empty strings;
1324+
# JSON bool transport is the supported shape.
1325+
out = await run(
1326+
{"prediction_horizon": 10, "ignore_pv_feedback_during_curtailment": "false"}
1327+
)
1328+
self.assertIs(out["passed_data"]["ignore_pv_feedback_during_curtailment"], True)
1329+
12841330
def test_param_to_config(self):
12851331
"""Test converting built params back to a flat config dictionary and masking secrets."""
12861332
# Create a mock parameter dictionary with the required categories

0 commit comments

Comments
 (0)