Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for DST #333

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
#endif
#include <ip_addr.h>
#include "esp-link/cgi.h"

#include "config.h"
#include <cgiservices.h>

#ifdef CMD_DBG
#define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
Expand Down Expand Up @@ -199,6 +199,8 @@ cmdWifiStatus(CmdPacket *cmd) {
// Command handler for time
static void ICACHE_FLASH_ATTR
cmdGetTime(CmdPacket *cmd) {
cgiServicesCheckDST(); // This can cause DST to change, so the sntp call to return 0.

cmdResponseStart(CMD_RESP_V, sntp_get_current_timestamp(), 0);
cmdResponseEnd();
return;
Expand Down
212 changes: 209 additions & 3 deletions esp-link/cgiservices.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#ifdef SYSLOG
#include "syslog.h"
#endif
#include "time.h"
#include "cgiservices.h"

#ifdef CGISERVICES_DBG
#define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
Expand All @@ -26,6 +28,10 @@ char* flash_maps[7] = {

static ETSTimer reassTimer;

// Daylight Savings Time support
static ETSTimer dstTimer;
static bool old_dst;

// Cgi to update system info (name/description)
int ICACHE_FLASH_ATTR cgiSystemSet(HttpdConnData *connData) {
if (connData->conn == NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
Expand Down Expand Up @@ -96,14 +102,203 @@ int ICACHE_FLASH_ATTR cgiSystemInfo(HttpdConnData *connData) {
return HTTPD_CGI_DONE;
}

static bool ICACHE_FLASH_ATTR isDSTEurope(struct tm *tp) {
int mon = tp->tm_mon + 1;

if (mon < 3 || tp->tm_mon > 11) return false;
if (mon > 3 && tp->tm_mon < 11) return true;

int previousSunday = tp->tm_mday - tp->tm_wday;

if (mon == 10) {
if (previousSunday < 25)
return true;
if (tp->tm_wday > 0)
return false;
if (tp->tm_hour < 2)
return true;
return false;
}
if (mon == 3) {
if (previousSunday < 25)
return false;
if (tp->tm_wday > 0)
return true;
if (tp->tm_hour < 2)
return false;
return true;
}

return true;
}

static bool ICACHE_FLASH_ATTR isDSTUSA(struct tm *tp) {
int month = tp->tm_mon + 1;

// January, february, and december are out.
if (month < 3 || month > 11)
return false;

// April to October are in
if (month > 3 && month < 11)
return true;

int previousSunday = tp->tm_mday - tp->tm_wday;

// In march, we are DST if our previous sunday was on or after the 8th.
if (month == 3)
return previousSunday >= 8;

// In november we must be before the first sunday to be dst.
// That means the previous sunday must be before the 1st.
return previousSunday <= 0;
}

/*
* When adding more regions, this must be extended.
* If someone needs a half hour timezone, then it might be better to change
* the return type to int instead of bool.
* It could then mean the number of minutes to add.
*/
static bool ICACHE_FLASH_ATTR isDST(struct tm *tp) {
switch (flashConfig.dst_mode) {
case DST_EUROPE:
return isDSTEurope(tp);
case DST_USA:
return isDSTUSA(tp);
case DST_NONE:
default:
return false;
}
}

static ICACHE_FLASH_ATTR char *dstMode2text(int dm) {
switch(dm) {
case DST_EUROPE:
return "Europe";
case DST_USA:
return "USA";
case DST_NONE:
default:
return "None";
}
}


#if 1
/*
* For debugging only
* This function is called yet another timeout after changing the timezone offset.
* This is the only way to report the time correctly if we're in DST.
*/
static void ICACHE_FLASH_ATTR dstDelayed2() {
time_t ts = sntp_get_current_timestamp();
if (ts < 10000)
return;
struct tm *tp = gmtime(&ts);

os_printf("dstDelayed (dst) : date %04d-%02d-%02d time %02d:%02d:%02d\n",
tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
}
#endif

/*
* This gets called a while after initializing SNTP.
* The assumption is we will now know the date/time so we can determine whether we're in
* a Daylight Savings Time period.
* And then set the time offset accordingly.
*/
static void ICACHE_FLASH_ATTR dstDelayed() {
time_t ts = sntp_get_current_timestamp();
if (ts < 10000)
return; // FIX ME failed to obtain time. Now what ?

struct tm *tp = gmtime(&ts);

bool dst = isDST(tp);

os_printf("dstDelayed : date %04d-%02d-%02d time %02d:%02d:%02d, wday = %d, DST = %s (%s)\n",
tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec,
tp->tm_wday, dst ? "true" : "false", dstMode2text(flashConfig.dst_mode));

old_dst = dst;

// Only change the timezone offset if we're in DST
if (dst) {
// Changing timezone offset requires a SNTP stop/start.
sntp_stop();
if (! sntp_set_timezone(flashConfig.timezone_offset + 1))
os_printf("sntp_set_timezone(%d) failed\n", flashConfig.timezone_offset + 1);
sntp_init();

#if 1
// FIX ME for debugging only
os_timer_disarm(&dstTimer);
os_timer_setfn(&dstTimer, dstDelayed2, NULL);
os_timer_arm(&dstTimer, 5000, 0); // wait 5 seconds
#endif
}
}

void ICACHE_FLASH_ATTR cgiServicesSNTPInit() {
if (flashConfig.sntp_server[0] != '\0') {
sntp_stop();
if (true == sntp_set_timezone(flashConfig.timezone_offset)) {
sntp_setservername(0, flashConfig.sntp_server);
sntp_init();
}
DBG("SNTP timesource set to %s with offset %d\n", flashConfig.sntp_server, flashConfig.timezone_offset);
old_dst = false;

// If we support daylight savings time, delay setting the timezone until we know the date/time
if (flashConfig.dst_mode != 0) {
os_timer_disarm(&dstTimer);
os_timer_setfn(&dstTimer, dstDelayed, NULL);
os_timer_arm(&dstTimer, 5000, 0); // wait 5 seconds

DBG("SNTP timesource set to %s with offset %d, awaiting DST info\n",
flashConfig.sntp_server, flashConfig.timezone_offset);
} else {
DBG("SNTP timesource set to %s with offset %d\n",
flashConfig.sntp_server, flashConfig.timezone_offset);
}
}
}

/*
* Check whether to change DST, at every CMD_GET_TIME call.
*
* If we need to change, then the next call to sntp_get_current_timestamp() will return 0
* so a retry will be required. The apps already had to cope with such situations, I believe.
*/
void ICACHE_FLASH_ATTR cgiServicesCheckDST() {
if (flashConfig.dst_mode == 0)
return;

time_t ts = sntp_get_current_timestamp();
if (ts < 10000)
return; // FIX ME failed to obtain time. Now what ?

struct tm *tp = gmtime(&ts);
bool dst = isDST(tp);

if (dst != old_dst) {
// Just calling this would be easy but too slow, we already know the new DST setting.
// cgiServicesSNTPInit();

old_dst = dst;

int add = dst ? 1 : 0;
sntp_stop();
if (! sntp_set_timezone(flashConfig.timezone_offset + add))
os_printf("sntp_set_timezone(%d) failed\n", flashConfig.timezone_offset + add);
sntp_init();

#if 1
// FIX ME for debugging only
os_timer_disarm(&dstTimer);
os_timer_setfn(&dstTimer, dstDelayed2, NULL);
os_timer_arm(&dstTimer, 5000, 0); // wait 5 seconds
#endif
}
}

Expand All @@ -124,7 +319,8 @@ int ICACHE_FLASH_ATTR cgiServicesInfo(HttpdConnData *connData) {
"\"timezone_offset\": %d, "
"\"sntp_server\": \"%s\", "
"\"mdns_enable\": \"%s\", "
"\"mdns_servername\": \"%s\""
"\"mdns_servername\": \"%s\", "
"\"dst_mode\": \"%d\" "
" }",
#ifdef SYSLOG
flashConfig.syslog_host,
Expand All @@ -136,7 +332,13 @@ int ICACHE_FLASH_ATTR cgiServicesInfo(HttpdConnData *connData) {
flashConfig.timezone_offset,
flashConfig.sntp_server,
flashConfig.mdns_enable ? "enabled" : "disabled",
flashConfig.mdns_servername
flashConfig.mdns_servername,
#if 0
flashConfig.dst_mode ?
(flashConfig.dst_mode == DST_EUROPE ? "Europe" : "USA")
: "None"
#endif
flashConfig.dst_mode
);

jsonHeader(connData, 200);
Expand Down Expand Up @@ -172,6 +374,10 @@ int ICACHE_FLASH_ATTR cgiServicesSet(HttpdConnData *connData) {
sntp |= getStringArg(connData, "sntp_server", flashConfig.sntp_server, sizeof(flashConfig.sntp_server));
if (sntp < 0) return HTTPD_CGI_DONE;

sntp |= getUInt8Arg(connData, "dst_mode", &flashConfig.dst_mode);
os_printf("DSTMODE %d\n", flashConfig.dst_mode);
if (sntp < 0) return HTTPD_CGI_DONE;

if (sntp > 0) {
cgiServicesSNTPInit();
}
Expand Down
7 changes: 7 additions & 0 deletions esp-link/cgiservices.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@

#include "httpd.h"

// Need to be in sync with html/services.html
#define DST_NONE 0
#define DST_EUROPE 1
#define DST_USA 2

int cgiSystemSet(HttpdConnData *connData);
int cgiSystemInfo(HttpdConnData *connData);

void cgiServicesSNTPInit();
void cgiServicesCheckDST();

int cgiServicesInfo(HttpdConnData *connData);
int cgiServicesSet(HttpdConnData *connData);

Expand Down
1 change: 1 addition & 0 deletions esp-link/cgiwifi.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define CGIWIFI_H

#include "httpd.h"
#include "cmd.h"

enum { wifiIsDisconnected, wifiIsConnected, wifiGotIP };
typedef void(*WifiStateChangeCb)(uint8_t wifiStatus);
Expand Down
1 change: 1 addition & 0 deletions esp-link/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ FlashConfig flashDefault = {
.data_bits = EIGHT_BITS,
.parity = NONE_BITS,
.stop_bits = ONE_STOP_BIT,
.dst_mode = 0,
};

typedef union {
Expand Down
1 change: 1 addition & 0 deletions esp-link/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ typedef struct {
int8_t data_bits;
int8_t parity;
int8_t stop_bits;
uint8_t dst_mode;
} FlashConfig;
extern FlashConfig flashConfig;

Expand Down
11 changes: 10 additions & 1 deletion html/services.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,16 @@ <h1>
<div>
<label>Timezone Offset</label>
<input type="text" name="timezone_offset" />
<div class="popup">Offset hours to apply (no daylight savings support)</div>
<div class="popup">Offset hours to apply</div>
</div>
<div>
<label>Daylight Savings Time (DST) mode</label>
<select name="dst_mode" href="#">
<option value="0">None</option>
<option value="1">Europe</option>
<option value="2">USA</option>
</select>
<div class="popup">Region specific DST automation</div>
</div>
</div>
<button id="SNTP-button" type="submit" class="pure-button button-primary">
Expand Down