Skip to content

Commit 47e9376

Browse files
fix(inverter): hard-raise when no charge window source is configured at all
Splits what update_status() previously collapsed into one "safe defaults, retry next update" path, per maintainer review on #4288: - REST configured (givtcp_rest) but rest_data hasn't returned anything yet this cycle - genuinely transient (fetch hiccup, or before the first poll on a fresh start), so still falls through to the graceful retry path. - Neither a REST API nor charge_start_time configured at all - a permanent setup gap, not something that resolves on its own. Retrying forever would misrepresent a plan Predbat can't actually deliver, so this now raises a ValueError with a proper message (unlike the original bare `raise ValueError`) and records the error status before doing so. Split into two tests: the existing no-source-configured test now expects the raise, and a new test covers the REST-pending case staying graceful.
1 parent 227f573 commit 47e9376

2 files changed

Lines changed: 68 additions & 12 deletions

File tree

‎apps/predbat/inverter.py‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,14 +1467,23 @@ def update_status(self, minutes_now, quiet=False):
14671467
elif "charge_start_time" in self.base.args:
14681468
charge_start_time = time_string_to_stamp(self.base.get_arg("charge_start_time", index=self.id))
14691469
charge_end_time = time_string_to_stamp(self.base.get_arg("charge_end_time", index=self.id))
1470-
else:
1471-
# Neither a data source nor a fallback config value is available - log the specific
1472-
# reason here, but let the shared handling below (which also covers a source that
1473-
# returned an unusable value) set the status entity and safe defaults, so this
1474-
# doesn't hard-crash the whole plan for what may just be a temporarily empty feed.
1475-
self.log("Error: Inverter {} unable to read charge window time as neither REST, charge_start_time or charge_start_hour are set".format(self.id))
1470+
elif self.rest_api:
1471+
# A REST data source (givtcp_rest) is configured but hasn't returned anything this
1472+
# cycle - genuinely transient (a fetch hiccup, or before the first poll on a fresh
1473+
# start, e.g. GivEnergy cloud "no devices"), so fall through to the same safe-defaults/
1474+
# retry-next-update handling below as a configured-but-currently-unusable value,
1475+
# rather than crashing the whole plan for something that should resolve itself.
14761476
charge_start_time = None
14771477
charge_end_time = None
1478+
else:
1479+
# Neither a REST API nor a charge_start_time config value is configured at all - a
1480+
# permanent setup gap, not something that will resolve on its own. Retrying every
1481+
# cycle forever would be misleading, so don't pretend to make a plan Predbat can't
1482+
# actually deliver (maintainer call on #4288/#4179 - see PR review discussion).
1483+
message = "Error: Inverter {} unable to read charge window time as neither REST, charge_start_time or charge_start_hour are set".format(self.id)
1484+
self.log(message)
1485+
self.base.record_status(message, had_errors=True)
1486+
raise ValueError(message)
14781487

14791488
if charge_start_time is None or charge_end_time is None:
14801489
self.log("Warn: Inverter {} unable to read charge window time as charge_start_time or charge_end_time is None, will retry next update".format(self.id))

‎apps/predbat/tests/test_inverter.py‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,10 +1395,11 @@ def test_charge_window_none_value(test_name, my_predbat, dummy_items):
13951395

13961396
def test_charge_window_no_source_configured(test_name, my_predbat, dummy_items):
13971397
"""
1398-
Test charge window handling when neither REST data nor a charge_start_time/charge_end_time
1399-
config source is available at all (issue #4179) - previously a bare `raise ValueError` that
1400-
crashed the whole plan with an unhelpful "Error: Exception raised" status. Should now fall
1401-
through to the same safe-defaults/retry-next-update handling as an unusable configured value.
1398+
Test charge window handling when neither a REST API (givtcp_rest) nor a charge_start_time/
1399+
charge_end_time config source is configured at all (issue #4179 / PR #4288 review). This is a
1400+
permanent setup gap, not a transient one, so update_status() should raise a clear, informative
1401+
ValueError (unlike the original bare `raise ValueError` with no message) rather than silently
1402+
retrying forever on a config gap that will never close itself - maintainer call on #4288.
14021403
"""
14031404
failed = False
14041405
print(f"**** Running Test: {test_name} ****")
@@ -1415,12 +1416,54 @@ def test_charge_window_no_source_configured(test_name, my_predbat, dummy_items):
14151416
original_charge_end_time = my_predbat.args.pop("charge_end_time", None)
14161417
dummy_items["switch.scheduled_charge_enable"] = "on"
14171418

