A More Clever System for "Locking" Lights?

If you got this working after only a couple of weeks with OH then you are doing great! This is an alternative way of implementing Design Pattern: Manual Trigger Detection using proxy Items that I haven’t written up yet.

From looking at your rules I’v ea couple of recommendations mostly having to do with style:

    then
        LOCK_LTS_GF_OFFC.sendCommand(receivedCommand)
    end

OK, with that out of the way I suspect you have these rules repeated over and over again, once for each room with a motion sensor. You might be able to make these Rule generic using triggeringItem, Design Pattern: Associated Items, and Design Pattern: Working with Groups in Rules.

For example, here is how I detect a manual command to a light and set an override flag (I use timestamps instead of a proxy Item).

// Theory of operation: any change in the relevant lights that occur more than five seconds after
// the change to DAY or after a change caused by cloudy is an override
rule "Watch for overrides"
when
  Item aFrontLamp changed or
        Item aFamilyLamp changed
then
  // wait a minute before reacting after vTimeOfDay changes, ignore all other times of day
  if(vTimeOfDay.state != "DAY" || vTimeOfDay.lastUpdate("mapdb").isAfter(now.minusMinutes(1).millis)) return;

  // Assume any change to a light that occurs more than n seconds after time of day or cloudy is a manual override
  val n = 5
  val causedByClouds = vIsCloudy.lastUpdate("mapdb").isAfter(now.minusSeconds(n).millis)
  val causedByTime = vTimeOfDay.lastUpdate("mapdb").isAfter(now.minusSeconds(n).millis)

  if(!causedByClouds && !causedByTime) {
    logInfo(logName, "Manual light trigger detected, overriding cloudy control for " + triggeringItem.name)
    postUpdate(triggeringItem.name+"_Override", "ON")
  }
end

// Thoery of operation: If it is day time, turn on/off the weather lights when cloudy conditions
// change. Trigger the rule when it first becomes day so we can apply cloudy to lights then as well.
rule "Turn on lights when it is cloudy"
when
  Item vIsCloudy changed or
  Item vTimeOfDay changed
then
  // We only care about daytime
  if(vTimeOfDay.state != "DAY") return;

  // give the time of day time to complete
  if(triggeringItem.name == "vTimeOfDay") Thread::sleep(1000)

  logInfo(logName, "It is DAY and cloudy changed: " + vIsCloudy.state.toString)

  // Apply the cloudy state to all the lights in the weather group
  gLights_ON_WEATHER.members.forEach[ l |

          val overrideName = l.name+"_Override"
          val override = gLights_WEATHER_OVERRIDE.members.findFirst[ o | o.name == overrideName ] as SwitchItem

          if(override.state != ON && l.state != vIsCloudy.state) l.sendCommand(vIsCloudy.state as OnOffType)

          logInfo(logName, l.name + if(override.state == ON) " is overridden" else " is not overridden")
  ]
end

So for you the rule could look something like the following:

rule "Manual turn on light"
when
    Item ALEXA_LTS_GF_OFFC received command or
    Item ALEXA_blah_blah_blah received command or
    ...
then
    sendCommand(triggeringItem.name.replace("ALEXA", "LOCK"), receivedCommand)
end

Wow that’s satisfying. A one liner that handles ALL of your locking of the lights and all you have to do is add triggers (and keep up this naming convention). Note this is one of the few cases where using the sendCommand Action is appropriate.

You could do the same for the motion sensor rule.

rule "Motion Sensors"
when
    Item MS_GF_OFFC changed or
    Item MS_blah_blah changed or
    ...
then
    val lock = LightLocks.members.findFirst[ l | l.name == triggeringItem.name.replace("MS", "LOCK_LTS") ]

    // only send a command if the lock isn't ON
    if(lock.state != ON){
        sendCommand(triggeringItem.name.replace("MS", "LTS"), triggeringItem.state)
    }
end

Note that I’m not certain this will work for you as there are some different Items that you are using in your example Rule that do not follow the naming pattern. You would also need to put all your locks into a Group so you can get access to them using the findFirst trick.

I leave it to you to determine if this approach will work for you.

1 Like