Skip to content

Conversation

@netmindz
Copy link
Collaborator

@netmindz netmindz commented Nov 22, 2025

Summary by CodeRabbit

  • New Features

    • One-time install/upgrade prompt with Report / Not Now / Never Ask and optional anonymous upgrade reporting.
    • New OTA update workflow with integrity checks and bootloader SHA‑256 support.
  • Improvements

    • Stronger HTTP caching with ETag/conditional responses and cache‑busting build timestamp.
    • Corrected PSRAM reporting and memory stats.
    • Enhanced device fingerprinting using ADC calibration data.
  • Chores

    • Build configuration and .gitignore updates.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 22, 2025

Walkthrough

Adds client-side version-check/reporting with an install/upgrade modal and persistence; injects a build-time WEB_BUILD_TIME macro; introduces ETag-based HTTP caching; adds a streamed OTA subsystem with bootloader SHA‑256 support and OTA validation; corrects PSRAM JSON key and extends ESP32 fingerprinting; exposes AnimArtrix build flags.

Changes

Cohort / File(s) Summary
Version Reporting & UI
wled00/data/index.js, wled00/data/simple.js
Adds one-time version check (versionCheckDone) invoked after /json/info parsing; fetches /edit?edit=/version-info.json; shows install/upgrade modal with actions (Yes / Not Now / Never Ask); reports upgrades to https://usage.wled.me/api/usage/upgrade; persists version-info.json via /upload. Adds checkVersionUpgrade(), showVersionUpgradePrompt(), reportUpgradeEvent(), updateVersionInfo(). Updates PSRAM display to use tpsram/psusedram.
Build-Time Macro Injection
tools/cdata.js
Adds generateBuildTime() and injects WEB_BUILD_TIME macro into generated C arrays header to embed UNIX build timestamp.
ETag-based Caching & Conditional Responses
wled00/wled_server.cpp
Adds ETag generation and If-None-Match handling: generateEtag(...), setStaticContentCacheHeaders(..., code, eTagSuffix), handleIfNoneMatchCacheHeader(...). Applies headers and conditional 304 responses across static/content routes; 404 handling updated to use headers.
OTA Subsystem & Bootloader SHA256
wled00/ota_update.cpp, wled00/ota_update.h
Implements OTA workflow with UpdateContext, streaming handlers and lifecycle APIs (initOTA, endOTA, setOTAReplied, getOTAResult, handleOTAData). Adds ESP32 bootloader SHA‑256 calculation/caching (getBootloaderSHA256Hex, calculate/invalidate helpers) and optional release metadata validation.
JSON / PSRAM / Info Fields
wled00/json.cpp
Adds bootloaderSHA256 to serialized /json/info (guarded by OTA flags) and corrects PSRAM JSON key from tpramtpsram.
Device Fingerprint / ADC Calibration
wled00/util.cpp
Extends ESP32 device fingerprint with ADC calibration data and curve coeffs; updates ESP‑IDF version check and integrates esp_adc_cal usage.
Build Config / Platform Flags
platformio.ini
Adds/propagates AnimArtrix build flags and -DUSERMOD_ARTIFX / related lib ignore/unflag changes across specified environments.
Miscellaneous
.gitignore
Adds _codeql_detected_source_root to ignore list.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Browser
    participant Device as WLED_Device
    participant Analytics as usage.wled.me

    Browser->>Device: GET /json/info (requestJson)
    Device-->>Browser: JSON info (currentVersion, hw fields)
    Browser->>Browser: checkVersionUpgrade(info) if not versionCheckDone
    Browser->>Device: GET /edit?edit=/version-info.json
    alt version-info exists
        Device-->>Browser: version-info.json (oldVersion, neverAsk)
    else 404 / missing
        Browser-->>Browser: treat as first-install (oldVersion = "")
    end
    Browser->>Browser: compare oldVersion vs currentVersion and neverAsk
    alt show prompt
        Browser->>User: showVersionUpgradePrompt(oldVersion,newVersion)
        User-->>Browser: selects action (Yes / Not Now / Never Ask)
        alt Yes (report)
            Browser->>Device: GET /json/info (fresh)
            Device-->>Browser: fresh info
            Browser->>Analytics: POST /api/usage/upgrade (UpgradeEventRequest)
            alt Analytics 200
                Analytics-->>Browser: OK
                Browser->>Device: POST /upload (persist version-info.json with newVersion)
            else Analytics error
                Analytics-->>Browser: error (do not update stored version)
            end
        else Never Ask
            Browser->>Device: POST /upload (persist newVersion + neverAsk=true)
        else Not Now
            Browser-->>Browser: dismiss
        end
    else no action
        Browser-->>Browser: no prompt
    end
    Browser->>Browser: set versionCheckDone = true
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing focused review:
    • ETag generation, If-None-Match comparison, and header semantics in wled00/wled_server.cpp.
    • OTA streaming lifecycle, concurrency, error paths, and reboot/cleanup in wled00/ota_update.*.
    • Bootloader SHA‑256 calculation correctness and cache invalidation on ESP32.
    • Client-side reporting payload composition, network error handling, and Never-Ask persistence in wled00/data/index.js.
    • Consistency of PSRAM key rename (tpsram) across serialization, UI, and uses.
    • PlatformIO AnimArtrix flag interactions with environments and lib ignore lists.

Poem

🐇 I hopped through bytes with timestamped cheer,

I nudged the board to whisper, "Is update near?"
A prompt, a report, a little upload dance,
ETAGs hum low while SHA256 takes a glance,
Carrots of code — hop, persist, advance.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.63% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Upgrade reporting' directly corresponds to the main feature added: a comprehensive client-side version/upgrade reporting system that detects version changes, prompts users, and reports upgrade events to a remote server.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@softhack007
Copy link
Collaborator

@netmindz you could also add these items

  • product - in our case its "MoonModules" - also supported by upstream, and meant for "branding"
    root[F("product")] = F(WLED_PRODUCT_NAME); //WLEDMM + Moustachauve/Wled-Native
  • flash - flash size in MB
    root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024;
  • tpram = total PSRAM (MM specific, but can be added to upstream easily)
    root[F("tpram")] = ESP.getPsramSize(); //WLEDMM

