[SOLVED] First stab at Jython

I can post the rules now but do not have access to the Items text files remotely. They are pretty basic anyway.

BasementTimer is a 2 minute expiry timer.

rule "Basement Light On"
when
    Item basement_motion_1 changed from OFF to ON or Item basement_motion_2 changed from OFF to ON
then
    basement_light.sendCommand(ON)
    // start or reset Timer
    BasementTimer.sendCommand(ON)
end

rule "Basement Light Off"
when
    Item BasementTimer changed from ON to OFF
then
    basement_light.sendCommand(OFF)
end

Now the messy ones.

rule "Plug_Sunset"
when
    Channel "astro:sun:local:set#event" triggered START 
then
    christmas_lights.sendCommand(ON)
end

rule "Plug_Night"
when
    Time cron "0 30 21 ? * *"
then
    christmas_lights.sendCommand(OFF)
end

rule "Plug_Morning"
when
    Time cron "0 45 05 ? * *"
then
    christmas_lights.sendCommand(ON)
end

rule "Plug_Sunrise"
when
    Channel "astro:sun:local:rise#event" triggered START
then
    christmas_lights.sendCommand(OFF)
end

A straight forward port of these Rules is pretty simple.

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

@rule("Papillon Motion", description="turn on the light for 5 minutes on motion but only at night.")
@when("Item papillon_motion changed")
def papillon_motion(event):
    if items["IsItDark"] == ON:
        events.sendCommand("papillon_light", "ON")
        ScriptExecution.createTimer(DateTime.now().plusMinutes(5), 
                                    lambda: events.sendCommand("papillon_light", "OFF"))

I just typed in the above, there may be errors. The code for stuff like this, as you can see, is pretty easy. But the advantage of learning how to use something like Scott’s Mode library is that when you decided to get more complicated, it’s much less work. A little bit of work up front can save more work later on.

One of the things that we will be pushing for in the near future is a change in OH culture to move away from needing to copy/paste/edit and understand code examples to building up complicated Rules using libraries containing code you never have to touch, just use. That’s why I’ve submitted a bunch of PRs for basic building blocks and some simple things like Gatekeeper, Timer Manager, and Generic Presence Detection.

If those are the messy ones we have a very different definition of “messy.” :wink:

I meant messy Newbie code. not difficult rules.
Actually, 1 & 3 were translated from my Home Assistant daze ;).

It works. I do not understand the lambda concept though.
Does the timer get refreshed on every motion change like my old rule?

The script gives this error when executed.

NameError: global name 'IsItDark' is not defined

Know how you see [ | ] in rules DSL a lot? Usually when creating Timers or in forEach statements? Those are lambdas, a function that you can pass to another function where it gets called.

In this case you are creating a lambda that calls events.sendCommand. if you have more than one thing you need to do when the timer expires, you will need to create a new function and create a lambda that calls your function.

Now that I look at it, you need to save the Timer to a going variable and reschedule it if it already exists.

1 Like

Unfortunately, no :frowning_face:

Oops… forgot the quotes…

events.sendCommand("IsItDark", “ON” if “set” in str(event.channel).split(":") else “OFF”)

I didn’t see your Items, so I made some guesses. This should be everything you’d need to setup area triggers and actions. Grab the latest Mode (Time of Day), since I fixed a bug related to the use of Channels.

In your Item file(s) add the following…

Group                          gArea_Trigger            "Area Triggers"                <presence>
    Group:Switch:OR(ON,OFF)        gPapillon_Trigger        "Papillon Trigger [%s]"        <presence>    (gArea_Trigger)
    Group:Switch:OR(ON,OFF)        gBasement_Trigger        "Basement Trigger [%s]"        <presence>    (gArea_Trigger)
    Group:Switch:OR(ON,OFF)        gOutside_Trigger         "Outside Trigger [%s]"         <presence>    (gArea_Trigger)
Group	                       gArea_Action             "Area Actions" 	               <presence>
    Group                          gPapillon_Action         "Papillon Action"              <presence>    (gArea_Action)
    Group                          gBasement_Action         "Basement Action"              <presence>    (gArea_Action)
    Group                          gOutside_Action          "Outside Action"               <presence>    (gArea_Action)

Switch    papillon_motion      "Papillon Motion [%s]"      <motion>	   (gPapillon_Trigger)    {channel="zwave:device:55555:node2:sensor_binary"}
Switch    papillon_light       "Papillon Light [%s]"       <switch>	   (gPapillon_Action)     {channel="zwave:device:55555:node3:switch_binary"}

Switch    basement_motion_1    "Basement Motion 1 [%s]"    <motion>	   (gBasement_Trigger)    {channel="zwave:device:55555:node4:sensor_binary"}
Switch    basement_motion_2    "Basement Motion 2 [%s]"    <motion>	   (gBasement_Trigger)    {channel="zwave:device:55555:node5:sensor_binary"}
Switch    basement_light       "Basement Light [%s]"       <switch>	   (gBasement_Action)     {channel="zwave:device:55555:node6:switch_binary"}

