Occupancy Management - how I do it

Over the last 6 months or so, I have put together a Jython extension that assists with Occupancy Management. If you are familiar with Jython and would like to give this a spin, post below.

See the current readme here https://github.com/mjcumming/Occupancy-Manager/blob/master/README.md

Occupancy Manager

Introduction

Home automation often relies on knowing if the home or an area of the home is occupied. Occupancy Manager simplifies home automation tasks by tracking the occupancy state of area (locations or rooms). The occupancy manager uses existing devices that generate events to determine occupancy state. Based on the state of an area (vacant or occupied) lights, HVAC, and media devices can be turned on or off.

Unfortunately, it is often difficult to determine if an area is occupied unless each area has dedicated presence sensors. However, many areas will have other devices within them that can be used to decide if an area is occupied or vacant. Events from these devices (i.e. light switches, audio/video devices, security devices including motion sensors or door contacts) are used to set an area occupied or vacant.

An area is a discrete location in a home that we want to track occupancy for. Areas can be arranged in a hierarchical (i.e. Home/Main Floor/Bathroom) fashion which allows for more sophisticated management of occupancy control using event propagation. An area is given an occupancy time when an occupancy event occurs. For instance, in a bathroom, if a light switch is turned on, the bathroom is set to occupied for the duration of the occupancy time. The bathroom will remain occupied until that time expires and then it will become vacant. When the bathroom becomes vacant, the lights and/or exhaust fan can be set to automatically turn off with no user programming or scripting. While the bathroom is occupied, if there additional events (i.e. a light switch is changed, the exhaust fan is turned on) the occupancy time is reset.

The occupancy manager encapsulates all the logic needed to provide sophisticated occupancy-driven automation without requiring any additional programming. Item tags and metadata are used to control the behavior of the occupancy manager.

Operation

An area is a location/room/space that we want to track occupancy for. In OpenHAB each area is represented by a group item. It is beneficial (but not required) to setup areas in a hierarchical fashion. The rationale for doing this is it allows for event propagation from child to parent areas and for control of occupancy from parent to child areas. For instance, events in above example of a bathroom, are propagated to the Main Floor area, and to the Home and these areas remain occupied when their child area, the bathroom, updates it occupancy state.

Areas

Areas are defined in “.items” files as group items. Item files are currently required to be as used as the occupancy manager uses item tags and metadata to set behavior (metadata cannot be set using the PaperUI). Areas are tagged using the tag [“Area”]. This allows the occupancy manager to find areas it is to manage. Settings that determine the behavior of an area are set in the group items metadata. Each area can have set of automatic actions that happen when its state (Occupied or Vacant) changes. Each area has a default occupancy time that is used to determine how long the area remains in an occupied state after an event in that area occurs. If no further occupancy events occur in that time, the area becomes vacant. Additionally, item events can be used to set an area vacant (i.e. a presence sensor indicates the area is vacant).

Area metadata is stored in the namespace “OccupancySettings”. The value for the namespace is not used and is set to “” . The namespace configuration values (text with the square brackets) determines area behavior.

Metadata settings:

Time = number of minutes an area is occupied after an event that occurs that indicates the area is occupied.

VacantActions = actions to take when an area becomes vacant. Current allowed actions include LightsOff, SceneOff, AVOff, ExhaustFansOff

OccuppiedActions = action to take when an area becomes occupied. Current allowed actions include LightsOn, LightsOnIfDark, SceneOn, SceneOnIfDark

Area Group Item Examples:

Group gMF_Bathroom “Bathroom” (gIN_MainFloor) [“Area”] {OccupancySettings = “” [ Time = 15, VacantActions = “LightsOff,ExhaustFansOff” ]}

In this example, the area represents a bathroom. The gMF_Bathroom area is an area in the gIN_MainFloor area, i.e. the gIN_MainFloor area is the parent area. When the bathroom area becomes occupied, the occupancy time is set to 15 minutes. This means that if there are no other events in that area (i.e. a light switch changes or any other event that we track) the area will become vacant in 15 minutes. When the area becomes vacant, the VacantActions are executed, which in this case means to turn off the lights and exhaust fans (more later how to set this up)

