Skip to content

Conversation

PagedPenguin
Copy link

@PagedPenguin PagedPenguin commented Jul 25, 2025

I'm trying to make it easy to add lots more scheduled presets, and I'm burnt out after coding for 15 hours, so I don't feel like writing a detailed pr description.

Summary by CodeRabbit

  • New Features

    • Increased maximum time-controlled presets to 32 with dynamic add/remove controls in the settings UI.
    • Added a dedicated Sunrise/Sunset presets section with offset and weekday controls.
    • Introduced support for separate sunrise and sunset timers with customizable offsets.
  • Improvements

    • Replaced fixed-size timer arrays with a dynamic, thread-safe timer system for better scalability.
    • Enhanced timer serialization and deserialization with clearer encoding and separate handling of sunrise/sunset timers.
    • Added comprehensive validation of timer parameters and expanded scheduling options including date ranges.
    • Updated timer checking logic for improved reliability and thread safety.
    • Expanded internal timer storage arrays to support additional timers and special types.
  • Bug Fixes

    • Removed hardcoded limits and embedded sunrise/sunset presets from the UI, resolving related issues.
    • Fixed weekday bitmask handling and enabled state encoding for all timers including special sunrise/sunset types.

Copy link
Contributor

coderabbitai bot commented Jul 25, 2025

## Walkthrough

The changes migrate the timer system from fixed-size arrays to a dynamic vector-based approach throughout the codebase. Timer management, serialization, deserialization, and UI handling are refactored to support up to 32 timers plus separate sunrise/sunset timers, with validation and more flexible data structures. The user interface for timer presets is now dynamic, allowing users to add or remove presets interactively and manage sunrise/sunset presets separately.

## Changes

| File(s)                        | Change Summary                                                                                                                                                                                                                 |
|-------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| wled00/cfg.cpp                | Refactored timer serialization/deserialization to use a vector-based system; replaced fixed arrays, clarified enabled state encoding, and improved date range handling; separated sunrise/sunset timers.                          |
| wled00/data/settings_time.htm | Converted time presets UI from a static table to a dynamic, user-expandable list supporting up to 32 presets; added separate Sunrise/Sunset presets section with dedicated controls and processing logic.                      |
| wled00/fcn_declare.h          | Added timer management function declarations, constants, a new Timer struct with helper methods, and an external vector of Timer objects; increased max presets to 32; defined special sunrise/sunset constants.                 |
| wled00/ntp.cpp                | Replaced legacy timer arrays with a vector; added thread-safe timer management functions with validation; synchronized vector to legacy arrays; updated timer checking logic to use vector and support sunrise/sunset timers. |
| wled00/set.cpp                | Refactored timer handling in settings to clear and add timers via new functions; added validation; supported separate sunrise/sunset timers with offsets; expanded timer indexing beyond 10 fixed timers.                      |
| wled00/wled.h                 | Expanded global timer arrays from 10 to 34 elements to accommodate 32 regular timers plus sunrise/sunset timers; updated initialization accordingly.                                                                           |

## Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

- Complexity: The changes span core logic, UI, and configuration handling, introducing a new data structure and validation logic, and updating multiple interconnected modules. The review requires careful attention to data migration, backward compatibility, and UI/logic correctness.

## Suggested reviewers

- DedeHai

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32a2d67 and 81848f7.

