diff --git a/scripter.md b/scripter.md index e82a8c1bc..fe1380194 100644 --- a/scripter.md +++ b/scripter.md @@ -94,7 +94,9 @@ special variables (read only): **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) to a hex nibble string -**st(svar c n)** = stringtoken gets the n th substring of svar separated by c +**st(svar c n)** = stringtoken gets the n th substring of svar separated by c +**sl(svar)** = gets the length of a string +**sb(svar p n)** = gets a substring from svar at position p (if p<0 counts from end) and length n **s(x)** = explicit conversion from number x to string **mqtts** = state of mqtt disconnected=0, connected>0 **wifis** = state of wifi disconnected=0, connected>0 @@ -139,7 +141,7 @@ a single percent sign must be given as **%%** **special** cmds: ->**=\> print** prints to info log for debugging +>**print** or **=\>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 @@ -188,7 +190,8 @@ and on the same line conditions may be bracketed e.g. if ((a==b) and ((c==d) or >**#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 +params can be numbers or strings and on mismatch are converted +**=(svar)** executes a script in a string variable (dynamic or self modifying code) >**for var from to inc** **next** diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 38cd33568..616b21818 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -298,7 +298,7 @@ // 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 (+17k code) - #define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support + //#define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support // #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) diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 3801590b5..535c19d47 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -2399,7 +2399,7 @@ int WebSend(char *buffer) int http_code = http.GET(); // Start connection and send HTTP header if (http_code > 0) { // http_code will be negative on error if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { -/* +#ifdef USE_WEBSEND_RESPONSE // Return received data to the user - Adds 900+ bytes to the code String result = http.getString(); // File found at server - may need lot of ram or trigger out of memory! uint32_t j = 0; @@ -2412,7 +2412,13 @@ int WebSend(char *buffer) } mqtt_data[j] = '\0'; MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); -*/ +#ifdef USE_SCRIPT +extern uint8_t tasm_cmd_activ; + // recursive call must be possible in this case + tasm_cmd_activ=0; + XdrvRulesProcess(); +#endif +#endif } status = 0; // No error - Done } else { diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index 0d1ed84e6..6e7daa2ab 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -134,6 +134,7 @@ struct SCRIPT_MEM { uint8_t *vnp_offset; char *glob_snp; // string vars pointer char *scriptptr; + char *scriptptr_bu; char *script_ram; uint16_t script_size; uint8_t *script_pram; @@ -161,6 +162,7 @@ uint8_t tasm_cmd_activ=0; uint8_t fast_script=0; uint32_t script_lastmillis; + char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); char *ForceStringVar(char *lp,char *dstr); @@ -241,6 +243,7 @@ char *script; glob_script_mem.max_ssize=SCRIPT_SVARSIZE; glob_script_mem.scriptptr=0; + if (!*script) return -999; float fvalues[MAXVARS]; @@ -539,6 +542,7 @@ char *script; // store start of actual program here glob_script_mem.scriptptr=lp-1; + glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr; return 0; } @@ -943,9 +947,15 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso } if (jo->success()) { char *subtype=strchr(vname,'#'); + char *subtype2; if (subtype) { *subtype=0; subtype++; + subtype2=strchr(subtype,'#'); + if (subtype2) { + *subtype2=0; + *subtype2++; + } } vn=vname; str_value = (*jo)[vn]; @@ -957,6 +967,23 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso jo=&jobj1; str_value = (*jo)[vn]; if ((*jo)[vn].success()) { + // 2. stage + if (subtype2) { + JsonObject &jobj2=(*jo)[vn]; + if ((*jo)[vn].success()) { + vn=subtype2; + jo=&jobj2; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + goto skip; + } else { + goto chknext; + } + } else { + goto chknext; + } + } + // end goto skip; } } else { @@ -1492,6 +1519,34 @@ chknext: fvar=strlen(glob_script_mem.script_ram); goto exit; } + if (!strncmp(vname,"sl(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + lp++; + len=0; + fvar=strlen(str); + goto exit; + } + if (!strncmp(vname,"sb(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + SCRIPT_SKIP_SPACES + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp++; + len=0; + if (fvar1<0) { + fvar1=strlen(str)+fvar1; + } + memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2); + sp[(uint8_t)fvar2] = '\0'; + goto strexit; + } if (!strncmp(vname,"st(",3)) { lp+=3; char str[SCRIPT_MAXSSIZE]; @@ -2155,7 +2210,7 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { if (tasm_cmd_activ && tlen>0) return 0; - uint8_t vtype=0,sindex,xflg,floop=0,globvindex; + uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; int8_t globaindex; struct T_INDEX ind; uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0; @@ -2445,11 +2500,17 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { #endif #endif - else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2)) { + else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"print",5)) { // execute cmd - uint8_t sflag=0,svmqtt,swll; - if (*lp=='-') sflag=1; - lp+=2; + uint8_t sflag=0,pflg=0,svmqtt,swll; + if (*lp=='p') { + pflg=1; + lp+=5; + } + else { + if (*lp=='-') sflag=1; + lp+=2; + } char *slp=lp; SCRIPT_SKIP_SPACES #define SCRIPT_CMDMEM 512 @@ -2472,8 +2533,9 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { Replace_Cmd_Vars(cmd,tmp,SCRIPT_CMDMEM/2); //toSLog(tmp); - if (!strncmp(tmp,"print",5)) { - toLog(&tmp[5]); + if (!strncmp(tmp,"print",5) || pflg) { + if (pflg) toLog(tmp); + else toLog(&tmp[5]); } else { if (!sflag) { snprintf_P(log_data, sizeof(log_data), PSTR("Script: performs \"%s\""), tmp); @@ -2508,9 +2570,23 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { lp++; plen++; } - Run_Scripter(slp,plen,0); + if (fromscriptcmd) { + char *sp=glob_script_mem.scriptptr; + glob_script_mem.scriptptr=glob_script_mem.scriptptr_bu; + Run_Scripter(slp,plen,0); + glob_script_mem.scriptptr=sp; + } else { + Run_Scripter(slp,plen,0); + } lp=slp; goto next_line; + } else if (!strncmp(lp,"=(",2)) { + lp+=2; + char str[128]; + str[0]='>'; + lp=GetStringResult(lp,OPER_EQU,&str[1],0); + lp++; + execute_script(str); } // check for variable result @@ -2651,6 +2727,7 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { // called from cmdline lp++; section=1; + fromscriptcmd=1; goto startline; } if (!strncmp(lp,type,tlen)) { @@ -3200,12 +3277,11 @@ void ScriptSaveSettings(void) { #endif void execute_script(char *script) { -char *svd_sp=glob_script_mem.scriptptr; + char *svd_sp=glob_script_mem.scriptptr; strcat(script,"\n#"); glob_script_mem.scriptptr=script; Run_Scripter(">",1,0); glob_script_mem.scriptptr=svd_sp; - Scripter_save_pvars(); } #define D_CMND_SCRIPT "Script" #define D_CMND_SUBSCRIBE "Subscribe" @@ -3240,6 +3316,7 @@ bool ScriptCommand(void) { if (XdrvMailbox.data[count]==';') XdrvMailbox.data[count]='\n'; } execute_script(XdrvMailbox.data); + Scripter_save_pvars(); } } return serviced; @@ -3332,7 +3409,7 @@ bool ScriptMqttData(void) value.trim(); //Create an new event. Cannot directly call RulesProcessEvent(). - //snprintf_P(Rules.event_data, sizeof(Rules.event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); + //snprintf_P(event_data, sizeof(event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); char sbuffer[128]; snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); //toLog(sbuffer);