Group gIN_MainFloor “First Floor” (gHM_Interior) [“Area”] {OccupancySettings = “” [ Time = 120, VacantActions = “LightsOff” ]}

In this example, the area represents the main floor of the home. This area is child area of the gHM_Interior area. The area is occupied for 120 minutes when there is any event in that area or any child areas (i.e. the bathroom above) change occupancy state. VacantActions are “LightsOff” which tells the occupancy manager to turn off any lights in the gIN_MainFloor group with the item tag [“Lighting”].

Group gEX_Garage “Garage” (gHM_Exterior) [“Area”] {OccupancySettings = “” [ Time = 10, OccupiedActions = “SceneOnIfDark”, VacantActions = “SceneOff” ]}

In this example, the garage, when it becomes occupied, the lights are turned on if it is dark outside. When the area becomes vacant, all the lights are turned off.

Ares may have any number of items that generate events that indicate that the area is occupied or vacant. For instance, turning a light switch on indicates that the area is occupied or if a motion sensor detects motion, then the area is occupied. Additionally, turning off a light switch can be used to change an area to vacant. Items (switches, sensors, or any device that generates an event) are added to the area group. More below.

Areas can be arranged in a hierarchical manner which allows for tracking of occupancy and subsequent actions across a range of areas.

For instance, if we have a home

  1. Home
  2. First floor
    1. Livingroom
    2. Bathroom
    3. Kitchen
  3. Second floor
    1. Bathroom
    2. Bedroom 1
    3. Bedroom 2

An occupancy event in the First Floor bathroom will automatically update the occupancy state and time of the First Floor and the Home. If, through a rule, the Home is set to Vacant, the vacancy event will propagate to all of the other areas and perform all the vacancy actions set in those areas. In short, setting item that represents the Home to vacant, all of the lights in the home could be turned off.

Item Events

Any OpenHAB item (device) can be used to set the occupancy or vacancy of an area. Items used change occupancy status are assigned to the group gOccupancyItem . This group is used in a rule to catch changes to its members and propagate that event to the occupancy manager. The gOccupanyItem group is created automatically by the occupancy manager.

Items used for occupancy use item metadata to determine how the events from the item change occupancy status. The namespace OccupancyEvent is set to the type of event that the item generates (examples below). Currently, there 4 types of predefined events that items can generate.

OnOff = Generates an occupancy event when the item is turned on (i.e. light switch). No event when the item is turned off.

ContactMotion = Generates an occupancy event when the contact is opened, no event when the contact closes

ContactDoor = Generates an occupancy event when the contact is opened, the area remains in an occupied state until the door is closed. The occupancy timer starts when the door is closed.

ContactPresence = Generates an occupancy event when the contact is opened, the area remains in an occupied state until the sensor is closed. The area is set to vacant immediately when the close event occurs.

Switches (i.e. lights, power states) and Dimmer (lights, volume controls) items used the OnOff event. Contact items uses one of the 3 contact event types

In addition to generate events to set an area as occupied or vacant, there are metadata key/value pairs that are used to modify the standard behavior of the predefined occupancy events. These key value pairs include BeginOccupiedTime and EndOccupiedTime. These additional settings allow overriding the default occupied time for an area. Example below.

Metadata settings for items used in occupancy management:

OccupancyEvent = occupancy event type

Additional settings for using the configuration portion of the metadata include:

BeginOccupiedTime, minutes

EndOccupiedTime, minutes

Item Examples:

Switch MF_MainFloorBathroom_Scene “Main Floor Bathroom Lights” (gMF_Bathroom ,gOccupancyItem) [“Lighting”,“AreaScene”] {channel=“xxx”, OccupancyEvent = “OnOff”}

Contact MF_MainFloorBathrrom_MotionSensor “Bathroom Motion” (gMF_Mudroom, gOccupancyItem) {channel=“xxx”, OccupancyEvent = “ContactMotion”}

Item Control

Items to be controlled by the occupancy manager must be tagged so the occupancy manager can identify them. Current defined include Lighting, AreaScene, AVPower, ExhaustFan.

Item Examples:

