FYI: Alternative approach to Python rules

Hi all,

A while ago a few of us discussed about wrapping a layer above the Open Hab items. So instead of flat list of items, we would bind them into zones or rooms. A zone would contains one or more devices/sensors. Once the structure has been populated (into a ZoneManager that contains multiple zones), actions can be written to re-act on sensor events.

I’ve created a simple API to do that and applied to a number of rules for my house. It is not necessary better; it’s just a different approach than Python rules with its own pros and cons.

Cheers,

1 Like

sort of like this?

@Bruce_Osborne That’s interesting. I didn’t know Scott also tried to tackle this. Yes, the concept is similar, but the approach is quite different.

IMO, there are two aspects of this kind of systems: the front end and the back end.

  1. The front end takes care of organizing flat items into an object hierarchy.
  2. The back end exposes a set of API that allows querying the object hierchary to ask questions such as
    • Which zone am I being triggered in?
    • What other devices are there in the current zone, and what are their states?
    • What are the zones besides me?
    • Does the system has a device of specific type?

Scott’s approach tackles the front end by using the existing group concept and as such is easier for existing users to migrate over. The actions essentially operate directly on items. The big advantage is that there is no new concept to learn.

My framework focuses more on the backend to expose a set of APIs that actions can interface with. It is textbook example of Object Oriented design.

Actions do not operate on items; instead they operate on concrete objects that abstract away the items. It leaves open how you build up the object hierarchy; there are many way to do that. At the moment, I just parse my items based on certain naming pattern.

Here’s the main flow when building up from the beginning:

  1. Construct a set of Zone objects, define neighbouring relationship between them and add them to the ZoneManager.
  2. Construct a set of Device objects, and add them to the appropriate Zone objects.
  3. Create a set of Action objects, and add them to the appropriate Zone.

A Python script will route OpenHab events --> ZoneManager --> Zone(s) --> Action(s).

Here’s the full code for the a simple action:

from aaa_modules.layout_model.zone import ZoneEvent
from aaa_modules.layout_model.neighbor import Neighbor, NeighborType
from aaa_modules.layout_model.devices.switch import Light
from aaa_modules.layout_model.action import Action

class TurnOffAdjacentZones(Action):
    '''
    Turn off the lights in the zones adjacent to the current zone if the 
    current zone's liht is on and if the adjacent zones are of the OPEN_SPACE
    and OPEN_SPACE_SLAVE type.
    '''

    def getTriggeringEvents(self):
        '''
        :return: list of triggering events this action process.
        :rtype: list(ZoneEvent)
        '''
        return [ZoneEvent.SWITCH_TURNED_ON]

    def onAction(self, eventInfo):
        events = eventInfo.getEventDispatcher()
        zone = eventInfo.getZone()
        zoneManager = eventInfo.getZoneManager()

        if None == zoneManager:
            raise ValueError('zoneManager must be specified')

        lights = zone.getDevicesByType(Light)
        if len(lights) == 0:
            return False

        adjacentZones = zone.getNeighborZones(zoneManager,
                [NeighborType.OPEN_SPACE, NeighborType.OPEN_SPACE_SLAVE])
        for z in adjacentZones:
            if z.isLightOn():
                z.turnOffLights(events)
        
        return True

And here is a slightly more complicate rule to control light and fan switch:

Here’s an example of the object structure in my house:

2020-04-10 18:11:10.607 [INFO ] [eclipse.smarthome.model.script.Rules] - Configured ZoneManager with 14 zones.
2020-04-10 18:11:10.642 [INFO ] [eclipse.smarthome.model.script.Rules] - 14 zones
Zone: Porch, floor 1, external, 3 devices
  Door: FF_Porch_Door
  MotionSensor: FF_Porch_MotionSensor, lastOnTimestamp: -1
  Camera: FF_Porch_Camera

  Action: 1 -> AlertOnEntraceActivity
  Action: 1 -> SimulateDaytimePresence
  Action: 4 -> AlertOnExternalDoorLeftOpen
  Action: 5 -> AlertOnExternalDoorLeftOpen
  Action: 5 -> ArmAfterFrontDoorClosed

  Neighbor: 1_Office, 2
  Neighbor: 1_Foyer, 2
Zone: MasterWashroomShower, floor 2, internal, 2 devices
  Fan: SF_MasterWashroomShower_FanSwitch, SF_MasterWashroomShower_FanSwitch_Timer
  MotionSensor: SF_MasterWashroomShower_FanSwitch_MotionSensor, lastOnTimestamp: -1

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones
  Action: 2 -> PlayMusicDuringShower
  Action: 3 -> PlayMusicDuringShower

  Neighbor: 2_MasterWashroom, 2
Zone: Garage, floor 1, external, 1 devices
  Door: FF_Garage_Door

  Action: 1 -> AlertOnEntraceActivity
  Action: 1 -> SimulateDaytimePresence
  Action: 4 -> AlertOnExternalDoorLeftOpen
  Action: 5 -> AlertOnExternalDoorLeftOpen
  Action: 5 -> ArmAfterFrontDoorClosed
