Author Topic: 1 scene script works, same script multiple scenes, chaos  (Read 3482 times)

Offline rsampayo

  • Newbie
  • *
  • Posts: 19
  • Karma: +0/-0
1 scene script works, same script multiple scenes, chaos
« on: February 14, 2014, 02:18:51 am »
Can some one help me out.
I have a script to turn on a light when there is motion, and turn it of after no motion has been detected for a while.
Problem is, the script works flawlessly when I test it with one scene, once I set it up on many scenes and walk a across multiple sensor , they all turn on but usually the last  scene to trigger doesnt turn off.

------------
local DEVICE_NO_SENS_L  = 29 -- the light sensor device number
local DEVICE_NO_SENS_M  = 32  -- the motion sensor device number
local DEVICE_NO_DIM = 20 -- the dimmer device number

local DELAY = 30 -- time the light remains on
local LOW_LEVEL  = 100 -- the light level threshold for night
local DIM_LEVEL = 35 -- Dimmer load level threshold

local LS_SID  = "urn:micasaverde-com:serviceId:LightSensor1" -- the LightSensor service ID
local SS_SID = "urn:micasaverde-com:serviceId:SecuritySensor1" -- Security Sensor Service ID
local DIM_SID  = "urn:upnp-org:serviceId:Dimming1" -- the Dimmer service ID

local currentLevel = luup.variable_get (LS_SID, "CurrentLevel", DEVICE_NO_SENS_L) or 0
local lightLevel = luup.variable_get(DIM_SID, "LoadLevelStatus", DEVICE_NO_DIM) or 0
currentLevel = tonumber(currentLevel)
lightLevel =   tonumber(lightLevel)

if (currentLevel >= LOW_LEVEL or lightLevel >= DIM_LEVEL) then return false end
luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget=DIM_LEVEL }, DEVICE_NO_DIM)

function checkLastTrip()
   local lastTrip = luup.variable_get (SS_SID, "LastTrip", DEVICE_NO_SENS_M) or os.time()
   local lightLevel = luup.variable_get(DIM_SID, "LoadLevelStatus", DEVICE_NO_DIM) or 0
        lightLevel = tonumber(lightLevel)
   if (lightLevel ~= DIM_LEVEL) then return false end
   if (os.difftime (os.time(), tonumber (lastTrip)) >= DELAY) then
      luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget="100" },DEVICE_NO_DIM)
     luup.sleep(100)
     luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget="0" },DEVICE_NO_DIM)
  else
      luup.call_delay ("checkLastTrip", DELAY) -- Check when the sensor was last tripped every <DELAY> seconds.
  end
end
 
luup.call_delay ("checkLastTrip", DELAY)
 
return true

---------

Offline RexBeckett

  • Beta Testers
  • Master Member
  • *****
  • Posts: 3891
  • Karma: +482/-12
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #1 on: February 14, 2014, 03:18:23 am »
There are two problems at work here:

checkLastTrip() is global so, if you have multiple scenes, it will be overwritten each time a new scene runs. This is not a problem if the function is identical and self-contained. It is safer to change the name for each scene or place a single copy of the function in Startup Lua.

Your global function is accessing local variables. Most of the time they will no longer exist when the call-back occurs. When you have multiple scenes running, a call-back may get lucky and find valid local variables but they will probably be from a different scene. Result: chaos...

There are two fixes to this: Create uniquely-named call-back functions for each scene with the parameters hard-coded -or- use a single function but pass the parameters in the optional call-back string. There are examples of how to pass parameters here and here.
   

Offline rsampayo

  • Newbie
  • *
  • Posts: 19
  • Karma: +0/-0
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #2 on: February 15, 2014, 03:54:48 pm »
There are two problems at work here:

checkLastTrip() is global so, if you have multiple scenes, it will be overwritten each time a new scene runs. This is not a problem if the function is identical and self-contained. It is safer to change the name for each scene or place a single copy of the function in Startup Lua.

