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.

279 lines
7.4 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
6 years ago
8 years ago
  1. /*
  2. NTP MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if NTP_SUPPORT
  6. #include <TimeLib.h>
  7. #include <WiFiClient.h>
  8. #include <Ticker.h>
  9. #include <libs/NtpClientWrap.h>
  10. Ticker _ntp_defer;
  11. bool _ntp_report = false;
  12. bool _ntp_configure = false;
  13. bool _ntp_want_sync = false;
  14. // -----------------------------------------------------------------------------
  15. // NTP
  16. // -----------------------------------------------------------------------------
  17. #if WEB_SUPPORT
  18. bool _ntpWebSocketOnReceive(const char * key, JsonVariant& value) {
  19. return (strncmp(key, "ntp", 3) == 0);
  20. }
  21. void _ntpWebSocketOnSend(JsonObject& root) {
  22. root["ntpVisible"] = 1;
  23. root["ntpStatus"] = (timeStatus() == timeSet);
  24. root["ntpServer"] = getSetting("ntpServer", NTP_SERVER);
  25. root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
  26. root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
  27. root["ntpRegion"] = getSetting("ntpRegion", NTP_DST_REGION).toInt();
  28. }
  29. #endif
  30. time_t _ntpSyncProvider() {
  31. _ntp_want_sync = true;
  32. return 0;
  33. }
  34. void _ntpWantSync() {
  35. _ntp_want_sync = true;
  36. }
  37. int _ntpSyncInterval() {
  38. return secureRandom(NTP_SYNC_INTERVAL, NTP_SYNC_INTERVAL * 2);
  39. }
  40. int _ntpUpdateInterval() {
  41. return secureRandom(NTP_UPDATE_INTERVAL, NTP_UPDATE_INTERVAL * 2);
  42. }
  43. void inline _ntpSetSyncInterval(bool shortInterval = false) {
  44. if (shortInterval) {
  45. setSyncInterval(_ntpSyncInterval());
  46. } else {
  47. setSyncInterval(_ntpUpdateInterval());
  48. }
  49. }
  50. void _ntpStart() {
  51. _ntpConfigure();
  52. // short (initial) and long (after sync) intervals
  53. NTPw.setInterval(_ntpSyncInterval(), _ntpUpdateInterval());
  54. DEBUG_MSG_P(PSTR("[NTP] Update intervals: %us / %us\n"),
  55. NTPw.getShortInterval(), NTPw.getLongInterval());
  56. // setSyncProvider will immediatly call given function by setting next sync time to the current time.
  57. // Avoid triggering sync immediatly by canceling sync provider flag and resetting sync interval again
  58. setSyncProvider(_ntpSyncProvider);
  59. _ntp_want_sync = false;
  60. _ntpSetSyncInterval(true);
  61. }
  62. void _ntpConfigure() {
  63. _ntp_configure = false;
  64. int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
  65. int sign = offset > 0 ? 1 : -1;
  66. offset = abs(offset);
  67. int tz_hours = sign * (offset / 60);
  68. int tz_minutes = sign * (offset % 60);
  69. if (NTPw.getTimeZone() != tz_hours || NTPw.getTimeZoneMinutes() != tz_minutes) {
  70. NTPw.setTimeZone(tz_hours, tz_minutes);
  71. _ntp_report = true;
  72. }
  73. bool daylight = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1;
  74. if (NTPw.getDayLight() != daylight) {
  75. NTPw.setDayLight(daylight);
  76. _ntp_report = true;
  77. }
  78. String server = getSetting("ntpServer", NTP_SERVER);
  79. if (!NTPw.getNtpServerName().equals(server)) {
  80. NTPw.setNtpServerName(server);
  81. }
  82. uint8_t dst_region = getSetting("ntpRegion", NTP_DST_REGION).toInt();
  83. NTPw.setDSTZone(dst_region);
  84. // Set short interval to sync again shortly, randomized in time to avoid clogging the server with simultaious requests from multiple devices (i.e. on power surge, when devices start up at the same time)
  85. setSyncInterval(true);
  86. // Some remote servers can be slow to respond, increase accordingly
  87. // TODO does this need upper constrain?
  88. NTPw.setNTPTimeout(getSetting("ntpTimeout", NTP_TIMEOUT).toInt());
  89. }
  90. void _ntpReport() {
  91. _ntp_report = false;
  92. #if WEB_SUPPORT
  93. wsSend(_ntpWebSocketOnSend);
  94. #endif
  95. if (ntpSynced()) {
  96. time_t t = now();
  97. DEBUG_MSG_P(PSTR("[NTP] UTC Time : %s\n"), ntpDateTime(ntpLocal2UTC(t)).c_str());
  98. DEBUG_MSG_P(PSTR("[NTP] Local Time: %s\n"), ntpDateTime(t).c_str());
  99. }
  100. }
  101. #if BROKER_SUPPORT
  102. void inline _ntpBroker() {
  103. static unsigned char last_minute = 60;
  104. if (ntpSynced() && (minute() != last_minute)) {
  105. last_minute = minute();
  106. brokerPublish(BROKER_MSG_TYPE_DATETIME, MQTT_TOPIC_DATETIME, ntpDateTime().c_str());
  107. }
  108. }
  109. #endif
  110. void _ntpLoop() {
  111. // Disable ntp sync when softAP is active. This will not crash, but instead spam debug-log with pointless sync failures.
  112. if (!wifiConnected()) return;
  113. if (_ntp_configure) _ntpConfigure();
  114. // NTPClientLib will trigger callback with sync status
  115. // see: NTPw.onNTPSyncEvent([](NTPSyncEvent_t error){ ... }) below
  116. if (_ntp_want_sync) {
  117. _ntp_want_sync = false;
  118. NTPw.getTime();
  119. }
  120. // Print current time whenever configuration changes or after successful sync
  121. if (_ntp_report) _ntpReport();
  122. #if BROKER_SUPPORT
  123. _ntpBroker();
  124. #endif
  125. }
  126. // TODO: remove me!
  127. void _ntpBackwards() {
  128. moveSetting("ntpServer1", "ntpServer");
  129. delSetting("ntpServer2");
  130. delSetting("ntpServer3");
  131. int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
  132. if (-30 < offset && offset < 30) {
  133. offset *= 60;
  134. setSetting("ntpOffset", offset);
  135. }
  136. }
  137. // -----------------------------------------------------------------------------
  138. bool ntpSynced() {
  139. #if NTP_WAIT_FOR_SYNC
  140. // Has synced at least once
  141. return (NTPw.getFirstSync() > 0);
  142. #else
  143. // TODO: runtime setting?
  144. return true;
  145. #endif
  146. }
  147. String ntpDateTime(time_t t) {
  148. char buffer[20];
  149. snprintf_P(buffer, sizeof(buffer),
  150. PSTR("%04d-%02d-%02d %02d:%02d:%02d"),
  151. year(t), month(t), day(t), hour(t), minute(t), second(t)
  152. );
  153. return String(buffer);
  154. }
  155. String ntpDateTime() {
  156. if (ntpSynced()) return ntpDateTime(now());
  157. return String();
  158. }
  159. // XXX: returns garbage during DST switch
  160. time_t ntpLocal2UTC(time_t local) {
  161. int offset = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt();
  162. if (NTPw.isSummerTime()) offset += 60;
  163. return local - offset * 60;
  164. }
  165. // -----------------------------------------------------------------------------
  166. void ntpSetup() {
  167. _ntpBackwards();
  168. #if TERMINAL_SUPPORT
  169. terminalRegisterCommand(F("NTP"), [](Embedis* e) {
  170. if (ntpSynced()) {
  171. _ntpReport();
  172. terminalOK();
  173. } else {
  174. DEBUG_MSG_P(PSTR("[NTP] Not synced\n"));
  175. }
  176. });
  177. terminalRegisterCommand(F("NTP.SYNC"), [](Embedis* e) {
  178. _ntpWantSync();
  179. terminalOK();
  180. });
  181. #endif
  182. NTPw.onNTPSyncEvent([](NTPSyncEvent_t error) {
  183. if (error) {
  184. #if WEB_SUPPORT
  185. wsSend_P(PSTR("{\"ntpStatus\": false}"));
  186. #endif
  187. if (error == noResponse) {
  188. DEBUG_MSG_P(PSTR("[NTP] Error: NTP server not reachable\n"));
  189. } else if (error == invalidAddress) {
  190. DEBUG_MSG_P(PSTR("[NTP] Error: Invalid NTP server address\n"));
  191. }
  192. } else {
  193. _ntp_report = true;
  194. setTime(NTPw.getLastNTPSync());
  195. }
  196. });
  197. wifiRegister([](justwifi_messages_t code, char * parameter) {
  198. if (code == MESSAGE_CONNECTED) {
  199. if (!ntpSynced()) {
  200. _ntp_defer.once_ms(secureRandom(NTP_START_DELAY, NTP_START_DELAY * 15), _ntpWantSync);
  201. }
  202. }
  203. });
  204. #if WEB_SUPPORT
  205. wsOnSendRegister(_ntpWebSocketOnSend);
  206. wsOnReceiveRegister(_ntpWebSocketOnReceive);
  207. #endif
  208. // Main callbacks
  209. espurnaRegisterLoop(_ntpLoop);
  210. espurnaRegisterReload([]() { _ntp_configure = true; });
  211. // Sets up NTP instance, installs ours sync provider
  212. _ntpStart();
  213. }
  214. #endif // NTP_SUPPORT