📒 Files selected for processing (1)
  • wled00/set.cpp (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • wled00/set.cpp
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@DedeHai
Copy link
Collaborator

DedeHai commented Jul 25, 2025

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jul 25, 2025

✅ Actions performed

Review triggered.

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.

1 similar comment
Copy link
Contributor

coderabbitai bot commented Jul 25, 2025

✅ Actions performed

Review triggered.

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.

Copy link
Contributor

@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: 6

🧹 Nitpick comments (4)
wled00/cfg.cpp (1)

708-711: Consider simplifying the enabled bit logic.

The current implementation shifts weekdays and then conditionally sets the enabled bit. This could be simplified and made more readable:

-    uint8_t weekdays = timer[F("dow")] | 0;
-    weekdays <<= 1; // shift to make room for enabled bit
-    bool enabled = timer["en"] | false;
-    if (enabled) weekdays |= 0x01; // set enabled bit
+    uint8_t weekdays = (timer[F("dow")] | 0) << 1; // shift weekdays to upper 7 bits
+    if (timer["en"] | false) weekdays |= 0x01; // set enabled bit if timer is enabled
wled00/data/settings_time.htm (1)

241-242: Use more specific button IDs to avoid conflicts

The button IDs "+" and "-" are too generic and might conflict with other page elements.

-				<button type="button" id="+" onclick="addTimePreset()">+</button>
-				<button type="button" id="-" onclick="removeLastPreset()">-</button>
+				<button type="button" id="btnAddTimer" onclick="addTimePreset()">+</button>
+				<button type="button" id="btnRemoveTimer" onclick="removeLastPreset()">-</button>

Also update the references in updateButtonStates():

 function updateButtonStates() {
-	gId("+").style.display = (currentPresetCount<maxTimePresets) ? "inline":"none";
-	gId("-").style.display = (currentPresetCount>1) ? "inline":"none";
+	gId("btnAddTimer").style.display = (currentPresetCount<maxTimePresets) ? "inline":"none";
+	gId("btnRemoveTimer").style.display = (currentPresetCount>1) ? "inline":"none";
 }
wled00/ntp.cpp (2)

414-420: Consider rejecting invalid values instead of silently constraining

The validation silently constrains invalid month/day values instead of rejecting them, which could hide user input errors.

-  // Validate month range
-  monthStart = constrain(monthStart, 1, 12);
-  monthEnd = constrain(monthEnd, 1, 12);
-  
-  // Validate day range
-  dayStart = constrain(dayStart, 1, 31);
-  dayEnd = constrain(dayEnd, 1, 31);
+  // Validate month range
+  if (monthStart < 1 || monthStart > 12 || monthEnd < 1 || monthEnd > 12) {
+    DEBUG_PRINTLN(F("Error: Invalid month range"));
+    return;
+  }
+  
+  // Validate day range
+  if (dayStart < 1 || dayStart > 31 || dayEnd < 1 || dayEnd > 31) {
+    DEBUG_PRINTLN(F("Error: Invalid day range"));
+    return;
+  }

489-489: Document the date range encoding format

The encoding of month start/end into a single byte is not documented, which could confuse future maintainers.

Add a comment explaining the encoding:

         // Date range info (only for regular timers)
+        // Encode monthStart in upper 4 bits, monthEnd in lower 4 bits
         timerMonth[regularTimerCount] = (timer.monthStart << 4) | (timer.monthEnd & 0x0F);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab5b6f9 and bc19e3a.

📒 Files selected for processing (5)
  • wled00/cfg.cpp (2 hunks)
  • wled00/data/settings_time.htm (3 hunks)
  • wled00/fcn_declare.h (1 hunks)
  • wled00/ntp.cpp (3 hunks)
  • wled00/set.cpp (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
wled00/cfg.cpp (1)
wled00/ntp.cpp (4)
  • clearTimers (429-432)
  • clearTimers (429-429)
  • addTimer (383-426)
  • addTimer (383-385)
⏰ 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). (2)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (usermods)
🔇 Additional comments (8)
wled00/set.cpp (1)

554-591: Well-implemented timer validation and migration.

The validation logic properly handles:

  • Different constraints for regular vs sunrise/sunset timers
  • Minute field as offset for sunrise/sunset (-59 to 59)
  • Weekday bits limited to 7 bits
  • Date ranges only for regular timers

The use of constrain() for safe value clamping and the extended ASCII indexing (0-9, A-F) for supporting 16 timers are good design choices.

wled00/data/settings_time.htm (4)

31-44: Well-structured dynamic table generation

The refactored BTa() function properly implements dynamic timer generation with good separation of concerns. The minimum timer count of 1 ensures the UI is always usable.


76-86: Robust implementation of timer addition

The addTimePreset() function correctly enforces the maximum timer limit and properly updates the DOM and UI state.


88-102: Correct handling of timer removal

The function properly maintains the minimum timer count and correctly removes both the main and detail rows for each timer.


109-118: Successful adaptation to dynamic timer count

The Wd() function correctly uses currentPresetCount for iteration and properly constructs the weekday bitmask for each timer.

wled00/ntp.cpp (3)

547-587: Well-refactored timer checking logic

The modified checkTimers() function correctly handles all timer types with proper validation and debugging. The use of Timer methods provides clean abstraction.


510-534: Clean getter function implementations

The timer count functions are well-implemented and appropriate for the expected small dataset size.


387-390: No undefined constant – maxTimePresets is declared in wled00/fcn_declare.h
The reference to maxTimePresets is valid; it’s defined as

// wled00/fcn_declare.h
const uint8_t maxTimePresets = 16;

Please disregard the “undefined constant” concern. The remaining validation suggestions still apply:

  • Sunrise/sunset minute validation
    Allows values from –59 to 59 but does not check against actual sunrise/sunset times. Consider stricter bounds or explicit rejection of unrealistic values.
  • Month/day validation
    Uses constrain(), which silently corrects out-of-range inputs. To catch invalid parameters, consider rejecting values outside acceptable ranges instead of auto-clamping.

Likely an incorrect or invalid review comment.

@PagedPenguin
Copy link
Author

Now that I’ve slept, I can write more info.
I added scalability to the current timed presets and started integrating it into the UI. After struggling for 7 hours to get it to do what I want, it’s mostly functional—except it doesn’t populate the saved data correctly and I......just........ squeak of frustration can’t... it’s being so difficult >w<

@PagedPenguin
Copy link
Author

I fixed everything i could think of, but still cant get the UI to populate correctly

@PagedPenguin
Copy link
Author

PagedPenguin commented Jul 26, 2025

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jul 26, 2025

✅ Actions performed

Review triggered.

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.

Copy link
Contributor

@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: 1

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

640-641: Same preset validation issue as sunrise timer.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc19e3a and 63b4e72.

📒 Files selected for processing (6)
  • wled00/cfg.cpp (2 hunks)
  • wled00/data/settings_time.htm (4 hunks)
  • wled00/fcn_declare.h (1 hunks)
  • wled00/ntp.cpp (3 hunks)
  • wled00/set.cpp (1 hunks)
  • wled00/wled.h (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • wled00/wled.h
🚧 Files skipped from review as they are similar to previous changes (2)
  • wled00/fcn_declare.h
  • wled00/cfg.cpp
🧰 Additional context used
🧠 Learnings (2)
wled00/ntp.cpp (4)

Learnt from: blazoncek
PR: #4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize)) is unnecessary and could be problematic, as it bypasses proper size verification.

Learnt from: blazoncek
PR: #4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.199Z
Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.

Learnt from: blazoncek
PR: #4482
File: wled00/udp.cpp:147-149
Timestamp: 2025-02-19T12:43:34.200Z
Learning: In WLED, maximum segment name length varies by platform:

  • ESP8266: 32 characters (WLED_MAX_SEGNAME_LEN = 32)
  • ESP32: 64 characters (WLED_MAX_SEGNAME_LEN = 64)
    This platform difference can cause truncation when syncing longer names from ESP32 to ESP8266. Additionally, the WLED UI has limitations regarding modified maximum segment name lengths.

Learnt from: KrX3D
PR: #4664
File: wled00/syslog.cpp:50-51
Timestamp: 2025-04-26T12:51:04.993Z
Learning: In the WLED codebase, syslog configuration variables (syslogHost, syslogPort, syslogProtocol, syslogFacility, syslogSeverity) are defined as global variables in wled.h and are accessed directly in the SyslogPrinter implementation, rather than being class members.

wled00/set.cpp (3)

Learnt from: blazoncek
PR: #4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize)) is unnecessary and could be problematic, as it bypasses proper size verification.

Learnt from: KrX3D
PR: #4237
File: usermods/INA219_v2/INA219_v2.cpp:265-276
Timestamp: 2025-04-18T22:27:58.634Z
Learning: When implementing MQTT message handling in WLED usermods, use strstr(topic, "/specific/path") instead of strcmp_P(topic, PSTR("/specific/path")) to properly match topics that include the device prefix. The full MQTT topic typically follows the pattern <mqttDeviceTopic>/specific/path.

Learnt from: blazoncek
PR: #4482
File: wled00/udp.cpp:343-347
Timestamp: 2025-02-19T12:41:22.676Z
Learning: The setName() method in WLED's Segment class (FX_fcn.cpp) handles all necessary validation internally, including:

  1. Null pointer checks
  2. Length validation against WLED_MAX_SEGNAME_LEN
  3. Memory allocation/reallocation
    No additional validation or name clearing is needed before calling setName().
🔇 Additional comments (11)
wled00/set.cpp (2)

590-591: Verify the logic for adding timers with preset 0.

The condition allows adding a timer when preset == 0 if the enabled bit is set. This might lead to unexpected behavior if preset 0 has special meaning (e.g., default action or disabled state).

Consider whether this is intentional or if the condition should be:

-if (preset > 0 || (weekdays & 0x01)) {
+if (preset > 0 && (weekdays & 0x01)) {

645-681: Well-structured regular timer processing.

The implementation properly validates all inputs and skips invalid entries. Good use of constrain() to ensure values are within valid ranges.

wled00/ntp.cpp (5)

13-52: Excellent thread safety implementation.

The dual approach using FreeRTOS mutex for ESP32 and interrupt disabling for single-core platforms is well-designed. The RAII pattern ensures proper cleanup.


424-474: Robust timer validation and memory protection.

Excellent implementation with comprehensive validation, memory growth protection, and appropriate error messages. The differentiated validation for regular timers vs sunrise/sunset offsets is particularly well done.


538-540: Clever month range encoding.

Good use of bit packing to store both monthStart and monthEnd in a single byte. This maintains backward compatibility while extending functionality.


565-594: Clean and thread-safe helper functions.

Well-designed API with focused, single-purpose functions. Consistent use of thread safety throughout.


596-652: Well-refactored timer checking logic.

Clean separation of timer types with appropriate handling for each. The sunrise/sunset offset calculation is correctly implemented.

wled00/data/settings_time.htm (4)

11-30: Proper initialization of timer count from server data.

The implementation correctly determines the existing timer count by checking for valid weekday values, which should resolve UI population issues.


72-115: Excellent data preservation in row generation.

The function properly extracts and preserves all timer fields from existing data, which should prevent data loss during UI updates.


134-139: Verify row removal logic for detail rows.

The function removes the last 2 rows assuming they're a main/detail pair. Ensure the detail row is always present or check if it exists before removal to prevent removing the wrong rows.

Consider checking row count or class names to ensure you're removing the correct rows.


150-160: Correctly updated to handle dynamic timer count.

The function properly iterates over all active timers and correctly constructs the weekday bitmask including the enabled bit.

@PagedPenguin
Copy link
Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jul 26, 2025

✅ Actions performed

Review triggered.

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.

Copy link
Contributor

@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: 1

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

595-643: Duplicate validation logic - consider the same refactoring.

The sunset timer processing uses identical validation patterns as the sunrise timer. The same refactoring suggestion applies here to reduce code duplication.

🧹 Nitpick comments (1)
wled00/set.cpp (1)

545-593: Consider refactoring sunrise timer processing for better maintainability.

The sunrise timer processing logic is correct and includes thorough validation, but it's quite verbose. The validation pattern is repeated for each parameter with similar structure.

Consider extracting a helper function to reduce code duplication:

+// Helper function for parameter validation with debug logging
+template<typename T>
+T validateTimerParam(const String& value, T minVal, T maxVal, const char* paramName, const char* timerType) {
+  T rawValue = static_cast<T>(value.toInt());
+  if (rawValue < minVal || rawValue > maxVal) {
+    DEBUG_PRINTF("%s timer: Invalid %s %d, constraining to range %d-%d\n", 
+                 timerType, paramName, (int)rawValue, (int)minVal, (int)maxVal);
+  }
+  return constrain(rawValue, minVal, maxVal);
+}

Then use it like:

-int presetRaw = request->arg("SR_T").toInt();
-if (presetRaw < 0 || presetRaw > 250) {
-  DEBUG_PRINTF("Sunrise timer: Invalid preset %d, constraining to range 0-250\n", presetRaw);
-}
-uint8_t preset = constrain(presetRaw, 0, 250);
+uint8_t preset = validateTimerParam(request->arg("SR_T"), 0, 250, "preset", "Sunrise");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63b4e72 and 32a2d67.

📒 Files selected for processing (1)
  • wled00/set.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
wled00/set.cpp (3)

Learnt from: blazoncek
PR: #4667
File: usermods/user_fx/user_fx.cpp:27-30
Timestamp: 2025-04-30T05:41:03.633Z
Learning: In WLED, the Segment::allocateData() method already includes optimization to check if data is allocated and sufficiently sized, handling buffer reuse to reduce memory fragmentation. Adding an external check like if (SEGENV.data == nullptr && !SEGENV.allocateData(dataSize)) is unnecessary and could be problematic, as it bypasses proper size verification.

Learnt from: KrX3D
PR: #4237
File: usermods/INA219_v2/INA219_v2.cpp:265-276
Timestamp: 2025-04-18T22:27:58.634Z
Learning: When implementing MQTT message handling in WLED usermods, use strstr(topic, "/specific/path") instead of strcmp_P(topic, PSTR("/specific/path")) to properly match topics that include the device prefix. The full MQTT topic typically follows the pattern <mqttDeviceTopic>/specific/path.

Learnt from: blazoncek
PR: #4482
File: wled00/udp.cpp:343-347
Timestamp: 2025-02-19T12:41:22.676Z
Learning: The setName() method in WLED's Segment class (FX_fcn.cpp) handles all necessary validation internally, including:

  1. Null pointer checks
  2. Length validation against WLED_MAX_SEGNAME_LEN
  3. Memory allocation/reallocation
    No additional validation or name clearing is needed before calling setName().
🧬 Code Graph Analysis (1)
wled00/set.cpp (1)
wled00/ntp.cpp (4)
  • clearTimers (477-481)
  • clearTimers (477-477)
  • addTimer (425-474)
  • addTimer (425-427)
🔇 Additional comments (5)
wled00/set.cpp (5)

542-543: LGTM: Clean timer initialization approach.

The approach of clearing all existing timers first and then rebuilding from form data ensures a clean state and prevents stale timer entries from persisting.


646-681: Robust regular timer processing with good validation.

The regular timer processing logic is well-implemented with:

  • Proper existence checks for timer entries
  • Validation of empty/zero preset values (addressing the past review comment)
  • Appropriate parameter constraints for regular timers (0-23 hours, 0-59 minutes)
  • Correct weekday bit masking (0x7F for 7 bits vs 0xFF for sunrise/sunset)

651-654: Excellent implementation of the improved timer existence check.

This properly addresses the previous review comment by checking both for argument existence AND meaningful content. The validation correctly skips empty or zero preset values before processing the timer.


667-668: Good differentiation between regular and sunrise/sunset timer constraints.

The code correctly applies different validation rules:

  • Regular timers: 7-bit weekdays mask (0x7F) since they don't need the enabled bit
  • Sunrise/sunset timers: 8-bit weekdays (0xFF) to preserve the enabled bit
  • Regular timers: 0-59 minutes vs sunrise/sunset: -59 to +59 offset

This demonstrates proper understanding of the different timer types and their requirements.


647-648: maxTimePresets value and loop bounds validated

maxTimePresets is consistently defined as 32 in wled00/fcn_declare.h and mirrored in data/settings_time.htm. All loops in set.cpp (and related code) iterate from 0 to 31, matching the intended 32 regular timers. No adjustments required.

@PagedPenguin
Copy link
Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jul 26, 2025

✅ Actions performed

Review triggered.

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.

@PagedPenguin PagedPenguin marked this pull request as ready for review July 26, 2025 23:52
@PagedPenguin
Copy link
Author

PagedPenguin commented Jul 26, 2025

I know this is not fully ready for review (to merge) but its is ready for someone else to look at (it is beyond my ability's)

Copy link
Collaborator

@blazoncek blazoncek left a comment

Choose a reason for hiding this comment

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

I am wondering what will happen when users upgrade their WLED and have all 10 timed presets used? I do not see an upgrade code.

I'm also wondering why did you choose to add semaphoring to timer logic? IMO that's an overkill not needed in this particular case.

I did not look at JS/HTML code thoroughly since the underlying C/C++ code needs more work.

}

// Load sunrise/sunset timers from separate "sunrise_sunset" object
JsonObject sunriseSunset = tm["sunrise_sunset"];
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to have such long key, as this is not a UI element. Use short keys (not necessarily descriptive) like "ss".

Also. Why would you limit yourself to just one "sunrise" and "sunset" event?
You should be able to schedule different sunrise and sunset events as well.

for (JsonObject timer : timers) {
if (it > 9) break;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where do you handle legacy configuration? Consider upgrade path.

@@ -200,9 +311,132 @@ <h3>Button actions</h3>
</tbody>
</table>
<a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a>
<h3>Sunrise/Sunset Presets</h3>
<div style="display: inline-block;">
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use classes instead of style properties.

bool hasSunsetTimer();

// Timer constants
const uint8_t maxTimePresets = 32;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use constexpr or #define if this is pure constant so that compiler can optimize.

const uint8_t TIMER_HOUR_SUNRISE = 255; // Special value for sunrise timer
const uint8_t TIMER_HOUR_SUNSET = 254; // Special value for sunset timer

// External timer declarations
Copy link
Collaborator

Choose a reason for hiding this comment

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

These should go into .cpp file.


// WARNING: may cause errors in sunset calculations on ESP8266, see #3400
// building with `-D WLED_USE_REAL_MATH` will prevent those errors at the expense of flash and RAM

// Dynamic timer storage with thread safety
std::vector<Timer> timers;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use static as this vector should not be accessible from elsewhere. You added wrappers.

applyPreset(timerMacro[i]);
TimerCriticalSection lock;
for (const auto& timer : timers) {
if (!timer.isEnabled() || timer.preset == 0) continue;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Indentation.

WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL int8_t timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerHours[34] _INIT_N(({ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }));
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is hard for ESP8266, consuming a lot of precious RAM.

@netmindz netmindz marked this pull request as draft July 27, 2025 12:54
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.

3 participants