function os_time(cTime) if cTime then return cTime+tonumber(timeDrift) else return os.time()+tonumber(timeDrift) end end -- global definitions qgF fc="";fbF=fc.."firebrick>"sbF=fc.."sandybrown>";lpF=fc.."LIGHTCORAL>";lgF=fc.."#8ab92d>";dkF=fc.."darkgrey>";gF=fc.."Chartreuse>"; yF=fc.."gold>";ygF=fc.."yellowgreen>";cF=fc.."cadetblue>";dbF=fc.."darkblue>";bF=fc.."blue>";wF=fc.."peru>";whF=fc.."white>";rF=fc.."OrangeRed>";irF=fc.."indianred>";vF=fc.."cadetblue>";qrF=fc.."#ff6666>";qgF=fc.."grey>";grF=fc.."LightGray>";lbF=fc.."lightblue>";lsbF=fc.."LightSkyBlue>";orF=fc.."OrangeRed>";lyF=fc.."LightYellow>"; bgF=fc.."#354052>";oF=fc.."orange>"; eF="";eFb=eF.."
"; errMsg="";aoqOnline=true;keyinId="";gpsStatus=false;hc3Platform=" HC3 YH HC3L "; dataCol={};iMSg="";jMeSum=0 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;refreshTimeout=40000;lastId=1;refInt={};refTime={};onTimeLoop=0;onTimeMax=0;logCnt=5;logStatus="" jRun={}jTime={}jD={}jDp={}jGlbl={}jLog={}jMe={}jGps={}jF={}jT={}jI={}jAct={}jTop={}jTabAct={}jAlarm={}jErr={}jRpt={}jEvent={}jCevent={}jBack={}jMem={}jVar={}jGvar={}jN2s={}jDid={}jPrf={}jM2n={}jDlist={numDev=0,phDev=0,batDev=0,vDev=0,vdId="",qaDev=0,qaId="",niceDev=0,niceId="",zigbee=0,plugIn=0,pinId="",scenes=0};dColor={};dly="#" if dbgInfo==nil then dbgInfo=true end;if dbgTrueState==nil then dbgTrueState=true end;if dbgFalseState==nil then dbgFalseState=true end if dbgCmdTrue==nil then dbgCmdTrue=true end if dbgInitState==nil then dbgInitState=true end;if dbgCmdFalse==nil then dbgCmdFalse=true end;if shortFmt==nil then shortFmt=true end if not dbgInfo then dbgTrueState,dbgFalseState,dbgInitState,dbgCmdFalse,dbgCmdTrue=false,false,false,false,false end jDbg={["dbgTrueState"]=dbgTrueState,["dbgFalseState"]=dbgFalseState,["dbgInitState"]=dbgInitState,["dbgCmdFalse"]=dbgCmdFalse,["dbgCmdTrue"]=dbgCmdTrue} if slaveDeadRpt == nil then slaveDeadRpt=true end debugHttp= (debugHttp or {["GET"]=false,["POST"]=false,["PUT"]=false}) logActState=false initMode=true;trueFalseColor={[true]=ygF,[false]=lpF,["true"]=ygF,["false"]=lpF,["fail"]=rF,["normal"]=ygF,["stop"]=lpF,["1"]=ygF,["2"]=ygF,["0"]=lpF,};stColor={[false]="",[true]=""} jGvarName="
";powerOutage=false;atHome=true;vacMode={[false]="Vacation",[true]="Home"};b2n={[true]=1,[false]=0,[0]=false,[1]=true}; intValue={internet="online",hc3online="true",lan="false",wifi="false",newSw="false",newBeta="false",skin="",leds={}} dispGuide=os.time();zmStr="";lastEmitEvent="NA";runErrBuf={};log_trueFalse=false;eCmd={dev=0};sunUse="";errCnt=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()};aosAct=irF.."AOQ Supported Actions:

