diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 350f49dfd..df124ecfa 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -36,7 +36,7 @@ enum TimerCommands { CMND_TIMER, CMND_TIMERS }; const char kTimerCommands[] PROGMEM = D_CMND_TIMER "|" D_CMND_TIMERS ; -power_t fired = 0; +uint16_t fired = 0; void TimerEverySecond() { @@ -45,6 +45,7 @@ void TimerEverySecond() uint8_t days = 1 << (RtcTime.day_of_week -1); for (byte i = 0; i < MAX_TIMERS; i++) { + if (Settings.timer[i].device >= devices_present) Settings.timer[i].data = 0; // Reset timer due to change in devices present if (Settings.timer[i].arm) { if (time == Settings.timer[i].time) { if (!bitRead(fired, i) && (Settings.timer[i].days & days)) { @@ -88,67 +89,74 @@ boolean TimerCommand() int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kTimerCommands); if ((CMND_TIMER == command_code) && (index > 0) && (index <= MAX_TIMERS)) { uint8_t error = 0; - if (XdrvMailbox.data_len) { - StaticJsonBuffer<128> jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(dataBufUc); - if (!root.success()) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed - error = 1; - } - else { - char parm_uc[10]; - - index--; - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { - Settings.timer[index].arm = (root[parm_uc] != 0); + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { + if (XdrvMailbox.payload == 0) { + Settings.timer[index -1].data = 0; // Clear timer + } else { + Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; // Copy timer } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { - uint16_t itime = 0; - uint8_t value = 0; - char time_str[10]; + } else { + StaticJsonBuffer<128> jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(dataBufUc); + if (!root.success()) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed + error = 1; + } + else { + char parm_uc[10]; + index--; + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { + Settings.timer[index].arm = (root[parm_uc] != 0); + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { + uint16_t itime = 0; + uint8_t value = 0; + char time_str[10]; - snprintf(time_str, sizeof(time_str), root[parm_uc]); - const char *substr = strtok(time_str, ":"); - if (substr != NULL) { - value = atoi(substr); - if (value > 23) value = 23; - itime = value * 60; - substr = strtok(NULL, ":"); + snprintf(time_str, sizeof(time_str), root[parm_uc]); + const char *substr = strtok(time_str, ":"); if (substr != NULL) { value = atoi(substr); - if (value > 59) value = 59; - itime += value; + if (value > 23) value = 23; + itime = value * 60; + substr = strtok(NULL, ":"); + if (substr != NULL) { + value = atoi(substr); + if (value > 59) value = 59; + itime += value; + } + } + Settings.timer[index].time = itime; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { + // SMTWTFS = 1234567 = 0011001 = 00TW00S = --TW--S + Settings.timer[index].days = 0; + const char *tday = root[parm_uc]; + char ch = '.'; + + uint8_t i = 0; + while ((ch != '\0') && (i < 7)) { + ch = *tday++; + if (ch == '-') ch = '0'; + uint8_t mask = 1 << i++; + Settings.timer[index].days |= (ch == '0') ? 0 : mask; } } - Settings.timer[index].time = itime; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { - // SMTWTFS = 1234567 = 0011001 = 00TW00S = --TW--S - Settings.timer[index].days = 0; - const char *tday = root[parm_uc]; - char ch = '.'; - - uint8_t i = 0; - while ((ch != '\0') && (i < 7)) { - ch = *tday++; - if (ch == '-') ch = '0'; - uint8_t mask = 1 << i++; - Settings.timer[index].days |= (ch == '0') ? 0 : mask; + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { + Settings.timer[index].repeat = (root[parm_uc] != 0); } - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { - Settings.timer[index].repeat = (root[parm_uc] != 0); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DEVICE))].success()) { - Settings.timer[index].device = ((uint8_t)root[parm_uc] -1) & 0x0F; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_POWER))].success()) { - Settings.timer[index].power = (uint8_t)root[parm_uc] & 0x03; - } - if (Settings.timer[index].arm) bitClear(fired, index); + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DEVICE))].success()) { + uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; + Settings.timer[index].device = (device < devices_present) ? device : devices_present -1; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_POWER))].success()) { + Settings.timer[index].power = (uint8_t)root[parm_uc] & 0x03; + } + if (Settings.timer[index].arm) bitClear(fired, index); - index++; + index++; + } } } if (!error) { @@ -181,6 +189,150 @@ boolean TimerCommand() return serviced; } +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB +const char HTTP_TIMER_SCRIPT[] PROGMEM = + "var pt=[],ct=99;" + "function qs(s){" // Save code space + "return document.querySelector(s);" + "}" + "function ce(i,q){" // Create select option + "var o=document.createElement('option');" + "o.textContent=i;" + "q.appendChild(o);" + "}" + "function st(){" // Save parameters to hidden area + "var d,h,i,m,n,s,p;" + "h=qs('#ho');" + "m=qs('#mi');" + "d=qs('#d1');" + "s=0;" + "n=1<<30;if(eb('a0').checked){s|=n;}" // Get arm + "n=1<<29;if(eb('r0').checked){s|=n;}" // Get repeat + "for(i=0;i<7;i++){n=1<<(16+i);if(eb('w'+i).checked){s|=n;}}" // Get weekdays + "s|=(eb('p1').value<<27);" // Get power + "s|=(d.selectedIndex<<23);" // Get device + "s|=((h.selectedIndex*60)+m.selectedIndex)&0x7FF;" // Get time + "pt[ct]=s;" + "eb('t0').value=pt.join();" // Save parameters from array to hidden area + "}" + "function ot(t,e){" + "var d,h,i,m,n,s,tl,p,q;" + "h=qs('#ho');" + "m=qs('#mi');" + "d=qs('#d1');" + "if(ct==99){" // Do this once + "pt=eb('t0').value.split(',').map(Number);" // Get parameters from hidden area to array + "for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,h);}" // Create hours select options + "for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,m);}" // Create minutes select options + "for(i=0;i<}1;i++){ce(i+1,d);}" // Create devices + "}else{" + "st();" // Save changes + "}" + "tl=document.getElementsByClassName('tl');" // Remove the background color of all tablinks/buttons + "for(i=0;i>(16+i))&1;eb('w'+i).checked=p;}" // Set weekdays + "p=(s>>23)&0xF;d.value=p+1;" // Set device + "p=(s>>27)&3;eb('p1').value=p;" // Set power + "p=(s>>29)&1;eb('r0').checked=p;" // Set repeat + "p=(s>>30)&1;eb('a0').checked=p;" // Set arm + "ct=t;" + "}"; +const char HTTP_TIMER_STYLE[] PROGMEM = + ".tl{float:left;border-radius:0;border:1px solid #fff;padding:1px;width:6.25%;}" + ""; +const char HTTP_FORM_TIMER[] PROGMEM = + "
 " D_TIMER_PARAMETERS " 
" + " " + "" D_TIMER_POWER " " + "
" + "
" +// "Time  " + "" D_TIMER_TIME "  :  " + "" D_TIMER_ARM " " + "" D_TIMER_REPEAT "" + "

" + "
"; +const char HTTP_FORM_TIMER2[] PROGMEM = + "type='submit' onclick='st();this.form.submit();'"; + +const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; + +void HandleTimerConfiguration() +{ + if (HTTP_USER == webserver_state) { + HandleRoot(); + return; + } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); + + String page = FPSTR(HTTP_HEAD); + page.replace(F("{v}"), FPSTR(S_CONFIGURE_TIMER)); + page += FPSTR(HTTP_TIMER_SCRIPT); + page += FPSTR(HTTP_HEAD_STYLE); + page.replace(F(""), FPSTR(HTTP_TIMER_STYLE)); + page += FPSTR(HTTP_FORM_TIMER); + for (byte i = 0; i < MAX_TIMERS; i++) { + if (i > 0) page += F(","); + page += String(Settings.timer[i].data); + } + page += F("' hidden>
"); + for (byte i = 0; i < MAX_TIMERS; i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(""), + i, (0 == i) ? " id='dP'" : "", i +1); + page += mqtt_data; + } + page += FPSTR(HTTP_FORM_TIMER1); + page.replace(F("}1"), String(devices_present)); + char day[4] = { 0 }; + for (byte i = 0; i < 7; i++) { + strncpy_P(day, PSTR(D_DAY3LIST) + (i *3), 3); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), i, i, day); + page += mqtt_data; + } + page += F("
"); + + page += FPSTR(HTTP_FORM_END); + page.replace(F("type='submit'"), FPSTR(HTTP_FORM_TIMER2)); + page += F(""); // Get the element with id='defaultOpen' and click on it + page += FPSTR(HTTP_BTN_CONF); + ShowPage(page); +} + +void TimerSaveSettings() +{ + char tmp[MAX_TIMERS *12]; // Need space for MAX_TIMERS x 10 digit numbers separated by a comma + + WebGetArg("t0", tmp, sizeof(tmp)); + char *p = tmp; + for (byte i = 0; i < MAX_TIMERS; i++) { + uint32_t data = strtol(p, &p, 10); + p++; // Skip comma + if ((data & 0x7FF) < 1440) Settings.timer[i].data = data; + } +} +#endif // USE_TIMERS_WEB +#endif // USE_WEBSERVER + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -202,4 +354,4 @@ boolean Xdrv09(byte function) return result; } -#endif // USE_TIMERS \ No newline at end of file +#endif // USE_TIMERS