Skip to content

Conversation

PagedPenguin
Copy link

@PagedPenguin PagedPenguin commented Jul 14, 2025

Note: This is a cleaner reimplementation of the scheduling system originally proposed in PR #4772. It replaces that version with improved structure and maintainability.

Disclaimer: This is a conceptual implementation intended to look at improved approaches to scheduling in WLED. It primarily serves as a foundation on how best to evolve or replace the existing timer system.

What this PR Does
Introduces a lightweight, modular scheduling system supporting up to 32 schedule events (vs. the legacy ~10 timer slots).

Each schedule event supports:
Time-based preset triggers (hour + minute)
Optional date ranges (start/end month and day)
Weekly repeat bitmasks for specific weekdays
Scheduling data is stored and loaded from a simple JSON file (schedule.json), making it easier to backup and edit outside the device.
The scheduler runs in the main loop, applying presets once per minute when conditions match.

Comparison to Existing Timer System

  • The existing timer system is limited in capacity and tightly coupled to macros, with some legacy code complexity.
  • Functionality overlaps significantly — both systems trigger presets on time/date conditions — but the new scheduler lays groundwork for future extensibility.
  • It does not yet have UI editing features.
  • The existing timers include some specialized features (sunrise/sunset, hour 24 triggers) which are not replicated here.

Why Some Features Are Not Included Yet
This scheduler focuses on being lightweight and efficient to make it feasible as a default built-in feature, rather than a usermod. As such, it currently:

  • Triggers only presets (no macros or complex HTTP actions) to minimize code size and complexity.
  • Does not yet handle sunrise/sunset events or other advanced triggers, which add significant logic and state tracking.
    The aim is to provide a clean, minimal core that covers the most common scheduling needs with minimal RAM and CPU overhead.

Thoughts on Combining with the Existing Timer System

  • The existing timer system already supports things like sunrise/sunset and running macros.
  • We could dynamically add timers to either system depending on whether they fit the new scheduler’s style, but having both running at the same time feels a bit unnecessary.
  • Combining both into one system is possible but could make the code more complex and harder to keep lightweight.

Feedback on how to best handle this would be helpful.

Technical details

How it works

  • A schedule.json can be uploaded via /settings/time
  • The schedule system loads this file on startup and when a new schedule.json is uploaded
  • Each schedule entry contains a time, an optional date range, and/or a weekly repeat bitmask.
  • When the current time matches an entry, the configured preset is applied.
  • The checkSchedule() gets called in the wled.cpp loop() and if it was triggered less than a minute ago doesn't (fully) trigger.

JSON Formating

  • sm, sd, em, ed: Start/end month & day (date range)
  • r: Weekly repeat bitmask (bit 0 = Sunday, bit 6 = Saturday)
  • h, m: Hour and minute to trigger
  • p: Preset ID to apply
[
  {
    "sm": 7,
    "sd": 4,
    "em": 7,
    "ed": 4,
    "r": 0,
    "h": 20,
    "m": 30,
    "p": 5
  },
  {
    "sm": 0,
    "sd": 0,
    "em": 0,
    "ed": 0,
    "r": 34,
    "h": 7,
    "m": 15,
    "p": 2
  }
]

Explanation:
First entry: Runs preset 5 on July 4th at 20:30 every year.

Second entry: Runs preset 2 every Monday and Friday at 07:15.
(Repeat bitmask 34 = 0b100010 = Monday and Friday)

Summary by CodeRabbit

  • New Features

    • Introduced dynamic time-controlled presets with add/remove capabilities in the time settings.
    • Added schedule-based automation supporting date ranges, repeat options, and preset triggers.
    • Enabled upload and download of preset schedules via the time settings interface.
  • Bug Fixes

    • Enhanced schedule file upload with atomic replacement and immediate application.
    • Improved error handling during file uploads for better reliability.

Copy link
Contributor

coderabbitai bot commented Jul 14, 2025

Walkthrough

