--[[ %% autostart %% properties %% weather Temperature Humidity Wind WeatherCondition --]] --========================== USER CONFIGURABLE PARAMETERS --==== GPS mobile to name gpsDevice={["999"]="UserName",["9999"]="skip"} --=== jSon Parameters to number jP2n={minHum=20,maxHum="?`minHum`+60?"} --=== jSon String to name jS2n={motionSensor=230, switchLight=600} --=== jSon Main actions table jM={ {0, "$RAM",{state=">85",lineState="if{$isFalse=0}",trueAct={{"RAM>85%","sendNote","Warning. \nSystem memory is $RAM% ",true},{"|$lineId|","setStateValue","80"}},falseAct={{"|$lineId|","setStateValue","85"},{"RAM<80%","sendNote","System memory is $RAM%"}}}}, {0, "$HC2onLine",{state="true",trueAct={"","tts","$dayTime, Home center is on line","{$24clock:value=?$sunriseHour+7200?~?$sunsetHour+3600?}{$24clock:value=?$sunsetHour+3600?~?$sunriseHour+7200?}"},falseAct={"","tts","$dayTime, Home center is off line"}}}, {0, "$internet",{state="online",trigAct={"","tts","$dayTime, Internet is $internet"}}}, } --=========================== GLOBAL PARAMETERS eMail = {true,"2"} -- if true, scene's alerts will emailed to user IDs list. To include more users use: eMail={true,"2,5,20"} popupNote = false -- if true, scene's alert popup notification will send to all users pushNote = {false,""} -- if true, scene's alerts interactive push notification will send sms = {false,"123456"} -- if true,scene's alerts SMS will send. Before use, update sendSms() function with your setup. deadNote = true -- if true, send auto-alert notification on dead Z-wave traffic occurrences. autoRestart = true -- in case the scene is failed, send notification and restart the scene automatically. wakeUpRate = 10 --(minutes) wakeUp dead devices time rate. 0-disable timeDrift = 0 --(seconds) system's time drift. rstDeadRpt = 24 --(hours) reset RPT and Dead data. 0-disable alertRate = 90 --(minutes) alert report repeat time rate. 0-disable tblRefresh = 300 --(seconds) table view refresh rate. 0-disable cnnLine = 25 -- number of last commands in order of execution to display at bottum of the table. (0-60) dataRecord = {values=10,commands=6} --number of last values and commands to display (1-20) global4local= {false,gVarName="",varArray={onState="init:0",myVar="isOff"}} global4rmd = {false,gVarName="jRmd"} topActive = 15 -- number (0-30) of top active slave devices to display at dropdown menu. 0-disable. Default 10 topZwave = 15 -- number (0-30) of top active master devices in table view. 0-disable. Default 10 dbgInfo = true -- display line and action states condition according to dbgTrueState/dbgFalseState dbgTrueState= true -- display line and action states when condition is true dbgFalseState=true -- display line and action states when condition is false dbgCmdFalse = true -- display command's condition when false and not executed userName = "" -- user name as defined in Access Control panel passwd = "" -- user password as defined in Access Control panel --========================== USER DEFINED FUNCTIONS =============================== ---------------------------- TTS function ------------------------------------------------------------------------------- function homeTts(text2speech,userParam) -- fibaro:startScene('ttsSceneID',{text2speech,""}) end -- function ======================================================================= ----------------------------------SMS function (User defined)------------------------------------------------------------------------------- function sendSms(phone,subject,body) --fibaro:startScene(sceneID,{phone,smsMessage}) end --================= ADVANCED USER PARAMETERS (see Advanced User’s Guide)=============================== userKeyWord={{"$dayTime"},{ function() return os.date("*t",os_time()).hour<6 and "Good night," or os.date("*t",os_time()).hour<12 and "Good morning," or os.date("*t",os_time()).hour<18 and "Good afternoon," or os.date("*t",os_time()).hour<=23 and "Good evening," end }} jCmd={["turnOff"]="value=0|+|#|call|~|",["turnOn"]="value=1|+|#|call|~|",["setValue"]="value=x|+|#|call,|~|",["setColor"]="color=x,x,x,x|+|#|call,|~|",["setArmed"]="armed=x|+|#|call,|~|",["forceArm"]="n/a|+|#|call|~|",["pressButton"]="n/a=x|+|#|call,|~|", --==================== PLEASE DO NOT CHANGE CODE BELOW =============================================== --======================Copy form here for updates============================================================================= ["poll"]="n/a|+|#|call|~|",["setSlider"]="n/a=x,x|+|#|call,|~|",["setProperty"]="n/a=x,x|+|#|call,|~|",["addRmd"]="n/a=x|-|>|rmd|$|",["logRmd"]="n/a=x|-|>|rmd|$|",["setRmd"]="n/a=x|-|>|rmd|$|",["delRmd"]="n/a|-|&|rmd|$|",["delLogRmd"]="n/a|-|&|rmd|$|",["delAllRmd"]="n/a|-|&|rmd|$|",["startScene"]="n/a=x|+|#|scene,,,|~|",["enableScene"]="n/a|+|#|scene|~|",["disableScene"]="n/a|+|#|scene|~|",["killScene"]="n/a|+|#|scene|~|", ["setGlobal"]="n/a|+|$|global|$|",["reboot"]="n/a|-|&|sys|~|",["shutdown"]="n/a|-|&|sys|~|",["setTimeSpan"]="n/a|+|<|time|$|",["setTimeSlot"]="n/a|+|<|time|$|",["setTimeDrift"]="n/a|+|!|drift|$|",["tts"]="n/a|-|&|tts|$|",["setStateValue"]="n/a|+|<|int|$|",["setStateFormula"]="n/a|+|<|int|$|",["setStateDelay"]="n/a|+|<|int|$|",["setState"]="n/a|+|<|int|$|",["powerOutage"]="n/a|+|!|int|?:true:false:|",["setIcon"]="n/a=x|+|#|misc,|~|", ["sendEmail"]="n/a=x|-|$|notify,,,|$|",["sendPush"]="n/a=x|-|$|notify,,,|$|",["sendiPush"]="n/a=x|-|$|notify,,,|$|",["sendPopup"]="n/a=x|-|$|notify,,,|$|",["sendSms"]="n/a=x|-|$|notify,,,|$|",["sendNote"]="n/a|-|$|notify|$|", ["vacOn"]="n/a|+|!|vac|~|",["vacOff"]="n/a|+|!|vac|~|",["runAction"]="n/a=x|+|<|int,,,|?:trueAct:falseAct:trigAct:timeoutAct:timeLoopAct:initAct|",["rptAction"]="n/a=x|+|<|int,,,|?:trueAct:falseAct:trigAct:timeoutAct:timeLoopAct:initAct|", ["debug"]="n/a|+|&|prt|$|",["trace"]="n/a|+|&|prt|$|",["error"]="n/a|+|&|prt|$|",["warning"]="n/a|+|&|prt|$|"} function os_time(cTime) if cTime then return cTime+tonumber(timeDrift) else return os.time()+tonumber(timeDrift) end end notProperty=" firmwareUpdate mainLoop visible wakeUpTime pollingTimeSec icon currentIcon rows pushNotificationID pushNotificationType emailNotificationID emailNotificationType smsNotificationID smsNotificationType updateVersion saveLogs deadReason deviceIcon logTemp log configured manufacturer zwaveInfo zwaveVersion parametersTemplate remoteGatewayId zwaveCompany productInfo model nodeId serialNumber endPointId deviceControlType categories batteryLowNotification " notZwave=" lastWorkingRouteResponseTimestamp useTemplate sunsetHour sunriseHour neighborListResponseTimestamp lastBreached icon currentIcon " fc="";lpF=fc.."lightpink>";lgF=fc.."#8ab92d>";dkF=fc.."darkgrey>";gF=fc.."Chartreuse>";yF=fc.."yellow>";ygF=fc.."yellowgreen>";cF=fc.."cadetblue>";mF=fc.."magenta>";dbF=fc.."darkblue>";bF=fc.."blue>";wF=fc.."wheat>";whF=fc.."white>";rF=fc.."indianred>";irF=fc.."indianred>";grF=fc.."grey>";lbF=fc.."lightblue>";lsbF=fc.."LightSkyBlue>";orF=fc.."OrangeRed>";eF="";eFb=eF.."
";spanStr="" gData={idList=bF.." jM hash table inventory
",logAct={},logActNum=10,transfer="nack",zTotal=0,prvAlerts=0,lAlert=os_time(),tKeyWord="system status",tableTime=(tblRefresh<120 and 120 or tblRefresh),tableTimeStamp=os_time(),startTime=os_time(),totalDeads=0,numDeads=0,deadLog=8,rstDeadRpt=os_time(),numAlerts=0,alertId={}} f1frmt={["#"]="\"|devID|\"
e.g. \"|823|999|\"",[">"]="\"|devID > jM_line|\"
e.g. \"|823>3|999>16|\"",["<"]="\"|devID > jM_line|\"
e.g. \"|823>3|999>16|\"",["$"]="action's valid data."};tmpStr="";tmpNum=0;internet="online";refreshTime=os.time();sunUse="";errCnt=0;gpsStatus=false;jMeSum=0 jD={}jLog={}jErr={}jF={}jT={}jI={}jSP={}jMe={}jGps={}jAct={}jTop={}jTabAct={}jEvent={}jBack={}jMem={}jVar={}jGvar={}jN2s={}jBuf={}jDid={}jM2n={}jDlist={numDev=0,phDev=0,batDev=0,vDev=0,vdId="",plugIn=0,pinId="",scenes=0};dataCol={};dColor={};cnnLine=math.min(60,cnnLine);dly="#";initMode=true;trueFalseColor={[true]=ygF,[false]=lpF,["true"]=ygF,["false"]=lpF,["fail"]=rF,["normal"]=ygF,["stop"]=lpF,["1"]=ygF,["2"]=ygF,["0"]=lpF,} powerOutage=false;atHome=true;vacMode={[false]="Vacation",[true]="Home"};b2n={[true]=1,[false]=0,[0]=false,[1]=true};noteList={sendPush={},sendEmail={}};xTime={min=100,avg=0,max=0};top={active=math.min((topActive or 15),30),zwave=math.min((topZwave or 15),30)};dispGuide=os.time();zmStr="No activity";logStatus="" runErrBuf={};log_trueFalse=false;isAct={sendPush=true,sendNote=b2n[math.max(b2n[eMail[1]],b2n[popupNote],b2n[pushNote[1]],b2n[sms[1]])]};eCmd={dev=0};eCmdType={["rmd"]="reminder",["call"]="device control",["notify"]="notification",["tts"]="text-to-speech",["drift"]="time drift",["int"]="parameters setup"} sColor={rF,ygF,lpF,yF,dkF,lbF};zw={min=-1,lNum=0,max=100,rate=200,lTime=os.time()};dataRecord.values=math.min(dataRecord.values,20);dataRecord.commands=math.min(dataRecord.commands,20);aosAct=irF.."AOS Supported Actions:
- ";sysStatus="";indent="";time2disp=300;intValue={};keyinId="" tblVal="centralSceneSupport" dbgInfo=(dbgInfo==nil and false or dbgInfo) if not dbgInfo then dbgTrueState,dbgFalseState,dbgCmdFalse=false,false,false end logLineStatus=false;logActState=false;tableview=(tableView==nil and true or tableView) prtFunc={["debug"]=ygF,["trace"]=yF,["error"]=rF,["warning"]=mF} tableOrder={{"Z.Device","Plugin","V.Device", "Variable", "Keyword","luaScene","blockScene","magicScene", "Auto-luaScene","Auto-blockScene","Auto-magicScene"}, { "wheat","#eaba61", "orange", "Chocolate", "coral", "lightblue","cadetblue", "DarkTurquoise", "DarkCyan", "CornflowerBlue", "DodgerBlue"}, {"Z.Dev", "Plugin", "V.Dev", "S.Var", "K.Word", "Lua", "Block", "Magic", "A.Lua", "A.Block", "A.Magic"},{} } task={all={"trueAct","falseAct","initAct","timeoutAct","timeLoopAct","trigAct","errAct","okAct","timeSlot","vacation","initOnStartup","property","state","onClock","timeSpanOn","lineState","trigAll","trueActState","falseActState","initActState","trigActState","timeoutActState","timeLoopActState"}, act={"falseAct","trueAct","timeoutAct","timeLoopAct","initAct","trigAct","timeEndAct","errAct","okAct",},--"lineState"}, sceneAuto={"property","state","initOnStartup","trigAct","timeLoopAct","trueAct","falseAct","errAct","okAct","lineState"},sceneAutoAct={"timeSlot","vacation","timeoutAct","initAct"}, scene={"property","state","initOnStartup","trueAct","falseAct","lineState"}, sceneAct={"timeSlot","vacation","trigAct","timeoutAct","timeLoopAct","initAct","errAct","okAct",}, prop={"state","initOnStartup","trueAct","falseAct","initAct","errAct","okAct",}, rule={"property","state","timeSlot","initOnStartup","vacation","onClock","timeSpanOn","trigAll","lineState"}, ruleType={ "string" ,"string","string" , "boolean" , "string" ,"boolean", "boolean" ,"boolean", "table"}, state={"lineState","trueActState","falseActState","initActState","trigActState","timeoutActState","timeLoopActState"}} sysKeyWord={["$HC2onLine"]=function() return intValue.hc2online end,["$blank"]="blank", ["$lineStatus"]=function() return (jD[keyinId] and jD[keyinId].status or "") end, ["$lineState"]=function() return (jD[keyinId] and jD[keyinId].state or "") end, ["$timeLoop"]="inLoop",["$internet"]=function() return intValue.internet end, ["$RAM"]=function() return intValue.ram end,["$id"]=function() return realId(keyinId) end, ["$lineId"]= function() if keyinId:sub(1,1)=="$" then return "$##"..keyinId:sub(2) end return keyinId end, ["$room"]=function() if keyinId:find("&") then return "Global Variable" end return addSign(jDid[tableId(keyinId)].room) end, ["$name"]=function() if keyinId:find("&") then return realId(keyinId) end return addSign(jDid[tableId(keyinId)].name) end, ["$isFalse"]=0,["isTrue"]=1, ["$12clock"]=function() return os.date("%I:%M %p",os_time()) end, ["$24clock"]= function() return os.date("%H:%M",os_time()) end, ["$month"]=function() return os.date("*t").month end,["$mName"]=function() return os.date("%B") end, ["$day"]=function() return os.date("*t").day end,["$wday"]=function() return os.date("*t").wday end, ["$wName"]=function() return os.date("%A") end,["$hour"]=function() return os.date("*t",os_time()).hour end, ["$min"]=function() return os.date("*t",os_time()).min end, ["$osTimeDrift"]=function() return os_time() end, ["$osDateDrift"]=function() return os.date("%c",os_time()) end, ["$osTime"]=function() return os.time() end, ["$osDate"]=function() return os.date("%c") end, ["$actDate"]=function() return os.date("%a %b %d %H:%M %Y",os_time()) end, ["$timeSpan"]=function() if jD[keyinId].tstRate then return jD[keyinId].tstRate end end, ["$value"]=function() if jD[keyinId][jD[keyinId].property].value then return jD[keyinId][jD[keyinId].property].value end sceneAbort("(70) Keyword "..wF.."$value"..eF.." not valid for "..findNameRoom(keyinId,2),{"$value"},jD[keyinId].lineNum) return "error" end, ["$param"]=function() if jD[keyinId].property then return jD[keyinId].property end sceneAbort("(69) Keyword "..wF.."$param"..eF.." not found for "..findNameRoom(keyinId,2),{"$param"},jD[keyinId].lineNum) return rF.."error"..eF end, ["$stateValue"]=function() if jD[keyinId].stateValue then return doTheMath(jD[keyinId].stateValue,keyinId) end sceneAbort("(71) Keyword "..wF.."$stateValue"..eF.." not valid for "..findNameRoom(keyinId,2),{"$stateValue"},jD[keyinId].lineNum) return "error" end, ["$timeDrift"]=function() return timeDrift end, ["$sunsetHour"]=function() return intValue.sunsetHour end, ["$sunriseHour"]=function() return intValue.sunriseHour end, ["$timezoneOffset"]=function() return intValue.timezoneOffset end, ["$homeMode"]=function() return vacMode[atHome] end, ["$powerOutage"]=function() return tostring(powerOutage) end, ["$geoAction"]=function() return jGps.data.geofenceAction end, ["$geoUser"]=function() return (gpsDevice[tostring(jGps.data.deviceId)] and gpsDevice[tostring(jGps.data.deviceId)] or jGps.user[tostring(jGps.data.userId)]) end, ["$geoUserId"]=function() return jGps.data.userId end, ["$geoLocationId"]=function() return jGps.data.locationId end, ["$geoLocation"]=function() return jGps[tostring(jGps.data.locationId)].name end, ["$geoDeviceId"]=function() return jGps.data.deviceId end, ["$geoDeviceName"]=function() return jGps.ios[tostring(jGps.data.deviceId)] end, ["$geoRadius"]=function() return jGps[tostring(jGps.data.locationId)].radius end} lastRefresh=tonumber(api.get("/refreshStates")["last"]) sceneName=api.get("/scenes/" .. __fibaroSceneId)["name"];errMsg="" htmlStr="" aosVer="8";--supVer=""..aosVer..""; function urlEncode(url,deep) if url == nil then return end for i=1,(deep or 2) do url = url:gsub("([^%w])", function(c) return string.format("%%%02X", string.byte(c)) end) end return url end--==================================== urlEncode function prtLine(msg) print(msg.."") end function getLastState(last) net.HTTPClient({timeout=600}):request("http://127.0.0.1:11111/api/refreshStates?last="..last, {success = function(response) k=json.decode(response.data);lastNext = k.last lastState(k);getLastState(lastNext) end, error = function(error) onTime();runAction(); getLastState(lastNext) end, option = {method = "GET"} }) end function table_concat(t,s,f,d) local i,j,k=0,0,"";s=(s or "");d=(d or f and ":" or "") for i,j in pairs(t) do k=k..(f and i or "")..d..j..s end return k end --==================================== getDevAct function getIntValue() intValue.sunsetHour=api.get("/settings/info")["sunsetHour"] intValue.sunriseHour=api.get("/settings/info")["sunriseHour"] intValue.ram=100-tonumber(api.get("/diagnostics")["memory"]["free"]) intValue.hc2online=tostring(api.get("/settings/info")["online"]) intValue.timezoneOffset=tonumber(api.get("/settings/location")["timezoneOffset"]) intValue.internet="online" end --================================== getIntValue function getDevAct(act1,cmd,inId,forceAdd) local cnt,fStatus,mStr=0,true,nil;tmpNum="";inGid=inId if jCmd[cmd] and not jCmd[cmd].f2:match("call") then return true end if #act1<1 then if jCmd[cmd] then sceneAbort("(68) No devices specified for '"..wF..cmd..eF.."' action.
e.g. {"..ygF.."\""..wF.."item-ID"..eF.."\",\""..cmd.."\",\"\""..eF.."}",{cmd},jD[inId].lineNum) else sceneAbort("(84) Action "..wF..cmd..eF.." not supported.
"..aosAct,{cmd},jD[inId].lineNum) end end -- for dId in act1:gmatch("%d+") do cnt=cnt+1 for dId in act1:gsub("|"," "):gmatch("%S+") do cnt=cnt+1 if dId:sub(1,1)=="&" then sceneAbort("(90) Action "..wF..cmd..eF.." not available for global "..wF..dId..eF.." Supported action:
setGlobal",{cmd,dId},jD[inId].lineNum) end if ("$lineId $id"):find(dId) then mStr=dId;dId=tableId(inId) end if dId:sub(1,1)=="$" then sceneAbort("(91) Action "..wF..cmd..eF.." not available for keyword "..wF..dId..(mStr and " in "..mStr or "")..eF.."
Supported action:
setStateValue
setState
setStateFormula
setStateDelay
setTimeSpan
setTimeSlot",{cmd,(mStr or dId)},jD[inId].lineNum) end if not jDlist[dId] then sceneAbort("(92) Item ID "..wF..dId..eF.." not found in the system.",{dId},jD[inId].lineNum) end if not jDlist[dId] and not jCmd[cmd] then sceneAbort("(85) Action "..wF..cmd..eF.." not supported.
"..aosAct,{cmd},jD[inId].lineNum) end if cmd=="poll" then if jDlist["pId"..jDlist[dId]] then jDlist["cmd"..dId][#jDlist["cmd"..dId]]="poll" else sceneAbort("(86) Master device for "..wF..findNameRoom(dId,2)..eF.." not found. "..wF.."poll"..eF.." action couldn't be executed.",{"poll"},jD[inId].lineNum) end end if json.encode(jDlist["cmd"..dId]):find("\""..cmd.."\"") or forceAdd then if not json.encode(jDlist["cmd"..dId]):find("\""..cmd.."\"") then jDlist["cmd"..dId][#jDlist["cmd"..dId]+1]=cmd end if not jCmd[cmd] then jCmd[cmd]={resp="n/a=x",init="+",f1="#",f2="call,,,",f3="~"} userAct=(userAct and userAct or irF.."User Defined Actions:
- ")..ygF.." "..cmd..eF end else fStatus=false;tmpStr="";tmpNum=(cnt==1 and dId or tmpNum.."|"..dId);tmpStr=tmpStr.."
"..wF.."`"..(jN2s[dId] or "").."` "..findNameRoom(dId,2)..eF..irF.." Device's actions are:
- "..table.concat(jDlist["cmd"..dId]," ").."
" end end if not jCmd[cmd] or not fStatus then sceneAbort("(8) Action '"..wF..cmd..eF.."' not found for ID("..wF..tmpNum..eF..") Supported actions:
"..tmpStr..(userAct and userAct.."
" or "")..aosAct,{cmd,tmpNum},jD[inId].lineNum) end end --================================== getDevAct function tableSortByKey(t,desc,member) local temp={} for key,_ in pairs(t) do table.insert(temp,key) end if desc=="primeSort" then table.sort(temp); return temp end if desc then table.sort(temp,function(a,b) return (member and t[a][member] or t[a])<(member and t[b][member] or t[b]) end) else table.sort(temp,function(a,b) return (member and t[a][member] or t[a])>(member and t[b][member] or t[b]) end) end return temp end --================= tableSortByKey function getLedStatus() sysStatus="
" if api.get("/settings/info")["updateStableAvailable"] then sysStatus=orF.."SW Update Available: "..eF..lsbF..""..api.get("/settings/info")["newestStableVersion"]..eF.."
" elseif api.get("/settings/info")["updateBetaAvailable"] then sysStatus=orF.."Beta SW Update Available: "..eF..lsbF..""..api.get("/settings/info")["newestBetaVersion"]..eF.."
" end local l,ledStr,ledTxt,onOff="","","",{["on"]="🌐 ",["off"]="O ",["blink"]="🌐 "} l=api.get("/proxy?url=http://"..urlEncode((userName or ""))..":".. urlEncode((passwd or "")).."@127.0.0.1/services/system/ledStatus.php") if l==nil then return "        " end ledStr=onOff[l.service]..onOff[l.update]..onOff[l.recovery]..onOff[l.learn]..onOff[l.zwave]..onOff[l.internet]..onOff[l.ethernet]..onOff[l.power] intValue.internet=l.internet.."line" if l.zwave~="off" then sysStatus=sysStatus..rF.."Warning! Z-Wave overload, system freeze expected.
"..eF end return "    "..ledStr.." " end --=============================== getLedStatus function logJmLine(lStr) local i for i=1,#task.all do lStr=lStr:gsub("\""..task.all[i].."\"=","
"..wF..task.all[i].." = ") end for i,_ in pairs(jCmd) do lStr=lStr:gsub(i,ygF..i..eF) end return lStr:sub(2,-2) end --================================ logJmLine function setActTop() local tab,i,rStr=tableSortByKey(jTop,false),0,{} for i=1,math.min(top.active,#tab) do rStr[#rStr+1]=string.format("%5d : %-9s",jTop[tab[i]],math.floor(jTop[tab[i]]/gData.zTotal*1000)/10 .."%")..findNameRoom(tab[i]:match("%w+"),2).."
" end return (top.active >0 and bF.." Top "..math.min(top.active,#tab).." most active slave devices:

"..eF..json.encode(rStr):gsub("[\"\\%[%],]","") or "Top active menu disabled") end --================================ setActTop function start_scene(sId,sParam) local sArg,bPath={},"%b{}" if not sParam:match("%b{}") then sParam=","..sParam:gsub(",",",,")..",";bPath="%b,," end for i in sParam:gmatch(bPath) do sArg[#sArg+1]=i:sub(2,-2) end fibaro:startScene(realId(sId),sArg) end --================================ start_scene function setIcon(inId,iconId) local pData,iType,path={},inId:sub(1,1)=="_" and "scene" or "device","" path=(iType=="scene" and "/scenes/" or "/devices/")..realId(inId) if not json.encode(jIcons):find(iconId) then iMsg="icon not found. Action aborted"..eF return false end if not tonumber(iconId) then iMsg="icon not a number. Action aborted"..eF return false end icondId=tonumber(iconId) pData=api.get(path) if iType=="scene" then api.put(path,{iconID=iconId}) else pData={properties={deviceIcon=iconId}}; api.put(path,pData) end return true end --================================ setIcon function setInitAct(orgAct,inId,actName) local act2set=json.decode(json.encode(orgAct)) if type(act2set[1])=="table" then for i=#act2set,1,-1 do if (jCmd[act2set[i][2]:match("%w+")].init=="-" and act2set[i][5]==nil) or act2set[i][5]==false then table.remove(act2set,i) end end if not act2set[1] then act2set=nil end else if (jCmd[act2set[2]:match("%w+")].init=="-" and act2set[5]==nil) or act2set[5]==false then act2set=nil end end jD[inId].initAct=act2set end --============= setInitAct function ctlChar(msg,remove) if remove then return tostring(msg):gsub("[\n\t]"," ") else return tostring(msg):gsub("[\n\t]",function(a) return (a:match("[\n]") and "\\n " or "\\t ") end) end end function parseCmd() local i,j,f,k,jRef,fields=0,0,0,0,{},{{"resp","init","f1","f2","f3"},{"[/=x]","+-","[!#><$&]","call call, scene scene,,, global drift tts notify,,, int,,, rmd, prt vac sys time int misc,","[~$&?!]"}};tmpTab={} for i,j in pairs(jCmd) do k=0;jRef[i]={};tmpTab[#tmpTab+1]=i for f in string.gmatch(j:gsub("|"," "),("%S+")) do k=k+1;jRef[i][fields[1][k]]=f if not f:find(fields[2][k]) and not fields[2][k]:find(f) then sceneAbort("(56) Syntax error in "..i.." at '"..j:gsub(f,wF..f..eF).."'.
Please refer to Advanced user Guide: Action format section",{i,f},{".jCmd hash table:",json.encode(jCmd):gsub("[\"\\]","")}) end end end jCmd=jRef;jRef={};table.sort(tmpTab);j=0 for i=1,#tmpTab do j=(json.encode(jM):find(tmpTab[i]) and j+1 or j); aosAct=aosAct..(json.encode(jM):find(tmpTab[i]) and ygF..tmpTab[i]..eF or tmpTab[i])..(i%6==0 and "
- " or " ") end aosAct=aosAct:gsub(":",": ("..math.floor(j/#tmpTab*100).."%% in use)") end --==================================== parseCmd function updateTimeDrift(drift) local i=0 gData.lAlert=gData.lAlert+drift;gData.tableTimeStamp=gData.tableTimeStamp+drift;gData.startTime=gData.startTime+drift;gData.rstDeadRpt=gData.rstDeadRpt+drift for i=1,#jMem do jMem[i].lTime=jMem[i].lTime+drift end for i,_ in pairs(jVar) do if jVar[i.."lTime"] then jVar[i.."lTime"]=jVar[i.."lTime"]+drift end end for i,_ in pairs(jD) do jD[i].lTime=jD[i].lTime+drift;jD[i].maxTimeStamp=jD[i].maxTimeStamp+drift;jD[i].lagTime=jD[i].lagTime+drift; if jD[i][jD[i].property] then jD[i][jD[i].property].lTime=jD[i][jD[i].property].lTime+drift end end end --=================================== cutStr function cutStr(str2cut,cutLen,lChar) str2cut=tostring(str2cut) return (str2cut:len()<=cutLen and str2cut or str2cut==" " and "" or str2cut:sub(1,cutLen).."..."..(lChar and str2cut:sub(-1,-1) or "") ) end function menuLen(iMsg,rule,inMsg,shadow) --======== menuLen local menuMax,brNum,mNum,mLen,mStr,shadowClr=550,0,(iMsg:find("") and 5.9 or 5.3),0,"",{"127,255,0","255,0,0","0,0,255","0,0,0","255,215,0","218,165,32","30,144,255","130,144,255","30,144,180","230,144,255","30,144,255"} if not rule then rule="right" end iMsg:gsub(".-
",function (a) mLen=math.max(mLen,a:gsub("[\t]"," "):gsub("%b<>",""):len()) end) iMsg:gsub(".+
(.+)",function (a) mLen=math.max(mLen,a:gsub("[\t]"," "):gsub("%b<>",""):len()) end) if mLen==0 then mLen=iMsg:gsub("%b<>",""):len() end if iMsg:find("fixedMenu") then brNum=tonumber(iMsg:match("fixedMenu(%d+)")) or 30; mNum=6;iMsg=iMsg:gsub(iMsg:match("fixedMenu(%d+)") and "fixedMenu"..brNum or "fixedMenu","") else _,brNum=iMsg:gsub("
"," ") end; mStr=" style='width:"..math.min(math.ceil(mLen*mNum)+mLen,(brNum>50 and brNum or brNum>18 and 730 or menuMax)).."px;"..(rule:match("%d") and rule or rule..math.min(100,math.floor(mLen*2)).."px;") if brNum>18 then mStr=mStr.."position:fixed;top:auto;left:0px;right:0px;bottom:10px;margin: 0px 0px 0px 200px;" end mStr=(shadow and mStr.."box-shadow: 5px 8px 8px 5px rgba("..shadowClr[shadow]..",0.3);'>" or mStr.."'>") if inMsg then mStr=mStr..(iMsg:sub(1,4):find("
") and iMsg:sub(5) or iMsg) end return mStr end --==================================== menuLen function getGlobalVal(cmd,inId,txt) if not cmd:match("[%$@&]") then return cmd end if cmd:find("$") then cmd=cmd:gsub("$[%w_]+", function(a) if txt then return " "..setKeyword(a,inId).." $:"..a:match("%w+").." " else return setKeyword(a,inId) end end) end if cmd:find("&") then cmd=cmd:gsub("&([%w_']+)", function(a) if txt then return " "..fibaro_getGlobalValue(a).." &:"..a.." " else return fibaro_getGlobalValue(a:match("[%w_']+")) end end) end if cmd:find("@") then cmd=cmd:gsub("[@][%w_']+[>]?[%w_]+", function(a) if txt then return " "..extractValue(a).." "..jDid[tableId(a)].name..":"..realId(a)..(a:match(">%w+") or "notFound").." " else return extractValue(a):gsub("true","1"):gsub("false","0") end end) end return cmd end --==================================== getGlobalVal function mathTipText(cmd,inId, inFunc) if not cmd:match("[%$@&]") and not cmd:match("%b??") then return cmd end if cmd:match("[%$@&]") and not cmd:match("%b??")then cmd=getGlobalVal(cmd,inId,"text") end if cmd:match("%b??") then cmd=cmd:gsub("%b??",function(a) return math2text(a:sub(2,-2),inId,"parse") end) end return cmd end --==================================== mathTipText function math2text(cmd,inId, inFunc) if not cmd:match("[%(%+%-%*/%$@&]") then return cmd end if (inFunc and not inFunc:find("varOnly")) or (not inFunc and cmd:match("[%(%+%-%*/]") and tonumber(doTheMath(cmd,inId))) then cmd=(tonumber(cmd) and cmd or " "..doTheMath("?"..cmd.."?",inId).." "..cmd.." ") end cmd=getGlobalVal(cmd,inId,"text") if inFunc then return cmd else return math2text(cmd,inId,"varOnly") end end --================================== math2text function extractValue(cmd,prop) local val2get,id,index="value",cmd:match("(%w+)[>]([%w%.]+)") id=tonumber(id) if jD[id] and jD[id].idType=="V.Device" then return jD[id].log.value end if tonumber(index) then if not prop then return jD[cmd:match("@(%d+>%d+)")][jD[cmd:match("@(%d+>%d+)")].property].value else return jD[cmd:match("@(%d+>%d+)")].property end end if tblVal:find(index) then return jDlist[index..id] end if index:find("Modified") then val2get="modified"; index=index:gsub("Modified","") end if initMode then confirmProperty(index,id) end --if not api.get("/devices/"..id.."/properties/"..index) then sceneAbort("(52) In device ID "..wF..id..eF..", property '"..index.."' not found.
To retrieve last modification time, add '"..wF.."Modified"..eF.." at end of property name (case sensitive)
Available properties:"..wF..getIdData(id,"properties")..eF,{index}) end if val2get=="modified" or index=="lastBreached" then return getModificationTime(cmd) else return jDlist[index..id] and jDlist[index..id] or tostring(api.get("/devices/"..id.."/properties/"..index)[val2get]) end end --================================ calculate function calculate(cmd,v) local v=v or 0;cmd=tostring(cmd);local count,i,l,o,r,j,s,subCmd=0,0,0,0,0,0,0,{} local function helper(o,v,r)-- a local helper function to speed things up and keep the code smaller if type(v)=="string" and v:find("%D") and to_number(math[v]) then v=to_number(math[v]) end --then if type(r)=="string" and r:find("%D") and to_number(math[r]) then r=to_number(math[r]) end -- A mirror from above but this affects the other side of the equation r=to_number(r); return o=="+" and r+v or o=="-" and r-v or o=="/" and r/v or o=="*" and r*v or o=="^" and r^v end -- local function helper(o,v,r) for i,v in pairs(math) do cmd=cmd:gsub(i.."(%b())", function(a) a=a:sub(2,-2) if a:sub(1,1)=="-" then a="0"..a end if a:match("%b()") then a=a:gsub("%w+%b()",calculate(a:match("%w+%b()"))) end _,j=a:gsub(","," ") if j==1 then l,o=a:match("(.*),(.*)") return v(calculate(l),calculate(o)) elseif j>1 then for j in string.gmatch(a:gsub(","," "),("%S+")) do s=s+1 if s==1 then subCmd[s]=j else subCmd[1]=v(calculate(subCmd[1]),calculate(j)) end end return subCmd[1] else return v(calculate(a)) end end) end cmd=cmd:gsub("%b()",function(a) return calculate(a:sub(2,-2)) end) for l,o,r in cmd:gmatch("(.*)([%+%^%-%*/-])(.*)") do count=count+1 if l:find("[%+%^%-%*/]") then if l:sub(-1,-1):match("[%+%^%-%*/]") then r=o..r;o=l:sub(-1,-1);l=l:sub(1,-2) end v=helper(o,r,calculate(l,v)) elseif count==1 then v=helper(o,r,l) end end if count==0 then return (to_number(cmd) or to_number(math[cmd])) or cmd end return v end --======================================= doTheMath function doTheMath(cmd,inId,fType) if not cmd:match("[%$@&]") and not cmd:match("%b??") then return cmd end if fType then return mathTipText(cmd,inId) end cmd=getGlobalVal(cmd,inId) cmd=cmd:gsub("%b??",function(a) orgCmd=cmd:sub(2,-2) return runFormula(a:sub(2,-2),inId) end) return cmd end --======================================= doTheMath function runFormula(cmd,inId,fType) if not cmd:match("[%(%+%-%*/]") then return cmd end if cmd:match("%d+:%d+") and cmd:match("[%(%+%-%*/]") then return os.date("!%H:%M", to_number(runFormula(cmd:gsub("(%d+):(%d+)", function (a,b) return (a*60+b)*60 end),inId))) end local st,res= pcall(calculate,cmd) if not st then if not jErr[inId] then jErr[inId]=true sceneAbort("(51) Formula syntax error in '"..wF..orgCmd..eF.."' -> '"..wF..cmd..eF.."'
"..res:gsub("lua:","lua;"),{orgCmd},jD[inId].lineNum) end res= rF.."calcERROR"..eF end return tostring(res) end --================================= runFormula function logReminder(remTrg,remAct,remStr,inId) local i,j,remIn=0,0,false if json.encode(jMem):find(remStr) then remIn=true end if remAct:find(",") then remAct,tmpNum=remAct:match("(.*),(.*)") else tmpNum=900 end if (("logRmd addRmd setRmd"):find(remAct) and remIn) or (remAct:find("del") and not remIn) then return false end if ("delRmd delLogRmd"):find(remAct) then for i=#jMem,1,-1 do if jMem[i].remMsg:find(remStr) then tmpStr=jMem[i].remMsg;table.remove(jMem,i) if #remTrg>5 then homeTts(doTheMath(remTrg,inId),"");lastAct(trueFalseColor[true]..""..inId.."ttsRmd{'"..cutStr(doTheMath(remTrg,inId),25).."'}
",inId) --reson to delete message if remAct=="delLogRmd" then sendNote(sceneName.." (Delete Reminder)",doTheMath(remTrg,inId).."\nDeleted reminder:\n"..tmpStr) end;remTrg="" end end end elseif remAct=="delAllRmd" then jMem={} if #remTrg>5 then homeTts(doTheMath(remTrg,inId),"");lastAct(trueFalseColor[true]..""..inId.."ttsRmd{'"..cutStr(doTheMath(remTrg,inId),25).."'}
",inId) end else if not tmpNum then tmpNum=900 else if to_number(tmpNum)<60 then tmpNum=60 end end --1 min. default jMem[#jMem+1]={note=false,devId=inId,remMsg=remStr,srcTrg=remTrg,remRate=to_number(tmpNum),lTime=os_time()} if remAct:find("log") then jMem[#jMem].note=true end if ("logRmd addRmd"):find(remAct) then execReminder(#jMem) end end if global4rmd[1] then fibaro:setGlobal(global4rmd.gVarName,json.encode(jMem)) end return true end --======================== checkReminder function checkReminder() for m=1,#jMem do if os_time()-jMem[m].lTime > jMem[m].remRate then if logTrueFalse(jMem[m].srcTrg,jMem[m].devId,"status") then execReminder(m) return end end end end -- function =================== execReminder function execReminder(remNum) jMem[remNum].lTime=os_time(); homeTts(doTheMath(jMem[remNum].remMsg,jMem[remNum].devId),20) if jMem[remNum].note then sendNote(sceneName.." (Reminder)",doTheMath(jMem[remNum].remMsg,jMem[remNum].devId)) end end --=================================== wdtReset function wdtReset() if rstDeadRpt==0 then return end if os_time()-gData.rstDeadRpt>=rstDeadRpt*3600 then gData.numDeads=0;jF={};gData.rstDeadRpt=os_time() end end --=================================== confirmProperty function confirmProperty(pName,inId) if fibaro:get(tonumber(inId),pName) then return true else sceneAbort("(52) In device ID "..wF..inId..eF..", property '"..pName.."' not found.
To retrieve last modification time, add '"..wF.."Modified"..eF.." at end of property name (case sensitive)
Available properties:"..wF..getIdData(inId,"properties")..eF,{pName},jD[inGid].lineNum) return false end end --=================================== getModificationTime function getModificationTime(inId) if inId:find("[$]") or inId:find("stateModified") then return os_time()-jD[inGid].lTime end if inId:find("&") then return fibaro_getGlobalValue(realId(inId),"?"):gsub(".+",function (a) if a=="Local" then return os_time()-jVar[realId(inId).."lTime"] else return os.time()-fibaro:getGlobalModificationTime(realId(inId)) end end) end if initMode then confirmProperty(inId:match(".*[>]([%w%.]+)"):gsub("Modified",""),realId(inId)) end return inId:match(".*[>]([%w%.]+)"):gsub("Modified",""):gsub(".+",function (a) return(a=="lastBreached" and os.time()-api.get("/devices/"..realId(inId))["properties"]["lastBreached"] or os.time()-api.get("/devices/"..realId(inId).."/properties/"..inId:match(".*[>]([%w%.]+)"):gsub("Modified",""))["modified"]) end) end --====================================== logTrueFalse function logTrueFalse(cmd,dId,status) local cnt,fStatus,logTxt=0,false,"" if not cmd:find("{") then log_trueFalse=false; return (cmd:len()>2 and cmd..":" or "") end logStatus="" if cmd=="{else}" then indent=indent:sub(1,-3) return bF.."
"..indent.."else
" end logTxt=bF..indent.."if ";cmd=spKeyword(cmd,dId) --:gsub(" ","") if cmd:sub(1,1)=="=" then logTxt=bF..indent.."WHEN "; cmd=cmd:sub(2) end local function getStatus(evalStr) local lgStatus,lStatus,lCnt,cVal,dState,fStatus,inId,stColor,o,val,rVal,ref,z,val1,timeVal={[false]=irF,[true]=lgF},false,0,0,"",0,"",{[false]="",[true]=""},"","" for z in evalStr:gmatch("(.-)[|]") do --print(evalStr,z); inId,ref,o,rVal=z:match("([$&]?[%w_]+)%s-[:]%s-([%w%.]+)%s-([=<>][<>=]?)%s-(.+)");lCnt=lCnt+1;inId=inId..">"..ref dState,logTxt=ref.." "..o.." "..doTheMath(rVal,dId,"parse"),logTxt..(lCnt>1 and bF..indent.." and "..eF or "")..findNameRoom(inId,2) val=doTheMath(rVal,dId);lStatus=false if inId:find("[&$]") and not ref:find("value") then sceneAbort("(17) Property error in '"..cmd:gsub(ref,wF..ref..eF).."'. Acceptable properties for global and keyword variables are: "..wF.."value valueModified"..eF,{cmd},jD[dId].lineNum) end if ref:find("lastBreached") or ref:find("Modified") then cVal=getModificationTime(inId) elseif inId:find("&") then cVal=fibaro_getGlobalValue(inId:match("[&][%w_]+")) elseif inId:find("[$]") then cVal=setKeyword(inId:match("[$][%w_]+"),dId) else cVal=extractValue(inId) end; cVal=tostring(cVal);timeVal=val if val:find("~") then o="~";val,val1=val:match("(.*)~(.*)") end if tonumber(cVal) and tonumber(val) then cVal=to_number(cVal);val=to_number(val);val1=to_number(val1) end if type(cVal)~=type(val) then sceneAbort("(53) Attempt to compare "..type(cVal).." with "..type(val).." in ' "..wF..z..eF.." ' statement. ( "..wF.. tostring(cVal) ..o.. tostring(val)..eF.." )",{z},jD[dId].lineNum); cVal=tostring(cVal);val=tostring(val) end if (tostring(timeVal):match("(%d+:%d+~%d+:%d+)") and isOnTime(timeVal,dId) or o=="=" and cVal==val) or (o==">" and cVal>val) or (o=="<" and cVal" and cVal~=val) or (o=="~" and cVal>val and cVal"..realId(inId).." "..findNameRoom(inId,6))..(o:find("=") and ":" or "("..cVal.."):")..dState:gsub(" ","")..eF else logStatus= logStatus..(lCnt==1 and "{" or cF.." and "..eF)..lgStatus[lStatus]..(jN2s[realId(inId)] or inId:find("[&$]") and tableId(inId) or ""..realId(inId).." "..findNameRoom(inId,6)).."("..cVal.."):"..dState:gsub(" ","")..eF end --logStatus=logStatus..(lCnt==1 and "{" or " and ")..lgStatus[lStatus]..(jN2s[realId(inId)] or inId:find("[&$]") and tableId(inId) or ""..realId(inId)..""..findNameRoom(inId,6))..(lStatus and ":" or "("..cVal.."):")..dState:gsub(" ","").."" logTxt=logTxt.."("..cVal..") ["..stColor[lStatus]..dState.."]
" end logStatus=logStatus.."}" return (fStatus==lCnt and true or false) end --local function getStatus(id,ref,o,val) for a in cmd:gmatch("%b{}") do a=(a:sub(2,-2).."|"):gsub("||","|");cnt=cnt+1;if cnt>1 then logTxt=logTxt..bF..indent.."or ";logStatus=logStatus..cF.." or "..eF end if getStatus((a:sub(1,1):gsub("|","")..a:sub(2)):gsub("(.-)|", function (s) a1=s:match(".-(:.+)") return s:gsub(",",a1.."|").."|" end)) and status then return true end end if status then return fStatus else log_trueFalse=true; return logTxt..bF..indent.."then
" end end --=================================== setVacation function setVacation(cmd,inId) local i,vMode=0,{["vacOn"]=true,["vacOff"]=false,[true]=true,[false]=false} if vMode[cmd]~=atHome then return end if vMode[cmd] and atHome then atHome=false end for i,_ in pairs(jD) do if vMode[cmd] then if ("Z.Device Plugin Variable Keyword"):find(jD[i].idType) then jD[i].tstRate=math.min(jD[i].tstRate,0) elseif not jD[i].idType:find("Auto") then jD[i].tstRate=-1 end if jD[i].vacation=="normal" then jD[i].tstRate=jD[i].tstRateOrg elseif jD[i].vacation=="stop" then jD[i].tstRate=-1 end if jD[i].idType:find("luaScene") and not fibaro:isSceneEnabled(tonumber(realId(i))) then jD[i].tstRate=-10 end else jD[i].tstRate=jD[i].tstRateOrg end end if not vMode[cmd] then atHome=true end end --==================================== cmdActive function cmdActive(cmd,sColor,mStr) return isAct[cmd]==false and fc..(sColor or "red")..">"..(mStr or cmd)..eF or (mStr or cmd) end --==================================== setToolTipTxt function setToolTipTxt(actData,inId,k) local dId if initMode then jTabAct[inId]=(jTabAct[inId] or {}) if type(actData[1])=="string" then jTabAct[inId][k]=cmdActive(actData[2]:match("%w+[%?]?"))..(cmdActive(actData[2]:match(";else;%w+[%?]?")) and ""..cmdActive(actData[2]:match(";else;(%w+[%?]?)")).."" or "").." " else for _,dId in ipairs(actData) do jTabAct[inId][k]=(jTabAct[inId][k] or "")..cmdActive(dId[2]:match("%w+[%?]?"))..(cmdActive(dId[2]:match(";else;%w+[%?]?")) and ""..cmdActive(dId[2]:match(";else;(%w+[%?]?)")).."" or "").." " end end end return jTabAct[inId][k] end --======================================== setTipTxt function getTipTxt(actData,inId,log_trueFalseCont) local dId,tipTxt,inSlot=0,"","";inGid=inId;if not inElse then indent=(jD[inId].lineState and " " or "") end local act3,cmd,_act3=subSign(doTheMath(actData[3],inId,"parse")),actData[2]:match("%w+"),nil if not actData[2]:find"setTimeSlot" then _act3=act3:match("~(.-)~");act3=act3:gsub("%b~~","") end if jCmd[cmd].f3=="~" then if act3:find(":") and actData[4]~="{else}" and not act3:find("time ("..rF..act3:gsub(inSlot,""..inSlot..eF)..eF..")"..bF.." then
";indent=indent.." " elseif act3:len()>0 and actData[4]~="{else}" or _act3 then tipTxt=tipTxt..indent..bF.."delay "..(_act3 and _act3 or act3).." sec. then
"; indent=indent.." " end end if actData[4] then tipTxt=tipTxt..logTrueFalse(actData[4],inId); indent=indent.." " end tipTxt=tipTxt..indent if cmd=="startScene" then tipTxt=tipTxt.." startScene {"..doTheMath(actData[2]:gsub("startScene,",""):gsub("startScene",""),inId,"parse").."} " elseif cmd=="setGlobal" then tipTxt=tipTxt.." "..actData[2].." (\""..actData[1]:gsub("&","").. "\", \""..act3.."\") " elseif ("time int,,, drift"):find(jCmd[cmd].f2) then if cmd=="rptAction" then tmpStr=rptAct(actData[2],act3,actData[1]:match("(%S+)[|]?"),inId,"logInfo") end if cmd=="runAction" then execAct(actData[2],act3,actData[1]:match("(%S+)[|]?"),inId,"logInfo") end tipTxt=tipTxt.." "..(cmd=="rptAction" and cmd..tmpStr or actData[2]).." ("..act3..") " elseif ("notify,,,"):find(jCmd[cmd].f2) then tipTxt=tipTxt..sendNote(subSign(doTheMath(actData[1],inId,"parse")),act3,doTheMath(actData[2],inId),"logInfo") elseif ("delRmd delAllRmd delLogRmd"):find(cmd) then tipTxt=tipTxt.." "..actData[2].." \""..act3.."\""..(#actData[1]>1 and "
"..indent..bF.." do tts \""..subSign(doTheMath(actData[1],inId,"parse")).."\"" or "")..(cmd=="delLogRmd" and bF.." and sendNote (reminder deleted)" or "") elseif ("rmd,,,"):find(jCmd[cmd].f2) then trueFalseOrg=log_trueFalse;tipTxt=tipTxt.." "..actData[2]:match("%w+").." (⟳ "..(actData[2]:match(",(%d+)") and math.max(actData[2]:match(",(%d+)"),60) or "900").." sec.) "..(cmd=="logRmd" and bF.." and "..eF.." sendNote (reminder text)" or "") .."
"..logTrueFalse(actData[1],inId)..indent..bF.. " do tts \""..act3.."\""..(log_trueFalse and "
"..bF..indent.."endif" or "");log_trueFalse=trueFalseOrg elseif ("tts,,,"):find(jCmd[cmd].f2) then tipTxt=tipTxt.." "..actData[2].." (\""..act3.."\" , {".. subSign(doTheMath(actData[1],inId,"parse")).."}) " elseif ("prt"):find(jCmd[cmd].f2) then tipTxt=tipTxt.." f."..actData[2].." ("..(actData[1]:len()>2 and "["..actData[1].."]," or "").."\""..act3.."\")" else tipTxt=tipTxt.." "..cmd.." "..doTheMath(actData[2]:gsub(cmd,""),inId,"parse"):gsub("%?"," (and verify)") end if jCmd[cmd].f1:find("[#<>]") and not ("rmd,,,"):find(jCmd[actData[2]:match("%w+")].f2) then for dId in string.gmatch(actData[1]:gsub("|"," "),"%S+") do dId=spKeyword(dId,inId);tipTxt=tipTxt.."
"..indent..findNameRoom(dId,2)..(cmd:find("press") and "btn "..getButtonName(dId,doTheMath(actData[2]:gsub(cmd,""))).."" or "" )..(dId:find(">") and " jM line "..dId:match(">(%d+)").."" or " ") end end -- if tipTxt:find(">delay") then indent=indent:sub(1,-3);tipTxt=tipTxt.."
"..bF..indent.."enddelay"..eF end if log_trueFalse and not log_trueFalseCont then indent=indent:sub(1,-3);tipTxt=tipTxt.."
"..bF..indent.."endif";log_trueFalse=false end if tipTxt:find(">delay") and not inElse then indent=indent:sub(1,-3);tipTxt=tipTxt.."
"..bF..indent.."enddelay"..eF end if tipTxt:find("iftime") and not inElse then indent=indent:sub(1,-3);tipTxt=tipTxt.."
"..indent..bF.."endtime" end return tipTxt end --====================================== spKeyword function setTipTxt(actData,inId,inAct) local mStr,i,tipTxt,did,cmdData="",0,"",0,{{""},{""}} if os.time()-dispGuide>time2disp then return "" end inElse=false if type(actData[1])=="table" then for _,dId in pairs(actData) do tipTxt=tipTxt..setTipTxt(dId,inId,inAct).."
" end return tipTxt end if not json.encode(actData):find(";else;") then return getTipTxt(actData,inId) end if not actData[4] and not jD[inId][inAct.."State"] then sceneAbort("(30) Missing conditional definition while else statement defined.
"..inAct.."={"..ygF.."\"".. table.concat(actData,"\",\""):gsub("else",wF.."else"..eF).."\"} and "..inAct.."State={} not found",actData,jD[inId].lineNum) end if actData[4] and actData[4]:sub(1,1)=="=" then sceneAbort("(79) Action's statement set to WHEN therefore else condition is unavailble.
"..ygF.."\"".. table.concat(actData,"\",\""):gsub("={","={"):gsub("else",wF.."else").."\"

",actData,jD[inId].lineNum) end for i=1,#actData do cmdData[1][i]=(actData[i]:match("(.-);else;") or actData[i]);cmdData[2][i]=(actData[i]:match(";else;(.+)") or actData[i]) end cmdData[2][4]="{else}"; inElse=true;mStr=getTipTxt(cmdData[1],inId,true)..getTipTxt(cmdData[2],inId) if mStr:find(">delay") then indent=indent:sub(1,-3);mStr=mStr.."
"..bF..indent.."enddelay"..eF end if mStr:find("iftime") then indent=indent:sub(1,-3);mStr=mStr.."
"..bF..indent.."endtime"..eF end return mStr end --====================================== spKeyword function spKeyword(rxMsg,inId) if not rxMsg:find("%$") then return rxMsg end local i,spKey=0,{"$id","$lineId"};for i=1,#spKey do rxMsg=rxMsg:gsub(spKey[i],setKeyword(spKey[i],inId)) end return rxMsg end --=========================== setKeyword function setKeyword(rxMsg,devId) if not rxMsg:find("%$") then return rxMsg end if not devId then keyinId=9999 else keyinId=devId end local i=0 for i=1,#userKeyWord[1] do rxMsg=rxMsg:gsub(userKeyWord[1][i],userKeyWord[2][i]) end for i in string.gmatch(rxMsg,"$%w+") do rxMsg=rxMsg:gsub(i,sysKeyWord[i]) end rxMsg=rxMsg:gsub("##","") return rxMsg end --=========================== runAction function runAction() local i,k,cmd,cmdRef,inId,inRpt,cStatus,elseStatus=0,0,0,0,0,";",false,false if #jMem > 0 then checkReminder() end if json.encode(jAct):len()< 5 then jAct={} else for i,_ in pairs(jAct) do inId=i:gsub("[:].+","") if i:find(":rpt") then inRpt=inRpt..inId..";" end logLineStatus=false mStr=grF.."jM{"..jD[inId].lineNum.."} "..(jAct[i].type or "U")..""..dColor[jD[inId].idType]..findNameRoom(inId,5).."["..sColor[jD[inId].status+1]..jD[inId][jD[inId].property].value.."] => " if jD[inId].lineState and not jAct[i].lineState then if not logTrueFalse(jD[inId].lineState[1],inId,"status") then if dbgFalseState then prtLine(mStr.."lineState"..logStatus.." -> no actions.") end if jD[inId].lineState[1]:sub(1,1)=="=" and not initMode then jAct[i].lTime=os_time() goto nextAct end if jD[inId].errAct then jAct[i]={act=json.decode(json.encode(jD[inId].errAct)),actState="errAct",lTime=os_time(),type="Se"} else jAct[i]=nil goto nextAct end else jAct[i].lineState=true if dbgTrueState then prtLine(mStr.."lineState"..logStatus) else logLineStatus=mStr.."lineState"..logStatus end if jD[inId].okAct then jAct[inId..":ls"]={act=json.decode(json.encode(jD[inId].okAct)),actState="okAct",lTime=os_time(),type="So"} end end end -- if jD[inId].lineState and if type(jAct[i].act[1])=="string" then jAct[i].act={jAct[i].act} end logActStatus=false;cStatus=true if jAct[i].actState and jD[inId][jAct[i].actState.."State"] and not jAct[i].cmdState then jAct[i].cmdState=true cStatus=logTrueFalse(jD[inId][jAct[i].actState.."State"][1],inId,"status") if not cStatus then if not json.encode(jAct[i]):find(";else;") then if dbgInfo and dbgFalseState then prtLine(mStr..jAct[i].actState.."State"..logStatus.." -> no actions.") end jAct[i]=nil; goto nextAct else if dbgFalseState then prtLine(mStr..jAct[i].actState.."State"..logStatus.." -> else actions.") else logActStatus=mStr..jAct[i].actState.."State"..logStatus.." -> else actions." end end else if dbgInfo and dbgTrueState then prtLine(mStr..jAct[i].actState.."State"..logStatus) else logActStatus=mStr..jAct[i].actState.."State"..logStatus end end end --if jAct[i].actState then for k,cmdRef in pairs(jAct[i].act) do logStatus="";cmd=json.decode(json.encode(cmdRef)) if json.encode(cmd):find(";else;") then elseStatus=true else elseStatus=false end if not cStatus then if cmdRef[4] or not elseStatus then jAct[i].act[k][1]="done" end end if elseStatus and not cmd[4] then cmd[4]=jD[inId][jAct[i].actState.."State"][1] end if jAct[i].act[k][1]~="done" and getDelay(inId,cmd,jAct[i].lTime) and sendCmd(cmd,jAct[i].lTime,inId,jAct[i].type) then jMe[inId]=(jMe[inId] and jMe[inId]+1 or 1);jMeSum=jMeSum+1;jAct[i].act[k] = "done" end end for k=#jAct[i].act,1,-1 do if jAct[i].act[k]=="done" then table.remove(jAct[i].act,k) end end if #jAct[i].act==0 then jAct[i]=nil end ::nextAct:: end end if json.encode(jRpt):len()<15 then jRpt={} else for i=#jRpt,1,-1 do if not inRpt:find(";"..jRpt[i].id..";") then jAct[jRpt[i].id..":rpt"]={act=json.decode(json.encode(jRpt[i].act)),lTime=os_time()+jRpt[i].dTime,type=jRpt[i].type} inRpt=inRpt..jRpt[i].id..";"; table.remove(jRpt,i) end end end if json.encode(jBack):len()< 5 then jBack={} return end for i,_ in pairs(jBack) do if os.time()-jBack[i].lTime>jBack[i].cnt*3 then if readBack(i,jBack[i]) then jBack[i]=nil else jBack[i].cnt=jBack[i].cnt+1 end end end end --=================================== readBack function readBack(devId,rData) if rData.resp==fibaro:getValue(devId,rData.rCmd) then return true end if rData.cnt>4 then lastAct(trueFalseColor["fail"]..""..rData.inId..""..rData.rCmd.."{"..devId..") readback failed}",rData.inId) sceneAbort("(75) Z-wave readback fail on "..wF..findNameRoom(devId,2)..eF.."
executed command: "..wF.."fibaro:call("..devId..","..rData.cmd..")"..eF.." readback command: "..wF.."fibaro:getValue("..devId..","..rData.rCmd..")"..eF,{rData.cmd.."?"},jD[rData.inId].lineNum) homeTts(sceneName.." alert. Unable to "..rData.cmd:gsub("%?","").." "..findNameRoom(devId,1)) return true else return false end end --=================================== fibaro_call function fibaro_call(devId,param,inId) if powerOutage then return "fail" end if param=="poll" then devId=jDlist["pId"..jDlist[devId]] end local cNum,i,cVal,cmd,cRes,rCmd,res=0,0,{},param:match("%w+"),"","","" for i in string.gmatch(param:gsub(","," "),"%S+") do cVal[#cVal+1]=i end if debugCall then print("fibaro:call(",devId,cmd,cVal[2],cVal[3],cVal[4],cVal[5],cVal[6],cVal[7],")") end if jCmd[cmd].resp:find("n/a") then fibaro:call(devId,cmd,cVal[2],cVal[3],cVal[4],cVal[5],cVal[6],cVal[7]) return true end rCmd,res=jCmd[param:match("%w+")].resp:match("(.*)=(.*)") if rCmd then cRes=fibaro:getValue(devId,rCmd) end if cRes then cRes=cRes:gsub(" ","") end if res:find("x") then res=param:gsub(cmd..",",""):gsub(cmd.."%?,","") end eCmd.dev=eCmd.dev+1 fibaro:call(devId,cmd,cVal[2],cVal[3],cVal[4],cVal[5],cVal[6],cVal[7]) if not param:find(cmd.."%?") then return true end jBack[devId]={rCmd=rCmd,cmd=cmd,cnt=1,inId=inId,lTime=os.time(),resp=res} return true end --========================= add/subSign function subSign(mStr) return tostring(mStr):gsub("[%(%+%-%*/@&%$]%%", function (a) return a:gsub("%%","") end) end function addSign(mStr) return tostring(mStr):gsub("[%(%+%-%*/@&%$]", function (a) return a.."%" end) end function execCmd(actData,actTime,devId,aType) local cStatus,i,dId,sMode,cmdStr,devStr,dTime,dName=true,0,0,{["enableScene"]=true,["disableScene"]=false},actData[2]:match("[%w_]+"),""..(aType or "U")..""..devId:gsub("[%w_']+",function (a) return jN2s[a] or a end).."","","" if os_time()-actTime < 0 then return false end if actData[4] and not logTrueFalse(actData[4],devId,"status") then if actData[4]:sub(1,1)=="=" and not initMode then return false else if dbgCmdFalse then prtLine(grF.."jM{"..jD[devId].lineNum.."} "..aType..""..dColor[jD[devId].idType]..findNameRoom(devId,5).."["..sColor[jD[devId].status+1]..jD[devId][jD[devId].property].value.."] : "..rF..actData[2]..eF.." {"..ygF..(jN2s[actData[1]] or actData[1])..eF..") "..logStatus.." => "..rF.."no action"..eF..eF) end return true end end local cmdType,act1,act2,act3=jCmd[cmdStr].f2:match("%w+"),subSign(doTheMath(actData[1],devId)),doTheMath(actData[2],devId),subSign(doTheMath(actData[3],devId)) if not actData[2]:find("setTimeSlot") and tonumber(act3:match("~(.-)~")) then dTime=""..yF.. math.floor(tonumber(act3:match("~(.-)~"))+.5).."s>"..eF end act3=act3:gsub("%b~~","") if jCmd[cmdStr].f3=="~" then if act3:find(":") and not act3:find(""..eF elseif to_number(to_number(act3))>0 then dTime=""..yF..to_number(to_number(act3)).."s>"..eF end end if initMode then dTime="" end;devStr=devStr..dTime if not jCmd[cmdStr].f1:find("[#<>]") then eCmd[cmdType]=(eCmd[cmdType] and eCmd[cmdType]+1 or 1) end if cmdType=="global" then lastAct(trueFalseColor[fibaro_setGlobal(act1,act3,actData[1])]..devStr..act2.."("..ygF..actData[1]..eF..grF..", "..act3..eF..")
",devId) elseif actData[2]=="shutdown" then HomeCenter.SystemService.shutdown() elseif actData[2]=="reboot" then HomeCenter.SystemService.reboot() elseif actData[2]=="powerOutage" then powerOutage=b2n[to_number(actData[3])];setVacation(powerOutage,devId);if not initMode then onTime();dispInfo() end lastAct(trueFalseColor[true]..devStr..act2.."{"..act3.."}
",devId) elseif cmdType=="notify" then sendNote(act1,act3,act2);lastAct(trueFalseColor[true]..devStr..cmdActive(act2)..cmdActive(act2:gsub("[.*]",act2:match("%w+")),"red","("..txtNote..") '"..cutStr(ctlChar(act1),15).."','"..cutStr(ctlChar(act3),25).."')").."",devId) elseif cmdType=="rmd" then lastAct(trueFalseColor[logReminder(actData[1],act2,actData[3]:gsub("%b~~",""),devId)]..devStr..act2.."{"..cutStr(act3,25).."}",devId) elseif cmdType=="tts" then homeTts(act3,act1);lastAct(trueFalseColor[true]..devStr.."tts{'"..grF..cutStr(act3,25)..eF.."'"..grF..cutStr(" "..act1,15)..eF.."}",devId) elseif cmdType=="prt" then act1=(act1:len()>2 and act1 or act2):upper();lastAct(trueFalseColor[true]..devStr..prtFunc[act2].."["..act1.."] "..act3.."",devId) elseif cmdType=="drift" then updateTimeDrift(to_number(act3)-timeDrift);timeDrift=to_number(act3);lastAct(trueFalseColor[true]..devStr..act2.."("..grF..timeDrift..eF..")",devId) elseif cmdType=="vac" then setVacation(act2,devId);lastAct(trueFalseColor[true]..devStr.."vac{"..grF..act2:gsub("vac","")..eF.."}",devId);if not initMode then onTime();dispInfo() end elseif jCmd[cmdStr].f1:find("[#<>]") then act1=spKeyword(actData[1],devId):gsub(" ","");cmdAct="";cmdId="" cmdId=trueFalseColor[true] inCmd=act2:match("[%w_]+") for dId in act1:gsub("|"," "):gmatch("%S+") do iMsg=false dName=dId:gsub("[_&$]?[%w_']+", function (a) return jN2s[a] or a end) if eCmd[cmdType] then eCmd[cmdType]=eCmd[cmdType]+1 else eCmd[cmdType]=1 end --if actData[2]:find("startScene") then start_scene(realId(dId),act2:gsub("startScene,",""):gsub("startScene",""));;cmdId=cmdId..ygF..dName..eF..",,";cmdAct=trueFalseColor[true]..devStr.."startScenecmdId "..grF..(actData[2]:match("%b{}") and actData[2]:gsub("startScene,",""):gsub("startScene",""):gsub("}{","},{") or actData[2]:gsub("startScene,",""):gsub("startScene",""))..eF..")"..eF..eF if actData[2]:find("startScene") then --start_scene(realId(dId),act2:gsub("startScene,",""):gsub("startScene",""));;cmdId=cmdId..ygF..dName..eF..", "..grF..(actData[2]:match("%b{}") and actData[2]:gsub("startScene,",""):gsub("startScene",""):gsub("}{","},{") or actData[2]:gsub("startScene,",""):gsub("startScene",""))..eF..",";cmdAct=trueFalseColor[true]..devStr.."startScenecmdId"..eF..eF start_scene(realId(dId),act2:gsub("startScene,",""):gsub("startScene","")) cmdId=cmdId..dName..",";cmdAct=trueFalseColor[true]..devStr..actData[2]:gsub(inCmd..",",inCmd..grF..","):gsub("}{","},{")..eF.."cmdId"..eF..eF elseif actData[2]:find("setIcon") then cmdId=cmdId..trueFalseColor[setIcon(dId,act2:gsub("setIcon,",""))]..dName..eF..",";cmdAct=trueFalseColor[true]..devStr..actData[2].."cmdId "..(iMsg or "")..eF..eF elseif actData[2]=="setStateValue" then if jD[dId].stateValue~=act3 then jD[dId].stateValue=act3;cmdId=cmdId..ygF..dName..eF..grF..", "..act3..eF..",";cmdAct=trueFalseColor[true]..devStr..act2.."cmdId"..eF..eF end elseif actData[2]=="setStateFormula" then jD[dId].stateValue=actData[3];cmdId=cmdId..ygF..dName..eF..grF..", "..act3.." ["..cutStr(actData[3],25).."]"..eF..",";cmdAct=trueFalseColor[true]..devStr..act2.."cmdId"..eF..eF elseif actData[2]=="setStateDelay" then if jD[dId].lag~=act3 then jD[dId].lag=act3;jD[dId].lagStr=""..dly..jD[dId].lag.."";cmdId=cmdId..ygF..dName..eF..grF..", #"..act3..eF..","; cmdAct=trueFalseColor[true]..devStr..act2.."cmdId"..eF..eF end elseif actData[2]=="setState" then setNewState(act3,dId);cmdId=cmdId..ygF..dName..eF..grF..", "..act3..eF..",";cmdAct=trueFalseColor[true]..devStr..act2.."cmdId"..eF..eF elseif actData[2]:find("runAction") then cmdId=cmdId..trueFalseColor[execAct(act2,act3,dId,devId)]..dName..eF..",";cmdAct=trueFalseColor[true]..devStr.."runAction "..act3.."("..act2:gsub("runAction[,]?","")..")cmdId"..eF..eF elseif actData[2]:find("rptAction") then cmdId=cmdId..trueFalseColor[rptAct(act2,act3,dId,devId)]..dName..eF..",";cmdAct=trueFalseColor[true]..devStr.."repeat "..act3.."{} "..rptAct(act2,act3,dId,devId,"logInfo").."cmdId"..eF..eF elseif cmdType=="time" then cmdId=cmdId..ygF..dName..eF..grF..", "..setTimeSpec(cmdStr,act3,dId)..eF..",";cmdAct=trueFalseColor[true]..devStr..act2.."cmdId"..eF..eF elseif ("enableScene disableScene"):find(actData[2]) then cmdId=cmdId..trueFalseColor[sceneEnabled(dId,sMode[actData[2]])]..dName..eF..",";cmdAct=trueFalseColor[true]..devStr..actData[2].."(cmdId)"..eF..eF elseif ("killScene"):find(actData[2]) then fibaro:killScenes(tonumber(realId(dId)));cmdId=cmdId..ygF..dName..eF..",";cmdAct=trueFalseColor[true]..devStr..actData[2].."cmdId"..eF..eF elseif cmdType=="call" then cmdId=cmdId..trueFalseColor[fibaro_call(realId(dId),act2,devId)]..dName..act2:gsub(".+",function (a) if a:find("press") then return ""..getButtonName(dId,act2).."" else return "" end end)..eF..","; cmdAct=trueFalseColor[true]..devStr..act2:gsub(inCmd..",",inCmd..grF..",")..eF.."cmdId"..eF..eF else cmdAct=trueFalseColor["fail"]..devStr.."Unknown Command("..act1..",'"..act2.."', "..act3..")" end end -- for dId in string.gmatch(string.gsu lastAct(cmdAct:gsub("cmdId"," {"..cmdId:sub(1,-2)..eF..")"),devId) else lastAct(rF..devStr.."Unknown Command("..act1..",'"..act2.."', "..act3..")",devId) end return true end --=================================== execCmd function sendCmd(actData,actTime,devId,aType) local i,cmdData=0,{{""},{""}};inGid=devId;elseAct="" if not json.encode(actData):find(";else;") or not actData[4] then return execCmd(actData,actTime,devId,aType) end for i=1,#actData do cmdData[1][i]=(actData[i]:match("(.-);else;") or actData[i]);cmdData[2][i]=(actData[i]:match(";else;(.+)") or actData[i]) end cmdData[1][4]="";cmdData[2][4]=""; if logTrueFalse(actData[4],devId,"status") then return execCmd(cmdData[1],actTime,devId,aType) else elseAct="else" return execCmd(cmdData[2],actTime,devId,aType) end end --=================================== sendCmd function getDelay(inId,actData,actTime) local cmdStr,act3=actData[2]:match("[%w_]+"),subSign(doTheMath(actData[3],inId)) if not actData[2]:find("setTimeSlot") and tonumber(act3:match("~(.-)~")) and os_time()-actTime< tonumber(act3:match("~(.-)~")) then return false end act3=act3:gsub("%b~~","") if jCmd[cmdStr].f3=="~" then if act3:find(":") and not act3:find("#jD[inId][act2exec] or tonumber(i)<1 then sceneAbort("(81) "..wF..cmd.." error! Array number "..irF..i.." not found in "..wF..act2exec.." on jM line "..jD[inId].lineNum..":
"..json.encode(jM[jD[inId].lineNum]):gsub(act2exec,irF..act2exec..eF),{inId,cmd},jD[devId].lineNum) return "fail" end aTable[#aTable+1]=jD[inId][act2exec][tonumber(i)] end if logInfo then return true end if #aTable==0 then aTable=json.decode(json.encode(jD[inId][act2exec])) end jAct[inId..":run"]={act=aTable,actState=act2exec,lTime=os_time(),type="R"} return true else sceneAbort("(80) Error in "..wF..cmd.." array "..irF..act2exec.." not found in jM line "..jD[inId].lineNum..":
"..json.encode(jM[jD[inId].lineNum]),{cmd,inId},jD[devId].lineNum) return "fail" end end --=================================== execAct function rptAct(cmd,act2exec,inId,devId,logInfo) local i,rptParam,rAct,lAct=0,{},{},{} for i in cmd:gmatch(",(%d+)") do rptParam[#rptParam+1]=tonumber(i) end if not rptParam[2] then sceneAbort("(87) Error in "..wF..cmd.." parameters. Missing "..wF..(rptParam[1] and "" or "repetitions, ")..(rptParam[2] and "" or "delay")..eF.." parameter.
Format is:"..wF.."\"rptAction,[rptNum],[delay]\""..eF,{cmd},jD[devId].lineNum) return "fail" end for i in act2exec:gmatch(",(%d+)") do rptParam[#rptParam+1]=tonumber(i) end act2exec=act2exec:match("%w+") if not jD[inId][act2exec] then sceneAbort("(88) Error in "..wF..cmd.." array "..irF..act2exec.." not found in jM line "..jD[inId].lineNum..":
"..json.encode(jM[jD[inId].lineNum]),{cmd,inId},jD[devId].lineNum) return "fail" end if rptParam[3] and not jD[inId][act2exec][rptParam[3]] then sceneAbort("(89) "..wF..cmd.." error! Array number "..irF..rptParam[3].." not found in "..wF..act2exec.." on jM line "..jD[inId].lineNum..":
"..wF..json.encode(jM[jD[inId].lineNum]):gsub(act2exec,irF..act2exec..eF)..eF,{inId,cmd,act2exec..","..rptParam[3]},jD[devId].lineNum) return "fail" end if logInfo then return " x"..rptParam[1].." every "..rptParam[2].."s " end for i in act2exec:gmatch(",(%d+)") do rptParam[#rptParam+1]=tonumber(i) end rAct=json.decode(json.encode(jD[inId][act2exec])) if rptParam[3] then rAct=json.decode(json.encode(jD[inId][act2exec][rptParam[3]])) lAct=json.decode(json.encode(jD[inId][act2exec]));table.remove(lAct,rptParam[3]) jRpt[1]={id=inId,act=json.decode(json.encode(lAct)),type="P-"..rptParam[3],dTime=rptParam[2]} end for i=1,rptParam[1] do jRpt[#jRpt+1]={id=inId,act=json.decode(json.encode(rAct)),type="P"..tonumber(rptParam[1])-i+1,dTime=(i==rptParam[1] and 0 or rptParam[2])} end return true end --=================================== rptAct function setNewState(nState,inId) local nProperty,nOper,nValue nOper=nState:match("[=<>!][>=]?"); if nOper then nProperty,nValue=nState:match("(.-)"..nOper.."(.*)") end if nOper and nProperty and nValue then tmpNum=api.get("/devices/"..realId(inId))["properties"][nProperty] if tmpNum then jD[inId].stateOper,jD[inId].property,jD[inId].stateValue=nOper,nProperty,tostring(nValue);jD[inId][jD[inId].property]={value=tostring(tmpNum),lTime=os_time()} jD[inId].userTerms=jD[inId].property..jD[inId].stateOper else sceneAbort("(73) Attempt to index "..wF.."property"..eF.." field a nil value in setState command.",{nState},jD[inId].lineNum) end else sceneAbort("(72) Syntax error in setState: '"..wF..nState..eF.."'. Acceptable format is:
[property] [operand] [value]
e.g."..wF.."value>30"..eF,{nState},jD[inId].lineNum) end end --=================================== setNewState function setTimeSpec(cmd,tSpec,inId) if cmd:find("Span") then tmpNum=tonumber(tSpec) if tmpNum then jD[inId].tstRate=tmpNum;jD[inId].tstRateOrg=tmpNum return jD[inId].tstRate else sceneAbort("(68) Set timeSpan error '"..wF..tSpec..eF.."'. No changes done on time span.",{cmd,tSpec},jD[inId].lineNum) return jD[inId].tstRate end elseif cmd:find("Slot") then tSpec=tSpec:gsub(" ","") tSpec=(tSpec:match("(.-)=") and jD[inId].timeSlot:gsub(tSpec:match("(.-)="),tSpec:match("=(.+)")) or tSpec) jD[inId].timeSlot=(meanVsSlot(inId,tSpec) and tSpec or jD[inId].timeSlot);jD[inId].timeSlotOrg=jD[inId].timeSlot return jD[inId].timeSlot end end --================================== setTimeSpec function sceneEnabled(inId,state) local tSlot,fStatus={},fibaro:isSceneEnabled(tonumber(realId(inId))) if jD[inId] and atHome then tSlot={[true]=jD[inId].tstRateOrg,[false]=-10};jD[inId].tstRate=tSlot[fStatus]; if not fStatus then jD[inId].status=4 end end if state==nil then return fStatus elseif state==fStatus then return false end fibaro:setSceneEnabled(tonumber(realId(inId)),state) return true end --=================================== sceneEnabled function logVal(val,inId,vCol) if jD[inId].trigAll and jD[inId].trigOn then jD[inId].inVal=val elseif jD[inId].inVal==val then return else jD[inId].inVal=val end if #jD[inId].lVal==dataRecord.values then table.remove(jD[inId].lVal,1) end jD[inId].lVal[#jD[inId].lVal]=jD[inId].lVal[#jD[inId].lVal].."
";jD[inId].lVal[#jD[inId].lVal+1]=vCol.. os.date(" %d %b %H:%M:%S",os_time()).." "..val..(jD[inId].state==3 and " ("..vCol..jD[inId].property..") " or " ") if initMode or jD[inId].tstRate<0 then return end if jD[inId].trigAct and jD[inId].tstRate>=0 then if jD[inId].state==2 or jD[inId].status~=3 then jAct[inId..":trig"]={act=json.decode(json.encode(jD[inId].trigAct)),actState="trigAct",lTime=os_time(),type="G"} end end end --=================================== logVal function lastAct(actStr,inId) if #actStr<5 then return end if #jD[inId].lAct==dataRecord.commands then table.remove(jD[inId].lAct,1) end jD[inId].lAct[#jD[inId].lAct+1]=sColor[jD[inId].status+1].."
"..os.date(" %d %b %H:%M:%S",os_time()).." "..jD[inId].inVal.." "..actStr:gsub(">.+",">"):gsub("",""):gsub("","").." "; if #jD[inId].lAct==dataRecord.commands or #jD[inId].lAct==1 then jD[inId].lAct[1]="
"..jD[inId].lAct[1] end if dbgInfo then if logLineStatus then prtLine(logLineStatus); logLineStatus=false end if logActStatus then prtLine(logActStatus); logActStatus=false end prtLine(grF.."jM{"..jD[inId].lineNum.."} "..(actStr:match("%S+") or "U")..""..dColor[jD[inId].idType]..findNameRoom(inId,5).."["..sColor[jD[inId].status+1]..jD[inId][jD[inId].property].value.."] => "..elseAct..actStr:gsub("^.-","")..(logStatus:len()>5 and "  "..logStatus.."" or "")) end if gData.logAct[#gData.logAct]==actStr then return end if #gData.logAct==cnnLine then table.remove(gData.logAct,1) end;gData.logAct[#gData.logAct+1]=actStr end --=============================== lastAct function logEvent(inId) if not jDlist["pId"..jDlist[inId]] then return end --not Z-wave local pId=jDlist["pId"..jDlist[inId]] jTop[inId]=(jTop[inId] or 0)+1;gData.zTotal=gData.zTotal+1; jEvent[pId]=jEvent[pId] or {name=findNameRoom(pId,2),total=0,child={}};jEvent[pId].child[inId]=jEvent[pId].child[inId] or {} jEvent[pId].child[inId][1]=(jEvent[pId].child[inId][1] or 0) +1 jEvent[pId].child[inId][2]=findNameRoom(inId,2).." ("..jEvent[pId].child[inId][1]..")
" jEvent[pId].total=jEvent[pId].total+1 end --================================= logEvent function logDead(inId,dReason) local deadOnMsg,subject,pId="is dead ",sceneName.." Alert (Dead)",jDlist["pId"..jDlist[inId]] or "none" jF[inId]=jF[inId] or {total=0,reason={},nack=0,id=inId,dead="false"} if dReason=="" then dReason="wkUp";deadOnMsg="is wake up";subject=sceneName.." Report (wakeUp)" elseif dReason==gData.transfer then jF[inId].nack=jF[inId].nack+1 else jF[inId].total=jF[inId].total+1 end if dReason~="wkUp" then gData.numDeads=gData.numDeads+1;gData.totalDeads=gData.totalDeads+1 end if not (json.encode(jF[inId].reason)):find(dReason.. os.date(" %d %b %H:%M",os_time())) then if #jF[inId].reason >= gData.deadLog then table.remove(jF[inId].reason,1) end jF[inId].reason[#jF[inId].reason+1]=dReason.. os.date(" %d %b %H:%M",os_time()) if deadNote and dReason~=gData.transfer then if not jLog[pId] or jLog[pId]~=deadOnMsg or pId=="none" or rptAllDead then jLog[pId]=deadOnMsg homeTts(subject:gsub("%b()","").."! "..findNameRoom(inId,1).. ". ".. deadOnMsg) if tostring(api.get("/devices/"..realId(inId))["properties"]["dead"]) ~=jF[inId].dead then jF[inId].dead=tmpStr;sendNote(subject,"Device "..realId(inId)..": "..findNameRoom(inId,1).." "..deadOnMsg.."\nEvent history: "..json.encode(jF[inId].reason):gsub("\"","")) end end end end --if not json.encode(jF[parentId]) end --=============================== logDead function wakeUpDead() local i=0;for i,_ in pairs(jF) do if api.get("/devices/"..i.."/properties/dead")["value"] then api.post("/devices/"..i.."/action/wakeUpDeadDevice") end end end --=====================realId tableId jDtableId jId================================================ function realId(idx) return tostring(idx):match("^[_$&@]?([%w_]+)") end --===================================== function tableId(idx) return tostring(idx):match("[_$&]?[%w_]+") end --============================== function jDtableId(idx) return tostring(idx):match("[_$&]?[%w_]+>%w+") end --======================== function jsId(idx) return realId(idx)..""..(jN2s[idx] or "").."" end --================ function setTickTime(tblId,setMax) local refMax=jD[tblId].maxTime if jD[tblId].status<2 or jD[tblId].timeSpanOn then jD[tblId].maxTime=math.max(jD[tblId].maxTime,os_time()-jD[tblId].lTime) if refMax~=jD[tblId].maxTime then jD[tblId].maxTimeStamp=os_time() end end if jD[tblId].lTime<0 or jD[tblId].tstRate==0 then jD[tblId].maxTime = 0 end if setMax then return jD[tblId].maxTime end end --=================================== setTickTime function setParams(iData,inId,reload) if not inId:match("%d+>%d+") then return end if reload then for l,_ in pairs(jD) do if inId:match("%d+>%d+") then setParams(api.get("/devices/"..realId(l)),l) end end return end if iData.properties.batteryLevel then jD[inId].pRef.batteryLevel=iData.properties.batteryLevel end if iData.properties.armed~=nil then jD[inId].pRef.armed=iData.properties.armed end --if to_number(iData.properties.armed)==1 then jD[inId].armed="🚨" end end jD[inId].cfg=" by '"..(iData.isPlugin and "Plug-in: "..iData.type:gsub("com.fibaro.","") or iData.type=="virtual_device" and "Virtual Device IP:"..iData.properties.ip.." Port:"..iData.properties.port or #iData.properties.zwaveCompany>1 and iData.properties.zwaveCompany or "Unknown")..(iData.properties.zwaveVersion and "\" ( zVer="..iData.properties.zwaveVersion.." )" or "'") end --=================================== setParams function checkType(dType,inId,apiData) if dType=="device" then dType="Z.Device" if not apiData.type then sceneAbort("(34) ID "..wF..jsId(inId)..eF.." is not a \""..dType.."\" type.",{inId},jD[inId].lineNum) end if apiData.properties then setParams(apiData,inId) end if apiData.isPlugin then dType="Plugin" end if apiData.type=="virtual_device" then dType="V.Device" elseif not apiData.type:find("com.fibaro") then sceneAbort("(35) Device "..wF..findNameRoom(inId,2)..eF.." is "..rF..apiData.type..eF.." type and not supported.
Supported devices:"..wF.." Zwave, Plugin, Virtual"..eF,{inId},jD[inId].lineNum) end elseif dType=="scene" then dType=string.gsub(api.get("/scenes/"..realId(inId))["type"],"com.fibaro.","") if api.get("/scenes/"..realId(inId))["autostart"]==true and api.get("/scenes/"..realId(inId))["runConfig"]=="TRIGGER_AND_MANUAL" then dType="Auto-"..dType end end jD[inId].idType=dType;tableOrder[4][dType]=tableOrder[4][dType]+1; end --================================== checkType function checkAndOr(sLine,inId,sErr,acTbl) local k=0 for k in sLine:gmatch("%b{}") do if k:find(" or ") then sceneAbort("(98) Format syntax error in "..wF..k:gsub(" or ",rF.." or "..eF)..eF.." statement.
The "..wF.."or"..eF.." function is strictly prohibited inside brackets {}. Only "..wF.."and"..eF.." function could be used.
Syntax is "..sErr,{k},jD[inId].lineNum) end end for k in sLine:gmatch("%b}{") do if k:find(" and ") then sceneAbort("(99) Format syntax error in "..wF..k:gsub(" and ",rF.." and "..eF)..eF.." statement.
The "..wF.."and"..eF.." function is strictly prohibited between brackets. Only "..wF.."or"..eF.." function could be used.
Syntax is "..sErr,{k},jD[inId].lineNum) end end tmpStr,tmpNum=sLine:gsub("{","");tmpStr,lNum=tmpStr:gsub("}","|") if tmpNum~=lNum or sLine:find("{{") or sLine:find("}}") then sceneAbort("(6) Syntax error in statement "..wF..acTbl..eF.." table, '{' expected to close '}' at \""..wF..sLine:gsub("{",rF.."{"..eF):gsub("}",rF.."}"..eF).."\"
Syntax is "..sErr,{acTbl,sLine},jD[inId].lineNum) end end --=================================== checkAndOr function checkAct4(actStr,inId,acTbl) local k,i,j,dNum,lNum,lSeen,pName,o,sErr=0,0,0,0,0,{},"","",lgF.."\""..wF.."["..eF.."if/when"..wF.."]"..eF.." {"..wF.."ID"..eF..":[property][<=>][value]}\"
e.g. \"[if/when] "..wF.."{"..eF.."999:value=true "..wF.."and"..eF.." 888:lastBreached>10"..wF.."} or {"..eF.."$hour:value=6 "..wF.."and"..eF.." &gVar:valueModified>20"..wF.."}"..eF.."\"" if actStr[4] then checkAndOr(actStr[4],inId,sErr,acTbl) actStr[4]=actStr[4]:gsub("%s+|%s+","|"):gsub("[%s+]?if[%s+]?{","{"):gsub("%s+and%s+","|"):gsub("[%s+]?when[%s+]?{","={"); lSeen[#lSeen+1]=actStr[4] end if actStr[1]:match("%b{}") or acTbl:find("State") then checkAndOr(actStr[1],inId,sErr,acTbl) actStr[1]=actStr[1]:gsub("%s+|%s+","|"):gsub("[%s+]?if[%s+]?{","{"):gsub("[%s+]?when[%s+]?{","={"):gsub("%b{}", function(a) return a:gsub("%s+and%s+","|") end); lSeen[#lSeen+1]=actStr[1] end if #lSeen==0 then return end for j=1,#lSeen do lSeen[j]=spKeyword(lSeen[j],inId); for i in lSeen[j]:gmatch("([&$][%w_']+[:]?)[<=>]") do if not i:find(":") then lSeen[j]=lSeen[j]:gsub(i,i..":value") if actStr[4] and j==1 then actStr[4]=lSeen[j] else actStr[1]=lSeen[j] end end end tmpStr=lSeen[j]:gsub("{",""):gsub("}","|") if not lSeen[j]:sub(1,1):match("[={]") then sceneAbort("(5) Condition format syntax error in "..wF..acTbl..eF.." missing "..wF.."[if/when]{"..eF.." in \""..wF..lSeen[j].. "\"
Syntax is "..sErr,{acTbl,lSeen[j]:sub(1,2)},jD[inId].lineNum) end for i in tmpStr:gmatch("(.-)[|]") do if i:len()==0 then goto nextLseen end if i:lower():find("modified") and not i:find("Modified") then sceneAbort("(4) Format error in \""..wF..i..eF.."\". Missing capital letter '"..lgF.."M"..eF.."odified'
Syntax is "..wF.."\"device property"..lgF.."Modified"..eF..eF.."\"
e.g. "..lgF.."\"{999:powerModified<60 and 888:valueModified>10}\"",{acTbl,i},jD[inId].lineNum) end if not i:find(":") or not i:find("[<=>]") then sceneAbort("(3) Format syntax error in \""..wF..lSeen[j]:gsub(i,rF..i..eF)..eF.."
Syntax is "..sErr,{acTbl,lSeen[j]},jD[inId].lineNum) end dNum,pName,o,val=i:match("([$&]?[%w_]+)%s-[:]%s-([%w%.]+)%s-([=<>][<>=]?)%s-(.+)") if not val then sceneAbort("(1) Syntax error in '"..wF..lSeen[j]:gsub(i,rF..i..eF)..eF.."', incorrect value.
Syntax is "..sErr,{acTbl,lSeen[j]},jD[inId].lineNum) end if dNum:gsub("or",""):find("%D") and not dNum:find("[&$]") then sceneAbort("(2) Syntax error in '"..i:gsub(dNum,wF..dNum..eF).."'
Syntax is "..sErr,{acTbl,dNum},jD[inId].lineNum) end end ::nextLseen:: end end --=========================== checkAct4 function checkFormat(k,inId,acTbl) if json.encode(k):find("@[&$]") then tmpStr=json.encode(k):match("@[$&]%w+") sceneAbort("(83) Format syntax error, Attempt to retrive non-device value "..wF..tmpStr..eF..". Syntax is:"..wF.."
@[DevID]>[property] (@407>power)
&variableName
$keywordName"..eF,{tmpStr},jD[inId].lineNum) end if type(k)~="table" then sceneAbort("(76) Syntax error, Attempt to access a corrupted table "..wF.. acTbl.."{}"..eF..". Verify that proper structures are defined.",{acTbl,k},jD[inId].lineNum) end if #k<3 or #k>5 then sceneAbort("(9) Wrong number of "..#k.." arguments found in '"..wF..acTbl.."' commands table.
Requires 3 mandatory and 2 (4th and 5th) optional arguments.",{acTbl,"\""..k[#k].."\""},jD[inId].lineNum) end for i=1,4 do if k[i] and k[i]:match("%b[]") then k[i]=k[i]:gsub("%b[]",function (a) if a:match("(%[%%)") then return a:gsub("%[%%","["):gsub("%%%]","]") else return a:sub(2,-2) end end) end end if k[2]=="setGlobal" then k[1]="&"..k[1]:gsub("&","") end local cmdStr,i,uAdd=k[2]:match("%w+"),0,nil if k[2]:sub(1,1)=="+" then k[2]=k[2]:sub(2);uAdd=1 end; getDevAct(k[1],cmdStr,inId,uAdd) if jCmd[cmdStr].f1:find("[#<>]") then k[1]=k[1]:gsub(" ","") end if #k<3 or #k>5 then sceneAbort("(9) Wrong number of "..#k.." arguments found in '"..wF..acTbl.."' commands table.
Requires 3 mandatory and 2 (4th and 5th) optional arguments.",{acTbl,"\""..k[#k].."\""},jD[inId].lineNum) end if k[5] and type(k[5])~="boolean" then sceneAbort("(63) Wrong type of parameter "..wF..k[5]..eF.." in "..wF..k[2]:match("%w+")..eF.." command. Boolean required ("..bF.."false true"..eF..") "..type(k[5]).." type found.",{acTbl,"\""..k[5].."\""},jD[inId].lineNum) end for i=1,3 do if type(k[i])~="string" then tmpNum=json.decode(json.encode(k));k[i]=tostring(k[i]);sceneAbort("(54) Wrong parameter "..i.." type ("..type(tmpNum[i])..") in '"..wF..json.encode(k):gsub("\""..k[i].."\"",rF..k[i]..eF)..eF.." Please change it to (string)",{tostring(json.encode(tmpNum):gsub("%[",""):gsub("%]",""))},jD[inId].lineNum) end end if (jCmd[cmdStr].resp:find("x") and not jCmd[cmdStr].f2:find(",,,")) and (jCmd[cmdStr].f2:find(",") or k[2]:find(",")) then _,tmpNum = string.gsub(jCmd[cmdStr].resp,"x",""); _,j1=string.gsub(k[2]:gsub("%b()",""),",","") --"\""..k[5].."\"" if tmpNum~=j1 then sceneAbort("(10) Wrong number ("..j1..") of parameters in '"..wF..k[2].."' action.
Required parameter(s) '"..wF..k[2]:match("%w+")..", ["..jCmd[cmdStr].resp:match("[=](.*)"):gsub("x",(cmdStr=="startScene" and " {arg1}{arg2}.."or " param")).. " ]'",{k[2]},jD[inId].lineNum) end end if not jCmd[cmdStr].resp:find("x") and k[2]:find(",") then sceneAbort("(11) Wrong number of parameters in '"..wF..k[2].."' action.
Command doesn't accept parameters.",{k[2]},jD[inId].lineNum) end if jCmd[cmdStr].f3:sub(1,1)=="!" and k[3]:len()>0 then sceneAbort("(12) Syntax error in "..cmdStr.." at '"..wF..k[3]..eF.."' no data allowed.
e.g. {\""..k[1].."\", \""..wF..cmdStr.."\", \""..k[3].."\""..eF.."}",{cmdStr,k[3]},jD[inId].lineNum) end if jCmd[cmdStr].f1:sub(1,1)=="!" and k[1]:len()>0 then sceneAbort("(13) Syntax error in "..cmdStr.." at '"..wF..k[1]..eF.."' no data allowed.
e.g. "..wF.."{\""..k[1].."\", \""..cmdStr..eF.."\", \""..k[3].."\"}",{cmdStr,k[1]},jD[inId].lineNum) end if jCmd[cmdStr].f3:sub(1,1)=="?" and not jCmd[cmdStr].f3:find(":"..k[3]:match("%w+")..":") then sceneAbort("(14) Wrong parameter '"..wF..k[3]..eF.."' in "..wF..k[2]..eF.." command.
Acceptable parameter(s):"..wF..jCmd[cmdStr].f3:sub(2,-1):gsub(":","
")..eF,{k[2]},jD[inId].lineNum) end if jCmd[cmdStr].f1:find("[#$<]") and k[1]:len()==0 then sceneAbort("(15) Action's 1st field in '"..wF..cmdStr..eF.."' is empty.
Field should include "..wF..f1frmt[jCmd[cmdStr].f1]..eF,{cmdStr},jD[inId].lineNum) end if jCmd[cmdStr].f1:find("[>#<]") then k[1]=k[1]:gsub(" ","") end if k[1]:find("[{|}]") then tmpStr=k[1]:gsub("[{}]",""):gsub("||","|"):gsub("|"," ") for i in tmpStr:gmatch("%S+") do i=spKeyword(i,inId) if (jCmd[cmdStr].f1:find("[<>]") and not i:find("[>:]")) or (jCmd[cmdStr].f1:find("[#]") and i:find(">")) then sceneAbort("(55) Device syntax error in "..cmdStr.." at '"..k[1]:gsub(i,wF..i..eF).."'
Syntax is "..wF..f1frmt[jCmd[cmdStr].f1]..eF,{k[1],cmdStr},jD[inId].lineNum) end end end if k[3]:gsub(" ",""):len()==0 and jCmd[cmdStr].f3:find("%$") then sceneAbort("(18) Action's 3rd field in '"..wF..cmdStr..eF.."' is empty.
Field should include "..wF..f1frmt[jCmd[cmdStr].f3]..eF,{cmdStr},jD[inId].lineNum) end end --===================================== checkFormat function cmdFormat(actData,inId,acTbl) local i,cmdData=0,{{""},{""}} if not json.encode(task.act):find(acTbl) then return end if not json.encode(actData):find(";else;") then return checkFormat(actData,inId,acTbl) end for i=1,#actData do cmdData[1][i]=(actData[i]:match("(.-);else;") or actData[i]);cmdData[2][i]=(actData[i]:match(";else;(.+)") or actData[i]) end checkFormat(cmdData[1],inId,acTbl);checkFormat(cmdData[2],inId,acTbl) end --===================================== cmdFormat function setOrder(k,inId,acTbl) if #k<2 or type(k)~="table" then sceneAbort("(94) Syntax error in "..wF..acTbl..eF.." table. Syntax is:"..wF.."
"..acTbl.."={\"devID\",\"command\",\"...\"}"..eF,{acTbl},jD[inId].lineNum) end if k[1]:find("[%s+]? and [%s+]?") then k[1]=k[1]:gsub("[%s+]? and [%s+]?","|") end local nK={k[1],k[2],"","na","na"} if #k==2 then k[3]="" else for i=3,#k do if type(k[i])=="number" then k[i]=tostring(k[i]) end if type(k[i])=="boolean" then nK[5]=k[i];if nK[4]=="na" then nK[4]=false end elseif k[i]:find("[{}]") and k[i]:find(":") then nK[4]=k[i] else nK[3]=k[i] end end for i=5,4,-1 do if nK[i]=="na" then table.remove(nK,i) end end for i=1,#nK do k[i]=nK[i] end end end --===================================== setOrder function sceneAbort(fMsg,str2find,jLine) local errIn,mStr="","" --99 str2find=json.decode(json.encode(str2find));subStr=json.decode(json.encode(str2find)) for i=1,#str2find do if #str2find[i]==0 then str2find[i]="!!!" end ;tmpStr="";str2find[i]=string.gsub(str2find[i]:gsub("[%?%(%)%+%-%*%[%]%$]",function (a) return "%"..a end),"\"","\"") if i==1 and type(jLine)=="number" then errIn=".jM hash table:"..jLine; errMsg=json.encode(jM[jLine]):gsub("\\/","/"):gsub("[%[%]]",function (a) return a:find("%[") and "{" or a:find("%]") and "}" end) end if not jLine and i==1 then for j=1, #jM do if json.encode(jM[j]):gsub("\\/","/"):find(str2find[i]) then tmpStr=jM2n[j];errIn=".jM hash table:"..j;jLine=j;errMsg=json.encode(jM[j]):gsub("\\/","/"):gsub("[%[%]]",function (a) return a:find("%[") and "{" or a:find("%]") and "}" end) break end end end if i==1 and type(jLine)=="table" then errIn=jLine[1];errMsg=jLine[2]:gsub("[%[%]]",function (a) return a:find("%[") and "{" or a:find("%]") and "}" end) end errMsg=errMsg:gsub(str2find[i],rF..subStr[i].."") end if type(jLine)=="number" and jM[jLine][2] and not jM2n[jLine]:find(jM[jLine][2])then tmpStr=jM2n[jLine] end mStr="[ "..rF.." ] exception: /opt/fibaro/scene/"..__fibaroSceneId..errIn..": "..tmpStr.."
"..errMsg:gsub("\":","\"=").."

"..fMsg:gsub("e%.g%.","e.g.").."" if initMode then print(mStr);fibaro:abort() elseif "fixedMenu"..mStr~=table.concat(runErrBuf," ") then errCnt=0 runErrBuf[1]="fixedMenu"..mStr;dispInfo() return false end end --============================================= sceneAbort function checkData(dParam,inId,jmLine,lineNum) local apiData,dParamFound,actFound,fStatus,i,j,i1,j1,dType={},false,false,true,0,0,0,0,(inId:find("_") and "scene" or inId:find("&") and "Variable" or inId:find("[$]") and "Keyword" or "device") if not inId:find("[$&]") then apiData=api.get("/"..dType.."s/"..realId(inId)) if not apiData then sceneAbort("(36) "..dType .." ID "..wF..jsId(inId)..eF.." not found in the system.",{realId(inId)},lineNum) end end if jDlist["mId"..realId(inId)] then sceneAbort("(37) Master device ID "..wF..jsId(inId)..eF.." prohibited to use.",{realId(inId)},lineNum) end checkType(dType,inId,apiData) if not dParam then return end for i=1,#task.rule do if dParam[task.rule[i]] and type(dParam[task.rule[i]])~=task.ruleType[i] then sceneAbort("(38) Incorrect type definition of "..wF..task.rule[i]..eF..". Found ("..type(dParam[task.rule[i]])..") instead of ("..task.ruleType[i]..")",{task.rule[i]},lineNum) break end end for j,_ in pairs(dParam) do fStatus=false if tonumber(j) then sceneAbort("(19) Unrecognized table array "..wF..j.."={"..json.encode(dParam[j]):gsub("[%]%[]","").."}"..eF.."
Please vefiry proper action tables format.",dParam[j],lineNum) end for i=1,#task.all do if j==task.all[i] then dParamFound=true;fStatus=true end end if not fStatus then sceneAbort("(20) Unrecognized action/criteria table \""..wF..j.."'
Accepted action/criteria tables:
"..wF..json.encode(task.all):gsub("\",\""," "):gsub("[%[%]\"]","")..eF,{j},lineNum) break end end for i1,j in pairs(dParam) do dataCol[i1]=true if json.encode(task.act):find(i1) then if type(j[1])=="table" then for _,k in pairs(j) do setOrder(k,inId,i1);cmdFormat(k,inId,i1);checkAct4(k,inId,i1) end else setOrder(j,inId,i1);cmdFormat(j,inId,i1);checkAct4(j,inId,i1) end end --if json.encode(criteria[2]):find(i1) then end -- for i1,j in pairs(dParam) do if inId:find("_") or not inId:find(">") then for i=1,#task.scene do if dParam[task.scene[i]] then sceneAbort("(21) Wrong action/criteria "..wF..task.scene[i]..eF.." found for "..dColor[jD[inId].idType]..jD[inId].idType..eF.." |"..dColor[jD[inId].idType]..findNameRoom(inId,2)..eF.."|
Accpted actions/criteria:
"..lgF.. table.concat(task.sceneAct," ")..eF,{task.scene[i],inId},lineNum) end end end if jD[inId].idType:find("Auto") then for i=1,#task.sceneAuto do if dParam[task.sceneAuto[i]] then sceneAbort("(59) Wrong action/criteria "..wF..task.scene[i]..eF.." found for "..dColor[jD[inId].idType]..jD[inId].idType..eF.." |"..dColor[jD[inId].idType]..findNameRoom(inId,2)..eF.."|
Accpted actions/criteria:
"..lgF.. table.concat(task.sceneAutoAct," ")..eF,{task.scene[i],inId},lineNum) end end end if dParam.property then if inId:sub(1,1):find("[$&]") then sceneAbort("(93) Property rule is not availabe for "..jD[inId].idType.." "..wF..findNameRoom(inId,2)..eF.."
Available options are:
1. "..wF.."state=\"value=...\""..eF.."
2."..wF.." Empty "..eF.."(do not define "..wF.."state property"..eF..")",{"property",inId},lineNum) end for i=1,#task.prop do if dParam[task.prop[i]] then sceneAbort("(22) While porperty criteria defined, wrong table array found for ".. jD[inId].idType.." |"..wF..findNameRoom(inId,2)..eF.."|
Accpted table arrays:
"..lgF.. table.concat(task.prop," ")..eF,{task.prop[i],inId},lineNum) end end if dParam.state then sceneAbort("(23) Criteria '"..wF.."state' and '"..wF.."property' are not allowed on the same line.",{"\"property\"","\"state\""},lineNum) end if api.get("/"..dType.."s/"..realId(inId))["properties"][dParam.property] == nil then sceneAbort("(24) Property '"..wF..dParam.property ..eF.."' is not available for ".. jD[inId].idType.."|"..wF..findNameRoom(realId(inId),2)..eF.."|
Available properties:"..wF..getIdData(inId,"properties")..eF,{dParam.property,inId},lineNum) end if inId:find("[$&]") then sceneAbort("(25) Criteria 'property' is unavailable for "..wF..realId(inId)..eF.." "..fibaro_getGlobalValue(realId(inId),"?").." variable.",{"property",realId(inId)},lineNum) end if not dParam.trigAct and jD[inId].tstRate==0 then sceneAbort("(74) While |"..wF..findNameRoom(inId,2)..eF.."| defined as activity ("..wF.. "timeSpan=0"..eF.."), action table "..wF.."trigAct{}"..eF.." is missing.
Please define it or change timeSpan.",{"property",realId(inId),"0,"},lineNum) end end if dParam.state then if not dParam.state:match("[=<>]") then sceneAbort("(26) Operator syntax error in \""..wF..dParam.state..eF.."\"
Accepted operators are:"..wF.."
= equal to
< less than
> greater than
<> not equal to
e.g. state=\"value<>"..dParam.state:match("%p+(.*)").."\"",{dParam.state},lineNum) end if inId:find("[$&]") and dParam.state:match("%w+")~="value" then sceneAbort("(27) Incorrect property defined in {state=\""..wF..dParam.state..eF.."\"} for "..wF..tableId(inId)..eF..(inId:find("[$]") and " keyword" or " variable")..".
Only "..wF.."value"..eF.." property is accepted.
"..wF.."e.g. state=\"value"..dParam.state:match("[=<>].*").."\"",{"state\"=\"",dParam.state:match("(%w+)[=<>]"),tableId(inId)},lineNum) end -- if inId:find("[$&]") and not dParam.state:find("value") then sceneAbort("(27) Incorrect property in {state=\""..dParam.state:gsub("(%w+)=", function (a) return wF..a..eF.."=" end).."\"} for '"..wF..realId(inId)..eF.."' ".. -- fibaro_getGlobalValue(realId(inId),"?").." variable.
For "..fibaro_getGlobalValue(realId(inId),"?").." variables, property "..wF.."value"..eF.." only is accepted.
"..wF.."e.g. state=\"value"..dParam.state:match("=.*").."\"",{"state\"=\"",dParam.state:match("(%w+)="),realId(inId)},lineNum) end if not inId:find("[$&]") and api.get("/"..dType.."s/"..realId(inId))["properties"][dParam.state:match("(%S-)[=<>]")] == nil and dParam.state:sub(1,1)~="+" then sceneAbort("(28) State property \""..wF..dParam.state:match("(%S-)[=<>]").."\" is not available for ".. jD[inId].idType.." |"..wF..findNameRoom(realId(inId),2)..eF.."|
Available properties:"..wF..getIdData(inId,"properties")..eF,{dParam.state:match("(.*)[=<>]"),inId},lineNum) end if dParam.state:lower():find("modified") and not dParam.state:find("Modified") then sceneAbort("(29) Spelling error in \""..wF..dParam.state..eF.."\". Missing capital letter '"..gF.."M"..eF.."odified'
Syntax is "..wF.."\"device property"..gF.."Modified"..eF.."\"",{dParam.state},jD[inId].lineNum) end end if dParam.timeoutAct and dParam.timeLoopAct then sceneAbort("(31) Action tables "..wF.."timeoutAct{}"..eF.." and "..wF.."timeLoopAct{}"..eF.." are not allowed on same line.",{"timeoutAct","timeLoopAct"},lineNum) end -- if dParam.timeSlot then dParam.timeSlot=dParam.timeSlot:gsub(" ","");meanVsSlot(inId,dParam.timeSlot) end if dParam.timeSlot then meanVsSlot(inId,extractTimeSlot(dParam.timeSlot)) end if dParam.vacation and dParam.vacation~="stop" and dParam.vacation~="normal" then sceneAbort("(32) Unrecognized vacation value \""..wF..dParam.vacation..eF.."\"
Accepted values:
"..wF.."stop
normal",{dParam.vacation},lineNum) end for i=1,#task.state do if dParam[task.state[i]] then checkAct4(dParam[task.state[i]],inId,task.state[i]) end end end --================================== checkData function getIdData(devId,dataType) local i,pStr=0,"" if not jDid[realId(devId)] then return sceneAbort("(97) ID "..wF..jsId(devId)..eF.." not found in the system.",{realId(devId)},jD[inGid].lineNum) end for i,_ in pairs(api.get("/devices/"..realId(devId))[dataType]) do if not notProperty:find(i) then pStr=pStr.."
- "..i end end return pStr end --====================================== getIdData function fibaro_setGlobal(varName,value,ref) local fStatus,vName=false,varName:gsub("&","") for i=1,2 do vName=(i==1 and vName or ref:gsub("&","")) if jVar[vName] then fStatus=true if tostring(jVar[vName])~=tostring(value) then jVar[vName]=value;jVar[vName.."lTime"]=os_time() if global4local[1] then fibaro:setGlobal(global4local.gVarName,json.encode(jVar)) end end else tmpStr=fibaro:getGlobalValue(vName) if tmpStr then fStatus=true if tostring(tmpStr)~=tostring(value) then fibaro:setGlobal(vName,value) end end end if fStatus then break end end if not fStatus then sceneAbort("(33) Global variable "..wF..ref..eF.." or its value "..wF..vName..eF.." not found as global.",{"setGlobal",ref},jD[inGid].lineNum) end return fStatus end --====================================== fibaro_setGlobal function fibaro_getGlobalValue(vName,vType) vName=vName:gsub("&","");if vName:sub(1,1)=="%" then return "&"..vName end if not vType then vType="" end if vType=="?" then if jVar[vName] then return "Local" else return "Global" end end if jVar[vName] then return jVar[vName] end if fibaro:getGlobalValue(vName) then return fibaro:getGlobalValue(vName) end tmpStr="Scene global variables:
";tmpNum=api.get("/globalVariables") for i,j in pairs(global4local.varArray) do tmpStr=tmpStr..i.." " end tmpStr=tmpStr..eF.."
System Global variables:
" for i in json.encode(tmpNum):gmatch("name\":\"(.-)\"") do tmpStr=tmpStr..i.." " end sceneAbort("(39) Global Variable '&"..vName.."' not found. Available variables are:
"..tmpStr..eF,{vType:sub(1,1).."&"..vName..vType:sub(2,2)}) end --=========================== fibaro_getGlobalValue function to_number(value) return tostring(value):find("true") and 1 or tostring(value):find("false") and 0 or tostring(value):len()==0 and 0 or tonumber(value) == nil and nil or type(value) == "string" and tonumber(value) or type(value)=="number" and value end --============================== to_number function compare(o1,o,o2,o3) return o=="=" and o1==o2 or o==">" and o1>o2 or o=="<" and o1" and o1~=o2 or o=="==" and (tostring(o1):find(tostring(o2)) and true or tostring(o2):find("|"..o1.."|") and true) -- or o=="==" and (o1:find(o2) and true) or (o=="~" and o1>o2 and o1<(o3 or o2)) end function checkState(inId) if not jD[inId].property then return false end if inId:find("&") then jD[inId][jD[inId].property].value=fibaro_getGlobalValue(realId(inId)) end if inId:find("[$]") then jD[inId][jD[inId].property].value=setKeyword(tableId(inId)) end if jD[inId].state~=2 then return true end local val1,sOper,val2=tostring(doTheMath(jD[inId].stateValue,inId)),jD[inId].stateOper,"" if tostring(val1):find("~") then val1,val2=tostring(val1):match("(.*)~(.*)"); sOper="~" end if to_number(val1) and to_number(jD[inId][jD[inId].property].value) then return compare(to_number(jD[inId][jD[inId].property].value),sOper,to_number(val1),to_number(val2)) else return compare(jD[inId][jD[inId].property].value,sOper,val1,val2) end end --=============================== checkState function inProperty(inId) if jD[inId].state==1 or jD[inId].status==0 then return jD[inId].lTime elseif jD[inId].state==2 then -- state criteria defined if jD[inId].status==3 then return os_time() end -- not in timeslot if tmpStatus then jD[inId].status=1 return jD[inId].lTime else jD[inId].status=2 return (jD[inId].timeSpanOn and jD[inId].lTime or os_time()) end else return jD[inId].lTime end end --================================ inProperty function isOnTime(tRange,inId,iLog) local t,fStatus,cTime="",false,os.date("%H:%M",os_time()) for t in string.gmatch(tRange:gsub(","," "),"%S+") do --t=doTheMath(t,inId) if checkDate(t) then local sTime,eTime=tSlotFormat(t:match("(%w+:%w+)~")),tSlotFormat(t:match("~(%w+:%w+)")) if not sTime and not eTime then sTime=t:match("(%w+:%w+)") end if eTime and not sTime then if eTime==os.date("%H:%M",os_time()) then fStatus=t end elseif sTime and not eTime then if sTime==os.date("%H:%M",os_time()) then fStatus=t end elseif not sTime and not eTime then fStatus=t elseif (cTime>sTime and cTime=eTime and (cTimesTime)) then fStatus=t end end end if iLog then if fStatus and jD[inId].tstRate>=0 then return ygF..fStatus:gsub("(%w+:%w+)", function(a) return tSlotFormat(a,"disp") end)..eF else return tRange:gsub("(%w+:%w+)", function(a) return tSlotFormat(a,"disp") end) end else return fStatus end end --================================ isOnTime function checkDate(tSlot) local j,i,k,mStr,date,tDate,fStatus,sDate=0,0,0,"","",os.date("*t",os_time()),false,{{"[m]","[d]","[w]"},{"month","day","wday"}} if not tSlot:match("(%a)[!=]") or not tSlot:find("[mdw]") then return true else date=tSlot:match(".+[;]?"):gsub("=","=;")..";" end date=date:gsub("[mwd]",function(a) return " "..a end) for i,j in date:gmatch("(%d+)~(%d+)") do mStr="" for k=i,j do mStr=mStr..k..";" end date=date:gsub(i.."~"..j,mStr:sub(1,-2)) end for j=1,3 do if date:find(sDate[1][j]) then fStatus=false if date:match(sDate[1][j].."(%S+)"):find("!=") then if not date:match(sDate[1][j].."(%S+)"):find(";"..tDate[sDate[2][j]]..";") then fStatus=true end elseif date:match(sDate[1][j].."(%S+)"):find(";"..tDate[sDate[2][j]]..";") then fStatus=true end if not fStatus then return false end end end return true end --=================================== checkDate function extractTimeSlot(mTime) local rSlot=mTime:gsub(" ","") if not mTime:find("true") and not mTime:find("false") then return mTime else mTime="" end rSlot=rSlot:gsub("%b??", function (a) return a:gsub(",","!!") end) for i in string.gmatch(rSlot:gsub(","," "),"%S+") do if i:find("true") then i=i:gsub("true",""):gsub("~","").."~" end if i:find("false") then i="~"..i:gsub("false",""):gsub("~","") end mTime=mTime..i:gsub("!!",",").."," end return mTime:sub(1,-2) end --=================================== extractTimeSlot function meanVsSlot(inId,timeTable,disp) local slotStr,sTime,fStatus,i,j,realSlot,hTime,mTime,mathTime=",",{},true,0,0,"",0,0,"" if not timeTable then return true, "" end mathTime=doTheMath(timeTable,inId) if mathTime==math.huge then return sceneAbort("(65) Format/Syntax/Formula error in time slot '"..wF..i..eF.."'
See Advanced User's Guide for proper time slot format definition.",{timeTable,"timeSlot"},jD[inId].lineNum) end for i in string.gmatch(mathTime:gsub(","," "),"%S+") do realSlot=i if not realSlot:find("~") and not realSlot:match("(%a)[!=]") then return sceneAbort("(57) Syntax error in timeslot "..wF..timeTable.."
e.g. timeSlot=\"m=5;d=6;21:00~22:00, w=3;06:00~09:00, d!=1, m=3;4\"
",{timeTable,"timeSlot"},jD[inId].lineNum) end if realSlot:match("(%a)[!=]") and not realSlot:find("[mdw]") then return sceneAbort("(64) Syntax error in timeslot date format "..wF..timeTable.."
e.g. timeSlot=\"m=5;d=6;21:00~22:00, w=3;06:00~09:00, d!=1, m=3;4\"
",{timeTable,"timeSlot"},jD[inId].lineNum) end if disp then slotStr=slotStr..isOnTime(realSlot,inId,"log")..", ";goto tContinue end sTime[1]=tSlotFormat(realSlot:match("(%w+:%w+)[:~]"));sTime[2]=tSlotFormat(realSlot:match("~(%w+:%w+)")) if not sTime[1] and not sTime[2] then if realSlot:find("[mdw]") then goto tContinue else sceneAbort("(78) Syntax error in timeslot "..wF..realSlot.."
e.g. timeSlot=\"21:00~22:00, 06:00~, ~01:00\"",{timeTable,"timeSlot"},jD[inId].lineNum) end end if sTime[1] and not sTime[2] then goto tContinue end if sTime[2] and not sTime[1] then goto tContinue end if not sTime[1]:match("(%d+):(%d+)") or not sTime[2]:match("(%d+):(%d+)") then return sceneAbort("(66) Wrong format in timeslot "..wF..realSlot.."
e.g. timeSlot=\"21:00~22:00, 06:00~, ~01:00\"",{timeTable,"timeSlot"},jD[inId].lineNum) end for j=1,2 do hTime,mTime=sTime[j]:match("(%d+):(%d+)");sTime[j]=hTime*60+mTime end if sTime[2] "..sTime[2]-sTime[1]..eF..") in
\""..timeTable:gsub(i,wF..i..eF).."\"",{jD[inId].tstRate..",",i,"timeSlot"},jD[inId].lineNum) end ::tContinue:: end return disp and (slotStr:gsub("~[,<]",function (a) return a:gsub("~","true")end):gsub("[,>][%s+]?~", function (a) return a:gsub("~","false")end)) or true end --================================== meanVsSlot function tSlotFormat(tSlot,disp) if not tSlot then return (disp and "" or tSlot) end -- if disp then return "" else return tSlot end end tSlot=tSlot:gsub("(%w+):(%w+)",function (a,b) return (#a==1 and "0"..a or a)..":"..(#b==1 and "0"..b or b) end) local hTime,mTime=tSlot:match("(%d+):(%d+)") if disp and os.date("!%H:%M",hTime*3600+mTime*60)~=tSlot then return os.date("!%H:%M",hTime*3600+mTime*60) else return os.date("!%H:%M",hTime*3600+mTime*60) end end --================================== tSlotFormat function clockTime(timeInt) local _,onTime=math.modf((os.date("*t",os_time()).day*1440+os.date("*t",os_time()).hour*60+ os.date("*t",os_time()).min)/timeInt) if onTime==0 then return true else return false end end --=================================== clockTime function checkInterval(inId,lSeen) if jD[inId].onClock==0 then return lSeen end local _,onTime=math.modf((os.date("*t",os_time()).day*1440+os.date("*t",os_time()).hour*60+ os.date("*t",os_time()).min)/jD[inId].tstRate) jD[inId].lTime=os_time()-onTime*(jD[inId].tstRate*60) if onTime==0 then if jD[inId].onClock==1 then jD[inId].onClock=2 return onTime else jD[inId].maxTime=0 return os_time() end else if jD[inId].onClock==2 then jD[inId].onClock=1;jD[inId].maxTime=0 end return os_time() end end --================================ checkInterval function inTimeSlot(inId) local lCnt,sCnt,i,fStatus,cTime,realSlot,mathTime=0,0,0,true,os.date("%H:%M",os_time()),"","" if jD[inId].timeSlot then fStatus=false mathTime=jD[inId].timeSlot:gsub("$sunsetHour",intValue.sunsetHour):gsub("$sunriseHour",intValue.sunriseHour) mathTime=doTheMath(mathTime,inId) for i in string.gmatch(mathTime:gsub(","," "),"%S+") do realSlot=i;lCnt=lCnt+1 local sTime,eTime=tSlotFormat(realSlot:match("(%w+:%w+)~")),tSlotFormat(realSlot:match("~(%w+:%w+)")) if not sTime and eTime then setTimeActions(realSlot,eTime,"falseAct",1,inId);sCnt=sCnt+1 elseif sTime and not eTime then setTimeActions(realSlot,sTime,"trueAct",2,inId);sCnt=sCnt+1 elseif not sTime and not eTime then fStatus=checkDate(realSlot) elseif (cTime>sTime and cTime=eTime and (cTimesTime)) then fStatus=checkDate(realSlot) end --if checkDate(realSlot) then fStatus=true end end end if sCnt==lCnt then fStatus=true end end if not fStatus then jD[inId].status=3 return false end if jD[inId].status>2 then jD[inId].lTime=os_time(); jD[inId].status=to_number(tmpStatus)+1 if jD[inId].initOnStartup then if jD[inId].initAct then jAct[inId]={act=json.decode(json.encode(jD[inId].initAct)),actState="initAct",lTime=os_time(),type="Is"} else if jD[inId].state~=3 then jD[inId].status=(jD[inId].status % 2) + 1;setActions(inId,"s") end end end --if jD[inId].initOnStartup then end return fStatus end --=========================== setTimeActions ============================================ function setTimeActions(tDate,tAct,inAct,aMode,inId) if checkDate(tDate) and tAct==os.date("%H:%M",os_time()) then if not jD[inId][tAct] and jD[inId][inAct] then jAct[inId]={act=json.decode(json.encode(jD[inId][inAct])),actState=inAct,lTime=os_time(),type=inAct:upper():sub(1,1).."t"};jD[inId][tAct]=true end else jD[inId][tAct]=false end end --================================ setTimeActions function setActions(inId,aType) if initMode and jD[inId].state~=3 then return end aType=aType or "" if jD[inId].status==1 and not tmpStatus then if jD[inId].falseAct then jAct[inId]={act=json.decode(json.encode(jD[inId].falseAct)),actState="falseAct",lTime=os_time(),type="F"..aType} end elseif jD[inId].status==2 and tmpStatus and inTimeSlot(inId) then if jD[inId].trueAct then jAct[inId]={act=json.decode(json.encode(jD[inId].trueAct)),actState="trueAct",lTime=os_time(),type="T"..aType} end elseif jD[inId].trigAll and jD[inId].trigOn and jD[inId].status~=3 then if jD[inId][task.all[jD[inId].status]] then jAct[inId]={act=json.decode(json.encode(jD[inId][task.all[jD[inId].status]])),actState=task.all[jD[inId].status],lTime=os_time(),type="A"..aType} end end if jD[inId].lState~=tmpStatus then jD[inId].lTime=os_time();jD[inId].lState = tmpStatus end end --================================ setActions function onTime(inId) local fStatus,modNum,ttsMsg,noteMsg,i=true,"devices","","",0;gData.alertId={} for i,_ in pairs(jD) do if inId and i~=inId or os_time()-jD[i].lagTime<0 then goto nextOnTime end if jD[i].tstRate<0 then jD[i].status=4; goto nextOnTime end setTickTime(i,true);tmpStatus=checkState(i);setActions(i) if jD[i].timeoutAct and not jD[i].active and jD[i].tstRate>0 and os_time()-jD[i].lTimejD[i].tstRate*60 and jD[i].tstRate>0 then if jD[i].timeoutAct then if jD[i].active then jD[i].active=false;jAct[i]={act=json.decode(json.encode(jD[i].timeoutAct)),actState="timeoutAct",lTime=os_time(),type="O"} end elseif jD[i].timeLoopAct then jD[i].maxTime=0;jD[i].lTime=os_time();jAct[i]={act=json.decode(json.encode(jD[i].timeLoopAct)),actState="timeLoopAct",lTime=os_time(),type="L"} else if jD[i].status==1 or (jD[i].timeSpanOn and jD[i].status~=0) then jD[i].tAlerts=jD[i].tAlerts+1;jD[i].alertTime[#jD[i].alertTime+1]=os.date(" %d %b %H:%M:%S",os_time()) end jD[i].status=0; gData.numAlerts=gData.numAlerts+1;gData.alertId[#gData.alertId+1]=i;fStatus=false ttsMsg=ttsMsg..findNameRoom(i,1)..", " noteMsg=noteMsg..jD[i].idType..":"..jD[i].id..": "..findNameRoom(i,1).." ("..jD[i].userTerms..jD[i].stateValue..")\n" end elseif jD[i].status==0 then jD[i].status=1 end if jD[i].property then logVal(jD[i][jD[i].property].value,i,sColor[jD[i].status+1]) end jD[i].trigOn=false ::nextOnTime:: end -- for i,_ in pairs(jD) do if not inId then if not fStatus and (gData.prvAlerts~= #gData.alertId or os_time()-gData.lAlert > alertRate*60) then gData.lAlert=os_time() if #gData.alertId==1 then modNum="device" end homeTts(sceneName.." alert on ".. #gData.alertId .. " "..modNum ..". "..ttsMsg) sendNote(sceneName.." Alert ("..#gData.alertId..")","Time out on next "..modNum..":\n"..("-"):rep(25).."\n"..noteMsg) elseif gData.prvAlerts>0 and #gData.alertId==0 then homeTts(sceneName.." back to normal.");sendNote(sceneName.." OK","All devices back to normal activitiy.") end gData.prvAlerts=#gData.alertId end end --======================================== onTime function sendNote(subject,body,noteType,logInfo) if not inGid then inGid=1 end local i,cmd,runScene,idStr,nLine,fStatus,rStr,mStr=0,"",0,"","","","
"..indent..ctlChar(" subj: "..subject.." body: "..body.." ",txtLine),"";txtNote="" if not noteType or noteType=="sendNote" then noteType="sendNote";nLine="
"..indent;mStr="" fStatus=" "..noteType:match("%w+").." Error: All notification means are set to "..bF.."FALSE"..eF else mStr=rStr end cmd=noteType;noteType=noteType:gsub(",run[_]?%d+",function(a) runScene=tonumber(a:match("%d+")) return "" end) if eMail[1] and noteType=="sendNote" or noteType:match("%w+")=="sendEmail" then fStatus="";tmpStr=eMail[2] if noteType:find(",") then tmpStr=noteType:gsub("sendEmail","") end for i in string.gmatch(tmpStr:gsub(","," "),"%S+") do if noteList.sendEmail[i] then txtNote=txtNote..noteList.sendEmail[i]:match("(.+):")..",";idStr=idStr..", "..i..""..noteList.sendEmail[i].." " else sceneAbort("(58) eMail user ID '"..wF..i..eF.." has not been found in the system. Available users:
"..ygF.. table_concat(noteList.sendEmail," ",true)..eF,{cmd},jD[inGid].lineNum) end if not logInfo then fibaro:call(to_number(i),"sendEmail",subject,body) end end fStatus=indent.." sendEmail "..idStr..mStr if noteType~="sendNote" and runScene>0 then fibaro:startScene(runScene,{noteType}) end end if popupNote and noteType=="sendNote" or noteType:match("%w+")=="sendPopup" then fStatus=fStatus:gsub("%b{}","")..nLine.." sendPopup, All users "..mStr if not logInfo then HomeCenter.PopupService.publish({title=subject,contentBody=body..(runScene>0 and "\nstartScene("..runScene..")" or ""),type="Critical",buttons={{caption='OK',sceneId=runScene}} }) end end if pushNote[1] and noteType=="sendNote" or noteType:find("Push") then fStatus=fStatus:gsub("%b{}","");idStr="";tmpStr=pushNote[2] if noteType:find(",") then tmpStr=noteType:gsub("send%w+","") end; isAct.sendPush=true for i in string.gmatch(tmpStr:gsub(","," "),"%S+") do if noteList.sendPush[i] then txtNote=txtNote..noteList.sendPush[i]..",";idStr=idStr..", "..i..""..noteList.sendPush[i].." " else sceneAbort("(16) Mobile ID ("..wF..i..eF..") has not been found in the system.
List of mobiles (inDark Green"..eF.." - authorized):
"..wF.. table_concat(noteList.sendPush," ",true)..eF,{cmd},jD[inGid].lineNum) end if not logInfo then if noteType:find("sendiPush") then api.post('/mobile/push',{mobileDevices={tonumber(i)},message=body,title="iPUSH:"..subject,category='RUN_CANCEL',data={sceneId=runScene}}) else fibaro:call(tonumber(i), "sendPush",(#subject>2 and subject.."\n" or "")..body) end end end --[[end fot i]] if idStr:len()==0 then fStatus=fStatus..nLine.." "..cmd:match("%w+").." Error: mobile users not defined.";isAct.sendPush=false else fStatus=fStatus..nLine.." "..noteType:match("send%w+").." "..idStr..mStr..(runScene>0 and "
"..indent.." RUN scene "..runScene or "") end end if sms[1] and noteType=="sendNote" or noteType:match("%w+")=="sendSms" then tmpStr=sms[2];if noteType:find(",") then tmpStr=noteType:gsub("sendSms","") end fStatus=fStatus:gsub("%b{}","")..nLine.." sendSms"..tmpStr..mStr for i in string.gmatch(tmpStr:gsub(","," "),"%S+") do --fStatus=fStatus.." #"..i if not logInfo then sendSms(i,subject,body) end end if noteType~="sendNote" and runScene>0 then fibaro:startScene(runScene,{noteType}) end end if noteType=="sendNote" and isAct["sendNote"] then txtNote="";fStatus=" sendNote distribuiton list:
"..fStatus:gsub("mark>","mark2>")..rStr end if logInfo then return fStatus end end --=================================== sendNote function checkOnScreen(pattern) local dMsg=api.get("/scenes/"..__fibaroSceneId.."/debugMessages") if json.encode(dMsg):len()>110000 and not json.encode(dMsg):find(gData.tKeyWord) then api.delete("/scenes/" .. __fibaroSceneId .. "/debugMessages") return false end return json.encode(dMsg):find(pattern) and true or false end --================================== checkOnScreen function getButtonName(inId,bNum) local i,j;bNum=bNum:match("%d+") if not jDid[inId].button then jDid[inId].button={} for _,i in pairs(api.get("/devices/"..inId)["properties"]["rows"]) do if i.type=="button" then for _,j in pairs(i.elements) do jDid[inId].button[tostring(j.id)]=j.caption end end end end if jDid[inId].button[bNum] then return jDid[inId].button[bNum] else sceneAbort("(77) VD "..wF..findNameRoom(inId,2)..eF.." button number '"..wF..bNum..eF.."' is not found. Available buttons:
"..json.encode(jDid[inId].button):gsub("\",\"","
"):gsub("\":\"","-"):gsub("[{}\"]",""),{inId,"pressButton",","..bNum},jD[inGid].lineNum) end end --=============================== getButtonName function findNameRoom(dId,f) dId=tableId(dId) if not jDid[dId] then if initMode then return realId(dId) end sceneAbort("(82) Item ID "..wF..dId..eF.." not found in the system.",{dId},jD[inGid].lineNum) end local idF={jDid[dId].name.." @ "..jDid[dId].room, jDid[dId].name..":"..realId(dId)..":"..jDid[dId].room, "", jDid[dId].name..""..realId(dId)..""..jDid[dId].room, jDid[dId].room..":"..realId(dId)..":"..jDid[dId].name} return (dId:find("[&]") and dId..":"..jDid[dId].name or dId:find("[%$]") and dId or f and idF[f] or idF[4]) end --=============================== findNameRoom function dispInfo() gData.tableTimeStamp=os_time();refreshTime=os.time();--getIntValue() local i,j,k,k1,tipTxt,devAct,execCmd,notAct=0,0,0,0,"",{"trueAct","falseAct","trigAct","timeoutAct","timeLoopAct","initAct","errAct","okAct"},"","" local inRoom,rowStr,mStr,zBuffer,deadColor,dArm="","", "",{},{[true]=rF,[false]=ygF},{" ","🚨"} local tHeader={"Type","Item Description","Value","Criteria","in State",rF..""..gData.prvAlerts.."
","TimeSpan","Time Slot","trueAct","falseAct","trigAct","timeoutAct","timeLoopAct","initAct","errAct","okAct","Active Rules","jM{#}","jM { hash table line }"} local inBuffer,dHeader,rptHeader,gHeader,rmdHeader,zHeader,gpsHeader="",{" Name :ID: Room ","nAck","Dead"," Is dead "," Events history "},{"Master :ID: Room"," # "," Events history "},{" Type "," Name "," Value "," Modified "},{" Rate/Left(sec) "," Run Terms "," Note "," Reminder "},{"Master :ID: Room","#","%"},{"Name","ID","Radius"} zBuffer[1]=htmlStr;tmpStr=ygF.." ".. math.floor((os_time()-gData.startTime)/86400) .."d,"..os.date("!%H:%M:%S ",os_time()-gData.startTime).."" if gData.zTotal>1 then for i,j in pairs(eCmd) do k=k+j if i=="call" then execCmd=execCmd.. string.format(" %-18s",eCmdType[i])..j.."/"..eCmd.dev.."
" elseif i=="dev" then k=k elseif eCmdType[i] then execCmd=execCmd.. string.format(" %-18s",eCmdType[i])..j.. "
" else execCmd=execCmd.. string.format(" %-18s",i.." control")..j.."
" end end; execCmd=bF.." "..k.." activities recorded

"..execCmd mStr=tostring(zw.min):gsub(".+",function (a) if a~= "-1" then return string.format(" %.1fsMin",tonumber(a)) else return "" end end)..string.format(" %.1fsAvg",(os_time()-gData.startTime)/gData.zTotal)..tostring(zw.max):gsub(".+",function (a) if a~= "100" then return string.format(" %.1fsMax",tonumber(a)) else return "" end end) tmpStr=tmpStr.." "..cF..""..gData.zTotal.." zEvents