From 946fcd2fe49806495d73138b1138e0812a1e4087 Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Sat, 14 Apr 2018 14:39:16 +0200 Subject: [PATCH 1/3] v5.12.0l - Increase rules save area to 511 chars 5.12.0l * Release rules up to 511 characters * Prepare for feature release - call on translators to update their language files --- README.md | 16 ++++++++++++++-- sonoff/_releasenotes.ino | 6 +++++- sonoff/settings.h | 6 +++--- sonoff/sonoff.h | 6 +++--- sonoff/sonoff.ino | 2 +- sonoff/support.ino | 4 ++-- sonoff/xdrv_10_rules.ino | 10 +++++----- 7 files changed, 33 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 695d7895c..604183bad 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ ## Sonoff-Tasmota -Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. -Current version is **5.12.0k** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. +Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web**, **timers**, 'Over The Air' (**OTA**) firmware updates and **sensors support**, allowing control under **Serial**, **HTTP** and **MQTT**, so as to be used on **Smart Home Systems**. Written for Arduino IDE and PlatformIO. + +[![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) +[![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) +[![License](https://img.shields.io/github/license/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/blob/development/LICENSE.txt) + +If you like **Sonoff Tasmota**, give it a star, or fork it and contribute! +[![GitHub stars](https://img.shields.io/github/stars/arendst/Sonoff-Tasmota.svg?style=social&label=Star)](https://github.com/arendst/Sonoff-Tasmota/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/arendst/Sonoff-Tasmota.svg?style=social&label=Fork)](https://github.com/arendst/Sonoff-Tasmota/network) + +### Development: +[![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) + +Current version is **5.12.0l** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. ### Quick install diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index c441078ef..0860a476d 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,8 @@ -/* 5.12.0k +/* 5.12.0l + * Release rules up to 511 characters + * Prepare for feature release - call on translators to update their language files + * + * 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 diff --git a/sonoff/settings.h b/sonoff/settings.h index c0f8c90b2..3e0d85c7d 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -49,7 +49,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t no_power_on_check : 1; // bit 21 (v5.11.1i) uint32_t mqtt_serial : 1; // bit 22 (v5.12.0f) uint32_t rules_enabled : 1; // bit 23 (v5.12.0j) - uint32_t rules_once : 1; // bit 23 (v5.12.0k) + uint32_t rules_once : 1; // bit 24 (v5.12.0k) uint32_t spare25 : 1; uint32_t spare26 : 1; uint32_t spare27 : 1; @@ -258,9 +258,9 @@ struct SYSCFG { byte free_6b8[72]; // 6B8 - char rules[MAX_RULE_SIZE]; // 700 + char rules[MAX_RULE_SIZE]; // 700 uses 512 bytes in v5.12.0l - // 800 - FFF free locations + // 900 - FFF free locations } Settings; struct RTCMEM { diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index c0a4e9a00..bf9407312 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -48,7 +48,7 @@ typedef unsigned long power_t; // Power (Relay) type #define MAX_FRIENDLYNAMES 4 // Max number of Friendly names #define MAX_DOMOTICZ_IDX 4 // Max number of Domoticz device, key and switch indices #define MAX_DOMOTICZ_SNS_IDX 12 // Max number of Domoticz sensors indices -#define MAX_RULE_SIZE 256 // Max number of characters in rules +#define MAX_RULE_SIZE 512 // Max number of characters in rules #define MODULE SONOFF_BASIC // [Module] Select default model @@ -86,10 +86,10 @@ typedef unsigned long power_t; // Power (Relay) type #define SERIALLOG_TIMER 600 // Seconds to disable SerialLog #define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware -#define INPUT_BUFFER_SIZE 255 // Max number of characters in (serial) command buffer +#define INPUT_BUFFER_SIZE 512 // Max number of characters in (serial and http) command buffer #define CMDSZ 24 // Max number of characters in command #define TOPSZ 100 // Max number of characters in topic string -#define LOGSZ 400 // Max number of characters in log +#define LOGSZ 512 // Max number of characters in log #define MIN_MESSZ 893 // Min number of characters in MQTT message #ifdef USE_MQTT_TLS diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 27dd3b9e0..e1e6e2200 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -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 // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) diff --git a/sonoff/support.ino b/sonoff/support.ino index 6b17f0b75..e200d0240 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -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); diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 61742f0b6..690b5a934 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -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 , around and before 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()); From a612be5bcd78f0a3e5160810129e0423c60cf8cd Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Sat, 14 Apr 2018 18:00:18 +0200 Subject: [PATCH 2/3] Add timer sunrise and sunset offset 5.12.0l * Add timer sunrise and sunset offset (#2378) --- sonoff/_releasenotes.ino | 1 + sonoff/xdrv_09_timers.ino | 129 +++++++++++++++++++++++++++++++------- 2 files changed, 108 insertions(+), 22 deletions(-) diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 0860a476d..44ca02078 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,6 +1,7 @@ /* 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 diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index e864e0062..c30cd153d 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -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+=\"\"+a.substring(i*3,(i*3)+3)+\"\"}" @@ -503,15 +581,22 @@ const char HTTP_FORM_TIMER1[] PROGMEM = "" D_TIMER_REPEAT "" "
" "
" -// "Time " #ifdef USE_SUNRISE "
" "" D_TIMER_TIME " " - "" - " " D_HOUR_MINUTE_SEPARATOR " " - "
" + "" + " " D_HOUR_MINUTE_SEPARATOR " " + "
" "" D_SUNRISE "
" "" D_SUNSET "
" + "
" "
" #else "" D_TIMER_TIME " " @@ -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; } From 96d13d05a7e10cf1c525cf164089f7a72e1957ae Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Sun, 15 Apr 2018 10:18:48 +0200 Subject: [PATCH 3/3] Add contribution --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 604183bad..51c26c777 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead. [![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) [![License](https://img.shields.io/github/license/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/blob/development/LICENSE.txt) -If you like **Sonoff Tasmota**, give it a star, or fork it and contribute! +If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute! [![GitHub stars](https://img.shields.io/github/stars/arendst/Sonoff-Tasmota.svg?style=social&label=Star)](https://github.com/arendst/Sonoff-Tasmota/stargazers) [![GitHub forks](https://img.shields.io/github/forks/arendst/Sonoff-Tasmota.svg?style=social&label=Fork)](https://github.com/arendst/Sonoff-Tasmota/network) @@ -70,6 +70,15 @@ The following devices are supported: - [Luani HVIO board](https://luani.de/projekte/esp8266-hvio/) - Wemos D1 mini, NodeMcu and Ledunia +### Contribute + +You can contribute to Sonoff-Tasmota by +- providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) +- testing new released features and report issues +- donating to acquire hardware for testing and implementating or out of gratitude + +[![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota) + ### License This program is licensed under GPL-3.0 \ No newline at end of file