A scheduling feature was introduced, allowing users to upload, download, and manage a /schedule.json file that defines preset activation events based on time and date. The core logic for parsing, loading, and applying schedules was implemented, with integration into the main application loop and enhanced upload handling for atomic schedule updates. The time-controlled presets UI was refactored to support dynamic addition and removal of presets.

Changes

Files/Group Change Summary
wled00/data/settings_time.htm Refactored time presets section for dynamic add/remove of presets; added buttons for preset management; added UI for schedule JSON upload/download.
wled00/schedule.cpp, wled00/schedule.h Added schedule event struct and functions to load, parse, and check scheduled events from /schedule.json.
wled00/wled.cpp Integrated schedule loading during setup and schedule checking in the main application loop.
wled00/wled_server.cpp Modified upload handler to support atomic upload of /schedule.json with temporary file and immediate reload; added error handling on file operations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Suggested reviewers

  • blazoncek

Note

⚡️ Unit Test Generation - Beta

CodeRabbit's unit test generation is now available in Beta! Automatically generate comprehensive unit tests for your code changes, ensuring better test coverage and catching edge cases you might miss. Our AI analyzes your code structure and creates tests that follow best practices and your project's testing patterns. Learn more here, or just try it under ✨ Finishing Touches.

✨ Finishing Touches
🧪 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.

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

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

87-147: Consider making the JSON buffer size configurable.

The hardcoded 4096-byte buffer on line 99 might be limiting for complex schedules. Consider defining it as a constant for easier maintenance.

Add a constant definition after the SCHEDULE_FILE definition:

 #define SCHEDULE_FILE "/schedule.json"
+#define SCHEDULE_JSON_BUFFER_SIZE 4096

Then use it in the function:

-    DynamicJsonDocument doc(4096);
+    DynamicJsonDocument doc(SCHEDULE_JSON_BUFFER_SIZE);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b7bfd6f and 9d1d4fe.