Switch MF_MainFloorBathroom_Scene “Main Floor Bathroom Lights” (gMF_Bathroom ,gOccupancyItem) [“Lighting”,“AreaScene”] {channel=“xxx”, OccupancyEvent = “OnOff”}

In the example of above, this item, a lighting scene is tagged with AreaScene which is used to indicate to the occupancy manager to use this item to turn on/off lighting in this area when occupancy/vacancy events occur.

Extended Functionality

The occupancy manager automatically creates additional items that can be used to generate custom automation beyond the default behavior set in the occupancy manager. Documentation below to be expanded in the future.

Occupancy State Item

Each area includes an occupancy state item that generates On (occupied) or Off (vacant) events. These items are named OS_XXX, XXX set to the name of the area. These events can be used to trigger additional actions using standard OpenHAB rules. The occupancy state item for an area can also be used to set the occupancy state of that area in instances where the standard behavior of the occupancy manager does not meet the users needs.

Occupancy Locking Item

Each area includes an occupancy locking item. This item is represents the locking state of an area. These items are named OL_XXX, XXX set to the name of the area. This is used in situations where the user does not want item events to change the occupancy state of an area. For instance, a home maybe vacant and the user wants to turn on inside lights to make the home look occupied. In this instance, the user would not want the occupancy state of the home turned to occupied.

The occupancy locking items are NOT to be directly changed by external events or controls. Locking state is controlled by the occupancy control item below.

Occupancy Control Item

Each area includes an occupancy control item. These items are named OC_XXX, XXX set to the name of the area. This item is used to set the occupancy locking for that area. The occupancy control is set to LOCK or UNLOCK or CLEARLOCKS.

LOCK = lock the occupancy state of the area. Subsequent LOCK commands increase the level of locking.

UNLOCK = decreases the locking level of an area. When the locking level is 0 there area becomes unlocked.

CLEARLOCKS = Sets the locking level of an area to 0 and unlocks the area regardless of how many LOCK commands were sent.

13 Likes

Code is available on github

1 Like

Hey @mjcumming Great work!. Will this become part of the community library package?

I would like to get it there. I would like to see a few more users and to flesh out details and concepts a little more.

@theiding Mark, in reference to your prior post.

  1. The module was built on the premise of a child only having a single parent. I can see how your changes would work. I am not convinced its a good idea or not. One way to solve your use case is create multiple items using the same channel and to place those items in the Area group you need. If we don’t enforce the concept of each child having a single parent, I can see users creating issues where groups loose their hierarchical structure.

  2. Locking: I think we need to flesh this out a little more. Ie. how does a lock propagate. Currently the lock propagates from the Area to its Child Areas only. It does not propagate back up to its parent. In retrospect, The lock should propagate to the Parent and back to the root area. This would allow removal of your specialized area.

3, Overriding Locks. The way you have this implemented it doesn’t propagate. So if you had an Area locked and it was vacant, then overrode the lock and set it occupied, then the parent Areas would stay Vacant - which shouldn’t happen. What is the use case?

Thoughts?

@mjcumming Mike, appreciate your review. Here are my thoughts:
ad 1) Creating multiple items with the same channel only works in cases where an item has multiple parents (I do use that a lot). However, I added the multiple parent support for cases where an OM Area has multiple parents. E.g say Area A has child Area B and Area C and Area D has child Area B and Area D. If I am not allowed to give Area B two parents I am forced to create Area B’ as an identical copy. This use case is common when Areas represent activities. This occurs even more commonly for locks (and that’s where I ran into this first), where I frequently use the same lock for multiple Areas. It is very cumbersome to force a user to create identical lock copies for each Area it’s used in. I view multiple parents simply as widening the use of Areas without losing anything - strict hierarchies are still supported exactly as they were before. What issue do you see?

ad 2) Lock propagation has two aspects:
a) Lock state: Since an AreaLock is just an Area it can have other Areas as children and events propagate through that hierarchy just like any Area hierarchy. This propagation determines the state of the lock. It would be a complex lock that requires an Area hierarchy to determine the lock state.
b) Lock assignment: Say we have Area A with child Area B. Then we assign AreaLock L to A. Right now my PR has no lock assignment propagation, i.e. B would remain without an assigned lock. If we want L to also lock B then we can simply assign it to B as well. Or we can create a different AreaLock for B. This gives the most flexibility - that’s why I started with this approach. Later we could add configuration for assignment propagation (e.g. adding a lock to the parent would propagate the lock assignment to all children). I didn’t add this to my PR as I didn’t want to further complicate the initial PR.
The benefit of the AreaLock is that it brings all the power of Areas to locks for free and that it can replace the existing lock code. Less code, more functionality.

ad 3) I am viewing an override truly as a local override. If you feel an override should propagate we can add that. My use case is that I want light switches to always operate even if they are actioned through a locked area.

Hi Mark,

  1. I think I understand but help me with a use case. When you say an area represents activities I am not sure I understand? I don’t see any definite issues other than it breaks the concept. Would there be a better way to accomplish what you need? - hence the ask for a use case.

  2. Locking: I think we need to flesh this out more to understand how you and I use this. I’ll give you 3 scenarios that I have:
    a. The Home is Vacant. Motion outside the home is detected and I want to turn on an inside light. Normally turning that light ON would set the Home to Occupied (not desired), so I use locking to prevent that.
    b. We are having a party and I don’t want any changes to occupancy. Ie. I don’t want bathroom lights turned off automatically if the bathroom area becomes vacant. So in that case, I can lock the main floor of the house and the lights, audio etc are not changed.
    c. If the patio door is open, I lock the patio area state to occupied (I don’t have any other devices to get consistent occupancy events there). That way the patio audio stays on. When the patio door is closed the lock releases and the occupancy count down starts and when it expires the audio is turned off (ie we forget it is on)

  3. The issue with an override becomes keeping the parent/child relationship intact. A child cannot be occupied and the parent vacant. What is the use case where you would need to override a lock?

Thank you for helping flesh these ideas out!

Thanks for the continued discussion.

Ad 1:
Use case 1:
AreaFrontDoor, occupied through combination of door contact and door lock plus timers
AreaGarage, occupied through motion sensor
AreaOutside, parent of AreaFrontDoor and AreaGarage
AreaKitchen, occupied through motion sensor
AreaInside, parent of AreaKitchen and AreaFrontDoor
Here AreaFrontDoor sits on the border of AreaInside and AreaOutside and participates in both and thus would like to have two parents. Workaround would be to create duplicates AreaFrontDoorForInside and AreaFrontDoorForOutside.

Use case 2:
AreaRearDoorBolt, occupied for 5min if bolt is either locked or unlocked
AreaRearDoorContact, occupied without timer if contact is opened, occupied for 5min if contact is closed
RearDoor is an OR Group of AreaRearDoorBolt and AreaRearDoorContact (i.e. RearDoor is ON if either area is ON)
RearDoor then actions RearDoorLight through Area Triggers & Actions
Turning RearDoorLight on or off via its light switch should lock the ReadDoorLight for 5min (i.e. turning off the light and then opening the rear door should not turn the light right back on).
For that I use AreaLockRearDoor, which is “occupied” by the light switch for 5min. AreaLockRearDoor has both AreaRearDoorBolt and AreaRearDoorContact as its parent, as a lock is assigned through parent child relationship. Now as long as AreaLockRearDoor is occupied then both AreaRearDoorBolt and AreaRearDoorContact will ignore occupancy events as desired.
Note that we need multiple parents to assign locks to multiple areas - here it is not to have two parents in an occupancy hierarchy. I implemented multiple parents for this case first. To only support it for locks but not for occupancy hierarchies would have been additional code and given use case 1 I saw additional value for occupancy hierarchies as well.

ad 2:
a) That makes perfect sense. I assume you use the outside motion event as an occupancy trigger for the room you want to turn on?
Side note 1 re. activity vs occupancy: If you wanted to trigger a random light in the house I would envision to create an AreaSecurity that gets triggered by the outside motion sensor (it could have a timer for say 1h to stay on and then go off again). The action for AreaSecurity would then pick a random light in the house and turn it on an off as per AreaSecurity occupancy. AreaSecurity would not represent occupancy within your house occupancy hierarchy, but rather a standalone non-hierarchical “activity” - that’s what I meant by the term activity. You would then need to OR AreaSecurity together with each of the participating Areas.
Side note 2: Since I use Area Triggers & Actions for all my actions I can’t just turn a light on in the case above. If I do that Area Triggers & Actions can at any point re-evaluate brightness settings (e.g. on a mode or luminance change) and undo my change right away (e.g. I turn light on, mode changes from Day to Evening, light is configured for brightness 0 at night and light gets turned off). That’s why I proposed the notion of an override. It is interesting to see how much more complexity I have to go through for of using Area Triggers & Actions - I will think more through whether there could be a simpler path while still getting all the benefits from Area Triggers & Actions.

b) Got it, makes sense
c) I have a very similar use case (see AreaRearDoorContact above). Why do you need a lock here? Doesn’t a regular Area do exactly what you need? Door open occupies AreaPatio, door close starts a timer for AreaPatio. Audio is then the action for AreaPatio and thus will be on when the door is open and for x minutes after door close.

I think my lock use case above and your cases a+b are very similar. That’s why I didn’t change anything in the functional behavior of locks, just the implementation. I don’t think I understand c) enough yet to comment.

ad 3) Let’s start by looking at the need to override a lock outside of a hierarchy. Here is a use case:
AreaBedroom is triggered by a motion sensor (with timer).
AreaBedroom actions BedroomLight.
When a user flips the switch of BedroomLight I want the light to remain in that state and ignore any occupancy changes for the next 30 minutes. Otherwise you could for example have somebody turn off a light and it would turn right back on with the next motion.
For that I use AreaBedroomLock that is “occupied” by the light switch for 30min (timer is set for both on and off events).
Remember that in the world of Area Triggers & Actions I can’t just turn a light on or off. Instead I have to set the occupancy for an area correctly and then the right state for the light will follow. For BedroomLight to actually come on/off from the switch I therefore have to make it a trigger for AreaBedroom. However, since BedroomLight is also a trigger for the lock it might lock itself out. Thus the need for a lock override so that BedroomLight can go on and off even if AreaBedroom is locked.

Another example would be your party case. If the first floor is locked but I want to allow people to still turn a switch on or off manually I need to allow a lock override. In your case you action directly from within OM and a manual light switch flip is the override in itself, but if you have a framework such as Area Triggers & Actions evaluate the state based on Area occupancy an explicit override is needed.

As to overrides and hierarchies: since I can only action a light by changing occupancy I by definition would be forced to have a child occupied and a parent vacant to implement your use case 2a. But isn’t that ok? Default is occupancy propagation through the hierarchy, locks and overrides can then change that where needed. Doesn’t that work well?

Phew - this is definitely a complex topic. Thank you for sharing your use cases. Very helpful for me to shed more light and to think more broadly. I hope the above helps, too.

Sorry for the delay, I can see how difficult this is with overlapping concepts and the many ways to solve automation issues.

Let’s solve the hierarchy issue first. I really want to stay with concept that a child area can only have a single parent unless we cannot find a way to work around that limitation.

For a door, that involves two areas, Front Porch and Foyer. In this case, I simply have 2 door items using the same channel with each belonging to one of the areas.

Contact     MF_BackPatioDoors_Sensor    "Back Patio Doors"   (gMF_FamilyRoom, gOccupancyItem)   {channel="omnilink:zone:home:84:contact", OccupancyEvent = "Contact"} 
Contact     EX_BackPatioDoors_Sensor    "Back Patio Doors"   (gEX_BackPorch, gOccupancyItem)   {channel="omnilink:zone:home:84:contact", OccupancyEvent = "Contact" [ModifyBehavior = "OccupiedUntilEnded"]} // duplicate, for back porch occupancy

An alternative would be to have a single item belong to 2 Areas.

Contact     MF_BackPatioDoors_Sensor    "Back Patio Doors"   (gMF_FamilyRoom,gEX_BackPorch, gOccupancyItem)   {channel="omnilink:zone:home:84:contact", OccupancyEvent = "Contact"} 

