LUUP function works in a scene, not globally.

So I was testing a LUUP function to ramp up these GE lights. It works great if they are individual scenes per room. I decided to go ahead and make the function portable so I would be able to call in from anywhere, and that’s where things go south. LUUP code in the scene :
local device = 20
local LightState = luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, device)
local rampVal = LightState
local rampRate = 2
local rampTarget = 100
local sleepTime = 6
for i = 1,100 do
if (tonumber(rampVal) > tonumber(rampTarget)) then
rampVal=rampTarget
end
if (tonumber(rampVal) < tonumber(rampTarget)) then
rampVal=rampVal + rampRate
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = rampVal}, device)
luup.sleep(sleepTime)
end
end

The function :
function local_functions.ramp_up(device,rampTarget,rampRate,rampDelay)
local LightState = luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, device)
local rampVal=LightState
for i = 1,100 do
if (tonumber(rampVal) > tonumber(rampTarget)) then
rampVal=rampTarget
end
if (tonumber(rampVal) < tonumber(rampTarget)) then
rampVal=rampVal + rampRate
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = rampVal}, device)
luup.log("Ramping up to level : " … rampVal)
if (tonumber(rampVal) == tonumber(rampTarget)) then
i = 100
end
luup.sleep(sleepTime)
end
end
luup.log(“We are done”)
end

So I call the function, and it does mostly what I ask of it, though there is a difference. After the last call, the dimmer goes to a random value, then back to 100(or whatever the ramp target is). From the logs :
04 11/12/13 23:58:27.303 <0x2e8cc680>
50 11/12/13 23:58:27.304 luup_log:0: Ramping up to level : 100 <0x2e8cc680>
50 11/12/13 23:58:27.306 luup_log:0: We are done <0x2e8cc680>
02 11/12/13 23:58:27.373 UPDATE MANUAL ROUTE2 11=0xb7e1c7 <0x2c25f680>
02 11/12/13 23:58:27.374 ZW_Send_Data node 11 USING ROUTE 8.0.0.0 <0x2c25f680>
02 11/12/13 23:58:27.483 UPDATE MANUAL ROUTE2 11=0xb7e1c7 <0x2c25f680>
02 11/12/13 23:58:27.484 ZW_Send_Data node 11 USING ROUTE 8.0.0.0 <0x2c25f680>
06 11/12/13 23:58:27.592 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelStatus was: 0 now: 10 #hooks: 2 upnp: 0 v:0xb97628/NONE duplicate:0 <0x2c05f680>
06 11/12/13 23:58:27.593 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:SwitchPower1 variable: Status was: 0 now: 1 #hooks: 0 upnp: 0 v:0xbab4c0/NONE duplicate:0 <0x2c05f680>
04 11/12/13 23:58:27.594 <0x2c05f680>
02 11/12/13 23:58:27.596 UPDATE MANUAL ROUTE2 11=0xb7e1c7 <0x2c25f680>
02 11/12/13 23:58:27.596 ZW_Send_Data node 11 USING ROUTE 8.0.0.0 <0x2c25f680>
06 11/12/13 23:58:27.702 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelStatus was: 10 now: 100 #hooks: 2 upnp: 0 v:0xb97628/NONE duplicate:0 <0x2c05f680>
06 11/12/13 23:58:27.703 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:SwitchPower1 variable: Status was: 1 now: 1 #hooks: 0 upnp: 0 v:0xbab4c0/NONE duplicate:1 <0x2c05f680>
04 11/12/13 23:58:27.704 <0x2c05f680>

So in this example, I just copied the last “setTarget” that sets it to 100%, you see the log entry for “we are all done”, meaning the loop count it done. Then right after that it send the target from what it thinks is “0” to 10, then from 10 back to 100. Obviously this is not right, but I’m not sure why.

Any ideas?

There are a few things that may be contributing to your problem.

You are not passing the function a value for sleepTime so where it is getting this from? It is not a good idea to try to exit a for loop by forcing the loop variable. Use break. Your logic does strange things when rampVal is greater than rampTarget: It sets rampVal to rampTarget but does not perform the luup.call_action. Whilst it shouldn’t do any harm, you do not need to use tonumber(…) on an integer variable - only for strings.

Try:

function local_functions.ramp_up(device,rampTarget,rampRate,rampDelay) local LightState = luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelStatus", device) local rampVal=tonumber(LightState) local sleepTime = 6 for i = 1,100 do if (rampVal < rampTarget) then rampVal=rampVal + rampRate end if (rampVal > rampTarget) then rampVal=rampTarget end luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {newLoadlevelTarget = rampVal}, device) luup.log("Ramping up to level : " .. rampVal) luup.sleep(sleepTime) if (rampVal == rampTarget) then break end end luup.log("We are done") end

Yea, that didn’t make a difference. Here is the log :

