Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
openshwprojects committed Dec 28, 2024
2 parents 1f044b2 + cafa361 commit a11c671
Show file tree
Hide file tree
Showing 9 changed files with 429 additions and 9 deletions.
3 changes: 2 additions & 1 deletion openBeken_win32_mvsc2017.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@
<ClCompile Include="src\selftest\selftest_mqtt.c" />
<ClCompile Include="src\selftest\selftest_multiplePinsOnChannel.c" />
<ClCompile Include="src\selftest\selftest_ntp.c" />
<ClCompile Include="src\selftest\selftest_ntp_DST.c" />
<ClCompile Include="src\selftest\selftest_repeatingEvents.c" />
<ClCompile Include="src\selftest\selftest_role_toggleAll.c" />
<ClCompile Include="src\selftest\selftest_script.c" />
Expand Down Expand Up @@ -1173,4 +1174,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
3 changes: 2 additions & 1 deletion openBeken_win32_mvsc2017.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@
<ClCompile Include="src\selftest\selftest_mqtt.c" />
<ClCompile Include="src\selftest\selftest_multiplePinsOnChannel.c" />
<ClCompile Include="src\selftest\selftest_ntp.c" />
<ClCompile Include="src\selftest\selftest_ntp_DST.c" />
<ClCompile Include="src\selftest\selftest_repeatingEvents.c" />
<ClCompile Include="src\selftest\selftest_role_toggleAll.c" />
<ClCompile Include="src\selftest\selftest_script.c" />
Expand Down Expand Up @@ -535,4 +536,4 @@
<CustomBuild Include="src\rgb2hsv.h" />
<CustomBuild Include="..\..\platforms\bk7231t\bk7231t_os\application.mk" />
</ItemGroup>
</Project>
</Project>
13 changes: 12 additions & 1 deletion src/cmnds/cmd_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,11 @@ float getMonth(const char *s) {
float getMDay(const char *s) {
return NTP_GetMDay();
}

#if ENABLE_NTP_DST
float isDST(const char *s){
return Time_IsDST();
}
#endif
#if ENABLE_NTP_SUNRISE_SUNSET

float getSunrise(const char *s) {
Expand Down Expand Up @@ -475,6 +479,13 @@ const constant_t g_constants[] = {
////cnstdetail:"descr":"",
////cnstdetail:"requires":""}
{ "$today", &getToday },
#if ENABLE_NTP_DST
////cnstdetail:{"name":"$isDST",
////cnstdetail:"title":"$isDST",
////cnstdetail:"descr":"",
////cnstdetail:"requires":""}
{ "$isDST", &isDST },
#endif
#if ENABLE_NTP_SUNRISE_SUNSET
////cnstdetail:{"name":"$sunrise",
////cnstdetail:"title":"$sunrise",
Expand Down
243 changes: 237 additions & 6 deletions src/driver/drv_ntp.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

#include "drv_ntp.h"

#define LTSTR "Local Time: %04d-%02d-%02d %02d:%02d:%02d"
#define LTM2TIME(T) (T)->tm_year+1900, (T)->tm_mon+1, (T)->tm_mday, (T)->tm_hour, (T)->tm_min, (T)->tm_sec

extern void NTP_Init_Events(void);
extern void NTP_RunEvents(unsigned int newTime, bool bTimeValid);

Expand All @@ -22,6 +25,19 @@ extern void NTP_CalculateSunrise(byte *outHour, byte *outMinute);
extern void NTP_CalculateSunset(byte *outHour, byte *outMinute);
#endif

#if ENABLE_NTP_DST
static uint32_t Start_DST_epoch=0 , End_DST_epoch=0, next_DST_switch_epoch=0;
static uint8_t nthWeekEnd, monthEnd, dayEnd, hourEnd, nthWeekStart, monthStart, dayStart, hourStart;
static int8_t g_DST_offset=0, g_DST=-128; // g_DST_offset: offset during DST; 0: unset / g_DST: actual DST_offset in hours; -128: not initialised
#define useDST (g_DST_offset)
const uint32_t SECS_PER_MIN = 60UL;
const uint32_t SECS_PER_HOUR = 3600UL;
const uint32_t SECS_PER_DAY = 3600UL * 24UL;
const uint32_t MINS_PER_HOUR = 60UL;
#define LEAP_YEAR(Y) ((!(Y%4) && (Y%100)) || !(Y%400))
static const uint8_t DaysMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
#endif

#define LOG_FEATURE LOG_FEATURE_NTP

typedef struct
Expand Down Expand Up @@ -104,7 +120,18 @@ commandResult_t NTP_SetTimeZoneOfs(const void *context, const char *cmd, const c
}
g_ntpTime -= oldOfs;
g_ntpTime += g_timeOffsetSeconds;
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP offset set");
#if ENABLE_NTP_DST
// in rare cases time can be decreased so time of next DST is wrong
// e.g. by mistake we set offset to two hours in EU and we have just passed start of summertime - next DST switch will be end of summertime
// if we now adjust the clock to the correct offset of one hour, we are slightliy before start of summertime
// so just to be sure, recalculate DST in any case
setDST(1); // setDST will take care of all details
#endif
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP offset set"
#if ENABLE_NTP_DST
" - DST offset set to %i hours",g_DST
#endif
);
return CMD_RES_OK;
}

Expand Down Expand Up @@ -185,6 +212,182 @@ commandResult_t NTP_Info(const void *context, const char *cmd, const char *args,
addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "Server=%s, Time offset=%d", CFG_GetNTPServer(), g_timeOffsetSeconds);
return CMD_RES_OK;
}

