fix(deye): send touDays, so the TOU programme actually runs - #4591
Open
springfall2008 wants to merge 1 commit into
Open
fix(deye): send touDays, so the TOU programme actually runs#4591springfall2008 wants to merge 1 commit into
springfall2008 wants to merge 1 commit into
Conversation
Follow-up to #4589's note asking whether deye.py shares the per-slot gap found on Sunsynk. It does not - but the same class of defect is here at the top level instead. **touDays was never sent.** DEYE's TOU programme runs only on the days named in touDays, and Predbat sent touAction without it, leaving the active days at whatever the inverter already held. A programme whose days are empty, or which omits the day the plan is for, is stored and never applied - the same silent failure as Sunsynk's dropped flags, at a different level. All four official strategy samples (clientcode/strategy/dynamic_control_*.py) send the full seven-day list, and the endpoint contract in the design spec has carried touDays[] since the beginning; it simply never reached the payload. Sunsynk sets its seven mondayOn..sundayOn flags for exactly this reason. All seven is the only correct value: Predbat re-derives a 24h programme every cycle and has no notion of a day its plan should be dormant. **The per-slot field set is complete, so the Sunsynk failure cannot occur.** TimeUseSettingItem is exactly {time, power, soc, enableGridCharge, enableGeneration} in the official commission sample and in all four strategy samples, and Predbat writes all five on every slot. There is no sell/export enable to omit: that bit exists in the underlying register (prog{n} 0x40 single-phase, 0x20 three-phase) but the DEYE cloud does not expose it per slot, deriving export from workMode instead. A new test pins the field set rather than trusting each call site to remember it - proven to fail by dropping one field. **Two inferences from #4580 are now confirmed from DEYE's own side.** ZERO_EXPORT_TO_CT for non-export states and solarSellAction on were both taken from Sunsynk hardware and flagged as unverified on DEYE. The official self-consumption sample is exactly ZERO_EXPORT_TO_CT with solarSellAction on; three of the four samples send solarSellAction on. Recorded in the comments where those decisions live. Follows up #4589 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Ensures Predbat’s DEYE dynamic control writes include the touDays field so the inverter’s TOU programme actually runs on all days, matching DEYE’s documented/sample payload contract. This is part of the DEYE control-path (apps/predbat/deye.py) that builds and posts the 24h TOU schedule every cycle.
Changes:
- Add
touDays(all seven days) to the DEYE dynamic control payload. - Introduce
DEYE_TOU_DAYSconstant documenting and centralizing the required seven-day list. - Add DEYE control tests that verify
touDaysis always present and that each TOU slot includes the complete documented field set.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| apps/predbat/deye.py | Adds touDays to the dynamic payload and updates inline rationale/comments. |
| apps/predbat/deye_const.py | Defines DEYE_TOU_DAYS and expands documentation around TOU slot field completeness. |
| apps/predbat/tests/test_deye_control.py | Adds tests for touDays presence and per-slot field set completeness; registers them in the DEYE suite. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| """ | ||
| failed = False | ||
| d = MockDeye().with_rating("INV1") | ||
| expected = set(TOU_FIELD.values()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to the note at the end of #4589, which asked whether
deye.pyshares the per-slot gap found on Sunsynk. It does not — but the same class of defect is present at the top level instead.touDayswas never sentDEYE's TOU programme runs only on the days named in
touDays. Predbat senttouAction: "on"without it, leaving the active days at whatever the inverter already held. A programme whose days are empty, or which omits the day the plan is for, is stored and simply never applied — the same silent failure mode as Sunsynk's dropped per-slot flags, one level up.All four official strategy samples send the full seven-day list:
and the endpoint contract in the design spec has carried
touDays[]from the start — it just never reached the payload. Sunsynk sets its sevenmondayOn..sundayOnflags for exactly this reason.All seven days is the only correct value here: Predbat re-derives a 24h programme every cycle and has no notion of a day its plan should be dormant.
The per-slot field set is complete, so #4589's failure cannot occur here
TimeUseSettingItemis exactly{time, power, soc, enableGridCharge, enableGeneration}in the officialcommission/sys_tou_update.pysample and in all fourstrategy/dynamic_control_*.pysamples, and Predbat writes all five on every slot.There is no sell/export enable to omit. That bit does exist in the underlying register —
prog{n}0x40single-phase,0x20three-phase, which is what Sunsynk Connect exposes assellTime{n}En— but the DEYE cloud does not expose it per slot, deriving export fromworkModeinstead.A new test pins the field set rather than trusting each call site to remember it. It was proven to fail by dropping one field:
Two inferences from #4580 are now confirmed from DEYE's own side
ZERO_EXPORT_TO_CTfor non-export states andsolarSellAction: "on"were both taken from Sunsynk hardware and shipped in #4586 flagged as unverified on DEYE. The official samples settle both:dynamic_control_self_consumption.pyis exactlyworkMode: ZERO_EXPORT_TO_CTwithsolarSellAction: "on"solarSellAction: "on"; only fully-charge omits itdynamic_control_fully_charge.pyusesZERO_EXPORT_TO_CTwithgridChargeAction: "on"and a high slot SOC — Predbat's charge stateRecorded in the comments where those decisions live, so the "unconfirmed on DEYE" caveat does not outlive the evidence.
Noted but deliberately not changed
The samples set
enableGridCharge: Trueon every slot in all four strategies, controlling behaviour through the top-levelgridChargeActionand the slot SOC target instead. Predbat sets it per-slot so a single payload can carry a charge window among self-use slots — something none of the samples exercise, since each is a whole-day single behaviour. Left alone; worth revisiting if a live DEYE write ever shows the per-slot flag behaving differently.maxSellPower/maxSolarPoweralso appear in the export sample. They are installer-owned export caps, not Predbat's to write, so they stay out of the payload.Tests
tou_daysslot_field_setAll 7 DEYE suites, the full
--quickrun andrun_pre_commitpass.Follows up #4589
🤖 Generated with Claude Code