06 11/13/13 11:29:39.685 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelTarget was: 90 now: 100 #hooks: 0 upnp: 0 v:0xfa2b70/NONE duplicate:0 <0x2dbde680>
02 11/13/13 11:29:39.696 ZWJob_SendData UPDATE MANUAL ROUTE 11=0xf60d27 <0x2dbde680>
04 11/13/13 11:29:39.698 <0x2dbde680>
50 11/13/13 11:29:39.700 luup_log:0: Ramping up to level : 100 <0x2dbde680>
02 11/13/13 11:29:39.728 UPDATE MANUAL ROUTE2 11=0xf60d27 <0x2b7b3680>
02 11/13/13 11:29:39.728 ZW_Send_Data node 11 USING ROUTE 7.0.0.0 <0x2b7b3680>
50 11/13/13 11:29:39.760 luup_log:0: We are done <0x2dbde680>
02 11/13/13 11:29:39.838 UPDATE MANUAL ROUTE2 11=0xf60d27 <0x2b7b3680>
02 11/13/13 11:29:39.838 ZW_Send_Data node 11 USING ROUTE 7.0.0.0 <0x2b7b3680>
06 11/13/13 11:29:39.946 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelStatus was: 0 now: 80 #hooks: 3 upnp: 0 v:0xfa2b90/NONE duplicate:0 <0x2b3b3680>
06 11/13/13 11:29:39.947 Device_Variable::m_szValue_set device: 20 service: urn:micasaverde-com:serviceId:EnergyMetering1 variable: Watts was: 0 now: 0 #hooks: 0 upnp: 0 v:0xfa3098/NONE duplicate:1 <0x2b3b3680>
06 11/13/13 11:29:39.948 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:SwitchPower1 variable: Status was: 0 now: 1 #hooks: 1 upnp: 0 v:0xf8bfc0/NONE duplicate:0 <0x2b3b3680>
06 11/13/13 11:29:39.948 Device_Variable::m_szValue_set device: 20 service: urn:micasaverde-com:serviceId:EnergyMetering1 variable: Watts was: 0 now: 128 #hooks: 0 upnp: 0 v:0xfa3098/NONE duplicate:0 <0x2b3b3680>
06 11/13/13 11:29:39.949 Device_Variable::m_szValue_set device: 20 service: urn:micasaverde-com:serviceId:EnergyMetering1 variable: Log was: 0,23,160,1384358033,1434 now: 128,9,160,1384360179,3580 #hooks: 0 upnp: 0 v:(nil)/NONE duplicate:0 <0x2b3b3680>
04 11/13/13 11:29:39.950 <0x2b3b3680>
02 11/13/13 11:29:39.952 UPDATE MANUAL ROUTE2 11=0xf60d27 <0x2b7b3680>
02 11/13/13 11:29:39.952 ZW_Send_Data node 11 USING ROUTE 7.0.0.0 <0x2b7b3680>
06 11/13/13 11:29:40.056 Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelStatus was: 80 now: 100 #hooks: 3 upnp: 0 v:0xfa2b90/NONE duplicate:0 <0x2b3b3680>

Thank you for taking the time to help out, but it doesn’t appear to make much difference. It’s still going all the way up to 100, then in this case down to 80, then back up. Again, only happens w/ the global function. If I add the same exact code to a scene and run it, all is well.

The log shows that Vera is still running jobs to set the LoadLevel. The issue may be that you are firing off actions faster than Vera/Z-Wave can respond and some of the jobs are getting queued and only run when you’ve stopped kicking-off new ones. You could prevent this by monitoring LoadLevelStatus and only firing a new action when the previous one has completed.

Where does it show that it’s still running actions? I’ve even tried to dumb this down and put a larger delay in there to make sure that wasn’t the case, and it does the same thing.

I have set it to step by 10 so it was nice and easy, and I see this :
Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelTarget was: 0 now: 10 #hooks: 0 upnp: 0 v:0xc40720/NONE duplicate:0 <0x2e660680>

all the way to :
Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelTarget was: 90 now: 100 #hooks: 0 upnp: 0 v:0xc40720/NONE duplicate:0 <0x2e660680>

Then about 300ms later :
Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelStatus was: 0 now: 90 #hooks: 3 upnp: 0 v:0xc2a7a8/NONE duplicate:0 <0x2be35680>

And 100ms later :
Device_Variable::m_szValue_set device: 20 service: urn:upnp-org:serviceId:Dimming1 variable: LoadLevelStatus was: 90 now: 100 #hooks: 3 upnp: 0 v:0xc2a7a8/NONE duplicate:0 <0x2be35680>

And that also doesn’t seem to explain why it works fine for a scene, but not a global function. I even tried in the scene version to step by 1, and delay almost nothing so I could “flood” the Vera w/ a ton of updates, and it works perfectly.

Where does it show that it's still running actions?
See the entries for Job IDs 11 and 10. Also note that Job 11 was aborted and replaced by Job 12. I think you are filling-up Vera's job queue faster than it can respond and things are getting out of sequence.

I don’t know why it would work in a scene. I have observed different response times for actions between plugins and scenes so perhaps Luup prioritizes calls from scenes differently.

