diff --git a/lib/Adafruit_SGP30-1.0.0.13/.github/ISSUE_TEMPLATE.md b/lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md
old mode 100644
new mode 100755
similarity index 100%
rename from lib/Adafruit_SGP30-1.0.0.13/.github/ISSUE_TEMPLATE.md
rename to lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md
diff --git a/lib/Adafruit_SGP30-1.0.0.13/.github/PULL_REQUEST_TEMPLATE.md b/lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md
old mode 100644
new mode 100755
similarity index 100%
rename from lib/Adafruit_SGP30-1.0.0.13/.github/PULL_REQUEST_TEMPLATE.md
rename to lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md
diff --git a/lib/Adafruit_SGP30-1.0.0.13/.gitignore b/lib/Adafruit_SGP30-1.0.3/.gitignore
old mode 100644
new mode 100755
similarity index 100%
rename from lib/Adafruit_SGP30-1.0.0.13/.gitignore
rename to lib/Adafruit_SGP30-1.0.3/.gitignore
diff --git a/lib/Adafruit_SGP30-1.0.0.13/.travis.yml b/lib/Adafruit_SGP30-1.0.3/.travis.yml
old mode 100644
new mode 100755
similarity index 100%
rename from lib/Adafruit_SGP30-1.0.0.13/.travis.yml
rename to lib/Adafruit_SGP30-1.0.3/.travis.yml
diff --git a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.cpp b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp
old mode 100644
new mode 100755
similarity index 83%
rename from lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.cpp
rename to lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp
index b2ccbe8da..ce6116863
--- a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.cpp
+++ b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp
@@ -37,7 +37,7 @@
//#define I2C_DEBUG
/**************************************************************************/
-/*!
+/*!
@brief Instantiates a new SGP30 class
*/
/**************************************************************************/
@@ -45,7 +45,7 @@ Adafruit_SGP30::Adafruit_SGP30() {
}
/**************************************************************************/
-/*!
+/*!
@brief Setups the hardware and detects a valid SGP30. Initializes I2C
then reads the serialnumber and checks that we are talking to an SGP30
@param theWire Optional pointer to I2C interface, otherwise use Wire
@@ -60,31 +60,32 @@ boolean Adafruit_SGP30::begin(TwoWire *theWire) {
_i2c = theWire;
}
- _i2c->begin();
+// assume i2c initialized already to avoid resetting clock stretching
+// _i2c->begin();
+
-
uint8_t command[2];
command[0] = 0x36;
command[1] = 0x82;
- if (! readWordFromCommand(command, 2, 10, serialnumber, 3))
+ if (! readWordFromCommand(command, 2, 10, serialnumber, 3))
return false;
uint16_t featureset;
command[0] = 0x20;
command[1] = 0x2F;
- if (! readWordFromCommand(command, 2, 10, &featureset, 1))
+ if (! readWordFromCommand(command, 2, 10, &featureset, 1))
return false;
//Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
- if (featureset != SGP30_FEATURESET)
+ if (featureset != SGP30_FEATURESET)
return false;
- if (! IAQinit())
+ if (! IAQinit())
return false;
return true;
}
/**************************************************************************/
-/*!
+/*!
@brief Commands the sensor to begin the IAQ algorithm. Must be called after startup.
@returns True if command completed successfully, false if something went wrong!
*/
@@ -97,7 +98,7 @@ boolean Adafruit_SGP30::IAQinit(void) {
}
/**************************************************************************/
-/*!
+/*!
@brief Commands the sensor to take a single eCO2/VOC measurement. Places results in {@link TVOC} and {@link eCO2}
@returns True if command completed successfully, false if something went wrong!
*/
@@ -113,9 +114,9 @@ boolean Adafruit_SGP30::IAQmeasure(void) {
eCO2 = reply[0];
return true;
}
-
+
/**************************************************************************/
-/*!
+/*!
@brief Request baseline calibration values for both CO2 and TVOC IAQ calculations. Places results in parameter memory locaitons.
@param eco2_base A pointer to a uint16_t which we will save the calibration value to
@param tvoc_base A pointer to a uint16_t which we will save the calibration value to
@@ -135,7 +136,7 @@ boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base)
}
/**************************************************************************/
-/*!
+/*!
@brief Assign baseline calibration values for both CO2 and TVOC IAQ calculations.
@param eco2_base A uint16_t which we will save the calibration value from
@param tvoc_base A uint16_t which we will save the calibration value from
@@ -157,7 +158,30 @@ boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
}
/**************************************************************************/
-/*!
+/*!
+ @brief Set the absolute humidity value [mg/m^3] for compensation to increase precision of TVOC and eCO2.
+ @param absolute_humidity A uint32_t [mg/m^3] which we will be used for compensation. If the absolute humidity is set to zero, humidity compensation will be disabled.
+ @returns True if command completed successfully, false if something went wrong!
+*/
+/**************************************************************************/
+boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
+ if (absolute_humidity > 256000) {
+ return false;
+ }
+
+ uint16_t ah_scaled = (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
+ uint8_t command[5];
+ command[0] = 0x20;
+ command[1] = 0x61;
+ command[2] = ah_scaled >> 8;
+ command[3] = ah_scaled & 0xFF;
+ command[4] = generateCRC(command+2, 2);
+
+ return readWordFromCommand(command, 5, 10);
+}
+
+/**************************************************************************/
+/*!
@brief I2C low level interfacing
*/
/**************************************************************************/
@@ -186,16 +210,16 @@ boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[], uint8_t commandLe
delay(delayms);
- if (readlen == 0)
+ if (readlen == 0)
return true;
uint8_t replylen = readlen * (SGP30_WORD_LEN +1);
- if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
+ if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
return false;
uint8_t replybuffer[replylen];
#ifdef I2C_DEBUG
Serial.print("\t\t<- ");
-#endif
+#endif
for (uint8_t i=0; iread();
#ifdef I2C_DEBUG
diff --git a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.h b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h
old mode 100644
new mode 100755
similarity index 97%
rename from lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.h
rename to lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h
index cc95fa54b..6f27aad04
--- a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.h
+++ b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h
@@ -42,6 +42,7 @@ class Adafruit_SGP30 {
boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
+ boolean setHumidity(uint32_t absolute_humidity);
/**
* The last measurement of the IAQ-calculated Total Volatile Organic Compounds in ppb. This value is set when you call {@link IAQmeasure()}
diff --git a/lib/Adafruit_SGP30-1.0.0.13/README.md b/lib/Adafruit_SGP30-1.0.3/README.md
old mode 100644
new mode 100755
similarity index 100%
rename from lib/Adafruit_SGP30-1.0.0.13/README.md
rename to lib/Adafruit_SGP30-1.0.3/README.md
diff --git a/lib/Adafruit_SGP30-1.0.0.13/examples/sgp30test/sgp30test.ino b/lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino
old mode 100644
new mode 100755
similarity index 58%
rename from lib/Adafruit_SGP30-1.0.0.13/examples/sgp30test/sgp30test.ino
rename to lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino
index 6b9b3ea05..b7ff8a70c
--- a/lib/Adafruit_SGP30-1.0.0.13/examples/sgp30test/sgp30test.ino
+++ b/lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino
@@ -3,6 +3,17 @@
Adafruit_SGP30 sgp;
+/* return absolute humidity [mg/m^3] with approximation formula
+* @param temperature [°C]
+* @param humidity [%RH]
+*/
+uint32_t getAbsoluteHumidity(float temperature, float humidity) {
+ // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
+ const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
+ const uint32_t absoluteHumidityScaled = static_cast(1000.0f * absoluteHumidity); // [mg/m^3]
+ return absoluteHumidityScaled;
+}
+
void setup() {
Serial.begin(9600);
Serial.println("SGP30 test");
@@ -22,6 +33,11 @@ void setup() {
int counter = 0;
void loop() {
+ // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals
+ //float temperature = 22.1; // [°C]
+ //float humidity = 45.2; // [%RH]
+ //sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));
+
if (! sgp.IAQmeasure()) {
Serial.println("Measurement failed");
return;
diff --git a/lib/Adafruit_SGP30-1.0.0.13/library.properties b/lib/Adafruit_SGP30-1.0.3/library.properties
old mode 100644
new mode 100755
similarity index 95%
rename from lib/Adafruit_SGP30-1.0.0.13/library.properties
rename to lib/Adafruit_SGP30-1.0.3/library.properties
index 3520b4d38..6c86464d1
--- a/lib/Adafruit_SGP30-1.0.0.13/library.properties
+++ b/lib/Adafruit_SGP30-1.0.3/library.properties
@@ -1,5 +1,5 @@
name=Adafruit SGP30 Sensor
-version=1.0.2
+version=1.0.3
author=Adafruit
maintainer=Adafruit
sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor
diff --git a/lib/Adafruit_SGP30-1.0.0.13/license.txt b/lib/Adafruit_SGP30-1.0.3/license.txt
old mode 100644
new mode 100755
similarity index 100%
rename from lib/Adafruit_SGP30-1.0.0.13/license.txt
rename to lib/Adafruit_SGP30-1.0.3/license.txt
diff --git a/platformio.ini b/platformio.ini
old mode 100644
new mode 100755
index 57dbddc59..c2d09ace8
--- a/platformio.ini
+++ b/platformio.ini
@@ -65,9 +65,9 @@ build_flags = ${esp82xx_defaults.build_flags}
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
-DVTABLES_IN_FLASH
-[core_2_5_1]
-; *** Esp8266 core for Arduino version 2.5.1
-platform = espressif8266@~2.1.1
+[core_2_5_2]
+; *** Esp8266 core for Arduino version 2.5.2
+platform = espressif8266@~2.2.0
build_flags = ${esp82xx_defaults.build_flags}
-Wl,-Teagle.flash.1m.ld
; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473
@@ -133,8 +133,8 @@ build_flags = ${esp82xx_defaults.build_flags}
;build_flags = ${core_2_3_0.build_flags}
;platform = ${core_2_4_2.platform}
;build_flags = ${core_2_4_2.build_flags}
-platform = ${core_2_5_1.platform}
-build_flags = ${core_2_5_1.build_flags}
+platform = ${core_2_5_2.platform}
+build_flags = ${core_2_5_2.build_flags}
;platform = ${core_stage.platform}
;build_flags = ${core_stage.build_flags}
diff --git a/scripter.md b/scripter.md
new file mode 100644
index 000000000..5b9ac7840
--- /dev/null
+++ b/scripter.md
@@ -0,0 +1,621 @@
+**Script Language for Tasmota**
+
+As an alternative to rules. (about 14,2k flash size, variable ram size)
+
+In submenu Configuration =\> edit script
+1535 bytes max script size (uses rules buffer)
+
+to enable:
+\#define USE_SCRIPT
+\#undef USE_RULES
+
+
+Up to 50 variables (45 numeric and 5 strings, maybe changed by #define)
+Freely definable variable names (all names are intentionally case sensitive)
+Nested if,then,else up to a level of 8
+Math operators **+,-,\*,/,%,&,|,^**
+all operators may be used in the op= form e.g. **+=**
+Left right evaluation with optional brackets
+all numbers are float
+e.g. temp=hum\*(100/37.5)+temp-(timer\*hum%10)
+no spaces allowed between math operations
+Comparison operators **==,!=,\>,\>=,<,<=**
+**and** , **or** support
+
+strings support **+** and **+=** operators
+string comparison **==,!=**
+max string size = 19 chars (default, can be increased or decreased by optional >D parameter)
+
+**Comments** start with **;**
+
+**Sections** defined:
+
+>**\>D ssize**
+ssize = optional max stringsize (default=19)
+define and init variables here, must be the first section, no other code allowed
+**p:**vname specifies permanent vars (the number of permanent vars is limited by tasmota rules space (50 bytes)
+numeric var=4 bytes, string var=lenght of string+1)
+**t:**vname specifies countdown timers, if >0 they are decremented in seconds until zero is reached. see example below
+**i:**vname specifies auto increment counters if >=0 (in seconds)
+**m:**vname specifies a median filter variable with 5 entries (for elimination of outliers)
+**M:**vname specifies a moving average filter variable with 8 entries (for smoothing data)
+(max 5 filters in total m+M)
+
+>all variable names length taken together may not exceed 256 characters, so keep variable names as short as possible.
+memory is dynamically allocated as a result of the D section.
+copying a string to a number or reverse is supported
+
+>**\>B**
+executed on BOOT time
+
+>**\>T**
+executed on teleperiod time (**SENSOR** and **STATE**), get tele vars only in this section
+
+>**\>S**
+executed every second
+
+>**\>E**
+executed e.g. on power change and mqtt **RESULT**
+
+>**\>R**
+executed on restart, p vars are saved automatically after this call
+
+
+special variables (read only):
+
+>**upsecs** = seconds since start
+**uptime** = minutes since start
+**time** = minutes since midnight
+**sunrise** = sunrise minutes since midnight
+**sunset** = sunset minutes since midnight
+**tper** = teleperiod (may be set also)
+**tstamp** = timestamp (local date and time)
+**topic** = mqtt topic
+**gtopic** = mqtt group topic
+**prefixn** = prefix n = 1-3
+**pwr[x]** = tasmota power state (x = 1-N)
+**sw[x]** = tasmota switch state (x = 1-N)
+>**pin[x]** = gpio pin level (x = 0-16)
+**pn[x]** = pin number for sensor code x, 99 if none
+**pd[x]** = defined sensor for gpio pin nr x none=999
+**gtmp** = global temperature
+**ghum** = global humidity
+**gprs** = global pressure
+**pow(x y)** = calculates the power of x^y
+**med(n x)** = calculates a 5 value median filter of x (2 filters possible n=0,1)
+**int(x)** = gets the integer part of x (like floor)
+**hn(x)** = converts x (0..255) zu a hex nibble string
+**mqtts** = state of mqtt disconnected=0, connected>0
+**wifis** = state of wifi disconnected=0, connected>0
+
+>**hours** = hours
+**mins** = mins
+**secs** = seconds
+**day** = day of month
+**wday** = day of week
+**month** = month
+**year** = year
+
+these variables are cleared after reading true
+>**chg[var]** = true if a variables value was changed (numeric vars only)
+**upd[var]** = true if a variable was updated
+**boot** = true on BOOT
+**tinit** = true on time init
+**tset** = true on time set
+**mqttc** = true on mqtt connect
+**mqttd** = true on mqtt disconnect
+**wific** = true on wifi connect
+**wifid** = true on wifi disconnect
+
+system vars (for debugging)
+>**stack** = stack size
+**heap** = heap size
+**ram** = used ram size
+**slen** = script length
+**micros** = running microseconds
+**millis** = running milliseconds
+**loglvl** = loglevel of script cmds, may be set also
+
+remarks:
+if you define a variable with the same name as a special
+variable that special variable is discarded
+
+
+**Tasmota** cmds start with **=\>**
+within cmds you can replace text with variables with **%varname%**
+a single percent sign must be given as **%%**
+
+**special** cmds:
+
+>**=\> print** prints to info log for debugging
+
+to save code space nearly no error messages are provided. However it is taken care of that at least it should not crash on syntax errors.
+if a variable does not exist a **???** is given on commands
+if a **SENSOR** or **STATUS** or **RESULT** message or a var does not exist the destination variable is NOT updated.
+
+2 possibilities for conditionals:
+>**if** a==b
+**and** x==y
+**or** k==i
+**then** => do this
+**else** => do that
+**endif**
+
+OR
+
+>**if** a==b
+**and** x==y
+**or** k==i **{**
+ => do this
+**} else {**
+ => do that
+**}**
+
+you may NOT mix both methods
+
+also possible e.g.
+
+>if var1-var2==var3*var4
+then
+
+remarks:
+the last closing bracket must be on a single line
+the condition may not be enclosed in brackets
+
+>**break** exits a section or terminates a for next loop
+**dprecx** sets decimal precision to x (0-9)
+**svars** save permanent vars
+**delay(x)** pauses x milliseconds (should be as short as possible)
+**spin(x m)** set gpio pin x (0-16) to value m (0,1) only the last bit is used, so even values set the pin to zero and uneven values set the pin to 1
+**spinm(x m)** set pin mode gpio pin x (0-16) to mode m (input=0,output=1)
+
+>**#name** names a subroutine, subroutines are called with **=#name**
+**#name(param)** names a subroutines with a parameter is called with **=#name(param)**
+subroutines end with the next '#' or '>' line or break, may be nested
+params can be numbers or strings and on mismatch are converted
+
+>**for var from to inc**
+**next**
+specifies a for next loop, (loop count must not be less then 1)
+
+>**switch x**
+**case a**
+**case b**
+**ends**
+specifies a switch case selector
+
+**konsole script cmds**
+>**script 1 or 0** switch script on or off
+**script >cmdline** executes the script cmdline
+can be used e.g. to set variables e.g. **script >mintmp=15**
+more then one line may be executed seperated by a semicolon e.g. **script >mintmp=15;maxtemp=40**
+script itself cant be set because the size would not fit the mqtt buffers
+
+***example script***
+meant to show some of the possibilities
+(actually this code ist too large)
+
+**\>D**
+; define all vars here
+p:mintmp=10 (p:means permanent)
+p:maxtmp=30
+t:timer1=30 (t:means countdown timer)
+t:mt=0
+i:count=0 (i:means auto counter)
+hello="hello world"
+string="xxx"
+url="[192.168.178.86]"
+hum=0
+temp=0
+timer=0
+dimmer=0
+sw=0
+rssi=0
+param=0
+
+col=""
+ocol=""
+chan1=0
+chan2=0
+chan3=0
+
+ahum=0
+atemp=0
+tcnt=0
+hour=0
+state=1
+m:med5=0
+M:movav=0
+
+**\>B**
+
+string=hello+"how are you?"
+=\>print BOOT executed
+=\>print %hello%
+=\>mp3track 1
+
+; list gpio pin definitions
+for cnt 0 16 1
+tmp=pd[cnt]
+=>print %cnt% = %tmp%
+next
+
+; get gpio pin for relais 1
+tmp=pn[21]
+=>print relais 1 is on pin %tmp%
+
+; pulse relais over raw gpio
+spin(tmp 1)
+delay(100)
+spin(tmp 0)
+
+; raw pin level
+=>print level of gpio1 %pin[1]%
+
+; pulse over tasmota cmd
+=>power 1
+delay(100)
+=>power 0
+
+**\>T**
+
+hum=BME280#Humidity
+temp=BME280#Temperature
+rssi=Wifi#RSSI
+string=SleepMode
+
+; add to median filter
+median=temp
+; add to moving average filter
+movav=hum
+
+; show filtered results
+=>print %median% %movav%
+
+if chg[rssi]>0
+then =>print rssi changed to %rssi%
+endif
+
+if temp\>30
+and hum\>70
+then =\>print damn hot!
+endif
+
+**\>S**
+
+; every second but not completely reliable time here
+; use upsecs and uptime or best t: for reliable timers
+
+; call subrountines with parameters
+=#sub1("hallo")
+=#sub2(999)
+
+; stop timer after expired
+if timer1==0
+then timer1=-1
+=>print timer1 expired
+endif
+
+; auto counter with restart
+if count>=10
+then =>print 10 seconds over
+count=0
+endif
+
+if upsecs%5==0
+then =\>print %upsecs% (every 5 seconds)
+endif
+
+; not recommended for reliable timers
+timer+=1
+if timer\>=5
+then =\>print 5 seconds over (may be)
+timer=0
+endif
+
+dimmer+=1
+if dimmer\>100
+then dimmer=0
+endif
+
+=\>dimmer %dimmer%
+=\>WebSend %url% dimmer %dimmer%
+
+; show on display
+dprec0
+=\>displaytext [c1l1f1s2p20] dimmer=%dimmer%
+
+=\>print %upsecs% %uptime% %time% %sunrise% %sunset% %tstamp%
+
+if time\>sunset
+and time< sunrise
+then
+; night time
+if pwr[1]==0
+then =\>power1 1
+endif
+else
+; day time
+if pwr[1]\>0
+then =\>power1 0
+endif
+endif
+
+; clr display on boot
+if boot\>0
+then =\>displaytext [z]
+endif
+
+; frost warning
+if temp<0
+and mt<=0
+then =#sendmail("frost alert")
+; alarm only every 5 minutes
+mt=300
+=>mp3track 2
+endif
+
+; var has been updated
+if upd[hello]>0
+then =>print %hello%
+endif
+
+; send to Thingspeak every 60 seconds
+; average data in between
+if upsecs%60==0
+then
+ahum/=tcnt
+atemp/=tcnt
+=>Websend [184.106.153.149:80]/update?key=PYUZMVWCICBW492&field1=%atemp%&field2=%ahum%
+tcnt=0
+atemp=0
+ahum=0
+else
+ahum+=hum
+atemp+=temp
+tcnt+=1
+endif
+
+hour=int(time/60)
+if chg[hour]>0
+then
+; exactly every hour
+=>print full hour reached
+endif
+
+if time>5 {
+=>print more then 5 minutes after midnight
+} else {
+=>print less then 5 minutes after midnight
+}
+
+
+; publish abs hum every teleperiod time
+if mqtts>0
+and upsecs%tper==0
+then
+; calc abs humidity
+tmp=pow(2.718281828 (17.67\*temp)/(temp+243.5))
+tmp=(6.112\*tmp\*hum\*18.01534)/((273.15+temp)\*8.31447215)
+; publish median filtered value
+=>Publish tele/%topic%/SENSOR {"Script":{"abshum":%med(0 tmp)%}}
+endif
+
+;switch case state machine
+switch state
+case 1
+=>print state=%state% , start
+state+=1
+case 2
+=>print state=%state%
+state+=1
+case 3
+=>print state=%state% , reset
+state=1
+ends
+
+
+; subroutines
+\#sub1(string)
+=>print sub1: %string%
+\#sub2(param)
+=>print sub2: %param%
+
+\#sendmail(string)
+=>sendmail [smtp.gmail.com:465:user:passwd:::alarm] %string%
+
+**\>E**
+=\>print event executed!
+
+
+; check if switch changed state
+sw=sw[1]
+if chg[sw]>0
+then =\>power1 %sw%
+endif
+
+hello="event occured"
+
+; check for Color change (Color is a string)
+col=Color
+; color change needs 2 string vars
+if col!=ocol
+then ocol=col
+=>print color changed %col%
+endif
+
+; or check change of color channels
+chan1=Channel[1]
+chan2=Channel[2]
+chan3=Channel[3]
+
+if chg[chan1]>0
+or chg[chan2]>0
+or chg[chan3]>0
+then => color has changed
+endif
+
+; compose color string for red
+col=hn(255)+hn(0)+hn(0)
+=>color %col%
+
+**\>R**
+=\>print restarting now
+
+**a real example**
+epaper 29 with sgp30 and bme280
+some vars are set from iobroker
+DisplayText substituted to save script space
+\>D
+hum=0
+temp=0
+press=0
+ahum=0
+tvoc=0
+eco2=0
+zwz=0
+wr1=0
+wr2=0
+wr3=0
+otmp=0
+pwl=0
+tmp=0
+DT="DisplayText"
+; preset units in case they are not available
+punit="hPa"
+tunit="C"
+
+\>B
+;reset auto draw
+=>%DT% [zD0]
+;clr display and draw a frame
+=>%DT% [x0y20h296x0y40h296]
+
+\>T
+; get tele vars
+temp=BME280#Temperature
+hum=BME280#Humidity
+press=BME280#Pressure
+tvoc=SGP30#TVOC
+eco2=SGP30#eCO2
+ahum=SGP30#aHumidity
+tunit=TempUnit
+punit=PressureUnit
+
+\>S
+// update display every teleperiod time
+if upsecs%tper==0
+then
+dprec2
+=>%DT% [f1p7x0y5]%temp% %tunit%
+=>%DT% [p5x70y5]%hum% %%[x250y5t]
+=>%DT% [p11x140y5]%press% %punit%
+=>%DT% [p10x30y25]TVOC: %tvoc% ppb
+=>%DT% [p10x160y25]eCO2: %eco2% ppm
+=>%DT% [p10c26l5]ahum: %ahum% g^m3
+
+dprec0
+=>%DT% [p25c1l5]WR 1 (Dach) : %wr1% W
+=>%DT% [p25c1l6]WR 2 (Garage): %-wr3% W
+=>%DT% [p25c1l7]WR 3 (Garten): %-wr2% W
+=>%DT% [p25c1l8]Aussentemperatur: %otmp% C
+=>%DT% [x170y95r120:30f2p6x185y100] %pwl% %%
+; now update screen
+=>%DT% [d]
+endif
+
+
+\>E
+
+\>R
+
+**another real example**
+ILI 9488 color LCD Display shows various energy graphs
+display switches on and off with proximity sensor
+BMP280 and vl5310x
+some vars are set from iobroker
+
+**>D**
+temp=0
+press=0
+zwz=0
+wr1=0
+wr2=0
+wr3=0
+otmp=0
+pwl=0
+tmp=0
+dist=0
+DT="DisplayText"
+punit="hPa"
+tunit="C"
+hour=0
+
+**>B**
+=>%DT% [z]
+
+// define 2 graphs, 2. has 3 tracks
+=>%DT% [zCi1G2656:5:20:400:80:1440:-5000:5000:3Ci7f3x410y20]+5000 W[x410y95]-5000 W [Ci7f1x70y3] Zweirichtungsz~80hler - 24 Stunden
+=>%DT% [Ci1G2657:5:120:400:80:1440:0:5000:3Ci7f3x410y120]+5000 W[x410y195]0 W [Ci7f1x70y103] Wechselrichter 1-3 - 24 Stunden
+=>%DT% [Ci1G2658:5:120:400:80:1440:0:5000:16][Ci1G2659:5:120:400:80:1440:0:5000:5]
+=>%DT% [f1s1b0:260:260:100:50:2:11:4:2:Rel 1:b1:370:260:100:50:2:11:4:2:Dsp off:]
+=>mp3volume 100
+=>mp3track 4
+
+**>T**
+; get some tele vars
+temp=BMP280#Temperature
+press=BMP280#Pressure
+tunit=TempUnit
+punit=PressureUnit
+dist=VL53L0X#Distance
+
+; check proximity sensor to switch display on/off
+; to prevent burn in
+if dist>300
+then
+if pwr[2]>0
+then
+=>power2 0
+endif
+else
+if pwr[2]==0
+then
+=>power2 1
+endif
+endif
+
+
+**>S**
+; update graph every teleperiod
+if upsecs%tper==0
+then
+dprec2
+=>%DT% [f1Ci3x40y260w30Ci1]
+=>%DT% [Ci7x120y220t]
+=>%DT% [Ci7x180y220T]
+=>%DT% [Ci7p8x120y240]%temp% %tunit%
+=>%DT% [Ci7x120y260]%press% %punit%
+=>%DT% [Ci7x120y280]%dist% mm
+dprec0
+=>%DT% [g0:%zwz%g1:%wr1%g2:%-wr2%g3:%-wr3%]
+if zwz>0
+then
+=>%DT% [p-8x410y55Ci2Bi0]%zwz% W
+else
+=>%DT% [p-8x410y55Ci3Bi0]%zwz% W
+endif
+=>%DT% [p-8x410y140Ci3Bi0]%wr1% W
+=>%DT% [p-8x410y155Ci16Bi0]%-wr2% W
+=>%DT% [p-8x410y170Ci5Bi0]%-wr3% W
+endif
+
+; chime every full hour
+hour=int(time/60)
+if chg[hour]>0
+then =>mp3track 4
+endif
+
+**>E**
+
+**>R**
diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h
index a1913c612..3209cc101 100644
--- a/sonoff/language/bg-BG.h
+++ b/sonoff/language/bg-BG.h
@@ -513,6 +513,7 @@
#define D_SENSOR_BUTTON "Бутон" // Suffix "1"
#define D_SENSOR_RELAY "Реле" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Брояч" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -578,6 +579,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h
index fcf9b7ec5..8a919715a 100644
--- a/sonoff/language/cs-CZ.h
+++ b/sonoff/language/cs-CZ.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Tlačítko" // Suffix "1"
#define D_SENSOR_RELAY "Relé" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1",
#define D_SENSOR_COUNTER "Počítadlo" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h
index 171256aa8..efc1b737a 100644
--- a/sonoff/language/de-DE.h
+++ b/sonoff/language/de-DE.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h
index 238ad7050..155bf2f4f 100644
--- a/sonoff/language/el-GR.h
+++ b/sonoff/language/el-GR.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h
index d8a78cec0..8d300a711 100644
--- a/sonoff/language/en-GB.h
+++ b/sonoff/language/en-GB.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/es-ES.h b/sonoff/language/es-ES.h
index caddf2f06..24200b752 100644
--- a/sonoff/language/es-ES.h
+++ b/sonoff/language/es-ES.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Botón" // Suffix "1"
#define D_SENSOR_RELAY "Relé" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Contador" // Suffix "1"
#define D_SENSOR_IRRECV "IR Rx"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h
index ac60a779e..493e8b395 100644
--- a/sonoff/language/fr-FR.h
+++ b/sonoff/language/fr-FR.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Bouton" // Suffix "1"
#define D_SENSOR_RELAY "Relais" // Suffix "1i"
#define D_SENSOR_LED "LED" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Compteur" // Suffix "1"
#define D_SENSOR_IRRECV "RécptIR"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h
index 86811cae7..c36f050a7 100644
--- a/sonoff/language/he-HE.h
+++ b/sonoff/language/he-HE.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "לחצן" // Suffix "1"
#define D_SENSOR_RELAY "ממסר" // Suffix "1i"
#define D_SENSOR_LED "לד" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "מונה" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h
index 50e266aa5..3b3ef7821 100644
--- a/sonoff/language/hu-HU.h
+++ b/sonoff/language/hu-HU.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Gomb" // Suffix "1"
#define D_SENSOR_RELAY "Relé" // Suffix "1i"
#define D_SENSOR_LED "LED" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Számláló" // Suffix "1"
#define D_SENSOR_IRRECV "IR vevő"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h
index 082526cb8..9078bc81e 100644
--- a/sonoff/language/it-IT.h
+++ b/sonoff/language/it-IT.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h
index be7c36b64..37c289963 100644
--- a/sonoff/language/ko-KO.h
+++ b/sonoff/language/ko-KO.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h
index 565ff2164..4525ce5cb 100644
--- a/sonoff/language/nl-NL.h
+++ b/sonoff/language/nl-NL.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relais" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Teller" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h
index 6605e6867..a2ed0721c 100644
--- a/sonoff/language/pl-PL.h
+++ b/sonoff/language/pl-PL.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Przyci" // Suffix "1"
#define D_SENSOR_RELAY "Przek" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Liczni" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h
index 953fbe979..c66867178 100644
--- a/sonoff/language/pt-BR.h
+++ b/sonoff/language/pt-BR.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Botão" // Suffix "1"
#define D_SENSOR_RELAY "Relé" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Contador" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h
index 880471f69..334844a72 100644
--- a/sonoff/language/pt-PT.h
+++ b/sonoff/language/pt-PT.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Botão" // Suffix "1"
#define D_SENSOR_RELAY "Relé" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Contador" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h
index 2f56eb44f..071be1394 100644
--- a/sonoff/language/ru-RU.h
+++ b/sonoff/language/ru-RU.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Кнопка" // Suffix "1"
#define D_SENSOR_RELAY "Реле" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Счетчик" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h
index 92b551488..7cdea4177 100644
--- a/sonoff/language/sk-SK.h
+++ b/sonoff/language/sk-SK.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Tlačidlo" // Suffix "1"
#define D_SENSOR_RELAY "Relé" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1",
#define D_SENSOR_COUNTER "Počítadlo" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h
index e34847480..f6b0638bd 100644
--- a/sonoff/language/sv-SE.h
+++ b/sonoff/language/sv-SE.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Knapp" // Suffix "1"
#define D_SENSOR_RELAY "Relä" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Räknare" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h
index f6a7ba233..dc764d006 100755
--- a/sonoff/language/tr-TR.h
+++ b/sonoff/language/tr-TR.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h
index 54b2bfa39..6f4d6208c 100644
--- a/sonoff/language/uk-UK.h
+++ b/sonoff/language/uk-UK.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Кнопка" // Suffix "1"
#define D_SENSOR_RELAY "Реле" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Лічильник" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h
index be8e1c91c..a1bca5a88 100644
--- a/sonoff/language/zh-CN.h
+++ b/sonoff/language/zh-CN.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h
index 07d63e30a..9b87d03f3 100644
--- a/sonoff/language/zh-TW.h
+++ b/sonoff/language/zh-TW.h
@@ -512,6 +512,7 @@
#define D_SENSOR_BUTTON "Button" // Suffix "1"
#define D_SENSOR_RELAY "Relay" // Suffix "1i"
#define D_SENSOR_LED "Led" // Suffix "1i"
+#define D_SENSOR_LED_LINK "LedLink" // Suffix "i"
#define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Counter" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
@@ -577,6 +578,7 @@
#define D_SENSOR_MY92X1_DI "MY92x1 DI"
#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI"
#define D_SENSOR_ARIRFRCV "ALux IrRcv"
+#define D_SENSOR_ARIRFSEL "ALux IrSel"
#define D_SENSOR_TXD "Serial Tx"
#define D_SENSOR_RXD "Serial Rx"
#define D_SENSOR_ROTARY "Rotary" // Suffix "1A"
diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h
index 16be53cac..b18949005 100644
--- a/sonoff/my_user_config.h
+++ b/sonoff/my_user_config.h
@@ -275,7 +275,8 @@
#define USE_WEBSERVER // Enable web server and Wifi Manager (+66k code, +8k mem)
#define WEB_PORT 80 // Web server Port for User and Admin mode
#define WEB_USERNAME "admin" // Web server Admin mode user name
- #define USE_EMULATION // Enable Belkin WeMo and Hue Bridge emulation for Alexa (+22k code, +2k mem)
+ #define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common)
+ #define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common)
// -- mDNS ----------------------------------------
#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem)
@@ -288,8 +289,11 @@
#define USE_SUNRISE // Add support for Sunrise and sunset tools (+16k)
#define SUNRISE_DAWN_ANGLE DAWN_NORMAL // Select desired Dawn Angle from (DAWN_NORMAL, DAWN_CIVIL, DAWN_NAUTIC, DAWN_ASTRONOMIC)
-// -- Rules ---------------------------------------
-#define USE_RULES // Add support for rules (+4k4 code)
+// -- Rules or Script ----------------------------
+// Select none or only one of the below defines
+#define USE_RULES // Add support for rules (+8k code)
+//#define USE_SCRIPT // Add support for script (+15k code)
+
// #define USE_EXPRESSION // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem)
// #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code)
@@ -342,6 +346,7 @@
// #define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
// #define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code)
// #define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code)
+ #define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code)
#define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
diff --git a/sonoff/settings.h b/sonoff/settings.h
index 185880a00..f28e95489 100644
--- a/sonoff/settings.h
+++ b/sonoff/settings.h
@@ -211,8 +211,9 @@ struct SYSCFG {
uint8_t weblog_level; // 1AC
uint8_t mqtt_fingerprint[2][20]; // 1AD
- uint8_t free_1D5[20]; // 1D5 Free since 5.12.0e
+ uint8_t free_1D5[19]; // 1D5 Free since 5.12.0e
+ uint8_t sps30_inuse_hours; // 1E8
char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6
uint16_t mqtt_port; // 20A - Keep together
char mqtt_client[33]; // 20C - Keep together
@@ -345,7 +346,7 @@ struct SYSCFG {
uint16_t weight_max; // 7BE Total max weight in kilogram
unsigned long weight_reference; // 7C0 Reference weight in gram
unsigned long weight_calibration; // 7C4
- unsigned long energy_frequency_calibration; // 7C8
+ unsigned long energy_frequency_calibration; // 7C8 also used by HX711 to save last weight
uint16_t web_refresh; // 7CC
char mems[MAX_RULE_MEMS][10]; // 7CE
char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b
diff --git a/sonoff/settings.ino b/sonoff/settings.ino
index 4fee5e8cc..a8445b232 100644
--- a/sonoff/settings.ino
+++ b/sonoff/settings.ino
@@ -444,6 +444,7 @@ void SettingsSaveAll(void)
Settings.power = 0;
}
XsnsCall(FUNC_SAVE_BEFORE_RESTART);
+ XdrvCall(FUNC_SAVE_BEFORE_RESTART);
#ifdef USE_EEPROM
EepromCommit();
#endif
diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino
index d90c8ef68..c935a0325 100755
--- a/sonoff/sonoff.ino
+++ b/sonoff/sonoff.ino
@@ -143,7 +143,10 @@ uint8_t backlog_pointer = 0; // Command backlog pointer
uint8_t sleep; // Current copy of Settings.sleep
uint8_t blinkspeed = 1; // LED blink rate
uint8_t pin[GPIO_MAX]; // Possible pin configurations
+uint8_t leds_present = 0; // Max number of LED supported
uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off))
+uint8_t led_power = 0; // LED power state
+uint8_t ledlnk_inverted = 0; // Link LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted)
uint8_t counter_no_pullup = 0; // Counter input pullup flag (1 = No pullup)
uint8_t energy_flg = 0; // Energy monitor configured
@@ -370,19 +373,56 @@ void SetDevicePower(power_t rpower, int source)
}
}
+void SetLedPowerIdx(uint8_t led, uint8_t state)
+{
+ if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present
+ if (pin[GPIO_LED2] < 99) { led = 1; }
+ }
+ if (pin[GPIO_LED1 + led] < 99) {
+ uint8_t mask = 1 << led;
+ if (state) {
+ state = 1;
+ led_power |= mask;
+ } else {
+ led_power &= (0xFF ^ mask);
+ }
+ digitalWrite(pin[GPIO_LED1 + led], bitRead(led_inverted, led) ? !state : state);
+ }
+}
+
void SetLedPower(uint8_t state)
{
- if (state) { state = 1; }
+ if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2
+ SetLedPowerIdx(0, state);
+ } else {
+ power_t mask = 1;
+ for (uint8_t i = 0; i < leds_present; i++) { // Map leds to power
+ bool tstate = (power & mask);
+ SetLedPowerIdx(i, tstate);
+ mask <<= 1;
+ }
+ }
+}
- uint8_t led_pin = 0;
- if (pin[GPIO_LED2] < 99) { led_pin = 1; }
- digitalWrite(pin[GPIO_LED1 + led_pin], (bitRead(led_inverted, led_pin)) ? !state : state);
+void SetLedPowerAll(uint8_t state)
+{
+ for (uint8_t i = 0; i < leds_present; i++) {
+ SetLedPowerIdx(i, state);
+ }
}
void SetLedLink(uint8_t state)
{
- if (state) { state = 1; }
- digitalWrite(pin[GPIO_LED1], (bitRead(led_inverted, 0)) ? !state : state);
+ uint8_t led_pin = pin[GPIO_LEDLNK];
+ uint8_t led_inv = ledlnk_inverted;
+ if (99 == led_pin) { // Legacy - LED1 is status
+ led_pin = pin[GPIO_LED1];
+ led_inv = bitRead(led_inverted, 0);
+ }
+ if (led_pin < 99) {
+ if (state) { state = 1; }
+ digitalWrite(led_pin, (led_inv) ? !state : state);
+ }
}
uint8_t GetFanspeed(void)
@@ -1422,7 +1462,8 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
}
Response_P(S_JSON_COMMAND_NVALUE, command, Settings.altitude);
}
- else if (CMND_LEDPOWER == command_code) {
+ else if ((CMND_LEDPOWER == command_code) && (index > 0) && (index <= MAX_LEDS)) {
+/*
if ((payload >= 0) && (payload <= 2)) {
Settings.ledstate &= 8;
switch (payload) {
@@ -1435,15 +1476,83 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
break;
}
blinks = 0;
- SetLedPower(Settings.ledstate &8);
+ SetLedPowerIdx(index -1, Settings.ledstate &8);
}
- Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(bitRead(Settings.ledstate, 3)));
+ Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(bitRead(Settings.ledstate, 3)));
+*/
+/*
+ if (99 == pin[GPIO_LEDLNK]) {
+ if ((payload >= 0) && (payload <= 2)) {
+ Settings.ledstate &= 8;
+ switch (payload) {
+ case 0: // Off
+ case 1: // On
+ Settings.ledstate = payload << 3;
+ break;
+ case 2: // Toggle
+ Settings.ledstate ^= 8;
+ break;
+ }
+ blinks = 0;
+ SetLedPower(Settings.ledstate &8);
+ }
+ Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(bitRead(Settings.ledstate, 3)));
+ } else {
+ if ((payload >= 0) && (payload <= 2)) {
+ Settings.ledstate &= 8; // Disable power control
+ uint8_t mask = 1 << (index -1); // Led to control
+ switch (payload) {
+ case 0: // Off
+ led_power &= (0xFF ^ mask);
+ case 1: // On
+ led_power |= mask;
+ break;
+ case 2: // Toggle
+ led_power ^= mask;
+ break;
+ }
+ blinks = 0;
+ SetLedPowerIdx(index -1, (led_power & mask));
+ }
+ Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(bitRead(led_power, index -1)));
+ }
+*/
+ if (99 == pin[GPIO_LEDLNK]) { index = 1; }
+ if ((payload >= 0) && (payload <= 2)) {
+ Settings.ledstate &= 8; // Disable power control
+ uint8_t mask = 1 << (index -1); // Led to control
+ switch (payload) {
+ case 0: // Off
+ led_power &= (0xFF ^ mask);
+ Settings.ledstate = 0;
+ break;
+ case 1: // On
+ led_power |= mask;
+ Settings.ledstate = 8;
+ break;
+ case 2: // Toggle
+ led_power ^= mask;
+ Settings.ledstate ^= 8;
+ break;
+ }
+ blinks = 0;
+ if (99 == pin[GPIO_LEDLNK]) {
+ SetLedPower(Settings.ledstate &8);
+ } else {
+ SetLedPowerIdx(index -1, (led_power & mask));
+ }
+ }
+ uint8_t state = bitRead(led_power, index -1);
+ if (99 == pin[GPIO_LEDLNK]) {
+ state = bitRead(Settings.ledstate, 3);
+ }
+ Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(state));
}
else if (CMND_LEDSTATE == command_code) {
if ((payload >= 0) && (payload < MAX_LED_OPTION)) {
Settings.ledstate = payload;
if (!Settings.ledstate) {
- SetLedPower(0);
+ SetLedPowerAll(0);
SetLedLink(0);
}
}
@@ -1469,7 +1578,12 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}"));
type = (char*)topicBuf;
}
- if (mqtt_data[0] != '\0') { MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); }
+ if (mqtt_data[0] != '\0') {
+ MqttPublishPrefixTopic_P(RESULT_OR_STAT, type);
+#ifdef USE_SCRIPT
+ XdrvRulesProcess();
+#endif
+ }
fallback_topic_flag = false;
}
@@ -1838,6 +1952,9 @@ void MqttPublishTeleState(void)
mqtt_data[0] = '\0';
MqttShowState();
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN);
+#ifdef USE_SCRIPT
+ RulesTeleperiod(); // Allow rule based HA messages
+#endif // USE_SCRIPT
}
bool MqttShowSensor(void)
@@ -1922,7 +2039,7 @@ void PerformEverySecond(void)
mqtt_data[0] = '\0';
if (MqttShowSensor()) {
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
-#ifdef USE_RULES
+#if defined(USE_RULES) || defined(USE_SCRIPT)
RulesTeleperiod(); // Allow rule based HA messages
#endif // USE_RULES
}
@@ -2025,8 +2142,6 @@ void Every250mSeconds(void)
}
}
if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) {
-// if ( (!Settings.flag.global_state && global_state.data) || ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) ) {
-// SetLedPower(blinkstate); // Set led on or off
SetLedLink(blinkstate); // Set led on or off
}
if (!blinkstate) {
@@ -2176,7 +2291,9 @@ void Every250mSeconds(void)
SettingsDefault();
restart_flag = 2;
}
- SettingsSaveAll();
+ if (2 == restart_flag) {
+ SettingsSaveAll();
+ }
restart_flag--;
if (restart_flag <= 0) {
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
@@ -2449,6 +2566,10 @@ void GpioInit(void)
bitSet(led_inverted, mpin - GPIO_LED1_INV);
mpin -= (GPIO_LED1_INV - GPIO_LED1);
}
+ else if (mpin == GPIO_LEDLNK_INV) {
+ ledlnk_inverted = 1;
+ mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK);
+ }
else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) {
bitSet(pwm_inverted, mpin - GPIO_PWM1_INV);
mpin -= (GPIO_PWM1_INV - GPIO_PWM1);
@@ -2560,10 +2681,24 @@ void GpioInit(void)
for (uint8_t i = 0; i < MAX_LEDS; i++) {
if (pin[GPIO_LED1 +i] < 99) {
- pinMode(pin[GPIO_LED1 +i], OUTPUT);
- digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i));
+#ifdef USE_ARILUX_RF
+ if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) {
+ pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; // Legacy support where LED4 was Arilux RF enable
+ pin[GPIO_LED4] = 99;
+ } else {
+#endif
+ pinMode(pin[GPIO_LED1 +i], OUTPUT);
+ leds_present++;
+ digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i));
+#ifdef USE_ARILUX_RF
+ }
+#endif
}
}
+ if (pin[GPIO_LEDLNK] < 99) {
+ pinMode(pin[GPIO_LEDLNK], OUTPUT);
+ digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted);
+ }
ButtonInit();
SwitchInit();
@@ -2645,6 +2780,13 @@ void setup(void)
sleep = Settings.sleep;
#ifndef USE_EMULATION
Settings.flag2.emulation = 0;
+#else
+#ifndef USE_EMULATION_WEMO
+ if (EMUL_WEMO == Settings.flag2.emulation) { Settings.flag2.emulation = 0; }
+#endif
+#ifndef USE_EMULATION_HUE
+ if (EMUL_HUE == Settings.flag2.emulation) { Settings.flag2.emulation = 0; }
+#endif
#endif // USE_EMULATION
if (Settings.param[P_BOOT_LOOP_OFFSET]) {
diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h
index 10a466981..6089adfa5 100644
--- a/sonoff/sonoff_post.h
+++ b/sonoff/sonoff_post.h
@@ -46,6 +46,13 @@ void KNX_CB_Action(message_t const &msg, void *arg);
* Default global defines
\*********************************************************************************************/
+#ifdef USE_EMULATION_HUE
+#define USE_EMULATION
+#endif
+#ifdef USE_EMULATION_WEMO
+#define USE_EMULATION
+#endif
+
#ifndef MODULE
#define MODULE SONOFF_BASIC // [Module] Select default model
#endif
diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h
index 071c1f420..2cf4404f6 100644
--- a/sonoff/sonoff_template.h
+++ b/sonoff/sonoff_template.h
@@ -171,7 +171,7 @@ enum UserSelectablePins {
GPIO_DCKI, // my92x1 CLK input
GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2)
GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2)
- GPIO_ARIRFRCV, // AliLux RF Receive input
+ GPIO_ARIRFRCV, // AriLux RF Receive input
GPIO_TXD, // Serial interface
GPIO_RXD, // Serial interface
GPIO_ROT1A, // Rotary switch1 A Pin
@@ -181,6 +181,9 @@ enum UserSelectablePins {
GPIO_HRE_CLOCK, // Clock/Power line for HR-E Water Meter
GPIO_HRE_DATA, // Data line for HR-E Water Meter
GPIO_ADE7953_IRQ, // ADE7953 IRQ
+ GPIO_LEDLNK, // Link led
+ GPIO_LEDLNK_INV, // Inverted link led
+ GPIO_ARIRFSEL, // Arilux RF Receive input selected
GPIO_SENSOR_END };
// Programmer selectable GPIO functionality
@@ -246,6 +249,8 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|"
D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|"
D_SENSOR_ADE7953_IRQ "|"
+ D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|"
+ D_SENSOR_ARIRFSEL "|"
;
// User selectable ADC0 functionality
@@ -448,6 +453,8 @@ const uint8_t kGpioNiceList[] PROGMEM = {
GPIO_LED3_INV,
GPIO_LED4,
GPIO_LED4_INV,
+ GPIO_LEDLNK, // Link led
+ GPIO_LEDLNK_INV, // Inverted link led
GPIO_PWM1, // RGB Red or C Cold White
GPIO_PWM1_INV,
GPIO_PWM2, // RGB Green or CW Warm White
@@ -616,7 +623,8 @@ const uint8_t kGpioNiceList[] PROGMEM = {
GPIO_ROT2B, // Rotary switch2 B Pin
#endif
#ifdef USE_ARILUX_RF
- GPIO_ARIRFRCV, // AliLux RF Receive input
+ GPIO_ARIRFRCV, // AriLux RF Receive input
+ GPIO_ARIRFSEL, // Arilux RF Receive input selected
#endif
#ifdef USE_HRE
GPIO_HRE_CLOCK,
@@ -1109,9 +1117,9 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, 0
},
{ "Huafan SS", // Hua Fan Smart Socket (ESP8266) - like Sonoff Pow
- GPIO_LED1_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status
+ GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status
0, 0,
- GPIO_LED2_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status
+ GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status
GPIO_KEY1, // GPIO04 Button
GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off)
// GPIO06 (SD_CLK Flash)
@@ -1304,7 +1312,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_PWM3, // GPIO12 RGB LED Blue
GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10)
GPIO_PWM1, // GPIO14 RGB LED Red
- GPIO_LED4_INV, // GPIO15 RF receiver control (Arilux LC10)
+ GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10)
0, 0
},
{ "Luani HVIO", // ESP8266_HVIO
@@ -1350,7 +1358,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
// (PwmFrequency 1111Hz)
GPIO_KEY1, // GPIO00 Optional Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
- GPIO_LED4_INV, // GPIO02 RF receiver control
+ GPIO_ARIRFSEL, // GPIO02 RF receiver control
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional)
GPIO_PWM1, // GPIO05 RGB LED Red
@@ -1370,7 +1378,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
// (PwmFrequency 540Hz)
GPIO_KEY1, // GPIO00 Optional Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
- GPIO_LED4_INV, // GPIO02 RF receiver control
+ GPIO_ARIRFSEL, // GPIO02 RF receiver control
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_PWM2, // GPIO04 RGB LED Green
GPIO_PWM1, // GPIO05 RGB LED Red
@@ -1498,9 +1506,9 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
// https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1
// https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1
// https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html
- GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status
+ GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
- GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
+ GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
0,
GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power
@@ -1624,7 +1632,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0,
GPIO_KEY1, // GPIO01 Serial TXD and Button
0,
- GPIO_LED2_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status
+ GPIO_LED1_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status
GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power
GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage
// GPIO06 (SD_CLK Flash)
@@ -1634,7 +1642,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
// GPIO11 (SD_CMD Flash)
GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
- GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status
+ GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status
GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On)
0, 0, 0
},
@@ -1678,7 +1686,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
},
{ "Gosund SP1 v23", // https://www.amazon.de/gp/product/B0777BWS1P
0,
- GPIO_LED1_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status
+ GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status
0,
GPIO_KEY1, // GPIO03 Serial TXD and Button
GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power
@@ -1690,7 +1698,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
// GPIO11 (SD_CMD Flash)
GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
- GPIO_LED2_INV, // GPIO13 LED2 (red) inv - Power status
+ GPIO_LED1_INV, // GPIO13 LED2 (red) inv - Power status
GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On)
0, 0, 0
},
@@ -1728,8 +1736,8 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
// GPIO11 (SD_CMD Flash)
GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage)
- GPIO_LED2_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status
- GPIO_LED1_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status
+ GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status
+ GPIO_LEDLNK_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status
GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On)
0, 0
},
@@ -1757,9 +1765,9 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
{ "Teckin US", // Teckin SP20 US with Energy Monitoring
// https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B
// https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN
- GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status
+ GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status
0,
- GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
+ GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
0,
GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On)
GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power
@@ -1809,8 +1817,8 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
// GPIO11 (SD_CMD Flash)
- GPIO_LED1_INV, // GPIO12 Green LED - Link status
- GPIO_LED2, // GPIO13 Red LED - Power status
+ GPIO_LEDLNK_INV, // GPIO12 Green LED - Link status
+ GPIO_LED1, // GPIO13 Red LED - Power status
0, 0, 0, 0
},
{ "YTF IR Bridge", // https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html
@@ -1854,7 +1862,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
},
{ "KA10", // SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y
0, // GPIO00
- GPIO_LED1_INV, // GPIO01 Blue LED - Link status
+ GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status
0, // GPIO02
GPIO_KEY1, // GPIO03 Button
GPIO_HJL_CF, // GPIO04 BL0937 CF power
@@ -1866,7 +1874,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
// GPIO11 (SD_CMD Flash)
GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage)
- GPIO_LED2, // GPIO13 Red LED - Power status
+ GPIO_LED1, // GPIO13 Red LED - Power status
GPIO_REL1, // GPIO14 Relay 1
0, 0, 0
},
@@ -1923,7 +1931,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
},
{ "WAGA CHCZ02MB", // WAGA life CHCZ02MB (HJL-01 Energy Monitoring)
// https://www.ebay.com/itm/332595697006
- GPIO_LED2_INV, // GPIO00 Red LED
+ GPIO_LED1_INV, // GPIO00 Red LED
0, // GPIO01 Serial RXD
0, // GPIO02
GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage)
@@ -1938,7 +1946,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_REL1, // GPIO12 Relay
GPIO_KEY1, // GPIO13 Button
GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current
- GPIO_LED1_INV, // GPIO15 Blue LED - Link status
+ GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status
0, 0
},
{ "SYF05", // Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726
@@ -2070,7 +2078,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_PWM3, // GPIO12 RGB LED Blue
GPIO_PWM4, // GPIO13 RGBW LED White
GPIO_PWM1, // GPIO14 RGB LED Red
- GPIO_LED4_INV, // GPIO15 RF receiver control
+ GPIO_ARIRFSEL, // GPIO15 RF receiver control
0, 0
}
diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h
index ce4413124..e06fab8d0 100644
--- a/sonoff/sonoff_version.h
+++ b/sonoff/sonoff_version.h
@@ -20,6 +20,6 @@
#ifndef _SONOFF_VERSION_H_
#define _SONOFF_VERSION_H_
-const uint32_t VERSION = 0x0605000B;
+const uint32_t VERSION = 0x0605000C;
#endif // _SONOFF_VERSION_H_
diff --git a/sonoff/support.ino b/sonoff/support.ino
index 317d25065..da79be0e0 100644
--- a/sonoff/support.ino
+++ b/sonoff/support.ino
@@ -674,6 +674,42 @@ double FastPrecisePow(double a, double b)
return r * u.d;
}
+float FastPrecisePowf(const float x, const float y)
+{
+// return (float)(pow((double)x, (double)y));
+ return (float)FastPrecisePow(x, y);
+}
+
+double TaylorLog(double x)
+{
+ // https://stackoverflow.com/questions/46879166/finding-the-natural-logarithm-of-a-number-using-taylor-series-in-c
+
+ if (x <= 0.0) { return NAN; }
+ double z = (x + 1) / (x - 1); // We start from power -1, to make sure we get the right power in each iteration;
+ double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); // Store step to not have to calculate it each time
+ double totalValue = 0;
+ double powe = 1;
+ double y;
+ for (int count = 0; count < 10; count++) { // Experimental number of 10 iterations
+ z *= step;
+ y = (1 / powe) * z;
+ totalValue = totalValue + y;
+ powe = powe + 2;
+ }
+ totalValue *= 2;
+/*
+ char logxs[33];
+ dtostrfd(x, 8, logxs);
+ double log1 = log(x);
+ char log1s[33];
+ dtostrfd(log1, 8, log1s);
+ char log2s[33];
+ dtostrfd(totalValue, 8, log2s);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("input %s, log %s, taylor %s"), logxs, log1s, log2s);
+*/
+ return totalValue;
+}
+
uint32_t SqrtInt(uint32_t num)
{
if (num <= 1) {
diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino
index c97f77c9d..9ae3df3b2 100644
--- a/sonoff/support_features.ino
+++ b/sonoff/support_features.ino
@@ -49,8 +49,8 @@ void GetFeatures(void)
#ifdef WEBSERVER_ADVERTISE
feature_drv1 |= 0x00000100; // xdrv_02_webserver.ino
#endif
-#ifdef USE_EMULATION
- feature_drv1 |= 0x00000200; // xplg_wemohue.ino
+#ifdef USE_EMULATION_HUE
+ feature_drv1 |= 0x00000200; // xdrv_20_hue.ino
#endif
#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT)
feature_drv1 |= 0x00000400; // xdrv_01_mqtt.ino
@@ -180,9 +180,13 @@ void GetFeatures(void)
#ifdef USE_SM16716
feature_drv2 |= 0x00040000; // xdrv_04_light.ino
#endif
+#ifdef USE_SCRIPT
+ feature_drv2 |= 0x00080000; // xdrv_10_scripter.ino
+#endif
+#ifdef USE_EMULATION_WEMO
+ feature_drv2 |= 0x00100000; // xdrv_21_wemo.ino
+#endif
-// feature_drv2 |= 0x00080000;
-// feature_drv2 |= 0x00100000;
// feature_drv2 |= 0x00200000;
// feature_drv2 |= 0x00400000;
diff --git a/sonoff/support_udp.ino b/sonoff/support_udp.ino
new file mode 100644
index 000000000..eccc4681d
--- /dev/null
+++ b/sonoff/support_udp.ino
@@ -0,0 +1,136 @@
+/*
+ support_udp.ino - Udp support for Sonoff-Tasmota
+
+ Copyright (C) 2019 Heiko Krupp and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_EMULATION
+
+#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message
+#define UDP_MSEARCH_SEND_DELAY 1500 // Delay in ms before M-Search response is send
+
+#include
+Ticker TickerMSearch;
+
+IPAddress udp_remote_ip; // M-Search remote IP address
+uint16_t udp_remote_port; // M-Search remote port
+
+bool udp_connected = false;
+bool udp_response_mutex = false; // M-Search response mutex to control re-entry
+
+/*********************************************************************************************\
+ * UPNP/SSDP search targets
+\*********************************************************************************************/
+
+const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**";
+const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice";
+const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all";
+const char SSDP_ALL[] PROGMEM = "ssdp:all";
+
+/*********************************************************************************************\
+ * UDP support routines
+\*********************************************************************************************/
+
+bool UdpDisconnect(void)
+{
+ if (udp_connected) {
+ PortUdp.flush();
+ WiFiUDP::stopAll();
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED));
+ udp_connected = false;
+ }
+ return udp_connected;
+}
+
+bool UdpConnect(void)
+{
+ if (!udp_connected) {
+ // Simple Service Discovery Protocol (SSDP)
+ if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) {
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
+ udp_response_mutex = false;
+ udp_connected = true;
+ } else {
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED));
+ udp_connected = false;
+ }
+ }
+ return udp_connected;
+}
+
+void PollUdp(void)
+{
+ if (udp_connected) {
+ if (PortUdp.parsePacket()) {
+ char packet_buffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP/SSDP packet
+
+ int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1);
+ packet_buffer[len] = 0;
+
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len);
+// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer);
+
+ // Simple Service Discovery Protocol (SSDP)
+ if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) {
+ udp_response_mutex = true;
+
+ udp_remote_ip = PortUdp.remoteIP();
+ udp_remote_port = PortUdp.remotePort();
+
+// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %s:%d\n%s"),
+// udp_remote_ip.toString().c_str(), udp_remote_port, packet_buffer);
+
+ uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); // 1500 - 2200 msec
+
+ LowerCase(packet_buffer, packet_buffer);
+ RemoveSpace(packet_buffer);
+
+#ifdef USE_EMULATION_WEMO
+ if (EMUL_WEMO == Settings.flag2.emulation) {
+ if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { // type1 echo dot 2g, echo 1g's
+ TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1);
+ return;
+ }
+ else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || // type2 Echo 2g (echo & echo plus)
+ (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) ||
+ (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) {
+ TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2);
+ return;
+ }
+ }
+#endif // USE_EMULATION_WEMO
+
+#ifdef USE_EMULATION_HUE
+ if (EMUL_HUE == Settings.flag2.emulation) {
+ if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) ||
+ (strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) ||
+ (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) ||
+ (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) {
+ TickerMSearch.attach_ms(response_delay, HueRespondToMSearch);
+ return;
+ }
+ }
+#endif // USE_EMULATION_HUE
+
+ udp_response_mutex = false;
+ }
+
+ }
+ delay(1);
+ }
+}
+
+#endif // USE_EMULATION
diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino
index 44efdac0f..8c406fad5 100644
--- a/sonoff/support_wifi.ino
+++ b/sonoff/support_wifi.ino
@@ -131,7 +131,7 @@ void WifiConfig(uint8_t type)
{
if (!wifi_config_type) {
if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; }
-#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
+#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
WiFi.disconnect(); // Solve possible Wifi hangs
@@ -159,7 +159,7 @@ void WifiConfig(uint8_t type)
#ifdef USE_SMARTCONFIG
else if (WIFI_SMARTCONFIG == wifi_config_type) {
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_1_SMARTCONFIG " " D_ACTIVE_FOR_3_MINUTES));
- WiFi.mode(WIFI_STA); // Disable AP mode
+ WiFi.mode(WIFI_STA); // Disable AP mode
WiFi.beginSmartConfig();
}
#endif // USE_SMARTCONFIG
@@ -211,7 +211,7 @@ void WifiBegin(uint8_t flag, uint8_t channel)
{
const char kWifiPhyMode[] = " BGN";
-#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
+#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
@@ -565,7 +565,7 @@ void WifiCheck(uint8_t param)
} else {
WifiSetState(0);
-#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
+#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
mdns_begun = 0;
diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino
index f714169ab..dcca253fe 100644
--- a/sonoff/xdrv_01_webserver.ino
+++ b/sonoff/xdrv_01_webserver.ino
@@ -493,9 +493,6 @@ void StartWebserver(int type, IPAddress ipweb)
WebServer->on("/rs", HandleRestoreConfiguration);
WebServer->on("/rt", HandleResetConfiguration);
WebServer->on("/in", HandleInformation);
-#ifdef USE_EMULATION
- HueWemoAddHandlers();
-#endif // USE_EMULATION
XdrvCall(FUNC_WEB_ADD_HANDLER);
XsnsCall(FUNC_WEB_ADD_HANDLER);
#endif // Not FIRMWARE_MINIMAL
@@ -1532,11 +1529,19 @@ void HandleOtherConfiguration(void)
#ifdef USE_EMULATION
WSContentSend_P(PSTR(""));
#endif // USE_EMULATION
@@ -2189,11 +2194,13 @@ void HandleNotFound(void)
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page.
#ifdef USE_EMULATION
+#ifdef USE_EMULATION_HUE
String path = WebServer->uri();
if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) {
HandleHueApi(&path);
} else
-#endif // USE_EMULATION
+#endif // USE_EMULATION_HUE
+#endif // USE_EMULATION
{
WSContentBegin(404, CT_PLAIN);
WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), WebServer->uri().c_str(), (WebServer->method() == HTTP_GET) ? "GET" : "POST", WebServer->args());
@@ -2430,7 +2437,16 @@ bool WebCommand(void)
}
#ifdef USE_EMULATION
else if (CMND_EMULATION == command_code) {
+#if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE)
if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) {
+#else
+#ifndef USE_EMULATION_WEMO
+ if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) {
+#endif
+#ifndef USE_EMULATION_HUE
+ if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) {
+#endif
+#endif
Settings.flag2.emulation = XdrvMailbox.payload;
restart_flag = 2;
}
diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino
index cc510b20c..4d60e157b 100644
--- a/sonoff/xdrv_02_mqtt.ino
+++ b/sonoff/xdrv_02_mqtt.ino
@@ -423,7 +423,7 @@ void MqttReconnect(void)
return;
}
-#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
+#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino
index c9bce585e..90b4f872e 100644
--- a/sonoff/xdrv_04_light.ino
+++ b/sonoff/xdrv_04_light.ino
@@ -716,6 +716,8 @@ void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *
if (r_b) *r_b = b;
}
+#define POW FastPrecisePowf
+
void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) {
float x = 0.31271f; // default medium white
float y = 0.32902f;
@@ -726,9 +728,9 @@ void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x,
float b = (float)i_b / 255.0f;
// https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d
// Gamma correction
- r = (r > 0.04045f) ? powf((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f);
- g = (g > 0.04045f) ? powf((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f);
- b = (b > 0.04045f) ? powf((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f);
+ r = (r > 0.04045f) ? POW((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f);
+ g = (g > 0.04045f) ? POW((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f);
+ b = (b > 0.04045f) ? POW((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f);
// conversion to X, Y, Z
// Y is also the Luminance
@@ -762,9 +764,9 @@ void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_
r = r / max; // normalize to max == 1.0
g = g / max;
b = b / max;
- r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * powf(r, (1.0f / 2.4f)) - 0.055f;
- g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * powf(g, (1.0f / 2.4f)) - 0.055f;
- b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * powf(b, (1.0f / 2.4f)) - 0.055f;
+ r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * POW(r, (1.0f / 2.4f)) - 0.055f;
+ g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * POW(g, (1.0f / 2.4f)) - 0.055f;
+ b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * POW(b, (1.0f / 2.4f)) - 0.055f;
//
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, "XyToRgb XZ (%s %s) rgb (%s %s %s)",
// String(X,5).c_str(), String(Z,5).c_str(),
@@ -1105,23 +1107,24 @@ void AriluxRfHandler(void)
void AriluxRfInit(void)
{
- if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_LED4] < 99)) {
+ if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) {
if (Settings.last_module != Settings.module) {
Settings.rf_code[1][6] = 0;
Settings.rf_code[1][7] = 0;
Settings.last_module = Settings.module;
}
arilux_rf_received_value = 0;
- digitalWrite(pin[GPIO_LED4], !bitRead(led_inverted, 3)); // Turn on RF
+
+ digitalWrite(pin[GPIO_ARIRFSEL], 0); // Turn on RF
attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE);
}
}
void AriluxRfDisable(void)
{
- if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_LED4] < 99)) {
+ if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) {
detachInterrupt(pin[GPIO_ARIRFRCV]);
- digitalWrite(pin[GPIO_LED4], bitRead(led_inverted, 3)); // Turn off RF
+ digitalWrite(pin[GPIO_ARIRFSEL], 1); // Turn off RF
}
}
#endif // USE_ARILUX_RF
@@ -1351,8 +1354,9 @@ void LightInit(void)
}
}
if (pin[GPIO_ARIRFRCV] < 99) {
- if (pin[GPIO_LED4] < 99) {
- digitalWrite(pin[GPIO_LED4], bitRead(led_inverted, 3)); // Turn off RF
+ if (pin[GPIO_ARIRFSEL] < 99) {
+ pinMode(pin[GPIO_ARIRFSEL], OUTPUT);
+ digitalWrite(pin[GPIO_ARIRFSEL], 1); // Turn off RF
}
}
}
diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino
index dcd18c6f1..3f838ed7a 100644
--- a/sonoff/xdrv_09_timers.ino
+++ b/sonoff/xdrv_09_timers.ino
@@ -285,7 +285,7 @@ void TimerEverySecond(void)
if (time == set_time) {
if (xtimer.days & days) {
Settings.timer[i].arm = xtimer.repeat;
-#ifdef USE_RULES
+#if defined(USE_RULES) || defined(USE_SCRIPT)
if (3 == xtimer.power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands
Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1);
XdrvRulesProcess();
@@ -359,7 +359,8 @@ bool TimerCommand(void)
Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; // Copy timer
}
} else {
-#ifndef USE_RULES
+//#ifndef USE_RULES
+#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0
if (devices_present) {
#endif
StaticJsonBuffer<256> jsonBuffer;
@@ -437,7 +438,8 @@ bool TimerCommand(void)
index++;
}
-#ifndef USE_RULES
+//#ifndef USE_RULES
+#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0
} else {
Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_TIMER_NO_DEVICE "\"}"), index); // No outputs defined so nothing to control
error = 1;
@@ -630,7 +632,7 @@ const char HTTP_TIMER_SCRIPT5[] PROGMEM =
"if(%d>0){" // Create Output and Action drop down boxes
"eb('oa').innerHTML=\"" D_TIMER_OUTPUT " " D_TIMER_ACTION " \";"
"o=qs('#p1');ce('" D_OFF "',o);ce('" D_ON "',o);ce('" D_TOGGLE "',o);" // Create offset direction select options
-#ifdef USE_RULES
+#if defined(USE_RULES) || defined(USE_SCRIPT)
"ce('" D_RULE "',o);"
#else
"ce('" D_BLINK "',o);"
@@ -768,7 +770,7 @@ bool Xdrv09(uint8_t function)
#ifdef USE_WEBSERVER
#ifdef USE_TIMERS_WEB
case FUNC_WEB_ADD_BUTTON:
-#ifdef USE_RULES
+#if defined(USE_RULES) || defined(USE_SCRIPT)
WSContentSend_P(HTTP_BTN_MENU_TIMER);
#else
if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); }
diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino
index 3c0d05d4a..7b3e4d316 100644
--- a/sonoff/xdrv_10_rules.ino
+++ b/sonoff/xdrv_10_rules.ino
@@ -18,6 +18,7 @@
*/
#ifdef USE_RULES
+#ifndef USE_SCRIPT
/*********************************************************************************************\
* Rules based heavily on ESP Easy implementation
*
@@ -586,6 +587,16 @@ void RulesEverySecond(void)
}
}
+void RulesSaveBeforeRestart(void)
+{
+ if (Settings.rule_enabled) { // Any rule enabled
+ char json_event[32];
+
+ strncpy_P(json_event, PSTR("{\"System\":{\"Save\":1}}"), sizeof(json_event));
+ RulesProcessEvent(json_event);
+ }
+}
+
void RulesSetPower(void)
{
rules_new_power = XdrvMailbox.index;
@@ -1278,6 +1289,9 @@ bool Xdrv10(uint8_t function)
case FUNC_RULES_PROCESS:
result = RulesProcess();
break;
+ case FUNC_SAVE_BEFORE_RESTART:
+ RulesSaveBeforeRestart();
+ break;
#ifdef SUPPORT_MQTT_EVENT
case FUNC_MQTT_DATA:
result = RulesMqttData();
@@ -1287,4 +1301,5 @@ bool Xdrv10(uint8_t function)
return result;
}
+#endif // Do not USE_SCRIPT
#endif // USE_RULES
\ No newline at end of file
diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino
new file mode 100644
index 000000000..9edd27a9f
--- /dev/null
+++ b/sonoff/xdrv_10_scripter.ino
@@ -0,0 +1,2238 @@
+/*
+ xdrv_10_scripter.ino - script support for Sonoff-Tasmota
+
+ Copyright (C) 2019 Gerhard Mutz and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_SCRIPT
+#ifndef USE_RULES
+/*********************************************************************************************\
+
+for documentation see up to date docs in file SCRIPTER.md
+
+uses about 14,2 k of flash
+more stack could be needed for sendmail => -D CONT_STACKSIZE=4800 = +0.8k stack -0.8k heap
+
+
+to do
+optimize code for space
+
+remarks
+goal is fast execution time, minimal use of ram and intuitive syntax
+therefore =>
+case sensitive cmds and vars (lowercase uses time and code)
+no math hierarchy (costs ram and execution time, better group with brackets, anyhow better readable for beginners)
+(will probably make math hierarchy an ifdefed option)
+keywords if then else endif, or, and are better readable for beginners (others may use {})
+
+\*********************************************************************************************/
+
+#define XDRV_10 10
+
+#define SCRIPT_DEBUG 0
+
+#define MAXVARS 50
+#define MAXNVARS 45
+#define MAXSVARS 5
+#define MAXFILT 5
+#define SCRIPT_SVARSIZE 20
+#define SCRIPT_MAXSSIZE 48
+#define SCRIPT_EOL '\n'
+#define SCRIPT_FLOAT_PRECISION 2
+#define SCRIPT_MAXPERM (MAX_RULE_MEMS*10)-4/sizeof(float)
+
+
+enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU};
+enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD};
+
+typedef union {
+ uint8_t data;
+ struct {
+ uint8_t is_string : 1; // string or number
+ uint8_t is_permanent : 1;
+ uint8_t is_timer : 1;
+ uint8_t is_autoinc : 1;
+ uint8_t changed : 1;
+ uint8_t settable : 1;
+ uint8_t is_filter : 1;
+ uint8_t constant : 1;
+ };
+} SCRIPT_TYPE;
+
+struct T_INDEX {
+ uint8_t index;
+ SCRIPT_TYPE bits;
+};
+
+struct M_FILT {
+ uint8_t numvals;
+ uint8_t index;
+ float maccu;
+ float rbuff[1];
+};
+
+// global memory
+struct SCRIPT_MEM {
+ float *fvars; // number var pointer
+ float *s_fvars; // shadow var pointer
+ struct T_INDEX *type; // type and index pointer
+ struct M_FILT *mfilt;
+ char *glob_vnp; // var name pointer
+ uint8_t *vnp_offset;
+ char *glob_snp; // string vars pointer
+ char *scriptptr;
+ uint8_t numvars;
+ void *script_mem;
+ uint16_t script_mem_size;
+ uint8_t script_dprec;
+ uint8_t var_not_found;
+ uint8_t glob_error;
+ uint8_t max_ssize;
+ uint8_t script_loglevel;
+} glob_script_mem;
+
+uint8_t tasm_cmd_activ=0;
+
+uint32_t script_lastmillis;
+
+char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo);
+
+void ScriptEverySecond(void) {
+
+ if (bitRead(Settings.rule_enabled, 0)) {
+ struct T_INDEX *vtp=glob_script_mem.type;
+ float delta=(millis()-script_lastmillis)/1000;
+ script_lastmillis=millis();
+ for (uint8_t count=0; count0) {
+ // decrement
+ *fp-=delta;
+ if (*fp<0) *fp=0;
+ }
+ }
+ if (vtp[count].bits.is_autoinc) {
+ // increments timers
+ float *fp=&glob_script_mem.fvars[vtp[count].index];
+ if (*fp>=0) {
+ *fp+=delta;
+ }
+ }
+ }
+ Run_Scripter(">S",2,0);
+ }
+}
+
+void RulesTeleperiod(void) {
+ if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">T",2, mqtt_data);
+}
+
+#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++;
+#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++;
+
+// allocates all variable and presets them
+int16_t Init_Scripter(char *script) {
+ // scan lines for >DEF
+ uint16_t lines=0,nvars=0,svars=0,vars=0;
+ char *lp=script;
+ char vnames[MAXVARS*10];
+ char *vnames_p=vnames;
+ char *vnp[MAXVARS];
+ char **vnp_p=vnp;
+ char strings[MAXSVARS*SCRIPT_MAXSSIZE];
+ struct M_FILT mfilt[MAXFILT];
+
+ char *strings_p=strings;
+ char *snp[MAXSVARS];
+ char **snp_p=snp;
+ uint8_t numperm=0,numflt=0,count;
+
+ glob_script_mem.max_ssize=SCRIPT_SVARSIZE;
+ glob_script_mem.scriptptr=0;
+ if (!*script) return -999;
+
+ float fvalues[MAXVARS];
+ struct T_INDEX vtypes[MAXVARS];
+ char init=0;
+ while (1) {
+ // check line
+ // skip leading spaces
+ SCRIPT_SKIP_SPACES
+ // skip empty line
+ if (*lp=='\n' || *lp=='\r') goto next_line;
+ // skip comment
+ if (*lp==';') goto next_line;
+ if (init) {
+ // init section
+ if (*lp=='>') {
+ init=0;
+ break;
+ }
+ char *op=strchr(lp,'=');
+ if (op) {
+ vtypes[vars].bits.data=0;
+ // found variable definition
+ if (*lp=='p' && *(lp+1)==':') {
+ lp+=2;
+ if (numpermMAXFILT) {
+ return -6;
+ }
+ } else {
+ vtypes[vars].bits.is_filter=0;
+ }
+ *vnp_p++=vnames_p;
+ while (lpMAXNVARS) {
+ return -1;
+ }
+ } else {
+ // string vars
+ op++;
+ *snp_p++=strings_p;
+ while (*op!='\"') {
+ if (*op==SCRIPT_EOL) break;
+ *strings_p++=*op++;
+ }
+ *strings_p++=0;
+ vtypes[vars].bits.is_string=1;
+ vtypes[vars].index=svars;
+ svars++;
+ if (svars>MAXSVARS) {
+ return -2;
+ }
+ }
+ vars++;
+ if (vars>MAXVARS) {
+ return -3;
+ }
+ }
+ } else {
+ if (!strncmp(lp,">D",2)) {
+ lp+=2;
+ SCRIPT_SKIP_SPACES
+ if (isdigit(*lp)) {
+ uint8_t ssize=atoi(lp)+1;
+ if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE;
+ glob_script_mem.max_ssize=ssize;
+ }
+ init=1;
+ }
+ }
+ // next line
+ next_line:
+ lp = strchr(lp, SCRIPT_EOL);
+ if (!lp) break;
+ lp++;
+ }
+
+ uint16_t fsize=0;
+ for (count=0; countnumvals=mfilt[count].numvals;
+ mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&0x7f)-1)*sizeof(float);
+ }
+
+ glob_script_mem.numvars=vars;
+ glob_script_mem.script_dprec=SCRIPT_FLOAT_PRECISION;
+ glob_script_mem.script_loglevel=LOG_LEVEL_INFO;
+
+
+#if SCRIPT_DEBUG>2
+ struct T_INDEX *dvtp=glob_script_mem.type;
+ for (uint8_t count=0; count0
+ ClaimSerial();
+ SetSerialBaudrate(9600);
+#endif
+
+ // store start of actual program here
+ glob_script_mem.scriptptr=lp-1;
+ return 0;
+
+}
+
+#define NUM_RES 0xfe
+#define STR_RES 0xfd
+#define VAR_NV 0xff
+
+#define NTYPE 0
+#define STYPE 0x80
+
+
+//Settings.seriallog_level
+//Settings.weblog_level
+
+float Get_MFilter(uint8_t index) {
+ uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
+ for (uint8_t count=0; countnumvals&0x80) {
+ // moving average
+ return mflp->maccu/(mflp->numvals&0x7f);
+ } else {
+ // median, sort array
+ float tbuff[mflp->numvals],tmp;
+ uint8_t flag;
+ memmove(tbuff,mflp->rbuff,sizeof(tbuff));
+ for (uint8_t ocnt=0; ocntnumvals; ocnt++) {
+ flag=0;
+ for (uint8_t count=0; countnumvals-1; count++) {
+ if (tbuff[count]>tbuff[count+1]) {
+ tmp=tbuff[count];
+ tbuff[count]=tbuff[count+1];
+ tbuff[count+1]=tmp;
+ flag=1;
+ }
+ }
+ if (!flag) break;
+ }
+ return mflp->rbuff[mflp->numvals/2];
+ }
+ }
+ mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float);
+ }
+ return 0;
+}
+
+void Set_MFilter(uint8_t index, float invar) {
+ uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
+ for (uint8_t count=0; countnumvals&0x80) {
+ // moving average
+ mflp->maccu-=mflp->rbuff[mflp->index];
+ mflp->maccu+=invar;
+ mflp->rbuff[mflp->index]=invar;
+ mflp->index++;
+ if (mflp->index>=(mflp->numvals&0x7f)) mflp->index=0;
+ } else {
+ // median
+ mflp->rbuff[mflp->index]=invar;
+ mflp->index++;
+ if (mflp->index>=mflp->numvals) mflp->index=0;
+ }
+ break;
+ }
+ mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float);
+ }
+}
+
+#define MEDIAN_SIZE 5
+#define MEDIAN_FILTER_NUM 2
+
+struct MEDIAN_FILTER {
+float buffer[MEDIAN_SIZE];
+int8_t index;
+} script_mf[MEDIAN_FILTER_NUM];
+
+float DoMedian5(uint8_t index, float in) {
+
+ if (index>=MEDIAN_FILTER_NUM) index=0;
+
+ struct MEDIAN_FILTER* mf=&script_mf[index];
+
+ float tbuff[MEDIAN_SIZE],tmp;
+ uint8_t flag;
+ mf->buffer[mf->index]=in;
+ mf->index++;
+ if (mf->index>=MEDIAN_SIZE) mf->index=0;
+ // sort list and take median
+ memmove(tbuff,mf->buffer,sizeof(tbuff));
+ for (uint8_t ocnt=0; ocnttbuff[count+1]) {
+ tmp=tbuff[count];
+ tbuff[count]=tbuff[count+1];
+ tbuff[count+1]=tmp;
+ flag=1;
+ }
+ }
+ if (!flag) break;
+ }
+ return tbuff[MEDIAN_SIZE/2];
+}
+
+
+// vtype => ff=nothing found, fe=constant number,fd = constant string else bit 7 => 80 = string, 0 = number
+// no flash strings here for performance reasons!!!
+char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) {
+ uint16_t count,len=0;
+ uint8_t nres=0;
+ char vname[32];
+ float fvar=0;
+ tind->index=0;
+ tind->bits.data=0;
+
+ if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') {
+ // isnumber
+ if (fp) *fp=CharToDouble(lp);
+ if (*lp=='-') lp++;
+ while (isdigit(*lp) || *lp=='.') {
+ if (*lp==0 || *lp==SCRIPT_EOL) break;
+ lp++;
+ }
+ tind->bits.constant=1;
+ tind->bits.is_string=0;
+ *vtype=NUM_RES;
+ return lp;
+ }
+ if (*lp=='"') {
+ lp++;
+ while (*lp!='"') {
+ if (*lp==0 || *lp==SCRIPT_EOL) break;
+ if (sp) *sp++=*lp;
+ lp++;
+ }
+ if (sp) *sp=0;
+ *vtype=STR_RES;
+ tind->bits.constant=1;
+ tind->bits.is_string=1;
+ return lp+1;
+ }
+
+ if (*lp=='-') {
+ // inverted var
+ nres=1;
+ lp++;
+ }
+
+ const char *term="\n\r ])=+-/*%>index=VAR_NV;
+ glob_script_mem.var_not_found=1;
+ return lp;
+ }
+
+ struct T_INDEX *vtp=glob_script_mem.type;
+ for (count=0; countindex=count; // overwrite with global var index
+ if (vtp[count].bits.is_string==0) {
+ *vtype=NTYPE|index;
+ if (vtp[count].bits.is_filter) {
+ fvar=Get_MFilter(index);
+ } else {
+ fvar=glob_script_mem.fvars[index];
+ }
+ if (nres) fvar=-fvar;
+ if (fp) *fp=fvar;
+ } else {
+ *vtype=STYPE|index;
+ if (sp) strlcpy(sp,glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize),SCRIPT_MAXSSIZE);
+ }
+ return lp+len;
+ }
+ }
+ }
+
+ if (jo) {
+ // look for json input
+ const char* str_value;
+ uint8_t aindex;
+ String vn;
+ char *ja=strchr(vname,'[');
+ if (ja) {
+ // json array
+ *ja=0;
+ ja++;
+ // fetch array index
+ float fvar;
+ GetNumericResult(ja,OPER_EQU,&fvar,0);
+ aindex=fvar;
+ if (aindex<1 || aindex>6) aindex=1;
+ aindex--;
+ }
+ if (jo->success()) {
+ char *subtype=strchr(vname,'#');
+ if (subtype) {
+ *subtype=0;
+ subtype++;
+ }
+ vn=vname;
+ str_value = (*jo)[vn];
+ if ((*jo)[vn].success()) {
+ if (subtype) {
+ JsonObject &jobj1=(*jo)[vn];
+ if (jobj1.success()) {
+ vn=subtype;
+ jo=&jobj1;
+ str_value = (*jo)[vn];
+ if ((*jo)[vn].success()) {
+ goto skip;
+ }
+ } else {
+ goto chknext;
+ }
+ }
+ skip:
+ if (ja) {
+ // json array
+ str_value = (*jo)[vn][aindex];
+ }
+ if (str_value && *str_value) {
+ if ((*jo).is(vn)) {
+ if (!strncmp(str_value,"ON",2)) {
+ if (fp) *fp=1;
+ } else if (!strncmp(str_value,"OFF",3)) {
+ if (fp) *fp=0;
+ } else {
+ *vtype=STR_RES;
+ tind->bits.constant=1;
+ tind->bits.is_string=1;
+ if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE);
+ return lp+len;
+ }
+ } else {
+ if (fp) *fp=CharToDouble((char*)str_value);
+ *vtype=NUM_RES;
+ tind->bits.constant=1;
+ tind->bits.is_string=0;
+ return lp+len;
+ }
+ }
+ }
+ }
+ }
+
+chknext:
+ switch (vname[0]) {
+ case 'b':
+ if (!strncmp(vname,"boot",4)) {
+ if (rules_flag.system_boot) {
+ rules_flag.system_boot=0;
+ fvar=1;
+ }
+ goto exit;
+ }
+ break;
+ case 'c':
+ if (!strncmp(vname,"chg[",4)) {
+ // var changed
+ struct T_INDEX ind;
+ uint8_t vtype;
+ isvar(vname+4,&vtype,&ind,0,0,0);
+ if (!ind.bits.constant) {
+ uint8_t index=glob_script_mem.type[ind.index].index;
+ if (glob_script_mem.fvars[index]!=glob_script_mem.s_fvars[index]) {
+ // var has changed
+ glob_script_mem.s_fvars[index]=glob_script_mem.fvars[index];
+ fvar=1;
+ len++;
+ goto exit;
+ } else {
+ fvar=0;
+ len++;
+ goto exit;
+ }
+ }
+ }
+ break;
+ case 'd':
+ if (!strncmp(vname,"day",3)) {
+ fvar=RtcTime.day_of_month;
+ goto exit;
+ }
+ break;
+ case 'g':
+ if (!strncmp(vname,"gtmp",4)) {
+ fvar=global_temperature;
+ goto exit;
+ }
+ if (!strncmp(vname,"ghum",4)) {
+ fvar=global_humidity;
+ goto exit;
+ }
+ if (!strncmp(vname,"gprs",4)) {
+ fvar=global_pressure;
+ goto exit;
+ }
+ if (!strncmp(vname,"gtopic",6)) {
+ if (sp) strlcpy(sp,Settings.mqtt_grptopic,glob_script_mem.max_ssize);
+ goto strexit;
+ }
+ break;
+ case 'h':
+ if (!strncmp(vname,"hours",5)) {
+ fvar=RtcTime.hour;
+ goto exit;
+ }
+ if (!strncmp(vname,"heap",4)) {
+ fvar=ESP.getFreeHeap();
+ goto exit;
+ }
+ if (!strncmp(vname,"hn(",3)) {
+ lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0);
+ if (fvar<0 || fvar>255) fvar=0;
+ lp++;
+ len=0;
+ if (sp) {
+ sprintf(sp,"%02x",(uint8_t)fvar);
+ }
+ goto strexit;
+ }
+ break;
+ case 'i':
+ if (!strncmp(vname,"int(",4)) {
+ lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
+ fvar=floor(fvar);
+ lp++;
+ len=0;
+ goto exit;
+ }
+ break;
+ case 'l':
+ if (!strncmp(vname,"loglvl",6)) {
+ fvar=glob_script_mem.script_loglevel;
+ tind->index=SCRIPT_LOGLEVEL;
+ exit_settable:
+ if (fp) *fp=fvar;
+ *vtype=NTYPE;
+ tind->bits.settable=1;
+ tind->bits.is_string=0;
+ return lp+len;
+ }
+ break;
+ case 'm':
+ if (!strncmp(vname,"med(",4)) {
+ float fvar1;
+ lp=GetNumericResult(lp+4,OPER_EQU,&fvar1,0);
+ SCRIPT_SKIP_SPACES
+ // arg2
+ float fvar2;
+ lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
+ fvar=DoMedian5(fvar1,fvar2);
+ lp++;
+ len=0;
+ goto exit;
+ }
+ if (!strncmp(vname,"micros",6)) {
+ fvar=micros();
+ goto exit;
+ }
+ if (!strncmp(vname,"millis",6)) {
+ fvar=millis();
+ goto exit;
+ }
+ if (!strncmp(vname,"mins",4)) {
+ fvar=RtcTime.minute;
+ goto exit;
+ }
+ if (!strncmp(vname,"month",5)) {
+ fvar=RtcTime.month;
+ goto exit;
+ }
+ if (!strncmp(vname,"mqttc",5)) {
+ if (rules_flag.mqtt_connected) {
+ rules_flag.mqtt_connected=0;
+ fvar=1;
+ }
+ goto exit;
+ }
+ if (!strncmp(vname,"mqttd",5)) {
+ if (rules_flag.mqtt_disconnected) {
+ rules_flag.mqtt_disconnected=0;
+ fvar=1;
+ }
+ goto exit;
+ }
+ if (!strncmp(vname,"mqtts",5)) {
+ fvar=!global_state.mqtt_down;
+ goto exit;
+ }
+ break;
+ case 'p':
+ if (!strncmp(vname,"pin[",4)) {
+ // raw pin level
+ GetNumericResult(vname+4,OPER_EQU,&fvar,0);
+ fvar=digitalRead((uint8_t)fvar);
+ // skip ] bracket
+ len++;
+ goto exit;
+ }
+ if (!strncmp(vname,"pn[",3)) {
+ GetNumericResult(vname+3,OPER_EQU,&fvar,0);
+ fvar=pin[(uint8_t)fvar];
+ // skip ] bracket
+ len++;
+ goto exit;
+ }
+ if (!strncmp(vname,"pd[",3)) {
+ GetNumericResult(vname+3,OPER_EQU,&fvar,0);
+ uint8_t gpiopin=fvar;
+ for (uint8_t i=0;iindex=SCRIPT_TELEPERIOD;
+ goto exit_settable;
+ }
+ if (!strncmp(vname,"tinit",5)) {
+ if (rules_flag.time_init) {
+ rules_flag.time_init=0;
+ fvar=1;
+ }
+ goto exit;
+ }
+ if (!strncmp(vname,"tset",4)) {
+ if (rules_flag.time_set) {
+ rules_flag.time_set=0;
+ fvar=1;
+ }
+ goto exit;
+ }
+ if (!strncmp(vname,"tstamp",6)) {
+ if (sp) strlcpy(sp,GetDateAndTime(DT_LOCAL).c_str(),glob_script_mem.max_ssize);
+ goto strexit;
+ }
+ if (!strncmp(vname,"topic",5)) {
+ if (sp) strlcpy(sp,Settings.mqtt_topic,glob_script_mem.max_ssize);
+ goto strexit;
+ }
+ break;
+ case 'u':
+ if (!strncmp(vname,"uptime",6)) {
+ fvar=MinutesUptime();
+ goto exit;
+ }
+ if (!strncmp(vname,"upsecs",6)) {
+ fvar=uptime;
+ goto exit;
+ }
+ if (!strncmp(vname,"upd[",4)) {
+ // var was updated
+ struct T_INDEX ind;
+ uint8_t vtype;
+ isvar(vname+4,&vtype,&ind,0,0,0);
+ if (!ind.bits.constant) {
+ if (!ind.bits.changed) {
+ fvar=0;
+ len++;
+ goto exit;
+ } else {
+ glob_script_mem.type[ind.index].bits.changed=0;
+ fvar=1;
+ len++;
+ goto exit;
+ }
+ }
+ goto notfound;
+ }
+ break;
+ case 'w':
+ if (!strncmp(vname,"wday",4)) {
+ fvar=RtcTime.day_of_week;
+ goto exit;
+ }
+ if (!strncmp(vname,"wific",5)) {
+ if (rules_flag.wifi_connected) {
+ rules_flag.wifi_connected=0;
+ fvar=1;
+ }
+ goto exit;
+ }
+ if (!strncmp(vname,"wifid",5)) {
+ if (rules_flag.wifi_disconnected) {
+ rules_flag.wifi_disconnected=0;
+ fvar=1;
+ }
+ goto exit;
+ }
+ if (!strncmp(vname,"wifis",5)) {
+ fvar=!global_state.wifi_down;
+ goto exit;
+ }
+ break;
+ case 'y':
+ if (!strncmp(vname,"year",4)) {
+ fvar=RtcTime.year;
+ goto exit;
+ }
+ break;
+ default:
+ break;
+ }
+ // nothing valid found
+notfound:
+ if (fp) *fp=0;
+ *vtype=VAR_NV;
+ tind->index=VAR_NV;
+ glob_script_mem.var_not_found=1;
+ return lp;
+ // return constant numbers
+exit:
+ if (fp) *fp=fvar;
+ *vtype=NUM_RES;
+ tind->bits.constant=1;
+ tind->bits.is_string=0;
+ return lp+len;
+ // return constant strings
+strexit:
+ *vtype=STYPE;
+ tind->bits.constant=1;
+ tind->bits.is_string=1;
+ return lp+len;
+}
+
+
+
+char *getop(char *lp, uint8_t *operand) {
+ switch (*lp) {
+ case '=':
+ if (*(lp+1)=='=') {
+ *operand=OPER_EQUEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_EQU;
+ return lp+1;
+ }
+ break;
+ case '+':
+ if (*(lp+1)=='=') {
+ *operand=OPER_PLSEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_PLS;
+ return lp+1;
+ }
+ break;
+ case '-':
+ if (*(lp+1)=='=') {
+ *operand=OPER_MINEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_MIN;
+ return lp+1;
+ }
+ break;
+ case '*':
+ if (*(lp+1)=='=') {
+ *operand=OPER_MULEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_MUL;
+ return lp+1;
+ }
+ break;
+ case '/':
+ if (*(lp+1)=='=') {
+ *operand=OPER_DIVEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_DIV;
+ return lp+1;
+ }
+ break;
+ case '!':
+ if (*(lp+1)=='=') {
+ *operand=OPER_NOTEQU;
+ return lp+2;
+ }
+ break;
+ case '>':
+ if (*(lp+1)=='=') {
+ *operand=OPER_GRTEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_GRT;
+ return lp+1;
+
+ }
+ break;
+ case '<':
+ if (*(lp+1)=='=') {
+ *operand=OPER_LOWEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_LOW;
+ return lp+1;
+ }
+ break;
+ case '%':
+ if (*(lp+1)=='=') {
+ *operand=OPER_PERCEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_PERC;
+ return lp+1;
+ }
+ break;
+ case '^':
+ if (*(lp+1)=='=') {
+ *operand=OPER_XOREQU;
+ return lp+2;
+ } else {
+ *operand=OPER_XOR;
+ return lp+1;
+ }
+ break;
+ case '&':
+ if (*(lp+1)=='=') {
+ *operand=OPER_ANDEQU;
+ return lp+2;
+ } else {
+ *operand=OPER_AND;
+ return lp+1;
+ }
+ break;
+ case '|':
+ if (*(lp+1)=='=') {
+ *operand=OPER_OREQU;
+ return lp+2;
+ } else {
+ *operand=OPER_OR;
+ return lp+1;
+ }
+ break;
+ }
+ *operand=0;
+ return lp;
+}
+
+
+#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1)
+// All version before core 2.4.2
+// https://github.com/esp8266/Arduino/issues/2557
+extern "C" {
+#include
+ extern cont_t g_cont;
+}
+uint16_t GetStack(void) {
+ register uint32_t *sp asm("a1");
+ return (4 * (sp - g_pcont.stack));
+}
+
+#else
+extern "C" {
+#include
+ extern cont_t* g_pcont;
+}
+uint16_t GetStack(void) {
+ register uint32_t *sp asm("a1");
+ return (4 * (sp - g_pcont->stack));
+}
+#endif
+
+char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) {
+ uint8_t operand=0;
+ uint8_t vtype;
+ char *slp;
+ struct T_INDEX ind;
+ char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE];
+ while (1) {
+ lp=isvar(lp,&vtype,&ind,0,str1,jo);
+ switch (lastop) {
+ case OPER_EQU:
+ strlcpy(str,str1,sizeof(str));
+ break;
+ case OPER_PLS:
+ strlcat(str,str1,sizeof(str));
+ break;
+ }
+ slp=lp;
+ lp=getop(lp,&operand);
+ switch (operand) {
+ case OPER_EQUEQU:
+ case OPER_NOTEQU:
+ case OPER_LOW:
+ case OPER_LOWEQU:
+ case OPER_GRT:
+ case OPER_GRTEQU:
+ lp=slp;
+ strcpy(cp,str);
+ return lp;
+ break;
+ default:
+ break;
+ }
+ lastop=operand;
+ if (!operand) {
+ strcpy(cp,str);
+ return lp;
+ }
+ }
+}
+
+char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo) {
+uint8_t operand=0;
+float fvar1,fvar;
+char *slp;
+uint8_t vtype;
+struct T_INDEX ind;
+ while (1) {
+ // get 1. value
+ if (*lp=='(') {
+ lp++;
+ lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo);
+ lp++;
+ //if (*lp==')') lp++;
+ } else {
+ lp=isvar(lp,&vtype,&ind,&fvar1,0,jo);
+ if (vtype!=NUM_RES && vtype&STYPE) {
+ // string type
+ glob_script_mem.glob_error=1;
+ }
+ }
+ switch (lastop) {
+ case OPER_EQU:
+ fvar=fvar1;
+ break;
+ case OPER_PLS:
+ fvar+=fvar1;
+ break;
+ case OPER_MIN:
+ fvar-=fvar1;
+ break;
+ case OPER_MUL:
+ fvar*=fvar1;
+ break;
+ case OPER_DIV:
+ fvar/=fvar1;
+ break;
+ case OPER_PERC:
+ fvar=fmod(fvar,fvar1);
+ break;
+ case OPER_XOR:
+ fvar=(uint32_t)fvar^(uint32_t)fvar1;
+ break;
+ case OPER_AND:
+ fvar=(uint32_t)fvar&(uint32_t)fvar1;
+ break;
+ case OPER_OR:
+ fvar=(uint32_t)fvar|(uint32_t)fvar1;
+ break;
+ default:
+ break;
+ }
+ slp=lp;
+ lp=getop(lp,&operand);
+ switch (operand) {
+ case OPER_EQUEQU:
+ case OPER_NOTEQU:
+ case OPER_LOW:
+ case OPER_LOWEQU:
+ case OPER_GRT:
+ case OPER_GRTEQU:
+ lp=slp;
+ *fp=fvar;
+ return lp;
+ break;
+ default:
+ break;
+ }
+ lastop=operand;
+ if (!operand) {
+ *fp=fvar;
+ return lp;
+ }
+ }
+}
+
+
+// replace vars in cmd %var%
+void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) {
+ char *cp;
+ uint16_t count;
+ uint8_t vtype;
+ float fvar;
+ cp=srcbuf;
+ struct T_INDEX ind;
+ char string[SCRIPT_MAXSSIZE];
+ for (count=0;count=sizeof(str)) len=len>=sizeof(str);
+ strlcpy(str,cp,len);
+ toSLog(str);
+}
+
+void toLogEOL(const char *s1,const char *str) {
+ if (!str) return;
+ uint8_t index=0;
+ char *cp=log_data;
+ strcpy(cp,s1);
+ cp+=strlen(s1);
+ while (*str) {
+ if (*str==SCRIPT_EOL) break;
+ *cp++=*str++;
+ }
+ *cp=0;
+ AddLog(LOG_LEVEL_INFO);
+}
+
+
+void toSLog(const char *str) {
+ if (!str) return;
+#if SCRIPT_DEBUG>0
+ while (*str) {
+ Serial.write(*str);
+ str++;
+ }
+#endif
+}
+
+#define IF_NEST 8
+// execute section of scripter
+int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) {
+ uint8_t vtype=0,sindex,xflg,floop=0,globvindex;
+ struct T_INDEX ind;
+ uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_result[IF_NEST],and_or,ifstck=0,s_ifstck=0;
+ if_state[ifstck]=0;
+ if_result[ifstck]=0;
+ char cmpstr[SCRIPT_MAXSSIZE];
+
+ if (tasm_cmd_activ) return 0;
+
+
+ float *dfvar,*cv_count,cv_max,cv_inc;
+ char *cv_ptr;
+ float fvar=0,fvar1,sysvar,swvar;
+ uint8_t section=0,sysv_type=0,swflg=0;
+
+ if (!glob_script_mem.scriptptr) {
+ return -99;
+ }
+
+ DynamicJsonBuffer jsonBuffer; // on heap
+ JsonObject &jobj=jsonBuffer.parseObject(js);
+ JsonObject *jo;
+ if (js) jo=&jobj;
+ else jo=0;
+
+ char *lp=glob_script_mem.scriptptr;
+
+ while (1) {
+ // check line
+ // skip leading spaces
+ startline:
+ SCRIPT_SKIP_SPACES
+ // skip empty line
+ SCRIPT_SKIP_EOL
+ // skip comment
+ if (*lp==';') goto next_line;
+ if (!*lp) break;
+
+ if (section) {
+ // we are in section
+ if (*lp=='>') {
+ section=0;
+ break;
+ }
+ if (*lp=='#') {
+ section=0;
+ break;
+ }
+ glob_script_mem.var_not_found=0;
+
+#if SCRIPT_DEBUG>0
+ char tbuff[128];
+ sprintf(tbuff,"stack=%d,state=%d,cmpres=%d line: ",ifstck,if_state[ifstck],if_result[ifstck]);
+ toLogEOL(tbuff,lp);
+#endif
+
+ if (!strncmp(lp,"if",2)) {
+ lp+=2;
+ if (if_state[ifstck]>0) {
+ if (ifstck=2) {
+ lp+=5;
+ if_state[ifstck]=0;
+ if (ifstck>0) {
+ ifstck--;
+ }
+ s_ifstck=ifstck; // >>>>>
+ goto next_line;
+ } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) {
+ lp+=2;
+ and_or=1;
+ } else if (!strncmp(lp,"and",3) && if_state[ifstck]==1) {
+ lp+=3;
+ and_or=2;
+ }
+
+ if (*lp=='{' && if_state[ifstck]==1) {
+ lp+=1; // then
+ if_state[ifstck]=2;
+ } else if (*lp=='{' && if_state[ifstck]==3) {
+ lp+=1; // after else
+ //if_state[ifstck]=3;
+ } else if (*lp=='}' && if_state[ifstck]>=2) {
+ lp++; // must check for else
+ char *slp=lp;
+ uint8_t iselse=0;
+ for (uint8_t count=0; count<8;count++) {
+ if (*lp=='}') {
+ // must be endif
+ break;
+ }
+ if (!strncmp(lp,"else",4)) {
+ // is before else, no endif
+ if_state[ifstck]=3;
+ lp+=4;
+ iselse=1;
+ break;
+ }
+ lp++;
+ }
+ if (!iselse) {
+ lp=slp;
+ // endif
+ if_state[ifstck]=0;
+ if (ifstck>0) {
+ ifstck--;
+ }
+ s_ifstck=ifstck; // >>>>>
+ }
+ }
+
+ if (!strncmp(lp,"for",3)) {
+ // start for next loop, fetch 3 params
+ // simple implementation, zero loop count not supported
+ lp+=3;
+ SCRIPT_SKIP_SPACES
+ lp=isvar(lp,&vtype,&ind,0,0,0);
+ if ((vtype!=VAR_NV) && (vtype&STYPE)==0) {
+ // numeric var
+ uint8_t index=glob_script_mem.type[ind.index].index;
+ cv_count=&glob_script_mem.fvars[index];
+ SCRIPT_SKIP_SPACES
+ lp=GetNumericResult(lp,OPER_EQU,cv_count,0);
+ SCRIPT_SKIP_SPACES
+ lp=GetNumericResult(lp,OPER_EQU,&cv_max,0);
+ SCRIPT_SKIP_SPACES
+ lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0);
+ SCRIPT_SKIP_EOL
+ cv_ptr=lp;
+ floop=1;
+ } else {
+ // error
+ toLogEOL("for error",lp);
+ }
+ } else if (!strncmp(lp,"next",4) && floop>0) {
+ // for next loop
+ *cv_count+=cv_inc;
+ if (*cv_count<=cv_max) {
+ lp=cv_ptr;
+ } else {
+ lp+=4;
+ floop=0;
+ }
+ }
+
+ if (!strncmp(lp,"switch",6)) {
+ lp+=6;
+ SCRIPT_SKIP_SPACES
+ lp=GetNumericResult(lp,OPER_EQU,&swvar,0);
+ swflg=1;
+ } else if (!strncmp(lp,"case",4) && swflg>0) {
+ lp+=4;
+ SCRIPT_SKIP_SPACES
+ float cvar;
+ lp=GetNumericResult(lp,OPER_EQU,&cvar,0);
+ if (swvar!=cvar) {
+ swflg=2;
+ } else {
+ swflg=1;
+ }
+ } else if (!strncmp(lp,"ends",4) && swflg>0) {
+ lp+=4;
+ swflg=0;
+ }
+
+ if (swflg==2) goto next_line;
+
+
+ SCRIPT_SKIP_SPACES
+ //SCRIPT_SKIP_EOL
+ if (*lp==SCRIPT_EOL) {
+ goto next_line;
+ }
+ //toLogN(lp,16);
+ if (if_state[s_ifstck]==3 && if_result[s_ifstck]) goto next_line;
+ if (if_state[s_ifstck]==2 && !if_result[s_ifstck]) goto next_line;
+
+ s_ifstck=ifstck;
+
+ if (!strncmp(lp,"break",5)) {
+ if (floop) {
+ // should break loop
+ floop=0;
+ } else {
+ section=0;
+ }
+ break;
+ } else if (!strncmp(lp,"dprec",5)) {
+ lp+=5;
+ // number precision
+ glob_script_mem.script_dprec=atoi(lp);
+ goto next_line;
+ } else if (!strncmp(lp,"delay(",6)) {
+ lp+=5;
+ // delay
+ lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ delay(fvar);
+ goto next_line;
+ } else if (!strncmp(lp,"spinm(",6)) {
+ lp+=6;
+ // set pin mode
+ lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ int8_t pinnr=fvar;
+ SCRIPT_SKIP_SPACES
+ lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ int8_t mode=fvar;
+ pinMode(pinnr,mode&1);
+ goto next_line;
+ } else if (!strncmp(lp,"spin(",5)) {
+ lp+=5;
+ // set pin mode
+ lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ int8_t pinnr=fvar;
+ SCRIPT_SKIP_SPACES
+ lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ int8_t mode=fvar;
+ digitalWrite(pinnr,mode&1);
+ goto next_line;
+ } else if (!strncmp(lp,"svars(",5)) {
+ lp+=5;
+ // save vars
+ Scripter_save_pvars();
+ goto next_line;
+ }
+
+ else if (!strncmp(lp,"=>",2)) {
+ // execute cmd
+ lp+=2;
+ SCRIPT_SKIP_SPACES
+ #define SCRIPT_CMDMEM 512
+ char *cmdmem=(char*)malloc(SCRIPT_CMDMEM);
+ if (cmdmem) {
+ char *cmd=cmdmem;
+ short count;
+ for (count=0; countfvar1);
+ break;
+ case OPER_GRTEQU:
+ res=(*dfvar>=fvar1);
+ break;
+ default:
+ // error
+ break;
+ }
+
+ if (!and_or) {
+ if_result[s_ifstck]=res;
+ } else if (and_or==1) {
+ if_result[s_ifstck]|=res;
+ } else {
+ if_result[s_ifstck]&=res;
+ }
+#if SCRIPT_DEBUG>0
+ char tbuff[128];
+ sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,if_result[s_ifstck]);
+ toLogEOL(tbuff,lp);
+#endif
+
+ } else {
+ // compare string
+ char str[SCRIPT_MAXSSIZE];
+ lp=GetStringResult(lp,OPER_EQU,str,0);
+ if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) {
+ uint8_t res=0;
+ res=strcmp(cmpstr,str);
+ if (lastop==OPER_EQUEQU) res=!res;
+ if (!and_or) {
+ if_result[s_ifstck]=res;
+ } else if (and_or==1) {
+ if_result[s_ifstck]|=res;
+ } else {
+ if_result[s_ifstck]&=res;
+ }
+ }
+ }
+ SCRIPT_SKIP_SPACES
+ if (*lp=='{' && if_state[ifstck]==1) {
+ lp+=1; // then
+ if_state[ifstck]=2;
+ }
+ goto next_line;
+ } else {
+ if (numeric) {
+ char *slp=lp;
+ glob_script_mem.glob_error=0;
+ lp=GetNumericResult(lp,OPER_EQU,&fvar,jo);
+ if (glob_script_mem.glob_error==1) {
+ // mismatch was string, not number
+ // get the string and convert to number
+ lp=isvar(slp,&vtype,&ind,0,cmpstr,jo);
+ fvar=CharToDouble(cmpstr);
+ }
+ switch (lastop) {
+ case OPER_EQU:
+ if (glob_script_mem.var_not_found) {
+ if (!js) toLog("var not found\n");
+ goto next_line;
+ }
+ *dfvar=fvar;
+ break;
+ case OPER_PLSEQU:
+ *dfvar+=fvar;
+ break;
+ case OPER_MINEQU:
+ *dfvar-=fvar;
+ break;
+ case OPER_MULEQU:
+ *dfvar*=fvar;
+ break;
+ case OPER_DIVEQU:
+ *dfvar/=fvar;
+ break;
+ case OPER_PERCEQU:
+ *dfvar=fmod(*dfvar,fvar);
+ break;
+ case OPER_ANDEQU:
+ *dfvar=(uint32_t)*dfvar&(uint32_t)fvar;
+ break;
+ case OPER_OREQU:
+ *dfvar=(uint32_t)*dfvar|(uint32_t)fvar;
+ break;
+ case OPER_XOREQU:
+ *dfvar=(uint32_t)*dfvar^(uint32_t)fvar;
+ break;
+ default:
+ // error
+ break;
+ }
+ // var was changed
+ glob_script_mem.type[globvindex].bits.changed=1;
+ if (glob_script_mem.type[globvindex].bits.is_filter) {
+ Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar);
+ }
+
+ if (sysv_type) {
+ switch (sysv_type) {
+ case SCRIPT_LOGLEVEL:
+ glob_script_mem.script_loglevel=*dfvar;
+ break;
+ case SCRIPT_TELEPERIOD:
+ if (*dfvar<10) *dfvar=10;
+ if (*dfvar>300) *dfvar=300;
+ Settings.tele_period=*dfvar;
+ break;
+ }
+ sysv_type=0;
+ }
+
+ } else {
+ // string result
+ char str[SCRIPT_MAXSSIZE];
+ char *slp=lp;
+ lp=GetStringResult(lp,OPER_EQU,str,jo);
+ if (!js && glob_script_mem.var_not_found) {
+ // mismatch
+ lp=GetNumericResult(slp,OPER_EQU,&fvar,0);
+ dtostrfd(fvar,6,str);
+ glob_script_mem.var_not_found=0;
+ }
+
+ if (!glob_script_mem.var_not_found) {
+ // var was changed
+ glob_script_mem.type[globvindex].bits.changed=1;
+ if (lastop==OPER_EQU) {
+ strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
+ } else if (lastop==OPER_PLSEQU) {
+ strlcat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
+ }
+ }
+ }
+ SCRIPT_SKIP_SPACES
+ if (*lp=='{' && if_state[ifstck]==3) {
+ lp+=1; // else
+ //if_state[ifstck]=3;
+ }
+ goto next_line;
+ }
+ } else {
+ // decode line
+ if (*lp=='>' && tlen==1) {
+ // called from cmdline
+ lp++;
+ section=1;
+ goto startline;
+ }
+ if (!strncmp(lp,type,tlen)) {
+ // found section
+ section=1;
+ // check for subroutine
+ if (*type=='#') {
+ // check for parameter
+ type+=tlen;
+ if (*type=='(') {
+ float fparam;
+ numeric=1;
+ glob_script_mem.glob_error=0;
+ GetNumericResult((char*)type,OPER_EQU,&fparam,0);
+ if (glob_script_mem.glob_error==1) {
+ // was string, not number
+ numeric=0;
+ // get the string
+ GetStringResult((char*)type+1,OPER_EQU,cmpstr,0);
+ }
+ lp+=tlen;
+ if (*lp=='(') {
+ // fetch destination
+ lp++;
+ lp=isvar(lp,&vtype,&ind,0,0,0);
+ if (vtype!=VAR_NV) {
+ // found variable as result
+ uint8_t index=glob_script_mem.type[ind.index].index;
+ if ((vtype&STYPE)==0) {
+ // numeric result
+ dfvar=&glob_script_mem.fvars[index];
+ if (numeric) {
+ *dfvar=fparam;
+ } else {
+ // mismatch
+ *dfvar=CharToDouble(cmpstr);
+ }
+ } else {
+ // string result
+ sindex=index;
+ if (!numeric) {
+ strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),cmpstr,glob_script_mem.max_ssize);
+ } else {
+ // mismatch
+ dtostrfd(fparam,6,glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // next line
+ next_line:
+ if (*lp==SCRIPT_EOL) {
+ lp++;
+ } else {
+ lp = strchr(lp, SCRIPT_EOL);
+ if (!lp) break;
+ lp++;
+ }
+ }
+}
+
+uint8_t script_xsns_index = 0;
+
+
+void ScripterEvery100ms(void)
+{
+ if (Settings.rule_enabled && (uptime > 4)) {
+ mqtt_data[0] = '\0';
+ uint16_t script_tele_period_save = tele_period;
+ tele_period = 2;
+ XsnsNextCall(FUNC_JSON_APPEND, script_xsns_index);
+ tele_period = script_tele_period_save;
+ if (strlen(mqtt_data)) {
+ mqtt_data[0] = '{';
+ snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data);
+ Run_Scripter(">T",2, mqtt_data);
+ }
+ }
+}
+
+//mems[MAX_RULE_MEMS] is 50 bytes in 6.5
+// can hold 11 floats or floats + strings
+// should report overflow later
+void Scripter_save_pvars(void) {
+ uint32_t lptr=(uint32_t)Settings.mems[0];
+ int16_t mlen=0;
+ lptr&=0xfffffffc;
+ float *fp=(float*)lptr;
+ fp++;
+ mlen+=sizeof(float);
+ struct T_INDEX *vtp=glob_script_mem.type;
+ for (uint8_t count=0; countMAX_RULE_MEMS*10) {
+ vtp[count].bits.is_permanent=0;
+ return;
+ }
+ *fp++=glob_script_mem.fvars[index];
+ }
+ }
+ char *cp=(char*)fp;
+ for (uint8_t count=0; countMAX_RULE_MEMS*10) {
+ vtp[count].bits.is_permanent=0;
+ return;
+ }
+ strcpy(cp,sp);
+ cp+=slen+1;
+ }
+ }
+}
+
+// works only with webserver
+#ifdef USE_WEBSERVER
+
+#define WEB_HANDLE_SCRIPT "s10"
+#define D_CONFIGURE_SCRIPT "Edit script"
+#define D_RULEVARS "edit script"
+
+const char S_CONFIGURE_SCRIPT[] PROGMEM = D_CONFIGURE_SCRIPT;
+
+const char HTTP_BTN_MENU_RULES[] PROGMEM =
+ "
";
+
+
+const char HTTP_FORM_SCRIPT[] PROGMEM =
+ "