Skip to content

Fetch effects from /json/effects to handle ESP8266 buffer truncation#2083

Draft
mik-laj wants to merge 8 commits into
frenck:mainfrom
mik-laj:fix/effects-boot-time-comparison
Draft

Fetch effects from /json/effects to handle ESP8266 buffer truncation#2083
mik-laj wants to merge 8 commits into
frenck:mainfrom
mik-laj:fix/effects-boot-time-comparison

Conversation

@mik-laj

@mik-laj mik-laj commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

On ESP8266 devices the /json response truncates the effects array when the output buffer is too small — the device may report fxcount=220 but return only 54 entries. This causes KeyError when a segment references an effect ID that is missing from the truncated list (WLED issue #5674).

Fix this by always fetching the complete list from /json/effects on initial load, which is a dedicated endpoint not affected by the buffer limit. On subsequent updates, re-fetch only when fxcount changes or a device restart is detected (boot_time shift > 2 s, derived as now - uptime to avoid false positives from the ever-incrementing uptime counter).

The approach mirrors the existing _check_presets_changed pattern already used for preset re-fetching.

Test plan

  • test_update_skips_effects_when_unchanged — no re-fetch when fxcount and boot_time unchanged
  • test_update_refetches_effects_after_device_restart — re-fetch triggered when uptime resets
  • test_update_uses_effects_endpoint_for_full_list — full list from /json/effects despite truncated /json response (ESP8266 scenario)

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Improvements
    • Effects data synchronization is now more efficient, fetching only when device effects have changed.
    • Enhanced handling for truncated effects lists that may occur on memory-constrained devices.
    • Effects are automatically refreshed following device restarts.

On ESP8266 devices the /json response truncates the effects array when
the output buffer is too small — the device may report fxcount=220 but
return only 54 entries. This causes KeyError when a segment references
an effect ID that is missing from the truncated list (WLED issue #5674).

Fix this by always fetching the complete list from /json/effects on
initial load, which is a dedicated endpoint not affected by the buffer
limit. On subsequent updates, re-fetch only when fxcount changes or a
device restart is detected (boot_time shift > 2 s, derived as now -
uptime to avoid false positives from the ever-incrementing uptime counter).

Add tests covering: skip when unchanged, re-fetch on fxcount change,
re-fetch after device restart, and full list from /json/effects despite
truncated /json response.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 10, 2026 00:29
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0433613-dea2-423d-b3b3-7e0c54ca28fb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.33333% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.63%. Comparing base (3e87d76) to head (95c40fc).
⚠️ Report is 617 commits behind head on main.

Files with missing lines Patch % Lines
src/wled/wled.py 63.33% 7 Missing and 4 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2083       +/-   ##
===========================================
+ Coverage   58.61%   96.63%   +38.02%     
===========================================
  Files           6        8        +2     
  Lines         662     1159      +497     
  Branches      143      119       -24     
===========================================
+ Hits          388     1120      +732     
+ Misses        270       25      -245     
- Partials        4       14       +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mik-laj mik-laj added the bugfix Inconsistencies or issues which will cause a problem for users or implementers. label Jun 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds robust effects fetching to avoid ESP8266 /json truncation by sourcing the full effects list from /json/effects and only re-fetching when the effect count changes or a restart is detected (boot_time shift). This aligns the effects refresh logic with the existing presets “versioning” pattern to reduce unnecessary network calls while preventing KeyError from missing effect IDs.

Changes:

  • Introduce _EffectsVersion tracking and _check_effects_changed() to decide when /json/effects must be fetched.
  • Update WLED.update() to fetch effects from /json/effects on initial load and when changes/restart are detected.
  • Extend test helpers and add new tests covering effects refetch behavior and truncated /json responses.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/wled/wled.py Adds effects version tracking + conditional fetch from /json/effects during update().
tests/conftest.py Updates shared mocking helper to include /json/effects.
tests/test_wled.py Adds tests for effects refetch rules and ESP8266 truncation scenario.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wled/wled.py Outdated
Comment thread src/wled/wled.py
Comment thread tests/test_wled.py
Comment thread tests/conftest.py
- Fix WLEDEmptyResponseError messages constructed as 1-tuples due to a
  trailing comma; the exception now receives a plain string in all four
  call sites (listen() and update())
- Treat uptime=0 as a valid state in _check_effects_changed by using an
  explicit `is None` guard instead of `not uptime`; previously a device
  polled in its first second of uptime would never write _effects_version
  and would re-fetch /json/effects on every subsequent call
- Freeze time.time() in test_update_skips_effects_when_unchanged to
  prevent flakiness when CI is slow enough that the boot_time delta
  exceeds the 2 s tolerance between update() calls
- Update mock_json_and_presets docstring to reflect three endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread src/wled/wled.py
Comment thread tests/test_wled.py Outdated
When _check_effects_changed returns False (no re-fetch needed), the
effects array from /json was still passed into update_from_dict(), which
would silently overwrite the full list previously fetched from
/json/effects with the truncated ESP8266 payload on every subsequent
poll.

Fix: pop "effects" from the /json payload when not re-fetching, so
update_from_dict() leaves the cached effects untouched.

Extend test_update_uses_effects_endpoint_for_full_list with a second
update() where /json is still truncated and no /json/effects stub is
registered, asserting the full effects list survives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mik-laj mik-laj force-pushed the fix/effects-boot-time-comparison branch from 5b2b3fd to 3498651 Compare June 10, 2026 01:00
@mik-laj mik-laj requested a review from Copilot June 10, 2026 01:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread src/wled/wled.py
Comment thread tests/test_wled.py
Comment thread tests/test_wled.py
Treat fxcount=0 as a valid value rather than "missing": change
`not fxcount` to `fxcount is None` in _check_effects_changed, consistent
with the earlier uptime fix. A device reporting zero effects would
otherwise be forced into a safe-refetch loop on every poll.

Update the inline comment in test_update_skips_presets_when_unchanged
to reflect that the first update now fetches three endpoints
(/json, /json/effects, /presets.json) after mock_json_and_presets
was extended to also stub /json/effects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mik-laj

mik-laj commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/wled/wled.py`:
- Around line 330-336: The current logic treats a valid empty effects list as an
error because it uses a falsey check on the result of
self.request("/json/effects"); change the guard to only raise when the request
returned None (or an explicitly invalid response) instead of any falsey
value—e.g., replace the condition with an explicit check like "if effects is
None:" (or validate with isinstance(effects, list) and raise only if the result
is None or not a list) before assigning data["effects"] = effects; keep the
WLEDEmptyResponseError raised only for truly missing/invalid responses.

In `@tests/test_wled.py`:
- Around line 401-423: The test
test_update_refetches_effects_after_device_restart mutates info["fxcount"] which
lets the code detect an effect change by count rather than by restart detection;
revert that change so fxcount remains the same between wled_data and
restarted_data, add the new effect only to restarted_data["effects"], then
update assertions to verify the new effect appears by name/content (e.g. check
that "Post Restart Effect" is present in device2.effects or that a specific
effect name changed) instead of asserting effects length increased based on
fxcount; keep using the existing variables and calls (wled.update(), device1,
device2, initial_effect_count) but replace the count-based assertion with a
presence/name check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ca0b655-ee1a-4c86-b8bb-36a4fe7a7ebd

📥 Commits

Reviewing files that changed from the base of the PR and between 67b0142 and ecca50a.

📒 Files selected for processing (3)
  • src/wled/wled.py
  • tests/conftest.py
  • tests/test_wled.py

Comment thread src/wled/wled.py Outdated
Comment on lines +330 to +336
if not (effects := await self.request("/json/effects")):
msg = (
f"WLED device at {self.host} returned an empty API"
" response on effects update"
)
raise WLEDEmptyResponseError(msg)
data["effects"] = effects

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle empty effects lists without treating them as an error.

At Line 330, if not (effects := await self.request("/json/effects")): will raise on a valid empty list ([]). That makes fxcount=0 scenarios fail despite receiving a valid JSON response.

💡 Suggested fix
-        if changed_effects:
-            if not (effects := await self.request("/json/effects")):
+        if changed_effects:
+            effects = await self.request("/json/effects")
+            if effects is None:
                 msg = (
                     f"WLED device at {self.host} returned an empty API"
                     " response on effects update"
                 )
                 raise WLEDEmptyResponseError(msg)
             data["effects"] = effects
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wled/wled.py` around lines 330 - 336, The current logic treats a valid
empty effects list as an error because it uses a falsey check on the result of
self.request("/json/effects"); change the guard to only raise when the request
returned None (or an explicitly invalid response) instead of any falsey
value—e.g., replace the condition with an explicit check like "if effects is
None:" (or validate with isinstance(effects, list) and raise only if the result
is None or not a list) before assigning data["effects"] = effects; keep the
WLEDEmptyResponseError raised only for truly missing/invalid responses.

Comment thread tests/test_wled.py
Replace `not effects` with `not isinstance(effects, list)` so that a
valid empty effects list from /json/effects no longer raises
WLEDEmptyResponseError (an empty list is falsy but not an error).

In test_update_refetches_effects_after_device_restart, remove the
fxcount increment from restarted_data so the re-fetch is driven solely
by the boot_time shift (restart detection), not by a count change.
Replace the length assertion with a name-presence check so the test
validates the actual content rather than an artifact of fxcount.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 10, 2026 01:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread src/wled/wled.py
Comment thread src/wled/wled.py
The WLEDEmptyResponseError for /json/effects said "empty API response"
but the guard is isinstance(effects, list), which also fires for a
non-empty but wrong-shaped payload. Change to "invalid response" so
the message matches the actual check.

Device.effects is typed as dict with default_factory=dict and is always
initialised in from_dict/update_from_dict, so it can never be None.
Remove the dead `or self._device.effects is None` branch from
_check_effects_changed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread tests/test_wled.py Outdated
Comment thread tests/test_wled.py
Comment thread tests/conftest.py
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 10, 2026 20:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread tests/test_wled.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Inconsistencies or issues which will cause a problem for users or implementers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants