Boring explanation
I'm using a couple of motion sensors to switch lights on when it's dark. Switching lights off happens when the motion sensor says there is no motion anymore.
Sounds simple, but it isn't.
I use a W800RF32 which gives dozens of "undefined byte from w800rf interface" per day. And sometimes these messages appear instead of the message from a motion sensor saying there is no motion. So Indigo doesn't get this info and the lights will not go off.
Besides that, I don't let the motion sensor switch off the light. I let it execute an action group instead. This action group switches off the light and does some other stuff.
The problem is that I want to be flexible in the timing before lights go out. So I used the delay option for the Action Group to increase the time it actually fires. (Yes, I could change the timing in the motion detector, but that is way too much work.)
Adding a delay to an action group is a nice feature, but unfortunately, it is not possible to remove delayed actions for an action group. That part only works for devices and triggers.
So once the delayed action group is set, it will go off. No matter what I do. This means lights will go out, even if new motion has been detected.
What can be done to solve this?
Then I started thinking about a solution. What I really need is an independent "something" that gets started when there is motion and stops when there hasn't been motion for a certain period of time. Whenever there is new motion, the "something" has to start again. And only when the motion really has stopped, it should execute an action group.
This "something" I need is actually a timer. When motion is detected, it gets set at a desired timeframe (in seconds). A background process decreases the timer and when it reaches 0, the action group is fired. When the motion sensor detects motion again, the timer is reset to it's start value.
This solution solves two problems:
1. I don't need the motion sensor to send a "no motion signal" anymore, so the W800RF32 errors won't interfere with my system.
2. I'm capable of changing the timer length without manually changing the timer setting in the motion sensor itself.
What is needed to implement a Timer?
To get a timer working in Indigo, you need this:
1. A variable "Timer<name>" that will hold the current timer value.
2. A trigger that sets the variable "Timer<name>" to it's start value whenever motion is detected.
3. A trigger that fires an action group when the variable "Timer<name>" reaches 0
4. A background task that decreases the value of "Timer<name>"
5. A trigger that starts this background task when the Indigo Server starts.
Steps 1, 2 and 3 are basic Indigo stuff, so I won't go into detail on them.
Step 4 is the fun part: the background task.
It's a piece of AppleScript which simply does the following:
Get all variables from Indigo whose names begin with "Timer".
If the value of such a variable is bigger than 0, decrease it by 15 seconds.
After all variables have been decreased, wait 15 seconds.
Repeat these steps forever.
So here's the code for the timer script.
To use it, copy it into the ScriptEditor and save it in Indigo's backgroud task folder.
(/Library/Application Support/Perceptive Automation/Indigo 2/Scripts/Background Tasks)
- Code: Select all
--
-- Retrieve Timer variables from Indigo and count them down.
--
-- Paul Roomberg
--
-- Version Date Info
-- ------- --------------- --------------------------------------------------------------------
-- 1.0 15-12-2006 First version
-- ------------------------------------------------------------------------------------------------
--
property timerRefreshRate : 15 -- refresh Timer variables every N seconds
property timerTimeOut : 5 -- Time Out value
property theHour : -1 -- Save the current hour
-- Count Down for one particular timer
-- Only if its value is positive, because negative or zero
-- means to not use it.
-- But when a timer counts down below zero, fix it at zero.
on doTimer(theTimer)
try
with timeout of timerTimeOut seconds
tell application "IndigoServer"
set theValue to value of variable theTimer
if theValue > 0 then
set theValue to theValue - timerRefreshRate
if theValue < 0 then
set theValue to 0
end if
set value of variable theTimer to theValue
end if
end tell
end timeout
on error number errNum
if errNum is -1712 then
tell application "IndigoServer"
log "Timer '" + theTimer + "' time out occured" using type "error"
end tell
else
tell application "IndigoServer"
log "Timer '" + theTimer + "' error occured: " & errNum using type "error"
end tell
end if
end try
end doTimer
-- Timer Main Loop
repeat
try
-- Timer Loop
repeat
try
with timeout of timerTimeOut seconds
tell application "IndigoServer"
-- Show a heartbeat every hour
if not (the hours of the (current date)) = theHour then
set theHour to the hours of the (current date)
log "Timer script heartbeat" using type "info"
end if
-- Fetch all timer variables and process them
repeat with aVar in variables
set theTimerName to the name of aVar
if theTimerName begins with "Timer" then
my doTimer(theTimerName)
end if
end repeat
end tell
end timeout
on error number errNum
if errNum is -1712 then
tell application "IndigoServer"
log "Timer loop time out occured" using type "error"
end tell
else
tell application "IndigoServer"
log "Timer loop error occured: " & errNum using type "error"
end tell
end if
end try
-- Wait till the next run
delay timerRefreshRate
end repeat
-- This is the most important part of this script.
-- Somehow the inner loop dies on a time out after a
-- period of time. I have no clue why this happens,
-- but adding an outer loop with timeout detection
-- solves this.
on error number errNum
if errNum is -1712 then
tell application "IndigoServer"
log "Timer main loop time out occured" using type "error"
end tell
else
tell application "IndigoServer"
log "Timer main loop error occured: " & errNum using type "error"
end tell
end if
end try
end repeat
This script needs to be started whenever the Indigo server is started. To get this done, define a new trigger "Indigo Server Startup" with a type "Indigo Server Startup". No condition needed, always fire this trigger.
And the action is to execute AppleScript in a file. Select the Timer script from above that you placed in the Background Tasks folder.
To start the script, you can restart the Indigo server or just execute the trigger.
Testing the script is easy: define a variable "Timer<something>" and give it a value of "40". If everything works well, you'll see the variable go to "25", then "10" and finally "0".
You can define as many timers as you want, as long as the name begins with "Timer". All you have to do to add a new timer is to define a variable "Timer<newname>", a trigger that sets the start value and a trigger that executes when "Timer<newname>" reaches 0.
I've been using timers for about a month by now and it is working ok. No more wondering if the lights will go out. They will. And they will only go out when there has been no more motion for a while.
The only strange problem I had was the timer script stopping without a reason. After debugging I found I needed an extra loop in the AppleScript code, as you can see in the code above. If anyone knows why this is happening, please let me know.
Enjoy!