1419+
try:
1420+
inv.update_status(my_predbat.minutes_now)
1421+
print(f"ERROR: {test_name} - update_status should raise ValueError when no charge window source is configured at all")
1422+
failed = True
1423+
except ValueError as e:
1424+
if "neither REST, charge_start_time or charge_start_hour are set" not in str(e):
1425+
print(f"ERROR: {test_name} - ValueError message should explain the cause, got: {e}")
1426+
failed = True
1427+
if "Error: Inverter" not in my_predbat.current_status:
1428+
print(f"ERROR: {test_name} - current_status should reflect the error, got: {my_predbat.current_status}")
1429+
failed = True
1430+
finally:
1431+
# Restore config so this doesn't affect later tests
1432+
if original_charge_start_time is not None:
1433+
my_predbat.args["charge_start_time"] = original_charge_start_time
1434+
if original_charge_end_time is not None:
1435+
my_predbat.args["charge_end_time"] = original_charge_end_time
1436+
1437+
return failed
1438+
1439+
1440+
def test_charge_window_rest_configured_but_no_data_yet(test_name, my_predbat, dummy_items):
1441+
"""
1442+
Test charge window handling when a REST API (givtcp_rest) *is* configured but rest_data hasn't
1443+
successfully returned anything yet this cycle (issue #4179's actual production trigger -
1444+
GivEnergy cloud "no devices" or a fresh start before the first poll). Unlike the "nothing
1445+
configured at all" case above, this is genuinely transient - the data source is legitimate, it
1446+
just hasn't produced a value yet - so it should fall through to the same safe-defaults/
1447+
retry-next-update handling as a configured-but-currently-unusable value, not raise.
1448+
"""
1449+
failed = False
1450+
print(f"**** Running Test: {test_name} ****")
1451+
1452+
inv = Inverter(my_predbat, 0)
1453+
inv.sleep = dummy_sleep
1454+
inv.inv_has_charge_enable_time = True
1455+
inv.rest_api = "http://givtcp:6345"
1456+
inv.rest_data = None
1457+
1458+
original_charge_start_time = my_predbat.args.pop("charge_start_time", None)
1459+
original_charge_end_time = my_predbat.args.pop("charge_end_time", None)
1460+
dummy_items["switch.scheduled_charge_enable"] = "on"
1461+
14181462
try:
14191463
inv.update_status(my_predbat.minutes_now)
14201464
except ValueError as e:
1421-
print(f"ERROR: {test_name} - update_status should not raise when no charge window source is configured, got ValueError({e})")
1465+
print(f"ERROR: {test_name} - update_status should not raise while a configured REST source just hasn't returned data yet, got ValueError({e})")
14221466
failed = True
1423-
# Restore config before returning so this doesn't affect later tests
14241467
if original_charge_start_time is not None:
14251468
my_predbat.args["charge_start_time"] = original_charge_start_time
14261469
if original_charge_end_time is not None:
@@ -2697,6 +2740,10 @@ def run_inverter_tests(my_predbat_dummy):
26972740
if failed:
26982741
return failed
26992742

2743+
failed |= test_charge_window_rest_configured_but_no_data_yet("charge_window_rest_configured_but_no_data_yet", my_predbat, dummy_items)
2744+
if failed:
2745+
return failed
2746+
27002747
# Test discharge window None handling
27012748
failed |= test_discharge_window_none_illegal_time("discharge_window_illegal_time", my_predbat, dummy_items)
27022749
if failed:

0 commit comments

Comments
 (0)