-
Compteur de contenus
215 -
Inscription
-
Dernière visite
-
Jours gagnés
45
Tout ce qui a été posté par jang
-
'self' is a variable passed on to all method calls. Ex. function QuickApp:test(x) print(self.name) end is the same as function QuickApp.test(self,x) print(self.name) end Lua will pass the same 'self' object every time a QuickApp:fun is called 'self' is a Lua table and you can add your own values to self self.xxx = "variable self" and later access it when a QuickApp:fun is called function QuickApp:test2(x) print(self.xxx) end A global Lua variable is accessible from all functions xxx = "Lua global" function QuickApp:test3() print(xxx) end function test3() print(xxx) end 'self' can be a convenient place to store values as not to pollute the global Lua name space, and you can have values shared privately between QuickApp:funs. What is faster? Well they are both a table access (table 'self' and table '_G') so without testing I expect them to be similar in efficiency. Note, if you create QuickAppChild objects in your QA they will have their own individual 'self' variables.
-
Yes, A json value can contain null values. If we replace null with nil in a Lua table the key/value is removed from the table. A proper json decoder for Lua would like to preserve the null values as it can be significant (key exist with null value, or key does not exist at all) The json decoder choose json.null to be a function value is that they wanted a value that is unique and quick to compare. In a QA, by providing the {others = {null=false}} option to json.decode it will replace null with Lua nil and the key removed. It doesn't work in Scenes as they use a different json decoder/encoder that doesn't take options.
-
cookie = [[JSESSIONID=723BE79E2C3D6EB5DD2072CAE855A73C; Path=/; Secure; HttpOnly, TS01b0e19c=01e5b3f1f84aa60001cd253c6f621db2b9d7722f8d2ab2c51a7723da6c8d46b152c406e57daf3b78bcb3c71f629062e1c84432b5f8; Path=/; Secure; HTTPOnly]] local cookie1,cookie2 = cookie:match("(.-);.-HttpOnly, (.-);") print(cookie1) print(cookie2) [11.12.2022] [10:18:31] [DEBUG] [QUICKAPP1001]: JSESSIONID=723BE79E2C3D6EB5DD2072CAE855A73C [11.12.2022] [10:18:31] [DEBUG] [QUICKAPP1001]: TS01b0e19c=01e5b3f1f84aa60001cd253c6f621db2b9d7722f8d2ab2c51a7723da6c8d46b152c406e57daf3b78bcb3c71f629062e1c84432b5f8
-
You see in the output above that uid versions uid#5 and uid#7 both has key 'TemperatureState' and then the question is which should be choosen? Now we can get both with keys["5#uid"] and keys["7#uid"]
-
How about this? local function find(uid,expr) local res,device = {} local function find_aux(expr) if type(expr) == 'table' then if expr[1] then -- array for _,e in ipairs(expr) do find_aux(e) end else -- key/value if expr.deviceURL then device = expr.deviceURL:match(uid) if device then device = device.."#" end end if device and expr.name and (expr.name:sub(1,3)=='io:' or expr.name:sub(1,5)=='core:') and expr.value then local key = device..expr.name if res[key] and res[key]~=expr.value then error("Duplicate key:"..key) end res[key]=expr.value else for k,v in pairs(expr) do find_aux(v) end end end end end uid = uid:gsub("%-","%%-").."#(%d+)$" find_aux(expr) return res end local uid = "io://0810-4343-0200/13610533" keys = find(uid,jsonResponse.devices) for k,v in pairs(keys) do print(k,"=",json.encode(v):sub(1,80)) end and output [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 3#core:LuminanceState = 4 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:RSSILevelState = 36 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:DryingDurationMaxState = 120 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:BoostDurationMaxState = 60 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:FilterFanCloggingErrorState = 12 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:PriorityLockTimerState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:SetpointLoweringTemperatureInProgModeState = 2.5 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 6#core:PowerSourceType = "mainSupply" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:BoostModeDurationState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:IdentifierState = "00000000" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 7#core:TemperatureState = 20.5 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:DiscreteRSSILevelState = "low" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 3#core:MeasuredValueType = "core:LuminanceInLux" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 5#core:TemperatureState = 23.5 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:TargetTemperatureState = 17 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 7#core:MeasuredValueType = "core:TemperatureInCelcius" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:MaximumHeatingLevelState = "unknown" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 5#core:MeasuredValueType = "core:TemperatureInCelcius" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 4#core:MeasuredValueType = "core:RelativeValueInPercentage" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:DateTimeState = {"month":12,"weekday":3,"day":1,"second":50,"year":2022,"minute":48,"hour":11} [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:DerogatedTargetTemperatureState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:ComfortRoomTemperatureState = 17 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:TimeProgramState = [{"monday":[{"start":"04:45","end":"05:45"},{"start":"00:00","end":"00:00"},{"st [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:CumulatedLoweringState = 18 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 3#core:PowerSourceType = "mainSupply" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:CurrentWorkingRateState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:TimerForTransitoryStateState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:LocalLeadTimeState = 3268 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:OperatingModeState = "external" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:VersionState = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:OnOffState = "on" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:NameState = "I2G_Actuator" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:RegulationModeState = "none" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 7#core:PowerSourceType = "mainSupply" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:ModelState = "Symphonik Gauche" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 6#core:StatusState = "available" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:TemperatureProbeCalibrationOffsetState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 4#core:StatusState = "available" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 2#core:OccupancyState = "noPersonInside" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 2#core:StatusState = "available" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:StatusState = "available" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 4#core:RelativeHumidityState = 44 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:EffectiveTemperatureSetpointState = 17 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:PowerState = 1750 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:TowelDryerTemporaryStateState = "permanentHeating" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 7#core:Manufacturer = "Atlantic Group" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 7#core:StatusState = "available" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 6#core:MeasuredValueType = "core:ElectricalEnergyInWh" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 2#core:PowerSourceType = "mainSupply" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 3#core:Manufacturer = "Atlantic Group" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 4#core:Manufacturer = "Atlantic Group" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 5#core:Manufacturer = "Atlantic Group" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 6#core:Manufacturer = "Atlantic Group" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:ControllerAddressState = 13610532 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 6#core:ElectricEnergyConsumptionState = 1011000 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 5#core:PowerSourceType = "mainSupply" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 3#core:StatusState = "available" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 5#core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 4#core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 5#core:StatusState = "available" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:EcoRoomTemperatureState = 2.5 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:TensionState = 235 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:DryingDurationState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:DryingDurationUserParameterState = 60 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:BoostDurationUserParameterState = 30 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 4#core:PowerSourceType = "mainSupply" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:OccupancyState = 0 [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:ManufacturerNameState = "Thermor" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:TargetHeatingLevelState = "comfort" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 3#core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 2#core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:Manufacturer = "Atlantic Group" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 2#core:Manufacturer = "Atlantic Group" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 7#core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 6#core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [16:29:00] [DEBUG] [QUICKAPP1001]: 1#io:AutoProgramState = {"saturday":["CONF_3_NIV1","CONF_3_NIV1","CONF_3_NIV1","CONF_3_NIV1","CONF_3_NIV It only collects keys with the uid and prefixes the keys with the number after # Then you can do print(keys['1#core:ComfortRoomTemperatureState']) The problem with the previous statement ssConsi = (find('name','core:ComfortRoomTemperatureState',find('deviceURL','io://0810%-4343%-0200/13610533..',jsonResponse.devices).states).value) -- #1 OK is that if <uid>#1 and <uid>#2 both have the key 'core:ComfortRoomTemperatureState' we just get the first random - but we should return both. With the new find taking the uid you can ask for the specfic uid number in the result Ex. '1#core:ComfortRoomTemperatureState' of uid "io://0810-4343-0200/13610533" Will that work?
-
or if you want to see all keys keys = find(jsonResponse.devices) for k,v in pairs(keys) do print(k,"=",json.encode(v)) end output [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:TimeProgramState = [{"monday":[{"end":"19:30","start":"18:15"},{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"}]},{"tuesday":[{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"}]},{"wednesday":[{"end":"19:30","start":"18:15"},{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"}]},{"thursday":[{"end":"19:30","start":"18:15"},{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"}]},{"friday":[{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"}]},{"saturday":[{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"}]},{"sunday":[{"end":"19:30","start":"18:00"},{"end":"00:00","start":"00:00"},{"end":"00:00","start":"00:00"}]}] [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:LocalLeadTimeState = 3154 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:FirmwareRevision = "2d202020ffffff210002" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:VersionState = "2d202020ffffff210002" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:ComfortRoomTemperatureState = 19 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:LocalIPv4AddressState = "192.168.1.15" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:RegulationModeState = "none" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:DryingDurationState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:RSSILevelState = 76 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:TensionState = 235 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:MaximumHeatingLevelState = "unknown" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:RelativeHumidityState = 49 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:AutoProgramState = {"thursday":["CONF_3_NIV1",....} [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:ElectricEnergyConsumptionState = 1748000 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:StatusState = "available" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:TemperatureState = 20.5 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:InternalExternalSchedulingTypeState = "internal" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:NameState = "I2G_Actuator" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:OperatingModeState = "external" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:BoostDurationUserParameterState = 60 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:CountryCodeState = "FR" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:ManufacturerNameState = "Thermor" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:MeasuredValueType = "core:TemperatureInCelcius" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:PowerSourceType = "mainSupply" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:FilterFanCloggingErrorState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:DerogatedTargetTemperatureState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:EffectiveTemperatureSetpointState = 14 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:BoostModeDurationState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:OccupancyState = "noPersonInside" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:OnOffState = "on" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:EcoRoomTemperatureState = 5 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:CurrentWorkingRateState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:TowelDryerTemporaryStateState = "permanentHeating" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:ModelState = "Symphonik Gauche" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:DryingDurationMaxState = 120 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:DryingDurationUserParameterState = 90 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:BoostDurationMaxState = 60 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:TargetTemperatureState = 19 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:TargetHeatingLevelState = "eco" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:LuminanceState = 21 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:TimerForTransitoryStateState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:ControllerAddressState = 15984323 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:IdentifierState = "00000000" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:CumulatedLoweringState = 19 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:DiscreteRSSILevelState = "normal" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:PriorityLockTimerState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:TemperatureProbeCalibrationOffsetState = 0 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:Manufacturer = "Atlantic Group" [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:PowerState = 1750 [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: core:DateTimeState = {"weekday":3,"hour":11,"second":48,"minute":48,"month":12,"year":2022,"day":1} [03.12.2022] [10:30:53] [DEBUG] [QUICKAPP1001]: io:SetpointLoweringTemperatureInProgModeState = 5
-
Maybe just collect all the keys starting with "io:" and "core:" ? local function find(expr) local res = {} local function find_aux(expr) if type(expr) == 'table' then if expr[1] then -- array for _,e in ipairs(expr) do find_aux(e) end else -- key/value if expr.name and (expr.name:sub(1,3)=='io:' or expr.name:sub(1,5)=='core:') and expr.value then res[expr.name]=expr.value else for k,v in pairs(expr) do find_aux(v) end end end end end find_aux(expr) return res end keys = find(jsonResponse.devices) print('io:TargetHeatingLevelState=',keys['io:TargetHeatingLevelState']) print('core:RSSILevelState=',keys['core:RSSILevelState']) print('core:LuminanceState=',keys['core:LuminanceState']) print('core:RelativeHumidityState=',keys['core:RelativeHumidityState']) print('core:TemperatureState=',keys['core:TemperatureState']) print('core:ElectricEnergyConsumptionState=',keys['core:ElectricEnergyConsumptionState']) Output [02.12.2022] [23:33:07] [DEBUG] [QUICKAPP1001]: io:TargetHeatingLevelState= eco [02.12.2022] [23:33:07] [DEBUG] [QUICKAPP1001]: core:RSSILevelState= 76.0 [02.12.2022] [23:33:07] [DEBUG] [QUICKAPP1001]: core:LuminanceState= 21 [02.12.2022] [23:33:07] [DEBUG] [QUICKAPP1001]: core:RelativeHumidityState= 49.0 [02.12.2022] [23:33:07] [DEBUG] [QUICKAPP1001]: core:TemperatureState= 20.5 [02.12.2022] [23:33:07] [DEBUG] [QUICKAPP1001]: core:ElectricEnergyConsumptionState= 1748000
-
local function find(key,val,list) for _,e in ipairs(list) do if tostring(e[key]):match( then return e end end end print(find('name','io:TargetHeatingLevelState',find('deviceURL','io://0810%-4343%-0200/13610533..',jsonResponse.devices).states).value) You need to quote '-' in the val string ex. '%-' Also, you match any character is with '.' dot, so '..' will match "#1" Is this what you need?
-
local function find(key,val,list) for _,e in ipairs(list) do if e[key]==val then return e end end end print(find('name','io:TargetHeatingLevelState',find('deviceURL','io://0810-4343-0200/13610533#1',jsonResponse.devices).states).value)
-
This kind of works - it's not super efficient but for functions used once in a while it's ok. The library, ex QA with id 1061 function foo(a,b) return a+b end function bar(a,b) return a*b end -------------------------------------------------- function QuickApp:RPC_CALL(path2,var2,n2,fun,args,qaf) local res if qaf then res = {n2,pcall(self[fun],self,table.unpack(args))} else res = {n2,pcall(_G[fun],table.unpack(args))} end api.put(path2,{name=var2, value=res}) end function print() end Then the client QA do -- could be hidden in a separate QA file local var,cid,n = "RPC"..plugin.mainDeviceId,plugin.mainDeviceId,0 local vinit,path = { name=var, value=""},"/plugins/"..cid.."/variables/"..var api.post("/plugins/"..cid.."/variables",{ name=var, value=""}) -- create var if not exist function fibaro._rpc(id,fun,args,timeout,qaf) n = n + 1 api.put(path,vinit) fibaro.call(id,"RPC_CALL",path,var,n,fun,args,qaf) timeout = os.time()+(timeout or 3) while os.time() < timeout do local r,_ = api.get(path) if r and r.value~="" then r = r.value if r[1] == n then if not r[2] then error(r[3],3) else return select(3,table.unpack(r)) end end end end error(string.format("RPC timeout %s:%d",fun,id),3) end function fibaro.rpc(id,name,timeout) return function(...) return fibaro._rpc(id,name,{...},timeout) end end end local foo = fibaro.rpc(1061,'foo') -- create function for remote function in QA 1061 with name foo local bar = fibaro.rpc(1061,'bar') -- create function for remote function in QA 1061 with name bar function QuickApp:onInit() self:debug("onInit") print(foo(2,4)) print(bar(2,4)) end 100 calls to foo(10,29) takes ~3s, so around 0.03s per call...
-
For a given event it becomes <time to select relevant rule(s)> + <time to execute rule(s)> time-to-execute may be hard to do something about, but If you have a lot of trigger rules, time-to-select will increase, even if the event doesn't trigger any rule at all. It could be optimized by some smart hash-function with the event as the key and possible matching rules as the value, reducing the number of rules needed to be searched through and filtered. Another thing about GEA as I understand it, is that it runs the non-trigger rules every 30s. I guess that every 30s then there is a peak when all rules evaluate their tests - a peak that increase with the number of rules? Here again, and it's a bit more complicated, one could analyze the rule tests when the rules are defined to see what events they depend on. ...and then only invoke the rules that have tests that depends on what events have happened the last 30s. (treating also time as an event...)
-
-- TEST QA function QuickApp:onInit() self:debug("onInit") end local timer function QuickApp:Cligno_ON() self:setVariable("ClignoRouge", "ON") self.cligno = self:getVariable("ClignoRouge") self:trace("var cligno = "..cligno) if not timer then self:loop() end self:trace("Mise en marche du cligno via le bouton") end function QuickApp:Cligno_OFF() self:setVariable("ClignoRouge", "OFF") self.cligno = self:getVariable("ClignoRouge") self:trace("var cligno = "..cligno) if timer then timer=fibaro.clearTimeout(timer) end self:trace("Mise en arret du cligno via le bouton") end ---------------------------------------------------------- --- Boucle loop ---------------------------------------------------------- function QuickApp:loop() self:trace("loop") hub.sleep(1000) hub.call(79, 'turnOn') hub.sleep(1000) hub.call(79, 'turnOff') timer = fibaro.setTimeout(2000, function() self:loop() end) end
-
function fibaro.getQAVariable(id,name) __assert_type(id,"number") __assert_type(name,"string") local props = (api.get("/devices/"..id) or {}).properties or {} for _, v in ipairs(props.quickAppVariables or {}) do if v.name==name then return v.value end end end function fibaro.setQAVariable(id,name,value) __assert_type(id,"number") __assert_type(name,"string") return fibaro.call(id,"setVariable",name,value) end function fibaro.getAllQAVariables(id) __assert_type(id,"number") local props = (api.get("/devices/"..id) or {}).properties or {} local res = {} for _, v in ipairs(props.quickAppVariables or {}) do res[v.name]=v.value end return res end Can be used from both Scenes and QuickApps
-
Your while loop will not give any time for button callbacks to call your Lua functions or update UI i.e. function QuickApp:Cligno_ON() and function QuickApp:Cligno_OFF() You need to use something like setTimeout to give time to your button handlers...
-
If you want "sourceTriggers" in your QA you can use https://forum.fibaro.com/topic/62600-detect-keyid-andor-keyattribute-in-a-qa-loop-from-a-device/?do=findComment&comment=255490 Ex. for customEvents function QuickApp:sourceTrigger(event) -- callback for subscribed events print("Event:",event.name) end function QuickApp:onInit() self:debug("Started") self:clearSubscriptions() -- Every restart, re-subscribe self:subscribe({type='custom-event', name='E1'}) -- Subscribe to custom event self:subscribe({type='custom-event', name='E2'}) -- Subscribe to custom event end ----------- Helper functions -------------- function QuickApp:clearSubscriptions() self:setVariable('TRIGGER_SUB',{}) end function QuickApp:subscribe(event) local s = self:getVariable('TRIGGER_SUB') if type(s)~='table' then s = {} end s[#s+1]=event self:setVariable('TRIGGER_SUB',s) end
- 16 réponses
-
- 2
-
-
-
- déclencheur
- event
-
(et 1 en plus)
Étiqueté avec :
-
Sorry, 'time' was not necessary in the last example (cut&paste error...) Another example is json.decode that takes a second optional value in QAs, not in Scenes! They have different implementations. In QAs this will then give an error a = json.decode(fibaro.getGlobalVariable("X")) as json.decode will expect the second argument to be a table with parsing options, and in this case gets the modification time, and will throw an error. Solution, like the last example is a = json.decode((fibaro.getGlobalVariable("X")))
-
fibaro.getGlobalVariable returns 2 values. String value of variable and time it was last changed. Both these values are then passed to tonumber as arguments The problem is that tonumber can take a second argument, the base for the string->number conversion. In this case it will interpret the change time as the base which is probably out of range. You can throw away all but the first returned value by putting the expression within parentheses. UserIDA = tonumber (( fibaro.getGlobalVariable ( " UserIDA " ))) This will work...
-
function QuickApp:pipup() self:debug("envoi du pipup") local data = { duration = "30", position = "2", -- 0 à 4 title = "Test PIPUP", titleColor = "#0066cc", titleSize = "20", message = "Message de Test Pipup", messageColor = "#000000", messageSize = "14", backgroundColor = "#ffffff", media = { image = { uri="https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/cfcc3137009463.5731d08bd66a1.png", width=480 } } } local pipup = net.HTTPClient():request("http://192.168.0.22:7979/notify",{ options = { method = "POST", headers = { ["Content-Type"] = "application/json" }, data = (json.encode(data)) }, success = function(response) self:debug(json.encode(response)) self:debug(json.encode(data)) end, error = function(err) self:error(err) end }) end
-
Don't lose any sleep over the post. The simple take-away was that Fibaro could have let us have both synchronous net.HTTPClient calls and asynchronous setTimeout, that would have made it easier for developers - and that with just a few lines of code. (then to prove it I gave an implementation, but that may be a bit advanced to follow, and put you to sleep )
-
If net.HTTPClient() was synchronous (returning the result immediately) it would have looked like local success,res = net.HTTPClient():request("http://myurl",options) if success then print(res) end a little like in the old HC2 VDs. If the function takes other functions for success/error or callback it's a hint that the function is asynchronous. E.g. will return immediately and later call your functions when it is ready. This is usually much more efficient if you have something else to do while you wait for the work to be completed. ...IF you have something else to do while you wait. Most code in the forums don't and a lot of beginners stumble on this. Fibaro could have let us both have the cake and eat it, however Fibaro is not very "developer friendly". With a few lines of code in the QA we could have had synchronous net.HTTPClient() and asynchronous setTimeout calls. If we wanted "parallel" net.HTTPClient() calls we would just call them in separate setTimeout functions. Just look at the section "Your QA code" in the code below QuickApp = {} ------------- Your QA code ------------ function QuickApp:ping() -- setTimeout loop logging "ping" every second self:debug("ping") setTimeout(function() self:ping() end,1000) end function QuickApp:onInit() self:debug("MyQA") setTimeout(function() self:ping() end,0) -- Start ping loop local success,res = net.HTTPClient():request("http://myurl") -- Run http request and wait for result if success==true then -- If success print result self:debug("Success:",res) end end ------------- End ---------------------- local http = {} net = { HTTPClient = function() return http end } function http:request(url) -- Synchronous http call that respect other timers print("net.HTTPClient: Calling ",url,"-will take 5 seconds") local co = coroutine.running() setTimeout(function() print("net.HTTPClient: ready") coroutine.resume(co,true,"This is the result") -- We always succeed end,5000) return coroutine.yield() end local timers = {} function setTimeout(f,t) table.insert(timers,{coroutine.wrap(f),t/1000+os.time()}) table.sort(timers,function(a,b) return a[2] <= b[2] end) end if QuickApp.onInit then function QuickApp:debug(...) print(os.date("%c"),...) end setTimeout(function() QuickApp:onInit() end,0) end while true do local t = table.remove(timers,1) while t[2] > os.time() do end t[1]() end It will run the "ping" loop while the net.HTTPClient() call waits for its result. We can write much more "natural" code and everyone would be more happy, and we would see less buggy code... However, it doesn't work on the HC3 as Fibaro in its infinite wisdom has decided to disable coroutines that is one of the biggest features of Lua. (You can try to run the example in any other "full" Lua environment to see it in action...) 'coroutines', in short, allow us to suspend execution of the code in the middle of your function (yield) and then later "wake up" the function so that it continues to run from where it was suspended (resume). In our code we let setTimeout wrap the function in a coroutine so that the function can be suspended and resumed. In the case above we suspend the net.HTTPClient() call, and then when it's ready (5s later) we wake it up so the function can return the result to the waiting caller. local success,res = net.HTTPClient():request("http://myurl") So, Lua is a very powerful language but we are unfortunately only allowed to use a small part of it...
-
local id = resp[1].nukiId
-
The problem you have running several 'setAudioAlarm' in parallel is that you store values in self. that is shared between all calls to 'setAudioAlarm'. It could work better if you sent them as parameters to the call, so each call had it's own version of the parameters. Running them with a random delay is like playing the lottery... where the prize is a buggy program. You still have the problem with nesting calls in request callbacks. Each event has it's own set of parameters in the .p key. The advantage of sending user/pwd with each event is that cameras can have different credentials. If not we could have used self.use and self.pwd instead in 'setAudioAlarm'. I also don't understand the enable/toggle logic. I guess you want to alternate between turning on and turning off the camera? To make it correct you should first read the enable state with a call and then set it to the opposite with another call. Otherwise you may get out of sync... So, first call checkSiren, set the p.enable to the opposite and call changeSirenToggle You could also have 2 buttons, turnOn and turnOff and send the enable state 1/0 with them (like I tried in turnOn, should enable be 1 or 0 ?) Btw, I put the QuickApp:turnOn function in the same QA as the rest of the code (using local 'post' function). Maybe you wanted to do it from another QA? I would have coded it this way, using the model of events. This way you avoid the nesting. It may be a bit advanced, but worth studying if you have trouble sleeping Disclaimer: I don't have access to a Reolink camera so I really don't know what I'm doing local testing = true -- simulate api calls local post function QuickApp:turnOn() self:debug( "binary switch turned on" ) self:updateProperty( "value" , true ) post({type='setAudioAlarm',p={camera="192.168.1.46", tool="1",user=self.user,pwd=self.pwd,enable=1}}) -- 'p' stands for 'parameters' post({type='setAudioAlarm',p={camera="192.168.1.48", tool="1",user=self.user,pwd=self.pwd,enable=1}}) -- should enable be 1 ? post({type='setAudioAlarm',p={camera="192.168.1.45", tool="1",user=self.user,pwd=self.pwd,enable=1}}) end function QuickApp:turnOff() self:debug( "binary switch turned off" ) self:updateProperty( "value" , false ) post({type='setAudioAlarm',p={camera="192.168.1.46", tool="1",user=self.user,pwd=self.pwd,enable=0}}) post({type='setAudioAlarm',p={camera="192.168.1.48", tool="1",user=self.user,pwd=self.pwd,enable=0}}) post({type='setAudioAlarm',p={camera="192.168.1.45", tool="1",user=self.user,pwd=self.pwd,enable=0}}) end local debug = true local EVENT={} local function DEBUG(...) if debug then print(...) end end function post(event) setTimeout(function() assert(EVENT[tostring(event.type)],"Undefined event ",tostring(event.type)) DEBUG("Event:",event.type) EVENT[event.type](event) end,0) end -------------------------------------------------- -------------------------------------------------- -- senCmd -------------------------------------------------- -------------------------------------------------- local function sendCmd(event,method,cmd,data) local url = "https://"..event.p.camera..":443/api.cgi?cmd="..cmd self.http:request(url,{ options = { checkCertificate= false , method= method or "GET" , headers = { [ "content-type" ] = "application/json" , [ "Accept" ] = "application/json" , }, data = data and json.encode(data) or nil }, success = function(resp) if resp.status == 200 then DEBUG("success ",url,resp.data) post({type=event.type.."_success",p=event.p,url=url,data=json.decode(resp.data)}) else post({type=event.type.."_error",p=event.p,url=url,error="status="..resp.status}) end end, error = function(err) post({type=event.type.."_error",p=event.p,url=url,error=err}) end } ) end if testing then -- redefine sendCmd for test purpose when no access to Relink function sendCmd(event,method,path,data) local data = {} if path:match("Login") then data = {{value={Token={name="myToken"}}}} elseif path:match("GetAudioAlarmV20") then data = {{value={Audio={enable=0}}}} end DEBUG("success ","https://"..event.p.camera..":443/api.cgi?cmd="..path,json.encode(data)) post({type=event.type.."_success",p=event.p,url=url,data=data}) end end -------------------------------------------------- -------------------------------------------------- -- connection to the camera and recovery of the token, if ok call ChangeSirenToggle -------------------------------------------------- -------------------------------------------------- function EVENT.setAudioAlarm(event) sendCmd(event,"POST","Login", { [ 'cmd' ]= "Login" , [ 'param' ]={ [ 'User' ]={ [ 'userName' ]=event.p.user, [ 'password' ]=event.p.pwd } } } ) end local function add(t,a) for k,v in pairs(a) do t[k]=v end return t end function EVENT.setAudioAlarm_success(event) local token = event.data[ 1 ][ "value" ][ "Token" ][ "name" ] -- login token DEBUG( "Camera(): Connected" ) -- Call to change the siren parameter post({type='ChangeSirenToggle',p=add(event.p,{token=token})}) end function EVENT.setAudioAlarm_error(event) quickApp:error( "Identification problem - Error - " , json.encode(event.error,event.url or "")) end -------------------------------------------------- -------------------------------------------------- -- Change siren parameter -------------------------------------------------- -------------------------------------------------- function EVENT.ChangeSirenToggle(event) sendCmd(event,"POST","SetAudioAlarmV20&token="..event.p.token, { [ 'cmd' ]= "SetAudioAlarmV20" , [ 'param' ]= { [ 'Audio' ]= { [ 'enable' ]= event.p.enable, [ 'schedule' ]= { [ 'channel' ]= 0 , [ 'table' ]= { [ 'MD' ]= string.rep("1",168) } } } } } ) end function EVENT.ChangeSirenToggle_success(event) post({type='checkSiren',p=event.p}) --- print siren value after update end function EVENT.ChangeSirenToggle_error(event) quickApp:updateView( "message" , "Title" , "Identification problem" ..json.encode(event.error)) quickApp:error( "Problem updating Siren parameter - Error - " , json.encode(event.error)) end --------------------------- -- Check the siren parameter value ---------------------------- function EVENT.checkSiren(event) sendCmd(event,"GET","GetAudioAlarmV20&token=" ..event.p.token, { [ 'cmd' ]= "GetAudioAlarmV20" , [ 'param' ]= { [ 'channel' ]= 0 } } ) end function EVENT.checkSiren_success(event) local siren=event.data[ 1 ][ "value" ][ "Audio" ][ "enable" ] print(event.p.camera.. "'s siren is on" , siren) post({type='logout',p=event.p}) --(self.camera, self.token) end function EVENT.checkSiren_error(event) quickApp:error( "Problem updating Siren parameter - Error - " , json.encode(event.error)) --self:logout(self.camera, self.enable) post({type='logout',p=event.p}) end -------------------------------------------------- -------------------------------------------------- -- Logout -------------------------------------------------- -------------------------------------------------- function EVENT.logout(event) sendCmd(event,"POST","Logout&token=" ..event.p.token, { [ 'cmd' ]= "Logout" , [ 'param' ]= {} } ) end function EVENT.logout_success(event) print( "Logout from " ..event.p.camera.. " done" ) end function EVENT.logout_error(event) quickApp:error( "Problem logging out - " , json.encode(event.error)) end -------------------------------------------------- -------------------------------------------------- -------------------- -- main loop -------------------------------------------------- -------------------------------------------------- -------------------- function QuickApp:mainLoop() -- self:logout(self.camera, self.enable) --self:setAudioAlarm(self.camera, self.enable) end -------------------------------------------------- -------------------------------------------------- ----------------- -- -------------------------------------------------- -------------------------------------------------- ----------------- function QuickApp:onInit() __TAG="QA_Reolink"..plugin.mainDeviceId self:trace( "------------------------------------------------------------ -----" ) self:trace( "-- Starting Reolink QA" ) self:trace( "------------------------------------------------------------ -----" ) --- login self.user = self:getVariable( "login" ) ---password self.pwd = self:getVariable( "password" ) --- IP URL -- self.camera = self:getVariable( "IP_Reolink" ) self.http = net.HTTPClient({ timeout = 3000 }) -- Toggle -- self.enable = 0 -- self:mainLoop() if testing then setTimeout(function() self:turnOn() end,1) end -- Just testing to push button... end
-
a = 1 b = 24 print(a << 8 | b) > 280
-
Ok, I have seen those errors too, despite the protective code. Maybe protect the net.HTTPClient():request(...) call too? and maybe the QuickApp:onInit().... ...just thinking out loud.
-
What is your QA doing? HTTP calls?
