Hi everyone,
I'm having some issues with Lua code in a virtual device I am writing.
I'm getting information on the status of my Bluespray irrigation controller.
It has a basic API and reports if any of the zones are on watering. I want to just query the unit and then print out if any are detected as on.
When I query bluespray controller and its not watering anything I get some JSON back which looks like this.
{
"result": {
"sensors": {
"door": 0,
"rain": 1
},
"active_zones": [
],
"timestr": "Sat Mar 11 2017 10:09:38 PM",
"time": 1489241378000,
"door": 0
},
"id": 528452
}
When its IS watering I get the following.
{
"result": {
"sensors": {
"door": 0,
"rain": 1
},
"active_zones": [
{
"on": 1489240754,
"off": 1489241054,
"run": 1489240754,
"terminal": 3
}
],
"timestr": "Sat Mar 11 2017 10:00:12 PM",
"time": 1489240812000,
"door": 0
},
"id": 218829
}
To make it even more complex sometimes the system reports multiple zones operating. (This is normal and ok. I just need to pick up the first zone reported)
{
"result": {
"sensors": {
"door": 0,
"rain": 1
},
"active_zones": [
{
"on": 1489241854,
"off": 1489241974,
"run": 1489241854,
"terminal": 4
},
{
"on": 1489241854,
"off": 1489241974,
"run": 1489241854,
"terminal": 8
}
],
"timestr": "Sat Mar 11 2017 10:17:38 PM",
"time": 1489241858000,
"door": 0
},
"id": 998161
}
My issue is detecting if I get none, one or multiple active zones. If I have a valid zone then I then want to put the first zone number into the zoneterminal variable. If I don't have a valid zone I just want to put 0 into the zoneterminal variable.
The problem is when I try something like the code below I get
[ERROR] 22:11:22: line 56: attempt to index field '?' (a nil value)
I cant figure out how to deal with LUA arrays correctly. (specifically detecting valid entries)
The json.null operator does not seem to work the same way on array values.
-- This script polls data from a local Bluespray Irrigation controller every 60 seconds and displays the status of the active zones
-- Its configured for a speicific system but could easily be adapted to any bluespray controller.
-- This script is written for Fibaro HC2
-- Script written by Brom (
[email protected])
-- version 0.1 (10/3/17)
function round(x, n)
n = math.pow(10, n or 0)
x = x * n
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
return x / n
end
fdata = {}
zonename_1 = "Front Lawn"
zonename_2 = "Road Lawn"
zonename_3 = "Front Garden"
zonename_4 = "Back Lawn Left"
zonename_7 = "Back Lawn Right"
zonename_5 = "Back Garden Left"
zonename_6 = "Back Garden Right"
fdata.selfId = fibaro:getSelfId()
fdata.selfIp = fibaro:get(fdata.selfId, "IPAddress");
-- Perform the grab from the irrigation controller.
local irrigationcontroller= Net.FHttp(fdata.selfIp);
response,status,errorCode = irrigationcontroller:GET("/api/status")
-- Basic debugging
fibaro:debug(response)
-- Check the respose from the controller is valid and Pull out the details from the JSON response.
if (tonumber(status)==200 and tonumber(errorCode)==0) then
local irrigation = json.decode(response)
-- If any of the responses are null then make them 0
if irrigation.result.time == json.null then
polltime = 0
else
polltime = irrigation.result.time
end
-- this bit does not work !!!!
zoneterminal = irrigation.result.active_zones[1].terminal
zoneon = irrigation.result.active_zones[1].on
zoneoff = irrigation.result.active_zones[1].off
zonerun = irrigation.result.active_zones[1].run
-- Basic Debugging
fibaro:debug("polltime: "..polltime)
fibaro:debug("zoneterminal: "..zoneterminal)
fibaro:debug("zoneon: "..zoneon)
fibaro:debug("zoneoff: "..zoneoff)
fibaro:debug("zonerun: "..zonerun)
timeleft = (zoneoff - zonerun) / 2 / 60
fibaro:debug("timeleft: "..timeleft)
-- Output to the device.
if zoneterminal == 0 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," OFF ")
end
if zoneterminal == 1 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," ON "..zonename_1)
fibaro:log("Time Left "..timeleft.." Min");
end
if zoneterminal == 2 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," ON "..zonename_2)
fibaro:log("Time Left "..timeleft.." Min");
end
if zoneterminal == 3 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," ON "..zonename_3)
fibaro:log("Time Left "..timeleft.." Min");
end
if zoneterminal == 4 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," ON "..zonename_4)
fibaro:log("Time Left "..timeleft.." Min");
end
if zoneterminal == 5 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," ON "..zonename_5)
fibaro:log("Time Left "..timeleft.." Min");
end
if zoneterminal == 6 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," ON "..zonename_6)
fibaro:log("Time Left "..timeleft.." Min");
end
if zoneterminal == 7 then
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value"," ON "..zonename_7)
fibaro:log("Time Left "..timeleft.." Min");
end
if zoneterminal == 0 or zoneterminal == 1 or zoneterminal == 2 or zoneterminal == 3 or zoneterminal == 4 or zoneterminal == 5 or zoneterminal == 6 or zoneterminal == 7 then
fibaro:debug("Valid Zone")
else
fibaro:call(fdata.selfId, "setProperty", "ui.watering.value","Error")
end
else
fibaro:debug("error Irrigation Controller: "..errorCode)
end
-- sleep for 1 min
fibaro:sleep(10000)
-- fibaro:sleep(60000)
Any help appreciated.
ps Code for the output is really extra long. Looking to understand how I can manipulate the variable name / number I am outputting to reduce the number of if statements.
Once I get this figured out I plan to also add in buttons to perform manual runs.
Brom