diff --git a/src/MyESP.cpp b/src/MyESP.cpp index f39406874..1d5392360 100644 --- a/src/MyESP.cpp +++ b/src/MyESP.cpp @@ -97,8 +97,9 @@ MyESP::MyESP() { // ntp _ntp_server = strdup(MYESP_NTP_SERVER); - _ntp_interval = 60; + _ntp_interval = NTP_INTERVAL_DEFAULT; _ntp_enabled = false; + _ntp_timezone = NTP_TIMEZONE_DEFAULT; // get the build time _buildTime = _getBuildTime(); @@ -251,8 +252,8 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) { // NTP now that we have a WiFi connection if (_ntp_enabled) { - NTP.Ntp(_ntp_server, _ntp_interval); // set up NTP server - myDebug_P(PSTR("[NTP] NTP internet time enabled via server %s"), _ntp_server); + NTP.Ntp(_ntp_server, _ntp_interval, _ntp_timezone); // set up NTP server + myDebug_P(PSTR("[NTP] NTP internet time enabled via server %s with timezone %d"), _ntp_server, _ntp_timezone); } // call any final custom stuff @@ -632,6 +633,27 @@ void MyESP::_telnet_setup() { memset(_command, 0, TELNET_MAX_COMMAND_LENGTH); } +// restart some services like web, mqtt, ntp etc... +void MyESP::_kick() { + myDebug_P(PSTR("Kicking services...")); + + // NTP - fetch new time + if (_ntp_enabled) { + myDebug_P(PSTR(" - Requesting NTP time")); + NTP.getNtpTime(); + } + + // kick web + myDebug_P(PSTR(" - Restart web server and web sockets")); + _ws->enable(false); + //_webServer->reset(); + _ws->enable(true); + + // kick mqtt + myDebug_P(PSTR(" - Restart MQTT")); + mqttClient.disconnect(); +} + // Show help of commands void MyESP::_consoleShowHelp() { myDebug_P(PSTR("")); @@ -652,7 +674,7 @@ void MyESP::_consoleShowHelp() { myDebug_P(PSTR("*")); myDebug_P(PSTR("* Commands:")); myDebug_P(PSTR("* ?/help=show commands, CTRL-D/quit=close telnet session")); - myDebug_P(PSTR("* set, system, restart, mqttlog")); + myDebug_P(PSTR("* set, system, restart, mqttlog, kick")); #ifdef CRASH myDebug_P(PSTR("* crash ")); @@ -696,6 +718,8 @@ void MyESP::_printSetCommands() { myDebug_P(PSTR(" set mqtt_keepalive [seconds]")); myDebug_P(PSTR(" set mqtt_retain [on | off]")); myDebug_P(PSTR(" set ntp_enabled ")); + myDebug_P(PSTR(" set ntp_interval [n]")); + myDebug_P(PSTR(" set ntp_timezone [n]")); myDebug_P(PSTR(" set serial ")); myDebug_P(PSTR(" set log_events ")); @@ -760,6 +784,9 @@ void MyESP::_printSetCommands() { #endif myDebug_P(PSTR(" ntp_enabled=%s"), (_ntp_enabled) ? "on" : "off"); + myDebug_P(PSTR(" ntp_interval=%d"), _ntp_interval); + myDebug_P(PSTR(" ntp_timezone=%d"), _ntp_timezone); + myDebug_P(PSTR(" log_events=%s"), (_general_log_events) ? "on" : "off"); // print any custom settings @@ -849,6 +876,10 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value) save_config = fs_setSettingValue(&_mqtt_heartbeat, value, false); } else if (strcmp(setting, "ntp_enabled") == 0) { save_config = fs_setSettingValue(&_ntp_enabled, value, false); + } else if (strcmp(setting, "ntp_interval") == 0) { + save_config = fs_setSettingValue(&_ntp_interval, value, NTP_INTERVAL_DEFAULT); + } else if (strcmp(setting, "ntp_timezone") == 0) { + save_config = fs_setSettingValue(&_ntp_timezone, value, NTP_TIMEZONE_DEFAULT); } else if (strcmp(setting, "log_events") == 0) { save_config = fs_setSettingValue(&_general_log_events, value, false); } else { @@ -946,6 +977,12 @@ void MyESP::_telnetCommand(char * commandLine) { return; } + // kick command + if ((strcmp(ptrToCommandName, "kick") == 0) && (wc == 1)) { + _kick(); + return; + } + // restart command if (((strcmp(ptrToCommandName, "restart") == 0) || (strcmp(ptrToCommandName, "reboot") == 0)) && (wc == 1)) { resetESP(); @@ -1282,8 +1319,9 @@ void MyESP::showSystemStats() { myDebug_P(PSTR(" [MQTT] is disconnected")); } - if (_ntp_enabled) { - myDebug_P(PSTR(" [NTP] Time in UTC is %02d:%02d:%02d"), to_hour(now()), to_minute(now()), to_second(now())); + if (_have_ntp_time) { + uint32_t real_time = getSystemTime(); + myDebug_P(PSTR(" [NTP] Local Time is %02d:%02d:%02d %s (%d)"), to_hour(real_time), to_minute(real_time), to_second(real_time), NTP.tcr->abbrev, real_time); } #ifdef CRASH @@ -1540,18 +1578,18 @@ char * MyESP::_mqttTopic(const char * topic) { // validates a file in SPIFFS, loads it into the json buffer and returns true if ok size_t MyESP::_fs_validateConfigFile(const char * filename, size_t maxsize, JsonDocument & doc) { + // myDebug_P(PSTR("[FS] Checking file %s"), filename); // remove for debugging + // see if we can open it File file = SPIFFS.open(filename, "r"); if (!file) { - myDebug_P(PSTR("[FS] File %s not found"), filename); + myDebug_P(PSTR("[FS] Cannot open config file %s for reading"), filename); return 0; } // check size size_t size = file.size(); - // myDebug_P(PSTR("[FS] Checking file %s (%d bytes)"), filename, size); // remove for debugging - if (size > maxsize) { file.close(); myDebug_P(PSTR("[FS] Error. File %s size %d is too large (max %d)"), filename, size, maxsize); @@ -1752,8 +1790,9 @@ bool MyESP::_fs_loadConfig() { _ntp_server = strdup(ntp["server"] | ""); _ntp_interval = ntp["interval"] | 60; if (_ntp_interval < 2) - _ntp_interval = 60; - _ntp_enabled = ntp["enabled"]; + _ntp_interval = NTP_INTERVAL_DEFAULT; + _ntp_enabled = ntp["enabled"]; + _ntp_timezone = ntp["timezone"] | NTP_TIMEZONE_DEFAULT; myDebug_P(PSTR("[FS] System config loaded (%d bytes)"), size); @@ -1800,6 +1839,18 @@ bool MyESP::fs_setSettingValue(uint8_t * setting, const char * value, uint8_t va return true; } +// saves a signed 8-bit integer into a config setting, using default value if non set +// returns true if successful +bool MyESP::fs_setSettingValue(int8_t * setting, const char * value, int8_t value_default) { + if (_hasValue(value)) { + *setting = (int8_t)atoi(value); + } else { + *setting = value_default; // use the default value + } + + return true; +} + // saves a bool into a config setting, using default value if non set // returns true if successful bool MyESP::fs_setSettingValue(bool * setting, const char * value, bool value_default) { @@ -1830,6 +1881,7 @@ bool MyESP::_fs_loadCustomConfig() { if (_fs_loadsave_callback_f) { const JsonObject & json = doc["settings"]; + if (!(_fs_loadsave_callback_f)(MYESP_FSACTION_LOAD, json)) { myDebug_P(PSTR("[FS] Error reading custom config")); return false; @@ -1852,6 +1904,7 @@ bool MyESP::fs_saveCustomConfig(JsonObject root) { // open for writing File configFile = SPIFFS.open(MYESP_CUSTOMCONFIG_FILE, "w"); + if (!configFile) { myDebug_P(PSTR("[FS] Failed to open custom config for writing")); ok = false; @@ -1888,15 +1941,16 @@ bool MyESP::fs_saveCustomConfig(JsonObject root) { // save system config to spiffs bool MyESP::fs_saveConfig(JsonObject root) { - bool ok = false; - // call any custom functions before handling SPIFFS if (_ota_pre_callback_f) { (_ota_pre_callback_f)(); } + bool ok = false; + // open for writing File configFile = SPIFFS.open(MYESP_CONFIG_FILE, "w"); + if (!configFile) { myDebug_P(PSTR("[FS] Failed to open system config for writing")); ok = false; @@ -1940,6 +1994,7 @@ bool MyESP::_fs_writeConfig() { general["serial"] = _general_serial; general["hostname"] = _general_hostname; general["log_events"] = _general_log_events; + general["version"] = _app_version; JsonObject mqtt = doc.createNestedObject("mqtt"); mqtt["enabled"] = _mqtt_enabled; @@ -1950,14 +2005,14 @@ bool MyESP::_fs_writeConfig() { mqtt["qos"] = _mqtt_qos; mqtt["keepalive"] = _mqtt_keepalive; mqtt["retain"] = _mqtt_retain; - - mqtt["password"] = _mqtt_password; - mqtt["base"] = _mqtt_base; + mqtt["password"] = _mqtt_password; + mqtt["base"] = _mqtt_base; JsonObject ntp = doc.createNestedObject("ntp"); ntp["server"] = _ntp_server; ntp["interval"] = _ntp_interval; ntp["enabled"] = _ntp_enabled; + ntp["timezone"] = _ntp_timezone; bool ok = fs_saveConfig(root); // save it @@ -1993,28 +2048,15 @@ void MyESP::_fs_setup() { (_ota_pre_callback_f)(); // call custom function } + // check SPIFFS is OK if (!SPIFFS.begin()) { - myDebug_P(PSTR("[FS] Formatting filesystem...")); if (SPIFFS.format()) { - if (_general_log_events) { - _writeEvent("WARN", "system", "File system formatted", ""); - } + myDebug_P(PSTR("[FS] File system formatted")); } else { myDebug_P(PSTR("[FS] Failed to format file system")); } } - - // load the main system config file if we can. Otherwise create it and expect user to configure in web interface - if (!_fs_loadConfig()) { - myDebug_P(PSTR("[FS] Creating a new system config")); - _fs_writeConfig(); // create the initial config file - } - - // load system and custom config - if (!_fs_loadCustomConfig()) { - _fs_createCustomConfig(); // create the initial config file - } - + /* // fill event log with tests SPIFFS.remove(MYESP_EVENTLOG_FILE); @@ -2039,6 +2081,17 @@ void MyESP::_fs_setup() { } } + // load the main system config file if we can. Otherwise create it and expect user to configure in web interface + if (!_fs_loadConfig()) { + myDebug_P(PSTR("[FS] Creating a new system config")); + _fs_writeConfig(); // create the initial config file + } + + // load system and custom config + if (!_fs_loadCustomConfig()) { + _fs_createCustomConfig(); // create the initial config file + } + if (_ota_post_callback_f) { (_ota_post_callback_f)(); // call custom function } @@ -2266,7 +2319,7 @@ void MyESP::_writeEvent(const char * type, const char * src, const char * desc, // this will also create the file if its doesn't exist File eventlog = SPIFFS.open(MYESP_EVENTLOG_FILE, "a"); if (!eventlog) { - //Serial.println("[SYSTEM] Error opening event log for writing"); // for debugging + // Serial.println("[SYSTEM] Error opening event log for writing"); // for debugging eventlog.close(); return; } @@ -2279,12 +2332,9 @@ void MyESP::_writeEvent(const char * type, const char * src, const char * desc, root["time"] = now(); // is relative if we're not using NTP // Serialize JSON to file - size_t n = serializeJson(root, eventlog); - eventlog.print("\n"); // this indicates end of the entry + (void)serializeJson(root, eventlog); - if (!n) { - //Serial.println("[SYSTEM] Error writing to event log"); // for debugging - } + eventlog.print("\n"); // this indicates end of the entry eventlog.close(); } @@ -2453,17 +2503,7 @@ void MyESP::_procMsg(AsyncWebSocketClient * client, size_t sz) { uint8_t page = doc["page"]; _sendEventLog(page); } else if (strcmp(command, "clearevent") == 0) { - if (_ota_pre_callback_f) { - (_ota_pre_callback_f)(); // call custom function - } - if (SPIFFS.remove(MYESP_EVENTLOG_FILE)) { - _writeEvent("WARN", "system", "Event log cleared", ""); - } else { - myDebug_P(PSTR("[WEB] Could not clear event log")); - } - if (_ota_post_callback_f) { - (_ota_post_callback_f)(); // call custom function - } + _emptyEventLog(); } else if (strcmp(command, "scan") == 0) { WiFi.scanNetworksAsync(std::bind(&MyESP::_printScanResult, this, std::placeholders::_1), true); } else if (strcmp(command, "gettime") == 0) { @@ -2480,6 +2520,22 @@ void MyESP::_procMsg(AsyncWebSocketClient * client, size_t sz) { client->_tempObject = NULL; } +// delete the event log +void MyESP::_emptyEventLog() { + if (_ota_pre_callback_f) { + (_ota_pre_callback_f)(); // call custom function + } + if (SPIFFS.remove(MYESP_EVENTLOG_FILE)) { + _writeEvent("WARN", "system", "Event log cleared", ""); + myDebug_P(PSTR("[WEB] Event log cleared")); + } else { + myDebug_P(PSTR("[WEB] Could not clear event log")); + } + if (_ota_post_callback_f) { + (_ota_post_callback_f)(); // call custom function + } +} + // read both system config and the custom config and send as json to web socket bool MyESP::_fs_sendConfig() { File configFile; @@ -2866,6 +2922,16 @@ void MyESP::_sendTime() { _ws->textAll(buffer, len); } +// returns real time from the internet/NTP if availble +// otherwise elapsed system time +unsigned long MyESP::getSystemTime() { + if (_have_ntp_time) { + return now(); + } + + return millis(); // elapsed time +} + // bootup sequence // quickly flash LED until we get a Wifi connection, or AP established void MyESP::_bootupSequence() { @@ -2964,7 +3030,8 @@ void MyESP::loop() { _heartbeatCheck(); _bootupSequence(); // see if a reset was pressed during bootup - jw.loop(); // WiFi + jw.loop(); // WiFi + ArduinoOTA.handle(); // OTA ESP.wdtFeed(); // feed the watchdog... diff --git a/src/MyESP.h b/src/MyESP.h index fbebf7a32..be5ee1525 100644 --- a/src/MyESP.h +++ b/src/MyESP.h @@ -9,7 +9,7 @@ #ifndef MyESP_h #define MyESP_h -#define MYESP_VERSION "1.2.13" +#define MYESP_VERSION "1.2.14" #include #include @@ -295,6 +295,7 @@ class MyESP { bool fs_setSettingValue(char ** setting, const char * value, const char * value_default); bool fs_setSettingValue(uint16_t * setting, const char * value, uint16_t value_default); bool fs_setSettingValue(uint8_t * setting, const char * value, uint8_t value_default); + bool fs_setSettingValue(int8_t * setting, const char * value, int8_t value_default); bool fs_setSettingValue(bool * setting, const char * value, bool value_default); // Web @@ -306,17 +307,18 @@ class MyESP { void crashInfo(); // general - void end(); - void loop(); - void begin(const char * app_hostname, const char * app_name, const char * app_version, const char * app_url, const char * app_url_api); - void resetESP(); - int getWifiQuality(); - void showSystemStats(); - bool getHeartbeat(); - uint32_t getSystemLoadAverage(); - uint32_t getSystemResetReason(); - uint8_t getSystemBootStatus(); - bool _have_ntp_time; + void end(); + void loop(); + void begin(const char * app_hostname, const char * app_name, const char * app_version, const char * app_url, const char * app_url_api); + void resetESP(); + int getWifiQuality(); + void showSystemStats(); + bool getHeartbeat(); + uint32_t getSystemLoadAverage(); + uint32_t getSystemResetReason(); + uint8_t getSystemBootStatus(); + bool _have_ntp_time; + unsigned long getSystemTime(); private: // mqtt @@ -418,6 +420,7 @@ class MyESP { char * _getBuildTime(); bool _hasValue(const char * s); void _printHeap(const char * s); + void _kick(); // reset reason and rtcmem bool _rtcmem_status; @@ -464,6 +467,7 @@ class MyESP { // log void _sendEventLog(uint8_t page); + void _emptyEventLog(); // web void _onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len); @@ -481,6 +485,7 @@ class MyESP { char * _ntp_server; uint8_t _ntp_interval; bool _ntp_enabled; + uint8_t _ntp_timezone; }; extern MyESP myESP; diff --git a/src/Ntp.cpp b/src/Ntp.cpp index 679bbae7b..5b294ee40 100644 --- a/src/Ntp.cpp +++ b/src/Ntp.cpp @@ -3,16 +3,75 @@ */ #include "Ntp.h" +#include "MyESP.h" -char * NtpClient::TimeServerName; -time_t NtpClient::syncInterval; -IPAddress NtpClient::timeServer; -AsyncUDP NtpClient::udpListener; -byte NtpClient::NTPpacket[NTP_PACKET_SIZE]; +char * NtpClient::TimeServerName; +Timezone * NtpClient::tz; +TimeChangeRule * NtpClient::tcr; +time_t NtpClient::syncInterval; +IPAddress NtpClient::timeServer; +AsyncUDP NtpClient::udpListener; +byte NtpClient::NTPpacket[NTP_PACKET_SIZE]; -void ICACHE_FLASH_ATTR NtpClient::Ntp(const char * server, time_t syncMins) { +// Australia Eastern Time Zone (Sydney, Melbourne) +TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660}; // UTC + 11 hours +TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600}; // UTC + 10 hours +Timezone ausET(aEDT, aEST); + +// Moscow Standard Time (MSK, does not observe DST) +TimeChangeRule msk = {"MSK", Last, Sun, Mar, 1, 180}; +Timezone tzMSK(msk); + +// Central European Time (Frankfurt, Paris) +TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time +TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time +Timezone CE(CEST, CET); + +// United Kingdom (London, Belfast) +TimeChangeRule BST = {"BST", Last, Sun, Mar, 1, 60}; // British Summer Time +TimeChangeRule GMT = {"GMT", Last, Sun, Oct, 2, 0}; // Standard Time +Timezone UK(BST, GMT); + +// UTC +TimeChangeRule utcRule = {"UTC", Last, Sun, Mar, 1, 0}; // UTC +Timezone UTC(utcRule); + +// US Eastern Time Zone (New York, Detroit) +TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240}; // Eastern Daylight Time = UTC - 4 hours +TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300}; // Eastern Standard Time = UTC - 5 hours +Timezone usET(usEDT, usEST); + +// US Central Time Zone (Chicago, Houston) +TimeChangeRule usCDT = {"CDT", Second, Sun, Mar, 2, -300}; +TimeChangeRule usCST = {"CST", First, Sun, Nov, 2, -360}; +Timezone usCT(usCDT, usCST); + +// US Mountain Time Zone (Denver, Salt Lake City) +TimeChangeRule usMDT = {"MDT", Second, Sun, Mar, 2, -360}; +TimeChangeRule usMST = {"MST", First, Sun, Nov, 2, -420}; +Timezone usMT(usMDT, usMST); + +// Arizona is US Mountain Time Zone but does not use DST +Timezone usAZ(usMST); + +// US Pacific Time Zone (Las Vegas, Los Angeles) +TimeChangeRule usPDT = {"PDT", Second, Sun, Mar, 2, -420}; +TimeChangeRule usPST = {"PST", First, Sun, Nov, 2, -480}; +Timezone usPT(usPDT, usPST); + +// build index of all timezones +Timezone * timezones[] = {&ausET, &tzMSK, &CE, &UK, &UTC, &usET, &usCT, &usMT, &usAZ, &usPT}; + +void ICACHE_FLASH_ATTR NtpClient::Ntp(const char * server, time_t syncMins, uint8_t tz_index) { TimeServerName = strdup(server); syncInterval = syncMins * 60; // convert to seconds + + // check for out of bounds + if (tz_index > NTP_TIMEZONE_MAX) { + tz_index = NTP_TIMEZONE_DEFAULT; + } + tz = timezones[tz_index]; // set timezone + WiFi.hostByName(TimeServerName, timeServer); setSyncProvider(getNtpTime); setSyncInterval(syncInterval); @@ -35,10 +94,23 @@ time_t ICACHE_FLASH_ATTR NtpClient::getNtpTime() { NTPpacket[15] = 52; if (udpListener.connect(timeServer, 123)) { udpListener.onPacket([](AsyncUDPPacket packet) { - unsigned long highWord = word(packet.data()[40], packet.data()[41]); - unsigned long lowWord = word(packet.data()[42], packet.data()[43]); - time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL; - setTime(UnixUTCtime); + unsigned long highWord = word(packet.data()[40], packet.data()[41]); + unsigned long lowWord = word(packet.data()[42], packet.data()[43]); + time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL; + time_t adjustedtime = (*tz).toLocal(UnixUTCtime, &tcr); + + myESP.myDebug("[NTP] UTC internet time is %d/%d %02d:%02d:%02d. System time set to local timezone which is %02d:%02d:%02d %s\n", + to_day(UnixUTCtime), + to_month(UnixUTCtime), + to_hour(UnixUTCtime), + to_minute(UnixUTCtime), + to_second(UnixUTCtime), + to_hour(adjustedtime), + to_minute(adjustedtime), + to_second(adjustedtime), + tcr->abbrev); + + setTime(adjustedtime); }); } udpListener.write(NTPpacket, sizeof(NTPpacket)); diff --git a/src/Ntp.h b/src/Ntp.h index 7eca60b9e..03bb88946 100644 --- a/src/Ntp.h +++ b/src/Ntp.h @@ -10,18 +10,24 @@ #include #include -#include "TimeLib.h" // customized version of the time library +#include "TimeLib.h" // customized version of the Time library +#include "Timezone.h" #define NTP_PACKET_SIZE 48 // NTP time is in the first 48 bytes of message +#define NTP_INTERVAL_DEFAULT 60 // every hour +#define NTP_TIMEZONE_DEFAULT 2 // CE +#define NTP_TIMEZONE_MAX 9 class NtpClient { public: - void ICACHE_FLASH_ATTR Ntp(const char * server, time_t syncMins); + void ICACHE_FLASH_ATTR Ntp(const char * server, time_t syncMins, uint8_t tz_index); ICACHE_FLASH_ATTR virtual ~NtpClient(); - static char * TimeServerName; - static IPAddress timeServer; - static time_t syncInterval; + static char * TimeServerName; + static IPAddress timeServer; + static time_t syncInterval; + static Timezone * tz; + static TimeChangeRule * tcr; static AsyncUDP udpListener; diff --git a/src/TimeLib.cpp b/src/TimeLib.cpp index e59b24dc2..0595323c4 100644 --- a/src/TimeLib.cpp +++ b/src/TimeLib.cpp @@ -1,3 +1,8 @@ +/* + * customized version of Time library, originally Copyright (c) Michael Margolis 2009-2014 + * modified by Paul S https://github.com/PaulStoffregen/Time + */ + #include "TimeLib.h" static tmElements_t tm; // a cache of time elements @@ -96,6 +101,34 @@ void breakTime(time_t timeInput, tmElements_t & tm) { tm.Day = time + 1; // day of month } +// assemble time elements into time_t +time_t makeTime(const tmElements_t & tm) { + int i; + uint32_t seconds; + + // seconds from 1970 till 1 jan 00:00:00 of the given year + seconds = tm.Year * (SECS_PER_DAY * 365); + for (i = 0; i < tm.Year; i++) { + if (LEAP_YEAR(i)) { + seconds += SECS_PER_DAY; // add extra days for leap years + } + } + + // add days for this year, months start from 1 + for (i = 1; i < tm.Month; i++) { + if ((i == 2) && LEAP_YEAR(tm.Year)) { + seconds += SECS_PER_DAY * 29; + } else { + seconds += SECS_PER_DAY * monthDays[i - 1]; // monthDay array starts from 0 + } + } + seconds += (tm.Day - 1) * SECS_PER_DAY; + seconds += tm.Hour * SECS_PER_HOUR; + seconds += tm.Minute * SECS_PER_MIN; + seconds += tm.Second; + return (time_t)seconds; +} + void refreshCache(time_t t) { if (t != cacheTime) { breakTime(t, tm); @@ -118,6 +151,26 @@ uint8_t to_hour(time_t t) { // the hour for the given time return tm.Hour; } +uint8_t to_day(time_t t) { // the day for the given time (0-6) + refreshCache(t); + return tm.Day; +} + +uint8_t to_month(time_t t) { // the month for the given time + refreshCache(t); + return tm.Month; +} + +uint8_t to_weekday(time_t t) { + refreshCache(t); + return tm.Wday; +} + +uint16_t to_year(time_t t) { // the year for the given time + refreshCache(t); + return tm.Year + 1970; +} + void setTime(time_t t) { sysTime = (uint32_t)t; nextSyncTime = (uint32_t)t + syncInterval; diff --git a/src/TimeLib.h b/src/TimeLib.h index 0531e270a..604caa0e1 100644 --- a/src/TimeLib.h +++ b/src/TimeLib.h @@ -1,3 +1,8 @@ +/* + * customized version of Time library, originally Copyright (c) Michael Margolis 2009-2014 + * modified by Paul S https://github.com/PaulStoffregen/Time + */ + #ifndef _TimeLib_h #define _TimeLib_h @@ -38,8 +43,13 @@ void setSyncProvider(getExternalTime getTimeFunction); // identify the e void setSyncInterval(time_t interval); // set the number of seconds between re-sync time_t makeTime(const tmElements_t & tm); // convert time elements into time_t -uint8_t to_hour(time_t t); // the hour for the given time -uint8_t to_minute(time_t t); // the minute for the given time -uint8_t to_second(time_t t); // the second for the given time +uint8_t to_hour(time_t t); // the hour for the given time +uint8_t to_minute(time_t t); // the minute for the given time +uint8_t to_second(time_t t); // the second for the given time +uint8_t to_day(time_t t); // the day for the given time (0-6) +uint8_t to_month(time_t t); // the month for the given time +uint8_t to_weekday(time_t t); // weekday, sunday is day 1 +uint16_t to_year(time_t t); // the year for the given time + } #endif diff --git a/src/Timezone.cpp b/src/Timezone.cpp new file mode 100644 index 000000000..4aaa88e2b --- /dev/null +++ b/src/Timezone.cpp @@ -0,0 +1,200 @@ +/*----------------------------------------------------------------------* + * Arduino Timezone Library * + * Jack Christensen Mar 2012 * + * * + * Stripped down for myESP by Paul Derbyshire * + * * + * Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and * + * licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html * + *----------------------------------------------------------------------*/ + +#include "Timezone.h" + +/*----------------------------------------------------------------------* + * Create a Timezone object from the given time change rules. * + *----------------------------------------------------------------------*/ +Timezone::Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart) + : m_dst(dstStart) + , m_std(stdStart) { + initTimeChanges(); +} + +/*----------------------------------------------------------------------* + * Create a Timezone object for a zone that does not observe * + * daylight time. * + *----------------------------------------------------------------------*/ +Timezone::Timezone(TimeChangeRule stdTime) + : m_dst(stdTime) + , m_std(stdTime) { + initTimeChanges(); +} + +/*----------------------------------------------------------------------* + * Convert the given UTC time to local time, standard or * + * daylight time, as appropriate. * + *----------------------------------------------------------------------*/ +time_t Timezone::toLocal(time_t utc) { + // recalculate the time change points if needed + if (to_year(utc) != to_year(m_dstUTC)) + calcTimeChanges(to_year(utc)); + + if (utcIsDST(utc)) + return utc + m_dst.offset * SECS_PER_MIN; + else + return utc + m_std.offset * SECS_PER_MIN; +} + +/*----------------------------------------------------------------------* + * Convert the given UTC time to local time, standard or * + * daylight time, as appropriate, and return a pointer to the time * + * change rule used to do the conversion. The caller must take care * + * not to alter this rule. * + *----------------------------------------------------------------------*/ +time_t Timezone::toLocal(time_t utc, TimeChangeRule ** tcr) { + // recalculate the time change points if needed + if (to_year(utc) != to_year(m_dstUTC)) + calcTimeChanges(to_year(utc)); + + if (utcIsDST(utc)) { + *tcr = &m_dst; + return utc + m_dst.offset * SECS_PER_MIN; + } else { + *tcr = &m_std; + return utc + m_std.offset * SECS_PER_MIN; + } +} + +/*----------------------------------------------------------------------* + * Convert the given local time to UTC time. * + * * + * WARNING: * + * This function is provided for completeness, but should seldom be * + * needed and should be used sparingly and carefully. * + * * + * Ambiguous situations occur after the Standard-to-DST and the * + * DST-to-Standard time transitions. When changing to DST, there is * + * one hour of local time that does not exist, since the clock moves * + * forward one hour. Similarly, when changing to standard time, there * + * is one hour of local times that occur twice since the clock moves * + * back one hour. * + * * + * This function does not test whether it is passed an erroneous time * + * value during the Local -> DST transition that does not exist. * + * If passed such a time, an incorrect UTC time value will be returned. * + * * + * If passed a local time value during the DST -> Local transition * + * that occurs twice, it will be treated as the earlier time, i.e. * + * the time that occurs before the transistion. * + * * + * Calling this function with local times during a transition interval * + * should be avoided! * + *----------------------------------------------------------------------*/ +time_t Timezone::toUTC(time_t local) { + // recalculate the time change points if needed + if (to_year(local) != to_year(m_dstLoc)) + calcTimeChanges(to_year(local)); + + if (locIsDST(local)) + return local - m_dst.offset * SECS_PER_MIN; + else + return local - m_std.offset * SECS_PER_MIN; +} + +/*----------------------------------------------------------------------* + * Determine whether the given UTC time_t is within the DST interval * + * or the Standard time interval. * + *----------------------------------------------------------------------*/ +bool Timezone::utcIsDST(time_t utc) { + // recalculate the time change points if needed + if (to_year(utc) != to_year(m_dstUTC)) + calcTimeChanges(to_year(utc)); + + if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz + return false; + else if (m_stdUTC > m_dstUTC) // northern hemisphere + return (utc >= m_dstUTC && utc < m_stdUTC); + else // southern hemisphere + return !(utc >= m_stdUTC && utc < m_dstUTC); +} + +/*----------------------------------------------------------------------* + * Determine whether the given Local time_t is within the DST interval * + * or the Standard time interval. * + *----------------------------------------------------------------------*/ +bool Timezone::locIsDST(time_t local) { + // recalculate the time change points if needed + if (to_year(local) != to_year(m_dstLoc)) + calcTimeChanges(to_year(local)); + + if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz + return false; + else if (m_stdLoc > m_dstLoc) // northern hemisphere + return (local >= m_dstLoc && local < m_stdLoc); + else // southern hemisphere + return !(local >= m_stdLoc && local < m_dstLoc); +} + +/*----------------------------------------------------------------------* + * Calculate the DST and standard time change points for the given * + * given year as local and UTC time_t values. * + *----------------------------------------------------------------------*/ +void Timezone::calcTimeChanges(int yr) { + m_dstLoc = toTime_t(m_dst, yr); + m_stdLoc = toTime_t(m_std, yr); + m_dstUTC = m_dstLoc - m_std.offset * SECS_PER_MIN; + m_stdUTC = m_stdLoc - m_dst.offset * SECS_PER_MIN; +} + +/*----------------------------------------------------------------------* + * Initialize the DST and standard time change points. * + *----------------------------------------------------------------------*/ +void Timezone::initTimeChanges() { + m_dstLoc = 0; + m_stdLoc = 0; + m_dstUTC = 0; + m_stdUTC = 0; +} + +/*----------------------------------------------------------------------* + * Convert the given time change rule to a time_t value * + * for the given year. * + *----------------------------------------------------------------------*/ +time_t Timezone::toTime_t(TimeChangeRule r, int yr) { + uint8_t m = r.month; // temp copies of r.month and r.week + uint8_t w = r.week; + if (w == 0) // is this a "Last week" rule? + { + if (++m > 12) // yes, for "Last", go to the next month + { + m = 1; + ++yr; + } + w = 1; // and treat as first week of next month, subtract 7 days later + } + + // calculate first day of the month, or for "Last" rules, first day of the next month + tmElements_t tm; + tm.Hour = r.hour; + tm.Minute = 0; + tm.Second = 0; + tm.Day = 1; + tm.Month = m; + tm.Year = yr - 1970; + time_t t = makeTime(tm); + + // add offset from the first of the month to r.dow, and offset for the given week + t += ((r.dow - to_weekday(t) + 7) % 7 + (w - 1) * 7) * SECS_PER_DAY; + // back up a week if this is a "Last" rule + if (r.week == 0) + t -= 7 * SECS_PER_DAY; + return t; +} + +/*----------------------------------------------------------------------* + * Read or update the daylight and standard time rules from RAM. * + *----------------------------------------------------------------------*/ +void Timezone::setRules(TimeChangeRule dstStart, TimeChangeRule stdStart) { + m_dst = dstStart; + m_std = stdStart; + initTimeChanges(); // force calcTimeChanges() at next conversion call +} diff --git a/src/Timezone.h b/src/Timezone.h new file mode 100644 index 000000000..2e8a93248 --- /dev/null +++ b/src/Timezone.h @@ -0,0 +1,53 @@ +/*----------------------------------------------------------------------* + * Arduino Timezone Library * + * Jack Christensen Mar 2012 * + * * + * Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and * + * licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html * + *----------------------------------------------------------------------*/ + +#ifndef TIMEZONE_H_INCLUDED +#define TIMEZONE_H_INCLUDED +#include +#include + +// convenient constants for TimeChangeRules +enum week_t { Last, First, Second, Third, Fourth }; +enum dow_t { Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat }; +enum month_t { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec }; + +// structure to describe rules for when daylight/summer time begins, +// or when standard time begins. +struct TimeChangeRule { + char abbrev[6]; // five chars max + uint8_t week; // First, Second, Third, Fourth, or Last week of the month + uint8_t dow; // day of week, 1=Sun, 2=Mon, ... 7=Sat + uint8_t month; // 1=Jan, 2=Feb, ... 12=Dec + uint8_t hour; // 0-23 + int offset; // offset from UTC in minutes +}; + +class Timezone { + public: + Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart); + Timezone(TimeChangeRule stdTime); + time_t toLocal(time_t utc); + time_t toLocal(time_t utc, TimeChangeRule ** tcr); + time_t toUTC(time_t local); + bool utcIsDST(time_t utc); + bool locIsDST(time_t local); + void setRules(TimeChangeRule dstStart, TimeChangeRule stdStart); + + private: + void calcTimeChanges(int yr); + void initTimeChanges(); + time_t toTime_t(TimeChangeRule r, int yr); + TimeChangeRule m_dst; // rule for start of dst or summer time for any year + TimeChangeRule m_std; // rule for start of standard time for any year + time_t m_dstUTC; // dst start for given/current year, given in UTC + time_t m_stdUTC; // std time start for given/current year, given in UTC + time_t m_dstLoc; // dst start for given/current year, given in local time + time_t m_stdLoc; // std time start for given/current year, given in local time +}; + +#endif diff --git a/src/ems-esp.cpp b/src/ems-esp.cpp index 9db85400d..c5773827f 100644 --- a/src/ems-esp.cpp +++ b/src/ems-esp.cpp @@ -70,7 +70,6 @@ Ticker showerColdShotStopTimer; #define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water // set this if using an external temperature sensor like a DS18B20 -// D5 is the default on a bbqkees board #define EMSESP_DALLAS_GPIO D5 #define EMSESP_DALLAS_PARASITE false @@ -80,8 +79,7 @@ Ticker showerColdShotStopTimer; #define EMSESP_LED_GPIO LED_BUILTIN typedef struct { - uint32_t timestamp; // for internal timings, via millis() - uint8_t dallas_sensors; // count of dallas sensors + uint8_t dallas_sensors; // count of dallas sensors // custom params bool shower_timer; // true if we want to report back on shower times @@ -930,7 +928,7 @@ void do_systemCheck() { // only if we have a EMS connection void do_regularUpdates() { if (ems_getBusConnected() && !ems_getTxDisabled()) { - myDebugLog("Starting scheduled query from EMS devices"); + myDebugLog("Fetching data from EMS devices"); ems_getThermostatValues(); ems_getBoilerValues(); ems_getSolarModuleValues(); @@ -1917,7 +1915,6 @@ void initEMSESP() { EMSESP_Settings.listen_mode = false; EMSESP_Settings.publish_time = DEFAULT_PUBLISHTIME; EMSESP_Settings.publish_always = false; - EMSESP_Settings.timestamp = millis(); EMSESP_Settings.dallas_sensors = 0; EMSESP_Settings.led_gpio = EMSESP_LED_GPIO; EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO; @@ -1937,6 +1934,7 @@ void initEMSESP() { * Shower Logic */ void showerCheck() { + uint32_t time_now = millis(); // if already in cold mode, ignore all this logic until we're out of the cold blast if (!EMSESP_Shower.doingColdShot) { // is the hot water running? @@ -1944,7 +1942,7 @@ void showerCheck() { // if heater was previously off, start the timer if (EMSESP_Shower.timerStart == 0) { // hot water just started... - EMSESP_Shower.timerStart = EMSESP_Settings.timestamp; + EMSESP_Shower.timerStart = time_now; EMSESP_Shower.timerPause = 0; // remove any last pauses EMSESP_Shower.doingColdShot = false; EMSESP_Shower.duration = 0; @@ -1952,13 +1950,12 @@ void showerCheck() { } else { // hot water has been on for a while // first check to see if hot water has been on long enough to be recognized as a Shower/Bath - if (!EMSESP_Shower.showerOn && (EMSESP_Settings.timestamp - EMSESP_Shower.timerStart) > SHOWER_MIN_DURATION) { + if (!EMSESP_Shower.showerOn && (time_now - EMSESP_Shower.timerStart) > SHOWER_MIN_DURATION) { EMSESP_Shower.showerOn = true; myDebugLog("[Shower] hot water still running, starting shower timer"); } // check if the shower has been on too long - else if ((((EMSESP_Settings.timestamp - EMSESP_Shower.timerStart) > SHOWER_MAX_DURATION) && !EMSESP_Shower.doingColdShot) - && EMSESP_Settings.shower_alert) { + else if ((((time_now - EMSESP_Shower.timerStart) > SHOWER_MAX_DURATION) && !EMSESP_Shower.doingColdShot) && EMSESP_Settings.shower_alert) { myDebugLog("[Shower] exceeded max shower time"); _showerColdShotStart(); } @@ -1966,11 +1963,11 @@ void showerCheck() { } else { // hot water is off // if it just turned off, record the time as it could be a short pause if ((EMSESP_Shower.timerStart) && (EMSESP_Shower.timerPause == 0)) { - EMSESP_Shower.timerPause = EMSESP_Settings.timestamp; + EMSESP_Shower.timerPause = time_now; } // if shower has been off for longer than the wait time - if ((EMSESP_Shower.timerPause) && ((EMSESP_Settings.timestamp - EMSESP_Shower.timerPause) > SHOWER_PAUSE_TIME)) { + if ((EMSESP_Shower.timerPause) && ((time_now - EMSESP_Shower.timerPause) > SHOWER_PAUSE_TIME)) { // it is over the wait period, so assume that the shower has finished and calculate the total time and publish // because its unsigned long, can't have negative so check if length is less than OFFSET_TIME if ((EMSESP_Shower.timerPause - EMSESP_Shower.timerStart) > SHOWER_OFFSET_TIME) { @@ -2062,8 +2059,6 @@ void setup() { // Main loop // void loop() { - EMSESP_Settings.timestamp = millis(); - // the main loop myESP.loop(); diff --git a/src/ems.cpp b/src/ems.cpp index 285c8c6ce..0ec2b1b7c 100644 --- a/src/ems.cpp +++ b/src/ems.cpp @@ -519,7 +519,6 @@ void ems_setTxMode(uint8_t mode) { EMS_Sys_Status.emsTxMode = mode; } - /** * debug print a telegram to telnet/serial including the CRC */ @@ -530,18 +529,42 @@ void _debugPrintTelegram(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram, uint8_t data_len = EMS_RxTelegram->data_length; // length of data block uint8_t length = EMS_RxTelegram->length; // includes CRC + // get elapsed system time or internet time if available + uint8_t t_sec, t_min, t_hour; + uint16_t t_msec; + unsigned long timestamp = EMS_RxTelegram->timestamp; + bool haveNTPtime = (timestamp > 1572307205); // after Jan 1st 1970 + + if (haveNTPtime) { + t_sec = timestamp % 60; + timestamp /= 60; // now it is minutes + t_min = timestamp % 60; + timestamp /= 60; // now it is hours + t_hour = timestamp % 24; + } else { + t_hour = timestamp / 3600000; + t_min = (timestamp / 60000) % 60; + t_sec = (timestamp / 1000) % 60; + t_msec = timestamp % 1000; + } + strlcpy(output_str, "(", sizeof(output_str)); strlcat(output_str, COLOR_CYAN, sizeof(output_str)); - strlcat(output_str, _smallitoa((uint8_t)((EMS_RxTelegram->timestamp / 3600000) % 24), buffer), sizeof(output_str)); + + strlcat(output_str, _smallitoa(t_hour, buffer), sizeof(output_str)); strlcat(output_str, ":", sizeof(output_str)); - strlcat(output_str, _smallitoa((uint8_t)((EMS_RxTelegram->timestamp / 60000) % 60), buffer), sizeof(output_str)); + strlcat(output_str, _smallitoa(t_min, buffer), sizeof(output_str)); strlcat(output_str, ":", sizeof(output_str)); - strlcat(output_str, _smallitoa((uint8_t)((EMS_RxTelegram->timestamp / 1000) % 60), buffer), sizeof(output_str)); - strlcat(output_str, ".", sizeof(output_str)); - strlcat(output_str, _smallitoa3(EMS_RxTelegram->timestamp % 1000, buffer), sizeof(output_str)); + strlcat(output_str, _smallitoa(t_sec, buffer), sizeof(output_str)); + + // internet time doesn't have millisecond precision, so ignore it + if (!haveNTPtime) { + strlcat(output_str, ".", sizeof(output_str)); + strlcat(output_str, _smallitoa3(t_msec, buffer), sizeof(output_str)); + } + strlcat(output_str, COLOR_RESET, sizeof(output_str)); strlcat(output_str, ") ", sizeof(output_str)); - strlcat(output_str, color, sizeof(output_str)); strlcat(output_str, prefix, sizeof(output_str)); @@ -605,8 +628,8 @@ void _ems_sendTelegram() { _EMS_RxTelegram EMS_RxTelegram; // create new Rx object EMS_RxTelegram.length = EMS_TxTelegram.length; // full length of telegram EMS_RxTelegram.telegram = EMS_TxTelegram.data; - EMS_RxTelegram.data_length = 0; // ignore #data= - EMS_RxTelegram.timestamp = millis(); // now + EMS_RxTelegram.data_length = 0; // ignore #data= + EMS_RxTelegram.timestamp = myESP.getSystemTime(); // now _debugPrintTelegram("Sending raw: ", &EMS_RxTelegram, COLOR_CYAN, true); } @@ -677,7 +700,7 @@ void _ems_sendTelegram() { EMS_RxTelegram.length = EMS_TxTelegram.length; // complete length of telegram incl CRC EMS_RxTelegram.data_length = 0; // ignore the data length for read and writes. only used for incoming. EMS_RxTelegram.telegram = EMS_TxTelegram.data; - EMS_RxTelegram.timestamp = millis(); // now + EMS_RxTelegram.timestamp = myESP.getSystemTime(); // now _debugPrintTelegram(s, &EMS_RxTelegram, COLOR_CYAN); } @@ -880,7 +903,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) { static _EMS_RxTelegram EMS_RxTelegram; // create the Rx package EMS_RxTelegram.telegram = telegram; - EMS_RxTelegram.timestamp = millis(); + EMS_RxTelegram.timestamp = myESP.getSystemTime(); EMS_RxTelegram.length = length; EMS_RxTelegram.src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here @@ -939,7 +962,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) { // here we know its a valid incoming telegram of at least 6 bytes // we use this to see if we always have a connection to the boiler, in case of drop outs - EMS_Sys_Status.emsRxTimestamp = EMS_RxTelegram.timestamp; // timestamp of last read + EMS_Sys_Status.emsRxTimestamp = millis(); // timestamp of last read EMS_Sys_Status.emsBusConnected = true; // now lets process it and see what to do next @@ -1526,7 +1549,7 @@ void _process_RCPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { // manual : 10 00 FF 0A 01 A5 02 // auto : Thermostat -> all, type 0x01A5 telegram: 10 00 FF 0A 01 A5 03 - // TODO this may be bit 2 instead of 1 on an RC300 - still to validate + // Note this may be bit 2 instead of 1 on an RC300 - still to validate EMS_Thermostat.hc[hc].mode = _bitRead(0, 0); // bit 1, mode (auto=1 or manual=0) EMS_Thermostat.hc[hc].day_mode = _bitRead(0, 1); // get day mode flag @@ -2791,6 +2814,7 @@ void ems_sendRawTelegram(char * telegram) { */ void ems_setThermostatTemp(float temperature, uint8_t hc_num, uint8_t temptype) { if (!ems_getThermostatEnabled()) { + myDebug_P(PSTR("Thermostat not online.")); return; } @@ -2905,6 +2929,7 @@ void ems_setThermostatTemp(float temperature, uint8_t hc_num, uint8_t temptype) */ void ems_setThermostatMode(uint8_t mode, uint8_t hc_num) { if (!ems_getThermostatEnabled()) { + myDebug_P(PSTR("Thermostat not online.")); return; } diff --git a/src/ems.h b/src/ems.h index fa52f1373..25dcac11b 100644 --- a/src/ems.h +++ b/src/ems.h @@ -147,23 +147,23 @@ typedef struct { uint8_t comparisonOffset; // offset of where the byte is we want to compare too during validation uint16_t comparisonPostRead; // after a successful write, do a read from this type ID bool forceRefresh; // should we send to MQTT after a successful Tx? - uint32_t timestamp; // when created + unsigned long timestamp; // when created uint8_t data[EMS_MAX_TELEGRAM_LENGTH]; } _EMS_TxTelegram; // The Rx receive package typedef struct { - uint32_t timestamp; // timestamp from millis() - uint8_t * telegram; // the full data package - uint8_t data_length; // length in bytes of the data - uint8_t length; // full length of the complete telegram - uint8_t src; // source ID - uint8_t dest; // destination ID - uint16_t type; // type ID as a double byte to support EMS+ - uint8_t offset; // offset - uint8_t * data; // pointer to where telegram data starts - bool emsplus; // true if ems+/ems 2.0 - uint8_t emsplus_type; // FF, F7 or F9 + unsigned long timestamp; // timestamp from millis() + uint8_t * telegram; // the full data package + uint8_t data_length; // length in bytes of the data + uint8_t length; // full length of the complete telegram + uint8_t src; // source ID + uint8_t dest; // destination ID + uint16_t type; // type ID as a double byte to support EMS+ + uint8_t offset; // offset + uint8_t * data; // pointer to where telegram data starts + bool emsplus; // true if ems+/ems 2.0 + uint8_t emsplus_type; // FF, F7 or F9 } _EMS_RxTelegram; // default empty Tx, must match struct diff --git a/src/websrc/myesp.htm b/src/websrc/myesp.htm index 88680f64a..81ffcd160 100644 --- a/src/websrc/myesp.htm +++ b/src/websrc/myesp.htm @@ -391,10 +391,29 @@ aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right" data-content="Time in minutes between scheduled NTP refreshes"> - +
+ +
+ + + + +
+
diff --git a/src/websrc/myesp.js b/src/websrc/myesp.js index 6a69766b4..9379ee7b0 100644 --- a/src/websrc/myesp.js +++ b/src/websrc/myesp.js @@ -2,7 +2,7 @@ var version = ""; var websock = null; var wsUri = "ws://" + window.location.host + "/ws"; -var utcSeconds; +var ntpSeconds; var data = []; var ajaxobj; @@ -37,7 +37,8 @@ var config = { "hostname": "", "serial": false, "password": "admin", - "log_events": true + "log_events": true, + "version": "1.0.0" }, "mqtt": { "enabled": false, @@ -53,13 +54,14 @@ var config = { }, "ntp": { "server": "pool.ntp.org", - "interval": 30, + "interval": 60, + "timezone": 2, "enabled": true } }; function browserTime() { - var d = new Date(0); + var d = new Date(0); // The 0 there is the key, which sets the date to the epoch var c = new Date(); var timestamp = Math.floor((c.getTime() / 1000) + ((c.getTimezoneOffset() * 60) * -1)); d.setUTCSeconds(timestamp); @@ -67,10 +69,8 @@ function browserTime() { } function deviceTime() { - var c = new Date(); var t = new Date(0); // The 0 there is the key, which sets the date to the epoch - var devTime = Math.floor(utcSeconds + ((c.getTimezoneOffset() * 60) * -1)); - t.setUTCSeconds(devTime); + t.setUTCSeconds(ntpSeconds); document.getElementById("utc").innerHTML = t.toUTCString().slice(0, -3); } @@ -96,7 +96,8 @@ function listntp() { websock.send("{\"command\":\"gettime\"}"); document.getElementById("ntpserver").value = config.ntp.server; - document.getElementById("intervals").value = config.ntp.interval; + document.getElementById("interval").value = config.ntp.interval; + document.getElementById("timezone").value = config.ntp.timezone; if (config.ntp.enabled) { $("input[name=\"ntpenabled\"][value=\"1\"]").prop("checked", true); @@ -137,7 +138,8 @@ function custom_saveconfig() { function saventp() { config.ntp.server = document.getElementById("ntpserver").value; - config.ntp.interval = parseInt(document.getElementById("intervals").value); + config.ntp.interval = parseInt(document.getElementById("interval").value); + config.ntp.timezone = parseInt(document.getElementById("timezone").value); config.ntp.enabled = false; if (parseInt($("input[name=\"ntpenabled\"]:checked").val()) === 1) { @@ -674,8 +676,6 @@ function initEventTable() { if (value < 1563300000) { return "(" + value + ")"; } else { - var comp = new Date(); - value = Math.floor(value + ((comp.getTimezoneOffset() * 60) * -1)); var vuepoch = new Date(value * 1000); var formatted = vuepoch.getUTCFullYear() + "-" + twoDigits(vuepoch.getUTCMonth() + 1) + @@ -769,7 +769,7 @@ function socketMessageListener(evt) { builddata(obj); break; case "gettime": - utcSeconds = obj.epoch; + ntpSeconds = obj.epoch; deviceTime(); break; case "ssidlist": diff --git a/tools/wsemulator/wserver.js b/tools/wsemulator/wserver.js index 6cb72cea6..da9a4ad54 100644 --- a/tools/wsemulator/wserver.js +++ b/tools/wsemulator/wserver.js @@ -54,7 +54,7 @@ var eventlog = { "list": [ "{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #1\",\"time\": 1563371160}", "{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #2\",\"time\":0}", - "{\"type\":\"INFO\",\"src\":\"system\",\"desc\":\"System booted\",\"data\":\"\",\"time\":1568660479}", + "{\"type\":\"INFO\",\"src\":\"system\",\"desc\":\"System booted Local Time is 13:02:54 CET\",\"data\":\"\",\"time\":1572613374}", "{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #3\",\"time\":0}" ] } @@ -70,6 +70,7 @@ var configfile = { "hostname": "myesp", "password": "admin", "serial": true, + "version": "1.9.1", "log_events": true }, "mqtt": { @@ -86,7 +87,8 @@ var configfile = { }, "ntp": { "server": "pool.ntp.org", - "interval": "30", + "interval": 60, + "timezone": 2, "enabled": false } }; @@ -128,7 +130,7 @@ function sendStatus() { "availsize": 2469, "ip": "10.10.10.198", "ssid": "my_ssid", - "mac": "DC:4F:11:22:93:06", + "mac": "DC:4F:12:22:13:06", "signalstr": 62, "systemload": 0, "mqttconnected": true, @@ -243,8 +245,7 @@ wss.on('connection', function connection(ws) { console.log("[INFO] Sending time"); var res = {}; res.command = "gettime"; - res.epoch = Math.floor((new Date).getTime() / 1000); - //res.epoch = 1567107755; + res.epoch = 1572613374; // this is 13:02:54 CET wss.broadcast(res); break; case "settime":