Area Triggers --> Area Actions

The main thing I am aiming to communicate in this tutorial is the concept of using the logic built into groups to lessen the complexity of rules. By setting up your Items and groups in the following way, you will be able to use one rule for much of the automation in your home. A second rule is needed if you’d like to have the lights adjust to Mode (Time of Day) and outside lux levels. The concept is that each area within a home has triggers and the combined state of those triggers can represent whether there is activity/presence within that area. By putting the triggers into a group, actions can take place when the state of that group changes.

Sensor state change → Area trigger group state change → Rule triggered → Action(s) determined from metadata → Action(s) called

Putting the logic into groups takes some getting used to, but by working with groups you will not need to build the area/room specific logic into your rules. I have been able to build out group logic to fit all of the areas inside and outside of our home for use with lighting (including colors and brightness level based on outside lux), music/sound, and notifications (e.g., when the doors are unlocked for too long). I originally wrote this about two years ago using the rules DSL and stored the mode based light levels in a Hashmap. Moving to the new rule engine and scripted automation, I was able to incorporate Item metadata.

Setup groups and Items
  1. Create regular groups named gArea_Trigger and gArea_Action. These groups will hold the respective trigger and action groups. The gArea_Trigger group will be used in the rule trigger.

     Group gArea_Trigger "Area Triggers"
     Group gArea_Action “Area Actions”
    
  2. Section out your home into areas where a lighting or sound automation would take place.

  3. Create an area trigger group for one of the areas using a name ending in “_Trigger”. This group will hold all of the Items that can be used to identify if the area is active or not. All of the Items in a trigger group must be of the same type (Switch or Contact). Reread that last sentence… it’s important! Most of my devices use Z-Wave and have both switch and contact Channels to choose from. Area trigger groups need to have a type of Switch or Contact, an aggregation function, and they need to be members of the gArea_Trigger group. When a trigger group is ON or OPEN, the area is active.

     Group:Switch:OR(ON,OFF) gDS_FamilyRoom_Trigger "Family Room [%s]" (gArea_Trigger)
    
  4. Add the triggering Items to the area trigger group that you have created. If you do not have devices to use for the trigger, you can create dummy Items that can be used to manually trigger the rule.

  5. Create another group with the same name as the area trigger group but this time ending in “_Action”. The naming is important, since the rule will be using the area action group associated with the area trigger group based on the name. This group will hold all of the Items in the area that you want to take action on when activity in the area starts and stops. Add this group to the gArea_Action group.

     Group gDS_FamilyRoom_Action "Family Room" (gArea_Action)
    
  6. Add the action Items into the area action group that you have created.

  7. Repeat steps 3-6 for each area in your home.

At times, you’ll need to invert the state of an Item or group of Items. To do this, create another group inside of the trigger group using Group:Switch:NAND(ON,OFF) and place the Item(s) you need to reverse the state for inside this group. For example, when door locks are unlocked, their state is OFF. By adding a lock to a NAND area trigger group, the group will be ON when the door is unlocked (OFF). If you add more than one lock to the group, the group will stay ON until all locks are ON (locked). There are several interesting solutions that can be created using group logic. Experiment with the possibilities and look through the Example Lighting Scenarios in Github. As I find time, I will document more complex scenarios. If you have questions or difficulty, just ask!

Here are some truth tables that are helpful for understanding the states of groups using logic based aggregation functions:

Example Group and Item structure:

Group    gArea_Trigger
Group:Switch:OR(ON,OFF)    gDS_FamilyRoom_Trigger    (gArea_Trigger)
Group:Switch:OR(ON,OFF)    gUS_LivingRoom_Trigger    (gArea_Trigger)
Group:Switch:OR(ON,OFF)    gUS_EntranceGarage_Trigger (gArea_Trigger)
    Group:Switch:NAND(ON,OFF)    gUS_EntranceGarage_Bathroom_Trigger    (gUS_EntranceGarage_Trigger)
    Group:Switch:NAND(ON,OFF)    gUS_EntranceGarage_Lock_Trigger (gUS_EntranceGarage_Trigger)

gArea_Trigger
    |_ gFamilyRoom_Trigger
        |_ DS_FamilyRoom_Motion
        |_ DS_FamilyRoom_Slider_Contact
    |_ gLivingRoom_Trigger
        |_ US_LivingRooom_Motion
        |_ US_LivingRoom_Slider_Contact
    |_ gEntranceGarage_Trigger
        |_ US_EntranceGarage_Motion
        |_ US_Laundry_Contact
        |_ gUS_EntranceGarage_Bathroom_Trigger
            |_ US_GuestBathroom_Contact
        |_ gUS_EntranceGarage_Lock_Trigger
            |_ Lock_GarageAttached_Inner
gArea_Action
    |_ gFamilyRoom_Action
        |_ gDS_FamilyRoom_Bulbs
    |_ gLivingRoom_Action
        |_ US_LivingRoom_Dimmer
    |_ gEntranceGarage_Action
        |_ US_EntranceGarage_Dimmer
Setup your rule

The link below is a full example that will work for everyone out of the box for lighting. By adding metadata, the lights will adjust based on the mode (time of day) and/or outside lux level. I’ve also included examples for speaker and notification area actions, though these are specific to my implementation. Custom area actions are easily integrated. If you build one that you feel will be useful for others, please post it here or submit it as a PR!

There is a lot of more information in github… Area Triggers and Actions.

Visualization

To see the state of the areas, a simple sitemap entry…

Group item=gArea_Trigger

… will give you a view into which are active or inactive. You can drill down into the groups to see which Items are triggering the activity. Depending on your mode and lux settings, an active area does not mean that the lights are turned ON.

This is much better illustrated with an SVG floorplan. Active areas are green and inactive are grey. Note, the sun was bright when I took this screenshot, so most lights are OFF in the active areas of the house.

Related Concepts:

I appreciate any feedback!

17 Likes

What do I do if I have already an established system with Contacts for doors and windows and Switches for motion and switches?
How can I agregate the two types?

It would be technically possible, but messy. I haven’t had that problem, but I’ll think on it. OTTOMH,

  1. setup the area trigger group with just the switches
  2. setup another group for the contacts (aggregation function but not an area trigger group)
  3. setup a rule and proxy SwitchItem for the contact group
  4. put the proxy Item in the switch group

If you are using zwave and the Channels are there, you can setup new Items and just run with two sets of Items per device.

I’ll give this some more thought. Maybe there’s something that could be done with profiles. A group function that could handle both types would be handy…

Have you looked into how you could use this with the symantic tagging define for HABot? I’ve not studied this very closely but it seems like one could leverage some of those standards here and get the benefit of both. Anyway, I’m mentally fried right now so I’ll come back later when I’ve had a rest and look more closely.

The only concrete suggestion I have is to add a link to Associated Items DP to provide further explanation for the naming patterns.

I had spent a while thinking through how symantic tagging could be incorporated, but everything I came up with was messy, and I didn’t see anything cleaner than doing everything through group membership and group logic. The Alexa V3 metadata is now using more group information. I tested this a while, but I needed/wanted more customization, so that e.g. different sets of speakers could have different actions.

Instead of associated Items, you could use metadata to link the triggers and actions, but that only adds complexity. For now, groups are easier to visualize and setup than metadata. I don’t know of any simpler way to do this and yet have as much customization as what I illustrate in the example in GH. Most automation will be doing something in response to something else. When someone is in this area of the house, I want something to happen, and when nobody is there, the action should stop (maybe on a timer). Area triggers and actions fits this model and is hopefully something a beginner could pick up. After adding some metadata, they’ll have the lights adjusting based on time of day and/or the outside light level.

It’s there in the references at the end of the post… but if you missed it, I should probably add in some more links. :slightly_smiling_face:

HABot also uses Group membership for some things like room definitions which is what made me think of it. So I was thinking about using the room definition standard from HABot instead of a custom standard. I wasn’t really thinking any further than that. How the Groups are used and metadata and all that would be the same as you presented.

I’d hate to have to manage two different FamilyRoom Groups in order to use both.

Hi,

this looks very interesting to me as I got several areas in the house with a group of lights which I’d like to control as areas. However I am somewhat struggling with the logic.

one example is the bathrooms, it has 3 individual light switches:

Switch    GF_MasterBath_Main_Light     "Main Light"            <light>         (gGF_MasterBath_Trigger,gGF_MasterBath_Action,GF_MasterBath)   ["Lighting", "Switchable"] 
Switch    GF_MasterBath_Toilet_Light   "Toilet Light"          <light>         (gGF_MasterBath_Action,GF_MasterBath)   ["Lighting", "Switchable"]   
Switch    GF_MasterBath_Shower_Light   "Shower Light"          <light>         (gGF_MasterBath_Action,GF_MasterBath)   ["Lighting", "Switchable"] 
Switch    GF_MasterBath_Closet_Light   "Closet Light"          <light>         (gGF_MasterBath_Action,GF_MasterBath)   ["Lighting", "Switchable"] 

Group:Switch:OR(ON, OFF)         gGF_MasterBath_Trigger         "Master Bath Lights"           (gArea_Trigger) 
Group                            gGF_MasterBath_Action          "Master Bath Lights"           (gArea_Action)

ideally I’d like to push one switch to have all lights turned on. however when I pus the main light on it inverts the state on the other lights. ig I use NAND or NOR instead of AND or OR it get into an infinite on/off loop.
I’ve build the logic as stand alone rule before, however I’d like to get more in general design pattern to make behavior easier reproducable throughout the house…

Any hint on what I am doing wrong?

PS I think I figured it out how to switch all with one, however now I am trying to figure out how to trigger when any of the groups switches is switched ON/OFF

Cheers

Christian

Did you get it working? What you are trying to do with one light as the trigger sounds fine, just be sure to use an AND or OR group aggregation function or the state will be reversed. When you mentioned the other lights inverting their state, I was surprised, because I do not see how that would be possible in the scenario you were describing.

I’m thinking the best way to do this is to just repeat the single switch solution for each of the switches, using different triggering groups for each switch but the same action group.

If you are not going to use modes or lux levels, you may want to consider not using automation for this and just using profiles.

Thanks for sharing this, @5iver. You asked for some feedback so I thought I’d share what I’ve been trying to do with our setup here.

Before doing so I will briefly mention I see the following exception once I added metadata to all my items:

2020-02-22 17:09:14.573 [ERROR] [jsr223.jython.Area triggers         ] - Traceback (most recent call last):
  File "/etc/openhab2/automation/lib/python/core/log.py", line 51, in wrapper
    return fn(*args, **kwargs)
  File "<script>", line 54, in area_triggers
  File "/etc/openhab2/automation/lib/python/community/area_triggers_and_actions/__init__.py", line 65, in start_action
    function(item, active)
  File "/etc/openhab2/automation/lib/python/community/area_triggers_and_actions/area_actions.py", line 55, in light_action
    elif item.type == "Color" or (item.type == "Group" and item.baseType == "Color"):
AttributeError: 'org.eclipse.smarthome.core.items.GroupItem' object has no attribute 'baseType'

The initial reason I added metadata to all items was to delay the off action by 600 seconds. Is there another way of doing this (eg in the area_triggers_and_actions_dict)? Also is there a way to control the delay off by mode, for example in the middle of the night you generally want the lights to go off as rapidly as possible so as to not disturb other people whereas during the day you generally want lights to stay on much longer so they don’t go off and cause irritation to people who are sitting still and not activating motion sensors.

I’m wondering how to best configure area triggers when we have many different living and bedroom areas which may be concurrently used by different people in different ways. A “whole house” mode to drive motion lighting seems to struggle when you have different family members going to bed at different times, some relaxing in (and not moving around within) their illuminated bedrooms, occasionally people home sick and preferring a dark bedroom to rest in, others occupying a living area etc. It’s almost as if each space needs its own mode to reflect what you want to have happening in that space.

Currently area triggers allows a light to be set to 100% brightness, in which case it will not be switched off. That covers the “manual override on” use case, but there are others like “manual override off” which I don’t see how to use.

Would it be reasonable to use Alexa in most bedrooms and have an “Alexa, good night” and “Alexa, good morning” sequence to respectively disable and re-enable motion control in that bedroom only? At a practical level would it work to have a nested group (eg a group that requires a “motion enabled” switch AND the motion sensor OR group to be active)?

I’m wondering what people usually do here? I previously used Hubitat and its approach was to track the level of the dimmer and compare the current dimmer-reported level with what Hubitat motion lighting last set it to before sending any further commands.

Also we have internal light sensors in each room. Is there a way to have a room-specific item name that provides its current ambient light level for the low_lux_trigger?

Thanks for any suggestions.

3 Likes

Oh, good… you found a bug/typo that I’ve never run into! You must have a group as a member of an action group that is not a dimmer. If you’d like to fix this and are not using the beta Jython bundle, use this for line 55…

    elif item.type == "Color" or (item.type == "Group" and item.baseItem.type == "Color"):

You will see something similar on line 46. You’ll also need to do the same on line 80. I have a lot of things to push, so I will get this one in too. Thank you for reporting this!

I understand the need, but this is not available out of the box. I use the timers built into our motion sensors and dimmers, so I have not needed to implement this functionality. However, area_triggers_and_actions is setup to be modular, so that you can create your own action in a personal module to implement this kind of functionality. If it is something that looks to be useful for others, then we can add it into the community module.

I think a delay ON/OFF based on mode is the best approach. If you start up a new topic for this, I’ll be glad to help put something together.

Another thought in case it helps, for an area used for watching movies or an office where you’re sitting still reading, I use smartplugs in the trigger group to keep the lights on. For example, a smartplug on the TV and AV keeps the family room active when watching a movie. It also allows us to turn them on/off through Alexa and saves a surprising amount of electricity by turning them off when they enter standby. I’ll add an example for this scenario in the docs.

Our daughter is 7, so our sleep/wake cycles are pretty much in sync, so I haven’t put much thought into this scenario. However, the lights coming on while trying to sleep off a cold is very annoying. What I have done is to use AND(ON,OFF) trigger groups, where the members are a switch and a group of triggers. The switch is then used to manually disable/enable the automations for that area…

Switch      US_MasterBedroom_Automation             "Master Bedroom (upstairs) [%s]"      <switch>        (gVirtual,gUS_MasterBedroom,gUS_MasterBedroom_Trigger)

Group:Switch:AND(ON,OFF)                    gUS_MasterBedroom_Trigger                   "Master Bedroom (upstairs) [%s]"                        <presence>            (gArea_Trigger)
    Group:Switch:OR(ON,OFF)                     gUS_MasterBedroom_Sensor_Trigger            "Master Bedroom (upstairs) [%s]"                        <presence>            (gUS_MasterBedroom_Trigger)

Another possibility for people with Z-Wave devices with a scene_number Channel is to disable the automation if the scene_number is set to 0 or 100. This signifies a manual switch/dimmer input. FYI, this will not work for Leviton devices, but hopefully @chris will add better handling of BASIC CC refreshes in the refactored binding.

Another possibility is to disable the Things for the lights or sensors in rooms were you do not want the automation to run.

Another possibility, which I used for a while to test out, was to not change the light if it is not currently set to one of the values that the automation would set it to. This still does not help with keeping a light turned off, but allows for custom dimming.

This can be accomplished with Modes like…

Night
Night (kid 1)
Night (kid 2)

… and then set the metadata in your Items for al of the Modes.

Thank you Scott for this lovely piece of work. It’s a clever design and I’ve started to adopt it here.

I already have an item for “Lighting Theme” (E.g. OFF, NORMAL, MOVIE, PARTY, PANIC) that I will synchronize to the “Mode” item. It would be great to be able to use a customized name instead of “Mode” btw. I also have separate modes for Clock_Time_Of_Day and Solar_Time_Of_Day that I use in my scripting.

I have defined my own light action function named generic_light_action and I have set it in the configuration file to be the default, "default_action_function": "generic_light_action".

I had some trouble to make it run instead of the light_action function and I’ve seen it’s been hard coded in the area_trigger.py as

                        if not action_function_names or "light_action" in action_function_names:
                            start_action(item, True if event is not None else trigger_group.state in [ON, OPEN], "light_action")

The following modified code works better for me:

                        if not action_function_names or area_triggers_and_actions_dict["default_action_function"] in action_function_names:
                            start_action(item, True if event is not None else trigger_group.state in [ON, OPEN], area_triggers_and_actions_dict["default_action_function"])

Cheers!

EDIT: Since the low_lux_trigger is baked into a named Mode, I’d also like to add an “ELSE” mode in so that I do’nt have to repeat low_lux_trigger for every possible mode. I’d like to suggest something like this:

low_lux_trigger = get_key_value(item.name, "area_triggers_and_actions", "modes", str(items["Mode"]), "low_lux_trigger") or get_key_value(item.name, "area_triggers_and_actions", "modes", 'ELSE', "low_lux_trigger") or area_triggers_and_actions_dict["default_levels"]["low_lux_trigger"]`

Then I could just add metadata like the following for a light bulb that’s ON in all modes:

set_metadata("Light_Facade_Tenants", "area_triggers_and_actions",
    {
        "modes": {
            "ELSE":    {"low_lux_trigger": 380, "brightness": 60}
        }
    }, overwrite=True)

And in my area_actions.py

    low_lux_trigger = get_key_value(item.name, "area_triggers_and_actions", "modes", str(items["Mode"]), "low_lux_trigger") or get_key_value(item.name, "area_triggers_and_actions", "modes", 'ELSE', "low_lux_trigger") or area_triggers_and_actions_dict["default_levels"]["low_lux_trigger"]
    hue = DecimalType(int(get_key_value(item.name, "area_triggers_and_actions", "modes", str(items["Mode"]), "hue") or get_key_value(item.name, "area_triggers_and_actions", "modes", 'ELSE', "hue") or area_triggers_and_actions_dict["default_levels"]["hue"]))
    saturation = PercentType(int(get_key_value(item.name, "area_triggers_and_actions", "modes", str(items["Mode"]), "saturation") or get_key_value(item.name, "area_triggers_and_actions", "modes", 'ELSE', "saturation") or area_triggers_and_actions_dict["default_levels"]["saturation"]))
    brightness = PercentType(int(get_key_value(item.name, "area_triggers_and_actions", "modes", str(items["Mode"]), "brightness") or get_key_value(item.name, "area_triggers_and_actions", "modes", 'ELSE', "brightness") or area_triggers_and_actions_dict["default_levels"]["brightness"]))

Edit: There is an issue with my code above so don’t use it. The issue is that if a zero brightness is set for an item, it’s sucessfully fetched but the or operator will interprete that as False and go on with fetching the next value in the chain. I have reworked the code since then.

(Needed the int cast due to that get_key_value might return a float)

@5iver,

I’ve been working more with your lovely contribution lately and it works really well here. It’s so cool. Thanks again for sharing it.

In my kitchen I always have some lights on when it’s dark or shady outside. I keep them at a relatively low dim level. When someone approaches the kitcken sink a motion sensor triggers and the “Kitchen Work Lights” over the sink lights up. However the ambient light in the kitchen might still be percieved as a bit murky for some. So I thought that it would be a great idea to raise the dim level a bit for some lights in the kitchen that already follows a “Mode”. As I already have a customized lights_actionit was quite easy for me to add some extra metadata like this:

    set_metadata("Light_Dim_Kitchen_Ceiling", "area_triggers_and_actions",
        {
            "emphasized_by_device": "MD_Kitchen",
            "manual_dimmer_lock_level": -1,
            "modes": {
                "OFF":    {"brightness": DIMLEVELS['OFF']},
                "NORMAL": {"brightness": DIMLEVELS['MEDIUM']},
                "MOVIE":  {"brightness": DIMLEVELS['MIN']},
                "DINNER": {"brightness": DIMLEVELS['LOW']},
                "PARTY":  {"brightness": DIMLEVELS['MEDIUM']},
                "ALERT":  {"brightness": DIMLEVELS['MAX']}
            }
        }, overwrite=True)

So in my customized lights_action I just check for if there is an emphasized_by_device value for the item and I check the state of the emphasized_by_device device to determine if I should raise the dim level or use the level that’s defined for the current mode. That works well but whenever the emphasized_by_device device changes it’s state I will need the “Mode or lux change” rule defined in community/area_triggers_and_actions/area_triggers.py to trigger so that the rule can iterates through all triggering groups and replays the actions. I made a quick and dirty fix and added an item trigger in that function and I also had to make set the variable mode_change_or_system_start to true when triggered by the newly added item. When doing that, I’m aware that my changes will be overwritten when i update that community file. Anyway, I thought that it would be nice if I could define a device or a list of devices in the configuration (e.g, 'replayTriggeringDevices': ['MD_Kitchen', 'MD_Hallway'],) that the “Mode or lux change” rule will pick up and trigger upon? Wouldn’t that be cool?

Cheers!

I’m very pleased to hear that you like and appreciate it!

I agree. It makes sense for this Item to be configurable. Please create an issue in the repo for this feature request and also the hard coded reference to “light_action”. I have local commits for these, as well as a few others.

I’m not sure what you mean here, but will take a look later tonight.

I think I have the exact same scenario… I’ll take a look at how I’ve managed this…

Just copy everything over into the /personal/ directories so that you can make customizations. Then diff the files when updating to see what changes were made. If you think your changes woiuld be useful, then submit an issue in the repo to discuss.

Hi there Scott!

I’ve implemented the example 2. A light switch with a motion sensor and contact sensor in Example Lighting Scenarios.

This example will describe the use of a contact sensor to stop a light from turning off if the door is closed. The light will turn ON when there is motion or the door closes. The light will turn OFF when there is no motion and the door is open.

Things are working as described but I think I need to customize it a bit because here we keep the bathroom door closed both when using the bathroom and after we’ve left it. I haven’t yet figured out a clever solution to achive what I want.

I’d like to turn on the lights in the bathroom when someone opens the door or when motion is detected (in case the door actually was opened before entering the bathroom). I’d wan’t to keep the lights on during the time it’s occupied. When the door opens again I’d like to turn off the lights immediately. I’d like a reset here so that the light will tun on again if the door is reopened. In case the door was never closed during the “visit” I’d need a timer to turn off the lights after some time.

I think my scenario is quite common. I’m not sure it’s possible to implement without lots of code… any ideas? Maybe I should just solve it by mounting a sensor on the lock of the door. That would be easy and I wouldn’t need a motion detector in the bathroom. The MD looks like a webcam anyway so :wink:

Cheers!

Edit: I’ll take the lock switch route. It involves some work mounting a micro switch to detect the lock status but I think it will be great.

I don’t think there is any way around this, other than leaving the door open when the room is empty. This has some benefits though, since the air will circulate through the house better… and you’ll never walk in on someone accidentally! To avoid walking into a dark room, I always set the next area out to become active so that the lights are always one step ahead!

Hey Amazing work ! I just managed to setup the first two areas without Metadata etc. Before doing this i was wondering how I would use separate indoor lux levels for all the areas. I use hue Sensors which provide ambient light as well so i would like to use that. I will now try to get my head around the whole logic as i would really love to use the logic for other things.

Thomas

I have setup some ESP12E based luminance/temp/humidity/pressure sensors and have adjusted the code for room level luminance. I have a lot of things to push and will try to get to them this weekend.

With the ESP’s do you have them in an enclosure? Link?

Not yet. They are working great but are still breadboarded until I find time to get some more experimenting in. Although, it’s been an amazingly good science project for our 7 year old, so it may be sooner than later! I definitely want to try out Tasmota, add motion, and setup an outdoor version with a camera. I plan to post when complete!

1 Like

That sounds amazing! Thanks a lot

1 Like