Any reason why we can’t solve your use case without changing the parent/child concept?

Below is how I have Areas setup


/* 
 
Areas

An Area is a physicial discrete location that contains items that are in that area.
 */



Group    gHome                                              "Home"                          <house>                 (gRoot)                {OccupancySettings = "" [ Time = 120, VacantActions = "" ]}          

    Group    gHM_Exterior                                   "Exterior"                      <lawnmower>             (gHome)                {OccupancySettings = "" [ Time = 120 ]} 
        Group    gEX_Patio                                  "Patio"                         <terrace>               (gHM_Exterior)         {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff,AVOff", OccupiedActions = "SceneOnIfDark"]}
        Group    gEX_BackYard                               "Backyard"                      <lawnmower>             (gHM_Exterior)         {OccupancySettings = "" [ Time = 120, VacantActions = "" ]}
        Group    gEX_FrontYard                              "Front Yard"                    <lawnmower>             (gHM_Exterior)         {OccupancySettings = "" [ Time = 120, VacantActions = "" ]}
        Group    gEX_FrontPorch                             "Front Porch"                   <none>                  (gHM_Exterior)         {OccupancySettings = "" [ Time = 120, VacantActions = "" ]}
        Group    gEX_BackPorch                              "Back Porch"                    <none>                  (gHM_Exterior)         {OccupancySettings = "" [ Time = 30, VacantActions = "LightsOff,AVOff", OccupiedActions = "SceneOnIfDark" ]}
        Group    gEX_Garage                                 "Garage"                        <none>                  (gHM_Exterior)         {OccupancySettings = "" [ Time = 10, OccupiedActions = "SceneOnIfDark", VacantActions = "SceneOff" ]}

    Group   gHM_Interior                                    "Inside"                        <house>                 (gHome)                {OccupancySettings = "" [ Time = 120, VacantActions = "" ]}  
        Group   gIN_Basement                                "Basement"                      <cellar>                (gHM_Interior)         {OccupancySettings = "" [ Time = 60, VacantActions = "SceneOff" ]} 
            Group    gBM_RecRoom                            "Rec Room"                      <none>                  (gIN_Basement)         {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff,AVOff" ]}
            Group    gBM_Bathroom                           "Bathroom"                      <bath>                  (gIN_Basement)         {OccupancySettings = "" [ Time = 15, VacantActions = "SceneOff,ExhaustFansOff" ]}
            Group    gBM_StorageRoom                        "Storage Room"                  <suitcase>              (gIN_Basement)         {OccupancySettings = "" [ Time = 5, VacantActions = "LightsOff", OccupiedActions = "LightsOn" ]}
            Group    gBM_Stairs                             "Stairs Up"                     <qualityofservice>      (gIN_Basement)         {OccupancySettings = "" [ Time = 120, VacantActions = "LightsOff" ]}
        
        Group   gIN_MainFloor                               "First Floor"                   <groundfloor>           (gHM_Interior)         {OccupancySettings = "" [ Time = 120, VacantActions = "" ]}
            Group    gMF_SideEntry                          "Side Entry"                    <none>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]} // duplicate of basement stairs, allows setting of MF occupancy
            Group    gMF_LivingRoom                         "Living Room"                   <sofa>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff,AVOff" ]}
            Group    gMF_DiningRoom                         "Dining"                                                (gIN_MainFloor)        {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}
            Group    gMF_FrontEntryHallway                  "Front Entry"                   <none>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}
            Group    gMF_Kitchen                            "Kitchen"                       <kitchen>               (gIN_MainFloor)        {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff,AVOff" ]}
            Group    gMF_BackHallway                        "Back Hallway"                  <none>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}
            Group    gMF_Pantry                             "Pantry"                        <none>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 20, VacantActions = "LightsOff" ]}
            Group    gMF_Mudroom                            "Mudroom"                       <none>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 15, VacantActions = "SceneOff", OccupiedActions = "SceneOnIfDark" ]}    
                Group    gMF_MudroomCloset                  "Mudroom Closet"                <none>                  (gMF_Mudroom)          {OccupancySettings = "" [ Time = 5, VacantActions = "LightsOff" ]}    
            Group    gMF_EatingArea                         "Eating Area"                   <none>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 60, VacantActions = "LightsOff" ]}
            Group    gMF_FamilyRoom                         "Family Room"                   <parents_2_4>           (gIN_MainFloor)        {OccupancySettings = "" [ Time = 60, VacantActions = "SceneOff,AVOff" ]}
            Group    gMF_Bathroom                           "Bathroom"                      <bath>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 15, VacantActions = "SceneOff,ExhaustFansOff" ]}
            Group    gMF_Stairs                             "Stairs"                        <none>                  (gIN_MainFloor)        {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}
        
        Group   gIN_SecondFloor                             "Second Floor"                  <firstfloor>            (gHM_Interior)         {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}
            Group    gSF_Master                             "Master"                        <bedroom_red>           (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}
                Group    gSF_MasterBedroom                  "Master Bedroom"                <bedroom_red>           (gSF_Master)           {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff,AVOff" ]}
                Group    gSF_MasterSittingRoom              "Master Sitting Room"           <none>                  (gSF_Master)           {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff,AVOff" ]}
                Group    gSF_MasterBathroom                 "Master Bathroom"               <bath>                  (gSF_Master)           {OccupancySettings = "" [ Time = 30, VacantActions = "SceneOff,AVOff" ]}
                    Group    gSF_MasterBathroomShower       "Master Bathroom Shower"        <bath>                  (gSF_MasterBathroom)   {OccupancySettings = "" [ Time = 15, VacantActions = "SceneOff,ExhaustFansOff" ]}
                    Group    gSF_MasterBathroomToilet       "Master Bathroom Toilet"        <bath>                  (gSF_MasterBathroom)   {OccupancySettings = "" [ Time = 10, VacantActions = "SceneOff,ExhaustFansOff" ]}
                    Group    gSF_MasterBathroomVanity       "Master Bathroom Vanity"        <bath>                  (gSF_MasterBathroom)   {OccupancySettings = "" [ Time = 30, VacantActions = "SceneOff,ExhaustFansOff" ]}
                Group    gSF_MasterNorthCloset              "Master North Closet"           <none>                  (gSF_Master)           {OccupancySettings = "" [ Time = 10, VacantActions = "LightsOff" ]}
                Group    gSF_MasterSouthCloset              "Master South Closet"           <none>                  (gSF_Master)           {OccupancySettings = "" [ Time = 10, VacantActions = "LightsOff" ]}
            Group    gSF_Hallway                            "Hallway"                       <corridor>              (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 30, VacantActions = "SceneOff" ]}
            Group    gSF_Stairs                             "Stairs Up"                     <qualityofservice>      (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}
            Group    gSF_GuestBedroom                       "Guest Room"                    <parents_4_3>           (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 120, VacantActions = "LightsOff" ]}
            Group    gSF_KidsBathroom                       "Kids Bathroom"                 <bath>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 20, VacantActions = "SceneOff,ExhaustFansOff" ]}
            Group    gSF_KidsNorthBedroom                   "Kids North Bedroom"            <none>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 120, VacantActions = "LightsOff" ]}
                Group    gSF_KidsNorthBedroomCloset         "Kids North Closet"             <none>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 15, VacantActions = "LightsOff" ]}
                Group    gSF_KidsNorthVanity                "Kids North Vanity"             <none>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 15, VacantActions = "LightsOff" ]}
            Group    gSF_KidsSouthBedroom                   "Kids South Bedroom"            <none>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 120, VacantActions = "LightsOff" ]}
                Group    gSF_KidsSouthBedroomCloset         "Kids South Closet"             <none>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 15, VacantActions = "LightsOff" ]}
                Group    gSF_KidsSouthVanity                "Kids South Vanity"             <none>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 15, VacantActions = "LightsOff" ]}
            Group    gSF_LaundryRoom                        "Laundry Room"                  <none>                  (gIN_SecondFloor)      {OccupancySettings = "" [ Time = 10, VacantActions = "SceneOff" ]}             
        
        Group   gIN_ThirdFloor                              "Third Floor"                   <attic>                 (gHM_Interior)         {OccupancySettings = "" [ Time = 120, VacantActions = "SceneOff" ]}            
            Group    gTF_Bedroom                            "Bedroom"                       <bedroom>               (gIN_ThirdFloor)       {OccupancySettings = "" [ Time = 120, VacantActions = "LightsOff" ]}
            Group    gTF_Office                             "Office"                        <office>                (gIN_ThirdFloor)       {OccupancySettings = "" [ Time = 60, VacantActions = "SceneOff"]}//, OccupiedActions = "SceneOn" ]}                                



