--============================= Z-Range test v1.3 =============================== --======================== User configurable parameters =================================== showDevices=false --if set to true, list of unsorted devices will be dispalyed. fontSize=100 --font size percentage (80-150).100% default --================================================================================== lgF="";gF="";yF="";ygF="";cF="";bF="";oF="";snHC=api.get("/settings/info/")["serialNumber"] pF="";wF="";rF="";irF="";eF="";jDlist={["numDev"]=0,["phDev"]=0,["idColor1"]=lgF,["roomName1"]=api.get("/settings/info/")["hcName"],["pName1"]=snHC} order={irF,"","","","","",lgF};sn=0;dateRange={min=os.time(),max=0,range}jP={}jMesh={}jF={}badId={}jFnum={}jCntrl={} credits="123credits: @10der @Alex";byRoom={}roomDev={}roomEff={} nStat={["ok"]=lgF.."ok"..eF,["error"]=irF.."error"..eF,["in progress"]=cF.."inProgress"..eF,["pending"]=yF.."pending"..eF};endStr={"
","","",""};absent="";hMax=0 tHeader={" # ","Parent ID:Description","Last Route","iRoute","iSee","Status","Time Stamp","24History"};dNum={0,0,0};dMaxLen=0;iSeeTotal=0;iSeeMax=0;iSeeMax1=0;iSeeHC=0;fSize=math.min(math.max(fontSize,80),150)/100 rHeader={" # "," Room Name "," Parent ID:Name "," % ","iSee","Last Route","iRoute","Status","Time Stamp","24History"} zBuf={}zBuf[1]="" function getRoom(roomId) return roomId==0 and "Unassigned" or api.get("/rooms/"..roomId)["name"] end function getWeekSpot() local wSpot,zLevel,tmpRoom=":",{},{} for w=1,#jP do iSeeLvl=300;did=nil for i=1,#jP do if not wSpot:find(":"..jP[i]..":") and not zLevel[jDlist["childRoom"..jP[i]]] then if jDlist["iSee"..jP[i]]>0 then iSeeIn=jDlist["iSee"..jP[i]] else iSeeIn=200 end iSeeLvl=math.min(iSeeLvl,iSeeIn);if iSeeLvl==iSeeIn then did=jP[i] end end end if did then wSpot=wSpot..did..":";zLevel[jDlist["childRoom"..did]]="found";tmpRoom[#tmpRoom+1]=jDlist["childRoom"..did];roomDev[jDlist["childRoom"..did]]={};roomEff[jDlist["childRoom"..did]]=0;dIn=0;pNum=0 for i=1,#jP do if jDlist["childRoom"..did]==jDlist["childRoom"..jP[i]] then dIn=dIn+1 if jDlist["iSee"..jP[i]]>0 then pNum=pNum+1; roomEff[jDlist["childRoom"..did]]=roomEff[jDlist["childRoom"..did]]+math.min(99,math.ceil(jDlist["iSee"..jP[i]]/iSeeAvg*100)) end roomDev[jDlist["childRoom"..did]][dIn]=jDlist["iSee"..jP[i]]*10000+jP[i] end end roomEff[jDlist["childRoom"..did]]=math.ceil(roomEff[jDlist["childRoom"..did]]/pNum) table.sort(roomDev[jDlist["childRoom"..did]]) end end for w=1,#tmpRoom do iSeeLvl=300;did=nil for i=1,#tmpRoom do if not json.encode(byRoom):find(tmpRoom[i]:gsub("[%?%(%)%+%-%*%[%]%$]",function (a) return "%"..a end)) then if roomEff[tmpRoom[i]]>0 then iSeeIn=roomEff[tmpRoom[i]] else iSeeIn=200 end iSeeLvl=math.min(iSeeLvl,iSeeIn);if iSeeLvl==iSeeIn then did=i end end end if did then byRoom[#byRoom+1]=tmpRoom[did] end end end --============================================= function percentTrueColor(percent,msg) if percent<=25 then pClr=lgF elseif percent<=50 then pClr=yF elseif percent<=75 then pClr=oF else pClr=rF end return pClr..msg.."%"..eF end function percentColor(percent,msg) if percent<=25 then pClr=rF elseif percent<=50 then pClr=oF elseif percent<=75 then pClr=yF else pClr=lgF end return tostring(percent)=="-nan" and absent..(msg or "").."na"..eF or pClr..(msg or "")..percent.."%"..eF end -- ================================ --==================================================== function getData(i,j) local x=0 if j.parentId>1 then if not jDlist[j.parentId] then jDlist[j.parentId]=" "..bF..(#j.properties.zwaveCompany>2 and j.properties.zwaveCompany or "Unknown") .." v"..j.properties.zwaveVersion..eF.."

" end jDlist["childRoom"..j.parentId]=(jDlist["childRoom"..j.parentId] or getRoom(j.roomID)) jDlist["childName"..j.parentId]=(jDlist["childName"..j.parentId] or j.name) jDlist["numDev"]=jDlist["numDev"]+1; for k=#hist,1,-1 do if hist[k]["deviceID"]==j.id then x=x+1 end end jDlist[j.parentId]=jDlist[j.parentId]..bF..string.format("%4d",j.id)..eF..":"..j.name..":"..getRoom(j.roomID)..(x>0 and " ("..x..")"or "").."
" jDlist["hist"..j.parentId]=(jDlist["hist"..j.parentId] or 0)+x;hMax=math.max(hMax,jDlist["hist"..j.parentId]) elseif j.parentId==1 then mStr=json.encode(j.properties.lastWorkingRoute):gsub("[%[%]]","") if #mStr==1 then jDlist["idColor"..j.id]=lgF;mStr="";dNum[1]=dNum[1]+1 elseif #mStr==0 then jDlist["idColor"..j.id]=irF;mStr="";dNum[3]=dNum[3]+1 else mStr=","..mStr:sub(3)..",";_,tmpNum=mStr:gsub(",","");jDlist["idColor"..j.id]=string.format("",255-math.min(4,tmpNum-2)*30);dNum[2]=dNum[2]+1 end jDlist["iCtable"..j.id]=j.properties.neighborList;jDlist["pRoute"..j.id]=mStr;jDlist["pName"..j.id]=j.name;jDlist["pRoom"..j.id]=j.roomID;jDlist["roomName"..j.id]=getRoom(j.roomID);jDlist["phDev"]=jDlist["phDev"]+1;dMaxLen=math.max(dMaxLen,#j.name + #jDlist["roomName"..j.id]+6) jDlist["nList"..j.id]=j.properties.neighborListRequestStatus:gsub(".+",function (a) if nStat[a:lower()] then return nStat[a:lower()] else return a end end) if j.properties.lastWorkingRouteResponseTimestamp > 0 then dateRange.min=math.min(dateRange.min,j.properties.lastWorkingRouteResponseTimestamp);dateRange.max=math.max(dateRange.min,j.properties.lastWorkingRouteResponseTimestamp) end jDlist["nDate"..j.id]=jDlist["idColor"..j.id]..os.date("%b/%d/%y %X",j.properties.lastWorkingRouteResponseTimestamp):gsub(".+",function (a) if a:find("01/70") then return "out of date" else return a end end)..eF; if #j.properties.neighborList>0 then iSeeNumDev=(iSeeNumDev or 0) +1; iSeeTotal=iSeeTotal+ #j.properties.neighborList end jDlist["iSee"..j.id]=#j.properties.neighborList;iSeeMax=math.max(iSeeMax,jDlist["iSee"..j.id]);if j.properties.neighborList[1]==1 then iSeeHC=iSeeHC+1 else iSeeMax1=math.max(iSeeMax1,jDlist["iSee"..j.id]) end end end --========================== if not debug then api.delete("/scenes/" .. __fibaroSceneId .. "/debugMessages") end print(ygF.."Retrieving working routes and neighbors mesh data...") hist=api.get("/panels/history?&from="..os.time()-24*3600 .."&to="..os.time()) print(ygF.."Retrieving history of last 24 hours...
") for i,j in pairs(api.get("/devices/")) do if j.id==1 or not j.parentId or j.parentId==0 or not j.properties.nodeId then goto nextId end; st,res=pcall(getData,i,j) if not st then jDlist["pName"..j.id]="Corrupt";jDlist["roomName"..j.id]="Device";jDlist["idColor"..j.id]=nil --"" badId[#badId+1]=rF.."/api/devices/"..j.id.." "..wF.." mesh data coruppted: "..res:gsub(".-:(%d+):",function(a) return "lua."..a..":" end).."
" else if j.parentId==1 then if not tonumber(j.properties.zwaveInfo:match("(%d+),.+")) or tonumber(j.properties.zwaveInfo:match("(%d+),.+"))<=2 then jCntrl[#jCntrl+1]=j.id else jP[#jP+1]=j.id; end end end ::nextId:: end iSeeAvg=math.ceil(iSeeTotal/iSeeNumDev);iSeeTotal=0;hFactor=math.floor(99/(math.floor(hMax/#hist*1000)/10)*100)/100 dateRange.range=wF.."12Devices: masters="..jDlist["phDev"].." slaves="..jDlist["numDev"].."123Data date range: "..os.date("%d %b %Y - ",dateRange.min)..os.date("%d %b %Y
",dateRange.max) for _,j in ipairs(jP) do if jDlist["childRoom"..j] then jDlist["roomName"..j]=(jDlist["roomName"..j]:match("Unassigned") and "("..jDlist["childRoom"..j]..")" or jDlist["roomName"..j]) end if jDlist["childName"..j] then jDlist["pName"..j]=(tonumber(jDlist["pName"..j]) and "("..jDlist["childName"..j]..")" or jDlist["pName"..j]) end iSeeTotal=iSeeTotal+math.ceil(math.min(jDlist["iSee"..j],iSeeAvg)); jDlist["mStyle"..j]=">";sMod=math.min(4,math.ceil(#jDlist["iCtable"..j]/10)) if sMod>1 then jDlist["mStyle"..j]="style='position:fixed;top:auto;bottom:10px;right:40px;'>" end for i=1,#jDlist["iCtable"..j] do inId=tostring(jDlist["iCtable"..j][i]); if not jDlist["idColor"..inId] then jFnum[inId]=(jFnum[inId] or 0 ) +1; jF[inId]=(jF[inId] or "")..(jDlist["idColor"..j] or absent)..string.format(((jDlist["idColor"..j] or absent):match(absent) or cF).."%4s
%-"..dMaxLen.."s",j,":"..jDlist["pName"..j]..":"..jDlist["roomName"..j])..eF..endStr[(jFnum[inId] % 2)+1] if not json.encode(badId):find(":"..inId.."<") then badId[#badId+1]=rF.."Device:"..inId..eF..""..wF.." found in routes table, but not in system's devices !
" end end jDlist["iCdev"..j]=(jDlist["iCdev"..j] or "")..(jDlist["idColor"..inId] or absent)..string.format(((jDlist["idColor"..inId] or absent):match(absent) or cF).."%4s
%-"..dMaxLen.."s",inId,":"..(jDlist["pName"..inId] or "Absent")..":"..(jDlist["roomName"..inId] or "Device"))..endStr[(i % sMod)+1]..eF end end cIndex=wF.."Table color index:
\t"..irF..dNum[3].." Device(s) unknown working route."..eF.."
\t"..yF..dNum[2]..eF.." device(s) in shades orange "..eF.."to yellow "..eF.."in descending order of nodes number in route.
\t".. lgF..dNum[1].." Device(s) in direct route to controller.


"..eF..cF.."Z-Capacity: "..math.ceil(jDlist["phDev"]/232*100).."%
" zBuf[#zBuf+1]=" efficiency: " if dNum[3]>0 then zBuf[#zBuf+1]=wF.."
" else zBuf[#zBuf+1]=wF.."HC2 location "..percentColor(math.min(100,math.ceil(iSeeHC/jDlist["phDev"]*100))).." Devices location "..percentColor(math.min(100,math.ceil(iSeeTotal/(jDlist["phDev"]*iSeeAvg)*100))).."
" end jMesh["iRouteMax"]=0 for _,j in ipairs(jP) do if not jMesh["iRoute"..j] then jMesh["iRoute"..j]="";jMesh[j]=0 end for i=1,#jP do if jDlist["pRoute"..jP[i]]:find(","..j..",") then jMesh[j]=jMesh[j]+1;jMesh["iRouteMax"]=math.max(jMesh["iRouteMax"],jMesh[j]);jMesh["iRoute"..j]=jMesh["iRoute"..j]..jDlist["idColor"..jP[i]]..string.format("%4s",jP[i])..":"..jDlist["pName"..jP[i]]..":"..jDlist["roomName"..jP[i]]..eF.."
"; end end end if showDevices then for i=1,#tHeader do zBuf[#zBuf+1]="" end; for j=1,#order do for i=1,#jP do if jDlist["idColor"..jP[i]]==order[j] then sn=sn+1;zBuf[#zBuf+1]="" zBuf[#zBuf+1]="" if not jDlist["pRoute"..jP[i]]:find(",") then zBuf[#zBuf+1]="" else zBuf[#zBuf+1]="" end tmpNum=math.max(0,255-(math.floor(150/jMesh["iRouteMax"]*jMesh[jP[i]]))); if jMesh[jP[i]]>0 then zBuf[#zBuf+1]="" else zBuf[#zBuf+1]="" end if jDlist["iSee"..jP[i]]>0 then _,badNum=jDlist["iCdev"..jP[i]]:gsub(absent,"");jDlist["iSee"..jP[i]]=jDlist["iSee"..jP[i]]-badNum/2 end tmpNum=math.max(0,255-(math.floor(255/jDlist["phDev"]*jDlist["iSee"..jP[i]])));tmpStr=string.format("",tmpNum,tmpNum) if jDlist["iSee"..jP[i]]>0 then if not jDlist["iCdev"..jP[i]]:find(":HC2%-") then tmpNum=math.max(0,(math.ceil(200/iSeeMax1*jDlist["iSee"..jP[i]])));tmpStr=string.format("",tmpNum,tmpNum) end zBuf[#zBuf+1]="" else zBuf[#zBuf+1]="" end zBuf[#zBuf+1]="";zBuf[#zBuf+1]="" if jDlist["hist"..jP[i]]>0 then zBuf[#zBuf+1]="" else zBuf[#zBuf+1]="" end end end end sIn=0 for j=1,#jCntrl do did=jCntrl[j];sn=sn+1;zBuf[#zBuf+1]="" zBuf[#zBuf+1]="" if j==1 then zBuf[#zBuf+1]="" zBuf[#zBuf+1]="";zBuf[#zBuf+1]="" end zBuf[#zBuf+1]="
"..tHeader[i].."
"..wF..sn..eF..""..jDlist["nList"..jP[i]]..""..jDlist["nDate"..jP[i]]..""..percentTrueColor(math.floor(jDlist["hist"..jP[i]]/#hist*1000)/10*hFactor,jDlist["hist"..jP[i]].."/"..math.floor(jDlist["hist"..jP[i]]/#hist*1000)/10).."
"..cF..sn..eF..""..cF.." Controllers + "..eF end --..""..jDlist["nList"..did]..""..jDlist["nDate"..did].."

".."Residence location efficiency (in ascending order)"; end; getWeekSpot() for i=1,#rHeader do zBuf[#zBuf+1]="" end; sn=0 for i=1,#byRoom do sIn=0 for j=1,#roomDev[byRoom[i]] do sIn=sIn+1;sn=sn+1;zBuf[#zBuf+1]="" if j==1 then zBuf[#zBuf+1]="" end did=roomDev[byRoom[i]][j]-math.floor(roomDev[byRoom[i]][j]/10000)*10000 -- Parent ID ============= zBuf[#zBuf+1]="" -- % if jDlist["iSee"..did]>0 then zBuf[#zBuf+1]="" else zBuf[#zBuf+1]="" end -- iSee devices =========== tmpNum=math.max(0,255-(math.floor(255/jDlist["phDev"]*jDlist["iSee"..did])));tmpStr=string.format("",tmpNum,tmpNum) if jDlist["iSee"..did]>0 then if not jDlist["iCdev"..did]:find(":HC2%-") then tmpNum=math.max(0,(math.ceil(200/iSeeMax1*jDlist["iSee"..did])));tmpStr=string.format("",tmpNum,tmpNum) end zBuf[#zBuf+1]="" else zBuf[#zBuf+1]="" end -- Last Route ============= if not jDlist["pRoute"..did]:find(",") then if jDlist["iSee"..did]==0 then zBuf[#zBuf+1]="" else zBuf[#zBuf+1]=""end else zBuf[#zBuf+1]="" end -- iRoute =================== tmpNum=math.max(0,255-(math.floor(150/jMesh["iRouteMax"]*jMesh[did]))); if jMesh[did]>0 then zBuf[#zBuf+1]="" else zBuf[#zBuf+1]="" end -- Status and Date ============ zBuf[#zBuf+1]="";zBuf[#zBuf+1]="" -- 24History ============ if jDlist["hist"..did]>0 then zBuf[#zBuf+1]="" else zBuf[#zBuf+1]=""end end --for j=1,#roomDev[byRoom[i]] if i ~= #byRoom then zBuf[#zBuf+1]="" end end sIn=0 zBuf[#zBuf+1]="" for j=1,#jCntrl do did=jCntrl[j];sIn=sIn+1;sn=sn+1;zBuf[#zBuf+1]="" if j==1 then zBuf[#zBuf+1]="" end zBuf[#zBuf+1]="" end zBuf[#zBuf+1]="
"..rHeader[i].."
"..wF..sn.."/"..sIn..eF..""..percentColor(roomEff[byRoom[i]],byRoom[i].." ")..""..percentColor(math.min(99,math.ceil(jDlist["iSee"..did]/iSeeAvg*100)))..""..absent.."na"..absent.."na"..absent.."na"..jDlist["nList"..did]..""..jDlist["nDate"..did]..""..percentTrueColor(math.floor(jDlist["hist"..did]/#hist*1000)/10*hFactor,jDlist["hist"..did].."/"..math.floor(jDlist["hist"..did]/#hist*1000)/10) .."
"..cF..sn.."/"..sIn..eF..""..cF.."Controllers +
" if not debug then api.delete("/scenes/" .. __fibaroSceneId .. "/debugMessages") end for i=1,#badId do if i<15 then headStr=(headStr or "")..rF..""..i.." " end errStr=(errStr or "").."

" end if errStr then errStr=headStr.."
"..errStr.."
" end tmpStr="ZRangev1.3 " zBuf[#zBuf+1]=wF.."
Last 24History data is based on saved logs in event panel "..eF..pF.."(some devices might be excluded)
"..eF..cF.."\tTotal saved events = "..#hist.."
\tAverage Z-traffic = "..math.floor((24*3600)/#hist*10)/10 .." sec."..eF print((errStr or "")..tmpStr..dateRange.range.."
"..table.concat(zBuf).."




")