Switch    outside_trigger      "Outside Trigger [%s]"      <switch>	   (gOutside_Trigger)
Switch    christmas_lights     "Christmas Lights [%s]"     <switch>	   (gOutside_Action)     {channel="zwave:device:55555:node7:switch_binary"}

Create an /automation/jsr223/python/personal/metadata.py script and add the following to it. You can let this run after every startup, but it’s a little cleaner to just comment everything out.

from core.metadata import set_metadata

set_metadata("papillon_light", "area_triggers_and_actions", {
        "modes": {
            "Morning": {"brightness": 98},
            "Day":     {"brightness": 0},# if there is motion during Day mode, the light will not turn on
            "Evening": {"brightness": 98},
            "Night":   {"brightness": 98}
        },
        "actions": {
            "light_action": {"OFF": {"delay": 300}}# the turning off of the light will be delayed by 5 minutes
        }
    }, overwrite=True)

set_metadata("basement_light", "area_triggers_and_actions", {# there is no modes metadata, so the default values in configuration.area_triggers_and_actions_dict will be used
        "actions": {
            "light_action": {"OFF": {"delay": 120}}
        }
    }, overwrite=True)

set_metadata("christmas_lights", "area_triggers_and_actions", {
        "modes": {
            "Morning": {"brightness": 98},
            "Day":     {"brightness": 0},
            "Evening": {"brightness": 98}
            "Night":   {"brightness": 0}
        }
    }, overwrite=True)

Use these In configuration.mode_dict…

mode_dict = OrderedDict([
    ("Morning", {"hour": 5,  "minute": 45,  "second": 0}),
    ("Day",     {"channel": "astro:sun:local:rise#event", "event": "START"}),
    ("Evening", {"channel": "astro:sun:local:set#event", "event": "START"}),
    ("Night"  , {"hour": 21, "minute": 30,  "second": 0})
])

You will need to turn on/off your outside_light Item to trigger the Christmas lights, so you might want to add it to a sitemap.

3 Likes

I just got to work this morning. I will look at this later.

Pull up just about any Design Pattern post. Or search the forum for “createTimer” or “forEach”. Also look at Reusable Functions: A simple lambda example with copious notes.

1 Like

OK, at sunset it just switched OFF :frowning:

2019-10-22 18:30:00.003 [vent.ChannelTriggeredEvent] - astro:sun:local:set#event triggered START

2019-10-22 18:30:00.004 [.event.RuleStatusInfoEvent] - 77bc6078-84c0-4bb9-8ce8-b6b2443bb0e6 updated: RUNNING

2019-10-22 18:30:00.010 [ome.event.ItemCommandEvent] - Item 'IsItDark' received command OFF

2019-10-22 18:30:00.011 [.event.RuleStatusInfoEvent] - 77bc6078-84c0-4bb9-8ce8-b6b2443bb0e6 updated: IDLE
1 Like

Just setting it to ON would not work?

In case you are wondering, the times for Morning & Night were determined by a little trial & error and would vary by location.Morning is always before Sunrise in my location & Night is always after Sunset…

Yeah… I was meaning you’d need to manually toggle the switch rather than an automation controlling it.

Did you try out area triggers?!

I am configuring them ( and found a missing comma too.) .
Mode has come up as NIGHT instead of EVENING. I figure it might sort itself out since I started it after sunset but before the NIGHT time.

So far, I am configuring the system without many of the channel links until I get the automation working. I will then move the devices to this new controller.

Instead of

Switch    outside_trigger      "Outside Trigger [%s]"      <switch>	   (gOutside_Trigger)

Wouldn’t this work?

Group:Switch:EQUALITY(ON)  outside_trigger      "Outside Trigger [%s]"      <switch>	   (gOutside_Trigger)

Maybe as a piece, but on it’s own, no. You’d need an Item in the group or you’d need to send the group an update. But why?

Wouldn’t it be always on?

Depends on the members… but if you create members, then just use the Item

I misunderstand EQUALITY then.

I like the Mode idea for Time of Day.

My area trigger does not seem to trigger the action. I do not yet hav channel bindings, but when I trigger the Papillon motion ( in the correct mode) it does not turn on the light switch. I see in the logs it changes the group trigger though.
This is in Morning, for instance. All I see in the log o:

2019-10-23 06:26:08.656 [vent.ItemStateChangedEvent] - papillon_motion changed from OFF to ON

2019-10-23 06:26:08.656 [GroupItemStateChangedEvent] - gPapillon_Trigger changed from OFF to ON through papillon_motion

I will need to troubleshoot more after work.