HAI Programming 102: Flags

cornutt

Active Member
OK, I know it took some time, but I've finally written another HAI Programming 101. This one concerns flags, and how to use them in programming.

What is a flag? You can think of it is an imaginary switch, sort of like, for example, a UPB or X-10 light switch that isn't connected to a fixture. Turning it on or off doesn't change anything in the house, but it does still have an on or off status. What's that good for? It's something that you can either trigger a block of code off of, or test within a block of code. Example: Some HAI users like to have a door chime function, where the console beeps whenever an entry door is opened. However, they don't want it to do it at all times of day; for instance, they might want it to be silenced during a child's afternoon nap time. The Omnis have a built-in door chime function; the trouble is, the only to turn it on or off is to go into the setup function that controls it, and automation code can't do that. So plan B is to write a block of code that beeps the console whenever the door is opened:

Code:
1. WHEN Front door NOT READY
   THEN All Consoles BEEP 1

So far, this isn't an improvement over the built-in function. What we need is some way to tell the code when it's nap time, so that it doesn't beep when we don't want it to. We can use a flag for this. Where do we get a flag? Well, truth is, we often talk in this forum about "creating a flag", but all the available flags already exist. (On the OPII, you get 119 of them. I think the Omni IIe and LT have the same number, but I'm not sure about that; it may be fewer.) Where do you find them? In PC Access. Click on your "Setup" tab and look at the choices on the left; one of them is "Flags". Click on that, and you'll get to the flags screen.

The flags screen is where you assign names to flags. Assigning a name is what we usually mean when we say we are "creating" a flag; the truth is, the flag already exists, but since it doesn't have a name, it will not normally show up in the pick list when we are creating a line of code in a code block. You can change that, but you really don't want to get into the business of referring to flags by their numbers -- that way lies madness. Here's a screen shot of the flags screen:

Flags Screen.jpg

Note the unit numbers, which start with 393. Don't use numbers 393 or 394 -- they are tied into the internal and external sounder circuits in some way that I don't understand, and if you try to use them, they will act strange. You can see here that I've started with 395. As in the case with lighting units, they each can have a short name and a long name. The short name is what appears on a console, but the long name is used most everywhere else, including in code listings in PC Access.

So for our problem above, we pick an unused flag and name it "Nap Time". Now, our block of code can check the flag to decide if it's nap time, and if so, not beep the console. It now looks like this:

Code:
1. WHEN Front door NOT READY
  AND IF Nap Time OFF
   THEN All Consoles BEEP 1

Now, turning on the "Nap Time" flag will prevent the door chime from happening. We can, if we want, turn the flag on and off manually using PC Access by clicking on our Status/Control tab, selecting "Flags", and double-clicking on the Nap Time flag in the list and using the pop-up dialog box. However, if nap time is the same every day, we can have the code do it for us:

Code:
135. TIMED 3:00 PM MTWTFSS
   THEN Nap Time ON
136. TIMED 4:00 PM MTWTFSS
   THEN Nap Time OFF

As in the case with lights, we can turn a flag on or off for a specified period of time, which allows us to use flags as timers. I have the following in my code:


Code:
60. WHEN Garage Doors Close
   THEN RUN Garage door west close
   THEN RUN Garage door east close
   THEN Garage door timer ON FOR 18 SECONDS
 
63. WHEN Garage door timer OFF
  AND IF Garage door east side NOT READY
  OR
  AND IF Garage door west side NOT READY
   THEN SHOW Garage door blocked WITH BEEP
   THEN LOG Garage door blocked

"Garage door timer" is a flag. When block 60 runs (it's a button that I can run from the console or from the touch screen), after commanding both of the garage doors to close, it sets the timer for 18 seconds. This gives the door openers enough time to actually close the doors, plus a few seconds. When the time runs out, it changes the flag's status from ON to OFF, and that triggers block 63. This block checks to see if either of the two doors is still open (because they jammed or were blocked by some object in the door's path), and if so, it alerts me with a console message.

Unlike a real light, you can't "dim" a flag. However, you can assign a numeric value to a flag. It will accept values between 0 and 255. (Computer types will recognize this as the range of values representable in an 8-bit unsigned byte.) There is an action that will increment (add 1) to the value of a flag, but it will not increment past 255. There is also an action that will decrement (subtract 1) from the value of a flag, but it will not decrement lower than 0. I have a set of "house occupancy mode" flags defined in my code that I have coded to change the behavior of a number of default actions. For instance, I have an electric heater in the master bath, that warms it up on cold mornings. On weekdays, I want it to come on before I wake up so that the bath is already warm when i get up. But it also consumes a fair amount of power and I don't want it to run on days when it doesn't need to. I have the following code:

Code:
19. TIMED 5:50 AM MTWTF--
  AND IF House is unoccupied CURRENT VALUE IS 0
  AND IF Sleeping in CURRENT VALUE IS 0
   THEN RUN Master Bath Heat

"House is unoccupied" and "Sleeping in" are both flags. I set "House is unoccupied" if we're away on a trip, and I set "Sleeping in" if we're not getting up at the normal weekday time (say, because it's a holiday). However, it's almost guaranteed that I will forget that I set them until the next day that I get up at my regular time to go to work, and the bathroom is cold. So here's what I do: I set them to the number of days that the condition (house is unoccupied, or sleeping is) is going to be true. The following block of code does the remembering for me:

Code:
24. TIMED 12:01 PM MTWTFSS
   THEN DECREMENT Guests in upstairs bedrooms
   THEN DECREMENT House is unoccupied
   THEN DECREMENT At home during work hours
   THEN DECREMENT Sleeping in

At noon (okay, one minute past noon), it subtracts one from each of the flags. So if I'm taking Monday and Tuesday off from work, Sunday night I set the "Sleeping in" flag to 2. Monday morning, the bath heat does not come on. Monday at noon, the flag is decremented to 1. Tuesday morning, the bath heat does not come on. Tuesday at noon, the flag is decremented to 0. Wednesday morning, the bath heat does come on. Once the flag is back at 0, the decrement action doesn't do anything, until I set the flag nonzero again.

If a condition checks the on/off status of a flag that has a numeric value assigned to it, the flag will be considered to be "off" if its value is zero, and "on" otherwise. Further, if an increment or decrement action causes the on/off status of a flag to change, that will trigger blocks of code that trigger on that flag. (You can't trigger on the numeric value of a flag, but you can trigger on the on/off status changes.) An example: I have a relative who refinishes furniture in her garage. A finish she uses takes 48 hours to cure. Say she has an Omni and wants it to remind her when the finish is cured and she can move the piece to a shop. Now, if you've played with setting timers, you've probably already figured out that the maximum duration of a timer is 18 hours. How to time a 48-hour interval? Name a flag "Finish reminder", and then write the following:

Code:
135. EVERY HOUR
   THEN DECREMENT Finish reminder
 
136. WHEN Finish reminder OFF
   THEN SHOW Finish is ready WITH BEEP

When she applies a finish to a piece, she sets the "Finish reminder" flag to 48, using PC Access or some other method. That causes the flag's on/off status to change to "on". 48 hours later (less as much as 59 minutes, depending on how many minutes after the hour it was when she set the flag), block 135 decrements the flag from 1 to 0, which causes its on/off status to change to "off". That in turn triggers block 136, and she gets a console message. Once the flag is at 0, block 135 will not have any further effect until she sets the flag to a nonzero value again.

There is one exception to the zero/nonzero rule, which provides a useful trick for a particular situation. Sometimes, when you use a flag as a timer, you want to be able to "cancel" the timer before it expires. Consider: You suspect that your child is getting up in the middle of the night to play games or whatever. You decide that you are going to have your Omni tattle on him, by checking the status of the light in his room: if he turns on the light during what is supposed to be sleep time, your console will beep. However, you want to allow a few minutes' grace so he can turn on the light to go to the bathroom or get a glass of water. So you write this code:

Code:
130. WHEN Child's bedroom light ON
   THEN Tattle Flag ON FOR 5 MINUTES
 
131. WHEN Child's bedroom light OFF
   THEN Tattle Flag OFF
 
132. WHEN Tattle Flag OFF
   THEN SHOW The little brat's out of bed again! WITH BEEP

This won't do what you want. It will tattle after five minutes; when the timer runs out, block 132 will be triggered. However, if the light is turned off within the five minutes, that will also trigger block 132. You need some way to shut off the timer without triggering block 132. Here's how you do it:

Code:
130. WHEN Child's bedroom light ON
   THEN Tattle Flag ON FOR 5 MINUTES
 
131. WHEN Child's bedroom light OFF
   THEN Tattle Flag SET VALUE TO 0
 
132. WHEN Tattle Flag OFF
   THEN SHOW The little brat's out of bed again! WITH BEEP

Setting a numeric value into the flag erases the timer value that was associated with it, without triggering any blocks that trigger on the flag's on/off status. So if the light is turned off within the five minutes, block 131 is triggered; it erases the five-minute timer and block 132 is not triggered.

Although you can't trigger on a specific numeric value of a flag, you can check its value in a condition. A condition can test to see if the flag is equal, not equal, less than, or greater than a given value. When a flag is being used as a timer, a condition can test the time remaining. A 5.7e or 10p touch screen page can retrieve and display the numeric value of a flag. Unfortunately, you can't do math on flags, other than the increment/decrement actions. However, for more sophisticated manipulation of flags, you could use OmniLink to connect them to an external application, such as Homeseer.
 
cornutt,

Thanks for another great writeup.

One minor correction regarding incrementing flags.
A flag can be incremented past a value of 255, but it "rolls over" and starts at a value of 0 again.

From the HAI KnowledgBase
Application_Directory_0206.pdf, page A-7

http://kb.homeauto.c...0491FF7}&Lang=1

When a counter is decremented to zero, the "When Unit Off" macro is executed. A counter will not decrement
below zero. The counter will, however, roll over from 255 to 0 when incremented. The "When Unit Off"
macro will be executed when the counter rolls over. This allows two counters to be cascaded to form a larger
counter.
 
If you missed Cornutt's "101" on flags, which has a lot of good stuff in it, go here.
 
Additionally, I posted an interactive dual-macro use of flags for setting up a hysteresis affect for a pond-fill here.
The idea is to use flags to monitor a zone state change (not ready/secure), while locking the 'intended action' into place for specified periods, even if the state rapidly changes.
The flags are placed into only two 'interactive' macros, effectively informing each other of state changes and how long to allow an 'intended action' to run, including some other features. 
 
It may look a little advanced at first glance, but its not.  The challenge/idea, and the implementation is simple... rather than run a series of macros independently for each state~incident, meaning series~logic for each of 'when not ready' and 'when secure', is it possible to reduce the amount of code needed by scripting the actions interactively (parallel~logic, so to speak).  This is what I asked myself after writing out the code using quite a number of macros for managing each affect individually when first laying out all the basic logistics of what I needed to have happen.  The code provides a decent fundamental example of using flags to allow two macros to interact/respond with one another.
 
TYVM!
This is very helpful - wish I knew about it many moons ago.
I'm following your Garage door timer example above and ran into a blocker.
Can you please tell me where in PCAccess I can find the Flags to build the When statement from the IDE?
They are not listed in Zones or any of the other trigger-type categories listed in the drop down box.
PS - thx also for the flag 393 & 394 gotcha - didn't know those and they potentially solve one of my other "problems"
Thx!
- Jeff
 
Jeff, Create a flag in the "Flags" section under setup tab, then access it under "Control" when building your statement. Good Luck.

jchotz said:
Can you please tell me where in PCAccess I can find the Flags to build the When statement from the IDE?
 
Back
Top