Automation #2: Bathroom Smart light

Hello guys!

For this second automation, I have a selected one that uses timers to determine the absence of movement (using a motion sensor) in a particular room. Like in my previous post I will share the implementation in the Rules DSL and Jython of this automation.

This solution is not final or complete but can help new members to start with OpenHAB (at least that’s my objective :slightly_smiling_face:). It is based on all feedback and solutions shared by members of the community to my questions in this forum.

Automation #2 Bathroom Smart Light
Turn on the light of the bathroom if a movement is detected and the ambient light is less than 40 lux.
Turn off light of the bathroom after two minutes without detecting any movement.

Items

Switch  Bathroom_Lamp  { channel="mqtt:topic:MyBroker:Home:Light2"}
Switch  Bathroom_Motion    { channel="mqtt:topic:MyBroker:Home:Motion3"}
Number  Bathroom_AmbientLight   { channel="mqtt:topic:MyBroker:Home:AmbientLight2"}
Switch  Bathroom_MotionTimer    { expire="2m,command=OFF" }

Rules DSL Implementation

rule "(DSL) Bathroom MotionSensor changed"
when
    Item Bathroom_Motion received update ON
then
    val ambientLight= (Bathroom_AmbientLight.state as DecimalType).intValue()
    if(ambientLight <= 40 && Bathroom_Lamp.state == OFF ) {
        // Turning ON Bathroom Lamp
        Bathroom_Lamp.sendCommand(ON) 
        // Start/Reset timer
        Bathroom_MotionTimer.sendCommand(ON) 
    } else if( Bathroom_Lamp.state == ON ) {
        Bathroom_MotionTimer.sendCommand(ON) 
    } 
end    

rule "Timer expired for Bathroom Motion Sensor"
when
    Item Bathroom_MotionTimer received command OFF
then
    // Timer expired, turning off the light 
    Bathroom_Lamp.sendCommand(OFF) 
end

Jython implementation

from core.rules import rule
from core.triggers import when
from core.actions import ScriptExecution as SE
from org.joda.time import DateTime as DT
import core

timer = None

def set_timer():
    global timer
    if timer is None or timer.hasTerminated():
        #Start new timer
        timer = SE.createTimer(DT.now().plusMinutes(2), lambda: events.sendCommand("Bathroom_Lamp", "OFF"))
    else:
        #Reschedule timer
        timer.reschedule(DT.now().plusMinutes(2))

@rule("(Py) Bathroom MotionSensor changed")
@when("Item Bathroom_Motion received update ON")
def bathroom_motion(event):
    if items.Bathroom_AmbientLight.intValue() <= 40 and items.Bathroom_Lamp == OFF:
        # Turning ON Bathroom Lamp
        events.sendCommand("Bathroom_Lamp", "ON")
        set_timer()
    elif items.Bathroom_Lamp == ON:
        # Light already ON, reset timer
        set_timer()

In the Jython solution, I use a helper function called set_timer to start or restart the timer that will turn OFF the lamp after two minutes without no motion detected.

Happy coding! :slightly_smiling_face:

Humberto

Note:

  • In the Jython solution I’m using the deprecated Joda library. I’m using Joda’s DateTime instead of Java’s ZonedDateTime because of the ScriptExecution.createTimer requires as first argument an org.joda.time.base.AbstractInstant. Maybe, I can update this code and use plain Python timers.
  • Suggestions or recommendations to improve the implementation are welcome!
  • Do you have more complex automations that shares the same logic of this example? Please share it :slightly_smiling_face:

Other automation examples

Related Posts

6 Likes

Check out the conversion functions in core.date to convert the ZonedDateTime to joda.DateTime. Just to clarify, Joda is probably not going away tomorrow, it’s not “wrong” to use it. When openHAB moves to a newer Java version, which there is no timeline for or talk of that I have seen yet, Joda will be depreciated but not gone.

Check this out for Python timers

1 Like

Hi @CrazyIvan359 thanks again for your feedback

I see your point, don’t worry. I’m a bit sleepy :sleeping: now, but I will check your suggestion tomorrow :+1:

There was a PR a year or so ago to replace Joda with the built in Java equivalent. Turned out that it wasn’t completely a drop in and it broke a lot of user’s Rules.

It is my understanding that moving to Java 11 (which was the target version last time it was discussed on the forum, the conversation on GitHub may have progressed) was primarily held up by up stream libraries that OH depends upon. I believe the Xtext/Xtend libraries were mentioned as being one of the culprits.

So as long as we are stuck with these up stream libraries we are stuck until those get replaced or upgraded to work.

I believe OH 3 is the target for when we will be able to upgrade.

1 Like

Hi @CrazyIvan359

Have you experienced this error while you convert a ZonedDateTime to DateTime (Joda)

Well if we push the use of java.time in all of the docs and forum posts we can, hopefully the impact will be lessened. With your posts and Scott’s work on the Helper Libraries and related, the use of the new rule engine is sure to grow.

Not completely drop-in, no, but most of the common methods are available so a transition wouldn’t be particularly involved. Plus if the user is changing over to Jython their are functions in the Date module that may help the process.

Hi guys!

This a version the Automation #2 using plain Jython Timers

from core.rules import rule
from core.triggers import when
from java.lang.Thread import State
from threading import Timer
import core

timer = None

def start_timer():
    global timer
    timer = Timer(5, lambda: events.sendCommand("Bathroom_Lamp", "OFF"))
    timer.start()

def set_timer():
    global timer
    if timer is None or timer.getState() == State.TERMINATED:
        #Start new timer
        start_timer()
    else:
        #Reschedule timer
        timer.stop()
        start_timer()

@rule("(Py) Bathroom MotionSensor changed")
@when("Item Bathroom_Motion received update ON")
def bathroom_motion(event):
    if items.Bathroom_AmbientLight.intValue() <= 40 and items.Bathroom_Lamp == OFF:
        # Turning ON Bathroom Lamp
        events.sendCommand("Bathroom_Lamp", "ON")
        set_timer()
    elif items.Bathroom_Lamp == ON:
        # Light already ON, reset timer
        set_timer()

Only the helper function set_timer was changed. I also created another helper function start_timer to initialize the timer every time is needed.

Happy coding! :slightly_smiling_face:

Heyo @rhumbertgz
I’m trying to do the same with a Fibaro Motion Sensor.
What’s the thing with these mqtt stuff? Is this just some other kind of sensor? (sorry for my noobish question).
Why you have an if and else function?
I would like to know what every piece of code is doing so I would understand more of it.

Thx

@Koensk

Yes, instead of use Zigbee or Zwave for communication MQTT-based sensors use Wi-Fi

The condition of the IF checks if the room is dark (<= 40) and the the Light is OFF, in that case the code turns the Light ON and start a timer.

The else part is executed when you still are in the room (multiple events on from motion sensor) and the light is ON, in that case you want to restart the timer, because you want to turn off the light after a period of inactivity in the room

1 Like