Merge pull request #53 from arendst/development

Update from Tasmota
This commit is contained in:
Adrian Scillato 2018-04-15 12:07:53 -03:00 committed by GitHub
commit 0fde4051ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 31 deletions

View File

@ -1,4 +1,9 @@
/* 5.12.0k
/* 5.12.0l
* Release rules up to 511 characters
* Prepare for feature release - call on translators to update their language files
* Add timer sunrise and sunset offset (#2378)
*
* 5.12.0k
* Prepare for simple rules of up to 255 characters by enlarging Settings area to now 2048 bytes
* Change Timer parameter name from Power to Action
* Add commands Publish, Rule, RuleTimer and Event. See Wiki about Rule restriction, usage and examples

View File

@ -25,7 +25,7 @@
- Select IDE Tools - Flash Size: "1M (no SPIFFS)"
====================================================*/
#define VERSION 0x050C000B // 5.12.0k
#define VERSION 0x050C000C // 5.12.0l
// Location specific includes
#include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0)

View File

@ -1593,10 +1593,10 @@ void AddLog_P(byte loglevel, const char *formatP, const char *formatP2)
AddLog(loglevel);
}
void AddLogSerial(byte loglevel, uint8_t *buffer, byte count)
void AddLogSerial(byte loglevel, uint8_t *buffer, int count)
{
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SERIAL D_RECEIVED));
for (byte i = 0; i < count; i++) {
for (int i = 0; i < count; i++) {
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer++));
}
AddLog(loglevel);

View File