#if ENABLE_NTP_DST

// derived from tasmotas "RuleToTime"

uint32_t RuleToTime(uint8_t dow, uint8_t mo, uint8_t week, uint8_t hr, int yr) {
struct tm tm={0};
uint32_t t=0;
int i;
uint8_t m=mo-1; // we use struct tm here, so month values are 0..11
uint8_t w=week; // m and w are copies of mo(nth) and week

if (0 == w) { // for "Last XX" (w=0), we compute first occurence in following month and go back 7 days
if (++m > 11) { // so go to the next month ...
m = 0; // .. and to next year, if we need last XX in December
yr++;
}
w = 1; // search first week of next month, we will subtract 7 days later
}

// avoid mktime - this enlarges the image especially for BL602!
// so calculate seconds from epoch locally
// we start by calculating the number of days since 1970 - will also be used to get weekday
uint16_t days;
days = (yr - 1970) * 365; // days per full years
// add one day every leap year - first leap after 1970 is 1972, possible leap years every 4 years
for (i=1972; i < yr; i+=4) days += LEAP_YEAR(i);
for (i=0; i<m; i++){
if (i==1 && LEAP_YEAR(yr)){
days += 29 ;
} else {
days += DaysMonth[i];
}
}
// we search for the first day of a month, so no offset for days needed

// calculate weekday from number of days since 1970
uint8_t wday = (days + 4) % 7 +1 ; // 1970-01-01 is Thursday ( = 4) -- since tasmota defines Sunday=1, add 1
// calc seconds from days and add hours
t = days * SECS_PER_DAY + hr * SECS_PER_HOUR;

t += (7 * (w - 1) + (dow - wday + 7) % 7) * SECS_PER_DAY;


/*
tm.tm_hour = hr;
// tm is set to {0}, so no need to set minute or second values equal to "0"
tm.tm_mday = 1; // first day of (next) month
tm.tm_mon = m ;
tm.tm_year = yr - 1900;
t = (uint32_t)mktime(&tm); // First day of the month, or first day of next month for "Last" rules
// since we use time functions, weekdays are 0 to 6 but tasmota settings use 1 to 7 (e.g. Sunday = 1)
// so we have to subtract 1 from dow to match tm_weekday here
t += (7 * (w - 1) + (dow - 1 - tm.tm_wday + 7) % 7) * SECS_PER_DAY;
*/

if (0 == week) {
t -= 7 * SECS_PER_DAY; // back one week if this is a "Last XX" rule
}
return t;
}