@softhack007
Copy link
Collaborator

softhack007 commented Nov 28, 2025

still missing:

  • backport of bootloaderSHA256
  • backport of upstream 75a7ed1 "fix stale UI after firmware update reporting"

@softhack007 softhack007 marked this pull request as ready for review November 28, 2025 22:04
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
wled00/data/index.js (3)

3732-3737: Consider notifying user of version check failures.

When the version check fails (line 3732), the error is only logged to console. While saving the current version for next time is good, users may benefit from knowing the check failed, especially if it's due to filesystem issues.

Consider adding a toast notification:

 	.catch(e => {
 		console.log('Failed to load version-info.json', e);
+		showToast('Unable to check for version updates', false);
 		// On error, save current version for next time
 		if (info && info.ver) {
 			updateVersionInfo(info.ver, false);
 		}
 	});

3744-3775: Improve accessibility and keyboard navigation.

The modal dialog lacks accessibility attributes and keyboard navigation support. Users relying on screen readers or keyboard navigation may have difficulty interacting with this prompt.

Add ARIA attributes and ESC key handler:

 	const overlay = d.createElement('div');
 	overlay.id = 'versionUpgradeOverlay';
+	overlay.setAttribute('role', 'dialog');
+	overlay.setAttribute('aria-modal', 'true');
+	overlay.setAttribute('aria-labelledby', 'versionUpgradeTitle');
 	overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;display:flex;align-items:center;justify-content:center;';
 	
 	const dialog = d.createElement('div');
 	dialog.style.cssText = 'background:var(--c-1);border-radius:10px;padding:25px;max-width:500px;margin:20px;box-shadow:0 4px 6px rgba(0,0,0,0.3);';
 	