@ -175,6 +175,43 @@ void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8
*minute_down = UntergangMinuten;
}
void ApplyTimerOffsets(Timer *duskdawn)
{
uint8_t hour[2];
uint8_t minute[2];
Timer stored = (Timer)*duskdawn;
// replace hours, minutes by sunrise
DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]);
uint8_t mode = (duskdawn->mode -1) &1;
duskdawn->time = (hour[mode] *60) + minute[mode];
// apply offsets, check for over- and underflows
uint16_t timeBuffer;
if ((uint16_t)stored.time > 720) {
// negative offset, time after 12:00
timeBuffer = (uint16_t)stored.time - 720;
// check for underflow
if (timeBuffer > (uint16_t)duskdawn->time) {
timeBuffer = 1440 - (timeBuffer - (uint16_t)duskdawn->time);
duskdawn->days = duskdawn->days >> 1;
duskdawn->days = duskdawn->days |= (stored.days << 6);
} else {
timeBuffer = (uint16_t)duskdawn->time - timeBuffer;
}
} else {
// positive offset
timeBuffer = (uint16_t)duskdawn->time + (uint16_t)stored.time;
// check for overflow
if (timeBuffer > 1440) {
timeBuffer -= 1440;
duskdawn->days = duskdawn->days << 1;
duskdawn->days = duskdawn->days |= (stored.days >> 6);
}
}
duskdawn->time = timeBuffer;
}
String GetSun(byte dawn)
{
char stime[6];
@ -212,23 +249,25 @@ void TimerEverySecond()
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
uint16_t set_time = Settings.timer[i].time;
Timer xtimer = Settings.timer[i];
uint16_t set_time = xtimer.time;
#ifdef USE_SUNRISE
if ((1 == Settings.timer[i].mode) || (2 == Settings.timer[i].mode)) { // Sunrise or Sunset
set_time = GetSunMinutes(Settings.timer[i].mode -1);
if ((1 == xtimer.mode) || (2 == xtimer.mode)) { // Sunrise or Sunset
ApplyTimerOffsets(&xtimer);
set_time = xtimer.time;
}
#endif
if (Settings.timer[i].arm) {
if (xtimer.arm) {
if (time == set_time) {
if (Settings.timer[i].days & days) {
Settings.timer[i].arm = Settings.timer[i].repeat;
if (xtimer.days & days) {
Settings.timer[i].arm = xtimer.repeat;
#ifdef USE_RULES
if (3 == Settings.timer[i].power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands
if (3 == xtimer.power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1);
RulesProcess();
} else
#endif // USE_RULES
ExecuteCommandPower(Settings.timer[i].device +1, Settings.timer[i].power);
ExecuteCommandPower(xtimer.device +1, xtimer.power);
}
}
}
@ -241,17 +280,22 @@ void PrepShowTimer(uint8_t index)
{
char days[8] = { 0 };
index--;
Timer xtimer = Settings.timer[index -1];
for (byte i = 0; i < 7; i++) {
uint8_t mask = 1 << i;
snprintf(days, sizeof(days), "%s%d", days, ((Settings.timer[index].days & mask) > 0));
snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0));
}
#ifdef USE_SUNRISE
int16_t hour = xtimer.time / 60;
if ((1 == xtimer.mode) || (2 == xtimer.mode)) { // Sunrise or Sunset
if (hour > 11) hour = (hour -12) * -1;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_MODE "\":%d,\"" D_JSON_TIMER_TIME "\":\"%02d:%02d\",\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d,\"" D_JSON_TIMER_OUTPUT "\":%d,\"" D_JSON_TIMER_ACTION "\":%d}"),
mqtt_data, index +1, Settings.timer[index].arm, Settings.timer[index].mode, Settings.timer[index].time / 60, Settings.timer[index].time % 60, days, Settings.timer[index].repeat, Settings.timer[index].device +1, Settings.timer[index].power);
mqtt_data, index, xtimer.arm, xtimer.mode, hour, xtimer.time % 60, days, xtimer.repeat, xtimer.device +1, xtimer.power);
#else
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_TIME "\":\"%02d:%02d\",\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d,\"" D_JSON_TIMER_OUTPUT "\":%d,\"" D_JSON_TIMER_ACTION "\":%d}"),
mqtt_data, index +1, Settings.timer[index].arm, Settings.timer[index].time / 60, Settings.timer[index].time % 60, days, Settings.timer[index].repeat, Settings.timer[index].device +1, Settings.timer[index].power);
mqtt_data, index, xtimer.arm, xtimer.time / 60, xtimer.time % 60, days, xtimer.repeat, xtimer.device +1, xtimer.power);
#endif // USE_SUNRISE
}
@ -298,18 +342,20 @@ boolean TimerCommand()
#endif
if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) {
uint16_t itime = 0;
uint8_t value = 0;
int8_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 < 0) value = abs(value) +12; // Allow entering timer offset from -11:59 to -00:01 converted to 12:01 to 23:59
if (value > 23) value = 23;
itime = value * 60;
substr = strtok(NULL, ":");
if (substr != NULL) {
value = atoi(substr);
if (value < 0) value = 0;
if (value > 59) value = 59;
itime += value;
}
@ -417,25 +463,50 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM =
"function gt(){" // Set hours and minutas according to mode
"var m,p,q;"
"m=qs('input[name=\"rd\"]:checked').value;" // Get mode
"if(m==0){p=pt[ct]&0x7FF;}" // Schedule time
"if(m==0){p=pt[ct]&0x7FF;so(0);}" // Schedule time, hide offset span
"if(m==1){p=pt[" STR(MAX_TIMERS) "];}" // Sunrise
"if(m==2){p=pt[" STR(MAX_TIMERS +1) "];}" // Sunset
"q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" // Set hours
"q=p%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set minutes
"if((m==1)||(m==2)){" // Sunrise or sunset is set
"p=pt[ct]&0x7FF;" // Load stored time for offset calculation
"q=Math.floor(p/60);" // Parse hours
"if(q>=12){q-=12;qs('#odr').selectedIndex=1;}" // Negative offset
"else{qs('#odr').selectedIndex=0;}"
"if(q<10){q='0'+q;}qs('#oho').value=q;" // Set offset hours
"q=p%60;if(q<10){q='0'+q;}qs('#omi').value=q;" // Set offset minutes
"so(1);" // Show offset span
"}"
"}"
"function so(b){" // Hide or show offset items
"if(b==1){qs('#ofs').style='';}"
"else{qs('#ofs').style='display:none;';}"
"}"
#endif
"function st(){" // Save parameters to hidden area
"var i,n,p,s;"
"s=0;"
"var i,l,m,n,p,s;"
"m=0;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
#ifdef USE_SUNRISE
"m=qs('input[name=\"rd\"]:checked').value;" // Check mode
"s|=(qs('input[name=\"rd\"]:checked').value<<11);" // Get mode
#endif
"s|=(eb('p1').value<<27);" // Get power
"s|=(qs('#d1').selectedIndex<<23);" // Get device
"s|=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;" // Get time
// "s|=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;" // Get time
"if(m==0){s|=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;}" // Get time
#ifdef USE_SUNRISE
"if((m==1)||(m==2)){"
"l=((qs('#oho').selectedIndex*60)+qs('#omi').selectedIndex);" // Buffer offset time
"if(qs('#odr').selectedIndex>0){l+=720;}" // If negative offset, add 12h to given offset time
"s|=l&0x7FF;" // Save offset instead of time
"}"
#endif
"pt[ct]=s;"
"eb('t0').value=pt.join();" // Save parameters from array to hidden area
"}"
@ -468,6 +539,13 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM =
"eb('bt').innerHTML=s;" // Create tabs
"o=qs('#ho');for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,o);}" // Create hours select options
"o=qs('#mi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" // Create minutes select options
#ifdef USE_SUNRISE // NEW: Create offset options (+/- up to 11h, 59m)
"o=qs('#odr');ce('+',o);ce('-',o);" // Create offset direction select options
"o=qs('#oho');for(i=0;i<=11;i++){ce((i<10)?('0'+i):i,o);}" // Create offset hours select options
"o=qs('#omi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" // Create offset minutes select options
#endif
"o=qs('#d1');for(i=0;i<}1;i++){ce(i+1,o);}" // Create devices
"var a='" D_DAY3LIST "';"
"s='';for(i=0;i<7;i++){s+=\"<input style='width:5%;' id='w\"+i+\"' name='w\"+i+\"' type='checkbox'><b>\"+a.substring(i*3,(i*3)+3)+\"</b>\"}"
@ -503,15 +581,22 @@ const char HTTP_FORM_TIMER1[] PROGMEM =
"<input style='width:5%;' id='r0' name='r0' type='checkbox'><b>" D_TIMER_REPEAT "</b>"
"</div><br/>"
"<div>"
// "<b>Time</b>&nbsp;<input type='time' style='width:25%;' id='s1' name='s1' value='00:00' pattern='[0-9]{2}:[0-9]{2}'>"
#ifdef USE_SUNRISE
"<fieldset style='width:299px;margin:auto;text-align:left;border:0;'>"
"<input id='b0' name='rd' type='radio' value='0' onclick='gt();'><b>" D_TIMER_TIME "</b>&nbsp;"
"<span><select style='width:60px;' id='ho' name='ho' onclick='eb(\"b0\").checked=1;'></select></span>"
"&nbsp;" D_HOUR_MINUTE_SEPARATOR "&nbsp;"
"<span><select style='width:60px;' id='mi' name='mi' onclick='eb(\"b0\").checked=1;'></select></span><br/>"
"<span><select style='width:60px;' id='ho' name='ho' onclick='eb(\"b0\").checked=1;so(0);'></select></span>"
"&nbsp;" D_HOUR_MINUTE_SEPARATOR "&nbsp;"
"<span><select style='width:60px;' id='mi' name='mi' onclick='eb(\"b0\").checked=1;so(0);'></select></span><br/>"
"<input id='b1' name='rd' type='radio' value='1' onclick='gt();'><b>" D_SUNRISE "</b><br/>"
"<input id='b2' name='rd' type='radio' value='2' onclick='gt();'><b>" D_SUNSET "</b><br/>"
"<span id='ofs' style='display:none;'>"
"&nbsp;"
"<span><select style='width:46px;' id='odr' name='odr'></select></span>"
"&nbsp;"
"<span><select style='width:60px;' id='oho' name='oho'></select></span>"
"&nbsp;" D_HOUR_MINUTE_SEPARATOR "&nbsp;"
"<span><select style='width:60px;' id='omi' name='omi'></select></span>"
"</span><br/>"
"</fieldset>"
#else
"<b>" D_TIMER_TIME "</b>&nbsp;"
@ -573,7 +658,7 @@ void TimerSaveSettings()
p++; // Skip comma
if (timer.time < 1440) {
#ifdef USE_SUNRISE
if ((1 == timer.mode) || (2 == timer.mode)) timer.time = Settings.timer[i].time; // Do not save time on Sunrise or Sunset
// if ((1 == timer.mode) || (2 == timer.mode)) timer.time = Settings.timer[i].time; // Do not save time on Sunrise or Sunset
#endif
Settings.timer[i].data = timer.data;
}

