sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago sys: clean-up system-specific functions
- shrink utils source file, move heartbeat and boot management into system
- improvise with 'heartbeat' functionality. include scheduler implementation that will
manage the per-module heartbeat callbacks with individual 'mode' and
'interval' settings. current ones are mqtt (including relays, lights, thermostat), debug and
influxdb. preserve heartbeat NONE, ONCE and REPEAT, REPEAT_STATUS is effectively a hbReport & status bit.
- mqtt heartbeat is managed through mqttHeartbeat() callbacks
- tweak mqtt callbacks to use lists instead of the vector, slighly reducing the size of the .bin
- update WebUI, include report setting and update hbMode values
- make sure general.h settings include new heartbeat,
move constant definitions outside of the header
- correctly include dependencies through the .cpp, avoid leaking
internal details.
- as a side-effect, base headers are no longer included recursively
3 years ago Terminal: change command-line parser (#2247)
Change the underlying command line handling:
- switch to a custom parser, inspired by redis / sds
- update terminalRegisterCommand signature, pass only bare minimum
- clean-up `help` & `commands`. update settings `set`, `get` and `del`
- allow our custom test suite to run command-line tests
- clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`)
- send parsing errors to the debug log
As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT`
- MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API.
- Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS.
Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :)
Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case. 4 years ago Terminal: change command-line parser (#2247)
Change the underlying command line handling:
- switch to a custom parser, inspired by redis / sds
- update terminalRegisterCommand signature, pass only bare minimum
- clean-up `help` & `commands`. update settings `set`, `get` and `del`
- allow our custom test suite to run command-line tests
- clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`)
- send parsing errors to the debug log
As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT`
- MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API.
- Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS.
Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :)
Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case. 4 years ago Terminal: change command-line parser (#2247)
Change the underlying command line handling:
- switch to a custom parser, inspired by redis / sds
- update terminalRegisterCommand signature, pass only bare minimum
- clean-up `help` & `commands`. update settings `set`, `get` and `del`
- allow our custom test suite to run command-line tests
- clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`)
- send parsing errors to the debug log
As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT`
- MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API.
- Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS.
Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :)
Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case. 4 years ago Terminal: change command-line parser (#2247)
Change the underlying command line handling:
- switch to a custom parser, inspired by redis / sds
- update terminalRegisterCommand signature, pass only bare minimum
- clean-up `help` & `commands`. update settings `set`, `get` and `del`
- allow our custom test suite to run command-line tests
- clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`)
- send parsing errors to the debug log
As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT`
- MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API.
- Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS.
Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :)
Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case. 4 years ago |
|
- /*
-
- NTP MODULE
-
- Based on esp8266 / esp32 configTime and C date and time functions:
- - https://github.com/esp8266/Arduino/blob/master/libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino
- - https://www.nongnu.org/lwip/2_1_x/group__sntp.html
- - man 3 ctime
-
- Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
-
- */
-
- #include "ntp.h"
-
- #if NTP_SUPPORT && !NTP_LEGACY_SUPPORT
-
- #include <Arduino.h>
- #include <coredecls.h>
- #include <Ticker.h>
-
- #include <lwip/apps/sntp.h>
- #include <TZ.h>
-
- #include <forward_list>
-
- static_assert(
- (SNTP_SERVER_DNS == 1),
- "lwip must be configured with SNTP_SERVER_DNS"
- );
-
- #include "config/buildtime.h"
-
- #include "ntp_timelib.h"
- #include "ws.h"
-
- // Arduino/esp8266 lwip2 custom functions that can be redefined
- // Must return time in milliseconds, legacy settings are in seconds.
-
- String _ntp_server;
-
- uint32_t _ntp_startup_delay = (NTP_START_DELAY * 1000);
- uint32_t _ntp_update_delay = (NTP_UPDATE_INTERVAL * 1000);
-
- uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000() {
- return _ntp_startup_delay;
- }
-
- uint32_t sntp_update_delay_MS_rfc_not_less_than_15000() {
- return _ntp_update_delay;
- }
-
- // We also must shim TimeLib functions until everything else is ported.
- // We can't sometimes avoid TimeLib as dependancy though, which would be really bad
-
- static bool _ntp_synced = false;
-
- static time_t _ntp_last = 0;
- static time_t _ntp_ts = 0;
-
- static tm _ntp_tm_local;
- static tm _ntp_tm_utc;
-
- void _ntpTmCache(time_t ts) {
- if (_ntp_ts != ts) {
- _ntp_ts = ts;
- localtime_r(&_ntp_ts, &_ntp_tm_local);
- gmtime_r(&_ntp_ts, &_ntp_tm_utc);
- }
- }
-
- int hour(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_local.tm_hour;
- }
-
- int minute(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_local.tm_min;
- }
-
- int second(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_local.tm_sec;
- }
-
- int day(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_local.tm_mday;
- }
-
- // `tm.tm_wday` range is 0..6, TimeLib is 1..7
- int weekday(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_local.tm_wday + 1;
- }
-
- // `tm.tm_mon` range is 0..11, TimeLib range is 1..12
- int month(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_local.tm_mon + 1;
- }
-
- int year(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_local.tm_year + 1900;
- }
-
- int utc_hour(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_utc.tm_hour;
- }
-
- int utc_minute(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_utc.tm_min;
- }
-
- int utc_second(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_utc.tm_sec;
- }
-
- int utc_day(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_utc.tm_mday;
- }
-
- int utc_weekday(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_utc.tm_wday + 1;
- }
-
- int utc_month(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_utc.tm_mon + 1;
- }
-
- int utc_year(time_t ts) {
- _ntpTmCache(ts);
- return _ntp_tm_utc.tm_year + 1900;
- }
-
- time_t now() {
- return time(nullptr);
- }
-
- // -----------------------------------------------------------------------------
-
- #if WEB_SUPPORT
-
- bool _ntpWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
- return (strncmp(key, "ntp", 3) == 0);
- }
-
- void _ntpWebSocketOnVisible(JsonObject& root) {
- root["ntpVisible"] = 1;
- }
-
- void _ntpWebSocketOnData(JsonObject& root) {
- root["ntpStatus"] = ntpSynced();
- }
-
- void _ntpWebSocketOnConnected(JsonObject& root) {
- root["ntpServer"] = getSetting("ntpServer", F(NTP_SERVER));
- root["ntpTZ"] = getSetting("ntpTZ", NTP_TIMEZONE);
- }
-
- #endif
-
- String _ntpGetServer() {
- String server;
-
- server = sntp_getservername(0);
- if (!server.length()) {
- auto ip = IPAddress(sntp_getserver(0));
- if (ip) {
- server = ip.toString();
- }
- }
-
- return server;
- }
-
- NtpInfo ntpInfo() {
- NtpInfo result;
-
- auto ts = now();
- result.now = ts;
-
- tm sync_tm;
- gmtime_r(&_ntp_last, &sync_tm);
- result.sync = ntpDateTime(&sync_tm);
-
- tm utc_tm;
- gmtime_r(&ts, &utc_tm);
- result.utc = ntpDateTime(&utc_tm);
-
- const char* cfg_tz = getenv("TZ");
- if ((cfg_tz != nullptr) && (strcmp(cfg_tz, "UTC0") != 0)) {
- tm local_tm;
- localtime_r(&ts, &local_tm);
- result.local = ntpDateTime(&local_tm);
- result.tz = cfg_tz;
- }
-
- return result;
- }
-
- void _ntpReport() {
- if (!ntpSynced()) {
- DEBUG_MSG_P(PSTR("[NTP] Not synced\n"));
- return;
- }
-
- auto info = ntpInfo();
- DEBUG_MSG_P(PSTR("[NTP] Server : %s\n"), _ntp_server.c_str());
- DEBUG_MSG_P(PSTR("[NTP] Sync Time : %s (UTC)\n"), info.sync.c_str());
- DEBUG_MSG_P(PSTR("[NTP] UTC Time : %s\n"), info.utc.c_str());
-
- if (info.tz.length()) {
- DEBUG_MSG_P(PSTR("[NTP] Local Time : %s (%s)\n"), info.local.c_str(), info.tz.c_str());
- }
- }
-
- void _ntpConfigure() {
- // Ignore or accept the DHCP SNTP option
- // When enabled, it is possible that lwip will replace the NTP server pointer from under us
- sntp_servermode_dhcp(getSetting("ntpDhcp", 1 == NTP_DHCP_SERVER));
-
- // Note: TZ_... provided by the Core are already wrapped with PSTR(...)
- // but, String() already handles every char pointer as a flash-string
- auto cfg_tz = getSetting("ntpTZ", NTP_TIMEZONE);
- const char* active_tz = getenv("TZ");
-
- bool changed = cfg_tz != active_tz;
- if (changed) {
- if (cfg_tz.length()) {
- setenv("TZ", cfg_tz.c_str(), 1);
- } else {
- unsetenv("TZ");
- }
- tzset();
- }
-
- const auto cfg_server = getSetting("ntpServer", F(NTP_SERVER));
- const auto active_server = _ntpGetServer();
- changed = (cfg_server != active_server) || changed;
-
- // We skip configTime() API since we already set the TZ just above
- // (and most of the time we expect NTP server to proxy to multiple servers instead of defining more than one here)
- if (changed) {
- sntp_stop();
- _ntp_server = cfg_server;
- sntp_setservername(0, _ntp_server.c_str());
- sntp_init();
- DEBUG_MSG_P(PSTR("[NTP] Server: %s, TZ: %s\n"), cfg_server.c_str(),
- cfg_tz.length() ? cfg_tz.c_str() : "UTC0");
- }
- }
-
- // -----------------------------------------------------------------------------
-
- bool ntpSynced() {
- return _ntp_synced;
- }
-
- String ntpDateTime(tm* timestruct) {
- char buffer[32];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%04d-%02d-%02d %02d:%02d:%02d"),
- timestruct->tm_year + 1900,
- timestruct->tm_mon + 1,
- timestruct->tm_mday,
- timestruct->tm_hour,
- timestruct->tm_min,
- timestruct->tm_sec
- );
- return String(buffer);
- }
-
- String ntpDateTime(time_t ts) {
- tm timestruct;
- localtime_r(&ts, ×truct);
- return ntpDateTime(×truct);
- }
-
- String ntpDateTime() {
- if (ntpSynced()) {
- return ntpDateTime(now());
- }
- return String();
- }
-
- // -----------------------------------------------------------------------------
-
- using NtpTickCallbacks = std::forward_list<NtpTickCallback>;
- NtpTickCallbacks _ntp_tick_callbacks;
-
- static Ticker _ntp_tick;
-
- void _ntpTickSchedule(int offset);
-
- void _ntpTickCallback() {
- if (!ntpSynced()) {
- _ntpTickSchedule(60);
- return;
- }
-
- const auto ts = now();
- tm local_tm;
- localtime_r(&ts, &local_tm);
-
- int now_hour = local_tm.tm_hour;
- int now_minute = local_tm.tm_min;
-
- static int last_hour = -1;
- static int last_minute = -1;
-
- // notify subscribers about each tick interval (note that both can happen simultaneously)
- if (last_hour != now_hour) {
- last_hour = now_hour;
- for (auto& callback : _ntp_tick_callbacks) {
- callback(NtpTick::EveryHour);
- }
- }
-
- if (last_minute != now_minute) {
- last_minute = now_minute;
- for (auto& callback : _ntp_tick_callbacks) {
- callback(NtpTick::EveryMinute);
- }
- }
-
- // try to autocorrect each invocation
- _ntpTickSchedule(60 - local_tm.tm_sec);
- }
-
- // XXX: NONOS SDK docs for some reason mention 100 micros as minimum time. Schedule next second in case this is 0
- void _ntpTickSchedule(int offset) {
- static bool scheduled { false };
- if (!scheduled) {
- scheduled = true;
- _ntp_tick.once_scheduled(offset ? offset : 1, []() {
- scheduled = false;
- _ntpTickCallback();
- });
- }
- }
-
- void _ntpSetTimeOfDayCallback() {
- _ntp_synced = true;
- _ntp_last = time(nullptr);
- static bool once = true;
- if (once) {
- schedule_function(_ntpTickCallback);
- once = false;
- }
- #if WEB_SUPPORT
- wsPost(_ntpWebSocketOnData);
- #endif
- schedule_function(_ntpReport);
- }
-
- void _ntpSetTimestamp(time_t ts) {
- timeval tv { ts, 0 };
- timezone tz { 0, 0 };
- settimeofday(&tv, &tz);
- }
-
- // -----------------------------------------------------------------------------
-
- void _ntpConvertLegacyOffsets() {
- bool save { true };
- bool found { false };
-
- bool europe { true };
- bool dst { true };
- int offset { 60 };
-
- settings::kv_store.foreach([&](settings::kvs_type::KeyValueResult&& kv) {
- const auto key = kv.key.read();
- if (key == F("ntpTZ")) {
- save = false;
- } else if (key == F("ntpOffset")) {
- offset = settings::internal::convert<int>(kv.value.read());
- found = true;
- } else if (key == F("ntpDST")) {
- dst = settings::internal::convert<bool>(kv.value.read());
- found = true;
- } else if (key == F("ntpRegion")) {
- europe = (0 == settings::internal::convert<int>(kv.value.read()));
- found = true;
- }
- });
-
- if (save && found) {
- // XXX: only expect offsets in hours
- String custom { europe ? F("CET") : F("CST") };
- custom.reserve(32);
-
- if (offset > 0) {
- custom += '-';
- }
- custom += abs(offset) / 60;
-
- if (dst) {
- custom += europe ? F("CEST") : F("EDT");
- if (europe) {
- custom += F(",M3.5.0,M10.5.0/3");
- } else {
- custom += F(",M3.2.0,M11.1.0");
- }
- }
-
- setSetting("ntpTZ", custom);
- }
-
- delSetting("ntpOffset");
- delSetting("ntpDST");
- delSetting("ntpRegion");
- }
-
- void ntpOnTick(NtpTickCallback callback) {
- _ntp_tick_callbacks.push_front(callback);
- }
-
- void ntpSetup() {
-
- // Randomized in time to avoid clogging the server with simultaneous requests from multiple devices
- // (for example, when multiple devices start up at the same time)
- const uint32_t startup_delay = getSetting("ntpStartDelay", NTP_START_DELAY);
- const uint32_t update_delay = getSetting("ntpUpdateIntvl", NTP_UPDATE_INTERVAL);
-
- _ntp_startup_delay = secureRandom(startup_delay, startup_delay * 2);
- _ntp_update_delay = secureRandom(update_delay, update_delay * 2);
- DEBUG_MSG_P(PSTR("[NTP] Startup delay: %u (s), Update delay: %u (s)\n"),
- _ntp_startup_delay, _ntp_update_delay);
-
- _ntp_startup_delay = _ntp_startup_delay * 1000;
- _ntp_update_delay = _ntp_update_delay * 1000;
-
- // start up with some reasonable timestamp already available
- _ntpSetTimestamp(__UNIX_TIMESTAMP__);
-
- // will be called every time after ntp syncs AND loop() finishes
- settimeofday_cb(_ntpSetTimeOfDayCallback);
-
- // generic configuration, always handled
- espurnaRegisterReload(_ntpConfigure);
- _ntpConvertLegacyOffsets();
- _ntpConfigure();
-
- // make sure our logic does know about the actual server
- // in case dhcp sends out ntp settings
- static WiFiEventHandler on_sta = WiFi.onStationModeGotIP([](WiFiEventStationModeGotIP) {
- if (!sntp_enabled()) {
- return;
- }
-
- auto server = _ntpGetServer();
- if (!server.length()) {
- DEBUG_MSG_P(PSTR("[NTP] Updating `ntpDhcp` to ignore the DHCP values\n"));
- setSetting("ntpDhcp", "0");
- sntp_servermode_dhcp(0);
- schedule_function(_ntpConfigure);
- return;
- }
-
- if (!_ntp_server.length() || (server != _ntp_server)) {
- DEBUG_MSG_P(PSTR("[NTP] Updating `ntpServer` setting from DHCP: %s\n"), server.c_str());
- _ntp_server = server;
- setSetting("ntpServer", server);
- }
- });
-
- // optional functionality
- #if WEB_SUPPORT
- wsRegister()
- .onVisible(_ntpWebSocketOnVisible)
- .onConnected(_ntpWebSocketOnConnected)
- .onData(_ntpWebSocketOnData)
- .onKeyCheck(_ntpWebSocketOnKeyCheck);
- #endif
-
- #if TERMINAL_SUPPORT
- terminalRegisterCommand(F("NTP"), [](const terminal::CommandContext&) {
- _ntpReport();
- terminalOK();
- });
-
- terminalRegisterCommand(F("NTP.SETTIME"), [](const terminal::CommandContext& ctx) {
- if (ctx.argc != 2) return;
- _ntp_synced = true;
- _ntpSetTimestamp(ctx.argv[1].toInt());
- terminalOK();
- });
-
- // TODO:
- // terminalRegisterCommand(F("NTP.SYNC"), [](const terminal::CommandContext&) { ... }
- //
- #endif
-
- }
-
- #endif // NTP_SUPPORT && !NTP_LEGACY_SUPPORT
|