+	// Handle ESC key
+	const closeOnEsc = (e) => {
+		if (e.key === 'Escape' && d.body.contains(overlay)) {
+			d.body.removeChild(overlay);
+			d.removeEventListener('keydown', closeOnEsc);
+		}
+	};
+	d.addEventListener('keydown', closeOnEsc);
+
 	// Build contextual message based on install vs upgrade
 	const title = isInstall 
 		? '🎉 Thank you for installing WLED-MM!' 
 		: '🎉 WLED-MM Upgrade Detected!';
 	
 	const description = isInstall
 		? `You are now running WLED-MM <strong>${newVersion}</strong>.`
 		: `Your WLED-MM has been upgraded from <strong>${oldVersion}</strong> to <strong>${newVersion}</strong>.`;
 	
 	const question = 'Would you like to help the WLED development team by reporting your installation? This helps us understand what hardware and versions are being used.'
 
 	dialog.innerHTML = `
-		<h2 style="margin-top:0;color:var(--c-f);">${title}</h2>
+		<h2 id="versionUpgradeTitle" style="margin-top:0;color:var(--c-f);">${title}</h2>
 		<p style="color:var(--c-f);">${description}</p>
 		<p style="color:var(--c-f);">${question}</p>

Also update event listeners to clean up the ESC handler:

 	gId('versionReportYes').addEventListener('click', () => {
 		reportUpgradeEvent(oldVersion, newVersion);
 		d.body.removeChild(overlay);
+		d.removeEventListener('keydown', closeOnEsc);
 	});

3864-3871: Silent failure when updating version info.

If the /upload request fails (lines 3869-3870), the error is only logged to console. Users won't know that their "Never Ask" preference failed to save, and they may be prompted again unexpectedly.

Consider showing a toast on failure:

 	.catch(e => {
 		console.log('Failed to update version-info.json', e);
+		showToast('Failed to save preferences', true);
 	});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9097dff and a967043.

📒 Files selected for processing (1)
  • wled00/data/index.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

wled00/data/**/*.{htm,html,css,js}: Use tabs for indentation in web UI files (.htm/.html/.css/.js) under wled00/data
After editing web UI files, run npm run build to regenerate embedded headers
Manually validate web UI changes via local HTTP server and browser

Files:

  • wled00/data/index.js
wled00/data/**/*.{htm,html,js,css}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Check the browser console for JavaScript errors after web UI changes

Files:

  • wled00/data/index.js
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/data/**/*.{htm,html,js,css} : Check the browser console for JavaScript errors after web UI changes

Applied to files:

  • wled00/data/index.js
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/data/**/*.{htm,html,css,js} : Manually validate web UI changes via local HTTP server and browser

Applied to files:

  • wled00/data/index.js
🔇 Additional comments (5)
wled00/data/index.js (5)

2232-2232: LGTM - Good integration point.

The placement of checkVersionUpgrade(i) after parseInfo(i) ensures device information is available for version checking and reporting.


3806-3822: Remove premature approval stamps — backend field validation required before approval.

The review comment contains and tags before verification is actually complete. The requested verification of whether product, flash, and tpram fields exist in the /json/info endpoint cannot be performed due to repository access constraints.

Blockers preventing verification:

  • Backend /json/info endpoint implementation cannot be accessed to confirm field availability
  • infoData.product, infoData.flash, and infoData.tpram existence in the response must be validated in C++ code before this JavaScript implementation can be approved
  • Coding guideline checks (tab indentation, npm build, browser console errors) cannot be verified

Before approval, verify:

  1. Backend: Confirm /json/info response includes product, flash, and tpram fields
  2. Frontend: Ensure wled00/data/index.js uses tabs (not spaces) for indentation
  3. Build: Run npm run build and confirm no browser console JavaScript errors
  4. Remove approval stamps until verification is complete

3699-3711: Based on my web search results, I can now evaluate the review comment:

The /edit?edit=version-info.json endpoint is WLED's documented standard pattern for file access

The web search confirms that:

  • /edit is WLED's file editor/manager endpoint for accessing device files (version-info.json, presets.json, cfg.json, etc.)
  • The query parameter ?edit=filename is the standard WLED way to specify which file to retrieve
  • This pattern is documented in WLED's mapping documentation and API reference

However, the search also reveals that WLED provides dedicated JSON API endpoints (/json/info, /json) that are arguably more appropriate for version checking, but the current approach using /edit?edit=version-info.json is still valid and documented.


The endpoint pattern is standard and correctly documented, not non-standard as stated in the review comment.

The /edit?edit=version-info.json endpoint is the documented WLED pattern for file access. The review comment's primary concern—that this is a non-standard endpoint—is incorrect. While WLED also offers dedicated JSON API endpoints like /json/info for version information, using /edit for this purpose is valid and follows WLED's documented file access patterns.


3856-3863: The review comment is incorrect. The FormData field name 'data' is correct for the WLED /upload endpoint.

Web search confirms that the WLED /upload endpoint expects the multipart form field to be named "data", not "upload". Official WLED documentation and community sources (WLED Discourse, Reddit) show the correct usage as:

curl -F "data=@file" http://WLED-IP/upload

The code at lines 3856-3863 correctly uses formData.append('data', blob, 'version-info.json'). The suggested change to 'upload' would break the upload functionality.

Likely an incorrect or invalid review comment.


3806-3822: Unable to confirm API requirement claim; code structure suggests field is optional.

Repository access failed and web searches found no published API schema specifying bootloaderSHA256 as a required field. However, the code at line 3814 uses an empty string fallback (infoData.bootloaderSHA256 || ''), which is the standard pattern for optional fields in JavaScript, not required ones. If the backend API strictly required this field, sending an empty string would fail validation—the fact that the code is written this way suggests the field is either:

  1. Optional in the backend API, or
  2. The backend hasn't yet enforced validation for it

The comment accurately states the field "is not yet availeable in WLEDMM," but the review's assertion that this "will cause API validation errors" cannot be verified without access to the actual API schema or server implementation.

Comment on lines +3825 to +3831
return fetch('https://usage.wled.me/api/usage/upgrade', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(upgradeData)
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add timeout and improve error handling for external API call.

The fetch request to https://usage.wled.me/api/usage/upgrade has no timeout and could hang indefinitely. Additionally, the hardcoded external URL may face CORS restrictions depending on the server configuration.

Add a timeout and better error context:

 		// Make AJAX call to postUpgradeEvent API
-		return fetch('https://usage.wled.me/api/usage/upgrade', {
+		const controller = new AbortController();
+		const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
+		
+		return fetch('https://usage.wled.me/api/usage/upgrade', {
 			method: 'POST',
 			headers: {
 				'Content-Type': 'application/json'
 			},
-			body: JSON.stringify(upgradeData)
+			body: JSON.stringify(upgradeData),
+			signal: controller.signal
+		}).finally(() => {
+			clearTimeout(timeoutId);
 		});
 	})
 	.then(res => {
 		if (res.ok) {
 			showToast('Thank you for reporting!');
 			updateVersionInfo(newVersion, false);
 		} else {
-			showToast('Report failed. Please try again later.', true);
+			showToast(`Report failed (${res.status}). Please try again later.`, true);
 			// Do NOT update version info on failure - user will be prompted again
 		}
 	})
 	.catch(e => {
-		console.log('Failed to report upgrade', e);
-		showToast('Report failed. Please try again later.', true);
+		const isTimeout = e.name === 'AbortError';
+		console.log('Failed to report upgrade', e);
+		showToast(isTimeout ? 'Report timed out. Please try again later.' : 'Report failed. Please try again later.', true);
 		// Do NOT update version info on error - user will be prompted again
 	});

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In wled00/data/index.js around lines 3825-3831, the fetch to
https://usage.wled.me/api/usage/upgrade has no timeout and lacks robust error
handling and CORS consideration; wrap the call in an async try/catch, use an
AbortController with a configurable timeout (clear the timeout on completion),
check response.ok and parse/attach response body or status to any thrown error,
and surface/log the enriched error context; additionally make the URL
configurable (env/config) or move the request to a server-side endpoint to avoid
client-side CORS issues.

@netmindz
Copy link
Collaborator Author

@netmindz you could also add these items

  • product - in our case its "MoonModules" - also supported by upstream, and meant for "branding"
    root[F("product")] = F(WLED_PRODUCT_NAME); //WLEDMM + Moustachauve/Wled-Native
  • flash - flash size in MB
    root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024;
  • tpram = total PSRAM (MM specific, but can be added to upstream easily)
    root[F("tpram")] = ESP.getPsramSize(); //WLEDMM

If you want to capture more, please open an issue against the wled_usage repo.

Am I not capturing the flash size already?

Add WEB_BUILD_TIME to html_ui.h and use it for ETag generation

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: DedeHai <[email protected]>
Co-authored-by: Aircoookie <[email protected]>
@softhack007
Copy link
Collaborator

If you want to capture more, please open an issue against the wled_usage repo.

OK

Am I not capturing the flash size already?

Yes you're taking the flash size, but not the filesystem size.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
tools/cdata.js (1)

116-155: Build-time WEB_BUILD_TIME macro injection looks good; optional: compute timestamp once per run

generateBuildTime() and the injected WEB_BUILD_TIME block correctly give the C++ side a stable UNIX timestamp for cache-busting and ETag generation, and the #ifdef/#undef guard prevents redefinition issues when multiple html headers are included. To guarantee all generated headers share an identical timestamp even if the script runs longer than a second, you could compute it once and reuse it:

-// Generate build timestamp as UNIX timestamp (seconds since epoch)
-function generateBuildTime() {
-  return Math.floor(Date.now() / 1000);
-}
+// Generate build timestamp as UNIX timestamp (seconds since epoch)
+const BUILD_TIME = Math.floor(Date.now() / 1000);
+function generateBuildTime() {
+  return BUILD_TIME;
+}
wled00/wled_server.cpp (2)

491-493: Call to setStaticContentCacheHeaders() on 404 is currently a no-op

Because setStaticContentCacheHeaders() immediately returns when code != 200, the call with 404 here has no effect and can be removed for clarity unless you plan to extend the helper to also handle non-200 responses:

     response->addHeader(FPSTR(s_content_enc),"gzip");
-    setStaticContentCacheHeaders(response, 404);
     request->send(response);

727-728: Settings pages now emit ETag; consider optional 304 handling for bandwidth savings

Settings HTML now gets Cache-Control and ETag via setStaticContentCacheHeaders(), but serveSettings() never calls handleIfNoneMatchCacheHeader(), so these responses will always be 200 with a full body even when the client sends a matching If-None-Match. If you ever want conditional GETs for these pages too, you could add an early:

if (handleIfNoneMatchCacheHeader(request)) return;

before constructing the AsyncWebServerResponse*, at least for the fully static subpages.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a967043 and 260f26d.

📒 Files selected for processing (2)
  • tools/cdata.js (2 hunks)
  • wled00/wled_server.cpp (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/wled_server.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/html_*.h : Do not edit generated embedded web header files: wled00/html_*.h

Applied to files:

  • tools/cdata.js
  • wled00/wled_server.cpp
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/data/**/*.{htm,html,css,js} : After editing web UI files, run npm run build to regenerate embedded headers

Applied to files:

  • tools/cdata.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (esp8266pro_16MB_M)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (esp32c3dev_2MB_M)
  • GitHub Check: Builds (esp8266_4MB_S)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32_16MB_V4_M_debug)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_4MB_M_eth)
  • GitHub Check: Builds (esp32_pico_4MB_V4_S)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32dev_compat)
🔇 Additional comments (2)
wled00/wled_server.cpp (2)

19-62: ETag generation and cache header helpers are coherent and safe

Using WEB_BUILD_TIME, cacheInvalidate, and an optional eTagSuffix to build the ETag string, and centralizing cache headers in setStaticContentCacheHeaders(), gives you a clear, predictable versioning story. The 200-only guard for ETag emission and the 304 handling in handleIfNoneMatchCacheHeader() are consistent and don’t introduce correctness issues.


512-524: Index route’s conditional GET handling with ETag looks correct

The //index path now cleanly short‑circuits on If-None-Match via handleIfNoneMatchCacheHeader(), and otherwise serves the gzipped PROGMEM page with Cache-Control and ETag set by setStaticContentCacheHeaders(). This matches the new caching model without altering existing control flow.

softhack007 and others added 6 commits November 29, 2025 00:48
…ting-0-15-x

Backport version reporting (PR wled#5093 and wled#5111) to 0.15.x
* reduce memory footprint by removing all unneeded functions in ota_update.cpp
* don't compile ota_update.cpp when WLED_DISABLE_OTA is defined
small update for code readability
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
wled00/data/index.js (1)

3795-3847: External upgrade-report POST still lacks timeout/error enrichment

The reportUpgradeEvent POST to https://usage.wled.me/api/usage/upgrade is still a bare fetch without timeout or richer error context:

return fetch('https://usage.wled.me/api/usage/upgrade', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(upgradeData)
});
...
.catch(e => {
  console.log('Failed to report upgrade', e);
  showToast('Report failed. Please try again later.', true);
});

As noted in a previous review, this can hang indefinitely on bad networks and gives users very little insight into what went wrong. A small wrapper with AbortController and differentiated messaging (timeout vs other errors, include status code on non‑OK) would make this much more robust, especially since it targets an external origin.

Given you’ve already seen this feedback and may have consciously deferred it, consider it a “when convenient” hardening task rather than a blocker.

What is the recommended way to implement a timeout around the Fetch API (e.g., using AbortController) in modern browsers?
🧹 Nitpick comments (5)
wled00/data/simple.js (1)

520-536: Remember to rebuild embedded assets and check browser console

Since this file lives under wled00/data/*.js, per project guidelines you should:

  • Run the web UI build (npm run build) so the embedded HTML/JS headers stay in sync.
  • Manually open the simple UI in a browser and confirm there are no new JS errors or layout issues in the console, especially around the updated PSRAM rows.
wled00/ota_update.h (1)

3-8: OTA header surface is fine; static helper declarations are internal-only

The header cleanly:

  • Avoids exposing the legacy Async OTA API by wrapping it in #if 0.
  • Limits the bootloader SHA helpers to #if defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DISABLE_OTA) so non-ESP32 builds aren’t affected.
  • Declares calculateBootloaderSHA256 and invalidateBootloaderSHA256Cache as static, which is appropriate if their definitions in ota_update.cpp also use static (internal linkage).

As long as the definitions in ota_update.cpp are also static, this won’t violate ODR and keeps the internal helpers out of the global symbol table. If they are currently non-static there, please make them static to match these declarations. The Clang “Arduino.h not found” error is a tooling configuration issue, not a code bug.

Also applies to: 58-76

wled00/data/index.js (1)

516-555: Apply UI build pipeline and console checks for the new version-reporting flow

Because index.js is part of the web UI bundle:

  • Re-run the UI build (npm run build) so any generated html_*.h assets pick up the new version-reporting logic.
  • Test the main UI in a browser against an ESP32 target with PSRAM and OTA enabled:
    • confirm /json/info contains bootloaderSHA256 and tpsram as expected,
    • exercise the “Yes / Not Now / Never Ask” paths,
    • and watch the browser console for CORS or JavaScript errors from checkVersionUpgrade, reportUpgradeEvent, and updateVersionInfo.

This will catch any subtle integration issues that static review can’t see.

Also applies to: 3690-3871

wled00/ota_update.cpp (2)

38-83: Note: Syntax error in disabled code block.

If this code is ever re-enabled, line 83 is missing a semicolon after the struct definition:

-}
+};

Not urgent since the block is currently disabled via #if 0.


326-329: Unused static function cannot be called externally.

invalidateBootloaderSHA256Cache() is declared static, preventing calls from other translation units. The comment suggests it should be called "after bootloader update," which would likely occur in other files.

Either:

  1. Remove static and declare the function in ota_update.h if external use is intended, or
  2. Remove the function if it's truly unnecessary (bootloader updates are rare and typically require a reboot anyway).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 260f26d and de9c6ae.

📒 Files selected for processing (8)
  • .gitignore (1 hunks)
  • platformio.ini (1 hunks)
  • wled00/data/index.js (3 hunks)
  • wled00/data/simple.js (1 hunks)
  • wled00/json.cpp (5 hunks)
  • wled00/ota_update.cpp (1 hunks)
  • wled00/ota_update.h (1 hunks)
  • wled00/util.cpp (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🧰 Additional context used
📓 Path-based instructions (3)
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

wled00/data/**/*.{htm,html,css,js}: Use tabs for indentation in web UI files (.htm/.html/.css/.js) under wled00/data
After editing web UI files, run npm run build to regenerate embedded headers
Manually validate web UI changes via local HTTP server and browser

Files:

  • wled00/data/simple.js
  • wled00/data/index.js
wled00/data/**/*.{htm,html,js,css}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Check the browser console for JavaScript errors after web UI changes

Files:

  • wled00/data/simple.js
  • wled00/data/index.js
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/json.cpp
  • wled00/util.cpp
  • wled00/ota_update.cpp
  • wled00/ota_update.h
🧠 Learnings (10)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
📚 Learning: 2025-10-22T21:50:25.839Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.

Applied to files:

  • wled00/data/simple.js
  • wled00/json.cpp
📚 Learning: 2025-11-02T22:07:15.689Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.

Applied to files:

  • wled00/data/simple.js
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/data/**/*.{htm,html,js,css} : Check the browser console for JavaScript errors after web UI changes

Applied to files:

  • wled00/data/index.js
📚 Learning: 2025-07-02T23:22:57.175Z
Learnt from: netmindz
Repo: MoonModules/WLED-MM PR: 248
File: platformio.ini:1613-1613
Timestamp: 2025-07-02T23:22:57.175Z
Learning: In WLED platformio.ini, the particle system disable flags (WLED_DISABLE_PARTICLESYSTEM1D and WLED_DISABLE_PARTICLESYSTEM2D) are intentionally varied across different build environments based on platform memory constraints. More memory-limited platforms (like ESP8266, ESP32-C3, ESP32-S2) disable both 1D and 2D particle systems, while platforms with more available memory (like esp32_4MB_V4_S with 4MB flash) may only disable the 1D version to preserve flash space while keeping 2D functionality available.

Applied to files:

  • platformio.ini
  • wled00/json.cpp
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/html_*.h : Do not edit generated embedded web header files: wled00/html_*.h

Applied to files:

  • wled00/json.cpp
📚 Learning: 2025-11-24T00:15:38.069Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-24T00:15:38.069Z
Learning: In MoonModules/WLED-MM, when reading ESP32/ESP32-S3 eFuse blocks using esp_efuse_read_field_blob(): EFUSE_BLK0 has only 7 × 32-bit registers (224 bits total), not 8 registers (256 bits). Reading more than 7 words from EFUSE_BLK0 triggers assertion failure in esp_efuse_utility_read_reg(). Use maximum 7 words when reading EFUSE_BLK0 to avoid crashes. This applies to all ESP32 variants (ESP32, ESP32-S2, ESP32-S3, ESP32-C3).

Applied to files:

  • wled00/json.cpp
📚 Learning: 2025-06-30T17:53:37.232Z
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 248
File: usermods/audioreactive/audio_reactive.h:199-200
Timestamp: 2025-06-30T17:53:37.232Z
Learning: In WLED AudioReactive usermod, when using ArduinoFFT (enabled via UM_AUDIOREACTIVE_USE_ARDUINO_FFT), it's acceptable and necessary to redefine the global sqrt macro to sqrtf within the conditional compilation block for performance optimization. This is a specific requirement for ArduinoFFT optimization that can provide 10-50% performance improvement on ESP32.

Applied to files:

  • wled00/json.cpp
📚 Learning: 2025-11-14T13:55:44.442Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-14T13:55:44.442Z
Learning: In MoonModules/WLED-MM, always flag Variable Length Arrays (VLA) during code reviews, especially when they could overflow available stack space (typically 2K-4K on ESP32/ESP8266). VLAs based on dynamic parameters like segment dimensions, particle counts, or pixel buffers should be highlighted and require justification from the author. Prefer fixed-size arrays, heap allocation, or bounded VLAs with explicit maximum size constraints.

Applied to files:

  • wled00/json.cpp
📚 Learning: 2025-07-12T04:20:14.546Z
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR #248 merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.

Applied to files:

  • wled00/util.cpp
🧬 Code graph analysis (2)
wled00/json.cpp (1)
wled00/ota_update.cpp (2)
  • getBootloaderSHA256Hex (321-324)
  • getBootloaderSHA256Hex (321-321)
wled00/ota_update.cpp (1)
wled00/wled.h (1)
  • instance (930-934)
🪛 Clang (14.0.6)
wled00/ota_update.h

[error] 3-3: 'Arduino.h' file not found

(clang-diagnostic-error)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (esp01_1MB_S)
  • GitHub Check: Builds (esp32_pico_4MB_V4_S)
  • GitHub Check: Builds (esp8266_4MB_S)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (esp8266pro_16MB_M)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp8266_2MB_S)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_M_debug)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (esp32_4MB_M_eth)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32_16MB_V4_S)
  • GitHub Check: Builds (esp32dev_compat)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
🔇 Additional comments (6)
wled00/data/simple.js (1)

525-533: PSRAM rows look consistent with new tpsram field

The changed PSRAM info rows correctly align with the new JSON field:

  • i.tpsram is now the total PSRAM in bytes; dividing by 1024 and showing kB is consistent with the json.cpp producer.
  • “Max Used PSRAM” uses i.tpsram - i.psusedram and switches to kB when above 16 kB, which matches the previous behavior while fixing the key name.

Given json.cpp only sets psram, psusedram, and tpsram together under BOARD_HAS_PSRAM, the guard on i.psusedram is sufficient.

wled00/json.cpp (2)

1-3: Bootloader SHA256 reporting is wired correctly and gated by OTA/ESP32

Including ota_update.h at the top and exposing bootloaderSHA256 only under:

#if defined(ARDUINO_ARCH_ESP32)
  ...
  #ifndef WLED_DISABLE_OTA
    root[F("bootloaderSHA256")] = getBootloaderSHA256Hex();
  #endif
#else
  // ESP8266...
#endif

is a clean way to surface the digest to /json/info without impacting ESP8266 or OTA-disabled builds. The header itself also gates the declaration of getBootloaderSHA256Hex() on ARDUINO_ARCH_ESP32 && !WLED_DISABLE_OTA, so the symbol is only required where it exists.

No issues here from a correctness or linkage perspective.

Also applies to: 1085-1090


1109-1127: tpsram JSON key matches the UI expectations

The PSRAM reporting block:

#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
  if (psramFound()) {
    root[F("tpsram")]   = ESP.getPsramSize();
    root[F("psram")]    = ESP.getFreePsram();
    root[F("psusedram")] = ESP.getMinFreePsram();
    ...
  }
#else
  // commented-out test values
#endif

uses tpsram as “total PSRAM in bytes”, with psram and psusedram as current and min free. That lines up with the consumers in both simple.js and index.js which expect i.tpsram to be the total and derive used values from it.

This looks consistent and backward compatible (all three keys remain under the same psramFound() guard).

wled00/util.cpp (1)

10-10: Based on my verification through documentation searches, I've found critical information about the ESP-IDF ADC calibration API. Let me provide the rewritten review comment:


Array bounds concern is incorrect; return value handling remains a valid consideration

The ADC calibration data concern needs correction:

  • The low_curve and high_curve pointers are documented 20-element lookup tables (LUT_POINTS = 20), not unspecified length. Reading 8 entries unconditionally is safe and poses no out-of-bounds risk. This information is available in esp_adc_cal_legacy.c and the ESP-IDF documentation.

  • The second concern about unchecked esp_adc_cal_characterize() return value remains valid from an API robustness perspective. The function can return ESP_ADC_CAL_VAL_NOT_SUPPORTED, ESP_ADC_CAL_VAL_DEFAULT_VREF, ESP_ADC_CAL_VAL_EFUSE_VREF, or ESP_ADC_CAL_VAL_EFUSE_TP. Per ESP-IDF guidance, the return value should be checked to determine whether the calibration succeeded and which data (coeff_a/b vs. curves) is valid. Ignoring it and unconditionally trusting low_curve/high_curve when NOT_SUPPORTED or DEFAULT_VREF is returned could use unpopulated or invalid data.

Suggested action:
Check the return value of esp_adc_cal_characterize() and only use low_curve/high_curve when the calibration mode indicates they are available (e.g., ESP_ADC_CAL_VAL_EFUSE_VREF or ESP_ADC_CAL_VAL_EFUSE_TP). For unsupported or default-vref cases, skip the curve mixing or fall back to coeff-only mixing.

Likely an incorrect or invalid review comment.

wled00/ota_update.cpp (2)

1-36: Well-structured platform configuration.

The bootloader offset constants are appropriately documented with the upstream reference, and the multi-platform support covers the relevant ESP32 variants thoroughly.


308-318: LGTM!

SHA256 finalization and hex conversion are implemented correctly. The explicit null termination on line 316 is redundant but harmless.

Comment on lines 2229 to 2235
if (json.info) {
let i = json.info;
parseInfo(i);
checkVersionUpgrade(i); // Check for version upgrade
populatePalettes(i);
if (isInfo) populateInfo(i);
}
Copy link

@coderabbitai coderabbitai bot Nov 29, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

version-info.json is likely never saved (/upload field name mismatch, no loc support)

The version-reporting flow is well thought out overall, but there are two concrete issues that will likely break persistence and file-mode behavior:

  1. Wrong form field name for /upload

Existing code uses uploadFileWithText() to POST files to /upload:

function uploadFileWithText(name, text) {
  ...
  var formData = new FormData();
  var blob = new Blob([text], {type : 'application/text'});
  var fileOfBlob = new File([blob], name);
  formData.append("upload", fileOfBlob);
  req.send(formData);
}

The new updateVersionInfo() instead does:

const blob = new Blob([JSON.stringify(versionInfo)], { type: 'application/json' });
const formData = new FormData();
formData.append('data', blob, 'version-info.json');

fetch('/upload', {
  method: 'POST',
  body: formData
})

The server-side upload handler in WLED expects the field name "upload" (as evidenced by uploadFileWithText); sending "data" means the firmware will not recognize or store the file. As a result:

  • version-info.json will never be created/updated.
  • checkVersionUpgrade() will keep treating this as a first install or as a version mismatch, so users will be re-prompted indefinitely.

You should either:

  • Reuse the existing helper:
-function updateVersionInfo(version, neverAsk) {
-  const versionInfo = { version, neverAsk };
-  // Create a Blob with JSON content and use /upload endpoint
-  const blob = new Blob([JSON.stringify(versionInfo)], { type: 'application/json' });
-  const formData = new FormData();
-  formData.append('data', blob, 'version-info.json');
-
-  fetch('/upload', { method: 'POST', body: formData });
-}
+function updateVersionInfo(version, neverAsk) {
+  const versionInfo = { version, neverAsk };
+  uploadFileWithText('/version-info.json', JSON.stringify(versionInfo));
+}
  • Or at least change the field name to "upload" and ensure the filename path matches what /edit?edit=version-info.json expects.
  1. Hard‑coded device-local URLs ignore loc / locip

Other fetches in this file consistently use (loc ? \http://${locip}` : '')as a prefix so the UI works both when served by the device and when opened fromfile:`. The new logic uses bare paths:

  • fetch('/edit?edit=version-info.json', ...)
  • fetch('/json/info', ...)
  • fetch('/upload', ...)

In file‑mode these will target the local filesystem origin instead of the WLED device, so:

  • checkVersionUpgrade() will fail its fetch, fall into the catch, and attempt to POST /upload to the wrong origin.
  • reportUpgradeEvent() will fail to fetch /json/info at all.

If you care about file‑mode parity, you should follow the same pattern used elsewhere:

-function checkVersionUpgrade(info) {
+function checkVersionUpgrade(info) {
+  const base = loc ? `http://${locip}` : '';
   ...