Your global function is accessing local variables. Most of the time they will no longer exist when the call-back occurs. When you have multiple scenes running, a call-back may get lucky and find valid local variables but they will probably be from a different scene. Result: chaos...

There are two fixes to this: Create uniquely-named call-back functions for each scene with the parameters hard-coded -or- use a single function but pass the parameters in the optional call-back string. There are examples of how to pass parameters here and here.
 

Thanks RexBeckett,
I read your posts and came up with this, what do you think?  One thing Im not sure is if the while loop will end with the return false.

-------
local DEVICE_NO_SENS_L  = 29 -- the light sensor device number
local DEVICE_NO_SENS_M  = 32  -- the motion sensor device number
local DEVICE_NO_DIM = 20 -- the dimmer device number

local DELAY = 30 -- time the light remains on
local LOW_LEVEL  = 100 -- the light level threshold for night
local DIM_LEVEL = 35 -- Dimmer load level threshold

local LS_SID  = "urn:micasaverde-com:serviceId:LightSensor1" -- the LightSensor service ID
local DIM_SID  = "urn:upnp-org:serviceId:Dimming1" -- the Dimmer service ID

local currentLevel = luup.variable_get (LS_SID, "CurrentLevel", DEVICE_NO_SENS_L) or 0
local lightLevel = luup.variable_get(DIM_SID, "LoadLevelStatus", DEVICE_NO_DIM) or 0
currentLevel = tonumber(currentLevel)
lightLevel = tonumber(lightLevel)

-- LIGHT ON --
if (currentLevel >= LOW_LEVEL or lightLevel >= DIM_LEVEL) then return false end
luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget=DIM_LEVEL }, DEVICE_NO_DIM)
--

-- LOOP CHECK MOVEMENT-
while true do
   local prms = string.format("%d,%d,%d",DEVICE_NO_SENS_M,DEVICE_NO_DIM,DELAY)
   luup.call_delay ("checkLastTrip", DELAY, prms)
end
--

return true


-- FUNCTION IN STARTUP LUA --
function checkLastTrip(parms)
  local SS_SID = "urn:micasaverde-com:serviceId:SecuritySensor1" -- Security Sensor Service ID
  local DIM_SID  = "urn:upnp-org:serviceId:Dimming1" -- the Dimmer service ID
  local DIM_LEVEL = 35

   local pdev,ptgt,pstp = string.match(parms,"(%d+),(%d+),([%-%d]+)")
  local DEVICE_NO_SENS_M = tonumber(pdev)
  local DEVICE_NO_DIM = tonumber(ptgt)
  local DELAY = tonumber(pstp)
   
   local lastTrip = luup.variable_get (SS_SID, "LastTrip", DEVICE_NO_SENS_M) or os.time()
   local lightLevel = luup.variable_get(DIM_SID, "LoadLevelStatus", DEVICE_NO_DIM) or 0
  lightLevel = tonumber(lightLevel)
   if (lightLevel ~= DIM_LEVEL) then return false end
   if (os.difftime (os.time(), tonumber (lastTrip)) >= DELAY) then
      luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget="100" },DEVICE_NO_DIM)
     luup.sleep(100)
     luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget="0" },DEVICE_NO_DIM)
     return false -- Will this end the "while" therefor ending the script??
  else
      return true
  end
end
--
--special thanks to RexBeckett

----------

Offline RichardTSchaefer

  • Community Beta
  • Master Member
  • ******
  • Posts: 10091
  • Karma: +764/-143
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #3 on: February 15, 2014, 05:50:36 pm »
Code: [Select]
-- LOOP CHECK MOVEMENT-
while true do
   local prms = string.format("%d,%d,%d",DEVICE_NO_SENS_M,DEVICE_NO_DIM,DELAY)
   luup.call_delay ("checkLastTrip", DELAY, prms)
end

This loop runs forever, it queues up requests to run checkLastTrip until Vera runs out of memory and restarts the LuaUPnP process.
Note: The call to luup.call_delay only takes a fraction of a mS .. and returns right away. It does NOT wait for the job to rub.
          And as I said ... the loop runs for ever. I am sure you will run out of memory long before the 30 sec delay expires.