Zone: Kitchen, floor 1, internal, 5 devices
  IlluminanceSensor: FF_Kitchen_LightSwitch_Illuminance
  Light: FF_Kitchen_LightSwitch, FF_Kitchen_LightSwitch_Timer, illuminance: 8
  MotionSensor: FF_Kitchen_LightSwitch_MotionSensor, lastOnTimestamp: -1
  MotionSensor: FF_Kitchen_LightSwitch_PantryMotionSensor, lastOnTimestamp: -1
  AstroSensor: VT_Time_Of_Day

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones

  Neighbor: 1_Foyer, 2
  Neighbor: 1_GreatRoom, 3
Zone: MainWashroomShower, floor 2, internal, 2 devices
  MotionSensor: SF_MainWashroomShower_FanSwitch_MotionSensor, lastOnTimestamp: -1
  Fan: SF_MainWashroomShower_FanSwitch, SF_MainWashroomShower_FanSwitch_Timer

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones
  Action: 2 -> PlayMusicDuringShower
  Action: 3 -> PlayMusicDuringShower
Zone: Office, floor 1, internal, 4 devices
  Plug: FF_Office_Plug
  MotionSensor: FF_Office_LightSwitch_MotionSensor, lastOnTimestamp: -1
  Light: FF_Office_LightSwitch, FF_Office_LightSwitch_Timer, illuminance: 8
  AstroSensor: VT_Time_Of_Day

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones

  Neighbor: 1_Foyer, 4
Zone: Foyer, floor 1, internal, 6 devices
  Door: FF_Foyer_Door
  MotionSensor: FF_Foyer_LightSwitch_ClosetMotionSensor, lastOnTimestamp: -1
  AlarmPartition: FF_Foyer_AlarmPartition
  Light: FF_Foyer_LightSwitch, FF_Foyer_LightSwitch_Timer, illuminance: 8
  MotionSensor: FF_Foyer_LightSwitch_MotionSensor, lastOnTimestamp: -1
  AstroSensor: VT_Time_Of_Day

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones
  Action: 6 -> TurnOffDevicesOnAlarmModeChange
  Action: 8 -> TurnOffDevicesOnAlarmModeChange

  Neighbor: 1_Office, 3
  Neighbor: 2_Lobby, 2
Zone: Virtual, floor 1, internal, 4 devices
  NetworkPresence: FF_Virtual_NetworkPresenceOwner3Phone
  NetworkPresence: FF_Virtual_NetworkPresenceOwner2Phone
  NetworkPresence: FF_Virtual_NetworkPresenceOwner1Phone
  ActivityTimes: ActivityTimesItem
Zone: GreatRoom, floor 1, internal, 5 devices
  Plug: FF_GreatRoom_Plug
  Light: FF_GreatRoom_LightSwitch, FF_GreatRoom_LightSwitch_Timer, illuminance: 8
  ChromeCastAudioSink: Chromecast-FF_GreatRoom_ChromeCast-chromecast:audio:greatRoom
  MotionSensor: FF_GreatRoom_LightSwitch_MotionSensor, lastOnTimestamp: -1
  AstroSensor: VT_Time_Of_Day

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones

  Neighbor: 1_Kitchen, 4
Zone: MasterBedroom, floor 2, internal, 1 devices
  ChromeCastAudioSink: Chromecast-SF_MasterBedroom_ChromeCast-chromecast:audio:masterBedroom
Zone: Patio, floor 1, external, 1 devices
  Door: FF_Patio_Door

  Action: 1 -> AlertOnEntraceActivity
  Action: 1 -> SimulateDaytimePresence
  Action: 4 -> AlertOnExternalDoorLeftOpen
  Action: 5 -> AlertOnExternalDoorLeftOpen
  Action: 5 -> ArmAfterFrontDoorClosed

  Neighbor: 1_Kitchen, 2
Zone: MasterWashroom, floor 2, internal, 6 devices
  MotionSensor: SF_MasterWashroom_LightSwitch_EntranceMotionSensor, lastOnTimestamp: -1
  ChromeCastAudioSink: Chromecast-SF_MasterWashroom_ChromeCast-chromecast:audio:masterBathroom
  IlluminanceSensor: SF_MasterWashroom_LightSwitch_Illuminance
  Light: SF_MasterWashroom_LightSwitch, SF_MasterWashroom_LightSwitch_Timer, illuminance: 8
  MotionSensor: SF_MasterWashroom_FanSwitch_MotionSensor, lastOnTimestamp: -1
  AstroSensor: VT_Time_Of_Day

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones

  Neighbor: 2_Lobby, 2
Zone: MainWashroom, floor 2, internal, 3 devices
  MotionSensor: SF_MainWashroom_FanSwitch_MotionSensor, lastOnTimestamp: -1
  Light: SF_MainWashroom_LightSwitch, SF_MainWashroom_LightSwitch_Timer, illuminance: 8
  AstroSensor: VT_Time_Of_Day

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones

  Neighbor: 2_Lobby, 2
Zone: Lobby, floor 2, internal, 3 devices
  Dimmer: SF_Lobby_LightSwitch, SF_Lobby_LightSwitch_Timer, illuminance: 8, dimLevel: 2, timeRanges: 20-8
  MotionSensor: SF_Lobby_LightSwitch_MotionSensor, lastOnTimestamp: -1
  AstroSensor: VT_Time_Of_Day

  Action: 1 -> TurnOnSwitch
  Action: 2 -> TurnOffAdjacentZones

  Neighbor: 1_Foyer, 2

Interesting approach - I am going to look deeper later.

Here is what I have done to manager “areas” for automated lighting and occupancy…