-  fetch('/edit?edit=version-info.json', { method: 'get' })
+  fetch(base + '/edit?edit=version-info.json', { method: 'get' })
   ...

and similarly for /json/info and /upload.

Once those two points are fixed, the flow (single per-load check, honor neverAsk, prompt on real version changes, and build an upgradeData payload from /json/info) looks structurally sound.

Also applies to: 3690-3871

Copy link
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai i have tested the code (latest commit of this PR) on several boards, and version-info.json is properly created on the device, and it is also loaded and recognized by the JS function.

please re-check your analysis, I think you made some wrong assumptions.

Copy link

Choose a reason for hiding this comment

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

@softhack007, thank you for testing and providing that feedback! I apologize for the incorrect analysis. If version-info.json is being properly created and recognized on the device, then my assumptions about the /upload endpoint were clearly wrong.

I'm curious about one thing for my own learning: I noticed that the new updateVersionInfo() function uses 'data' as the FormData field name:

formData.append('data', blob, 'version-info.json');

While the existing uploadFileWithText() function uses 'upload':

formData.append("upload", fileOfBlob);

Does the WLED firmware /upload endpoint accept both field names, or does it perhaps just look at the filename regardless of the field name? Understanding this would help me avoid similar misassumptions in the future.

In any case, I appreciate you taking the time to test this thoroughly—empirical evidence always beats theoretical analysis!