Offline RexBeckett

  • Beta Testers
  • Master Member
  • *****
  • Posts: 3891
  • Karma: +482/-12
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #4 on: February 15, 2014, 07:21:41 pm »
As @RichardTSchaefer pointed out, you do not want the while loop. There is no way to communicate back from the delayed call-back function to the calling code.

Have your main scene code turn on the light and make the luup.call_delay(...) just once then exit. Have the delayed call-back function check for motion. If there has been motion, make another luup.call_delay(...) to itself - using the original parameter. If there has been no motion, just turn off the light.

Offline rsampayo

  • Newbie
  • *
  • Posts: 19
  • Karma: +0/-0
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #5 on: February 15, 2014, 07:57:10 pm »
As @RichardTSchaefer pointed out, you do not want the while loop. There is no way to communicate back from the delayed call-back function to the calling code.

Have your main scene code turn on the light and make the luup.call_delay(...) just once then exit. Have the delayed call-back function check for motion. If there has been motion, make another luup.call_delay(...) to itself - using the original parameter. If there has been no motion, just turn off the light.

Thank you @RichardTSchaefer and @RexBeckett

so the function in startup lua calls itself

--

local DEVICE_NO_SENS_L  = 29 -- the light sensor device number
local DEVICE_NO_SENS_M  = 32  -- the motion sensor device number
local DEVICE_NO_DIM = 20 -- the dimmer device number

local DELAY = 30 -- time the light remains on
local LOW_LEVEL  = 100 -- the light level threshold for night
local DIM_LEVEL = 35 -- Dimmer load level threshold

local LS_SID  = "urn:micasaverde-com:serviceId:LightSensor1" -- the LightSensor service ID
local DIM_SID  = "urn:upnp-org:serviceId:Dimming1" -- the Dimmer service ID

local currentLevel = luup.variable_get (LS_SID, "CurrentLevel", DEVICE_NO_SENS_L) or 0
local lightLevel = luup.variable_get(DIM_SID, "LoadLevelStatus", DEVICE_NO_DIM) or 0
currentLevel = tonumber(currentLevel)
lightLevel = tonumber(lightLevel)

-- LIGHT ON --
if (currentLevel >= LOW_LEVEL or lightLevel >= DIM_LEVEL) then return false end
luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget=DIM_LEVEL }, DEVICE_NO_DIM)
--

local prms = string.format("%d,%d,%d",DEVICE_NO_SENS_M,DEVICE_NO_DIM,DELAY)
luup.call_delay ("checkLastTrip", DELAY, prms)
return true  -- is this necessary?


-- FUNCTION IN STARTUP LUA --
function checkLastTrip(parms)
  local SS_SID = "urn:micasaverde-com:serviceId:SecuritySensor1" -- Security Sensor Service ID
  local DIM_SID  = "urn:upnp-org:serviceId:Dimming1" -- the Dimmer service ID
  local DIM_LEVEL = 35

   local pdev,ptgt,pstp = string.match(parms,"(%d+),(%d+),([%-%d]+)")
  local DEVICE_NO_SENS_M = tonumber(pdev)
  local DEVICE_NO_DIM = tonumber(ptgt)
  local DELAY = tonumber(pstp)
   
   local lastTrip = luup.variable_get (SS_SID, "LastTrip", DEVICE_NO_SENS_M) or os.time()
   local lightLevel = luup.variable_get(DIM_SID, "LoadLevelStatus", DEVICE_NO_DIM) or 0
  lightLevel = tonumber(lightLevel)
   if (lightLevel ~= DIM_LEVEL) then return false end
   if (os.difftime (os.time(), tonumber (lastTrip)) >= DELAY) then
      luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget="100" },DEVICE_NO_DIM)
     luup.sleep(100)
     luup.call_action(DIM_SID,"SetLoadLevelTarget",{ newLoadlevelTarget="0" },DEVICE_NO_DIM)
  else
      luup.call_delay ("checkLastTrip", DELAY, parms) --parms insted of prms right?
  end
