mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 16:29:51 +03:00
timezone support
This commit is contained in:
155
src/MyESP.cpp
155
src/MyESP.cpp
@@ -97,8 +97,9 @@ MyESP::MyESP() {
|
|||||||
|
|
||||||
// ntp
|
// ntp
|
||||||
_ntp_server = strdup(MYESP_NTP_SERVER);
|
_ntp_server = strdup(MYESP_NTP_SERVER);
|
||||||
_ntp_interval = 60;
|
_ntp_interval = NTP_INTERVAL_DEFAULT;
|
||||||
_ntp_enabled = false;
|
_ntp_enabled = false;
|
||||||
|
_ntp_timezone = NTP_TIMEZONE_DEFAULT;
|
||||||
|
|
||||||
// get the build time
|
// get the build time
|
||||||
_buildTime = _getBuildTime();
|
_buildTime = _getBuildTime();
|
||||||
@@ -251,8 +252,8 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
|||||||
|
|
||||||
// NTP now that we have a WiFi connection
|
// NTP now that we have a WiFi connection
|
||||||
if (_ntp_enabled) {
|
if (_ntp_enabled) {
|
||||||
NTP.Ntp(_ntp_server, _ntp_interval); // set up 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"), _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
|
// call any final custom stuff
|
||||||
@@ -632,6 +633,27 @@ void MyESP::_telnet_setup() {
|
|||||||
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH);
|
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
|
// Show help of commands
|
||||||
void MyESP::_consoleShowHelp() {
|
void MyESP::_consoleShowHelp() {
|
||||||
myDebug_P(PSTR(""));
|
myDebug_P(PSTR(""));
|
||||||
@@ -652,7 +674,7 @@ void MyESP::_consoleShowHelp() {
|
|||||||
myDebug_P(PSTR("*"));
|
myDebug_P(PSTR("*"));
|
||||||
myDebug_P(PSTR("* Commands:"));
|
myDebug_P(PSTR("* Commands:"));
|
||||||
myDebug_P(PSTR("* ?/help=show commands, CTRL-D/quit=close telnet session"));
|
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
|
#ifdef CRASH
|
||||||
myDebug_P(PSTR("* crash <dump | clear | test [n]>"));
|
myDebug_P(PSTR("* crash <dump | clear | test [n]>"));
|
||||||
@@ -696,6 +718,8 @@ void MyESP::_printSetCommands() {
|
|||||||
myDebug_P(PSTR(" set mqtt_keepalive [seconds]"));
|
myDebug_P(PSTR(" set mqtt_keepalive [seconds]"));
|
||||||
myDebug_P(PSTR(" set mqtt_retain [on | off]"));
|
myDebug_P(PSTR(" set mqtt_retain [on | off]"));
|
||||||
myDebug_P(PSTR(" set ntp_enabled <on | off>"));
|
myDebug_P(PSTR(" set ntp_enabled <on | off>"));
|
||||||
|
myDebug_P(PSTR(" set ntp_interval [n]"));
|
||||||
|
myDebug_P(PSTR(" set ntp_timezone [n]"));
|
||||||
myDebug_P(PSTR(" set serial <on | off>"));
|
myDebug_P(PSTR(" set serial <on | off>"));
|
||||||
myDebug_P(PSTR(" set log_events <on | off>"));
|
myDebug_P(PSTR(" set log_events <on | off>"));
|
||||||
|
|
||||||
@@ -760,6 +784,9 @@ void MyESP::_printSetCommands() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
myDebug_P(PSTR(" ntp_enabled=%s"), (_ntp_enabled) ? "on" : "off");
|
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");
|
myDebug_P(PSTR(" log_events=%s"), (_general_log_events) ? "on" : "off");
|
||||||
|
|
||||||
// print any custom settings
|
// 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);
|
save_config = fs_setSettingValue(&_mqtt_heartbeat, value, false);
|
||||||
} else if (strcmp(setting, "ntp_enabled") == 0) {
|
} else if (strcmp(setting, "ntp_enabled") == 0) {
|
||||||
save_config = fs_setSettingValue(&_ntp_enabled, value, false);
|
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) {
|
} else if (strcmp(setting, "log_events") == 0) {
|
||||||
save_config = fs_setSettingValue(&_general_log_events, value, false);
|
save_config = fs_setSettingValue(&_general_log_events, value, false);
|
||||||
} else {
|
} else {
|
||||||
@@ -946,6 +977,12 @@ void MyESP::_telnetCommand(char * commandLine) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// kick command
|
||||||
|
if ((strcmp(ptrToCommandName, "kick") == 0) && (wc == 1)) {
|
||||||
|
_kick();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// restart command
|
// restart command
|
||||||
if (((strcmp(ptrToCommandName, "restart") == 0) || (strcmp(ptrToCommandName, "reboot") == 0)) && (wc == 1)) {
|
if (((strcmp(ptrToCommandName, "restart") == 0) || (strcmp(ptrToCommandName, "reboot") == 0)) && (wc == 1)) {
|
||||||
resetESP();
|
resetESP();
|
||||||
@@ -1282,8 +1319,9 @@ void MyESP::showSystemStats() {
|
|||||||
myDebug_P(PSTR(" [MQTT] is disconnected"));
|
myDebug_P(PSTR(" [MQTT] is disconnected"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_ntp_enabled) {
|
if (_have_ntp_time) {
|
||||||
myDebug_P(PSTR(" [NTP] Time in UTC is %02d:%02d:%02d"), to_hour(now()), to_minute(now()), to_second(now()));
|
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
|
#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
|
// 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) {
|
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
|
// see if we can open it
|
||||||
File file = SPIFFS.open(filename, "r");
|
File file = SPIFFS.open(filename, "r");
|
||||||
if (!file) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check size
|
// check size
|
||||||
size_t size = file.size();
|
size_t size = file.size();
|
||||||
|
|
||||||
// myDebug_P(PSTR("[FS] Checking file %s (%d bytes)"), filename, size); // remove for debugging
|
|
||||||
|
|
||||||
if (size > maxsize) {
|
if (size > maxsize) {
|
||||||
file.close();
|
file.close();
|
||||||
myDebug_P(PSTR("[FS] Error. File %s size %d is too large (max %d)"), filename, size, maxsize);
|
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_server = strdup(ntp["server"] | "");
|
||||||
_ntp_interval = ntp["interval"] | 60;
|
_ntp_interval = ntp["interval"] | 60;
|
||||||
if (_ntp_interval < 2)
|
if (_ntp_interval < 2)
|
||||||
_ntp_interval = 60;
|
_ntp_interval = NTP_INTERVAL_DEFAULT;
|
||||||
_ntp_enabled = ntp["enabled"];
|
_ntp_enabled = ntp["enabled"];
|
||||||
|
_ntp_timezone = ntp["timezone"] | NTP_TIMEZONE_DEFAULT;
|
||||||
|
|
||||||
myDebug_P(PSTR("[FS] System config loaded (%d bytes)"), size);
|
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;
|
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
|
// saves a bool into a config setting, using default value if non set
|
||||||
// returns true if successful
|
// returns true if successful
|
||||||
bool MyESP::fs_setSettingValue(bool * setting, const char * value, bool value_default) {
|
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) {
|
if (_fs_loadsave_callback_f) {
|
||||||
const JsonObject & json = doc["settings"];
|
const JsonObject & json = doc["settings"];
|
||||||
|
|
||||||
if (!(_fs_loadsave_callback_f)(MYESP_FSACTION_LOAD, json)) {
|
if (!(_fs_loadsave_callback_f)(MYESP_FSACTION_LOAD, json)) {
|
||||||
myDebug_P(PSTR("[FS] Error reading custom config"));
|
myDebug_P(PSTR("[FS] Error reading custom config"));
|
||||||
return false;
|
return false;
|
||||||
@@ -1852,6 +1904,7 @@ bool MyESP::fs_saveCustomConfig(JsonObject root) {
|
|||||||
|
|
||||||
// open for writing
|
// open for writing
|
||||||
File configFile = SPIFFS.open(MYESP_CUSTOMCONFIG_FILE, "w");
|
File configFile = SPIFFS.open(MYESP_CUSTOMCONFIG_FILE, "w");
|
||||||
|
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
myDebug_P(PSTR("[FS] Failed to open custom config for writing"));
|
myDebug_P(PSTR("[FS] Failed to open custom config for writing"));
|
||||||
ok = false;
|
ok = false;
|
||||||
@@ -1888,15 +1941,16 @@ bool MyESP::fs_saveCustomConfig(JsonObject root) {
|
|||||||
|
|
||||||
// save system config to spiffs
|
// save system config to spiffs
|
||||||
bool MyESP::fs_saveConfig(JsonObject root) {
|
bool MyESP::fs_saveConfig(JsonObject root) {
|
||||||
bool ok = false;
|
|
||||||
|
|
||||||
// call any custom functions before handling SPIFFS
|
// call any custom functions before handling SPIFFS
|
||||||
if (_ota_pre_callback_f) {
|
if (_ota_pre_callback_f) {
|
||||||
(_ota_pre_callback_f)();
|
(_ota_pre_callback_f)();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
// open for writing
|
// open for writing
|
||||||
File configFile = SPIFFS.open(MYESP_CONFIG_FILE, "w");
|
File configFile = SPIFFS.open(MYESP_CONFIG_FILE, "w");
|
||||||
|
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
myDebug_P(PSTR("[FS] Failed to open system config for writing"));
|
myDebug_P(PSTR("[FS] Failed to open system config for writing"));
|
||||||
ok = false;
|
ok = false;
|
||||||
@@ -1940,6 +1994,7 @@ bool MyESP::_fs_writeConfig() {
|
|||||||
general["serial"] = _general_serial;
|
general["serial"] = _general_serial;
|
||||||
general["hostname"] = _general_hostname;
|
general["hostname"] = _general_hostname;
|
||||||
general["log_events"] = _general_log_events;
|
general["log_events"] = _general_log_events;
|
||||||
|
general["version"] = _app_version;
|
||||||
|
|
||||||
JsonObject mqtt = doc.createNestedObject("mqtt");
|
JsonObject mqtt = doc.createNestedObject("mqtt");
|
||||||
mqtt["enabled"] = _mqtt_enabled;
|
mqtt["enabled"] = _mqtt_enabled;
|
||||||
@@ -1950,7 +2005,6 @@ bool MyESP::_fs_writeConfig() {
|
|||||||
mqtt["qos"] = _mqtt_qos;
|
mqtt["qos"] = _mqtt_qos;
|
||||||
mqtt["keepalive"] = _mqtt_keepalive;
|
mqtt["keepalive"] = _mqtt_keepalive;
|
||||||
mqtt["retain"] = _mqtt_retain;
|
mqtt["retain"] = _mqtt_retain;
|
||||||
|
|
||||||
mqtt["password"] = _mqtt_password;
|
mqtt["password"] = _mqtt_password;
|
||||||
mqtt["base"] = _mqtt_base;
|
mqtt["base"] = _mqtt_base;
|
||||||
|
|
||||||
@@ -1958,6 +2012,7 @@ bool MyESP::_fs_writeConfig() {
|
|||||||
ntp["server"] = _ntp_server;
|
ntp["server"] = _ntp_server;
|
||||||
ntp["interval"] = _ntp_interval;
|
ntp["interval"] = _ntp_interval;
|
||||||
ntp["enabled"] = _ntp_enabled;
|
ntp["enabled"] = _ntp_enabled;
|
||||||
|
ntp["timezone"] = _ntp_timezone;
|
||||||
|
|
||||||
bool ok = fs_saveConfig(root); // save it
|
bool ok = fs_saveConfig(root); // save it
|
||||||
|
|
||||||
@@ -1993,28 +2048,15 @@ void MyESP::_fs_setup() {
|
|||||||
(_ota_pre_callback_f)(); // call custom function
|
(_ota_pre_callback_f)(); // call custom function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check SPIFFS is OK
|
||||||
if (!SPIFFS.begin()) {
|
if (!SPIFFS.begin()) {
|
||||||
myDebug_P(PSTR("[FS] Formatting filesystem..."));
|
|
||||||
if (SPIFFS.format()) {
|
if (SPIFFS.format()) {
|
||||||
if (_general_log_events) {
|
myDebug_P(PSTR("[FS] File system formatted"));
|
||||||
_writeEvent("WARN", "system", "File system formatted", "");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
myDebug_P(PSTR("[FS] Failed to format file system"));
|
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
|
// fill event log with tests
|
||||||
SPIFFS.remove(MYESP_EVENTLOG_FILE);
|
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) {
|
if (_ota_post_callback_f) {
|
||||||
(_ota_post_callback_f)(); // call custom function
|
(_ota_post_callback_f)(); // call custom function
|
||||||
}
|
}
|
||||||
@@ -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
|
root["time"] = now(); // is relative if we're not using NTP
|
||||||
|
|
||||||
// Serialize JSON to file
|
// Serialize JSON to file
|
||||||
size_t n = serializeJson(root, eventlog);
|
(void)serializeJson(root, eventlog);
|
||||||
eventlog.print("\n"); // this indicates end of the entry
|
|
||||||
|
|
||||||
if (!n) {
|
eventlog.print("\n"); // this indicates end of the entry
|
||||||
//Serial.println("[SYSTEM] Error writing to event log"); // for debugging
|
|
||||||
}
|
|
||||||
|
|
||||||
eventlog.close();
|
eventlog.close();
|
||||||
}
|
}
|
||||||
@@ -2453,17 +2503,7 @@ void MyESP::_procMsg(AsyncWebSocketClient * client, size_t sz) {
|
|||||||
uint8_t page = doc["page"];
|
uint8_t page = doc["page"];
|
||||||
_sendEventLog(page);
|
_sendEventLog(page);
|
||||||
} else if (strcmp(command, "clearevent") == 0) {
|
} else if (strcmp(command, "clearevent") == 0) {
|
||||||
if (_ota_pre_callback_f) {
|
_emptyEventLog();
|
||||||
(_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
|
|
||||||
}
|
|
||||||
} else if (strcmp(command, "scan") == 0) {
|
} else if (strcmp(command, "scan") == 0) {
|
||||||
WiFi.scanNetworksAsync(std::bind(&MyESP::_printScanResult, this, std::placeholders::_1), true);
|
WiFi.scanNetworksAsync(std::bind(&MyESP::_printScanResult, this, std::placeholders::_1), true);
|
||||||
} else if (strcmp(command, "gettime") == 0) {
|
} else if (strcmp(command, "gettime") == 0) {
|
||||||
@@ -2480,6 +2520,22 @@ void MyESP::_procMsg(AsyncWebSocketClient * client, size_t sz) {
|
|||||||
client->_tempObject = NULL;
|
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
|
// read both system config and the custom config and send as json to web socket
|
||||||
bool MyESP::_fs_sendConfig() {
|
bool MyESP::_fs_sendConfig() {
|
||||||
File configFile;
|
File configFile;
|
||||||
@@ -2866,6 +2922,16 @@ void MyESP::_sendTime() {
|
|||||||
_ws->textAll(buffer, len);
|
_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
|
// bootup sequence
|
||||||
// quickly flash LED until we get a Wifi connection, or AP established
|
// quickly flash LED until we get a Wifi connection, or AP established
|
||||||
void MyESP::_bootupSequence() {
|
void MyESP::_bootupSequence() {
|
||||||
@@ -2965,6 +3031,7 @@ void MyESP::loop() {
|
|||||||
_bootupSequence(); // see if a reset was pressed during bootup
|
_bootupSequence(); // see if a reset was pressed during bootup
|
||||||
|
|
||||||
jw.loop(); // WiFi
|
jw.loop(); // WiFi
|
||||||
|
|
||||||
ArduinoOTA.handle(); // OTA
|
ArduinoOTA.handle(); // OTA
|
||||||
|
|
||||||
ESP.wdtFeed(); // feed the watchdog...
|
ESP.wdtFeed(); // feed the watchdog...
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#ifndef MyESP_h
|
#ifndef MyESP_h
|
||||||
#define MyESP_h
|
#define MyESP_h
|
||||||
|
|
||||||
#define MYESP_VERSION "1.2.13"
|
#define MYESP_VERSION "1.2.14"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
@@ -295,6 +295,7 @@ class MyESP {
|
|||||||
bool fs_setSettingValue(char ** setting, const char * value, const char * value_default);
|
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(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(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);
|
bool fs_setSettingValue(bool * setting, const char * value, bool value_default);
|
||||||
|
|
||||||
// Web
|
// Web
|
||||||
@@ -317,6 +318,7 @@ class MyESP {
|
|||||||
uint32_t getSystemResetReason();
|
uint32_t getSystemResetReason();
|
||||||
uint8_t getSystemBootStatus();
|
uint8_t getSystemBootStatus();
|
||||||
bool _have_ntp_time;
|
bool _have_ntp_time;
|
||||||
|
unsigned long getSystemTime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// mqtt
|
// mqtt
|
||||||
@@ -418,6 +420,7 @@ class MyESP {
|
|||||||
char * _getBuildTime();
|
char * _getBuildTime();
|
||||||
bool _hasValue(const char * s);
|
bool _hasValue(const char * s);
|
||||||
void _printHeap(const char * s);
|
void _printHeap(const char * s);
|
||||||
|
void _kick();
|
||||||
|
|
||||||
// reset reason and rtcmem
|
// reset reason and rtcmem
|
||||||
bool _rtcmem_status;
|
bool _rtcmem_status;
|
||||||
@@ -464,6 +467,7 @@ class MyESP {
|
|||||||
|
|
||||||
// log
|
// log
|
||||||
void _sendEventLog(uint8_t page);
|
void _sendEventLog(uint8_t page);
|
||||||
|
void _emptyEventLog();
|
||||||
|
|
||||||
// web
|
// web
|
||||||
void _onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len);
|
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;
|
char * _ntp_server;
|
||||||
uint8_t _ntp_interval;
|
uint8_t _ntp_interval;
|
||||||
bool _ntp_enabled;
|
bool _ntp_enabled;
|
||||||
|
uint8_t _ntp_timezone;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MyESP myESP;
|
extern MyESP myESP;
|
||||||
|
|||||||
76
src/Ntp.cpp
76
src/Ntp.cpp
@@ -3,16 +3,75 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Ntp.h"
|
#include "Ntp.h"
|
||||||
|
#include "MyESP.h"
|
||||||
|
|
||||||
char * NtpClient::TimeServerName;
|
char * NtpClient::TimeServerName;
|
||||||
|
Timezone * NtpClient::tz;
|
||||||
|
TimeChangeRule * NtpClient::tcr;
|
||||||
time_t NtpClient::syncInterval;
|
time_t NtpClient::syncInterval;
|
||||||
IPAddress NtpClient::timeServer;
|
IPAddress NtpClient::timeServer;
|
||||||
AsyncUDP NtpClient::udpListener;
|
AsyncUDP NtpClient::udpListener;
|
||||||
byte NtpClient::NTPpacket[NTP_PACKET_SIZE];
|
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);
|
TimeServerName = strdup(server);
|
||||||
syncInterval = syncMins * 60; // convert to seconds
|
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);
|
WiFi.hostByName(TimeServerName, timeServer);
|
||||||
setSyncProvider(getNtpTime);
|
setSyncProvider(getNtpTime);
|
||||||
setSyncInterval(syncInterval);
|
setSyncInterval(syncInterval);
|
||||||
@@ -38,7 +97,20 @@ time_t ICACHE_FLASH_ATTR NtpClient::getNtpTime() {
|
|||||||
unsigned long highWord = word(packet.data()[40], packet.data()[41]);
|
unsigned long highWord = word(packet.data()[40], packet.data()[41]);
|
||||||
unsigned long lowWord = word(packet.data()[42], packet.data()[43]);
|
unsigned long lowWord = word(packet.data()[42], packet.data()[43]);
|
||||||
time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL;
|
time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL;
|
||||||
setTime(UnixUTCtime);
|
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));
|
udpListener.write(NTPpacket, sizeof(NTPpacket));
|
||||||
|
|||||||
10
src/Ntp.h
10
src/Ntp.h
@@ -10,18 +10,24 @@
|
|||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESPAsyncUDP.h>
|
#include <ESPAsyncUDP.h>
|
||||||
|
|
||||||
#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_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 {
|
class NtpClient {
|
||||||
public:
|
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();
|
ICACHE_FLASH_ATTR virtual ~NtpClient();
|
||||||
|
|
||||||
static char * TimeServerName;
|
static char * TimeServerName;
|
||||||
static IPAddress timeServer;
|
static IPAddress timeServer;
|
||||||
static time_t syncInterval;
|
static time_t syncInterval;
|
||||||
|
static Timezone * tz;
|
||||||
|
static TimeChangeRule * tcr;
|
||||||
|
|
||||||
static AsyncUDP udpListener;
|
static AsyncUDP udpListener;
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
#include "TimeLib.h"
|
||||||
|
|
||||||
static tmElements_t tm; // a cache of time elements
|
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
|
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) {
|
void refreshCache(time_t t) {
|
||||||
if (t != cacheTime) {
|
if (t != cacheTime) {
|
||||||
breakTime(t, tm);
|
breakTime(t, tm);
|
||||||
@@ -118,6 +151,26 @@ uint8_t to_hour(time_t t) { // the hour for the given time
|
|||||||
return tm.Hour;
|
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) {
|
void setTime(time_t t) {
|
||||||
sysTime = (uint32_t)t;
|
sysTime = (uint32_t)t;
|
||||||
nextSyncTime = (uint32_t)t + syncInterval;
|
nextSyncTime = (uint32_t)t + syncInterval;
|
||||||
|
|||||||
@@ -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
|
#ifndef _TimeLib_h
|
||||||
#define _TimeLib_h
|
#define _TimeLib_h
|
||||||
|
|
||||||
@@ -41,5 +46,10 @@ time_t makeTime(const tmElements_t & tm); // convert time e
|
|||||||
uint8_t to_hour(time_t t); // the hour 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_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_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
|
#endif
|
||||||
|
|||||||
200
src/Timezone.cpp
Normal file
200
src/Timezone.cpp
Normal file
@@ -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
|
||||||
|
}
|
||||||
53
src/Timezone.h
Normal file
53
src/Timezone.h
Normal file
@@ -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 <Arduino.h>
|
||||||
|
#include <TimeLib.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
@@ -70,7 +70,6 @@ Ticker showerColdShotStopTimer;
|
|||||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
#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
|
// 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_GPIO D5
|
||||||
#define EMSESP_DALLAS_PARASITE false
|
#define EMSESP_DALLAS_PARASITE false
|
||||||
|
|
||||||
@@ -80,7 +79,6 @@ Ticker showerColdShotStopTimer;
|
|||||||
#define EMSESP_LED_GPIO LED_BUILTIN
|
#define EMSESP_LED_GPIO LED_BUILTIN
|
||||||
|
|
||||||
typedef struct {
|
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
|
// custom params
|
||||||
@@ -930,7 +928,7 @@ void do_systemCheck() {
|
|||||||
// only if we have a EMS connection
|
// only if we have a EMS connection
|
||||||
void do_regularUpdates() {
|
void do_regularUpdates() {
|
||||||
if (ems_getBusConnected() && !ems_getTxDisabled()) {
|
if (ems_getBusConnected() && !ems_getTxDisabled()) {
|
||||||
myDebugLog("Starting scheduled query from EMS devices");
|
myDebugLog("Fetching data from EMS devices");
|
||||||
ems_getThermostatValues();
|
ems_getThermostatValues();
|
||||||
ems_getBoilerValues();
|
ems_getBoilerValues();
|
||||||
ems_getSolarModuleValues();
|
ems_getSolarModuleValues();
|
||||||
@@ -1917,7 +1915,6 @@ void initEMSESP() {
|
|||||||
EMSESP_Settings.listen_mode = false;
|
EMSESP_Settings.listen_mode = false;
|
||||||
EMSESP_Settings.publish_time = DEFAULT_PUBLISHTIME;
|
EMSESP_Settings.publish_time = DEFAULT_PUBLISHTIME;
|
||||||
EMSESP_Settings.publish_always = false;
|
EMSESP_Settings.publish_always = false;
|
||||||
EMSESP_Settings.timestamp = millis();
|
|
||||||
EMSESP_Settings.dallas_sensors = 0;
|
EMSESP_Settings.dallas_sensors = 0;
|
||||||
EMSESP_Settings.led_gpio = EMSESP_LED_GPIO;
|
EMSESP_Settings.led_gpio = EMSESP_LED_GPIO;
|
||||||
EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO;
|
EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO;
|
||||||
@@ -1937,6 +1934,7 @@ void initEMSESP() {
|
|||||||
* Shower Logic
|
* Shower Logic
|
||||||
*/
|
*/
|
||||||
void showerCheck() {
|
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 already in cold mode, ignore all this logic until we're out of the cold blast
|
||||||
if (!EMSESP_Shower.doingColdShot) {
|
if (!EMSESP_Shower.doingColdShot) {
|
||||||
// is the hot water running?
|
// is the hot water running?
|
||||||
@@ -1944,7 +1942,7 @@ void showerCheck() {
|
|||||||
// if heater was previously off, start the timer
|
// if heater was previously off, start the timer
|
||||||
if (EMSESP_Shower.timerStart == 0) {
|
if (EMSESP_Shower.timerStart == 0) {
|
||||||
// hot water just started...
|
// 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.timerPause = 0; // remove any last pauses
|
||||||
EMSESP_Shower.doingColdShot = false;
|
EMSESP_Shower.doingColdShot = false;
|
||||||
EMSESP_Shower.duration = 0;
|
EMSESP_Shower.duration = 0;
|
||||||
@@ -1952,13 +1950,12 @@ void showerCheck() {
|
|||||||
} else {
|
} else {
|
||||||
// hot water has been on for a while
|
// 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
|
// 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;
|
EMSESP_Shower.showerOn = true;
|
||||||
myDebugLog("[Shower] hot water still running, starting shower timer");
|
myDebugLog("[Shower] hot water still running, starting shower timer");
|
||||||
}
|
}
|
||||||
// check if the shower has been on too long
|
// check if the shower has been on too long
|
||||||
else if ((((EMSESP_Settings.timestamp - EMSESP_Shower.timerStart) > SHOWER_MAX_DURATION) && !EMSESP_Shower.doingColdShot)
|
else if ((((time_now - EMSESP_Shower.timerStart) > SHOWER_MAX_DURATION) && !EMSESP_Shower.doingColdShot) && EMSESP_Settings.shower_alert) {
|
||||||
&& EMSESP_Settings.shower_alert) {
|
|
||||||
myDebugLog("[Shower] exceeded max shower time");
|
myDebugLog("[Shower] exceeded max shower time");
|
||||||
_showerColdShotStart();
|
_showerColdShotStart();
|
||||||
}
|
}
|
||||||
@@ -1966,11 +1963,11 @@ void showerCheck() {
|
|||||||
} else { // hot water is off
|
} else { // hot water is off
|
||||||
// if it just turned off, record the time as it could be a short pause
|
// if it just turned off, record the time as it could be a short pause
|
||||||
if ((EMSESP_Shower.timerStart) && (EMSESP_Shower.timerPause == 0)) {
|
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 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
|
// 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
|
// 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) {
|
if ((EMSESP_Shower.timerPause - EMSESP_Shower.timerStart) > SHOWER_OFFSET_TIME) {
|
||||||
@@ -2062,8 +2059,6 @@ void setup() {
|
|||||||
// Main loop
|
// Main loop
|
||||||
//
|
//
|
||||||
void loop() {
|
void loop() {
|
||||||
EMSESP_Settings.timestamp = millis();
|
|
||||||
|
|
||||||
// the main loop
|
// the main loop
|
||||||
myESP.loop();
|
myESP.loop();
|
||||||
|
|
||||||
|
|||||||
47
src/ems.cpp
47
src/ems.cpp
@@ -519,7 +519,6 @@ void ems_setTxMode(uint8_t mode) {
|
|||||||
EMS_Sys_Status.emsTxMode = mode;
|
EMS_Sys_Status.emsTxMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* debug print a telegram to telnet/serial including the CRC
|
* 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 data_len = EMS_RxTelegram->data_length; // length of data block
|
||||||
uint8_t length = EMS_RxTelegram->length; // includes CRC
|
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));
|
strlcpy(output_str, "(", sizeof(output_str));
|
||||||
strlcat(output_str, COLOR_CYAN, 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, ":", 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, ":", sizeof(output_str));
|
||||||
strlcat(output_str, _smallitoa((uint8_t)((EMS_RxTelegram->timestamp / 1000) % 60), 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, ".", sizeof(output_str));
|
||||||
strlcat(output_str, _smallitoa3(EMS_RxTelegram->timestamp % 1000, buffer), sizeof(output_str));
|
strlcat(output_str, _smallitoa3(t_msec, buffer), sizeof(output_str));
|
||||||
|
}
|
||||||
|
|
||||||
strlcat(output_str, COLOR_RESET, sizeof(output_str));
|
strlcat(output_str, COLOR_RESET, sizeof(output_str));
|
||||||
strlcat(output_str, ") ", sizeof(output_str));
|
strlcat(output_str, ") ", sizeof(output_str));
|
||||||
|
|
||||||
strlcat(output_str, color, sizeof(output_str));
|
strlcat(output_str, color, sizeof(output_str));
|
||||||
strlcat(output_str, prefix, sizeof(output_str));
|
strlcat(output_str, prefix, sizeof(output_str));
|
||||||
|
|
||||||
@@ -606,7 +629,7 @@ void _ems_sendTelegram() {
|
|||||||
EMS_RxTelegram.length = EMS_TxTelegram.length; // full length of telegram
|
EMS_RxTelegram.length = EMS_TxTelegram.length; // full length of telegram
|
||||||
EMS_RxTelegram.telegram = EMS_TxTelegram.data;
|
EMS_RxTelegram.telegram = EMS_TxTelegram.data;
|
||||||
EMS_RxTelegram.data_length = 0; // ignore #data=
|
EMS_RxTelegram.data_length = 0; // ignore #data=
|
||||||
EMS_RxTelegram.timestamp = millis(); // now
|
EMS_RxTelegram.timestamp = myESP.getSystemTime(); // now
|
||||||
_debugPrintTelegram("Sending raw: ", &EMS_RxTelegram, COLOR_CYAN, true);
|
_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.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.data_length = 0; // ignore the data length for read and writes. only used for incoming.
|
||||||
EMS_RxTelegram.telegram = EMS_TxTelegram.data;
|
EMS_RxTelegram.telegram = EMS_TxTelegram.data;
|
||||||
EMS_RxTelegram.timestamp = millis(); // now
|
EMS_RxTelegram.timestamp = myESP.getSystemTime(); // now
|
||||||
_debugPrintTelegram(s, &EMS_RxTelegram, COLOR_CYAN);
|
_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
|
static _EMS_RxTelegram EMS_RxTelegram; // create the Rx package
|
||||||
EMS_RxTelegram.telegram = telegram;
|
EMS_RxTelegram.telegram = telegram;
|
||||||
EMS_RxTelegram.timestamp = millis();
|
EMS_RxTelegram.timestamp = myESP.getSystemTime();
|
||||||
EMS_RxTelegram.length = length;
|
EMS_RxTelegram.length = length;
|
||||||
|
|
||||||
EMS_RxTelegram.src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here
|
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
|
// 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
|
// 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;
|
EMS_Sys_Status.emsBusConnected = true;
|
||||||
|
|
||||||
// now lets process it and see what to do next
|
// 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
|
// manual : 10 00 FF 0A 01 A5 02
|
||||||
// auto : Thermostat -> all, type 0x01A5 telegram: 10 00 FF 0A 01 A5 03
|
// 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].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
|
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) {
|
void ems_setThermostatTemp(float temperature, uint8_t hc_num, uint8_t temptype) {
|
||||||
if (!ems_getThermostatEnabled()) {
|
if (!ems_getThermostatEnabled()) {
|
||||||
|
myDebug_P(PSTR("Thermostat not online."));
|
||||||
return;
|
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) {
|
void ems_setThermostatMode(uint8_t mode, uint8_t hc_num) {
|
||||||
if (!ems_getThermostatEnabled()) {
|
if (!ems_getThermostatEnabled()) {
|
||||||
|
myDebug_P(PSTR("Thermostat not online."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,13 +147,13 @@ typedef struct {
|
|||||||
uint8_t comparisonOffset; // offset of where the byte is we want to compare too during validation
|
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
|
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?
|
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];
|
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||||
} _EMS_TxTelegram;
|
} _EMS_TxTelegram;
|
||||||
|
|
||||||
// The Rx receive package
|
// The Rx receive package
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t timestamp; // timestamp from millis()
|
unsigned long timestamp; // timestamp from millis()
|
||||||
uint8_t * telegram; // the full data package
|
uint8_t * telegram; // the full data package
|
||||||
uint8_t data_length; // length in bytes of the data
|
uint8_t data_length; // length in bytes of the data
|
||||||
uint8_t length; // full length of the complete telegram
|
uint8_t length; // full length of the complete telegram
|
||||||
|
|||||||
@@ -391,10 +391,29 @@
|
|||||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||||
data-content="Time in minutes between scheduled NTP refreshes"></i></label>
|
data-content="Time in minutes between scheduled NTP refreshes"></i></label>
|
||||||
<span class="col-xs-9 col-md-5">
|
<span class="col-xs-9 col-md-5">
|
||||||
<input class="form-control input-sm" placeholder="in Minutes" value="30" id="intervals" type="text">
|
<input class="form-control input-sm" placeholder="in Minutes" value="30" id="interval" type="text">
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-xs-3">Time Zone</label>
|
||||||
|
<span class="col-xs-9 col-md-5">
|
||||||
|
<select class="form-control input-sm" name="timzeone" id="timezone">
|
||||||
|
<option value="0">Australia Eastern (Sydney, Melbourne)</option>
|
||||||
|
<option value="1">Moscow (MSK, does not observe DST)</option>
|
||||||
|
<option selected="selected" value="2">Central European Time (Frankfurt, Paris, Amsterdam)</option>
|
||||||
|
<option value="3">United Kingdom (London, Belfast)</option>
|
||||||
|
<option value="4">UTC</option>
|
||||||
|
<option value="5">US Eastern (New York, Detroit)</option>
|
||||||
|
<option value="6">US Central (Chicago, Houston)</option>
|
||||||
|
<option value="7">US Mountain (Denver, Salt Lake City)</option>
|
||||||
|
<option value="8">Arizona (no DST)</option>
|
||||||
|
<option value="9">US Pacific (Las Vegas, Los Angeles)</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<div class="col-xs-9 col-md-8">
|
<div class="col-xs-9 col-md-8">
|
||||||
<button onclick="saventp()" class="btn btn-primary btn-sm pull-right">Save</button>
|
<button onclick="saventp()" class="btn btn-primary btn-sm pull-right">Save</button>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ var version = "";
|
|||||||
|
|
||||||
var websock = null;
|
var websock = null;
|
||||||
var wsUri = "ws://" + window.location.host + "/ws";
|
var wsUri = "ws://" + window.location.host + "/ws";
|
||||||
var utcSeconds;
|
var ntpSeconds;
|
||||||
var data = [];
|
var data = [];
|
||||||
var ajaxobj;
|
var ajaxobj;
|
||||||
|
|
||||||
@@ -37,7 +37,8 @@ var config = {
|
|||||||
"hostname": "",
|
"hostname": "",
|
||||||
"serial": false,
|
"serial": false,
|
||||||
"password": "admin",
|
"password": "admin",
|
||||||
"log_events": true
|
"log_events": true,
|
||||||
|
"version": "1.0.0"
|
||||||
},
|
},
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@@ -53,13 +54,14 @@ var config = {
|
|||||||
},
|
},
|
||||||
"ntp": {
|
"ntp": {
|
||||||
"server": "pool.ntp.org",
|
"server": "pool.ntp.org",
|
||||||
"interval": 30,
|
"interval": 60,
|
||||||
|
"timezone": 2,
|
||||||
"enabled": true
|
"enabled": true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function browserTime() {
|
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 c = new Date();
|
||||||
var timestamp = Math.floor((c.getTime() / 1000) + ((c.getTimezoneOffset() * 60) * -1));
|
var timestamp = Math.floor((c.getTime() / 1000) + ((c.getTimezoneOffset() * 60) * -1));
|
||||||
d.setUTCSeconds(timestamp);
|
d.setUTCSeconds(timestamp);
|
||||||
@@ -67,10 +69,8 @@ function browserTime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deviceTime() {
|
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 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(ntpSeconds);
|
||||||
t.setUTCSeconds(devTime);
|
|
||||||
document.getElementById("utc").innerHTML = t.toUTCString().slice(0, -3);
|
document.getElementById("utc").innerHTML = t.toUTCString().slice(0, -3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,8 @@ function listntp() {
|
|||||||
websock.send("{\"command\":\"gettime\"}");
|
websock.send("{\"command\":\"gettime\"}");
|
||||||
|
|
||||||
document.getElementById("ntpserver").value = config.ntp.server;
|
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) {
|
if (config.ntp.enabled) {
|
||||||
$("input[name=\"ntpenabled\"][value=\"1\"]").prop("checked", true);
|
$("input[name=\"ntpenabled\"][value=\"1\"]").prop("checked", true);
|
||||||
@@ -137,7 +138,8 @@ function custom_saveconfig() {
|
|||||||
|
|
||||||
function saventp() {
|
function saventp() {
|
||||||
config.ntp.server = document.getElementById("ntpserver").value;
|
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;
|
config.ntp.enabled = false;
|
||||||
if (parseInt($("input[name=\"ntpenabled\"]:checked").val()) === 1) {
|
if (parseInt($("input[name=\"ntpenabled\"]:checked").val()) === 1) {
|
||||||
@@ -674,8 +676,6 @@ function initEventTable() {
|
|||||||
if (value < 1563300000) {
|
if (value < 1563300000) {
|
||||||
return "(" + value + ")";
|
return "(" + value + ")";
|
||||||
} else {
|
} else {
|
||||||
var comp = new Date();
|
|
||||||
value = Math.floor(value + ((comp.getTimezoneOffset() * 60) * -1));
|
|
||||||
var vuepoch = new Date(value * 1000);
|
var vuepoch = new Date(value * 1000);
|
||||||
var formatted = vuepoch.getUTCFullYear() +
|
var formatted = vuepoch.getUTCFullYear() +
|
||||||
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
|
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
|
||||||
@@ -769,7 +769,7 @@ function socketMessageListener(evt) {
|
|||||||
builddata(obj);
|
builddata(obj);
|
||||||
break;
|
break;
|
||||||
case "gettime":
|
case "gettime":
|
||||||
utcSeconds = obj.epoch;
|
ntpSeconds = obj.epoch;
|
||||||
deviceTime();
|
deviceTime();
|
||||||
break;
|
break;
|
||||||
case "ssidlist":
|
case "ssidlist":
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ var eventlog = {
|
|||||||
"list": [
|
"list": [
|
||||||
"{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #1\",\"time\": 1563371160}",
|
"{\"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\":\"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}"
|
"{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #3\",\"time\":0}"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -70,6 +70,7 @@ var configfile = {
|
|||||||
"hostname": "myesp",
|
"hostname": "myesp",
|
||||||
"password": "admin",
|
"password": "admin",
|
||||||
"serial": true,
|
"serial": true,
|
||||||
|
"version": "1.9.1",
|
||||||
"log_events": true
|
"log_events": true
|
||||||
},
|
},
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
@@ -86,7 +87,8 @@ var configfile = {
|
|||||||
},
|
},
|
||||||
"ntp": {
|
"ntp": {
|
||||||
"server": "pool.ntp.org",
|
"server": "pool.ntp.org",
|
||||||
"interval": "30",
|
"interval": 60,
|
||||||
|
"timezone": 2,
|
||||||
"enabled": false
|
"enabled": false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -128,7 +130,7 @@ function sendStatus() {
|
|||||||
"availsize": 2469,
|
"availsize": 2469,
|
||||||
"ip": "10.10.10.198",
|
"ip": "10.10.10.198",
|
||||||
"ssid": "my_ssid",
|
"ssid": "my_ssid",
|
||||||
"mac": "DC:4F:11:22:93:06",
|
"mac": "DC:4F:12:22:13:06",
|
||||||
"signalstr": 62,
|
"signalstr": 62,
|
||||||
"systemload": 0,
|
"systemload": 0,
|
||||||
"mqttconnected": true,
|
"mqttconnected": true,
|
||||||
@@ -243,8 +245,7 @@ wss.on('connection', function connection(ws) {
|
|||||||
console.log("[INFO] Sending time");
|
console.log("[INFO] Sending time");
|
||||||
var res = {};
|
var res = {};
|
||||||
res.command = "gettime";
|
res.command = "gettime";
|
||||||
res.epoch = Math.floor((new Date).getTime() / 1000);
|
res.epoch = 1572613374; // this is 13:02:54 CET
|
||||||
//res.epoch = 1567107755;
|
|
||||||
wss.broadcast(res);
|
wss.broadcast(res);
|
||||||
break;
|
break;
|
||||||
case "settime":
|
case "settime":
|
||||||
|
|||||||
Reference in New Issue
Block a user