🧠 Learnings used
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.

@softhack007
Copy link
Collaborator

@netmindz I have tested the upgrade reporting procedure in several boards, it I think its working now. I don't know much about JS - maybe you want to check the recommendations made by the bunny?

Just give me your OK and i'll merge the PR to mdev.

* ``-DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE`` caused a crash when mounting the filesystem
* added "extends = " to MM legacy buildenvs
* make sure that flash_mode is always well-defined
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
wled00/wled_server.cpp (1)

492-492: No-op call to setStaticContentCacheHeaders with 404.

This call passes 404 as the status code, but setStaticContentCacheHeaders returns early for any non-200 status (line 33). This makes the call effectively a no-op.

This is addressed by the earlier suggestion to revise the setStaticContentCacheHeaders API design.

🧹 Nitpick comments (2)
wled00/wled_server.cpp (2)

19-21: Unused constant s_expires.

The s_expires PROGMEM string is defined but never referenced in this file. Line 623 uses F("Expires") directly instead of FPSTR(s_expires).

Apply this diff to use the PROGMEM constant or remove it:

 response->addHeader(F("Cache-Control"),"no-store");
-response->addHeader(F("Expires"),"0");
+response->addHeader(FPSTR(s_expires),"0");

Or remove the unused constant if it's not needed elsewhere.