end
--
--special thanks to RexBeckett

--

Offline RexBeckett

  • Beta Testers
  • Master Member
  • *****
  • Posts: 3891
  • Karma: +482/-12
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #6 on: February 15, 2014, 09:17:34 pm »
That looks a lot better, @rsampayo.

Quote
return true  -- is this necessary?
It would only be necessary if you wanted to execute any actions that had been set in the scene's Devices tab. If not, you don't need a return at all. It isn't doing any harm, though.

Quote
luup.call_delay ("checkLastTrip", DELAY, parms) --parms insted of prms right?
Quite right. You use the same string that the function received as a parameter.

I'm intrigued why you set the LoadLevelTarget to 100 before setting it to 0. What does this achieve?

Offline rsampayo

  • Newbie
  • *
  • Posts: 19
  • Karma: +0/-0
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #7 on: February 15, 2014, 09:34:52 pm »
 @RexBeckett

Quote
I'm intrigued why you set the LoadLevelTarget to 100 before setting it to 0. What does this achieve?

A little hack. When I flip the manual switch to turn it on it goes to the last LoadLevelTarget set. Since I am only turning the lights with this script to 35 % I would flip the switch and have no way to bring the brightness up to full power. So I put that line in so the last LoadLevelTarget is always 100. In practice it doesnt really work well since I would have to stop moving, wait for the light to turn off, then flip the switch.  My other solution was to create a scene with another switch that turns the light on 100% of that light. I'm still looking for a better solution. I'm using the Aeon micro dimmer with a mechanical on/off switch.

My main problem is the vera controller cant tell if the switch was manually flipped. 

Offline rsampayo

  • Newbie
  • *
  • Posts: 19
  • Karma: +0/-0
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #8 on: February 17, 2014, 09:12:17 pm »
 @RexBeckett Thanks for all the help. Learned a lot and Its now working perfectly!!

How can I add another var (number) the  param string sent to the function.

local prms = string.format("%d,%d,%d",a,b,c)  just add another %d ?

and

local pdev,ptgt,pstp = string.match(parms,"(%d+),(%d+),([%-%d]+)")  add  another (%d+)  before the ([%-%d]+)  ?


what is the ([%-%d]+) doing?
Ive been googling for an answer but haven't found it. I found out that d is for digit.

Online akbooer

  • Beta Testers
  • Master Member
  • *****
  • Posts: 6343
  • Karma: +288/-70
  • "Less is more"
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #9 on: February 18, 2014, 02:49:15 am »
Try here http://www.lua.org/pil/20.2.html.
Then download a complete copy of the Programming in Lua book and learn everything you will need.
Or better still, pay for a paper copy to support the development team!
3x Vera Lite-UI5/Edge-UI7, 25x Fibaro, 23x TKB, 9x MiniMote, 2x NorthQ Power, 2x Netatmo, 1x Foscam FI9831P, 9x Philips Hue,
Razberry, MySensors Arduino, HomeWave, AltUI, AltHue, DataYours, Grafana, openLuup, ZWay, ZeroBrane Studio.

Offline RexBeckett

  • Beta Testers
  • Master Member
  • *****
  • Posts: 3891
  • Karma: +482/-12
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #10 on: February 18, 2014, 03:17:08 am »
I agree with @akbooer: Get a copy of Programming in Lua. You'll find it very helpful.

As you already figured-out, you encode four integers with: local prms = string.format("%d,%d,%d,%d",a,b,c,d)
and decode them with: local pdev,ptgt,pstp,pxyz = string.match(parms,"(%d+),(%d+),(%d+)(%d+)")

The odd-looking term ([%-%d]+) is what it takes to allow a negative number.

Offline rsampayo

  • Newbie
  • *
  • Posts: 19
  • Karma: +0/-0
Re: 1 scene script works, same script multiple scenes, chaos
« Reply #11 on: February 18, 2014, 01:57:41 pm »
Thanks for the link @RexBeckett and @akbooer,  just placed the order.

Sent from my LG-D805 using Tapatalk