📒 Files selected for processing (5)
  • wled00/data/settings_time.htm (1 hunks)
  • wled00/schedule.cpp (1 hunks)
  • wled00/schedule.h (1 hunks)
  • wled00/wled.cpp (3 hunks)
  • wled00/wled_server.cpp (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
wled00/wled_server.cpp (1)
Learnt from: blazoncek
PR: wled/WLED#4700
File: wled00/wled_server.cpp:409-414
Timestamp: 2025-05-26T16:09:34.325Z
Learning: In wled00/wled_server.cpp, the OTA update access control logic intentionally allows OTA updates from different subnets when otaSameSubnet is true AND a PIN is set. This was a conscious design decision by blazoncek to provide flexibility for remote OTA updates with PIN protection, though they acknowledged it may not be the optimal security model.
wled00/wled.cpp (2)
Learnt from: KrX3D
PR: wled/WLED#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.
Learnt from: blazoncek
PR: wled/WLED#4700
File: wled00/wled_server.cpp:409-414
Timestamp: 2025-05-26T16:09:34.325Z
Learning: In wled00/wled_server.cpp, the OTA update access control logic intentionally allows OTA updates from different subnets when otaSameSubnet is true AND a PIN is set. This was a conscious design decision by blazoncek to provide flexibility for remote OTA updates with PIN protection, though they acknowledged it may not be the optimal security model.
🧬 Code Graph Analysis (1)
wled00/wled.cpp (2)
wled00/schedule.h (2)
  • checkSchedule (27-27)
  • loadSchedule (24-24)
wled00/schedule.cpp (4)
  • checkSchedule (33-83)
  • checkSchedule (33-33)
  • loadSchedule (87-147)
  • loadSchedule (87-87)
🔇 Additional comments (4)
wled00/data/settings_time.htm (1)

181-185: No action needed: uploadFile is defined

The uploadFile(fileObj, name) function is implemented in wled00/data/common.js, so the upload functionality is already available in the loaded scripts.

wled00/wled.cpp (1)

5-5: LGTM! Well-integrated scheduling functionality.

The scheduling system is properly integrated:

  • Header inclusion is correct
  • checkSchedule() is appropriately placed after handleTime() to ensure time is updated before checking
  • loadSchedule() is correctly called after filesystem initialization

Also applies to: 58-58, 419-420

wled00/schedule.h (1)

1-27: LGTM! Well-structured header file.

The header file is properly designed with:

  • Modern #pragma once guard
  • Clear documentation for all struct fields
  • Appropriate use of fixed-width integer types
  • Reasonable limit of 32 schedule events
wled00/wled_server.cpp (1)

178-277: Excellent implementation of atomic schedule upload!

The enhanced upload handling for /schedule.json is well-designed:

  • Uses temporary file to prevent corruption during upload
  • Implements atomic replacement with proper fallback logic
  • Includes comprehensive error handling and meaningful response messages
  • Immediately loads the new schedule ensuring changes take effect

@willmmiles
Copy link
Member

willmmiles commented Jul 14, 2025

My $0.02: this should be implemented as a usermod. I have no objections to including such a module in standard builds (like audioreactive), but from a code architectural standpoint, I think it's better to avoid adding more code to the core when the module framework provides APIs with sufficient power. (In fact, I am pushing to migrate existing core features to modules in the future - see cases like #4868).

@PagedPenguin
Copy link
Author

I definitely don’t know what would work best from an architectural standpoint, but from a user perspective, I’d really like this scheduling feature to be available by default. Most users aren’t going to compile WLED themselves just to add a usermod, so having it built-in makes it way more accessible, that is why I made this. That said, I’m open to ideas on how to implement it better while still making this easy for everyone to use.

@DedeHai
Copy link
Collaborator

DedeHai commented Jul 14, 2025

I'll ask again: why not extend the already existing scheduler and make that more versatile instead of adding another one?

@PagedPenguin
Copy link
Author

PagedPenguin commented Jul 14, 2025

Honestly, I'm not sure what’s best here. My main goal was to keep things simple and clean, but as its my first time doing anything like this is i didn't quite understand how the old way worked >///< (I've been figuring that out), I implemented a new way which could certainly be worse. I’m definitely open to ideas on extending the existing scheduler if that’s preferred.

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 welcome the idea but would prefer the configuration to be inside cfg.json and not in separate JSON file.
Remember: Configuration can and should be backed up as a single file using UI.

@blazoncek
Copy link
Collaborator

To add to the discussion: Enhancing current implementation (using vectors instead of fixed array with 10 elemts) would be better. The only added complexity would be date scheduling.

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e95c2b7 and e130bb9.

📒 Files selected for processing (3)
  • wled00/schedule.cpp (1 hunks)
  • wled00/schedule.h (1 hunks)
  • wled00/wled_server.cpp (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • wled00/wled_server.cpp
  • wled00/schedule.h
🧰 Additional context used
🧠 Learnings (1)
wled00/schedule.cpp (2)
Learnt from: KrX3D
PR: wled/WLED#4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.h:121-136
Timestamp: 2025-04-27T09:37:28.415Z
Learning: Using PROGMEM for the seven-segment font array (umSSDRNumbers) in the WLED SSDR usermod causes compilation problems, so it should be left as a regular array.
Learnt from: KrX3D
PR: wled/WLED#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`.
🔇 Additional comments (5)
wled00/schedule.cpp (5)

1-8: LGTM!

The file header and includes are properly structured and include all necessary dependencies for the scheduling functionality.


9-13: LGTM!

Good use of std::vector for dynamic storage as requested in previous reviews, and reasonable buffer size allocation.


15-29: LGTM!

The date range logic correctly handles both normal ranges and year-end wrap-around cases. The implementation properly checks if the current date falls within the specified start and end date range.


83-104: LGTM!

Proper file handling with existence checks, JSON buffer locking for thread safety, and appropriate error handling for JSON parsing.


32-79: Fix the date range check condition.

The condition if (e.startMonth) on line 68 will skip events scheduled for January since January is month 1, but the check only proceeds if startMonth is non-zero. Events with January start dates will be incorrectly skipped.

Apply this fix:

-        // Otherwise check if current date is within start and end date range
-        if (e.startMonth) {
+        // Check if current date is within start and end date range (when specified)
+        if (e.startMonth > 0) {

Additionally, consider improving the time validation guard:

-    if (now < 100000) return; // Invalid or uninitialized time guard
+    if (now < 946684800) return; // Invalid time guard (before year 2000)

Likely an incorrect or invalid review comment.

@PagedPenguin
Copy link
Author

@blazoncek I fixed the problems you mentioned.

@PagedPenguin PagedPenguin requested a review from blazoncek July 15, 2025 01:45
@blazoncek
Copy link
Collaborator

That's better.
However, without UI it is not much use for regular user.
Please add logic to Macro settings to add variable number of schedules. See example for wifi or LED outputs.

When that is done, replace existing schedules.

@Arcitec
Copy link
Contributor

Arcitec commented Jul 18, 2025

I had a quick look. Some things here look pretty nice. Thanks for taking the time to work on this.

But I agree with dedehai. Why not improve the existing scheduler instead of adding a 2nd?

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

🧹 Nitpick comments (1)
wled00/data/settings_time.htm (1)

123-163: Consider a more robust DOM manipulation approach.

While the current reindexing implementation works, the heavy reliance on regex string replacement in innerHTML could be fragile if HTML structure changes. Consider using more targeted DOM API methods for updating specific attributes.

For example, instead of regex replacement:

-row.innerHTML = row.innerHTML.replace(new RegExp(`name="W${oldIndex}"`, 'g'), `name="W${newIndex}"`)
+row.querySelectorAll(`[name="W${oldIndex}"]`).forEach(el => el.name = `W${newIndex}`)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between de9630e and 255ba9b.

📒 Files selected for processing (1)
  • wled00/data/settings_time.htm (4 hunks)
🔇 Additional comments (7)
wled00/data/settings_time.htm (7)

11-12: LGTM! Reasonable configuration values.

The maximum of 16 presets and initial count of 3 (sunrise, sunset, permanent) provides good balance between functionality and resource usage.


29-43: Excellent refactoring for maintainability.

The BTa() function is now much cleaner and more maintainable using the generatePresetRow() helper. The dynamic generation logic correctly handles different preset types.


45-77: Well-designed helper function with proper type handling.

The generatePresetRow() function effectively handles different preset types and generates clean HTML. The input constraints are appropriate (negative minutes for sunrise/sunset, positive for regular presets).


79-91: Solid implementation with proper validation.

The addTimePreset() function correctly validates against the maximum limit, safely manipulates the DOM, and maintains UI consistency by updating button states.


93-121: Safe removal operations with proper validation.

Both removal functions correctly protect built-in presets and maintain DOM consistency through reindexing. The user-friendly error messages provide clear feedback.


165-176: Clean button state management.

The updateButtonStates() function correctly manages add/remove button states with proper null checks and logical conditions.


306-338: Server-side JSON validation confirmed

schedule.cpp implements robust checks:

  • Uses a fixed-size DynamicJsonDocument and handles deserializeJson errors.
  • Validates all event fields (month, day, hour, minute, repeat count, preset ID) against allowable ranges, skipping any invalid entries.

No further action required.

Comment on lines 177 to 209
function FC()
{
for(i=0;i<10;i++)
for(i=0;i<currentPresetCount;i++)
{
let wd = gId("W"+i).value;
let wd = gId("W"+i);
if (!wd) continue; // Skip if preset doesn't exist
wd = wd.value;
for(j=0;j<8;j++) {
gId("W"+i+j).checked=wd>>j&1;
let checkbox = gId("W"+i+j);
if (checkbox) checkbox.checked=wd>>j&1;
}
if ((wd&254) != 254 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) {
expand(gId("CB"+i),i); //expand macros with custom DOW or date range set
if ((wd&254) != 254 || (i<8 && (gN("M"+i) && gN("D"+i) && gN("P"+i) && gN("E"+i) &&
(gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31)))) {
let cbElement = gId("CB"+i);
if (cbElement) expand(cbElement,i); //expand macros with custom DOW or date range set
}
}
updateAddButtonState();
}
function Wd()
{
a = [0,0,0,0,0,0,0,0,0,0];
for (i=0; i<10; i++) {
a = [];
for (i=0; i<currentPresetCount; i++) {
a[i] = 0;
m=1;
for(j=0;j<8;j++) { a[i]+=gId(("W"+i)+j).checked*m; m*=2;}
gId("W"+i).value=a[i];
for (j=0; j<8; j++) {
let checkbox = gId("W"+i+j);
if (checkbox && checkbox.checked) a[i]+=m;
m*=2;
}
let hiddenInput = gId("W"+i);
if (hiddenInput) hiddenInput.value = a[i];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Good adaptation to dynamic presets with minor issue.

The FC() and Wd() functions are correctly updated to handle dynamic presets with proper null checks. However, line 194 calls updateAddButtonState() which doesn't exist - should this be updateButtonStates()?

-updateAddButtonState();
+updateButtonStates();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function FC()
{
for(i=0;i<10;i++)
for(i=0;i<currentPresetCount;i++)
{
let wd = gId("W"+i).value;
let wd = gId("W"+i);
if (!wd) continue; // Skip if preset doesn't exist
wd = wd.value;
for(j=0;j<8;j++) {
gId("W"+i+j).checked=wd>>j&1;
let checkbox = gId("W"+i+j);
if (checkbox) checkbox.checked=wd>>j&1;
}
if ((wd&254) != 254 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) {
expand(gId("CB"+i),i); //expand macros with custom DOW or date range set
if ((wd&254) != 254 || (i<8 && (gN("M"+i) && gN("D"+i) && gN("P"+i) && gN("E"+i) &&
(gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31)))) {
let cbElement = gId("CB"+i);
if (cbElement) expand(cbElement,i); //expand macros with custom DOW or date range set
}
}
updateAddButtonState();
}
function Wd()
{
a = [0,0,0,0,0,0,0,0,0,0];
for (i=0; i<10; i++) {
a = [];
for (i=0; i<currentPresetCount; i++) {
a[i] = 0;
m=1;
for(j=0;j<8;j++) { a[i]+=gId(("W"+i)+j).checked*m; m*=2;}
gId("W"+i).value=a[i];
for (j=0; j<8; j++) {
let checkbox = gId("W"+i+j);
if (checkbox && checkbox.checked) a[i]+=m;
m*=2;
}
let hiddenInput = gId("W"+i);
if (hiddenInput) hiddenInput.value = a[i];
}
function FC()
{
for(i=0;i<currentPresetCount;i++)
{
let wd = gId("W"+i);
if (!wd) continue; // Skip if preset doesn't exist
wd = wd.value;
for(j=0;j<8;j++) {
let checkbox = gId("W"+i+j);
if (checkbox) checkbox.checked=wd>>j&1;
}
if ((wd&254) != 254 || (i<8 && (gN("M"+i) && gN("D"+i) && gN("P"+i) && gN("E"+i) &&
(gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))))
{
let cbElement = gId("CB"+i);
if (cbElement) expand(cbElement,i); //expand macros with custom DOW or date range set
}
}
- updateAddButtonState();
+ updateButtonStates();
}
function Wd()
{
a = [];
for (i=0; i<currentPresetCount; i++) {
a[i] = 0;
m=1;
for (j=0; j<8; j++) {
let checkbox = gId("W"+i+j);
if (checkbox && checkbox.checked) a[i]+=m;
m*=2;
}
let hiddenInput = gId("W"+i);
if (hiddenInput) hiddenInput.value = a[i];
}
🤖 Prompt for AI Agents
In wled00/data/settings_time.htm around lines 177 to 209, the function FC()
calls updateAddButtonState() on line 194, but this function is not defined.
Replace this call with updateButtonStates() to fix the undefined function error
and ensure the correct button state update logic is executed.

@PagedPenguin
Copy link
Author

i am working on a new implementation

@blazoncek
Copy link
Collaborator

Looks promising!

@PagedPenguin
Copy link
Author

superseded by #4789

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.

5 participants