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.

277 lines
7.4 KiB

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