Hi Mike, thanks for taking the call last weekend - that was really productive. As we discussed I will adjust the config mechanism for the Area_Lock and remove the multiple parent support for now.
To prepare for the change I setup a simple test case with a basic lock that I can simply control from a switch (lock is on when switch is on and lock if off when switch is off):
Group gPOC_Lock “POC Lock” (gPOC1_Trigger,gPOC2_Trigger,gOccupancyLock) {OccupancySettings = “”}
Switch POCLock “POC Lock [%s]” (gOccupancyItem, gPOC_Lock) {OccupancyEvent = “OnOff” [BeginOccupiedTime = “-1”, EndOccupiedTime = 0]}
While this works fine I realized that my POCLock switch already is the lock I want - the Area gPOC_Lock doesn’t add any value here as it simply follows the switch.
So just as I thought we discussed everything around locks I think we can easily make locks even more flexible: Any switch can be added as a lock to an Area. If the switch is ON the Area is locked, if the switch is OFF the Area is unlocked. Very simple. The user can select what kind of switch they need. A simple one like POCLock for my test case above, or leverage an Area when triggers, timers, hierarchies etc. are needed (using the OS_… switch from the Area) or any other switch.
That means I will monitor for any lock state changes in the Area (so I can cancel timers) and won’t need a derived class Area_Lock after all. We will get the best of all worlds: the power of Areas can be used for locks where appropriate, other switches can be leveraged and no need for an additional class while the existing lock code can still be removed.
I’ll put forward the PR and then you can test drive it for your use cases.