31-46: Consider simplifying the API or handling non-200 codes.

The setStaticContentCacheHeaders function accepts a code parameter but only processes code == 200 (line 33). The call at line 492 passes 404, which results in an early return and no headers being set.

Consider either:

  1. Removing the code parameter and only calling this function for 200 responses.
  2. Setting appropriate cache headers for other status codes (e.g., 404 responses could be cached).

The current design is functional but may confuse future maintainers who expect the function to handle multiple status codes based on its signature.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a4e7a6 and 520bc8f.

📒 Files selected for processing (3)
  • platformio.ini (2 hunks)
  • wled00/json.cpp (5 hunks)
  • wled00/wled_server.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • wled00/json.cpp
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/wled_server.cpp
🧠 Learnings (5)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/html_*.h : Do not edit generated embedded web header files: wled00/html_*.h

Applied to files:

  • wled00/wled_server.cpp
📚 Learning: 2025-07-02T23:22:57.175Z
Learnt from: netmindz
Repo: MoonModules/WLED-MM PR: 248
File: platformio.ini:1613-1613
Timestamp: 2025-07-02T23:22:57.175Z
Learning: In WLED platformio.ini, the particle system disable flags (WLED_DISABLE_PARTICLESYSTEM1D and WLED_DISABLE_PARTICLESYSTEM2D) are intentionally varied across different build environments based on platform memory constraints. More memory-limited platforms (like ESP8266, ESP32-C3, ESP32-S2) disable both 1D and 2D particle systems, while platforms with more available memory (like esp32_4MB_V4_S with 4MB flash) may only disable the 1D version to preserve flash space while keeping 2D functionality available.