View File

@ -40,10 +40,10 @@
* on rules#timer=1 do color 080800 endon
* on mqtt#connected do color 000010 endon on mqtt#disconnected do color 001010 endon on time#initialized do color 001000 endon on time#set do backlog color 000810;ruletimer1 10 endon on rules#timer=1 do color 080800 endon
* on event#anyname do color 100000 endon
* on event#anyname do color %eventvalue% endon
* on event#anyname do color %value% endon
* on power1#state=1 do color 001000 endon
* on button1#state do publish cmnd/ring2/power %eventvalue% endon on button2#state do publish cmnd/strip1/power %eventvalue% endon
* on switch1#state do power2 %eventvalue% endon
* on button1#state do publish cmnd/ring2/power %value% endon on button2#state do publish cmnd/strip1/power %value% endon
* on switch1#state do power2 %value% endon
*
* Notes:
* Spaces after <on>, around <do> and before <endon> are mandatory
@ -185,7 +185,7 @@ bool RulesRuleMatch(String &event, String &rule)
if (!root[rule_task][rule_name].success()) return false;
// No value but rule_name is ok
rules_event_value = str_value; // Prepare %eventvalue%
rules_event_value = str_value; // Prepare %value%
// Step 3: Compare rule (value)
if (str_value) {
@ -260,7 +260,7 @@ bool RulesProcess()
rules_event_value = "";
String event = event_saved;
if (RulesRuleMatch(event, event_trigger)) {
commands.replace(F("%eventvalue%"), rules_event_value);
commands.replace(F("%value%"), rules_event_value);
char command[commands.length() +1];
snprintf(command, sizeof(command), commands.c_str());