Weird. I just changed it to jump by 20, meaning it would just fire 5 events and it did the same thing. Think I should contact MCV? Or is there another better way to do the same thing?

I don’t know whether MCV would be able to help but you can try.

If it was my project, I would build in a check for LoadLevelStatus and use that to pace the action calls to see if that made any difference. I saw a similar problem with sending rapid on/off sequences to a relay from a scene a while back. Sometimes it worked and other times some of the transitions got lost. Once I added a check on the Status so that I didn’t send a new action until the previous one had completed, everything worked fine.

Any chance you’d be willing to give me an idea on how to do that without holding things up. I think I could do it w/ a for loop and just check to see if the value went up, but I don’t want to hold up processing other events too much if I can avoid it.

Thank you for your help, btw! Greatly appreciated.

The proper way to do it is by using luup.variable_watch to make a callback when the variable changes. The callback function needs to be a global reference so either in a plugin or startup lua.

To check if it solves the problem, you could try a simpler approach and use a loop with sleep:

luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {newLoadlevelTarget = rampVal}, device) for j=1,20 do luup.sleep(100) if (tonumber(luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelStatus", device)) == rampVal) then break end end etc...

I guess the problem with that is they are not instant status lights. So it does take forever. It’s just odd to me that I can run it from a Scene, but I can’t have it global.

I guess for the lights I need to do this for I will just have to call the scene to be run.

Could I suggest that the code is maybe written in a way that is much more complex than it needs to be and thus much harder to read and debug. If I may offer a couple of suggestions and observations you might find writing and maintaining you code a little easier.

Firstly there are 3 primary control structures available for general looping operations. These are “While Do”, “Repeat Until” and “For”. You chose to use a “For” loop to manage the repetative stepping in your logic. Given what you are trying to do the “For” type of loop is probably not the most appropriate because “For” loops are generally used when you know the number of cycles you want to perform. The other two types are used when you want the loop to automatically end when a condition is met. In your instance you really don’t have a finite number of cycles, or at least you don’t the way this was coded thus you need to use the break statement to get out of it when you suddenly find that you have gone through the loop enough. Try a “While Do” loop for your scenario. This would be:

While (tonumber(rampVal) <> tonumber(rampTarget)) Do

End

The advantage of this is:

a) if rampVal is already equal do rampTarget then the loop never executes so no need to break or internal loop logic to tell it not to do something because it should never have made it into the loop
b) when the target is achieved the loop automatically ends thus no break logic when complete
c) The objective of the loop is obvious to anybody who reads it
d) Less valiables which have no practical purpose for your logic such as the loop counter itself

Second, and this may be one of your problems, you have two “If” statements within the loop but these don’t cover all the scenarios. The first “If” deals with rampVal running past the desired rampTarget and the second deals with the rampVal being less than the rampTarget. What you don’t have is any logic which deals with rampVal actually being equal to the target which can easily cause the loop to run in circles doing nothing. In fact your first If statement may set the rampVal to be equal to the rampTarget then there is no logic to terminate your loop so the loop will run 100 times and do nothing.

Thirdly, you have encapsulated logic into the second If statement which should apply to both if statements. So if the first if statement correctly detects that the rampVal is greater than rampTarget it changes the rampVal but does nothing with it. It never makes the call_actions command so what is the point of this if statement since it does nothing with the updated value?

Could I suggest that your function could look like:

The function :
function local_functions.ramp_up(device,rampTarget,rampRate,rampDelay)
local LightState = luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, device)
local rampVal=LightState
While (tonumber(rampVal) <> tonumber(rampTarget)) Do
if (tonumber(rampVal) < tonumber(rampTarget)) then
rampVal=rampVal + rampRate
end
if (tonumber(rampVal) > tonumber(rampTarget)) then
rampVal=rampTarget
end
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = rampVal}, device)
luup.log("Ramping up to level : " … rampVal)
luup.sleep(sleepTime)
end
luup.log(“We are done”)
end

The benefits:

a) Loop never executes if it doesn’t need to saving processing time
b) No complex loop exit logic. Everything is declared at the loop start so easy to read, understand and debug
c) Eliminates the need to cover the missing if statement discussed above
d) Resequencing the two if statements fixes another bug in the second of your two if statements, ie the scenario that you are incrementing rampVal by say 10 but the starting point is 95. Since you don’t check this within your if statement before you fire off the call_action. Resequencing fixes this
e) Moving the call_action and log commands out of the if statements ensures that any actual changes to the rampVal get applied before the loop ends. That isn’t happening in your current logic.

I hope this helps. I haven’t changed any other logic or values but equally I haven’t passed this through to check for errors so you may want to validate the syntax.

Giddy,
Thank you for that, though it did not work either. Did the same thing. Seems the only way I can run this is from a scene. Lua must be doing something at some point in there that checks the Load Status and since it’s not an instant status light it knows it’s supposed to go to “100”, but it starts off back at 0 again.

@bigbasec

... make the function portable so I would be able to call in from anywhere .....

How did you make the function portable?