Premise Meaning of "this" in script macros triggered by timers changes?

Motorola Premise

etc6849

Senior Member
I was writing code for the HR12a keypad to turn on a lamp on the first press, but also turn on a ceiling fan if the button was held down for three seconds. However, I was having problems with a scriptMacro that handled the delayed action.

It seems "this" was changing meaning within the script macro depending on the location of the program counter in SYS during execution!?! Initially if I used "this" within the script macro SYS would stop execution due to an error and "this" would point to: sys://Home/HR12aZoneA/Button_3On/delayedAction and not the intended sys://Home/HR12aZoneA/Button_3On.

Next, I tried this.parent, but SYS would stop execution due to an error and this would point to sys://Home/HR12aZoneA/ .

What gives? It seems this can have more than one meaing in a script macro that is triggered by a timer? Is this always the case for script macros? I've never run into this issue with onpropertychange scripts. I remember reading something about this in the old forum, but can't seem to find it, so sorry if this has been covered before.

My working code to solve the problem:
Code for OnChangeButtonState:
Code:
If this.ButtonState = 1 Then
	'set the last button object pressed
	setStoredObject this
	Home.Theater.FloorLamp.PowerState = True
	system.addTimer 3, "this.delayedAction.Trigger = True", 1, this.Name & "_delayedAction_" & this.ObjectID
End If

Code for delayedAction scriptMacro that works:
Code:
If this.Parent.IsOfExplicitType(sys.Schema.Device.Keypads.Button.Path) Then
	If this.Parent.ButtonState = 2 Then
		Home.Theater.CeilingFan.PowerState = True
	End If
End If

Non working trials:
Code for delayedAction that didn't work (error: object doesn't support this property or method: 'this.Parent.ButtonState'):
Code:
If this.Parent.ButtonState = 2 Then
	Home.Theater.CeilingFan.PowerState = True
End If

Code for delayedAction that also didn't work (error: object doesn't support this property or method: 'this.ButtonState'):
Code:
If this.ButtonState = 2 Then
	Home.Theater.CeilingFan.PowerState = True
End If
 
I'm not sure how to reply other than to list a few definitions and show you another way to achieve your goal.
  • "this" refers to the current object. The exception is when it is used in simple timers. For example:
    system.addTimer 3, "this.whatever=true", 1, this.Name
    In this example, "this" does not refer to the current object (i.e. the timer itself) but to the calling object.
  • The parent of a button is the keypad.
  • The children of a keypad are its buttons ... and potentially other objects such as a PropertyChange script.
  • When referring to a parent object , or when referring to child objects, it is good practice to check the object's type (i.e. its class).
    For example, the children of a keypad are its buttons but it can also include a PropertyChange script. If you iterate through all children, and assume they can only be buttons, somthing will fail when it encounters the PropertyChange child. When iterating through child objects, an alternative to using GetChildren and testing each child with IsOfExplicitType, is to use GetObjectsByType.
The attached image shows another way of acting on a button press without using an OnChangeButtonState script. It uses two ScriptMacros and one DelayMacro.
Macros are executed in the order they are listed: SetFloorLamp, PauseThreeSeconds, SetCeilingLamp. When MyButton's ButtonState changes to Press, the first ScriptMacro (SetFloorLamp) is executed. It is followed by the next macro, a DelayMacro (PauseThreeSeconds) whose DelayTime property is set to 3000 milliseconds. After three seconds have passed, the final ScriptMacro (SetCeilingFan) is executed. If MyButton's ButtonState is Hold, it will turn on the CeilingFan.

In both ScriptMacros, "this" refers to MyButton. In the first ScriptMacro, I included, but commented out, "setStoredObject this". This appears to be a GlobalScript you've developed, I don't know its purpose, and it may need to be adapted.
 

Attachments

  • ButtonScripts.png
    ButtonScripts.png
    29.4 KB · Views: 36
Thanks 123.

Your code works great when using a delayMacro. However, I tried to use a timer with it and got the same error again. From your definitions it would seem the code using addTimer should work; especially since using delay macros seem to work. I simply added this line to your to script macro called setFloorLamp and deleted the delay macro.

Code:
system.addTimer 3, "this.setCeilingFan.Trigger = True", 1, this.Name & this.ObjectID

I've attached a picture of the error I'm getting.
 

Attachments

  • script_macro_error.JPG
    script_macro_error.JPG
    70.5 KB · Views: 43
...Your code works great when using a delayMacro. However, I tried to use a timer with it and got the same error again. ... deleted the delay macro.
There are two things wrong with this approach.

By removing the DelayMacro (PauseThreeSeconds), the two ScriptMacros will execute one after the other. That means that as soon as SetFloorLamp has done its work, SetCeilingLamp will run immediately. In this particular case it doesn't do any harm but this is probably not what you expected.

The timer's payload contains "this.SetCeilingLamp.Trigger=true". For the Timer, "This" is the calling object which is MyButton and that's exactly the interpretation we want: SetCeilingLamp is executed.

Now within SetCeilingLamp there is "this.ButtonState" but SYS no longer considers "this" to be "MyButton"! That meaning appears to be lost when the ScriptMacro is called from a Timer perhaps because it goes through two levels of indirection: One ScriptMacro calls a Timer, the Timer executes its payload which calls another ScriptMacro. At that point, "this" is the ScriptMacro itself (SetCeilingLamp) which does not contain a ButtonState property and that's why SYS halts on "this.ButtonState".

Conclusion:
  • Macros are designed to run in sequence.
  • ScriptMacros should (probably) not be treated like methods and called individually.
  • The meaning of "this" changes if one ScriptMacro calls another ScriptMacro via a Timer.
 
Thanks 123. Your statement below makes a lot of sense! This is the explanation I was looking for. You obviously have a very deep understanding of SYS far beyond mine ;)

I also now see the error in my treatment of script macros :( I believe my sloppy code in the original post works only because the instant a button is pressed ButtonState still equals 1, so the second script macro will not execute immediately, but only after the timer expires 3 seconds later.

Code:
Now within SetCeilingLamp there is "this.ButtonState" but SYS no longer considers "this" to be "MyButton"! That meaning appears to be lost when the ScriptMacro is called from a Timer perhaps because it goes through two levels of indirection: One ScriptMacro calls a Timer, the Timer executes its payload which calls another ScriptMacro. At that point, "this" is the ScriptMacro itself (SetCeilingLamp) which does not contain a ButtonState property and that's why SYS halts on "this.ButtonState".
 
Your first code example is a combination of a PropertyChange script and a ScriptMacro. Here's how it works:

When ButtonState changes to "Press" it activates the PropertyChange script and the ScriptMacro. When the ScriptMacro executes it performs no work because ButtonState is still "Press" and it is looking for "Hold".

Your PropertyChange script starts a timer. When the timer expires (3 seconds) it calls the ScriptMacro. The second time the ScriptMacro executes, it could perform work because Buttonstate may now be "Hold".

So the ScriptMacro is executed twice. The first execution serves no purpose.

To avoid this situation, I'd skip the PropertyChange script and use macros exclusively for this application.
 
Definitely using ScriptMacros is the preferred way to dealing with keypad button scripts... Reading your post several years later, you are 100% correct!
 
Back
Top