Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
9.5 KiB

6 years ago
6 years ago
  1. /*
  2. SCHEDULER MODULE
  3. Copyright (C) 2017 by faina09
  4. Adapted by Xose Pérez <xose dot perez at gmail dot com>
  5. */
  6. #if SCHEDULER_SUPPORT
  7. #include <TimeLib.h>
  8. int _sch_restore = 0;
  9. // -----------------------------------------------------------------------------
  10. #if WEB_SUPPORT
  11. bool _schWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  12. return (strncmp(key, "sch", 3) == 0);
  13. }
  14. void _schWebSocketOnVisible(JsonObject& root) {
  15. if (!relayCount()) return;
  16. root["schVisible"] = 1;
  17. }
  18. void _schWebSocketOnConnected(JsonObject &root){
  19. if (!relayCount()) return;
  20. root["maxSchedules"] = SCHEDULER_MAX_SCHEDULES;
  21. JsonObject &schedules = root.createNestedObject("schedules");
  22. uint8_t size = 0;
  23. JsonArray& enabled = schedules.createNestedArray("schEnabled");
  24. JsonArray& switch_ = schedules.createNestedArray("schSwitch");
  25. JsonArray& action = schedules.createNestedArray("schAction");
  26. JsonArray& type = schedules.createNestedArray("schType");
  27. JsonArray& hour = schedules.createNestedArray("schHour");
  28. JsonArray& minute = schedules.createNestedArray("schMinute");
  29. JsonArray& utc = schedules.createNestedArray("schUTC");
  30. JsonArray& weekdays = schedules.createNestedArray("schWDs");
  31. for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
  32. if (!hasSetting("schSwitch", i)) break;
  33. ++size;
  34. enabled.add<uint8_t>(getSetting("schEnabled", i, 0).toInt() == 1);
  35. utc.add<uint8_t>(getSetting("schUTC", i, 0).toInt() == 1);
  36. switch_.add(getSetting("schSwitch", i, 0).toInt());
  37. action.add(getSetting("schAction", i, 0).toInt());
  38. type.add(getSetting("schType", i, 0).toInt());
  39. hour.add(getSetting("schHour", i, 0).toInt());
  40. minute.add(getSetting("schMinute", i, 0).toInt());
  41. weekdays.add(getSetting("schWDs", i, SCHEDULER_WEEKDAYS));
  42. }
  43. schedules["size"] = size;
  44. schedules["start"] = 0;
  45. }
  46. #endif // WEB_SUPPORT
  47. // -----------------------------------------------------------------------------
  48. void _schConfigure() {
  49. bool delete_flag = false;
  50. for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
  51. int sch_switch = getSetting("schSwitch", i, 0xFF).toInt();
  52. if (sch_switch == 0xFF) delete_flag = true;
  53. if (delete_flag) {
  54. delSetting("schEnabled", i);
  55. delSetting("schSwitch", i);
  56. delSetting("schAction", i);
  57. delSetting("schHour", i);
  58. delSetting("schMinute", i);
  59. delSetting("schWDs", i);
  60. delSetting("schType", i);
  61. delSetting("schUTC", i);
  62. } else {
  63. #if DEBUG_SUPPORT
  64. bool sch_enabled = getSetting("schEnabled", i, 0).toInt() == 1;
  65. int sch_action = getSetting("schAction", i, 0).toInt();
  66. int sch_hour = getSetting("schHour", i, 0).toInt();
  67. int sch_minute = getSetting("schMinute", i, 0).toInt();
  68. bool sch_utc = getSetting("schUTC", i, 0).toInt() == 1;
  69. String sch_weekdays = getSetting("schWDs", i, SCHEDULER_WEEKDAYS);
  70. unsigned char sch_type = getSetting("schType", i, SCHEDULER_TYPE_SWITCH).toInt();
  71. DEBUG_MSG_P(
  72. PSTR("[SCH] Schedule #%d: %s #%d to %d at %02d:%02d %s on %s%s\n"),
  73. i, SCHEDULER_TYPE_SWITCH == sch_type ? "switch" : "channel", sch_switch,
  74. sch_action, sch_hour, sch_minute, sch_utc ? "UTC" : "local time",
  75. (char *) sch_weekdays.c_str(),
  76. sch_enabled ? "" : " (disabled)"
  77. );
  78. #endif // DEBUG_SUPPORT
  79. }
  80. }
  81. }
  82. bool _schIsThisWeekday(time_t t, String weekdays){
  83. // Convert from Sunday to Monday as day 1
  84. int w = weekday(t) - 1;
  85. if (0 == w) w = 7;
  86. char pch;
  87. char * p = (char *) weekdays.c_str();
  88. unsigned char position = 0;
  89. while ((pch = p[position++])) {
  90. if ((pch - '0') == w) return true;
  91. }
  92. return false;
  93. }
  94. int _schMinutesLeft(time_t t, unsigned char schedule_hour, unsigned char schedule_minute){
  95. unsigned char now_hour = hour(t);
  96. unsigned char now_minute = minute(t);
  97. return (schedule_hour - now_hour) * 60 + schedule_minute - now_minute;
  98. }
  99. void _schAction(int sch_id, int sch_action, int sch_switch) {
  100. unsigned char sch_type = getSetting("schType", sch_id, SCHEDULER_TYPE_SWITCH).toInt();
  101. if (SCHEDULER_TYPE_SWITCH == sch_type) {
  102. DEBUG_MSG_P(PSTR("[SCH] Switching switch %d to %d\n"), sch_switch, sch_action);
  103. if (sch_action == 2) {
  104. relayToggle(sch_switch);
  105. } else {
  106. relayStatus(sch_switch, sch_action);
  107. }
  108. }
  109. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  110. if (SCHEDULER_TYPE_DIM == sch_type) {
  111. DEBUG_MSG_P(PSTR("[SCH] Set channel %d value to %d\n"), sch_switch, sch_action);
  112. lightChannel(sch_switch, sch_action);
  113. lightUpdate(true, true);
  114. }
  115. #endif
  116. }
  117. // If daybefore and relay is -1, check with current timestamp
  118. // Otherwise, modify it by moving 'daybefore' days back and only use the 'relay' id
  119. void _schCheck(int relay, int daybefore) {
  120. time_t local_time = now();
  121. time_t utc_time = ntpLocal2UTC(local_time);
  122. int minimum_restore_time = -1440;
  123. int saved_action = -1;
  124. int saved_sch = -1;
  125. // Check schedules
  126. for (unsigned char i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
  127. int sch_switch = getSetting("schSwitch", i, 0xFF).toInt();
  128. if (sch_switch == 0xFF) break;
  129. // Skip disabled schedules
  130. if (getSetting("schEnabled", i, 0).toInt() == 0) continue;
  131. // Get the datetime used for the calculation
  132. bool sch_utc = getSetting("schUTC", i, 0).toInt() == 1;
  133. time_t t = sch_utc ? utc_time : local_time;
  134. if (daybefore > 0) {
  135. unsigned char now_hour = hour(t);
  136. unsigned char now_minute = minute(t);
  137. unsigned char now_sec = second(t);
  138. t = t - ((now_hour * 3600) + ((now_minute + 1) * 60) + now_sec + (daybefore * 86400));
  139. }
  140. String sch_weekdays = getSetting("schWDs", i, SCHEDULER_WEEKDAYS);
  141. if (_schIsThisWeekday(t, sch_weekdays)) {
  142. int sch_hour = getSetting("schHour", i, 0).toInt();
  143. int sch_minute = getSetting("schMinute", i, 0).toInt();
  144. int minutes_to_trigger = _schMinutesLeft(t, sch_hour, sch_minute);
  145. int sch_action = getSetting("schAction", i, 0).toInt();
  146. unsigned char sch_type = getSetting("schType", i, SCHEDULER_TYPE_SWITCH).toInt();
  147. if (sch_type == SCHEDULER_TYPE_SWITCH && sch_switch == relay && sch_action != 2 && minutes_to_trigger < 0 && minutes_to_trigger > minimum_restore_time) {
  148. minimum_restore_time = minutes_to_trigger;
  149. saved_action = sch_action;
  150. saved_sch = i;
  151. }
  152. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  153. if (SCHEDULER_TYPE_DIM == sch_type && sch_switch == relay && minutes_to_trigger < 0 && minutes_to_trigger > minimum_restore_time) {
  154. minimum_restore_time = minutes_to_trigger;
  155. saved_action = sch_action;
  156. saved_sch = i;
  157. }
  158. #endif
  159. if (minutes_to_trigger == 0 && relay == -1) {
  160. _schAction(i, sch_action, sch_switch);
  161. DEBUG_MSG_P(PSTR("[SCH] Schedule #%d TRIGGERED!!\n"), i);
  162. // Show minutes to trigger every 15 minutes
  163. // or every minute if less than 15 minutes to scheduled time.
  164. // This only works for schedules on this same day.
  165. // For instance, if your scheduler is set for 00:01 you will only
  166. // get one notification before the trigger (at 00:00)
  167. } else if (minutes_to_trigger > 0 && relay == -1) {
  168. #if DEBUG_SUPPORT
  169. if ((minutes_to_trigger % 15 == 0) || (minutes_to_trigger < 15)) {
  170. DEBUG_MSG_P(
  171. PSTR("[SCH] %d minutes to trigger schedule #%d\n"),
  172. minutes_to_trigger, i
  173. );
  174. }
  175. #endif
  176. }
  177. }
  178. }
  179. if (daybefore >= 0 && daybefore < 7 && minimum_restore_time == -1440 && saved_action == -1) {
  180. _schCheck(relay, ++daybefore);
  181. return;
  182. }
  183. if (minimum_restore_time != -1440 && saved_action != -1 && saved_sch != -1) {
  184. _schAction(saved_sch, saved_action, relay);
  185. }
  186. }
  187. void _schLoop() {
  188. // Check time has been sync'ed
  189. if (!ntpSynced()) return;
  190. if (_sch_restore == 0) {
  191. for (unsigned char i = 0; i < relayCount(); i++){
  192. if (getSetting("relayLastSch", i, SCHEDULER_RESTORE_LAST_SCHEDULE).toInt() == 1)
  193. _schCheck(i, 0);
  194. }
  195. _sch_restore = 1;
  196. }
  197. // Check schedules every minute at hh:mm:00
  198. static unsigned long last_minute = 60;
  199. unsigned char current_minute = minute();
  200. if (current_minute != last_minute) {
  201. last_minute = current_minute;
  202. _schCheck(-1, -1);
  203. }
  204. }
  205. // -----------------------------------------------------------------------------
  206. void schSetup() {
  207. _schConfigure();
  208. // Update websocket clients
  209. #if WEB_SUPPORT
  210. wsRegister()
  211. .onVisible(_schWebSocketOnVisible)
  212. .onConnected(_schWebSocketOnConnected)
  213. .onKeyCheck(_schWebSocketOnKeyCheck);
  214. #endif
  215. // Main callbacks
  216. espurnaRegisterLoop(_schLoop);
  217. espurnaRegisterReload(_schConfigure);
  218. }
  219. #endif // SCHEDULER_SUPPORT