Interesting approach. Sounds great. Let me know when the PR is ready!

Hi Mike, alright, PR is ready now: https://github.com/mjcumming/Occupancy-Manager/pull/1.
Hopefully you will be able to easily port and simplify your existing lock use cases to the new lock mechanism. Use an Area as your lock item when you need a timed and/or triggered lock, use a regular switch otherwise.

Next step would then be to remove the old lock implementation. Note that the new lock implementation requires the user to explicitly provide a lock switch item. Lock commands are sent directly to that lock item, which I find very intuitive. While we could provide an implicitly created default item I think that would only be confusing to the user. Let me know if you would like me to do the removal or if you prefer to do it.

Let me know what you think!

Mike, while you review the PR to parallelize our effort I wanted to lineup the next PR: For help with debugging I made minor additions/refinements to Area:log_details to log details on the lock as well and to Area_Manager to log the entire hierarchy. Output for an area will look something like (work in progress)

Area: gAreaFrontDoor_Contact, Occupied (7 minutes and 30 seconds remaining until 16:53:28 07/18/2020), Unlocked
  Occupancy Items:
    LightFrontDoor_ForContact event settings OnOff
    LockFrontDoorState2 event settings Contact
  Lock Item:
    Area: gAreaFrontDoor_Lock, Vacant, Unlocked
      Occupancy Items:
        LightFrontDoor_ForLock event settings OnOff

Ok that I submit that as the next PR once you are done with the above?

Hi @mjcumming, how are things going? Did you get a chance to look at the PR above?

Hi Mark, sorry been really busy at work plus a death in the family. Sometime soon :slight_smile:

Michael, thank you for the update. I am sorry to hear about your loss - best wishes for you and your family!

I should have some time to work on this. Where are we at? :slight_smile:

Excellent. Next up is my pending pull request.

1 Like

merge done, have not updated on my local OH instance to test

Thanks. Let me know what you see once you uptake it locally. I’ll need to get back into it now with the next steps.