Applied to files:

  • platformio.ini
📚 Learning: 2025-11-14T13:55:44.442Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-14T13:55:44.442Z
Learning: In MoonModules/WLED-MM, always flag Variable Length Arrays (VLA) during code reviews, especially when they could overflow available stack space (typically 2K-4K on ESP32/ESP8266). VLAs based on dynamic parameters like segment dimensions, particle counts, or pixel buffers should be highlighted and require justification from the author. Prefer fixed-size arrays, heap allocation, or bounded VLAs with explicit maximum size constraints.

Applied to files:

  • platformio.ini
📚 Learning: 2025-06-30T17:53:37.232Z
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 248
File: usermods/audioreactive/audio_reactive.h:199-200
Timestamp: 2025-06-30T17:53:37.232Z
Learning: In WLED AudioReactive usermod, when using ArduinoFFT (enabled via UM_AUDIOREACTIVE_USE_ARDUINO_FFT), it's acceptable and necessary to redefine the global sqrt macro to sqrtf within the conditional compilation block for performance optimization. This is a specific requirement for ArduinoFFT optimization that can provide 10-50% performance improvement on ESP32.

Applied to files:

  • platformio.ini
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (esp8266_4MB_S)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp8266_4MB_M)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp8266_2MB_S)
  • GitHub Check: Builds (seeed_esp32c3_4MB_S)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32_4MB_M_eth)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
  • GitHub Check: Builds (esp32_16MB_V4_S)
  • GitHub Check: Builds (esp32_pico_4MB_V4_S)
  • GitHub Check: Builds (esp32dev_compat)
  • GitHub Check: Builds (esp32_16MB_V4_M_debug)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
