In an effort to avoid leaving an open thread that others may search and find useless, I'm going to post my final solution. Hopefully someone finds it useful.
The resolution is...ActiveHome Pro sucks! It can't do things that seem simple in a way that makes sense. Some tasks can be kludged around to make a usable workaround, but that doesn't always work. In this case, the simple task is setting a timer and having the capability to cancel/update it once it's started. I can think of several ways this could be implemented, but X10 doesn't seem to have an interest in making a fully usable program. Well, enough dumping on X10's software "solution" and on to my current setup...
I decided that the logic should be as follows:
If Motion ON, then motion has been sensed so check to see if it's day or night. If day, run chime stuff. If night, run chime stuff and turn on lights. If it's night and the lights are on, then kill lights off delay stuff.
If Motion OFF, then sensor no longer sees motion. If it's day, do nothing. If it's night, run chime stuff and start a delay to turn the lights off.
DummyChime taken care of within CM15A
These 2 functions must be run in different threads to avoid locking up the processor inside the delay loop so as not to execute the MotionON function
Function MotionON
If DAY Then
Turn on DummyChime
Set MotionON flag
ElseIf NIGHT Then
If lights are off, turn them on
Turn on DummyChime
Set MotionON flag
EndIf
End MotionON
Function MotionOFF
If DAY Then
If lights are on, turn them off
clear MotionON flag
ElseIf NIGHT Then
Set delay stop time
Loop
If time = delay stop time Then
Turn off lights
Exit MotionOFF function
ElseIf MotionON flag is set Then
Exit MotionOFF function
EndIf
End Loop
EndIf
End MotionOFF
Since AHP can't implement that, I turned to another option...the AHP SDK. This allows for watching PLC and RF signals to be sent and received by the connected computer. The language I chose, AutoHotKey, is not the fastest, but it's easy to implement and modify since it's interpreted (but can be compiled if desired). Here's my code (currently set in debug mode so it can be run on a non-CM15A computer for testing of logic).
; CM15A RecvAction using AHK_L and X10's ActiveHome/SDK.
#Persistent
#SingleInstance force
;oX10 := ComObjCreate("X10.ActiveHome")
;ComObjConnect(oX10, "X10event_")
NIGHT := false
LightON := false
MotionON := false
MotionDelayTime := 5000 ;5*1000*60 ; 5 minutes
return
; Functions
X10event_RecvAction(Action, Parm1, Parm2, Parm3, Parm4, Parm5, Reserved)
{
global NIGHT
global LightON
global MotionON
global MotionDelayTime
Log("Event: " . Action . ", " . Parm1 . ", " . Parm2 . ", " . Parm3 . ", " . Parm4 . ", " . Parm5)
GoSub, LogVariables
If ((Action = "RecvPlc") AND (Parm1 = "a1") AND (Parm2 = "On"))
{
; Motion ON
Log("`tMotion ON received")
; Motion seen so disable delay timer if active
Log("`tClear Timer - MotionDelay")
SetTimer, MotionDelay, OFF
; Set MotionON flag
global MotionOn := true
; Turn on DummyChime
Log("`tSet DummyChime ON")
X10CM15a_Plc("C1", "On")
If NIGHT = 1
{
Log("`t`tTime is NIGHT")
; If lights are OFF, turn them ON
If NOT LightON
{
Log("`t`t`tTurn Lights ON")
X10CM15a_Plc("A6", "On")
global LightON := true
}
Else
{
Log("`t`t`tLights already ON")
}
}
Else
{
Log("`t`tTime is DAY")
; If lights are ON, turn them OFF
If LightON
{
Log("`t`t`tTurn Lights OFF")
X10CM15a_Plc("A6", "Off")
global LightON := false
}
Else
{
Log("`t`t`tLights already OFF")
}
}
}
Else If ((Action = "RecvPlc") AND (Parm1 = "a1") AND (Parm2 = "Off"))
{
; Motion OFF
Log("`tMotion OFF Received")
; Clear MotionON
global MotionOn := false
If NIGHT = 1
{
Log("`t`tTime is NIGHT")
; Set motion timer
Log("`t`tSet Timer - MotionDelay")
SetTimer, MotionDelay, %MotionDelayTime%
}
Else
{
Log("`t`tTime is DAY")
; If lights are ON, turn them OFF
If NOT LightON
{
Log("`t`t`tTurn Lights OFF")
X10CM15a_Plc("A6", "Off")
global LightON := false
}
Else
{
Log("`t`t`tLights already OFF")
}
}
}
Else If ((Action = "RecvPlc") AND (Parm1 = "p1") AND (Parm2 = "On"))
{
; NIGHT
Log("`tSunset")
global Night := true
}
Else If ((Action = "RecvPlc") AND (Parm1 = "p1") AND (Parm2 = "Off"))
{
; DAY
Log("`tSunrise")
global Night := false
; If lights are ON, turn them OFF
If LightON
{
Log("`t`tTurn Lights OFF")
X10CM15a_Plc("A6", "Off")
global LightON := true
}
Else
{
Log("`t`tLights already OFF")
}
}
}
return
X10CM15a_Plc(HUcode, State)
{
static sdkfile = "C:\Program Files\Common Files\X10\Common\ahcmd.exe"
;Run, %sdkfile% sendplc %HUcode% %State% , , hide
X10event_RecvAction("RecvPlc", HUcode, State, 0, 0, 0, 0)
}
return
Log(Text)
{
static LogFile = A_ScriptDir "\X10_PLC_Interpreter_Log.txt"
FormatTime, TimeString,, dd/MM/yyyy HH:mm:ss
FileAppend, %TimeString%`t%Text%`n, %LogFile%
}
return
MotionDelayEnded()
{
; AHK doesn't like setting globals from subroutines
global LightON := false
SetTimer, MotionDelay, OFF
Log("MotionDelay reached - Lights OFF")
X10CM15a_Plc("A6", "Off")
}
return
; Subroutines/Timers
MotionDelay:
{
MotionDelayEnded()
}
return
LogVariables:
{
Log("NIGHT=" . NIGHT . "`tLightON=" . LightON . "`tMotionON=" . MotionON)
}
return
; Hot keys
Esc::
{
Log("ExitApp`n`n")
ExitApp
}
return
; Test Hot Keys
F8:: ; MotionON
{
X10event_RecvAction("RecvPlc","a1","On",0,0,0,0)
sleep 500
}
return
F9:: ; MotionOFF
{
X10event_RecvAction("RecvPlc","a1","Off",0,0,0,0)
sleep 500
}
return
F10:: ; DAY
{
X10event_RecvAction("RecvPlc","p1","Off",0,0,0,0)
sleep 500
}
return
F11:: ; NIGHT
{
X10event_RecvAction("RecvPlc","p1","On",0,0,0,0)
sleep 500
}
return
F12:: ; Refresh Variables
{
GoSub, LogVariables
sleep 500
}
return
And a sample log file:
18/05/2011 07:52:36 NIGHT=0 LightON=0 MotionON=0
18/05/2011 07:52:42 Event: RecvPlc, a1, On, 0, 0, 0
18/05/2011 07:52:42 NIGHT=0 LightON=0 MotionON=0
18/05/2011 07:52:42 Motion ON received
18/05/2011 07:52:43 Clear Timer - MotionDelay
18/05/2011 07:52:43 Set DummyChime ON
18/05/2011 07:52:43 Event: RecvPlc, C1, On, 0, 0, 0
18/05/2011 07:52:43 NIGHT=0 LightON=0 MotionON=1
18/05/2011 07:52:43 Time is DAY
18/05/2011 07:52:43 Lights already OFF
18/05/2011 07:52:44 Event: RecvPlc, a1, On, 0, 0, 0
18/05/2011 07:52:44 NIGHT=0 LightON=0 MotionON=1
18/05/2011 07:52:44 Motion ON received
18/05/2011 07:52:44 Clear Timer - MotionDelay
18/05/2011 07:52:44 Set DummyChime ON
18/05/2011 07:52:44 Event: RecvPlc, C1, On, 0, 0, 0
18/05/2011 07:52:45 NIGHT=0 LightON=0 MotionON=1
18/05/2011 07:52:45 Time is DAY
18/05/2011 07:52:45 Lights already OFF
18/05/2011 07:52:45 Event: RecvPlc, a1, Off, 0, 0, 0
18/05/2011 07:52:45 NIGHT=0 LightON=0 MotionON=1
18/05/2011 07:52:45 Motion OFF Received
18/05/2011 07:52:45 Time is DAY
18/05/2011 07:52:46 Turn Lights OFF
18/05/2011 07:52:46 Event: RecvPlc, A6, Off, 0, 0, 0
18/05/2011 07:52:46 NIGHT=0 LightON=0 MotionON=0
18/05/2011 07:53:19 ExitApp
The only requirement for this to work is that the motion sensor has to send an 'ON' and 'OFF' command (obviously) and there must be a sunrise/sunset phantom module (P1) set on a timer to toggle in the morning and evening. I also have a chime (B1), a dummy chime (C1), and macro logic to only trigger a chime every 5 minutes. If motion is sensed during this delay, it does not make a noise.
One limitation I'm aware of is that does not save the state of 'NIGHT' in a file and can't calculate it's state. When this code is started, if it's night then the P1 module must be toggled in AHP to the 'ON' state (or trigger the F11 test hot key I suppose). I could add sunrise/sunset calculations, but I don't think it's a big enough issue currently to justify the extra coding.
This is certainly more complex than accepting AHP limitations. But, in it's current implementation, this code can be changed pretty easily to add more logic whereas AHP would probably require a rewrite of the entire SmartMacro logic to go forward. I'll probably use this type of code going forward as it's far more flexible and much less frustrating.
I'm aware of HomeSeer and the like, but these programs cost $$$ which is not in the budget. AHK is FREE and has great forum support. The SDK makes it a very viable option. Of course, an AHP API would be even better, but since that won't likely ever happen, this works well enough!