diff --git a/wled00/data/settings_time.htm b/wled00/data/settings_time.htm
index ae29065ea..8622b79c7 100644
--- a/wled00/data/settings_time.htm
+++ b/wled00/data/settings_time.htm
@@ -178,6 +178,11 @@
Countdown Goal:
Date: 20--
Time: ::
+
Upload Schedule JSON
+
+
+
+ Backup schedule
Macro presets
Macros have moved!
Presets now also can be used as macros to save both JSON and HTTP API commands.
diff --git a/wled00/schedule.cpp b/wled00/schedule.cpp
new file mode 100644
index 000000000..0d9685175
--- /dev/null
+++ b/wled00/schedule.cpp
@@ -0,0 +1,105 @@
+// schedule.cpp
+
+//TODO: make schedule.json trigger loadshedule(); on upload istead of just once a min (line 50)
+
+#include "schedule.h"
+#include
+#include
+
+#define SCHEDULE_FILE "/schedule.json"
+
+ScheduleEvent scheduleEvents[MAX_SCHEDULE_EVENTS];
+uint8_t numScheduleEvents = 0;
+
+bool isTodayInRange(uint8_t sm, uint8_t sd, uint8_t em, uint8_t ed, uint8_t cm, uint8_t cd)
+{
+ if (sm < em || (sm == em && sd <= ed))
+ {
+ return (cm > sm || (cm == sm && cd >= sd)) &&
+ (cm < em || (cm == em && cd <= ed));
+ }
+ else
+ {
+ return (cm > sm || (cm == sm && cd >= sd)) ||
+ (cm < em || (cm == em && cd <= ed));
+ }
+}
+
+
+// Checks the schedule and applies any events that match the current time and date.
+
+void checkSchedule() {
+ static int lastMinute = -1;
+
+ time_t now = localTime;
+ if (now < 100000) return;
+
+ struct tm* timeinfo = localtime(&now);
+ int thisMinute = timeinfo->tm_min + timeinfo->tm_hour * 60;
+
+ if (thisMinute == lastMinute) return;
+ lastMinute = thisMinute;
+
+
+ uint8_t cm = timeinfo->tm_mon + 1; // months since Jan (0-11)
+ uint8_t cd = timeinfo->tm_mday;
+ uint8_t wday = timeinfo->tm_wday; // days since Sunday (0-6)
+ uint8_t hr = timeinfo->tm_hour;
+ uint8_t min = timeinfo->tm_min;
+
+ loadSchedule();
+ DEBUG_PRINTF_P(PSTR("[Schedule] Checking schedule at %02u:%02u\n"), hr, min);
+
+ for (uint8_t i = 0; i < numScheduleEvents; i++)
+ {
+ const ScheduleEvent &e = scheduleEvents[i];
+ if (e.hour != hr || e.minute != min)
+ continue;
+
+ bool match = false;
+ if (e.repeatMask && ((e.repeatMask >> wday) & 0x01))
+ match = true;
+ if (e.startMonth)
+ {
+ if (isTodayInRange(e.startMonth, e.startDay, e.endMonth, e.endDay, cm, cd))
+ match = true;
+ }
+
+ if (match)
+ applyPreset(e.presetId);
+ DEBUG_PRINTF_P(PSTR("[Schedule] Applying preset %u at %02u:%02u\n"), e.presetId, hr, min);
+ DEBUG_PRINTF_P(PSTR("[Schedule] Checked event %u: match=%d\n"), i, match);
+ }
+}
+
+void loadSchedule()
+{
+ if (!WLED_FS.exists(SCHEDULE_FILE))
+ return;
+ File file = WLED_FS.open(SCHEDULE_FILE, "r");
+ if (!file)
+ return;
+
+ DynamicJsonDocument doc(4096);
+ if (deserializeJson(doc, file))
+ {
+ file.close();
+ return;
+ }
+ file.close();
+
+ numScheduleEvents = 0;
+ for (JsonObject e : doc.as())
+ {
+ if (numScheduleEvents >= MAX_SCHEDULE_EVENTS)
+ break;
+ scheduleEvents[numScheduleEvents++] = {
+ (uint8_t)e["sm"], (uint8_t)e["sd"], // start month, day
+ (uint8_t)e["em"], (uint8_t)e["ed"], // end month, day
+ (uint8_t)e["r"], (uint8_t)e["h"], // repeat mask, hour
+ (uint8_t)e["m"], (uint8_t)e["p"]}; // minute, preset
+ }
+ DEBUG_PRINTF_P(PSTR("[Schedule] Loaded %u schedule entries from schedule.json\n"), numScheduleEvents);
+
+}
+
diff --git a/wled00/schedule.h b/wled00/schedule.h
new file mode 100644
index 000000000..ced0688bc
--- /dev/null
+++ b/wled00/schedule.h
@@ -0,0 +1,20 @@
+// schedule.h
+#pragma once
+
+#include
+
+#define MAX_SCHEDULE_EVENTS 32
+
+struct ScheduleEvent {
+ uint8_t startMonth;
+ uint8_t startDay;
+ uint8_t endMonth;
+ uint8_t endDay;
+ uint8_t repeatMask;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t presetId;
+};
+
+void loadSchedule();
+void checkSchedule();
diff --git a/wled00/wled.cpp b/wled00/wled.cpp
index c372d22ab..d5d634177 100644
--- a/wled00/wled.cpp
+++ b/wled00/wled.cpp
@@ -54,6 +54,7 @@ void WLED::loop()
#endif
handleTime();
+ checkSchedule();
#ifndef WLED_DISABLE_INFRARED
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
#endif
@@ -526,6 +527,8 @@ void WLED::setup()
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector
#endif
+
+ loadSchedule();
}
void WLED::beginStrip()
diff --git a/wled00/wled.h b/wled00/wled.h
index 52bb2f936..a823c87c2 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -66,6 +66,8 @@
#include
#include
+#include "schedule.h"
+
// Library inclusions.
#include