Motion detected, light on, turn off after 3 minutes but only between 8pm-5am

Gotcha. It’ll amount to the same thing for me, as I’m not planning to learn another coding language. :wink:

rule "Light on for 3 minutes on motion detected" do
  changed Stairway_Motion, to: ON
  between "20:00".."05:00"
  run { Salt_Lamp.on for: 3.minutes }
end

This won’t set multiple timers when triggered again before the previous timer ended

Sure, but that wasn’t one of the requirements! :wink:

Thanks for the :for bit!

The other languages will however allow you to abstract stuff into separate methods that you can just call.

As an example I have a normal_schedule? method that returns true unless we are on vacation, the kids are sick, nobody is home, it’s a public or a school holiday and so on. Makes it super easy to write very terse rules.

All valid points although I must admit I’m not sold on the idea of rule templates. It is (to me) much simpler to just write the things I want especially since the jruby rules can be made so terse.

And having code that writes rules is very nice:

[
  { text: "Monday", cron: "0 25 15 ? * Mon *" },
  { text: "Friday", cron: "0 0 15 ? * Fri *" },
  { text: "late", cron: "0 15 16 ? * Tue,Wed,Thu *" },
].each do |e|
  rule "Boys, #{e[:text]} fetch from school" do
    cron e[:cron]
    only_if { normal_schedule? }
    run { announce FETCH.sample }
  end
end

I fully understand that there might be cases where blockly makes sense, but I guess the question I should have asked is if anyone with a development background still uses that.

Yes, I do use and love blockly.

We’re going slightly off-topic now, for which I apologize, but I’d be super curious to hear what your use-cases are for that and if you are also using any of the other rule engines. If so, which tasks you prefer to handle in blockly and which through the various DSLs.

By blindly starting a new timer without cancelling the previous timers, you can get in the situation where the light turns off “randomly” because the previous timer(s) that are still active just fired, even though your latest timer still has a few more minutes to go.

The simple Salt_Lamp.on for: 3.minutes takes care of that. (sorry there shouldn’t be a comma between .on and for)

Only some leftovers from old rules DSL cause I did not find time to move them to blockly. No other rules language in use here.

So you don’t use libraries? Because that would be kind of the same argument against that. All a rule template is is code you can use that you didn’t have to write.

Rule templates can be written in any language (mine are all Rules DSL, ECMAScript 5.1 and ECMAScript 2021). Same for UI rules for that matter. You can even use jRuby or a combo of languages in the same rule.

Blockly lets you create libraries too. There’s a section for them under Developer Tools. But you’ll need to know ECMAScript 5.1 to do so because that what your blocks will compile to.

And just in case it’s not clear, you can call and use personal libraries of classes and functions from UI created rules too.

I’ve coded professionally since graduating college in 1999 (less so now that I’m pushed into more managerial roles, but use C, C++, Java, and most recently Python) and I use and like Blockly for some things. But most of my UI rules are written in ECMAScript 2021 because it has the best support for writing script actions and conditions in the UI.

Often when there’s something really simple and straight forward that I don’t know off the top of my head how to do in JS Scripting and I’m too lazy to look it up. Or if I want to have a rule that I want my nine-year-old son to play with and modify I’ll code it up in Blockly.

I mostly use ECMAScript 2021 (i.e. JS Scripting). I would have used jRuby but the developers were slow to provide support for UI rules (I still think there isn’t the concept of cache to share variables between rules) and you still have to get the helper library through gems instead of coming with the add-on and the docs are maintained somewhere else instead of incorporated into the official docs. But sometimes something will be easier in Rules DSL or Blockly and I’ll do that in those cases. I’ve still some ECMAScript 5.1 hanging around but I’ve not had time to upgrade them.

A post was split to a new topic: jRuby Discussion

Out of respect for the original poster, who has not indicated that their problem is solved, this discussion should probably continue in a new thread. :wink:

1 Like

Getting back to the @aworldofchaos’s original question, here’s an updated rule in jruby that takes into account the sunset / sunrise time. I use Xiaomi Aqara PIR motion sensors, and it has a built in light sensor too. The second example takes it into account as well.

rule "Light on for 3 minutes on motion detected" do
  changed Stairway_Motion, to: ON
  between "4pm".."7am"                                            # restrict to a time range
  only_if { things["astro:sun:home"].getElevation(nil).to_f < 5 } # only from a bit before sunset, until a bit after sunrise
  run { Salt_Lamp.on for: 3.minutes }
end

This assumes you have configured the Astro binding and have a Thing called astro:sun:home configured with your latitude/longitude.

Making it generic

If you’ve set up your Semantic model so that Motion Sensors and Lights belong in their corresponding Rooms, then you can make one rule handle all of them.

Furthermore if you have Lux sensors in (some of) the rooms, you can incorporate that into the rule. Rooms that have no lux sensors will simply not take lux into consideration.

LUX_THRESHOLD = 300 | "lx" # adjust accordingly

rule "Light on for 3 minutes on motion detected" do
  changed gSensors_for_Motion_Activated_Lights.members, to: ON # add any motion sensors you'd like into this group
  between "4pm".."7am" # restrict to a time range
  only_if { dusk_to_dawn? } # only from a bit before sunset, until a bit after sunrise
  run do |event|
    lux = avg_lux(event.item.location)
    next unless lux.nil? || lux < LUX_THRESHOLD

    event.item
         .location
         .equipments(Semantics::Lightbulb) # select all the Lightbulbs in the room
         .members
         .points(Semantics::Control, Semantics::Power)
         .ensure
         .on for: 3.minutes
  end
end

def dusk_to_dawn?
  things["astro:sun:home"].getElevation(nil).to_f < 5
end

# Returns the average lux in the room (if there are multiple lux sensors in the same room)
# returns nil if there are no lux sensors in the room
def avg_lux(location)
  lux_sensors = location.equipments.members.points(Semantics::Measurement, Semantics::Light)
  return lux_sensors.sum(&:state) / lux_sensors.count unless lux_sensors.empty?
end

From here on, you just need to add the motion sensors that you want to trigger lights in the same Location into the gSensors_for_Motion_Activated_Lights. This is a separate group, in case you don’t want all your motion sensors to trigger lights in the room.

You can customise it further, say you want 3 minutes for room A and 30 minutes for room B, and 1 hour for room C, etc.

You can make the lux threshold configurable for each room, if you like.

Easy to do but I don’t want to make this post too long.

1 Like

Rich and Hans-Jörg, I cannot thank you enough for the detailed responses!

I come for the software and stay for the community!

I guess you did not really mean me. :wink:

Yeah. I did in fact mean Rich and Jim ;-), but I still very much appreciate all the work you have put into OH, so thank you for that!

1 Like