uint32_t setDST(bool setNTP)
{
if (useDST && g_synced){
int year=NTP_GetYear();
time_t tempt;
int8_t old_DST=0;
char tmp[40]; // to hold date string of timestamp
Start_DST_epoch = RuleToTime(dayStart,monthStart,nthWeekStart,hourStart,year);
End_DST_epoch = RuleToTime(dayEnd,monthEnd,nthWeekEnd,hourEnd,year);
old_DST = g_DST%128; // 0 if "unset" because -128%128 = 0

if ( Start_DST_epoch < End_DST_epoch ) { // Northern --> begin before end
if (g_ntpTime < Start_DST_epoch) {
// we are in winter time before start of summer time
next_DST_switch_epoch=Start_DST_epoch;
g_DST=0;
// tempt = (time_t)Start_DST_epoch;
// ADDLOG_INFO(LOG_FEATURE_RAW, "Before first DST switch in %i. Info: DST starts at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tempt));
} else if (g_ntpTime < End_DST_epoch ){
// we in summer time
next_DST_switch_epoch=End_DST_epoch;
g_DST=g_DST_offset;
// tempt = (time_t)End_DST_epoch;
// ADDLOG_INFO(LOG_FEATURE_RAW, "In DST of %i. Info: DST ends at %lu (%.24s local time)\r\n",year,End_DST_epoch,ctime(&tempt));
} else {
// we are in winter time, after summer time --> next DST starts in next year
Start_DST_epoch = RuleToTime(dayStart,monthStart,nthWeekStart,hourStart,year+1);
next_DST_switch_epoch=Start_DST_epoch;
g_DST=0;
// tempt = (time_t)Start_DST_epoch;
// ADDLOG_INFO(LOG_FEATURE_RAW, "After DST in %i. Info: Next DST start in next year at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tempt));
}
} else { // so end of DST before begin of DST --> southern
if (g_ntpTime < End_DST_epoch) {
// we in summer time at beginning of the yeay
next_DST_switch_epoch=End_DST_epoch;
g_DST=g_DST_offset;
// tempt = (time_t)End_DST_epoch;
// ADDLOG_INFO(LOG_FEATURE_RAW, "In first DST period of %i. Info: DST ends at %lu (%.24s local time)\r\n",year,End_DST_epoch,ctime(&tempt));
} else if (g_ntpTime < Start_DST_epoch ){
// we are in winter time
next_DST_switch_epoch=Start_DST_epoch;
g_DST=0;
// tempt = (time_t)Start_DST_epoch;
// ADDLOG_INFO(LOG_FEATURE_RAW, "Regular time of %i. Info: DST starts at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tempt));
} else {
// we in summer time at the end of the year --> DST will end next year
End_DST_epoch = RuleToTime(dayEnd,monthEnd,nthWeekEnd,hourEnd,year+1);
next_DST_switch_epoch=End_DST_epoch;
g_DST=g_DST_offset;
// tempt = (time_t)End_DST_epoch;
// ADDLOG_INFO(LOG_FEATURE_RAW, "In second DST of %i. Info: DST ends next year at %lu (%.24s local time)\r\n",year,End_DST_epoch,ctime(&tempt));
}
}
g_ntpTime += (g_DST-old_DST)*3600*setNTP;
tempt = (time_t)next_DST_switch_epoch;

struct tm *ltm;
ltm = gmtime(&tempt);
ADDLOG_INFO(LOG_FEATURE_RAW, "In %s time - next DST switch at %lu (" LTSTR ")\r\n",
(g_DST)?"summer":"standard", next_DST_switch_epoch, LTM2TIME(ltm));
return g_DST;
}
else return 0; // DST not (yet) set or can't be calculated (if ntp not synced)

}

int IsDST()
{
if (( g_DST == -128) || (g_ntpTime > next_DST_switch_epoch)) return (setDST(1)); // only in case we don't know DST status, calculate it - and while at it: set ntpTime correctly...
return (g_DST); // otherwise we can safely return the prevously calkulated value
}

commandResult_t CLOCK_CalcDST(const void *context, const char *cmd, const char *args, int cmdFlags) {
int year=NTP_GetYear();
time_t te,tb; // time_t of timestamps needed for ctime() to get string of timestamp

Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES);
// following check must be done after 'Tokenizer_TokenizeString',
// so we know arguments count in Tokenizer. 'cmd' argument is
// only for warning display
if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 8)) {
return CMD_RES_NOT_ENOUGH_ARGUMENTS;
}

// ADDLOG_INFO(LOG_FEATURE_RAW, "CLOCK_SetConfigs: we have %u Args\r\n", Tokenizer_GetArgsCount());
nthWeekEnd = Tokenizer_GetArgInteger(0);
monthEnd = Tokenizer_GetArgInteger(1);
dayEnd = Tokenizer_GetArgInteger(2);
hourEnd = Tokenizer_GetArgInteger(3);
nthWeekStart = Tokenizer_GetArgInteger(4);
monthStart = Tokenizer_GetArgInteger(5);
dayStart = Tokenizer_GetArgInteger(6);
hourStart = Tokenizer_GetArgInteger(7);
g_DST_offset=Tokenizer_GetArgIntegerDefault(8, 1);
ADDLOG_INFO(LOG_FEATURE_RAW, "read values: %u,%u,%u,%u,%u,%u,%u,%u,(%u)\r\n", nthWeekEnd, monthEnd, dayEnd, hourEnd, nthWeekStart, monthStart, dayStart, hourStart,g_DST_offset);

/* Start_DST_epoch = RuleToTime(dayStart,monthStart,nthWeekStart,hourStart,year);
End_DST_epoch = RuleToTime(dayEnd,monthEnd,nthWeekEnd,hourEnd,year);
te=(time_t)End_DST_epoch;
tb=(time_t)Start_DST_epoch;
ADDLOG_INFO(LOG_FEATURE_RAW, "Calculated DST switch epochs in %i. DST start at %lu (%.24s local time) - DST end at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tb),End_DST_epoch,ctime(&te));
*/
return CMD_RES_OK;
};



#endif