- ";sysStatus="";indent="";time2disp=300;refreshTime=os.time();lastDebugMsg="";actList="?:trueAct:falseAct:trigAct:timeoutAct:timeLoopAct:initAct:errAct:okAct:|" math.randomseed(os.time()); slaveApiTime=slaveApiTime or 600 demoId="xxx";--demo=true prtFunc={["debug"]={fibaro.debug,""},["trace"]={fibaro.trace,""},["error"]={fibaro.error,""},["warning"]={fibaro.warning,""}} errorResp= {value="networkError",modified=os.time(),maxRunningInstances=3,autostart=false,runningInstances=0, enabled=false,runConfig="DISABLED",type="networkError", {timestamp=os.time(),event={data={keyId="",keyAttribute="networkError"}}}, properties={}} jCmd={ ["poll"]="n/a|+|#|call|~|",["setConfiguration"]="n/a=x,x,x|+|#|call,|~|",["verify"]="n/a|+|!|int|~|",["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|~|",["armAlarm"]="n/a|+|$|alarm|~|",["disarmAlarm"]="n/a|+|$|alarm|~|",["pressButton"]="n/a=x|+|#|qad,|~|",["emitEvent"]="n/a|+|$|qad,|~|",["setSlider"]="n/a=x,x|+|#|qad,|~|",["setGlobal"]="n/a|+|$|global|$|",["setSkin"]="n/a|-|&|sys,,,|?:dark:light:|",["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|$|", ["setKeyword"]="n/a=x|+|#|int,|~|",["setState"]="n/a|+|<|int|$|",["powerOutage"]="n/a|+|!|int|?:true:false:|",["sendEmail"]="n/a=x|-|$|notify,,,|$|",["sendPush"]="n/a=x|-|$|notify,,,|$|",["sendiPush"]="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,,,|"..actList,["rptAction"]="n/a=x|+|<|int,,,|"..actList,["setProfile"]="n/a|+|$|profile|~|",["debug"]="n/a|+|&|prt|$|",["trace"]="n/a|+|&|prt|$|",["error"]="n/a|+|&|prt|$|",["warning"]="n/a|+|&|prt|$|",["setIcon"]="n/a=x|+|#|misc,|~|"} sysKeyWord={ ["$HC3onLine"]=function() return intValue.hc3online end, ["$blank"]="blank",["$timeLoop"]="inLoop", ["$customEvent"]=function() return lastEmitEvent end, ["$profile"]=function() return getProfiles("active") end, ["$lineStatus"]=function() return (jD[keyinId] and jD[keyinId].status or "") end, ["$lineState"]=function() return (jD[keyinId] and jD[keyinId].state or "") end, ["$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, ["$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, ["$actDate"]=function() return os.date("%a %b %d %H:%M %Y",os_time()) end, ["$osDate"]=function() return os.date("%c") end, ["$timeSpan"]=function() return jD[inGid]and jD[keyinId].tstRate or "nil" end, ["$isFalse"]=0,["$isTrue"]=1, ["$value"]=function() return jD[inGid] and jD[inGid][jD[inGid].property].value or "nil" end, ["$param"]=function() return jD[inGid] and jD[inGid].property or "nil" end, ["$stateValue"]=function() return jD[inGid] and doTheMath(jD[inGid].stateValue,inGid) or "nil" 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, ["$geoAddress"]=function() return jGps[tostring(jGps.data.locationId)].address end, ["$geoRadius"]=function() return jGps[tostring(jGps.data.locationId)].radius end, ["$lan"]=function() return intValue.lan end, ["$wifi"]=function() return intValue.wifi end, ["$newSw"]=function() return intValue.newSw end, ["$newBeta"]=function() return intValue.newBeta end, ["$skin"]=function() return intValue.skin end, ["$homeAlarm"]=function() return gData.alarmState end, ["$initMode"]=function() return tostring(initMode) end, ["$oldValue"]=function() return jD[inGid] and jD[inGid].oldValue or "nil" end, } notZwave= " lastWorkingRouteResponseTimestamp useTemplate sunsetHour sunriseHour neighborListResponseTimestamp icon currentIcon " sceneProperty="runConfig autostart maxRunningInstances runningInstances runningManualInstances visible isLua isRunning mode" notProperty= " viewLayout uiCallbacks mainFunction typeTemplateInitialized firmwareUpdate mainLoop visible wakeUpTime parameters pollingTimeSec icon currentIcon rows pushNotificationID pushNotificationType emailNotificationID emailNotificationType smsNotificationID smsNotificationType updateVersion saveLogs deviceIcon logTemp log configured manufacturer zwaveInfo zwaveVersion parametersTemplate remoteGatewayId zwaveCompany productInfo model nodeId serialNumber endPointId deviceControlType categories batteryLowNotification apiVersion useEmbededView quickAppVariables RFProtectionState protectionState localProtectionSupport protectionExclusiveControl protectionExclusiveControlSupport protectionTimeout RFProtectionSupport protectionTimeoutSupport localProtectionState armTimeTimestamp armError alarmTimeTimestamp armConditions alarmLevel alarmType armConfig armDelay alarmDelay alarmExclude fibaroAlarm useTemplate unlockSequence lockKey sequences keyOptions pendingActions isLight " tblVal="centralSceneSupport" gVarType={"Local","Global","AOQApp","QApp","SlaveGL","SlaveQA"} 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","dbgInfo","dbgTrueState","dbgFalseState","dbgInitState","dbgCmdFalse","dbgCmdTrue"}, act={"falseAct","trueAct","timeoutAct","timeLoopAct","initAct","trigAct","errAct","okAct"}, 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"}, prop={"state","trueAct","falseAct","trueActState","falseActState"}, propAct={"timeSlot","vacation","trigAct","timeoutAct","timeLoopAct","initAct","errAct","okAct","lineState","initActState","trigActState","timeoutActState","timeLoopActState"}, rule={ "property","state","timeSlot","initOnStartup","vacation","onClock","timeSpanOn","trigAll","dbgInfo","dbgTrueState","dbgFalseState","dbgInitState","dbgCmdFalse","dbgCmdTrue"}, ruleType={"string" ,"string","string" , "boolean" , "string" ,"boolean", "boolean" ,"boolean", "boolean", "boolean", "boolean", "boolean", "boolean", "boolean"}, actRule={"initOnStartup","vacation","onClock","timeSpanOn","trigAll","dbgInfo","dbgTrueState","dbgFalseState","dbgInitState","dbgCmdFalse","dbgCmdTrue"}, state={"lineState","trueActState","falseActState","initActState","trigActState","timeoutActState","timeLoopActState"}} htmlStr= "" --================================================================ aoqVer=15.4;supVer=""..aoqVer..""; __TAG="AOQ"..plugin.mainDeviceId --..""..aoqVer.."" nice={} function QuickApp:apiPut(host,url,basic,param,iMsg) if jSlave[host].online==false then return end local x=os.clock() if debugHttp["PUT"] then tmpStr=json.encode(param) fibaro.trace("PUT","["..ygF..host..eF.."]:"..cF..url..eF,ygF..tmpStr:sub(1,80)..(tmpStr:len()>80 and "...." or "")..eF) end self.httpSlave:request(url, {options={data = json.encode(param), method="PUT",--timeout=100000, headers={["Content-Type"]="application/json;charset=UTF-8",["Accept"]="application/json", ["Authorization"]="Basic "..basic}}, success=function(resp) jSlave[host].tApiPut=math.ceil((os.clock()-x)*1000)/1000 if resp.status<200 or resp.status>300 then self:error("status=",resp.status,url,json.encode(param)) end end, error = function(message) if not (iMsg or ""):find(message) then self:error(rF.."PUT",url,json.encode(param),message..eF) end end }) end--==================================== QuickApp:apiPut function QuickApp:apiPost(host,url,basic,param,iMsg) if jSlave[host].online==false then return end local x=os.clock() if debugHttp["POST"] then tmpStr=json.encode(param) fibaro.trace("POST","["..ygF..host..eF.."]:"..cF..url..eF,ygF..tmpStr:sub(1,80)..(tmpStr:len()>80 and "...." or "")..eF) end self.httpSlave:request(url, {options={data=param, method="POST", headers={["Content-Type"]="application/json;charset=UTF-8",["Accept"]="application/json", ["Authorization"]="Basic "..basic}}, success=function(resp) jSlave[host].tApiPost=math.ceil((os.clock()-x)*1000)/1000 if resp.status<200 or resp.status>300 then self:error("status=",resp.status,url,param) end end, error = function(message) if not ("Operation canceled"..(iMsg or "")):find(message) then self:error(rF.."POST",url,param,message..eF) end end }) end--==================================== QuickApp:apiPost function api_get(inId,urlStr,noResp) local x1,x,host=os.time(),os.clock(),(inId:match("'(%w+)") or "local") local hostErr=(host=="local" and "Local gateway" or "Slave gateway "..host) if not jSlave[host].online and os.time()-jSlave[host].lSeen<45 then return errorResp end if debugHttp["GET"] then tmpStr=json.encode(param) fibaro.trace("GET","["..ygF..host..eF.."]:"..cF..url..eF,ygF..tmpStr:sub(1,80)..(tmpStr:len()>80 and "...." or "")..eF) end if inId:find("'") then fibaro.sleep(200) end local resp,st=api.get(getProxy(inId)..urlStr) if (os.time()-x1) > 5 then fibaro.warning(__TAG,""..host..": TimeResp over",os.time()-x1,"sec. to execute api_get(", urlDecode(urlStr),")"," < "..json.encode(resp)) end if resp==nil and noResp then resp="OK" end -- if resp~=nil and resp.reason=="Network error" then if resp==nil or resp.reason=="Network error" then if initMode then return errorResp end jSlave[host].lSeen=os.time() if jSlave[host].online==true then jSlave[host].online=false;--jSlave[host].lSeen=os.time() homeTts("Warning, unable to access "..hostErr) sendNote(sceneName.." : Access Alert \n","Unable to access "..hostErr,"sendNote") sceneAbort("(102) Unable to access "..ygF..hostErr..eF..". Please check network connection",{"http://"..jSlave[host].ip,sysId(inId)},{":"..rF.." Network Error "..eF,(" http://"..jSlave[host].ip.."/api"..urlDecode(urlStr))}) end return errorResp end if jSlave[host].online==false then fibaro.debug(__TAG,ygF..hostErr.." back online"..eF) homeTts(hostErr.." back online") sendNote(sceneName.." : Access report\n",hostErr.." back online","sendNote") end jSlave[host].online=true;jSlave[host].tApiGet=math.floor((os.clock()-x)*1000)/1000; if resp==nil and not initMode then return errorResp else return resp end end--================================ api_get function fibaroGet(inId,pName) local pData={} if not jDid[inId] then sceneAbort("(104) ID "..wF..jsId(inId)..eF.." not found in the system.",{realId(inId)},jD[inGid].lineNum) return "null", os.time()+3600 end if pName=="alarm" and jAlarm[sysId(inId)] then return jAlarm[sysId(inId)],os.time() end if initMode then errorResp.properties[pName]="networkError" end pData=api_get(inId,"/devices/" .. sysId(inId) .. "/properties/" .. pName) if (pData == nil) then pData={}; pData.value="null";pData.modified=os.time()+3600 if initMode then sceneAbort("(52) In device ID "..wF..inId..eF..", property '"..wF..pName.."' not found."..(pName=="alarm" and " Verify device included in alarm zone(s)" or "").."
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) end end jDp[tableId(inId)][pName].value=tostring(pData.value); jDp[tableId(inId)][pName].mTime=pData.modified return tostring(pData.value), pData.modified end--================================ fibaroGet function getProxy(inId) if not inId:match("%w+'%w+") then return "/proxy?url=http://127.0.0.1:11111/api" end if initMode and not jSlave[inId:match("'(%w+)")] then tmpStr="
Available slaves:
" for i,_ in pairs(jSlave) do tmpStr=tmpStr..(i~="master" and i.."
" or "") end sceneAbort("(96) Slave controller "..wF..inId:match("'(%w+)").." for ID "..inId..eF.." not found."..tmpStr,{inId},jD[inId] and jD[inId].lineNum or jD[inGid].lineNum ) end return jSlave[inId:match("'(%w+)")].prfx end--================================ getProxy function getAuthorization(data) local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' return ((data:gsub('.', function(x) local r,b='',x:byte() for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end return r end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) if (#x < 6) then return '' end local c=0;for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end return b:sub(c+1,c+1) end)..({ '', '==', '=' })[#data%3+1]) end--================================ getAuthorization function roomById(rooms,inId) if inId==0 then return "Unassigned" end local i for _,i in pairs(rooms) do if i.id==inId then return i.name end end return "Not found" end--================================ roomById 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 urlDecode(url) local unescape=url:gsub("%%(%x%x)",function(a) return string.char(tonumber(a, 16)) end) return unescape end--==================================== urlDecode function stateLoop() local i,j for i,j in pairs(jSlave) do selfi:refreshState(i) end end--==================================== stateLoop function QuickApp:refreshState(i) self.http:request('http://'..jSlave[i].ip..'/api/refreshStates?last=' .. jSlave[i].last.."&logs=false", {options={method = 'GET', headers={["Content-Type"]="application/json;charset=UTF-8",["Accept"]="application/json", ["Authorization"]="Basic "..jSlave[i].access}}, success = function(response) st,resp = pcall(json.decode,response.data) if st then if jSlave[i].last~=resp.last then self:lastState(resp,jSlave[i].sfx) end jSlave[i].last=resp.last else fibaro.error(__TAG,rF.."Corrupted refresh state, please consider to reboot ["..i.."]"..eF.." => "..response:sub(1,(response:len()> 50 and 50 or response:len())).."...") end if aoqOnline then setTimeout(function() selfi:refreshState(i) end,math.random(250,600)) end end, error = function(error) if aoqOnline then setTimeout(function() selfi:refreshState(i) end,math.random(250,600)) end end }) end--====================================== QuickApp:refreshState function getDemo(rooms) nice[1]=json.decode('{"id":7,"name":"Nice Engine","roomID":0,"view":[],"type":"com.fibaro.niceEngine","baseType":"","enabled":true,"visible":false,"isPlugin":false,"parentId":0,"viewXml":false,"configXml":false,"interfaces":[],"properties":{"icon":{}},"actions":{},"created":1600805410,"modified":1600805410,"sortOrder":4}') nice[2]=json.decode('{"id":468,"name":"Nice Gate","roomID":448,"view":[{"assetsPath":"/dynamic-plugins/com.fibaro.baseShutter/assets","jsPath":"/dynamic-plugins/com.fibaro.baseShutter","name":"com.fibaro.baseShutter","translatesPath":"/dynamic-plugins/com.fibaro.baseShutter/i18n","type":"ts"}],"type":"com.fibaro.baseShutter","baseType":"com.fibaro.actor","enabled":true,"visible":true,"isPlugin":false,"parentId":7,"viewXml":false,"configXml":false,"interfaces":["nice","niceMono","stepByStep"],"properties":{"buttonHold":5000,"categories":["gates"],"dead":false,"deadReason":"","deviceControlType":65,"deviceIcon":201,"emailNotificationID":0,"emailNotificationType":0,"inputToChannelMap":{"close":[4],"open":[3],"partialOpen1":[],"step":[1],"stop":[]},"log":"","logTemp":"","manufacturer":"","model":"","niceId":1,"niceProtocol":"Opera0","numberOfSupportedButtons":8,"pushNotificationID":0,"pushNotificationType":0,"saveLogs":true,"smsNotificationID":0,"smsNotificationType":0,"state":"Unknown","userDescription":""},"actions":{"close":0,"open":0,"step":0,"stop":0},"created":1600805417,"modified":1600805417,"sortOrder":74}') apiDemo=nice[2] for i,j in pairs(nice) do j.id=tostring(j.id);jDid[j.id]={} jDid[j.id].name=j.name; jDid[j.id].room="demo" --roomById(rooms,j.roomID) jDlist["cmd"..j.id]=tableSortByKey(j.actions,"primeSort") if j.parentId and j.properties.nodeId and j.id:match("%d+")~="1" then jDlist[j.id.."zwave"]="yes" if not j.remoteGatewayId then j.remoteGatewayId=0 end -- for HC2 slave controllers if j.parentId>1 or j.remoteGatewayId>0 then jDlist["numDev"]=jDlist["numDev"]+1; jDlist[j.id]=j.properties.nodeId;jDlist["nId"..jDlist[j.id]]=os_time() else jDlist["pId"..j.properties.nodeId]=j.id;jDlist["mId"..j.id]=true;jDlist["phDev"]=jDlist["phDev"]+1 if j.properties.batteryLevel then jDlist["batDev"]=jDlist["batDev"]+1 end end end if json.encode(j.interfaces):find("quickApp") then -- if tonumber(j.id)~=plugin.mainDeviceId then getQaVar(j.id,"loadAllVar") end jDlist[j.id]="qa";jDlist["qaDev"]=jDlist["qaDev"]+1;jDlist.qaId=jDlist.qaId..","..j.id.."," elseif json.encode(j.interfaces):lower():find("nice") then jDlist[j.id]="nice";jDlist["niceDev"]=jDlist["niceDev"]+1;jDlist.niceId=jDlist.niceId..","..j.id.."," elseif j.type=="virtual_device" then jDlist[j.id]="vd";jDlist["vDev"]=jDlist["vDev"]+1 jDlist.vdId=jDlist.vdId..","..j.id.."," elseif j.isPlugin then jDlist[j.id]="plugIn";jDlist["plugIn"]=jDlist["plugIn"]+1 jDlist.pinId=jDlist.pinId..","..j.id.."," end end --for i,j in pairs(api.get("/devices/")) end function loadSceneProperty(pData,inId) local i,j;jDp[inId]={} for i,j in pairs(pData) do if sceneProperty:find(i) then jDp[inId]["runningInstances"]={value=0,lTime=os_time()} if i=="isRunning" and j then jDp[inId].runningInstances={value=1,lTime=os_time()} end jDp[inId][i]={value=(type(j)=="table" and "TBR" or j),lTime=os_time()} end end jDp[inId]["srcId"]={value=0,lTime=os_time()} jDp[inId]["srcType"]={value="system",lTime=os_time()} jDp[inId]["srcName"]={value="system",lTime=os_time()} jDp[inId]["actName"]={value="na",lTime=os_time()} end--====================================== loadSceneProperty function loadProperty(pData,inId) local i,j;jDp[inId]={["alarm"]={value=(jAlarm[inId] and jAlarm[inId] or "na") ,lTime=os.time()}} for i,j in pairs(pData) do if not notProperty:find(" "..i.." ") then jDp[inId][i]={value=(type(j)=="table" and "TBR" or tostring(j)),lTime=os_time()} if i=="state" or i=="status" then i=i:gsub("st","St") jDp[inId][i]={value=(type(j)=="table" and "TBR" or tostring(j)),lTime=os_time()} end if i=="lastBreached" then jDp[inId][i].mTime=j;jDp[inId][i].value=tostring(math.floor(os_time()-j)) end if i=="centralSceneSupport" then jDp[inId][i].value,jDp[inId][i].mTime=getHistory(inId) end end end jDp[inId]["srcId"]={value=0,lTime=os_time()} jDp[inId]["srcType"]={value="system",lTime=os_time()} jDp[inId]["srcName"]={value="system",lTime=os_time()} jDp[inId]["actName"]={value="na",lTime=os_time()} end--====================================== loadProperty function setStateFmt(stateStr) if not stateStr.state then return end if stateStr.state:sub(1,1):match("[=<>]") then stateStr.state="value"..stateStr.state elseif stateStr.state:sub(1,1):match("[@%?]") then stateStr.state="value="..stateStr.state elseif not stateStr.state:match("[=<>!][>=]?") then stateStr.state="value="..stateStr.state end end --====================================== setStateFmt function initTable() cnnLine=math.min(60,cnnLine);dataRecord.values=math.min(dataRecord.values,20);dataRecord.commands=math.min(dataRecord.commands,20);isAct={sendPush=true,sendNote=b2n[math.max(b2n[eMail[1]],b2n[popupNote],b2n[pushNote[1]],b2n[sms[1]])]};noteList={sendPush={},sendEmail={}};xTime={min=100,avg=0,max=0};top={active=math.min((topActive or 10),30),zwave=math.min((topZwave or 10),30)};hasSlaves=false;sceneName=selfi.name gData={lastLog=false,inIf="",noAct=" ▣"..eF,ifStart="",alarmState="disarmed",httpGet=nil,alarm="disarmed",idList=bF.." jM hash table inventory
",logAct={},logActNum=10,transfer="nack",zTotal=0,prvAlerts=0,lAlert=os_time(),tKeyWord="system status",tableTime=tblRefresh or 180,tableTimeStamp=os_time(),startTime=os_time(),totalDeads=0,wakeUpCall=0,numDeads=0,deadLog=8,rstDeadRpt=os_time(),numAlerts=0,alertId={}}; initMode=true;jQaGv="";lpF=fc..(falseStatus or "LIGHTCORAL")..">";ygF=fc..(trueStatus or "yellowgreen")..">"; yF=fc..(outOfTimeSlot or "gold")..">";sColor={rF,ygF,lpF,yF,dkF,lbF}; trueFalseColor={[true]=ygF,[false]=lpF,["true"]=ygF,["false"]=lpF,["fail"]=rF,["normal"]=ygF,["stop"]=lpF,["1"]=ygF,["2"]=ygF,["0"]=lpF,} tableOrder={ {"Z.Device","Zigbee","Plugin","QuickApp", "Nice","Variable", "Keyword","luaScene","blockScene","Auto-luaScene","Auto-blockScene"}, { Z_Device or "peru", Zigbee or "indianred",Plug_in or "#eaba61",QA_Dev or "DARKSEAGREEN",nice_Device or "ROSYBROWN",G_Variable or "PLUM", Key_Word or "LIGHTSTEELBLUE", lua_Scene or "lightblue",block_Scene or "cadetblue", Auto_luaScene or "DarkCyan", Auto_blockScene or "CornflowerBlue"}, {"Z.Dev", "Zigbee","Plugin", "Q.App", "Nice", "S.Var", "K.Wrd", "Lua", "Block", "A.Lua", "A.Block"},{} } if selfi.properties.model ~= "AOQ" then selfi:updateProperty("model","AOQ") end if selfi.properties.manufacturer ~= "cag014" then selfi:updateProperty("manufacturer","cag014") end if selfi.properties.mainFunction~="V"..aoqVer then selfi:updateProperty("mainFunction","V"..aoqVer) end printLabel("progressLabel",ygF.."Gathering information..."..json.encode(jM):len().."
") local jmStr,cTime,dType,devNum,delSpace,i,j,jv,k,numAct="",os.date("%H:%M",os_time()),"devices",0,{"property","state","timeSlot"},0,0,"",0,0 getProfiles();getAlarms();jGps.user={};jGps.data={};jGps.ios={} if not gpsDevice then gpsDevice={} end for _,j in ipairs(fibaro.getDevicesID({roomID=0,type="iOS_device"})) do tmpStr=rF; if tostring(api.get("/devices/"..j)["properties"]["Push"])=="true" then jv=jv..j..","; tmpStr="" end noteList.sendPush[tostring(j)]=tmpStr..fibaro.getName(j)..eF end if not pushNote[2] or not pushNote[2]:match("%d+") then pushNote[2]=","..jv end for _,j in ipairs(api.get("/users/")) do noteList.sendEmail[tostring(j.id)]=j.name..":"..j.email:gsub(j.name,""); jGps.user[tostring(j.id)]=j.name:match("%w+") end if not eMail[2] or eMail[2]:len()==0 then eMail[2]="2" end if not jSlave then jSlave={} else hasSlaves=true end for h,p in pairs(jSlave) do tmpStr=nil if not p.user or not p.passwd or not p.ip then tmpStr="parameter "..sbF..(p.user and "" or "user ")..(p.passwd and "" or "passwd ").. (p.ip and "" or "ip")..eF.." not found! " elseif type(p.user)~="string" or type(p.passwd)~="string" or type(p.ip)~="string" then tmpStr=" type error in "..sbF..(type(p.user)~="string" and "user " or "")..(type(p.passwd)~="string" and "passwd " or "").. (type(p.ip)~="string" and "ip " or "")..eF.." parameter. (string required)" end if tmpStr then sceneAbort("(94) In "..h.." Slave controller "..tmpStr.."
Format is:
"..sbF..h.."={user=\"xxx\",passwd=\"xxx\",ip=\"192.168.1.1\"}"..eF,{""},{":"..sbF.." jSlave{} table data error"..eF,h.."="..cF..json.encode(jSlave[h]):gsub("\"user\":",sbF.."user="..eF):gsub("\"passwd\":",sbF.."passwd="..eF):gsub("\"ip\":",sbF.."ip="..eF)..eF}) end jSlave[h].prfx="/proxy?url=http://"..urlEncode(p.user)..":"..urlEncode(p.passwd).."@"..p.ip.."/api" jSlave[h].sfx="'"..h;jSlave[h].last=1;jSlave[h].access=getAuthorization(jSlave[h].user..":"..jSlave[h].passwd) jSlave[h].act=p.act or {} jSlave[h].alarmPin=jSlave[h].alarmPin and tostring(jSlave[h].alarmPin) or "1111" end if not jSlave["home"] then jSlave["home"]={user="xxx",passwd=""} end jSlave["local"]={alarmPin="1111", user=jSlave["home"].user,passwd=jSlave["home"].passwd,slaveApiTime=600,online=true,tApiGet=0,lSeen=os.time(),tApiPost=0,tApiPut=0,prfx="",sfx="",act={},access=getAuthorization(jSlave["home"].user..":"..jSlave["home"].passwd),paltform="HC3",last=1,ip="127.0.0.1:11111",lanIp=jSlave["home"].ip} jSlave["home"]=nil for h,p in pairs(jSlave) do tmpStr=api.get(p.prfx.."/settings/info");rooms=api.get(p.prfx.."/rooms") if not tmpStr or tmpStr.reason or not rooms or rooms.reason then sceneAbort("(97) Failed to access "..sbF..h.."=( http://"..p.user..":"..p.passwd.."@"..p.ip.." )"..eF.."
Please verify "..h.." gateway is online, credentials, IP and access rights",{""},{":"..sbF.." jSlave{} table data error"..eF,h.."={ ".."user=\""..rF..p.user..eF.."\" passwd=\""..rF..p.passwd..eF.."\" ip=\""..rF..p.ip..eF.."\" }"}) end jSlave[h].name=tmpStr.hcName; jSlave[h].serial=tmpStr.serialNumber; jSlave[h].online=true jSlave[h].users={} jSlave[h].version=tmpStr.currentVersion.version jSlave[h].newSw=tmpStr.newestStableVersion jSlave[h].platform=tmpStr.serialNumber:match("%w+") jSlave[h].lSeen=os.time() jSlave[h].icons={device={},scene={}} jSlave[h].hc3platform=false if hc3Platform:find(" "..tmpStr.serialNumber:match("%w+").." ") then jSlave[h].hc3platform=true end jSlave[h].tApiGet=0;jSlave[h].tApiPost=0;jSlave[h].tApiPut=0; jSlave[h].slaveApiTime=(jSlave[h].slaveApiTime or slaveApiTime or 600) printLabel("progressLabel",ygF..h..(h=="local" and " gateway" or " @"..jSlave[h].ip)..": "..jSlave[h].name.." "..jSlave[h].serial.."".. jSlave[h].version..eF.."..."..eF..(tmpStr.updateStableAvailable and lsbF.." New SW:"..jSlave[h].newSw..eF or "")..(tmpStr.updateBetaAvailable and lpF.." ( New Beta SW:"..tmpStr.newestBetaVersion.." )"..eF or "").." Zwave e"..(tmpStr.zwaveEngineVersion or "2.0").."v"..tmpStr.zwaveVersion..""..eF) if h=="local" then k=api.get("/proxy?url=http://127.0.0.1:11111/api/icons") else k=api.get(p.prfx.."/icons") end for i,j in pairs(k) do if ("device scene"):find(i) then for _,j1 in pairs(j) do jSlave[h].icons[i][(j1.iconName or j1.iconSetName):gsub("com.fibaro.","")]=tostring(j1.id) jSlave[h].icons[i][tostring(j1.id)]=(j1.iconName or j1.iconSetName):gsub("com.fibaro.","") end end end if h=="local" then k=api.get(p.prfx.."/panels/location") for i,j in pairs(k) do jGps[tostring(j.id)]=j if j.home==true then jGps.data={timestamp=os.time(),geofenceAction="NA",userId=2,deviceId=999,locationId=j.id} end end end jSlave[h].users["0"]="system" for _,j in ipairs(api.get(p.prfx.."/users/")) do jSlave[h].users[tostring(j.id)]=j.name:match("[%w_]+") end k=api.get(p.prfx.."/devices/") for i,j in pairs(k) do j.id=tostring(j.id)..p.sfx;jDid[j.id]={};jDid[j.id].pRef={armed=""} if j.type=="iOS_device" and h=="local" then jGps.ios[tostring(j.id)]=j.name end jDid[j.id].name=j.name; jDid[j.id].room=roomById(rooms,j.roomID);jDid[j.id].idType="Z.Device" loadProperty(j.properties,j.id,p.sfx) jDlist["cmd"..j.id]=tableSortByKey(j.actions,"primeSort") if j.properties.ieeeAddress and j.properties.networkAddress then j.properties.nodeId=j.properties.networkAddress if j.parentId ==8 then j.parentId=1 end end if j.parentId and j.properties.nodeId and j.id:match("%d+")~="1" then jDlist[j.id.."zwave"]="yes" if not j.remoteGatewayId then j.remoteGatewayId=0 end -- for HC2 slave controllers if j.parentId>1 or j.remoteGatewayId>0 then if not j.properties.ieeeAddress then jDlist["numDev"]=jDlist["numDev"]+1 end jDlist["numDev"]=jDlist["numDev"]+1; jDlist[j.id]=j.properties.nodeId;jDlist["nId"..jDlist[j.id]]=os_time() else jDlist["pId"..j.properties.nodeId]=j.id;jDlist["mId"..j.id]=true;jDlist["phDev"]=jDlist["phDev"]+1 if j.properties.batteryLevel then jDlist["batDev"]=jDlist["batDev"]+1 end end end jDid[j.id].cfg=" by '"..(j.properties.zwaveCompany and #j.properties.zwaveCompany>1 and j.properties.zwaveCompany or "Unknown")..(j.properties.zwaveVersion and "\" ( zVer="..j.properties.zwaveVersion.." )" or "'") if json.encode(j.interfaces):find("quickApp") then jDid[j.id].idType="QuickApp" jDid[j.id].cfg=" QuickAPP: "..j.type:gsub("com.fibaro.","") jDlist[j.id]="qa";jDlist["qaDev"]=jDlist["qaDev"]+1;jDlist.qaId=jDlist.qaId..","..j.id.."," elseif j.properties.ieeeAddress and j.parentId>1 then jDid[j.id].idType="Zigbee";jDlist.zigbee=jDlist.zigbee+1; elseif json.encode(j.interfaces):lower():find("nice") then jDid[j.id].idType="Nice" jDid[j.id].cfg=" by 'Nice': "..j.type:gsub("com.fibaro.","").." (protocol:"..j.properties.niceProtocol or "Unknown"..")" jDlist[j.id]="nice";jDlist["niceDev"]=jDlist["niceDev"]+1;jDlist.niceId=jDlist.niceId..","..j.id.."," elseif j.type=="virtual_device" then jDlist[j.id]="vd";jDlist["vDev"]=jDlist["vDev"]+1 jDlist.vdId=jDlist.vdId..","..j.id..",";jDid[j.id].idType="V.Device" elseif j.isPlugin then jDlist[j.id]="plugIn";jDlist["plugIn"]=jDlist["plugIn"]+1;jDid[j.id].idType="Plugin" jDid[j.id].cfg=" Plug-in: "..j.type:gsub("com.fibaro.","") jDlist.pinId=jDlist.pinId..","..j.id.."," end if j.properties.batteryLevel then jDid[j.id].pRef.batteryLevel=j.properties.batteryLevel end if j.properties.armed~=nil then jDid[j.id].pRef.armed=j.properties.armed end end --for i,j in pairs(api.get("/devices/")) if hc3Platform:find(" "..p.platform.." ") then jQaGv=jQaGv..cF.."["..h.."] "..p.serial.." QA variables
" getQaGlobals(k,p.sfx) end for i,j in pairs(api.get(p.prfx.."/scenes")) do j.id="_"..j.id..p.sfx; jDid[j.id]={};jDlist["scenes"]=jDlist["scenes"]+1;jDid[j.id].name=j.name; loadSceneProperty(j,j.id,p.sfx) if hc3Platform:find(" "..p.platform.." ") then jDid[j.id].idType=j.type:gsub("json","block").."Scene" else jDid[j.id].idType=j.type:gsub("com.fibaro.","") if j.autostart==true and j.runConfig=="TRIGGER_AND_MANUAL" then jDid[j.id].idType="Auto-"..jDid[j.id].idType end end jDid[j.id].room=(j.roomID and roomById(rooms,j.roomID) or "Scene") end jGvarName=jGvarName..cF.."["..h.."] "..p.serial..":System variables:
"..eF gCnt=0 for i,j in pairs(api.get(p.prfx.."/globalVariables")) do gCnt=gCnt+1 inId="&"..j.name..(h=="local" and "" or "'"..h) jGvarName=jGvarName..ygF..inId jDp[inId]={value={value=j.value:sub(1,200),mTime=os_time(),type=(p.hc3platform and gVarType[2] or gVarType[5])}} jDid[inId]={};jDid[inId].room="Variable";jDid[inId].name=jDp[inId].value.type if p.hc3platform and j.isEnum then jDid[inId].enumValues=j.enumValues jGvarName=jGvarName.." "..cF..json.encode(j.enumValues)..eF end jGvarName=jGvarName..eF..whF..", "..eF..(gCnt%10==0 and "
" or " ") jDlist[inId]="GV";jDlist["cmd"..inId]="setGlobal" end jGvarName=jGvarName.."

" end --for h,p in pairs(jSlave) jGvarName=jGvarName..ygF..jQaGv:gsub(",",whF..","..eF)..eF getIntValue() if demo then getDemo(rooms) end printLabel("progressLabel",ygF.."Initializing jS2n, jP2n, global and userKeyWord arrays...
") if not jM or type(jM[1])~="table" then sceneAbort("(40) "..rF.."Attempt to access jM hash table (a nil value)"..eF,{"not defined"},{".jM hash table","See error below"}) end if #userKeyWord[1]~= #userKeyWord[2] then sceneAbort("(41) Number of elements in array doesn't match.
Array 1 has '"..wF..#userKeyWord[1].." elements
'
"..json.encode(userKeyWord[1]).."
Array 2 has '"..wF..#userKeyWord[2].." elements
'
"..json.encode(userKeyWord[2]),{"not defined"},{".userKeyword table:","See error below"}) end for i=1,#userKeyWord[1] do if userKeyWord[1][i]:sub(1,1)~="$" then sceneAbort("(42) keyword '"..wF..userKeyWord[1][i]..eF.."' missing prefix sign'"..wF.."$
'.",{userKeyWord[1][i]},{".userKeyword table:",json.encode(userKeyWord[1])}) end end parseCmd();jmNum=tostring(#jM):len() for j=1,#jM do if type(jM[j][1])~="number" then table.insert(jM[j],1,0) end if jM[j][3] then for i=1,#task.state do if jM[j][3][task.state[i]]~=nil then if type(jM[j][3][task.state[i]])~="table" then jM[j][3][task.state[i]]={jM[j][3][task.state[i]]} end if type(jM[j][3][task.state[i]][1])~="string" then assertType("(54)",jM[j][3][task.state[i]][1],task.state[i],"string",j) end if not tostring(jM[j][3][task.state[i]][1]):find("%b{}") then sceneAbort("(55) Syntax error in "..wF..task.state[i]..eF.." statement: "..wF..tostring(jM[j][3][task.state[i]][1])..eF.."
Please review and fix.", {tostring(jM[j][3][task.state[i]][1]),task.state[i]},j) end end end end --if jM[j][3] then / for i=1,#task.state do if type(jM[j][2])~="string" then sceneAbort("(47) Syntax error in JM table line "..j.." (wrong number of elements)
"..wF.."{25, \"devID\",{state=\"value=true\", trueAct={\"|792|\",\"trunOn\",\"\"}}}"..eF,{"jv2"},j) end jM2n[j]="{"..irF..jM[j][1].."
, \""..jM[j][2].."\"...}";jM[j][2]=jM[j][2]:gsub("[%s+]? and [%s+]?","|") end --for j=1,#jM do jmStr=json.encode(jM) for i=1,#tableOrder[1] do dColor[tableOrder[1][i]]="";tableOrder[4][tableOrder[1][i]]=0 end if global4local[1] then if not fibaro.getGlobalVariable(global4local.gVarName) then sceneAbort("(43) Global Variable '"..wF..global4local.gVarName.."' not found in the system.",{global4local.gVarName,"gVarName"},{".User defined global4local",json.encode(global4local)}) end jVar=fibaro.getGlobalVariable(global4local.gVarName) if jVar:len()<20 then jVar={} else jVar=json.decode(jVar) end print(ygF.."Local variables will be saved in system's "..wF..global4local.gVarName..eF.." global variable ...") else printLabel("progressLabel",lpF.."Saving Local variables set to false and "..rF.."won't be saved"..eF.." for next run...",self); jVar=json.decode(json.encode(global4local.varArray)); end --if global4local[1] then jGvarName=jGvarName..cF.."AOQ:local variables:
"..eF;gCnt=0 for i,j in pairs(global4local.varArray) do inId="&"..i; gCnt=gCnt+1 jGvarName=jGvarName..ygF..inId..eF..", "..(gCnt%6==0 and "
" or "") if global4local[1] then jVar[i]=(tostring(j):match("init:(.*)") or jVar[i]) jVar[i]=jVar[i] or j;jVar[i.."lTime"]=jVar[i.."lTime"] or os_time() else jVar[i]=tostring(j):gsub("init:","");jVar[i.."lTime"]=os_time() end jDp[inId]={value={value=jVar[i],mTime=jVar[i.."lTime"],type="Local",isEnum=false,enumValues={}}} jDid[inId]={};jDid[inId].room="Variable";jDid[inId].name="Local" jDlist[inId]="GV";jDlist["cmd"..inId]="setGlobal" end if global4local[1] then fibaro.setGlobalVariable(global4local.gVarName,json.encode(jVar)) end for j,i,k in jmStr:gmatch("(.)([&][%w_']+)(.)") do if jS2n[i:match("[&]([%w_']+)")] then sceneAbort("(44) Duplicate name "..wF..i:match("[&](%w+)")..eF.." found for global and jS2n{} table variable "..wF..i:match("[&]([%w_']+)").."="..jS2n[i:match("[&]([%w_']+)")]..eF.."
Please rename one of the variables.",{i:match("[&]([%w_']+)")}) end end tmpStr="";mCnt=0 local tmpKey={} for j,i in pairs(sysKeyWord) do tmpStr=tmpStr..j.."\"" end for i in jmStr:gmatch("([$][%w_.]+)") do if i:find("$geo") then gpsStatus=true end if i:find("$math") then if not tmpKey[i:match("$math([%d.]+)")] then mCnt=mCnt+1 tmpKey[i:match("$math([%d.]+)")]="$math"..mCnt sysKeyWord["$math"..mCnt]=i:match("$math([%d.]+)") jmStr=jmStr:gsub(i,"$math"..mCnt) i="$math"..mCnt;tmpStr=tmpStr..i.."\"" else i="$math"..mCnt end end jDid[i]={};jDid[i].name="keyWord"; jDid[i].room=(tmpStr:find(i) and "sysKey" or json.encode(userKeyWord[1]):find(i) and "usrKey" or "") if not (json.encode(jZone)..tmpStr..json.encode(userKeyWord[1])):find(i.."\"") then sceneAbort("(62) Keyword ("..wF..i..eF..") not found.
Available system keywords:
"..wF..tmpStr:gsub("[\",]"," ")..eF.."
Available user keywords:
"..wF..json.encode(userKeyWord[1]):gsub("[\",]"," ")..eF.."
Available Alarm keywords:
"..wF..json.encode(jZone):gsub("[\",]"," ")..eF,{i.." "}) end end -- end of for i in jmStr:gmatch("([$][%w_.]+)") do if global4rmd[1] then jMem=fibaro.getGlobalVariable(global4rmd.gVarName) if not jMem then sceneAbort("(61) Reminders Global variable '"..global4rmd.gVarName.."' Not found in the system.",{global4rmd.gVarName,"gVarName"},{".User defined global4rmd",json.encode(global4rmd)}) end printLabel("progressLabel",ygF.."Reminders will be saved in global "..wF..global4rmd.gVarName..eF.." variable ...",self) if json.encode(jMem):len()< 20 then jMem={} else jMem=json.decode(jMem) printLabel("progressLabel",ygF.."Validating saved reminders in "..wF..global4rmd.gVarName..eF.." variable ...") for i=1,#jMem do jMem[i].lTime=os_time() if jMem[i].srcTrg then checkReminder(jMem[i].srcTrg) end end end else printLabel("progressLabel",lpF.."Global variable for Reminders not defined. Reminders "..rF.."won't be saved"..eF.." for next run...") end jmStr=jmStr:gsub("dCt","dbgCmdTrue"):gsub("dFs","dbgFalseState"):gsub("dTs","dbgTrueState"):gsub("dIs","dbgInitState"):gsub("dCf","dbgCmdFalse"):gsub("dIf","dbgInfo") jM=json.decode(jmStr) for j=1,#jM do if json.encode(jM[j][3]):len()>3 then jM2n[j.."act"]=logJmLine(json.encode(jM[j][3]):gsub("\":","\"="):gsub(":%[","={"):gsub("[%[%]\\]",function (a) return a:find("%[") and "{" or a:find("%]") and "}" or a:find("\\") and "" end)) else jM2n[j.."act"]=grF.."{ no rules or actions defined }"..eF end end if jS2n then for k=1,5 do for i,j in pairs(jS2n) do jN2s[tostring(tableId(j))]=i;jmStr=jmStr:gsub("`"..i.."`",j) end end end if jP2n then for k=1,3 do for i,j in pairs(jP2n) do jmStr=jmStr:gsub("`"..i.."`",j) end end end jM=json.decode(jmStr) printLabel("progressLabel",ygF.."Parsing jM{"..#jM.."} hash table data...") for i,j in pairs(jM) do if not tonumber(i) then sceneAbort("(124) Array out of context in JM table: "..rF..i.."="..json.encode(j):gsub("[%[]","{"):gsub("[%]]","}").."
"..eF..wF.."e.g.
{25, \"devID\",{state=\"value=true\", trueAct={\"devID\",\"trunOn\",\"\"}}}"..eF,{i}) end end for j,jv in ipairs(jM) do if jM[j][1]==-999 then goto nextJM end if not jv[2] then sceneAbort("(123) Syntax error in JM table line "..j.." (wrong number of elements)
"..wF.."{25, \"|823|765|\",{state=\"value=true\", trueAct={\"|792|\",\"trunOn\",\"\"}}}"..eF,{"{","}"},j) end checkAlias(json.encode(jv),j);checkIdVal(json.encode(jv),j) jv[2]=jv[2]:gsub(" ","");devNum=0 for tblId in string.gmatch(jv[2],"['_%$&]?[%w_'$]+") do numAct=numAct+1 if tblId:sub(1,1)=="&" then tblId=setKeyword(tblId) end i=((jDlist[tblId] or tblId:find("[%$&]")) and tblId..">"..j or tblId); inGid=i jT[tableId(tblId)]=jT[tableId(tblId)] or {room={},idNum={},rowspan={},loc=""} jT[tableId(tblId)].idNum[#jT[tableId(tblId)].idNum+1] = i if #jT[tableId(tblId)].idNum==1 then jT[tableId(tblId)].rowspan[i] = true else jT[tableId(tblId)].rowspan[i] = false end if tonumber(realId(i))==plugin.mainDeviceId then sceneAbort("(49) QuickApp ID ("..wF..realId(i) ..eF..") is an AOQ and prohibited to use.",{realId(i)},j) end if jD[i] then sceneAbort("(50) Duplicated "..jD[i].idType.." ID ("..wF.. i..realId(i) ..eF..") in table.
Previous occurance in jM hash table:"..jD[i].lineNum..":
"..wF..jM[jD[i].lineNum][2]:gsub(i,rF..i..eF)..eF,{i},j) end jD[i]={lAct={},lineNum=j,timeSpanOn=false,pRef={armed=""},cfg="",userTerms="",timeSlot=nil,timeSlotOrg=nil,onClock=0;property=nil,lState=false,state=4,stateValue="",stateOper="",idType="na",id=tableId(tblId),active=true,trigAll=false,trigOn=false,initOnStartup=true,lVal={""},inVal="",total=0,lTime=os_time(),status=1,tstRate=jv[1],tstRateOrg=jv[1],maxTime=0,maxTimeStamp=os_time(),tAlerts=0,alertTime={};lag=0,lagStr="",lagTime=os_time()} for pn,pv in pairs(jDp[tableId(i)] or {}) do jD[i][pn]=pv end jD[i].state=4 jD[i].idType=(jDid[tblId] and jDid[tblId].idType or i:sub(1,1)=="&" and "Variable" or i:sub(1,1)=="$" and "Keyword" or "Unknown"); if jD[i].idType=="Unknown" then sceneAbort("(36) ID "..wF..tblId..eF.." not found.",{tblId},j) end tableOrder[4][jD[i].idType]=tableOrder[4][jD[i].idType]+1; if jDid[tblId] then jD[i].pRef.batteryLevel=jDid[tblId].pRef and jDid[tblId].pRef.batteryLevel or nil jD[i].pRef.armed= jDid[tblId].pRef and jDid[tblId].pRef.armed or "" jD[i].cfg=jDid[tblId].cfg or "" end if i:find("$customEvent") then jD[i].trigAll=true end if i:find("$geo") then jD[i].trigAll=true;jD[i].initOnStartup=false end if jD[i].tstRate<0 then jD[i].status=4;jD[i].initOnStartup=false end; if jv[3] then for ds=1,#task.rule do if jv[3][task.rule[ds]] then tmpStr=json.encode(jv[3][task.rule[ds]]):gsub("[ ]?[<=>][ ]?",function(a) return a:gsub(" ","") end);jv[3][task.rule[ds]]=json.decode(tmpStr) end end for k =1,#task.all do if jv[3][task.all[k]]~=nil then jD[i][task.all[k]]=jv[3][task.all[k]] end end else jv[3]={nil} end for K1 in json.encode(jv):gmatch("([&][%w_'$]+)") do i1=setKeyword(K1) if not jGvar[i1] then fibaro_getGlobalValue(i1,K1) end end setStateFmt(jv[3]) checkData(jv[3],i,jv[2],j);devNum=devNum+1; for d,_ in pairs(jDbg) do jD[i][d]=jDbg[d] if jv[3].dbgInfo~=nil then jD[i][d]=jv[3].dbgInfo end if jv[3][d]~=nil then jD[i][d]=jv[3][d] end end if jD[i].timeSlot then jD[i].timeSlot=extractTimeSlot(jD[i].timeSlot) end jD[i].timeSlotOrg=jD[i].timeSlot;jD[i].onClock=to_number(jD[i].onClock) jT[tableId(tblId)].room[#jT[tableId(tblId)].room+1]=jDid[tableId(i)].room if jD[i].property then if jv[3].property=="state" then jv[3].property="State" end jD[i].property=jv[3].property jD[i][jD[i].property].lTime=os_time();jD[i].userTerms=jv[3].property;jD[i].state=1 if jD[i].property:lower():find("scene") then jD[i].trigAll=true;jD[i].initOnStartup=false end elseif jv[3].state then jD[i].state=2 jD[i].stateOper=jv[3].state:match("[=<>!][>=]?") tmpStr,jD[i].stateValue=jv[3].state:match("(.-)"..jD[i].stateOper.."(.*)") if jD[i].stateValue:find(" or ") then if jD[i].stateOper=="==" then jD[i].stateValue="|"..jD[i].stateValue:gsub(" or ","|") .."|" else sceneAbort("(113) Syntax error in state "..wF..tmpStr..eF..rF..jD[i].stateOper..eF..wF..jD[i].stateValue..eF.. "
Multiple state values are available for double equal \"==\" opertor only.
e.g. state=\""..ygF..tmpStr..wF.."=="..eF..jD[i].stateValue..eF,{jv[3].state},j) end end if jD[i].stateValue:match("[%(%+%-%*/@&%$]") and not jD[i].stateValue:match("%b[]") and not jD[i].stateValue:match("%b??") then if jD[i].stateValue:find("#") then jD[i].stateValue=("?"..jD[i].stateValue):gsub("#","?#") else jD[i].stateValue="?"..jD[i].stateValue.."?" end end if tmpStr:sub(1,1)=="+" then tmpStr=tmpStr:sub(2) end if tmpStr=="state" or tmpStr=="status" then jD[i].property=tmpStr:gsub("stat","Stat") else jD[i].property=tmpStr end if jD[i].property:lower():find("scene") then jD[i].trigAll=true;jD[i].initOnStartup=false end jD[i].userTerms=jD[i].property..jD[i].stateOper if jD[i].stateValue:find(dly) then jD[i].lag=to_number(string.sub(jD[i].stateValue,jD[i].stateValue:find(dly)+1));jD[i].lagStr=""..dly..jD[i].lag.."" jD[i].stateValue=jD[i].stateValue:sub(1,jD[i].stateValue:find(dly)-1) end if jD[i].idType=="Keyword" then jD[i][jD[i].property]={value=tostring(setKeyword(tableId(tblId))),lTime=os_time()} end if jD[i].idType=="Variable" then jD[i][jD[i].property]={value=jDp[tableId(i)].value.value,lTime=jDp[tableId(i)].value.mTime} end if ("Z.Device Zigbee Plugin QuickApp Nice"):find(jD[i].idType) then if not jD[i][jD[i].property] then jD[i][jD[i].property]={lTime=os_time(),value="TBD"} end jD[i][jD[i].property].lTime=os_time() if jD[i][jD[i].property].value:find("table") then jD[i][jD[i].property].value="TBR" if tblVal:find(jD[i].property) then jD[i][jD[i].property].value=getHistory(i) end end end elseif i:find(">") then jD[i].state=3; jD[i].property="value"; if i:find("[&]") then jD[i][jD[i].property]={value=jDp[tableId(i)].value.value,lTime=os_time()} elseif i:find("[$]") then jD[i][jD[i].property]={value=tostring(setKeyword(tableId(tblId))),lTime=os_time()} -- jD[i].inVal=jD[i][jD[i].property].value else jD[i].property="n/a" ;jD[i][jD[i].property]={value="-",lTime=os_time()} end end if jD[i].idType:find("Scene") then jD[i].property="runningInstances" end if jD[i].idType:find("Auto") then jD[i].runningInstances.value=1 end --jD[i].instances=1 end end --for tblId in string.gmatch(string.gsub(jv[2 tmpNum=j ::nextJM:: end --for j,jv in ipairs(jM) do printLabel("progressLabel",ygF.."Initializing activities ... ") for _, i in pairs(jT) do for j=1,#i.room do jI[#jI+1]=i.room[j]..":"..i.idNum[j]..":" end end table.sort(jI); tmpStr="" for j=1,#jI do i=jI[j]:match("%b::"):sub(2,-2) if i:match("[%w']+")==tmpStr:match("[%w']+") then jT[tableId(i)].rowspan[i]=false else jT[tableId(i)].rowspan[i]=true end; tmpStr=i end for j,_ in ipairs(jM) do for i,k in string.gmatch(json.encode(jM[j]),"([_&%$]?[%w_']+)>(%d+)") do --"[!|]([&%$]?%w+>%d+)") do if i:find("[&%$]") or (i:match("%d+") and not i:match("%a+")) then i=i..">"..k if tonumber(k)>#jM then sceneAbort("(45) Reference to line "..i:match(">(%d+)").." in '"..wF..i..eF.."' is "..rF.."out of jM hash table range."..eF.."
jM hash table range is 1-"..#jM,{i},j) end if not jD[i] then tmpNum=tonumber(i:match(">(%d+)")) sceneAbort("(46) Incorrect reference. Instaed of '"..wF..i..eF.."' in jM line "..tmpNum..", found ID '"..wF..jM[tmpNum][2]..eF.."'",{i},j) end end end end for j=1,#jM do for i,_ in pairs(jD) do if j==1 then jN2s[tableId(i)]=(jN2s[tableId(i)] or tableId(i)); tmpStr=(jM2n[jD[i].lineNum]:find(jN2s[tableId(i)]) and jN2s[tableId(i)] or tableId(i)) jD[i].jm2=jM2n[jD[i].lineNum]:gsub(tmpStr,ygF..tmpStr..eF) for k=1,#task.actRule do if jM2n[jD[i].lineNum.."act"]:find(task.actRule[k]) then dataCol["Active Rules"]=true;tmpStr=tostring(jD[i][task.actRule[k]]) jD[i].rules=(jD[i].actRule and jD[i].actRule..trueFalseColor[tmpStr]..task.actRule[k].." " or trueFalseColor[tmpStr]..task.actRule[k].." ") end end end--if j==1 then jN2s[tableId(i)]= if jD[i].lineNum==j and jD[i].tstRate>-1 then jChk={};typePrfx="I" jD[i].status=(checkState(i) and 1 or 2) if jD[i].property then jD[i].inVal=jD[i][jD[i].property].value end if jD[i].initOnStartup and jD[i].state==2 and jD[i].status~=3 and not jD[i].initAct then if jD[i][task.all[jD[i].status]] then typePrfx="I"..task.all[jD[i].status]:sub(1,1) setInitAct(jD[i][task.all[jD[i].status]],i,typePrfx) jD[i].initActState=jD[i].initActState or jD[i][task.all[jD[i].status].."State"] end end -- jD[i].initOnStartup and jD[i].state==2 and... if jD[i].initAct then dataCol.initAct=true; jAct[i]={act=json.decode(json.encode(jD[i].initAct)),actState="initAct",lTime=0,type=typePrfx}; runAction(); logStatus="" end end --if jD[i].lineNum==j and jD[i].tstRate>-1 end --for i,_ in pairs(jD) do end --for j=1,#jM do tmpNum=0 for j=1,#tableOrder[1] do tmpStr=" "..tableOrder[4][tableOrder[1][j]];tmpNum=tmpNum+tableOrder[4][tableOrder[1][j]] gData.idList=gData.idList.." "..string.format("%-16s",tableOrder[1][j])..tmpStr:gsub(" 0"," - ").."
" end; gData.idList=menuLen(gData.idList.."----------------------
"..string.format("%-17s","Total")..tmpNum,"right:-",true) sysInventory=" "..sbF.."" ..api.get("/settings/info/")["hcName"].."" getProfiles();getAlarms();initDone=true end --==================================== initTable *0053 3 function checkIdVal(lStr,lNum) if not lStr:find("@") then return end local inId,i,j,aName=nil,0,0,"" for i in lStr:gmatch("[@][%w_']+[>]?%w+") do inId=i:match("[@]([%w_']+)"); aId=jN2s[inId] if not i:match("[>]%w+") then sceneAbort("(118) Syntax error in "..wF..i..eF.." property not defined.
Format is: @[Device] > [property]
e.g. "..wF.."@"..inId..">value"..eF..(aId and " or "..wF.."@`"..aId.."`>value"..eF or ""),{i},lNum) end if not jDp[inId] then tmpStr="" if jS2n[inId] then tmpStr="An alias has found in jS2n table "..wF..inId.."="..jS2n[inId]..eF.."
Please add back quotes for proper use.
e.g. "..wF.."@`"..inId.."`"..i:match(">%w+")..eF.." or "..wF.."@"..jS2n[inId]..i:match(">%w+")..eF end sceneAbort("(119) Device ID "..wF..inId..eF.." not found.
"..tmpStr,{i},lNum) end end end function checkAlias(lStr,lNum) local aName=lStr:match("(`[%w_]+`)"); if aName then sceneAbort("(48) Alias "..wF..aName.." not found. Available aliases
jS2n table:
"..ygF..json.encode(jS2n):gsub("[{\"}]",""):gsub(":","=")..eF.."
jP2n table
"..ygF..json.encode(jP2n):gsub("[{\"}]",""):gsub(":","=")..eF,{aName},lNum) end aName=lStr:match("(`%w+)") if not aName then aName=lStr:match("(%w+`)") end if aName then sceneAbort("(117) Syntax error at alias "..wF..aName..". Missing back quote mark (`)",{aName},lNum) end end function fibaro:abort() for i=1,10 do fibaro.warning(__TAG, rF.."QA terminated. Please refer to Advanced User’s Guide for more information...."..eF.." and restart AOQ") fibaro.sleep(600000) end fibaro:stop() end--======================== abort function printLabel(labelName,msg) local mStr="" selfi:setVariable("webEye", msg) if initMode and msg:find(":~:") then mStr=msg else mStr=msg:gsub(".-",""):gsub("font size=%d","font size=1"):gsub("bgcolor","clr") end selfi:updateView(labelName, "text", mStr); if initMode then if mStr:find(":~:") then selfi.error(__TAG,mStr) else selfi:debug(mStr) end end end--==================================== printLabel 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 --==================================== table_concat 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 cmd=="setConfiguration" then forceAdd=1 end if #act1<1 then if jCmd[cmd] then sceneAbort("(89) No devices specified for '"..wF..cmd..eF.."' action.
e.g. {"..ygF.."\""..wF.."item-ID"..eF.."\",\""..cmd.."\",\"\""..eF.."}",{cmd},jD[inId].lineNum) else sceneAbort("(98) Action "..wF..cmd..eF.." not supported.
"..aosAct,{cmd},jD[inId].lineNum) end end for dId in act1:gsub("|"," "):gmatch("%S+") do cnt=cnt+1 if dId:sub(1,1)=="&" then sceneAbort("(111) 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("(112) Action "..wF..cmd..eF.." not available for keyword "..wF..dId..(mStr and " in "..mStr or "")..eF.."
Supported action:

rptAction
runAction
setStateValue
setState
setStateFormula
setStateDelay
setTimeSpan
setTimeSlot",{cmd,(mStr or dId)},jD[inId].lineNum) end if not jDlist[dId] then sceneAbort("(35) Item ID "..wF..dId..eF..(jDlist["mId"..dId]and " is a master and prohibited to use." or " not found in the system."),{dId},jD[inId].lineNum) end if not jDlist[dId] and not jCmd[cmd] and not forceAdd then sceneAbort("(99) 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]+1]="poll" else sceneAbort("(93) 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] and "`"..jN2s[dId].."`" or "")..findNameRoom(dId,2)..eF..irF.." Device's actions are:
- "..table.concat(jDlist["cmd"..dId]," ").."
" end end --for dId in act1:gsub("| 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() if not initMode then getIntValue() end local ledStr="🟢" if not (" YH HCL HC3L "):find(" "..jSlave["local"].platform.." ") then local leds,onOff,inst="",{["on"]="🟤 ",["off"]="O ",["true"]="🟤 ",["false"]="O "},api.get("/installer") ledStr=(gData.alarm and "🔴 " or "🟢 ")..onOff.off..(intValue.newSw=="true" and "🟤 " or "O ")..(inst.remoteAccess and "🟢 " or inst.email=="" and "🔴 " or "🟤 ")..onOff[intValue.lan]..onOff[intValue.wifi]..(intValue.internet=="online" and "🟤 " or "🔴 ").."🟤 " 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 devices:

"..eF..json.encode(rStr):gsub("[\"\\%[%],]","") or "Top active menu disabled") end --================================ setActTop function setInitAct(orgAct,inId,actName) local iCnt,i,act2set=0,0,json.decode(json.encode(orgAct)) if type(act2set[1])~="table" then act2set={act2set} end 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 iCnt=iCnt+1 if jD[inId].dbgInitState then fibaro.trace(__TAG,setDispLine(inId,actName)..lpF.." "..act2set[i][2]:match("%w+").." "..eF..setCmdStr(act2set[i])..vF.." init disabled"..eF) end table.remove(act2set,i) end end if not act2set[1] then act2set=nil end jD[inId].initAct=act2set end --================================== setInitAct function setCmdStr(act) if act[1]:match("[&$]?[%w_']+") and not act[1]:find("%b{}") then return ygF.."{"..act[1]:gsub("|",","):gsub("[&$]?[%w_']+",function (a) return jN2s[a] or a end)..eF.."}" else return act[3]:len()>0 and "{"..grF..(initMode and act[3]:gsub("%b~~","") or cutStr(act[3]:gsub("%b~~",""),25))..eF.."}" or "" end end --================================== setCmdStr function setDispLine(inId,aType) aType=(aType or "U")..(aType:len()==1 and " " or "") if jRun.lastId~=inId then gData.inIf="" gData.ifStart="jM{"..jD[inId].lineNum.."}"..aType.." "..(" "):rep(jmNum-tostring(jD[inId].lineNum):len())..dColor[jD[inId].idType]..findNameRoom(inId,5).."["..sColor[jD[inId].status+1]..jD[inId][jD[inId].property].value.."] ➯ " elseif jD[inId].lastValue~=jD[inId][jD[inId].property].value then gData.inIf="";jD[inId].oldValue=jD[inId].lastValue gData.ifStart="jM{"..jD[inId].lineNum.."}"..aType.." "..(" "):rep(jmNum-tostring(jD[inId].lineNum):len())..""..findNameRoom(inId,5).."["..sColor[jD[inId].status+1]..jD[inId][jD[inId].property].value.."] ➯ " else gData.ifStart="jM{"..jD[inId].lineNum.."}"..aType.." "..(" "):rep(jmNum-tostring(jD[inId].lineNum):len())..""..findNameRoom(inId,5).."["..jD[inId][jD[inId].property].value.."] "..cF.."↪ "..gData.inIf end jD[inId].lastValue=jD[inId][jD[inId].property].value;jRun.lastId=inId return(gData.ifStart) end--================================ setDispLine function ctlChar(msg,replace,byChar) if replace and byChar then return tostring(msg):gsub(replace,byChar) elseif replace 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 --================================ ctlChar function parseCmd() local i,j,f,k,jRef,fields=0,0,0,0,{},{{"resp","init","f1","f2","f3"},{"[/=x]","+-","[!#><$&]","call call, qad qad, scene scene,,, profile global drift tts notify,,, int, int,,, rmd, prt alarm vac sys time int misc, sys,,,","[~$&?!]"}};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 --=================================== updateTimeDrift function cutStr(str2cut,cutLen,lChar) str2cut=tostring(str2cut) return (str2cut:len()<=cutLen+3 and str2cut or str2cut==" " and "" or str2cut:sub(1,cutLen).."...") end --======================== cutStr function menuLen(iMsg,rule,inMsg,shadow) 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 " "..fibaro_getGlobalValue(a).." &:"..a.."
" else return fibaro_getGlobalValue(a:match("[%w_'$]+")) end end) 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_']+[>]?[%w_%.]+", function(a) if txt then return " "..extractValue(a).." "..jDid[tableId(a)].name..":"..realId(a)..(a:match(">%w+") or "not defined").." " else return tostring(extractValue(a)):gsub("true","1"):gsub("false","0") end end) end return cmd end --==================================== getGlobalVal function mathTipText(cmd,inId, inFunc) -- if cmd:match("%b[]") and not cmd:match("json%b[]") then cmd=cmd:match("%b[]"):sub(2,-2) end 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 if cmd:match("[%$@&]") then cmd=getGlobalVal(cmd,inId,"text") 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,dId) local id,index=realId(cmd),cmd:match("[>]([%w%.]+)") tmpStr=index:gsub("Modified","") if not jDp[tableId(cmd)] then sceneAbort("(121) Device ID "..wF..tableId(cmd)..eF.." not found.",{tableId(cmd)},(dId and tonumber(dId:match(">(%d+)")) or jD[inGid].lineNum)) return "unknown device" end if not jDp[tableId(cmd)][tmpStr] then sceneAbort("(127) In device ID "..wF..tableId(cmd)..eF..", property '"..wF..tmpStr.."' is not available."..(tmpStr=="alarm" and " Verify device included in alarm zone(s)" or "").."
To retrieve property's last modification time, add '"..wF.."Modified"..eF.." at end of property name (case sensitive)
Device's available propertie are:"..wF..getIdData(id,"properties")..eF,{tmpStr},jD[inGid].lineNum) return "unknown property" end if tonumber(index) then return jD[id..">"..index][jD[id..">"..index].property].value end if index=="alarm" and jAlarm[sysId(cmd)] then return jAlarm[sysId(cmd)],os.time() end if index=="lastBreached" then return math.floor(os_time()-jDp[tableId(cmd)][index].mTime) end if index:find("Modified") then if not jDp[tableId(cmd)][index:gsub("Modified","")].mTime then return getModificationTime(cmd) else return math.floor(os.time()-jDp[tableId(cmd)][index:gsub("Modified","")].mTime) end end return jDp[tableId(cmd)][index:gsub("Modified","")].value --end --or fibaroGet(id,index) end end--================================ extractValue function apiGetValue(inId,pName) if inId:find(demoId) and demo then return apiDemo.properties[pName] end if pName=="alarm" then return jAlarm[realId(inId)] end if initMode then errorResp.properties[pName]="networkError" end return api_get(inId,"/devices/"..sysId(inId))["properties"][pName] end--================================ apiGetValue 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 r=to_number(r);v=to_number(v);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 if select(2,math.modf(v))==0 then return math.modf(v) else return v end end --======================================= calculate function doTheMath(cmd,inId,fType) if not cmd:match("[%$@&]") and not cmd:match("%b??") then return cmd end orgCmd=cmd if fType then return mathTipText(cmd,inId) end cmd=getGlobalVal(cmd,inId) cmd=cmd:gsub("%b??",function(a) 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 if orgCmd:find("%?") then orgCmd=orgCmd:match("%b??"):sub(2,-2) end 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,oldCmd,rStr=0,0,false,runCmd,doTheMath(remTrg,inId) 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 runCmd="delRmdNote" lastAct(trueFalseColor[true]..cF..inId.."delRmdNote{"..grF.."'*"..rStr.."*'"..eF.."}",inId) runCmd=oldCmd homeTts(rStr) if remAct=="delLogRmd" then sendNote(sceneName.." (Delete Reminder)",rStr.."\nDeleted reminder:\n"..tmpStr) end remTrg="" end end end elseif remAct=="delAllRmd" then jMem={} if #remTrg>5 then runCmd="delRmdNote" lastAct(trueFalseColor[true]..cF..inId.."delRmdNote{"..grF.."'*"..rStr.."*'"..eF.."}",inId) runCmd=oldCmd homeTts(rStr) end else tmpNum=(tmpNum or 900) if to_number(tmpNum)<10 then tmpNum=10 end --10 sec. minimum 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.setGlobalVariable(global4rmd.gVarName,json.encode(jMem)) end return true end --======================== logReminder function checkReminder(srcTrig) for m=1,#jMem do if srcTrig and initMode then local hTxt,jmLine,jmDev,inTrig="",tonumber(jMem[m].devId:match(">(%d+)")),jMem[m].devId:match("[$&]?[%w_']+"),jMem[m].srcTrg:gsub("{(.-:.-)}", function (s) a2,a1=s:match("(.-)(:.+)");a2=a2.."," return a2:gsub(",",a1.."|") end) if jM[jmLine] and jM[jmLine][2]:find(jmDev) then hTxt=" In saved reminder of jM{"..jmLine.."} "..jM2n[jmLine]..eF else hTxt=" In saved reminder, device "..wF..jmDev..eF.." at jM{"..jmLine.."}"..rF.." is not found in the current JM hash table"..eF end for s,v in inTrig:gmatch("([%w_'$]+):(%w+)") do v=v:gsub("Modified","") errTxt={hTxt,ygF.."
Trigger:"..eF.." \""..jMem[m].srcTrg.."\""..ygF.."
Message:"..eF.." \""..jMem[m].remMsg.."\""} if not jDp[s] then sceneAbort("(128) Device ID "..wF..s..eF.." not found.",{s},errTxt) end if not jDp[s][v] then sceneAbort("(129) In device ID "..wF..s..eF..", property '"..wF..v.."
' is not available."..(v=="alarm" and " Verify device included in alarm zone(s)" or "").."
To retrieve property's last modification time, add '"..wF.."Modified"..eF.." at end of property name (case sensitive)
Device's available properties are:"..wF..getIdData(s,"properties")..eF,{s,v},errTxt) end end--for s,v in jMem[i].srcT else if os_time()-jMem[m].lTime > jMem[m].remRate then if logTrueFalse(jMem[m].srcTrg,jMem[m].devId,"statusnomark") then execReminder(m) end end end end --for m=1,#jMem do end --============================ checkReminder function execReminder(remNum) local rStr,oldCmd=doTheMath(jMem[remNum].remMsg,jMem[remNum].devId),runCmd jMem[remNum].lTime=os_time() if jMem[remNum].note then sendNote(sceneName.." (Reminder)",rStr) end if not initMode then runCmd="reminder" lastAct(trueFalseColor[true]..""..jMem[remNum].devId.."reminder{"..grF.."'*"..rStr.."*'"..eF.."}",jMem[remNum].devId) runCmd=oldCmd end homeTts(rStr) end --======================================= execReminder 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 --=================================== wdtReset function getModificationTime(inId) local index if not inId:find("[$&]") then index=inId:match("[>]([%w%.]+)"):gsub("Modified","") end if inId:find("[$]") or inId:find("stateTime") then return os_time()-jD[inGid].lTime end if inId:find("&") then fibaro_getGlobalValue(realId(inId)) return math.floor(os.time()-jDp[tableId(inId)].value.mTime) end if not jDp[tableId(inId)][index] then sceneAbort("(115) lastBreached property unavailable for ID ["..wF..tableId(inId)..eF.."]",{"lastBreached",tableId(inId)},jD[inGid].lineNum) end if not jDp[tableId(inId)][index].mTime then jDp[tableId(inId)][index].mTime=select(2,fibaroGet(realId(inId),index)) end return math.floor(os.time()-jDp[tableId(inId)][index].mTime) end --====================================== getModificationTime 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="";gData.lastLog=false if cmd=="{else}" then indent=indent:sub(1,-3) return bF.."
"..indent.."else
" end logTxt=bF..indent.."if ";cmd=spKeyword(cmd,dId) 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,o,val,rVal,ref,z,val1,timeVal={[false]="",[true]=""},false,0,0,"",0,"","","" for z in evalStr:gmatch("(.-)[|]") do 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") or ref:find("stateTime") 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,dId) 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 vF.." and "..eF)..lgStatus[lStatus]..(jN2s[realId(inId)] or inId:find("[&$]") and tableId(inId) or ""..realId(inId).." "..findNameRoom(inId,6)).."("..cVal.."):"..dState:gsub(" ","")..eF end logTxt=logTxt.."("..cVal..") ["..stColor[lStatus]..dState.."]
" end logStatus=(logStatus.."}"):gsub(":value","") if not gData.lastLog then gData.lastLog=(fStatus==lCnt and true or false) end 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..vF.." or "..eF end getStatus((a:sub(1,1):gsub("|","")..a:sub(2)):gsub("(.-)|", function (s) a2,a1=s:match("(.-)(:.+)");a2=a2.."," return a2:gsub(",",a1.."|") end)) end if status and status:find("nomark") then logStatus=logStatus:gsub("","("):gsub("",")"):gsub("<[/]?mark[%d+]?>","") end if status then return gData.lastLog --fStatus else log_trueFalse=true; return logTxt..bF..indent.."then
" end end --=================================== logTrueFalse function emitEvent(name,inId) local rName="" for rName in name:gsub("|"," "):gmatch("%S+") do if not json.encode(jCevent):find("\""..rName.."\"") then sceneAbort("(84) Custom Event '"..wF..rName..eF.."' not found.
Available custom events:
"..ygF..table.concat(jCevent,"
")..eF,{"emitEvent",rName},jD[inId].lineNum) end end return "" end --=================================== emitEvent function checkAlarm(id,inId) local rName,i="",0 if id=="all" then return "" end for rName in id:gsub("|"," "):gmatch("%S+") do if not json.encode(jZone):find("\"$"..rName.."\"") and not jAlarm["-"..rName] then sceneAbort("(126) Alarm Zone '"..wF..rName..eF.."' not found.
Available zones:
"..ygF.."all
"..table.concat(jAlarm["all"],"
"):gsub("%$","")..eF,{"armAlarm",rName},jD[inId].lineNum) end end return "" end --=============================== checkAlarm function getAlarms() local i,j,z,jTmp,inId,tName,jBr,k local aState={[true]="armed",[false]="disarmed",["true"]="armed",["false"]="disarmed"} jTmp=api.get("/alarms/v1/partitions");gData.alarm=false;gData.alarmState="disarmed" if initMode then jAlarm={};jAlarm["all"]={};jZone={} end for i,j in pairs (jAlarm) do if jAlarm[i]=="armed" then jAlarm[i]="disarmed" end end for z,i in pairs(jTmp) do tName=i.name:gsub(" ","_") if not gData.alarm then gData.alarm=i.armed end jAlarm["all"][z]=i.id.." "..tName; jZone[#jZone+1]="$"..tName; jAlarm["-"..i.id]="$"..tName jAlarm["$"..tName]=i.id;jAlarm["$"..tName.."value"]=aState[i.armed] if gData.alarmState=="disarmed" then gData.alarmState=aState[i.armed] end if i.breached then jAlarm["$"..tName.."value"]="breached"; gData.alarmState="breached" end for j=1,#i.devices do inId=tostring(i.devices[j]); if not jAlarm[inId] then jAlarm[inId]="disarmed" end if not jAlarm[inId.."zone"] then jAlarm[inId.."zone"]={} end; jAlarm[inId.."zone"][z]=":"..i.id..":" if jAlarm[inId]=="breached" then jAlarm[inId]="disarmed" jBr=api.get("/alarms/v1/partitions/breached") if #jBr>0 then gData.alarmState="breached" end for k=1,#jBr do if json.encode(jAlarm[inId.."zone"]):find(":"..jBr[k]..":") then jAlarm[inId]="breached" end end elseif z==1 then jAlarm[inId]="disarmed" end if jAlarm[inId]=="disarmed" then jAlarm[inId]=aState[i.armed] end if not initMode then logValue(inId,"alarm",jAlarm[inId]) end end end--for z,i in pairs(jTmp) end --=================================== getAlarms function setProfile(id,inId,tipTxt) if not jPrf[id] then tmpStr=""; for i=1,20 do if jPrf[tostring(i)] then tmpStr=tmpStr..i..":" ..jPrf[tostring(i)].."
" else break end end sceneAbort("(83) Profile ID '"..wF..id..eF.."' not found.
Available profiles:
"..ygF..tmpStr..eF.."
Active profile: "..ygF..jPrf.activeProfile..eF,{"setProfile",'"'..id..'"'},jD[inId].lineNum) end if tipTxt then return jPrf[id] end if tonumber(id) then fibaro.profile("activateProfile",tonumber(id)) else fibaro.profile("activateProfile",tonumber(jPrf[id])) end end --=================================== setProfile function getProfiles(getData) local j,jTmp=0,api.get("/profiles") for _,j in pairs(jTmp.profiles) do jPrf[tostring(j.id)]=j.name;jPrf[j.name]=tostring(j.id) end jPrf.activeProfile=jPrf[tostring(jTmp.activeProfile)] if getData then return jPrf.activeProfile end jTmp=api.get("/customEvents") for _,j in pairs(jTmp) do jCevent[#jCevent+1]=tostring(j.name) end end --=================================== getProfiles function setVacation(cmd,inId) local i,vMode=0,{["vacOn"]=true,["vacOff"]=false,[true]=true,[false]=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 QuickApp 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 api_get(i,"/scenes/"..realId(i),"setVacation")["enabled"] then jD[i].tstRate=-10 end else jD[i].tstRate=jD[i].tstRateOrg end end if not vMode[cmd] then atHome=true end end --==================================== setVacation function cmdActive(cmd,sColor,mStr) return isAct[cmd]==false and fc..(sColor or "red")..">"..(mStr or cmd)..eF or (mStr or cmd) end --==================================== cmdActive 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_]+"))..(actData[2]:find("!%b{}") and "!" or "")..(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_]+"))..(dId[2]:find("!%b{}") and "!" or "")..(cmdActive(dId[2]:match(";else;[%w_]+")) and ""..cmdActive(dId[2]:match(";else;([%w_]+)")).."" or "").." " end end end return jTabAct[inId][k] end --================= setToolTipTxt function getTipTxt(inAct,actData,inId,log_trueFalseCont) local tstData,dId,tipTxt,inSlot=nil,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 actData[2]:find("!%b{}") then actData[2],tstData=actData[2]:match("(.-)!(.*)") end if tstData then if cmd=="verify" and not jD[inId].errAct and not jD[inId].okAct then sceneAbort("(108) While "..wF..cmd.."!"..tstData..eF.." defined, no errAct{} or okAct{} actions have been assigned",{cmd.."!"..tstData},jD[inId].lineNum) end if cmd~="verify" then tstData=bF.." and verify "..eF..(logTrueFalse(tstData,inId,"status") and "" or "")..logStatus:gsub("#9ACD90","darkgreen"):gsub(cF,bF) end 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. and
"; 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].." (\""..doTheMath(setKeyword(actData[1]),inId,"parse").. "\", \""..act3.."\") " elseif cmd=="verify" then tipTxt=tipTxt.." "..cmd..""..stColor[logTrueFalse(tstData,inId,"status")]..eF..logStatus:gsub("#9ACD90","darkgreen"):gsub(cF,bF)..(jD[inId].errAct and bF..": errAct{}"..eF or "")..(jD[inId].okAct and bF.." : okAct{}"..eF or "") elseif ("time int,,, drift"):find(jCmd[cmd].f2) then if cmd=="rptAction" then for dId in actData[1]:gsub("|"," "):gmatch("%S+") do tmpStr=rptAct(actData[2],act3,dId,inId,"getTipTextlogInfo") end end if cmd=="runAction" then act3=(actData[2]:match(",%d+") and act3:match("%w+")..actData[2]:match(",.*") or act3);actData[2]=actData[2]:match("%w+") execAct(actData[2],act3,actData[1]:match("(%S+)[|]?"),inId,"logInfo") end tipTxt=tipTxt.." "..(cmd=="rptAction" and cmd..tmpStr or actData[2]).." "..act3 --(act3:find(",") and act3:gsub(",","[").."]" or act3) elseif cmd=="setProfile" then tipTxt=tipTxt.." "..actData[2].." ("..actData[1]..":"..setProfile(actData[1],inId,"tipTxt")..") " elseif cmd=="emitEvent" then tipTxt=tipTxt.." "..actData[2].." ("..actData[1]:gsub("|",",")..emitEvent(actData[1],inId)..") " elseif cmd:find("armAlarm") then tipTxt=tipTxt.." "..actData[2].." ("..actData[1]:gsub("|",",")..checkAlarm(actData[1],inId)..") " 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(tonumber(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.."\""..(actData[1]:len()>0 and ", {".. subSign(doTheMath(actData[1],inId,"parse")).."}" or "")..") " 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") 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 "("..getButtonName(dId,actData[2]:gsub("pressButton,",""))..")" or "" )..(cmd:find("Slider") and "("..getSliderName(dId,actData[2]:match("setSlider,(%S+),"),"")..")" or "" )..(dId:find(">") and " jM line "..dId:match(">(%d+)").."" or " ") end end if tstData and cmd~="verify" then tipTxt=tipTxt.."
"..indent..tstData 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 --=========================== getTipTxt function setTipTxt(actDataRef,inId,inAct) local mStr,i,tipTxt,did,cmdData,actData="",0,"",0,{{""},{""}},json.decode(json.encode(actDataRef)) 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(inAct,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 "..wF.."else condition is unavailble.
"..inAct.."={"..ygF.."\"".. table.concat(actData,"\",\""):gsub("={","when{"):gsub("else",wF.."else").."\"}

",actData,jD[inId].lineNum) end for i=1,#actData do cmdData[1][i]=(tostring(actData[i]):match("(.-);else;") or actData[i]);cmdData[2][i]=(tostring(actData[i]):match(";else;(.+)") or actData[i]) end cmdData[2][4]="{else}"; inElse=true;mStr=getTipTxt(inAct,cmdData[1],inId,true)..getTipTxt(inAct,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 jD[inId][inAct.."State"] and mStr..bF.."
and"..eF or mStr end --====================================== setTipTxt 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 --=========================== spKeyword function getIntValue() local i,t,j,sTime,rxd=0,0,0,0,"" local cType,cmp={"Get","Post","Put"},"" for j,i in pairs(jSlave) do sTime=i.slaveApiTime/1000 for t=1,3 do cmp="tApi"..cType[t] if i[cmp]>sTime and i[cmp]<1000 then sceneAbort(""..j.." hub respond exceeds "..sTime.." Sec."..eF,{"rr"},{":"..rF.." REST API high respond time: "..eF, "API "..cType[t]:upper()..": "..i[cmp].." sec.
"},fibaro.warning) i[cmp]=i[cmp]*10000 end end end intValue.timezoneOffset=0 rxd=api.get("/settings/info") if rxd then intValue.sunsetHour=rxd.sunsetHour intValue.sunriseHour=rxd.sunriseHour; intValue.hc3online=tostring(rxd.online) intValue.timezoneOffset=rxd.timezoneOffset intValue.newSw=tostring(rxd.updateStableAvailable) intValue.newBeta=tostring(rxd.updateBetaAvailable) intValue.skin=rxd.skin else fibaro.error(__TAG,"/settings/info not received. Latest values in use.") end rxd=api.get("/diagnostics") if rxd then intValue.ram=100-rxd.memory.free else fibaro.error(__TAG,"/diagnostics not received. Latest values in use.") end intValue.internet=api.get("/proxy?url=http://127.0.0.1:11112/api/settings/network/connectivity")["internetConnectivity"]; k=api.get("/proxy?url=http://127.0.0.1:11112/api/settings/network/") intValue.wifi=tostring(k.networkConfig.wlan0.enabled) if k.networkConfig.eth0 then intValue.lan=tostring(k.networkConfig.eth0.enabled) end if intValue.internet then intValue.internet="online" else intValue.internet="offline" end end --======================================== getIntValue function setKeyword(rxMsg,devId) if not rxMsg:find("%$") then return rxMsg end if not devId then keyinId=9999 else keyinId=devId end inGid=(devId or inGid) local i,j for i in string.gmatch(rxMsg,"$[%w_]+") do if sysKeyWord[i] then rxMsg=rxMsg:gsub(i, sysKeyWord[i]) goto nextKey end for j=1,#jZone do if jZone[j]==i then rxMsg=rxMsg:gsub(jZone[j],jAlarm[jZone[j].."value"]) goto nextKey end end for j=1,#userKeyWord[1] do if userKeyWord[1][j]==i then rxMsg=rxMsg:gsub(userKeyWord[1][j],userKeyWord[2][j]) goto nextKey end end ::nextKey:: end --for i in string.gmatch(rxMsg,"$[%w_]+") do rxMsg=rxMsg:gsub("##","") return rxMsg end --=========================== setKeyword function checkTimer() local i=0 for i,_ in pairs(jTime) do if os_time()>=jTime[i][2] then jAct[jTime[i][3]]={act=json.decode(json.encode(jTime[i][1])),actState="",lineState=true,lTime=os_time(),type="Ti"} jTime[i]=nil end end end --=========================== checkTimer function runAction(actId) local i,k,cmd,cmdRef,inId,inRpt,cStatus,elseStatus=0,0,0,0,0,";",false,false if #jMem>0 then checkReminder() end checkTimer() if json.encode(jAct):len()< 15 then jAct={} else if initMode and initDone then initDone=false;jRun.lastId="" printLabel("progressLabel",ygF.."jM{} states validation...
") end for i,_ in pairs(jAct) do if actId and actId~=i then goto nextAct end inId=i:gsub(":.+","");inGid=inId;elseState=false if i:find(":rpt") then inRpt=inRpt..inId..";" end jRun[inId]={} if jD[inId].lineState and not jAct[i].lineState then if not logTrueFalse(jD[inId].lineState[1],inId,"statusnomark") then if jD[inId].lineState[1]:sub(1,1)=="=" and not initMode then jAct[i].lTime=os_time() goto nextAct end if jD[inId].errAct and not json.encode(jD[inId]):match("!%b{}") then if jD[inId].dbgFalseState then fibaro.trace(__TAG,setDispLine(inId,jAct[i].type)..vF.."if not lineState"..logStatus.." then errAct{}"..eF) end jAct[i]={act=json.decode(json.encode(jD[inId].errAct)),actState="errAct",lTime=os_time(),type="Se"} jAct[i].lineState=true else if jD[inId].dbgFalseState then fibaro.trace(__TAG,setDispLine(inId,jAct[i].type)..vF.."if not lineState"..logStatus.." then "..gData.noAct..eF) end jAct[i]=nil goto nextAct end else jAct[i].lineState=true if jD[inId].okAct and not json.encode(jD[inId]):match("!%b{}") then if jD[inId].dbgTrueState then fibaro.trace(__TAG,setDispLine(inId,jAct[i].type)..vF.."if lineState"..logStatus.." then okAct{}"..eF) end jAct[inId..":ls"]={act=json.decode(json.encode(jD[inId].okAct)),lineState=true,actState="okAct",lTime=os_time(),type="So"} gData.inIf="  " runAction(inId..":ls") else jRun[inId].lineState={aType=jAct[i].type,txt=vF.."if lineState"..logStatus.." then"..eF} if jD[inId].dbgTrueState then fibaro.trace(__TAG,setDispLine(inId,jAct[i].type)..jRun[inId].lineState.txt) end end end --if not logTrueFalse(jD[ if jAct[i].lineState==true and jD[inId].dbgTrueState then gData.inIf="  " end end -- if jD[inId].lineState and if type(jAct[i].act[1])=="string" then jAct[i].act={jAct[i].act} end 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,"statusnomark") if not cStatus then if jD[inId][jAct[i].actState.."State"][1]:sub(1,1)=="=" and not initMode then jAct[i].lTime=os_time(); jAct[i].cmdState=false; goto nextAct end if (jD[inId].dbgFalseState and not json.encode(jAct[i]):find(";else;")) or (jD[inId].dbgTrueState and json.encode(jAct[i]):find(";else;")) then fibaro.trace(__TAG,setDispLine(inId,jAct[i].type)..vF.."if not "..(jAct[i].actState.."State"):gsub("Act","")..""..logStatus.." then "..(json.encode(jAct[i]):find(";else;") and "" or gData.noAct)..eF) end if not json.encode(jAct[i]):find(";else;") then jAct[i]=nil; goto nextAct end else jRun[inId].actState={aType=jAct[i].type,txt=vF.."if "..(jAct[i].actState.."State"):gsub("Act","")..""..logStatus.." then"..eF} if jD[inId].dbgTrueState then fibaro.trace(__TAG, setDispLine(inId,jAct[i].type)..jRun[inId].actState.txt) end end if cStatus or json.encode(jAct[i]):find(";else;") then if jD[inId].dbgTrueState then gData.inIf=gData.inIf.."  " 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 if jAct[i].actState then cmd[4]=jD[inId][jAct[i].actState.."State"][1];elseState=true end 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][1]="done" end end --for k,cmd in pairs(jAct[i]. for k=#jAct[i].act,1,-1 do if jAct[i].act[k][1]=="done" then table.remove(jAct[i].act,k) end end if #jAct[i].act==0 then jAct[i]=nil end ::nextAct:: end --for i,_ in pairs(jAct) do inId=i: end --if json.encode(jAct):le 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)),lineState=true,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()< 10 then jBack={} return end for i,_ in pairs(jBack) do if os.time()-jBack[i].lTime>jBack[i].cnt then jBack[i].cnt=jBack[i].cnt+1;jBack[i].lTime=os_time() if readBack(i,jBack[i]) then jBack[i]=nil end end end end --============================== runAction function readBack(devId,rData) local readStatus=logTrueFalse(rData.tstData,devId,"status") if jD[devId].dbgCmdTrue then if not jD[rData.inId].okAct or not readStatus then fibaro.debug(__TAG,setDispLine(rData.inId,"V"..rData.cnt-1)..oF..(readStatus and rData.cmd:gsub("verify","").." verified " or "verifying "..(rData.cmd=="verify" and "" or rData.cmd))..eF..vF..logStatus..eF..(readStatus and (jD[rData.inId].okAct and "" or qgF.." ( okAct={} might be used for further actions )"..eF) or "")) end end if readStatus then if jD[rData.inId].okAct then jAct[rData.inId..":vOk"]={act=json.decode(json.encode(jD[rData.inId].okAct)),lineState=true,actState="okAct",lTime=os_time(),type="Vo"} end return true end if rData.cnt>rData.uRpt then lastAct("DNP"..trueFalseColor["true"].."V"..rData.inId..""..qrF.."verify"..logStatus.." failed"..eF..")",rData.inId,fibaro.error) if jD[rData.inId].errAct then jAct[rData.inId..":vErr"]={act=json.decode(json.encode(jD[rData.inId].errAct)),lineState=true,actState="errAct",lTime=os_time(),type="Ve"};jBack[rData.inId]=nil;runAction(rData.inId..":vErr") else homeTts("Warning. Verification on line "..jD[rData.inId].lineNum.."has failed") fibaro.error(__TAG,setDispLine(rData.inId,"Ve")..yF..rData.cmd..(rData.cmd=="verify" and "" or cF.." and "..eF.."verify")..eF.."!"..logStatus..""..qrF.." has FAILED"..eF..qgF.." ( errAct={} might be used for further actions )"..eF) end return true else if (rData.cnt-1) % math.floor(rData.uRpt/2) == 0 and rData.cmd~="verify" and not readStatus then jAct[rData.inId..":Va"]={act={rData.actData[1],rData.actData[2]:gsub("!%b{}",""),""},lineState=true,actState="",lTime=os_time(),type="Va"} runAction(rData.inId..":Va") end return false end end --=================================== readBack function fibaro_call(devId,param,inId) if powerOutage and jDlist[devId.."zwave"] then return "fail" end if param=="poll" then devId=jDlist["pId"..jDlist[devId]] end local pArgs,cnt,args,cmd,i,data={},0,"",param:match("[%w_]+"),0,param:gsub("json%b[]",function(a) return a:gsub(",","`") end) local pHost=(devId:match("'(%w+)") or "local") for i in string.gmatch(data..",","(.-),") do cnt=cnt+1 if cnt>1 then if i:find("json%[") then i=i:match("json(%b[])"):sub(2,-2):gsub("`",",") end pArgs[#pArgs+1]=(tonumber(i) or i) end end if cmd=="setArmed" and args:find("&arg1=0") then selfi:apiPost(pHost,"http://"..jSlave[pHost].ip.."/api/devices/"..devId:match("%w+").."/action/setArmed",jSlave[pHost].access,json.encode({["args"]={false,jSlave[pHost].alarmPin}})) else selfi:apiPost(pHost,"http://"..jSlave[pHost].ip.."/api/devices/"..devId:match("%w+").."/action/"..cmd,jSlave[pHost].access, json.encode({["args"]=(#pArgs==0 and {pArgs} or pArgs)})) -- api_get(devId,urlEncode("/callAction?deviceID="..devId:match("%w+").."&name="..cmd..args,1),"noResp") end if jDlist[devId.."zwave"] then eCmd.dev=eCmd.dev+1 end return true end --======================= fibaro_call function subSign(mStr) return tostring(mStr):gsub("[%(%+%-%*/@&%$]%%", function (a) return a:gsub("%%","") end) end --============================== subSign function addSign(mStr) return tostring(mStr):gsub("[%(%+%-%*/@&%$]", function (a) return a.."%" end) end --==================================== addSign function execCmd(actData,actTime,devId,aType) if not getDelay(devId,actData,actTime) then return false end local tstData,cStatus,i,dId,sMode,cmdStr,devStr,dTime,dName="",true,0,0,{["enableScene"]=true,["disableScene"]=false},actData[2]:match("[%w_]+"),""..aType..""..devId:gsub("[%w_']+",function (a) return jN2s[a] or a end).."","","" runCmd=cmdStr iMsg=false 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 jD[devId].dbgCmdFalse then fibaro.trace(__TAG,setDispLine(devId,aType)..(logStatus:len()>5 and vF.."skip "..eF or "").." "..actData[2]:gsub("!%b{}",""):gsub(cmdStr,lpF.." "..cmdStr.." "..eF).." "..setCmdStr(actData)..(actData[2]:match("!%b{}") and cF.." and "..eF..lpF.." verify "..eF or "")..(logStatus:len()>5 and vF.."  if not "..logStatus..eF or "")) end return true end end --if actData[4] then if actData[2]:find("!%b{}") then actData[2],tstData,tmpNum=actData[2]:match("(.-)!(%b{})(.*)") tmpNum=(tmpNum:match("%d+") and tonumber(tmpNum:match("(%d+)")) or (cmdStr=="verify" and 1 or 4)) if not jBack[devId] then jBack[devId]={type=aType,cmd=cmdStr,lTime=os.time(),actTime=actTime,actData=actData,uRpt=tmpNum,cnt=1,inId=devId,tstData=tstData} end actData[2]=actData[2].."!"..tstData 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 act3:match("timer[%w+]?,%d+") then tmpStr,timer=act3:match("(%w+,)(%d+)");actData[3]="" jTime[tmpStr]=(timer~="0" and {actData,os_time()+tonumber(timer),devId..":"..tmpStr} or nil) lastAct(trueFalseColor[true]..devStr..act2.."{"..ygF.."*"..act1:gsub("|",","):gsub("([%W_']+)", function (a) return jN2s[a] or a end).."*"..eF.."}
"..(timer=="0" and grF.." "..tmpStr:sub(1,-2).." cancelled"..eF or vF.." by "..eF..grF..act3.."s"),devId) elseif actData[2]=="shutdown" then --hub.homeCenter.systemService.suspend() lastAct(trueFalseColor[true]..devStr.."shutdown{"..rF.."Currently not supported"..eF.."}
",devId) elseif actData[2]=="reboot" then ;tmpStr=(act1:len()>1 and act1 or "local");selfi:apiPost(tmpStr,"http://"..jSlave[tmpStr].ip:match("(.+):").."/api/service/reboot",jSlave[tmpStr].access,json.encode({["args"]={{}}})) elseif cmdStr=="verify" then mStr=logStatus;logTrueFalse(tstData,devId);tmpStr=logStatus;logStatus=mStr lastAct(trueFalseColor[true]..devStr.."verify"..tmpStr.."",devId) elseif actData[2]=="setProfile" then setProfile(act1,devId);lastAct(trueFalseColor[true]..devStr.."setProfile{"..act1..":"..jPrf[act1].."}",devId) 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:sub(1,-2)..") '"..cutStr(ctlChar(act1),15).."','*"..ctlChar(act3).."*')").."",devId) elseif cmdType=="rmd" then lastAct(trueFalseColor[logReminder(actData[1],act2,actData[3]:gsub("%b~~",""),devId)]..devStr..act2.."{"..grF.."*"..(act2:find("del") and ".."..act3..".." or act3).."*"..eF.."}",devId) elseif cmdType=="tts" then homeTts(act3,act1);lastAct(trueFalseColor[true]..devStr.."tts{"..grF.."\"*"..act3.."*\""..(act1:len()>1 and cutStr(","..act1,15) or "")..eF.."}",devId) elseif cmdType=="prt" then tmpStr=trueFalseColor[true]..devStr..yF..actData[2]..eF.."("..grF..act3..eF..")" prtFunc[act2][1]((act1:len()>0 and act1 or __TAG),setDispLine(devId,aType)..(logStatus:len()>5 and vF.."if "..(jD[devId].status==2 and "not" or "")..logStatus.." then "..eF or "")..elseAct..tmpStr:gsub("^.-","")..(logStatus:len()>5 and vF.." end"..eF or "")) lastAct("DNP"..trueFalseColor[true]..devStr..prtFunc[act2][2]..act2.."{"..grF.."*"..act3.."*"..eF.."}",devId) elseif cmdType=="drift" then updateTimeDrift(to_number(act3)-timeDrift);timeDrift=to_number(act3);lastAct(trueFalseColor[true]..devStr..act2.."{"..timeDrift.."}",devId) elseif cmdType=="vac" then setVacation(act2,devId);lastAct(trueFalseColor[true]..devStr.."vac{"..act2:gsub("vac","").."}",devId); if not initMode then onTime();dispInfo() end elseif jCmd[cmdStr].f1:find("[#<>$]") then act1=spKeyword(actData[1],devId):gsub(" ","");cmdAct="" cmdId=trueFalseColor[true] inCmd=act2:match("[%w_]+") didCnt=0 for dId in act1:gsub("|"," "):gmatch("%S+") do iMsg=false;didCnt=didCnt+1 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..dName..",";cmdAct=trueFalseColor[true]..devStr..actData[2]:gsub(inCmd..",",inCmd..grF..","):gsub("}{","},{")..eF.."cmdId"..eF..eF elseif actData[2]:find("setKeyword") then cmdAct=trueFalseColor[setKeywordValue(devId,dId,act2:gsub("setKeyword,",""))]..devStr..act2..eF.."cmdId "..eF..eF cmdId=cmdId..(iMsg and rF..dName..eF or dName).."," elseif actData[2]:find("setIcon") then iconId=act2:gsub("setIcon,","") cmdAct=trueFalseColor[setIcon(devId,dId,iconId,actData[2])]..devStr..act2:gsub(inCmd..",",inCmd..grF..",")..""..(getIconName(devId,dId,iconId,(dId:find("_") and "scene" or "device")) or "not found")..""..eF.."cmdId "..eF..eF cmdId=cmdId..(iMsg and rF..dName..eF or dName).."," elseif actData[2]=="setGlobal" then cmdAct=trueFalseColor[fibaro_setGlobal(dId,act3)]..devStr..act2..", \""..grF..act3..eF.."\" cmdId"..eF..eF cmdId=cmdId..(iMsg and rF..setKeyword(dName)..eF or setKeyword(dName)).."," elseif actData[2]=="setStateValue" then if jD[dId].stateValue~=act3 then jD[dId].stateValue=act3;cmdId=cmdId..lastCmd(act2,dId,dName).."," cmdAct=trueFalseColor[true]..devStr..act2..","..act3.."cmdId"..eF..eF end elseif actData[2]=="setStateFormula" then jD[dId].stateValue=actData[3];cmdId=cmdId..lastCmd(act2,dId,dName)..","; cmdAct=trueFalseColor[true]..devStr..act2..",*"..actData[3]"*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..lastCmd(act2,dId,dName)..",";cmdAct=trueFalseColor[true]..devStr..act2..", #"..act3.."cmdId"..eF..eF end elseif actData[2]=="setState" then setNewState(act3,dId);cmdId=cmdId..lastCmd(act2,dId,dName)..","; cmdAct=trueFalseColor[true]..devStr..act2..","..act3.."cmdId"..eF..eF elseif actData[2]:find("pressButton") then tmpNum=actData[2]:gsub("pressButton,","") if initMode then getButtonName(dId,tmpNum) end api.get("/plugins/callUIEvent?deviceID="..dId.."&elementName="..(tonumber(tmpNum) and jDid[dId].button[tmpNum] or tmpNum).."&eventType=onReleased&value=null") cmdId=cmdId..lastCmd(act2,dId,dName)..",";cmdAct=trueFalseColor[true]..devStr..act2.."cmdId"..eF..eF elseif actData[2]:find("setSlider") then tmpNum,sVal=act2:match("setSlider,(%S+),(%d+)") if initMode then getSliderName(dId,tmpNum) end api.get("/plugins/callUIEvent?deviceID="..dId.."&elementName="..(tonumber(tmpNum) and jDid[dId].slider[tmpNum] or tmpNum).."&eventType=onChanged&value="..sVal) cmdId=cmdId..lastCmd(act2,dId,dName)..",";cmdAct=trueFalseColor[true]..devStr..act2.."cmdId"..eF..eF elseif actData[2]:find("runAction") then act3=(act2:match(",%d+") and act3:match("%w+")..actData[2]:match(",.*") or act3);act2=act2:match("%w+") cmdId=cmdId..dName..",";cmdAct=trueFalseColor[execAct(act2,act3,dId,devId)]..devStr.."runAction"..grF.."("..act3..") in jM"..eF.."cmdId"..eF..eF elseif actData[2]:find("rptAction") then --runCmd="repeat"; cmdId=cmdId..dName.."," cmdAct=trueFalseColor[rptAct(act2,act3,dId,devId)]..devStr..cmdStr.."("..grF..(act3:find(",") and act3:gsub(",","[").."]" or act3)..rptAct(act2,act3,dId,devId,"execCmdlogInfo")..") in jM"..eF.."cmdId"..eF..eF elseif cmdType=="time" then cmdId=cmdId..dName.."," cmdAct=trueFalseColor[true]..devStr..act2..","..setTimeSpec(cmdStr,act3,dId).."cmdId"..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 kill_scene(dId); cmdId=cmdId..dName..",";cmdAct=trueFalseColor[true]..devStr..actData[2].."cmdId"..eF..eF elseif ("emitEvent"):find(actData[2]) then fibaro.emitCustomEvent(dId) cmdId=cmdId..dId..",";cmdAct=trueFalseColor[true]..devStr..actData[2].."cmdId"..eF..eF elseif actData[2]:find("armAlarm") then mStr= (jAlarm["$"..dId:gsub("%$","")] and fibaro.alarm(jAlarm["$"..dId:gsub("%$","")],actData[2]:gsub("Alarm","")) or tonumber(dId) and fibaro.alarm(tonumber(dId),actData[2]:gsub("Alarm","")) or dId=="all" and fibaro.alarm(actData[2]:gsub("Alarm","")) ) cmdId=cmdId..lastCmd(act2,dId,dName)..",";cmdAct=trueFalseColor[true]..devStr..actData[2].."cmdId"..eF..eF elseif cmdType=="call" then fibaro_call(realId(dId),act2,devId); cmdId=cmdId..lastCmd(act2,dId,dName)..grF.."," cmdAct=trueFalseColor[true]..devStr..act2:gsub(inCmd..",",inCmd..grF..","..eF).."cmdId"..eF..eF else cmdAct=""..devStr..rF.."Unsupported Command("..act1..",'"..act2.."', "..act3..")" end if iMsg then fibaro.error(__TAG,"jM{"..jD[devId].lineNum.."} "..iMsg) end end -- for dId in string.gmatch(string.gsu lastAct(cmdAct:gsub("cmdId","{"..cmdId:sub(1,-2)..eF..")"),devId,iMsg and fibaro.error or nil) else lastAct(""..devStr..rF.."Unsupported Command("..act1..",'"..act2.."', "..act3..")",devId,fibaro.error) end return true end --=================================== execCmd function sendCmd(actDataRef,actTime,devId,aType) local i,cmdData,actData=0,{{""},{""}},json.decode(json.encode(actDataRef));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]=(tostring(actData[i]):match("(.-);else;") or actData[i]) cmdData[2][i]=(tostring(actData[i]):match(";else;(.+)") or actData[i]) end cmdData[1][4]=false;cmdData[2][4]=false;-- getLogStatus(actData[4],devId) if logTrueFalse(actData[4],devId,"status") then return execCmd(cmdData[1],actTime,devId,aType) else elseAct=vF.." else "..eF return execCmd(cmdData[2],actTime,devId,aType) end end --=================================== sendCmd function getDelay(inId,actData,actTime) if os_time()-actTime<0 then return false end 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][refAct] or tonumber(i)<1 then sceneAbort("(81) Error in "..wF.."runAction".." command! Action number "..rF..i.." not found in the "..wF..act2exec.." table on jM{"..jD[inId].lineNum.."} line:
"..wF..json.encode(jM[jD[inId].lineNum]):gsub(refAct.."\"",rF..refAct..eF.."\"")..eF,{inId,cmd,refAct},jD[devId].lineNum) return "fail" end aTable[#aTable+1]=jD[inId][refAct][tonumber(i)] end if logInfo then return true end if #aTable==0 then aTable=json.decode(json.encode(jD[inId][refAct])) end jAct[inId..":run"]={act=aTable,lineState=true,lTime=os_time(),type="R"} return true --,actState=refAct else sceneAbort("(80) Error in "..wF.."runAction"..eF..". Table "..rF..act2exec.." not found in jM{"..jD[inId].lineNum.."} line:
"..json.encode(jM[jD[inId].lineNum]),{"runAction",act2exec,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 jD[inId] then sceneAbort("(75) Error in "..wF.."rptAction"..eF.." command. jM{"..wF..inId..eF.. "} line not found",{"rptAction",inId},jD[devId].lineNum) return "fail" end if not act2exec:match("(%w+[,]?[%d+]?)"):find(act2exec) then sceneAbort("(108) Syntax error in \""..wF..act2exec..eF.."\" parameter for "..wF.."rptAction"..eF.." command.
Format is:"..wF.."\"(action table name) [,command # in the table]\""..eF,{"rptAction",act2exec},jD[devId].lineNum) return "fail" end if not rptParam[2] then sceneAbort("(101) Error in "..wF..cmd.." command 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("(100) Error in "..wF.."rptAction command. "..rF..act2exec.."{} table not found in jM{"..jD[inId].lineNum.."} line:
"..wF..json.encode(jM[jD[inId].lineNum])..eF,{"rptAction",inId},jD[devId].lineNum) return "fail" end if rptParam[3] and not jD[inId][act2exec][rptParam[3]] then sceneAbort("(103) Error in "..wF.."rptAction. Command number "..irF..rptParam[3].." not found in the "..wF..act2exec.."{} table in jM{"..jD[inId].lineNum.."} line:
"..wF..json.encode(jM[jD[inId].lineNum]):gsub(act2exec,rF..act2exec..eF)..eF,{inId,"rptAction",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)),actState=act2exec,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)),actState=act2exec,type="P"..tonumber(rptParam[1])-i+1,dTime=(i==rptParam[1] and 0 or rptParam[2])} end return true end --=================================== rptAct function kill_scene(sId) if not sId:find("'") then fibaro.scene("kill",{tonumber(sysId(sId))}) return end local pHost=sId:match("'(%w+)") if jSlave[pHost].hc3platform then selfi:apiPost(pHost,"http://"..jSlave[pHost].ip.."/api/scenes/"..sysId(sId).."/kill",jSlave[pHost].access,nil,"End of file") else selfi:apiPost(pHost,"http://"..jSlave[pHost].ip.."/api/scenes/"..sysId(sId).."/action/stop",jSlave[pHost].access,nil,"End of file") end end --================================ kill_scene function start_scene(sId,sParam) if not sId:find("'") then fibaro.scene("execute",{tonumber(sysId(sId))}) return end local i,sArg,bPath,pHost=0,{},"%b{}",sId:match("'(%w+)") if type(sParam)=="table" then tmpStr="" for i=1,#sParam do tmpStr=tmpStr.."{"..sParam[i].."}" end; sParam=tmpStr end if jSlave[pHost].platform~="HC2" or #sParam<1 then sArg={{}} else 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 end if jSlave[pHost].hc3platform then selfi:apiPost(pHost,"http://"..jSlave[pHost].ip.."/api/scenes/"..sysId(sId).."/execute",jSlave[pHost].access,json.encode({["args"]=sArg})) else selfi:apiPost(pHost,"http://"..jSlave[pHost].ip.."/api/scenes/"..sysId(sId).."/action/start",jSlave[pHost].access,json.encode({["args"]=sArg}),"End of file") end end --================================ start_scene function setKeywordValue(devId,key,value) if json.encode(userKeyWord[1]):find("\""..key.."\"") then for i=1,#userKeyWord[1] do if userKeyWord[1][i]==key then userKeyWord[2][i]=value break end end return true else if not initMode then iMsg=rF.."User's Keyword "..""..key..eF.." not found -> no actions" else sceneAbort("(122) User's Keyword "..""..key..eF.." not found
Available user keywords are:
"..ygF..json.encode(userKeyWord[1]):gsub("[\"%[%]]",""):gsub(",",", ")..eF,{"setKeyword,"..key},jD[devId].lineNum) end return false end end --================== setKeywordValue function getIconName(devId,inId,iconId,iType,jmIcon) local cnt,host,iList,i=0,(inId:match("'(%w+)") or "local"),"",0 if jSlave[host].icons[iType][iconId] then return jSlave[host].icons[iType][iconId] end jmIcon=jmIcon or "setIcon,"..iconId if not initMode then iMsg=rF.."Icon "..wF..iconId..eF.." for "..iType.." "..dColor[jDid[inId].idType]..inId..eF.." not found -> no actions" else for i=1,10000 do if jSlave[host].icons[iType][tostring(i)] then cnt=cnt+1; iList=iList..whF..i..eF.."="..jSlave[host].icons[iType][tostring(i)].." "..(cnt%9==0 and "
" or "") end end sceneAbort("(118) Icon "..wF..iconId.."
for "..iType.." "..dColor[jDid[inId].idType]..inId..eF.." not found.
Available icons for "..iType.."s are:
"..ygF..iList..eF,{inId,jmIcon},jD[devId].lineNum) end return false end --================================ getIconName function setIcon(devId,inId,iconId,jmIcon) local pData,path,pHost={},(inId:sub(1,1)=="_" and "/scenes/" or "/devices/")..sysId(inId),inId:match("'(%w+)") or "local" if not getIconName(devId,inId,iconId,(path:find("device") and "device" or "scene"),jmIcon) then return false end pData=api_get(inId,path) if path:find("device") then if not tonumber(iconId) then iconId=jSlave[pHost].icons["device"][iconId] end pData={properties={deviceIcon=tonumber(iconId)}} elseif path:find("scene") then if jSlave[pHost].hc3platform then if tonumber(iconId) then iconId=jSlave[pHost].icons["scene"][iconId] end pData.icon=iconId else if not tonumber(iconId) then iconId=jSlave[pHost].icons["scene"][iconId] end pData={iconID=tonumber(iconId)} end end selfi:apiPut(pHost,"http://"..jSlave[pHost].ip.."/api"..path,jSlave[pHost].access,pData,"End of file") return true end --================================ setIcon 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(inId,"/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()};errorResp.properties[nProperty]="networkError" 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 getSceneStatus(inId) local pHost,fStatus="local",true if inId:find("'") then pHost=inId:match("'(%w+)") end if hc3Platform:find(" "..jSlave[pHost].platform.." ") then return api_get(inId,"/scenes/"..sysId(inId),"getSceneStatus")["enabled"] else fStatus=api_get(inId,"/scenes/"..sysId(inId),"getSceneStatus")["runConfig"] if fStatus=="DISABLED" then return false else return true end end end --================================== getSceneStatus function sceneEnabled(inId,state) local tSlot,runConfig,fStatus={[true]=jD[inId].tstRateOrg,[false]=-10},{[true]="TRIGGER_AND_MANUAL",[false]="DISABLED"},getSceneStatus(inId) if jD[inId] and atHome then 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 if inId:find("'") then pHost=inId:match("'(%w+)") if hc3Platform:find(" "..jSlave[pHost].platform.." ") then selfi:apiPut(pHost,"http://"..jSlave[pHost].ip.."/api/scenes/"..sysId(inId),jSlave[pHost].access,{["enabled"]=state}) else fStatus=api_get(inId,"/scenes/"..sysId(inId),"sceneEnabled")["runConfig"] if fStatus==runConfig[state] then return false end selfi:apiPut(pHost,"http://"..jSlave[pHost].ip.."/api/scenes/"..sysId(inId),jSlave[pHost].access,{id = sysId(inId), runConfig = runConfig[state]},"End of file") end else api.put("/scenes/"..sysId(inId),{enabled=state}) end return true end --=================================== logVal function logVal(val,inId,vCol) if type(val)=="table" then val="TBR" end 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 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 --=================================== lastAct function lastAct(actStr,inId,errCode) if #actStr<5 then return end local fPrint=(errCode and errCode or fibaro.trace) if #jD[inId].lAct==dataRecord.commands then table.remove(jD[inId].lAct,1) end if actStr:match("!%b{}") then local oldStatus=logStatus logTrueFalse(actStr:match("!(%b{})"),inId,"statusnomark") actStr=actStr:gsub("!%b{}","!"..logStatus) logStatus=oldStatus end local prtStr=actStr:gsub("%b**",function(a) return a:gsub("*","") end):gsub("!%b{}","")..(actStr:match("!%b{}") and (runCmd=="verify" and "" or vF.." and "..yF.."verify"..eF..actStr:match("!(%b{})"):gsub(":value","")..eF) or "") prtStr=prtStr:gsub(runCmd,yF..runCmd..eF) actStr=actStr:gsub("%b**",function(a) return cutStr(a:gsub("*",""),25) end) if actStr:sub(1,3)=="DNP" then actStr=actStr:sub(4) else if jD[inId].dbgCmdTrue then logStatus=(elseState and "" or logStatus) if not jD[inId].dbgTrueState then if jRun[inId].lineState then fPrint(__TAG,setDispLine(inId,jRun[inId].lineState.aType)..jRun[inId].lineState.txt);gData.inIf="  " end if jRun[inId].actState then fPrint(__TAG,setDispLine(inId,jRun[inId].actState.aType)..jRun[inId].actState.txt) ;gData.inIf=gData.inIf.."  "end jRun[inId]={} end fPrint(__TAG,setDispLine(inId,(prtStr:match("(%S+)") or "U"))..elseAct..prtStr:gsub("^.-","")..(logStatus:len()>5 and vF.." if"..(gData.lastLog and "" or " not")..logStatus..eF or "")) end end jD[inId].lAct[#jD[inId].lAct+1]=sColor[jD[inId].status+1].."
"..os.date(" %d %b %H:%M:%S",os_time()).." "..jD[inId][jD[inId].property].value.." "..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 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 lastCmd(cmd,inId,dName) local host=inId:match("'(%w+)") if host and jSlave[host].online==false then return lpF..dName.." [networkError]"..eF end if jDlist["lCmd"..inId]==cmd then return lpF..dName..eF else jDlist["lCmd"..inId]=cmd return ygF..dName..eF end end --=============================== lastCmd function logEvent(inId) if not jDlist["pId"..jDlist[inId]] or inId:find("'") 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) if not slaveDeadRpt and inId:find("'") then return end 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~="nack" then dReason=(dReason=="true" and "Dead" or "wkUp")end if dReason=="wkUp" then 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 jF[inId].enabled=api_get(inId,"/devices/"..sysId(inId))["enabled"] if dReason=="Dead" 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 dReason~=gData.transfer then tmpStr=tostring(api_get(inId,"/devices/"..sysId(inId))["properties"]["dead"]) if not jLog[pId] or jLog[pId]~=deadOnMsg or pId=="none" or rptAllDead then jLog[pId]=deadOnMsg if deadNote then homeTts(subject:gsub("%b()","").."! "..findNameRoom(inId,1).. ". ".. deadOnMsg) end if dReason=="Dead" then fibaro.error(__TAG,dColor[jDid[inId].idType]..findNameRoom(inId,5)..eF.."["..rF.."dead"..eF.."] ➯ parent and children are disconnected."..(wakeUpRate==0 and " AOQ wakeUpDead action disabled by user [wakeUpRate=0]" or (jF[inId].enabled and "" or " Device disabled by user, AOQ wakeUpDead action suspended."))..eF) else fibaro.trace(__TAG,dColor[jDid[inId].idType]..findNameRoom(inId,5)..eF.."["..sColor[2].."awake"..eF.."] ➯ "..ygF.."parent and children are connected. "..eF) end if deadNote and tmpStr ~=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[inId].reason)) end --=============================== logDead function wakeUpDead() gData.wakeUpCall=os_time() local i,pHost=0,"local" for i,_ in pairs(jF) do if jF[i].dead=="true" and jF[i].enabled then pHost=(i:match("'(%w+)") or "local") -- api_get(i,urlEncode("/callAction?deviceID="..i:match("%w+").."&name=wakeUpDeadDevice",1),"no resp") selfi:apiPost(pHost,"http://"..jSlave[pHost].ip.."/api/devices/"..i:match("%w+").."/action/wakeUpDeadDevice",jSlave[pHost].access,json.encode({["args"]={{}}})) end end end --===================== wakeUpDead function qaId(idx) local h,id,name=idx:match("('%w+)"),idx:match("[&]?(%d+)(%w+)");id =h and id..h or id.."" return id,name end function realId(idx) -- return tostring(idx):gsub(">"," "):match("^[_$&@]?(%S+)") end --======return @ included return tostring(idx):match("^[_$&@]?([%w_']+)") end --========================= function sysId(idx) return idx:match("[_]?([%w_]+)") end --=== return ID as a number in system function tableId(idx) -- return tostring(idx):gsub(">"," "):match("[@]?(%S+)") end --============return w/o @ return tostring(idx):match("[_$&]?[%w_']+") end --============================== function jDtableId(idx) return tostring(idx):match("[_$&]?[%w_']+>%w+") end --======return w/o @ and > id ================== function jsId(idx) return realId(idx)..""..(jN2s[idx] or "").."" end --======================= setTickTime =========================================== 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 checkAndOr(sLine,inId,sErr,acTbl) local k=0 for k in sLine:gmatch("%b}{") do if k:find("and") then sceneAbort("(106) Format syntax error in "..wF.."{..."..k:gsub(" and ",rF.." and "..eF).."...}"..eF.." statement.
The "..wF.."and"..eF.." statement is prohibited between the brackets. Only "..wF.."or"..eF.." statement 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 the 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 sLine=sLine:gsub("}([%s+]?or[%s+]?){","}{"):gsub("%s+|%s+","|"):gsub("[%s+]?if[%s+]?{","{"):gsub("%s+and%s+","|"):gsub("[%s+]?when[%s+]?{","={"); sLine=sLine:gsub(" or ","}{") for i in sLine:gmatch("([$&@]?[%w_':]+[<=>])") do if not (" w= d= m= "):find(i) and not i:find(":") and not i:find("@") then sLine=sLine:gsub(i,i:sub(1,-2)..":value"..i:sub(-1,-1)) end end return sLine end --=================================== checkAndOr function checkAct4(actStr,inId,acTbl) local k,i,j,dNum,lNum,lSeen,pName,o,sErr="",0,0,0,0,{},"","",lgF.."\"[if"..wF.."/"..eF.."when] { "..wF.."ID"..eF..":[property][<=>][value]}\"
e.g. \"if "..wF.."{"..eF.."999=true "..wF.."and"..eF.." 888:lastBreached>10"..wF.."} or {"..eF.."$hour=6 "..wF.."and"..eF.." &gVar:valueModified>20"..wF.."} or {$maxPwr<@`boiler`>power}"..eF.."\"" for i=1,4 do if actStr[i] and actStr[i]:match("%b{}") then actStr[i]=checkAndOr(actStr[i],inId,sErr,acTbl) lSeen[#lSeen+1]=actStr[i]:match("!(%b{})") or actStr[i] end end if #lSeen==0 then return end for j=1,#lSeen do lSeen[j]=spKeyword(lSeen[j],inId) tmpStr=lSeen[j]:match("%b{}") for i in tmpStr:gsub("%d+[:]%d+",function (a) return a:gsub(":",";") end):gmatch("%b::") do if not i:find("|") then sceneAbort("(130) Syntax error in "..wF..acTbl..eF..". Unrecognized statement format in "..wF..tmpStr..eF.."
Syntax is "..sErr,{acTbl,lSeen[j]},jD[inId].lineNum) end end if tmpStr:find(":<=>") then 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:gsub("modif",rF.."m"..eF.."odif")..eF.."\". Missing capital letter '"..rF.."M"..eF.."'
Syntax is "..eF..wF.."\"device property"..lgF.."Modified"..eF..eF.."\"
e.g. "..lgF.."\"{999:powerModified<60 and 888:valueModified>10}\"",{i,acTbl},jD[inId].lineNum) end if not i:find(":") or not i:find("[<=>]") then sceneAbort("(3) Format syntax error in \""..wF..acTbl.." "..lSeen[j]:gsub(i,rF..i..eF)..eF.." Missing property comparison.
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 --for i in tmpStr:gmatch("(.-)[|]") do end --if tmpStr:find(":<=>") then ::nextLseen:: end end --====================== checkAct4 function checkFormat(k,inId,acTbl) if json.encode(k):find("@[&$]") then tmpStr=json.encode(k):match("@[$&][%w_']+") sceneAbort("(92) 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>6 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,"\""..tostring(k[#k]).."\""},jD[inId].lineNum) end for i=1,4 do if k[i] and k[i]:match("%b[]") and not k[i]:match("json(%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 tmpStr=json.encode(k); for i in string.gmatch(tmpStr,"json(%b[])") do st,cVal= pcall(json.decode,i:gsub("\\",""):sub(2,-2)) if not st then sceneAbort("(95) jSON Syntax error in "..wF.."json"..i:gsub("\\","")..eF.."
"..cVal,{"json"..i:gsub("\\","")},jD[inId].lineNum) end end if k[2]=="reboot" then tmpStr= k[1]:len()>1 and k[1] or "local" if not jSlave[tmpStr] or jSlave[tmpStr].user=="xxx" then sceneAbort("(107) Incorrect login to reboot "..wF..k[1]..eF.." controller.
Please add correct login info in user_data file:
"..wF.."jSlave={ "..(tmpStr=="local" and "home" or tmpStr).."={user=\"xxx\", passwd=\"xxx\", ip=\"192.168.10.1\"} }",{k[1],k[2]},jD[inId].lineNum) end end if k[2]=="setGlobal" then tmpStr=k[1]:gsub("&",""); k[1]="" for i in tmpStr:gsub("|"," "):gmatch("%S+") do k[1]=k[1].."&"..i.."|" end k[1]=k[1]:sub(1,-2) 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 jCmd[cmdStr].f3=="~" and k[3]:len()>0 and not k[3]:match("%d+") then tmpNum=k[3] if tmpNum:find("timer") then tmpNum=k[3]:match(",(%w+)") end if not tonumber(tmpNum) then assertType("(120)",k[3]:gsub(tmpNum,qrF..tmpNum..eF),cmdStr,"number",inId) end end if k[5] and type(k[5])~="boolean" then assertType("(63)",k[5],cmdStr,"boolean",inId) 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()",""):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(",") and not k[2]:find("!%b{}") then sceneAbort("(11) Wrong number of parameters in '"..wF..k[2].."' action.
Command does not 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[4] and k[4]:find("[{}]") then local _,tNum=k[4]:gsub("[{}]","") if tNum % 2 >0 then sceneAbort("(131) Syntax error in "..wF..k[2]..eF.." at statement"..wF..k[4]..eF.." missing curly brackets.
Syntax is "..wF.."\"{<conditional format>}\""..eF,{k[2],k[4]},jD[inId].lineNum) end end if k[2]:find("!") and k[2]:find(":") and k[2]:find("[%=%<%>]")and not k[2]:find("%b{}") then sceneAbort("(90) Syntax error in statement "..wF..k[2]:match("%w+").."!"..eF.." command verification \""..wF..k[2]:match("!(.*)"):gsub("{",rF.."{"..eF):gsub("}",rF.."}"..eF).."\"
Syntax is "..wF.."\"<cmd>!{"..wF.."<conditional format>}\""..eF,{k[2]:match("!(.*)")},jD[inId].lineNum) 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 assertType(eNum,param,cmd,rType,inId) sceneAbort(eNum.." Assetion failed: Expected "..yF..rType..eF.." type of the value '"..wF..tostring(param)..eF.."' in "..wF..cmd..eF,{cmd,""..(type(param)=="string" and "\""..param:gsub("<.->","").."\"" or tostring(param):gsub("<.->",""))},(jD[inId] and jD[inId].lineNum or inId)) end 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]=(tostring(actData[i]):match("(.-);else;") or actData[i]) cmdData[2][i]=(tostring(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) local i=0 for i=1,3 do if k[i] then k[i]=tostring(k[i]) end end if #k<2 or type(k)~="table" then sceneAbort("(112) Syntax error in "..wF..acTbl..eF.." table. Syntax is:"..wF.."
"..acTbl.."={\"devID\",\"command\",\"...\"}"..eF,{acTbl},jD[inId].lineNum) end if type(k[1])=="function" then sceneAbort("(125) First parameter syntax error "..wF..json.encode(k[1])..eF.." in "..wF..k[2]..eF.." command. Syntax is:"..wF.."
"..acTbl.."={\"devID\",\"command\",\"...\"}"..eF,{json.encode(k[1]),k[2]},jD[inId].lineNum) end if not k[1]:match("%b{}") and 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]:match("%b{}") and k[i]:match("%b{}"):find("[<=>]") then nK[4]=k[i] else if nK[3]=="" then nK[3]=k[i] end 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 -- if #k==2 then k[3]="" else end --===================================== setOrder function sceneAbort(fMsg,str2find,jLine,prtType) --131 local errIn,mStr,fPrint="","",(prtType and prtType or fibaro.error) 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("\\",""):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("json%b{}",function(a) return a:gsub("{","["):gsub("}","]") end) errMsg=errMsg:gsub(str2find[i],rF..subStr[i]..eF) end if type(jLine)=="number" and jM[jLine][2] then tmpStr=jM2n[jLine] or json.encode(jM[jLine]):sub(1,20) end mStr=" "..rF.." "..os.date("[%d %b %H:%M:%S]",os_time()).." AOQ"..plugin.mainDeviceId..supVer..errIn..":~: "..tmpStr.."
"..wF..errMsg:gsub("\":","\"=").."

"..fMsg:gsub("e%.g%.","e.g.").."" selfi:updateProperty("value", true) if fPrint==fibaro.warning then mStr=mStr:gsub("

","") mStr=mStr:gsub(rF.."","") end if initMode then printLabel("progressLabel",mStr.."

"); if not infoErr then fibaro:abort() else infoErr=false end else printLabel("progressLabel",mStr);fPrint(__TAG,mStr) errCnt=0; runErrBuf[1]="fixedMenu"..mStr;dispInfo() return false end end --======================================== sceneAbort function checkData(dParam,inId,jmLine,lineNum) local dParamFound,actFound,fStatus,i,j,i1,j1=false,false,true,0,0,0,0 if jDlist["mId"..realId(inId)] then sceneAbort("(37) Master device ID "..wF..jsId(inId)..eF.." prohibited to use.",{realId(inId)},lineNum) end 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 assertType("(38)",dParam[task.rule[i]],task.rule[i],task.ruleType[i],inId) end end -- 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 format and syntax of the line number "..lineNum.." in jM{} array.",(type(dParam[j])=="boolean" and {tostring(dParam[j]),j} or 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 l,k in pairs(j) do setOrder(k,inId,i1);cmdFormat(j[l],inId,i1);checkAct4(k,inId,i1) end else setOrder(j,inId,i1);cmdFormat(j,inId,i1); checkAct4(j,inId,i1)end end end -- for i1,j in pairs(dParam) do if inId:sub(1,1)=="_" 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("(34) Property is not availabe for "..jD[inId].idType.." "..wF..findNameRoom(inId,2)..eF.."
Available options are:
1."..wF.." state=\"value=...\""..eF.."
2. Do not define any "..wF.."state"..eF.." or "..wF.."property"..eF,{"property",inId},lineNum) end for i=1,#task.prop do if dParam[task.prop[i]] then infoErr=true; sceneAbort("(22) While porperty criteria defined, redundant "..(task.prop[i]:find("State") and "state " or "table array ")..wF..task.prop[i]..eF.." has found and "..qrF.." won't be used."..eF.."
Accpted states and table arrays for property:
"..lgF.. table.concat(task.propAct," ")..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 errorResp.properties[dParam.property]=nil --"networkError" if not jD[inId][dParam.property] 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 '"..wF.."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 not inId:find("[$&]") and dParam.state:find("alarm") and not jAlarm[sysId(inId)] then sceneAbort("(105) 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 not inId:find("[$&]") and not dParam.state:find("alarm") and not jD[inId][dParam.state:match("(%S-)[=<>]")] 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 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(inId,dataType) local i,pStr,lData=0,"",(inId:find(demoId) and apiDemo[dataType] or api_get(inId,"/devices/"..sysId(inId))[dataType]) if not lData then return "
- "..dataType.." not found" end for i,_ in pairs(lData) do if not notProperty:find(i) then pStr=pStr.."
- "..i end end return pStr end --========================= getIdData function fibaro_setGlobal(varName,value) local vName=setKeyword(varName):gsub("&","");value=tostring(value) inId="&"..vName if jDp[inId] then if not jGvar[inId] then jGvar[inId]="true" end if jDid[inId].enumValues and not json.encode(jDid[inId].enumValues):find("\""..value.."\"") then iMsg=rF.."Value "..eF..value..rF.." not in enumerated list "..eF..json.encode(jDid[inId].enumValues)..rF.." of "..jDp[inId].value.type..eF.." &"..vName..eF.." -> no actions" if initMode then sceneAbort("(116) Global "..wF..inId..eF.." is Enumerated variable. Value [ "..wF..value..eF.." ] is not accepted.
Enumerated values are:
"..ygF..json.encode(jDid[inId].enumValues)..eF,{inId,value},jD[inGid].lineNum) end return false end if jQaGv:find("&"..vName..",") then if vName:match("(%d+%w+)") and vName:find("'") then putQaVar(vName,value) elseif vName:match("(%d+%w+)") then fibaro.call(tonumber(vName:match("[&]?(%d+)")),"setVariable",select(2,qaId(vName)),value) else selfi:setVariable(vName,value) end return true end --if jQaGv:find("&"..vName..",") if vName:find("'") then local pHost=vName:match("'(%w+)") selfi:apiPut(pHost,"http://"..jSlave[pHost].ip.."/api/globalVariables/"..sysId(vName),jSlave[pHost].access,{["value"]=value}) jDp[inId].value.value=value jDp[inId].value.mTime=os_time() return true end if jVar[vName] then if tostring(jVar[vName])~=tostring(value) then jVar[vName]=value;jVar[vName.."lTime"]=os_time() jDp[inId]={value={value=jVar[vName],mTime=jVar[vName.."lTime"],type="Local",isEnum=false,enumValues={}}} if global4local[1] then fibaro.setGlobalVariable(global4local.gVarName,json.encode(jVar)) end end return true end fibaro.setGlobalVariable(vName,value) return true end -- if jDp[inId] then iMsg=rF.."Global "..eF.." "..inId..eF..rF.." not found"..eF.." -> no actions" if initMode then sceneAbort("(33) Unable to set global variable "..wF..inId..eF,{inId},jD[inGid].lineNum) end return false end --============================= fibaro_setGlobal function getQaGlobals(qData,h) local i,j,k,qId for i,j in ipairs(qData) do if j.properties.quickAppVariables then qId=j.id:match("%d+") if tonumber(j.id:match("%d+"))==plugin.mainDeviceId and h=="" then qId="" end for _,l in ipairs(j.properties.quickAppVariables) do inId="&"..qId..l.name..h jQaGv=jQaGv..inId..", " jDp[inId]={value={value=tostring(l.value):sub(1,200),mTime=os.time(),type=gVarType[4]}} jDid[inId]={};jDid[inId].room="Variable";jDid[inId].name=jDp[inId].value.type jDlist[inId]="GV";jDlist["cmd"..inId]="setGlobal" end if json.encode(j.properties.quickAppVariables):len()>5 then jQaGv=jQaGv.."
" end end end jQaGv=jQaGv.."
" end --============================= getQaGlobals function putQaVar(fullName,value) local inId,gName=qaId(fullName) local i,j,qData,pHost=0,0,"",inId:match("'(%w+)") qData=api_get(inId,"/devices/"..sysId(inId).."/properties/quickAppVariables")["value"] for j,i in ipairs(qData) do if i.name==gName then qData[j].value=value break end end selfi:apiPut(pHost,"http://"..jSlave[pHost].ip.."/api/devices/"..sysId(inId),jSlave[pHost].access,{["properties"]={quickAppVariables=qData}}) end --============================= putQaVar function getQaVar(inId,gName,lcl) if not inId or not gName then return false end local j,qData=0,api_get(inId,"/devices/"..sysId(inId).."/properties/quickAppVariables") for _,j in ipairs(qData.value) do if j.name==gName then jDp["&"..(lcl or sysId(inId))..gName..(inId:match("('%w+)") or "")].value={value=tostring(j.value),mTime=qData.modified,type=(lcl and gVarType[3] or gVarType[4])} return tostring(j.value) end end return "null" end --============================= getQaVar function fibaro_getGlobalValue(varName,gType) if varName:sub(1,1)=="$" then return "keyword" end vName=setKeyword(varName):gsub("&","") if vName:sub(1,1)=="%" then return "&"..vName end local inId,vType="&"..vName,(gType or "") if jDp[inId] then if not jGvar[inId] then jGvar[inId]="true" end if jQaGv:find(inId..",") then if vName:match("%d+%w+") then return vType=="?" and gVarType[4] or getQaVar(qaId(vName)) else return vType=="?" and gVarType[3] or getQaVar(tostring(plugin.mainDeviceId),vName,"") end else if jVar[vName] then jDp[inId].value.value=jVar[vName] jDp[inId].value.mTime=jVar[vName.."lTime"] return vType=="?" and gVarType[1] or jVar[vName],jVar[vName.."lTime"] end if not initMode and vType=="?" then return (vName:find("'") and gVarType[5] or gVarType[2]) end local val=api_get(vName,"/globalVariables/"..sysId(vName)) if val~= nil then jDp[inId].value.value=tostring(val.value) jDp[inId].value.mTime=val.modified if vType=="?" then return (vName:find("'") and gVarType[5] or gVarType[2]) else return val.value end end end --if jQaGv:find("&"..vName..",") then end --if jDp[inId] then initMode=true sceneAbort("(39) Global "..wF..inId.."
not found. Available variables are:
"..jGvarName,{inId,inId:sub(1,3)},jD[inGid].lineNum) end --==================================== fibaro_getGlobalValue function getHistory(inId) if inId then return "TBR",os_time() end local hData,pHost=nil,(inId:find("'") and inId:match("'(%w+)") or "local") if hc3Platform:find(" "..jSlave[pHost].platform.." ") then hData=api_get(inId,"/events/"..urlEncode("history?objectType=device&&numberOfRecords=1&objectId="..sysId(inId),1)) else hData=api_get(inId,"/panels/"..urlEncode("event?type=id&last=1&deviceID="..sysId(inId),1)) end if hData == nil then return "NA",os_time() end if not hData[1] or not json.encode(hData[1]):find("CentralSceneEvent") then return "TBR",os_time() end if hc3Platform:find(" "..jSlave[pHost].platform.." ") then return hData[1].data.keyId.."."..hData[1].data.keyAttribute,hData[1].timestamp else return hData[1].event.data.keyId.."."..hData[1].event.data.keyAttribute,hData[1].timestamp end end --==================================== getHistory function to_number(value) return string.lower(tostring(value)):find("true") and 1 or string.lower(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>o2 and o1<(o3 or o2)) end --================================ compare function checkState(inId) if not jD[inId].property and inId:sub(1,1)~="_" then return false end if jD[inId].property=="lastBreached" then jD[inId]["lastBreached"].value=tostring(math.floor(os_time()-jD[inId]["lastBreached"].mTime)) end local fId=(inId:sub(1,1)~="_" and inId:match("(%S+)>")..jD[inId].property or nil) if jChk[fId] then jD[inId][jD[inId].property].value=jChk[fId] goto hasValue end if inId:sub(1,1)=="&" and jDp["&"..realId(inId)].value.type==gVarType[1] then jD[inId][jD[inId].property].value=fibaro_getGlobalValue(realId(inId)) elseif inId:sub(1,1)=="&" and not jSlave[(inId:match("'(%w+)") or "local")].hc3platform then jD[inId][jD[inId].property].value=fibaro_getGlobalValue(realId(inId)) elseif inId:sub(1,1)=="$" then jD[inId][jD[inId].property].value=setKeyword(tableId(inId)) end if fId then jChk[fId]=jD[inId][jD[inId].property].value;jChk[fId.."trig"]=jD[inId].trigOn end ::hasValue:: if jD[inId].state~=2 then return true end --state not defined 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..math.modf(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) 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 realSlot==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 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 cTime=os.date("*t",os_time()) local _,onTime=math.modf((cTime.day*1440+cTime.hour*60+cTime.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,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 lCnt=lCnt+1; local sTime,eTime=tSlotFormat(i:match("(%w+:%w+)~")),tSlotFormat(i:match("~(%w+:%w+)")) if not sTime and eTime then setTimeActions(i,eTime,"falseAct",1,inId);sCnt=sCnt+1 elseif sTime and not eTime then setTimeActions(i,sTime,"trueAct",2,inId);sCnt=sCnt+1 elseif not sTime and not eTime then fStatus=checkDate(i) elseif (cTime>sTime and cTime=eTime and (cTimesTime)) then fStatus=checkDate(i) 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=(to_number(tmpStatus) % 2) + 1;setActions(inId,"s") end end end --if jD[inId].initOnStartup then end return fStatus end --======================= inTimeSlot 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) aType=aType or "" if jD[inId].status==1 and not tmpStatus then if jD[inId].falseAct then jD[inId].status=2 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 then if jD[inId].trueAct then jD[inId].status=1 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 setTickTime(inId);jD[inId].lTime=os_time(); 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 setTickTime(inId);jD[inId].lTime=os_time();jD[inId].lState = tmpStatus end if jAct[inId] then runAction() end end --================================ setActions function onTime(inId,rule) jChk={};gData.alertId={} local fStatus,modNum,ttsMsg,noteMsg,i,x=true,"devices","","",0,os.time() for i,_ in pairs(jD) do inGid=i 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..findNameRoom(i,1).. (#jD[i].userTerms>2 and " ("..jD[i].userTerms..jD[i].stateValue..")" or "").." >"..jD[i].tstRate.." min." 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 onTimeLoop=os.time()-x;onTimeMax=math.max(onTimeMax,onTimeLoop) if not inId and not rule 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..")\n","Time out on next "..modNum..":\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 runAction() end --======================================== onTime function parseNoteRun(noteType,runData) if not noteType:find("run") then return noteType end local i,nId=0,"" if noteType:find("run_") then nId=noteType:match("run_(%d+)") noteType=noteType:gsub(",run_"..nId,"") runData.note=nId..":"..api.get("/scenes/"..nId)["name"] runData.message=runData.message.."\nRun "..runData.note runData.data={sceneId=tonumber(nId)} elseif noteType:find("run") then runData.action="RunAction";runData.service="Device"; runData.data={deviceId=tonumber(noteType:match("run(%d+)")),actionName=noteType:match("run%d+{(%w+)")} runData.note=runData.data.deviceId..":"..api.get("/devices/"..runData.data.deviceId)["name"]..noteType:match("%b{}") runData.message=runData.message.."\nRun "..runData.note iArg=noteType:match("%b{}"):gsub("[{}]",""):gsub(runData.data.actionName,"") for i in string.gmatch(iArg:gsub(","," "),"%S+") do if not runData.data.args then runData.data.args={} end runData.data.args[#runData.data.args+1]=i end noteType=noteType:gsub(",run"..runData.data.deviceId,""):gsub("%b{}","") end return noteType end --======================================== parseNoteRun function sendNote(subject,body,noteType,logInfo) if not inGid then inGid=1 end local idType,runData,uId,i,cmd,runScene,idStr,nLine,fStatus,rStr,mStr="",{},{},0,"",0,"","","","
"..indent..ctlChar(" subj: "..subject.." body: "..body.." "),"";txtNote="" if not noteType or noteType=="sendNote" then noteType="sendNote";nLine="
"..indent;mStr="" fStatus=" "..noteType:match("%w+").." Error: All notification types are set to "..bF.."FALSE"..eF else mStr=rStr end cmd=noteType;if cmd:find("run") then runScene=1 end runData={note="",message=body,title=subject,category='RUN_CANCEL',action="Run",service="Scene",data={},mobileDevices={}}; noteType= parseNoteRun(noteType,runData) 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.alert("email",{tonumber(i)},subject.."\n"..body) end end fStatus=indent.." sendEmail "..idStr..mStr end --if noteType~="sendNote" and runScene>0 then fibaro.scene("execute",{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 fibaro.homeCenter.notificationService.publish({ type="MobilePopupNotification", priority = "info", data={title=subject, text=body..(runScene>0 and "\nstartScene("..runScene..")" or ""), button={type="STANDARD",caption="caption"}} }) 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; for i in string.gmatch(tmpStr:gsub(","," "),"%S+") do idType=(noteList.sendPush[i] and "mobile" or noteList.sendEmail[i] and "email" or "") if noteList.sendPush[i] or noteList.sendEmail[i] then txtNote=txtNote..(noteList.sendPush[i] or noteList.sendEmail[i]:match("(.+):"))..","; idStr=idStr..", "..i..""..(noteList.sendPush[i] or noteList.sendEmail[i]:match("(.+):")).." "; uId[#uId+1]=tonumber(i) else sceneAbort("(16) ID ("..wF..i..eF..") has not been found in the system.
Available mobiles (in Green"..eF.." - authorized):
"..wF.. table_concat(noteList.sendPush,"
",true)..eF.."
Available users:
"..ygF.. table_concat(noteList.sendEmail,"
",true)..eF,{cmd},jD[inGid].lineNum) end end --for i in string.gmatch(tmp if not logInfo then runData.mobileDevices=uId if noteType:find("sendiPush") and runScene>0 then api.post('/mobile/push',runData) else local pSt,pErr=pcall(hub.alert,(idType=="email" and "push" or "simplePush"),uId,subject.."\n"..body,false) --ctlChar(subject.." "..body,"\n","\\n"),false) if not pSt then hub.alert("push",uId,subject.."\n"..body,false) end --ctlChar(subject.." "..body,"\n","\\n"),false) end end end 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 "..runData.note 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 if not logInfo then sendSms(i,subject,body) end end if noteType~="sendNote" and runScene>0 then fibaro.scene("execute",{tonumber(runScene)}) 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 getButtonName(inId,bNum,tipTxt) local lStr,i,j="",0,0;--bNum=bNum:match("%w+") if not jDid[inId].button then jDid[inId].button={} for j,i in pairs(api.get("/devices/"..inId).properties.uiCallbacks) do if i.eventType=="onReleased" then if (tonumber(bNum)==j or bNum==i.name) and i.callback:len()<2 then sceneAbort("(88) QA "..wF..findNameRoom(inId,2)..eF.." UI callback for element: '"..wF..i.name..eF.."' not found.
"..cF.."Please define callback on event."..eF,{inId,"pressButton",","..sNum},jD[inGid].lineNum) end lStr=lStr..j.." : "..i.name.."
"; jDid[inId].button[i.name]=tostring(j);jDid[inId].button[tostring(j)]=i.name;end end end if not jDid[inId].button[bNum] then sceneAbort("(77) QA "..wF..findNameRoom(inId,2)..eF.." button ID '"..wF..bNum..eF.."' is not found.
"..cF.."Available button(s):
"..eF..ygF..lStr..eF,{inId,"pressButton",","..bNum},jD[inGid].lineNum) end if tipTxt then return jDid[inId].button[bNum] end end --=============================== getButtonName function getSliderName(inId,sNum,tipTxt) local sCall,lStr,i,j={},0,0;--sNum=sNum:match("%+") if not jDid[inId].slider then jDid[inId].slider={} for j,i in pairs(api.get("/devices/"..inId).properties.uiCallbacks) do if i.eventType=="onChanged" then if (tonumber(sNum)==j or sNum==i.name) and i.callback:len()<2 then sceneAbort("(87) QA "..wF..findNameRoom(inId,2)..eF.." UI callback for element: '"..wF..i.name..eF.."' not found.
"..cF.."Please define callback on event."..eF,{inId,"setSlider",","..sNum},jD[inGid].lineNum) end lStr=lStr..j.." : "..i.name.."
"; jDid[inId].slider[i.name]=tostring(j);jDid[inId].slider[tostring(j)]=i.name;end end end if not jDid[inId].slider[sNum] then sceneAbort("(86) QA "..wF..findNameRoom(inId,2)..eF.." slider ID '"..wF..sNum..eF.."' not found.
"..cF.."Available slider(s):
"..eF..ygF..lStr..eF,{inId,"setSlider",","..sNum},jD[inGid].lineNum) end if tipTxt then return jDid[inId].slider[sNum] end end --=============================== getSliderName 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, 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() if initMode then printLabel("progressLabel",ygF.."Arranging data table...
"); jRun.lastId="" end gData.tableTimeStamp=os_time();refreshTime=os.time() 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,sBuf,deadColor,dArm="","", "",{},{},{[true]=rF,[false]=ygF,["na"]=rF,["true"]=rF,["false"]=ygF},{["disarmed"]="🔿",["armed"]="🟢",["breached"]="🔴"} local tHeader={"Type","Item Description","Value","Criteria","in State",rF..""..gData.prvAlerts.."","TimeSpan","Time Slot","trueAct","falseAct","trigAct","timeoutAct","timeLoopAct","initAct","errAct","okAct","jM{#}","jM { hash table line }","Active Rules"} local inBuffer,dHeader,rptHeader,gHeader,rmdHeader,zHeader,slaveHeader,gpsHeader="",{" Name :ID: Room ","nAck","Dead"," Is dead "," Events history "},{"Master :ID: Room"," # "," Events history "},{" Type "," Name "," Value "," Modified "},{" Rate/Left(sec) "," Trigger "," Note "," Reminder "},{"Master :ID: Room","#","%"},{"Name","Type"," IP ", "Online ","ApiGet ","ApiPost ","ApiPut ","ApiLimit"},{"Name","ID","Radius","Address"} 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