Skip to content

Commit 6a9ed9b

Browse files
fix(enphase): treat a pending schedule family as supported
The cloud reports a schedule family as scheduleStatus "pending" while a change settles on the gateway - the normal state straight after any write Predbat makes. Only "active"/"enabled"/"supported"/"available" counted as supported, so a site with a perfectly good charge-from-grid family was judged incapable of it, automatic_config raised and run() returned False: Warn: Automatic configuration skipped - Charge-from-grid (CFG) scheduling not supported on this site, cannot configure seen on a site whose cfg family held an active schedule and whose profile reported scheduleSupported true for both cfg and dtg. "pending" now counts as supported, and so does any family that actually holds a schedule, whatever the status string says. "not_supported" still reports unsupported. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 5a126bd commit 6a9ed9b

2 files changed

Lines changed: 30 additions & 3 deletions

File tree

apps/predbat/enphase.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,10 +1334,14 @@ async def get_schedules(self, site_id):
13341334
entry = details[0] if details else {}
13351335
details = await self._prune_sibling_schedules(site_id, family_key, details, entry)
13361336
# "supported" gates whether Predbat can use this schedule family. Real accounts report
1337-
# a per-family scheduleStatus ("active" seen so far); treat the usable statuses as
1338-
# supported, with a fallback to the (unverified) boolean flags.
1337+
# a per-family scheduleStatus; "active" and "pending" have both been seen, and
1338+
# "not_supported" is how a genuinely unavailable family reports. "pending" only means a
1339+
# schedule change is still settling on the gateway - which is the normal state straight
1340+
# after any write Predbat makes - so it must not be read as unsupported, or Predbat
1341+
# decides mid-run that the site cannot charge from grid and abandons configuration.
1342+
# A family that actually holds a schedule is supported whatever the status says.
13391343
status_text = str(family_data.get("scheduleStatus", "")).strip().lower()
1340-
supported = status_text in ("active", "enabled", "supported", "available") or bool(family_data.get("scheduleSupported") or family_data.get("forceScheduleSupported"))
1344+
supported = status_text in ("active", "enabled", "supported", "available", "pending") or bool(details) or bool(family_data.get("scheduleSupported") or family_data.get("forceScheduleSupported"))
13411345
parsed[family_key] = {
13421346
"id": entry.get("scheduleId") or entry.get("id"),
13431347
"startTime": entry.get("startTime"),

apps/predbat/tests/test_enphase_api.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,28 @@ def test_get_schedules_supported_from_status():
840840
assert api.dtg_supported("12345") is False # 'not_supported' status
841841

842842

843+
def test_get_schedules_pending_family_is_still_supported():
844+
"""A family whose scheduleStatus is 'pending' is supported - a write is in flight, that is all.
845+
846+
The cloud reports a family as 'pending' while a schedule change settles on the gateway, which
847+
happens right after any write Predbat makes. Treating that as unsupported made Predbat decide
848+
the site could not do charge-from-grid at all and abandon automatic configuration, even with an
849+
active schedule sitting in the family.
850+
"""
851+
api = MockEnphaseAPI()
852+
detail = {"scheduleId": "c1", "startTime": "04:30", "endTime": "04:40", "limit": 5, "scheduleType": "CFG", "isDeleted": False, "isEnabled": True, "scheduleStatus": "active"}
853+
payload = {
854+
"type": "BATTERY_SCHEDULES_CONFIG",
855+
"cfg": {"scheduleStatus": "pending", "count": 1, "details": [detail]},
856+
"dtg": {"scheduleStatus": "pending", "count": 1, "details": [dict(detail, scheduleId="d1", scheduleType="DTG")]},
857+
"rbd": {"scheduleStatus": "active", "count": 0},
858+
}
859+
api.set_http_response("/service/batteryConfig/api/v1/battery/sites/12345/schedules", 200, payload)
860+
run_async(api.get_schedules("12345"))
861+
assert api.schedules["12345"]["cfg"]["supported"] is True
862+
assert api.dtg_supported("12345") is True
863+
864+
843865
def test_inverter_def_enphase():
844866
"""EnphaseCloud INVERTER_DEF exists with the agreed capability flags."""
845867
from config import INVERTER_DEF
@@ -1943,6 +1965,7 @@ def run_enphase_api_tests(my_predbat):
19431965
test_automatic_config_no_dtg_raises()
19441966
test_automatic_config_no_charge_support_raises()
19451967
test_get_schedules_supported_from_status()
1968+
test_get_schedules_pending_family_is_still_supported()
19461969
test_inverter_def_enphase()
19471970
test_run_first_polls_all_tiers()
19481971
test_get_today()

0 commit comments

Comments
 (0)