int NTP_GetWeekDay() {
struct tm *ltm;

Expand Down Expand Up @@ -269,11 +472,19 @@ int NTP_GetYear() {

return ltm->tm_year+1900;
}
#if ENABLE_NTP_DST
int Time_IsDST(){
return IsDST();
}
#endif
#if WINDOWS
bool b_ntp_simulatedTime = false;
void NTP_SetSimulatedTime(unsigned int timeNow) {
g_ntpTime = timeNow;
g_ntpTime += g_timeOffsetSeconds;
#if ENABLE_NTP_DST
g_ntpTime += setDST(0)*3600;
#endif
g_synced = true;
b_ntp_simulatedTime = true;
}
Expand Down Expand Up @@ -309,7 +520,13 @@ void NTP_Init() {
#if ENABLE_CALENDAR_EVENTS
NTP_Init_Events();
#endif

#if ENABLE_NTP_DST
//cmddetail:{"name":"CLOCK_CalcDST","args":"[nthWeekEnd monthEnd dayEnd hourEnd nthWeekStart monthStart dayStart hourStart [g_DSToffset hours - default is 1 if unset]",
//cmddetail:"descr":"Checks, if actual time is during DST or not.",
//cmddetail:"fn":"CLOCK_CalcDST","file":"driver/drv_ntp.c","requires":"",
//cmddetail:"examples":""}
CMD_RegisterCommand("clock_calcDST",CLOCK_CalcDST, NULL);
#endif

addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP driver initialized with server=%s, offset=%d", CFG_GetNTPServer(), g_timeOffsetSeconds);
g_synced = false;
Expand All @@ -319,7 +536,12 @@ unsigned int NTP_GetCurrentTime() {
return g_ntpTime;
}
unsigned int NTP_GetCurrentTimeWithoutOffset() {
#if ENABLE_NTP_DST
return g_ntpTime - g_timeOffsetSeconds - g_DST%128; // if g_DST is unset, it's -128 --> -128%128 = 0 as needed
#else
return g_ntpTime - g_timeOffsetSeconds;
#endif

}


Expand Down Expand Up @@ -440,10 +662,12 @@ void NTP_CheckForReceive() {

g_ntpTime = secsSince1900 - NTP_OFFSET;
g_ntpTime += g_timeOffsetSeconds;
#if ENABLE_NTP_DST
g_ntpTime += setDST(0)*3600; // just to be sure: recalculate DST before setting, in case we somehow "moved back in time" we start with freshly set g_ntpTime, so don't change it inside setDST()!
#endif
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Unix time : %u",(unsigned int)g_ntpTime);
ltm = gmtime(&g_ntpTime);
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Local Time : %04d-%02d-%02d %02d:%02d:%02d",
ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
addLogAdv(LOG_INFO, LOG_FEATURE_NTP, LTSTR, LTM2TIME(ltm));

if (g_synced == false) {
EventHandlers_FireEvent(CMD_EVENT_NTP_STATE, 1);
Expand Down Expand Up @@ -475,6 +699,13 @@ void NTP_SendRequest_BlockingMode() {
void NTP_OnEverySecond()
{
g_ntpTime++;
#if ENABLE_NTP_DST
if (useDST && (g_ntpTime >= next_DST_switch_epoch)){
int8_t old_DST=g_DST;
setDST(1);
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Passed DST switch time - recalculated DST offset. Was:%i - now:%i",old_DST,g_DST);
}
#endif

#if ENABLE_CALENDAR_EVENTS
NTP_RunEvents(g_ntpTime, g_synced);
Expand Down Expand Up @@ -523,8 +754,8 @@ void NTP_AppendInformationToHTTPIndexPage(http_request_t* request)
ltm = gmtime(&g_ntpTime);

if (g_synced == true)
hprintf255(request, "<h5>NTP (%s): Local Time: %04d-%02d-%02d %02d:%02d:%02d </h5>",
CFG_GetNTPServer(),ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
hprintf255(request, "<h5>NTP (%s): " LTSTR " </h5>",
CFG_GetNTPServer(),LTM2TIME(ltm));
else
hprintf255(request, "<h5>NTP: Syncing with %s....</h5>",CFG_GetNTPServer());
}
Expand Down
7 changes: 7 additions & 0 deletions src/driver/drv_ntp.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ int NTP_PrintEventList();
int NTP_GetEventTime(int id);
int NTP_RemoveClockEvent(int id);
int NTP_ClearEvents();
#if ENABLE_NTP_DST
int Time_IsDST();
// usually we want to set/correct g_ntpTime inside setDST() --> call setDST(1)
// only after setting g_ntpTime freshly from an NTP packet --> call setDST(0)
// we must not alter g_ntpTime inside setDST in this case (the old offsets are no longer valid)
uint32_t setDST(bool setNTP);
#endif

extern time_t g_ntpTime;
extern struct SUN_DATA { /* sunrise / sunset globals */
Expand Down
Loading

0 comments on commit a11c671

Please sign in to comment.