🔇 Additional comments (4)
platformio.ini (2)

1267-1271: Variable substitution syntax is now correct.

The previous syntax error using braces {} instead of ${} has been successfully remediated. AnimArtrix build flags and lib_ignore entries are now properly expanded via PlatformIO variable substitution. The logic of disabling AnimArtrix in 4MB environments due to flash constraints is sound.


2885-2886: Consistent AnimArtrix flag removal for memory-constrained environments.

The athom_music environment correctly removes AnimArtrix build flags using the same proper ${} substitution syntax as the 4MB base environments. Comment clarity indicates deliberate flash-saving optimization.

wled00/wled_server.cpp (2)

48-62: LGTM! Correct HTTP caching implementation.

The conditional request handling correctly implements RFC 7232:

  • Compares If-None-Match header with the generated ETag
  • Returns 304 Not Modified with appropriate headers when the ETag matches
  • Returns false to allow normal response flow when there's no match

27-29: The WEB_BUILD_TIME macro does not exist in the WLED or WLED-MM repositories.

After comprehensive searching:

  • GitHub search of MoonModules/WLED-MM: no matches for WEB_BUILD_TIME
  • GitHub search of upstream wled/WLED: no matches for WEB_BUILD_TIME
  • Web search of WLED documentation and codebase: no references found

The review comment requests verification of a macro that does not appear to be defined anywhere in the project. If the code shown actually uses WEB_BUILD_TIME without it being defined, compilation would fail—but since this macro is not found in either repository, the review comment's premise appears to be incorrect.

Likely an incorrect or invalid review comment.

small flash optimization by re-using string constants (string merging)
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
wled00/wled_server.cpp (3)

27-46: ETag generation and conditional response helpers look correct

  • generateEtag()’s format ("%u-%02x-%04x") and a 32‑byte buffer are comfortably sized for WEB_BUILD_TIME, cacheInvalidate, and a 16‑bit suffix, so no overflow risk with the current format.
  • handleIfNoneMatchCacheHeader() correctly:
    • only short-circuits for “would‑be 200” responses,
    • compares against the same ETag string you later emit in setStaticContentCacheHeaders(), and
    • returns a 304 with matching cache headers when appropriate.
  • Passing code=200 into setStaticContentCacheHeaders() even when creating a 304 response is a reasonable way to express “ETag and cache policy for a resource whose normal status is 200”.

If you ever extend the ETag format, consider snprintf_P instead of sprintf_P for extra future‑proofing, but with the current format this is not a blocker.

Also applies to: 48-62


485-493: setStaticContentCacheHeaders(response, 404) currently no-ops

setStaticContentCacheHeaders() early-returns for any code != 200, so the new call with 404 on the 404 page does not add either Cache-Control or ETag and is effectively a no-op.

If the intent is:

  • no ETag but still cache policy for 404 pages, consider either:
    • calling setStaticContentCacheHeaders(response) with the default code=200, or
    • adding a dedicated helper for non‑200 error pages that only sets the desired Cache-Control.
      If you deliberately want default caching semantics for 404, the helper call can simply be removed for clarity.

697-729: Reusing static cache headers for settings pages—consider stricter policy for sensitive subpages

With setStaticContentCacheHeaders(response) now applied to all settings HTML/CSS (including security-related subpages), those pages are served with Cache-Control: no-cache (in non‑debug builds). That allows browsers to store them on disk but forces revalidation.

For “normal” settings/welcome pages this is fine, but for pages that surface secrets (WiFi credentials, security PIN, etc.) you might prefer they are not stored at all. Two possible tweaks:

  • Skip setStaticContentCacheHeaders() for security‑sensitive subpages and add Cache-Control: no-store explicitly, or
  • Extend the helper (e.g., an extra flag or different code/mode) so those pages can opt into no-store while the rest stay on no-cache.

Not a correctness issue, but worth considering from a privacy standpoint.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 520bc8f and 47a1b62.

📒 Files selected for processing (1)
  • wled00/wled_server.cpp (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/wled_server.cpp
🧠 Learnings (2)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
📚 Learning: 2025-10-12T21:55:59.766Z
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-12T21:55:59.766Z
Learning: Applies to wled00/html_*.h : Do not edit generated embedded web header files: wled00/html_*.h

Applied to files:

  • wled00/wled_server.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (esp01_1MB_S)
  • GitHub Check: Builds (esp8266_4MB_M)
  • GitHub Check: Builds (esp8266pro_16MB_M)
  • GitHub Check: Builds (esp8266_2MB_S)
  • GitHub Check: Builds (esp32c3dev_2MB_M)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (seeed_esp32c3_4MB_S)
  • GitHub Check: Builds (esp32c3mini_dio_4MB_M)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32S3_8MB_S)
🔇 Additional comments (2)
wled00/wled_server.cpp (2)

19-22: Flash string for Cache-Control header is fine

Defining s_cache_control in PROGMEM matches the pattern of other header-name constants and is a small but good flash-usage optimisation. The commented-out s_no_store / s_expires can remain as-is or be dropped later; no functional impact either way.


620-623: Explicit no-store for serveSettingsJS is appropriate

Using:

response->addHeader(FPSTR(s_cache_control), F("no-store"));
response->addHeader(F("Expires"), "0");

on the generated settings JS is a good choice: it avoids the browser caching user/device-specific configuration script while still keeping the logic simple.

@softhack007 softhack007 merged commit 496daee into MoonModules:mdev Nov 30, 2025
32 checks passed
@netmindz netmindz deleted the add-report-version-feature-MM branch December 8, 2025 14:16
@softhack007 softhack007 added this to the 14.7.1 milestone Dec 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants