/* SCHEDULER MODULE Copyright (C) 2017 by faina09 Adapted by Xose PĂ©rez Module key prefix: sch */ #if SCHEDULER_SUPPORT #include // ----------------------------------------------------------------------------- #if WEB_SUPPORT bool _schWebSocketOnReceive(const char * key, JsonVariant& value) { return (strncmp(key, "sch", 3) == 0); } void _schWebSocketOnSend(JsonObject &root){ if (relayCount() > 0) { root["schVisible"] = 1; root["maxSchedules"] = SCHEDULER_MAX_SCHEDULES; JsonArray &sch = root.createNestedArray("schedule"); for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) { if (!hasSetting("schSwitch", i)) break; JsonObject &scheduler = sch.createNestedObject(); scheduler["schEnabled"] = getSetting("schEnabled", i, 1).toInt() == 1; scheduler["schSwitch"] = getSetting("schSwitch", i, 0).toInt(); scheduler["schAction"] = getSetting("schAction", i, 0).toInt(); scheduler["schType"] = getSetting("schType", i, 0).toInt(); scheduler["schHour"] = getSetting("schHour", i, 0).toInt(); scheduler["schMinute"] = getSetting("schMinute", i, 0).toInt(); scheduler["schUTC"] = getSetting("schUTC", i, 0).toInt() == 1; scheduler["schWDs"] = getSetting("schWDs", i, ""); } } } #endif // WEB_SUPPORT // ----------------------------------------------------------------------------- void _schConfigure() { bool delete_flag = false; for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) { int sch_switch = getSetting("schSwitch", i, 0xFF).toInt(); if (sch_switch == 0xFF) delete_flag = true; if (delete_flag) { delSetting("schEnabled", i); delSetting("schSwitch", i); delSetting("schAction", i); delSetting("schHour", i); delSetting("schMinute", i); delSetting("schWDs", i); delSetting("schType", i); delSetting("schUTC", i); } else { #if DEBUG_SUPPORT bool sch_enabled = getSetting("schEnabled", i, 1).toInt() == 1; int sch_action = getSetting("schAction", i, 0).toInt(); int sch_hour = getSetting("schHour", i, 0).toInt(); int sch_minute = getSetting("schMinute", i, 0).toInt(); bool sch_utc = getSetting("schUTC", i, 0).toInt() == 1; String sch_weekdays = getSetting("schWDs", i, ""); unsigned char sch_type = getSetting("schType", i, SCHEDULER_TYPE_SWITCH).toInt(); DEBUG_MSG_P( PSTR("[SCH] Schedule #%d: %s #%d to %d at %02d:%02d %s on %s%s\n"), i, SCHEDULER_TYPE_SWITCH == sch_type ? "switch" : "channel", sch_switch, sch_action, sch_hour, sch_minute, sch_utc ? "UTC" : "local time", (char *) sch_weekdays.c_str(), sch_enabled ? "" : " (disabled)" ); #endif // DEBUG_SUPPORT } } } bool _schIsThisWeekday(time_t t, String weekdays){ // Convert from Sunday to Monday as day 1 int w = weekday(t) - 1; if (0 == w) w = 7; char pch; char * p = (char *) weekdays.c_str(); unsigned char position = 0; while ((pch = p[position++])) { if ((pch - '0') == w) return true; } return false; } int _schMinutesLeft(time_t t, unsigned char schedule_hour, unsigned char schedule_minute){ unsigned char now_hour = hour(t); unsigned char now_minute = minute(t); return (schedule_hour - now_hour) * 60 + schedule_minute - now_minute; } void _schCheck() { time_t local_time = now(); time_t utc_time = ntpLocal2UTC(local_time); // Check schedules for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) { int sch_switch = getSetting("schSwitch", i, 0xFF).toInt(); if (sch_switch == 0xFF) break; // Skip disabled schedules if (getSetting("schEnabled", i, 1).toInt() == 0) continue; // Get the datetime used for the calculation bool sch_utc = getSetting("schUTC", i, 0).toInt() == 1; time_t t = sch_utc ? utc_time : local_time; String sch_weekdays = getSetting("schWDs", i, ""); if (_schIsThisWeekday(t, sch_weekdays)) { int sch_hour = getSetting("schHour", i, 0).toInt(); int sch_minute = getSetting("schMinute", i, 0).toInt(); int minutes_to_trigger = _schMinutesLeft(t, sch_hour, sch_minute); if (minutes_to_trigger == 0) { unsigned char sch_type = getSetting("schType", i, SCHEDULER_TYPE_SWITCH).toInt(); if (SCHEDULER_TYPE_SWITCH == sch_type) { int sch_action = getSetting("schAction", i, 0).toInt(); DEBUG_MSG_P(PSTR("[SCH] Switching switch %d to %d\n"), sch_switch, sch_action); if (sch_action == 2) { relayToggle(sch_switch); } else { relayStatus(sch_switch, sch_action); } } #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE if (SCHEDULER_TYPE_DIM == sch_type) { int sch_brightness = getSetting("schAction", i, -1).toInt(); DEBUG_MSG_P(PSTR("[SCH] Set channel %d value to %d\n"), sch_switch, sch_brightness); lightChannel(sch_switch, sch_brightness); lightUpdate(true, true); } #endif DEBUG_MSG_P(PSTR("[SCH] Schedule #%d TRIGGERED!!\n"), i); // Show minutes to trigger every 15 minutes // or every minute if less than 15 minutes to scheduled time. // This only works for schedules on this same day. // For instance, if your scheduler is set for 00:01 you will only // get one notification before the trigger (at 00:00) } else if (minutes_to_trigger > 0) { #if DEBUG_SUPPORT if ((minutes_to_trigger % 15 == 0) || (minutes_to_trigger < 15)) { DEBUG_MSG_P( PSTR("[SCH] %d minutes to trigger schedule #%d\n"), minutes_to_trigger, i ); } #endif } } } } void _schLoop() { // Check time has been sync'ed if (!ntpSynced()) return; // Check schedules every minute at hh:mm:00 static unsigned long last_minute = 60; unsigned char current_minute = minute(); if (current_minute != last_minute) { last_minute = current_minute; _schCheck(); } } // ----------------------------------------------------------------------------- void schSetup() { _schConfigure(); // Update websocket clients #if WEB_SUPPORT wsOnSendRegister(_schWebSocketOnSend); wsOnReceiveRegister(_schWebSocketOnReceive); wsOnAfterParseRegister(_schConfigure); #endif // Register loop espurnaRegisterLoop(_schLoop); } #endif // SCHEDULER_SUPPORT