Class: OpenHAB::DSL::Rules::BuilderDSL

Inherits:
Object
  • Object
show all
Includes:
Core::EntityLookup, OpenHAB::DSL
Defined in:
lib/openhab/dsl/rules/builder.rb

Overview

Rule configuration for openHAB Rules engine

Constant Summary

Constants included from OpenHAB::DSL

VERSION

Execution Blocks collapse

Configuration collapse

Guards collapse

Guards exist to only permit rules to run if certain conditions are satisfied. Think of these as declarative if statements that keep the run block free of conditional logic, although you can of course still use conditional logic in run blocks if you prefer.

#Guard Combination

Multiple guards can be used on the same rule. All must be satisfied for a rule to execute.

Examples:

rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is OFF and Door is CLOSED" do
  changed LightSwitch, to: ON
  run { OutsideDimmer << 50 }
  only_if { Door.closed? }
  not_if { OtherSwitch.on? }
end

Triggers collapse

Note:

The trigger attachment feature is not available for UI rules.

Triggers specify what will cause the execution blocks to run. Multiple triggers can be defined within the same rule.

#Trigger Attachments

All triggers support event attachments that enable the association of an object to a trigger. This enables one to use the same rule and take different actions if the trigger is different. The attached object is passed to the execution block through the OpenHAB::Core::Events::AbstractEvent#attachment accessor.

Examples:

rule 'Set Dark switch at sunrise and sunset' do
  channel 'astro:sun:home:rise#event', attach: OFF
  channel 'astro:sun:home:set#event', attach: ON
  run { |event| Dark << event.attachment }
end

Instance Method Summary collapse

Methods included from OpenHAB::DSL

after, between, config_description, debounce_for, ensure_states, ensure_states!, holiday_file, holiday_file!, items, only_every, persistence, persistence!, profile, provider, provider!, rule, rule!, rules, scene, scene!, script, script!, shared_cache, sitemaps, store_states, things, throttle_for, timers, transform, unit, unit!

Methods included from Core::ScriptHandling

script_loaded, script_unloaded

Methods included from Core::Actions

notify

Methods included from Core::EntityLookup

#method_missing

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class OpenHAB::DSL

Instance Method Details

#at(item) ⇒ void

This method returns an undefined value.

Creates a trigger based on the date and time stored in a DateTimeItem

The trigger will dynamically update whenever the state of the item changes. If the item is NULL or UNDEF, the trigger will not run.

To trigger just on the time portion of the item, use #every instead, e.g. every :day, at: MyDateTimeItem.

Examples:

rule "say hello when the kids get home from school" do
  at HomeFromSchool_Time
  run do
    KitchenEcho_TTS << "hi kids! how was school?"
  end
end

rule "set home from school time" do
  on_load
  every :day, at: "5:00am" do
  run do
    HomeFromSchool_Time.ensure.update(school_day? ? LocalTime.parse("3:30pm") : NULL)
  end
end

Parameters:

  • item (Item, String, Symbol)

    The item (or its name)



1726
1727
1728
1729
# File 'lib/openhab/dsl/rules/builder.rb', line 1726

def at(item)
  item = item.name if item.is_a?(Item)
  trigger("timer.DateTimeTrigger", itemName: item.to_s)
end

#between(range) ⇒ void

This method returns an undefined value.

Only execute rule if the current time is between the supplied time ranges.

If the range is of strings, it will be parsed to an appropriate time class.

Examples:

rule "Between guard" do
  changed MotionSensor, to: OPEN
  between "6:05".."14:05:05" # Include end
  run { Light.on }
end
rule "Between guard" do
  changed MotionSensor, to: OPEN
  between "6:05".."14:05:05" # Excludes end second
  run { Light.on }
end
rule "Between guard" do
  changed MotionSensor, to: OPEN
  between LocalTime.of(6, 5)..LocalTime.of(14, 15, 5)
  run { Light.on }
end

String of LocalTime

rule 'Log an entry if started between 3:30:04 and midnight using strings' do
  on_load
  run { logger.info ("Started at #{LocalTime.now}")}
  between '3:30:04'..LocalTime::MIDNIGHT
end

rule 'Log an entry if started between 3:30:04 and midnight using LocalTime objects' do
  on_load
  run { logger.info ("Started at #{LocalTime.now}")}
  between LocalTime.of(3, 30, 4)..LocalTime::MIDNIGHT
end

String of MonthDay

rule 'Log an entry if started between March 9th and April 10ths' do
  on_load
  run { logger.info ("Started at #{Time.now}")}
  between '03-09'..'04-10'
end

rule 'Log an entry if started between March 9th and April 10ths' do
  on_load
  run { logger.info ("Started at #{Time.now}")}
  between MonthDay.of(03,09)..'04-06'
end

Parameters:



609
# File 'lib/openhab/dsl/rules/builder.rb', line 609

prop :between

#changed(*items, to: nil, from: nil, for: nil, attach: nil) ⇒ void

This method returns an undefined value.

Creates a trigger when an item, member of a group, or a thing changed states.

When the changed element is a Thing, the from and to values will accept symbols and strings, where the symbol' matches the supported status.

The event passed to run blocks will be an Core::Events::ItemStateChangedEvent or a Core::Events::ThingStatusInfoChangedEvent depending on if the triggering element was an item or a thing.

Examples:

Single item trigger

rule "Execute rule when a sensor changed" do
  changed FrontMotion_Sensor
  run { |event| logger.info("Motion detected by #{event.item.name}") }
end

Multiple items can be separated with a comma:

rule "Execute rule when either sensor changed" do
  changed FrontMotion_Sensor, RearMotion_Sensor
  run { |event| logger.info("Motion detected by #{event.item.name}") }
end

Group member trigger

rule "Execute rule when member changed" do
  changed Sensors.members
  run { |event| logger.info("Motion detected by #{event.item.name}") }
end

You can optionally provide from and/or to states to restrict the cases in which the rule executes:

rule "Execute rule when item is changed to specific number, from specific number" do
  changed Alarm_Mode, from: 8, to: [14,12]
  run { logger.info("Alarm Mode Updated") }
end

Works with ranges:

rule "Execute when item changed to a range of numbers, from a range of numbers" do
  changed Alarm_Mode, from: 8..10, to: 12..14
  run { logger.info("Alarm Mode Updated") }
end

Works with endless ranges:

rule "Execute rule when item is changed to any number greater than 12"
  changed Alarm_Mode, to: (12..)   # Parenthesis required for endless ranges
  run { logger.info("Alarm Mode Updated") }
end

Works with procs:

rule "Execute when item state is changed from an odd number, to an even number" do
  changed Alarm_Mode, from: proc { |from| from.odd? }, to: proc {|to| to.even? }
  run { logger.info("Alarm Mode Updated") }
end

Works with lambdas:

rule "Execute when item state is changed from an odd number, to an even number" do
  changed Alarm_Mode, from: -> from { from.odd? }, to: -> to { to.even? }
  run { logger.info("Alarm Mode Updated") }
end

Works with regexes:

rule "Execute when item state is changed to something matching a regex" do
  changed Alarm_Mode, to: /armed/
  run { logger.info("Alarm armed") }
end

Delay the trigger until the item has been in the same state for 10 seconds

rule "Execute rule when item is changed for specified duration" do
  changed Closet_Door, to: CLOSED, for: 10.seconds
  run do
    Closet_Light.off
  end
end

for parameter can be a proc that returns a duration:

Alarm_Delay << 20

rule "Execute rule when item is changed for specified duration" do
  changed Alarm_Mode, for: -> { Alarm_Delay.state.to_i.seconds }
  run { logger.info("Alarm Mode Updated") }
end

Works with Things:

rule "Execute rule when thing is changed" do
  changed things["astro:sun:home"], :from => :online, :to => :uninitialized
  run { |event| logger.info("Thing #{event.uid} status <trigger> to #{event.status}") }
end

Real World Example

rule "Log (or notify) when an exterior door is left open for more than 5 minutes" do
  changed ExteriorDoors.members, to: OPEN, for: 5.minutes
  triggered {|door| logger.info("#{door.name} has been left open!") }
end

Parameters:

  • items (Item, GroupItem::Members, Thing)

    Objects to create trigger for.

  • from (State, Array<State>, #===, nil) (defaults to: nil)

    Only execute rule if previous state matches from state(s).

  • to (State, Array<State>, #===, nil) (defaults to: nil)

    Only execute rule if new state matches to state(s).

  • for (java.time.temporal.TemporalAmount, Proc, nil) (defaults to: nil)

    Duration for which the item/thing must remain in the same state before executing the execution blocks. When a proc is provided, it will be called when the rule is triggered to get the duration.

  • attach (Object, nil) (defaults to: nil)

    object to be attached to the trigger



1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
# File 'lib/openhab/dsl/rules/builder.rb', line 1138

def changed(*items, to: nil, from: nil, for: nil, attach: nil)
  changed = Changed.new(rule_triggers: @rule_triggers)
  # for is a reserved word in ruby, so use local_variable_get :for
  duration = binding.local_variable_get(:for)

  @ruby_triggers << [:changed, items, { to: to, from: from, duration: duration }]

  from = [nil] if from.nil?
  to = [nil] if to.nil?
  items.each do |item|
    case item
    when Core::Things::Thing,
         Core::Things::ThingUID,
         Core::Items::Item,
         Core::Items::GroupItem::Members
      nil
    else
      raise ArgumentError, "items must be an Item, GroupItem::Members, Thing, or ThingUID"
    end

    logger.trace { "Creating changed trigger for entity(#{item}), to(#{to.inspect}), from(#{from.inspect})" }

    Array.wrap(from).each do |from_state|
      Array.wrap(to).each do |to_state|
        changed.trigger(item: item, from: from_state, to: to_state, duration: duration, attach: attach)
      end
    end
  end
end

#channel(*channels, thing: nil, triggered: nil, attach: nil) ⇒ void

This method returns an undefined value.

Creates a channel trigger

The channel trigger executes rule when a specific channel is triggered. The syntax supports one or more channels with one or more triggers. thing is an optional parameter that makes it easier to set triggers on multiple channels on the same thing.

Examples:

rule "Execute rule when channel is triggered" do
  channel "astro:sun:home:rise#event"
  run { logger.info("Channel triggered") }
end
# The above is the same as each of the below

rule "Execute rule when channel is triggered" do
  channel "rise#event", thing: "astro:sun:home"
  run { logger.info("Channel triggered") }
end

rule "Execute rule when channel is triggered" do
  channel "rise#event", thing: things["astro:sun:home"]
  run { logger.info("Channel triggered") }
end

rule "Execute rule when channel is triggered" do
  channel "rise#event", thing: things["astro:sun:home"].uid
  run { logger.info("Channel triggered") }
end

rule "Execute rule when channel is triggered" do
  channel "rise#event", thing: ["astro:sun:home"]
  run { logger.info("Channel triggered") }
end

rule "Execute rule when channel is triggered" do
  channel things["astro:sun:home"].channels["rise#event"]
  run { logger.info("Channel triggered") }
end

rule "Execute rule when channel is triggered" do
  channel things["astro:sun:home"].channels["rise#event"].uid
  run { logger.info("Channel triggered") }
end
rule "Rule provides access to channel trigger events in run block" do
  channel "astro:sun:home:rise#event", triggered: 'START'
  run { |trigger| logger.info("Channel(#{trigger.channel}) triggered event: #{trigger.event}") }
end
rule "Keypad Code Received test" do
  channel "mqtt:homie300:mosquitto:backgate:keypad#code"
  run do |event|
    logger.info("Received keycode from #{event.channel.thing.uid.id}")
  end
end
rule "Rules support multiple channels" do
  channel "rise#event", "set#event", thing: "astro:sun:home"
  run { logger.info("Channel triggered") }
end
rule "Rules support multiple channels and triggers" do
  channel "rise#event", "set#event", thing: "astro:sun:home", triggered: ["START", "STOP"]
  run { logger.info("Channel triggered") }
end
rule "Rules support multiple things" do
  channel "keypad#code", thing: ["mqtt:homie300:keypad1", "mqtt:homie300:keypad2"]
  run { logger.info("Channel triggered") }
end

Parameters:

  • channels (String, Core::Things::Channel, Core::Things::ChannelUID)

    channels to create triggers for in form of 'binding_id:type_id:thing_id#channel_id' or 'channel_id' if thing is provided.

  • thing (String, Core::Things::Thing, Core::Things::ThingUID) (defaults to: nil)

    Thing(s) to create trigger for if not specified with the channel.

  • triggered (String, Array<String>) (defaults to: nil)

    Only execute rule if the event on the channel matches this/these event/events.

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



980
981
982
983
984
985
986
987
988
989
990
# File 'lib/openhab/dsl/rules/builder.rb', line 980

def channel(*channels, thing: nil, triggered: nil, attach: nil)
  channel_trigger = Channel.new(rule_triggers: @rule_triggers)
  flattened_channels = Channel.channels(channels: channels, thing: thing)
  triggers = [triggered].flatten
  @ruby_triggers << [:channel, flattened_channels, { triggers: triggers }]
  flattened_channels.each do |channel|
    triggers.each do |trigger|
      channel_trigger.trigger(channel: channel, trigger: trigger, attach: attach)
    end
  end
end

#channel_linked(attach: nil) ⇒ void

This method returns an undefined value.

Creates a channel linked trigger

Examples:

rule "channel linked" do
  channel_linked
  run do |event|
    logger.info("#{event.link.item.name} linked to #{event.link.channel_uid}.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1005
1006
1007
1008
# File 'lib/openhab/dsl/rules/builder.rb', line 1005

def channel_linked(attach: nil)
  @ruby_triggers << [:channel_linked]
  event("openhab/links/*/added", types: "ItemChannelLinkAddedEvent", attach: attach)
end

#channel_unlinked(attach: nil) ⇒ void

This method returns an undefined value.

Creates a channel unlinked trigger

Note that the item or the thing it's linked to may no longer exist, so if you try to access those objects they'll be nil.

Examples:

rule "channel unlinked" do
  channel_unlinked
  run do |event|
    logger.info("#{event.link.item_name} unlinked from #{event.link.channel_uid}.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1026
1027
1028
1029
# File 'lib/openhab/dsl/rules/builder.rb', line 1026

def channel_unlinked(attach: nil)
  @ruby_triggers << [:channel_linked]
  event("openhab/links/*/removed", types: "ItemChannelLinkRemovedEvent", attach: attach)
end

#cron(expression, attach: nil) ⇒ void #cron(second: nil, minute: nil, hour: nil, dom: nil, month: nil, dow: nil, year: nil, attach: nil) ⇒ void

This method returns an undefined value.

Create a cron trigger

Overloads:

  • #cron(expression, attach: nil) ⇒ void

    Examples:

    Using a cron expression

    rule "cron expression" do
      cron "43 46 13 ? * ?"
      run { Light.on }
    end

    Parameters:

  • #cron(second: nil, minute: nil, hour: nil, dom: nil, month: nil, dow: nil, year: nil, attach: nil) ⇒ void

    The trigger can be created by specifying each field as keyword arguments.

    When certain fields were omitted:

    • The more specific fields will default to 0 for hour, minute, and second, to MON for dow, and to 1 for dom and month.
    • The less specific fields will default to * or ? as appropriate.

    Each field is optional, but at least one must be specified.

    The same rules for the standard cron expression apply for each field. For example, multiple values can be separated with a comma within a string, and ranges can be specified with a dash or with a Ruby Range.

    Examples:

    Using String values

    # Run every 3 minutes on Monday to Friday
    # equivalent to the cron expression "0 */3 * ? * MON-FRI *"
    rule "Using cron fields" do
      cron minute: "*/3", dow: "MON-FRI"
      run { logger.info "Cron rule executed" }
    end

    Defaults for unspecified fields

    # Run at midnight on the first day of January, February, and March
    # equivalent to the cron expression "0 0 0 1 JAN-MAR ? *"
    rule "Using cron fields" do
      cron month: "JAN-MAR"
      run { logger.info "Cron rule executed" }
    end

    Using Ruby Range values

    # Run on the hour, every hour between 1pm and 5pm
    # equivalent to the cron expression "0 0 13-17 ? * ? *"
    rule "Using cron fields" do
      cron hour: 13..17
      run { logger.info "Cron rule executed" }
    end

    Parameters:

Raises:

  • (ArgumentError)


1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
# File 'lib/openhab/dsl/rules/builder.rb', line 1232

def cron(expression = nil, attach: nil, **fields)
  if fields.any?
    raise ArgumentError, "Cron elements cannot be used with a cron expression" if expression

    cron_expression = Cron.from_fields(fields)
    return cron(cron_expression, attach: attach)
  end

  raise ArgumentError, "Missing cron expression or elements" unless expression

  cron = Cron.new(rule_triggers: @rule_triggers)
  cron.trigger(config: { "cronExpression" => expression }, attach: attach)
end

#debounce_for(debounce_time) ⇒ void

This method returns an undefined value.

Waits until triggers have stopped firing for a period of time before executing the rule.

It ignores triggers that are "bouncing around" (rapidly firing) by ignoring them until they have quiesced (stopped triggering for a while).

#Comparison Table

Guard Triggers Immediately Description
#debounce_for No Waits until there is a minimum interval between triggers.
#throttle_for No Rate-limits the executions to a minimum interval, regardless of the interval between triggers. Waits until the end of the period before executing, ignores any leading triggers.
#only_every Yes Rate-limits the executions to a minimum interval. Immediately executes the first trigger, then ignores subsequent triggers for the period.

#Timing Diagram

The following timing diagram illustrates the difference between #debounce_for, #throttle_for, and #only_every guards:

TIME INDEX ===>                1    1    2    2    3    3    4    4
                     0    5    0    5    0    5    0    5    0    5
Triggers          : "X.X...X...X..XX.X.X....X.XXXXXXXXXXX....X....."
debounce_for 5    : "|......................X.|..............X....."
debounce_for 5..5 : "|....X|....X.|....X....|....X|....X|....X....X"
debounce_for 5..6 : "|.....X...|.....X.|....X.|.....X|.....X.|....X"
debounce_for 5..7 : "|......X..|......X|....X.|......X|......X....."
debounce_for 5..8 : "|.......X.|.......X....|.......X|.......X....."
debounce_for 5..20: "|...................X..|................X....."
# throttle_for will fire every 5 intervals after the "first" trigger
throttle_for 5    : "|....X|....X.|....X....|....X|....X|....X....."
only_every 5      : "X.....X......X....X....X....X....X......X....."

Triggers          : "X.X...X...X..XX.X.X..X...XXXXXXXXXXX.X..X.X..."
debounce_for 5..44: "|...........................................X."

# Notice above, triggers keep firing with intervals less than 5, so
# debouncer keeps waiting, but puts a stop at 44 (the end of range).

Examples:

Wait until item stopped changing for at least 1 minute before running the rule

rule do
  changed Item1
  debounce_for 1.minute
  run { ... }
end

Alert when door is open for a while

# Note: When combined with a state check (only_if), this becomes functionally
# equivalent to the changed duration feature.
rule "Door alert" do
  changed Door_State
  debounce_for 10.minutes
  only_if { Door_State.open? }
  run { notify("The Door has been open for 10 minutes!") }
end

Parameters:

  • debounce_time (Duration, Range)

    The minimum interval between two consecutive triggers before the rules are allowed to run.

    When specified just as a Duration or an endless range, it sets the minimum interval between two consecutive triggers before rules are executed. It will wait endlessly unless this condition is met or an end of range was specified.

    When the end of the range is specified, it sets the maximum amount of time to wait from the first trigger before the rule will execute, even when triggers continue to occur more frequently than the minimum interval.

    When an equal beginning and ending values are given, it will behave just like #throttle_for.

See Also:



750
751
752
753
# File 'lib/openhab/dsl/rules/builder.rb', line 750

def debounce_for(debounce_time)
  idle_time = debounce_time.is_a?(Range) ? debounce_time.begin : debounce_time
  debounce(for: debounce_time, idle_time: idle_time)
end

#delay(duration) ⇒ void

This method returns an undefined value.

Add a wait between or after run blocks.

The delay property is a non thread-blocking element that is executed after, before, or between run blocks.

Examples:

rule "delay execution" do
  changed MotionSensor, to: CLOSED
  delay 5.seconds
  run { Light.off }
end
rule 'Delay sleeps between execution elements' do
  on_load
  run { logger.info("Sleeping") }
  delay 5.seconds
  run { logger.info("Awake") }
end

Like other execution blocks, multiple can exist in a single rule.

rule 'Multiple delays can exist in a rule' do
  on_load
  run { logger.info("Sleeping") }
  delay 5.seconds
  run { logger.info("Sleeping Again") }
  delay 5.seconds
  run { logger.info("Awake") }
end

You can use Ruby code in your rule across multiple execution blocks like a run and a delay.

rule 'Dim a switch on system startup over 100 seconds' do
  on_load
  100.times do
    run { DimmerSwitch.dim }
    delay 1.second
  end
end

Parameters:



418
# File 'lib/openhab/dsl/rules/builder.rb', line 418

prop_array :delay, array_name: :run_queue, wrapper: Delay

#dependencies(trigger_types = %i[changed updated]) ⇒ Array<Item, GroupItem::Members>

Returns all Items (or GroupItem::Members) referenced by the specified trigger types in this rule.

Examples:

Ensure all dependencies have a state when executing a rule

rule do |rule|
  changed Item1, Item2, Item3
  only_if { rule.dependencies.all?(&:state?) }
  run { FormulaItem.update(Item3.state - (Item1.state + Item2.state)) }
end

Parameters:

  • trigger_types (Symbol, Array<Symbol>) (defaults to: %i[changed updated])

    Trigger types to search for dependencies

Returns:



520
521
522
523
524
525
526
527
528
# File 'lib/openhab/dsl/rules/builder.rb', line 520

def dependencies(trigger_types = %i[changed updated])
  trigger_types = Array.wrap(trigger_types)

  ruby_triggers.flat_map do |t|
    next [] unless trigger_types.include?(t.first)

    t[1].select { |i| i.is_a?(Item) || i.is_a?(GroupItem::Members) }
  end.uniq
end

#description(value) ⇒ void

This method returns an undefined value.

Set the rule's description.

Parameters:

  • value (String)


474
# File 'lib/openhab/dsl/rules/builder.rb', line 474

prop :description

#enabled(value) ⇒ void

This method returns an undefined value.

Enable or disable the rule from executing

Examples:

rule "disabled rule" do
  enabled(false)
end

Parameters:

  • value (true, false)


504
# File 'lib/openhab/dsl/rules/builder.rb', line 504

prop :enabled

#event(topic, source: nil, types: nil, attach: nil) ⇒ void

This method returns an undefined value.

Creates a trigger on events coming through the event bus

Examples:

rule "thing updated" do
  event("openhab/things/*/updated", types: "ThingUpdatedEvent")
  run do |event|
    logger.info("#{event.thing.uid} updated")
  end
end

Parameters:

  • topic (String)

    The topic to trigger on; can contain the wildcard *.

  • source (String, nil) (defaults to: nil)

    The sender of the event to trigger on. Default does not filter on source.

  • types (String, Array<String>, nil) (defaults to: nil)

    Only subscribe to certain event types. Default does not filter on event types.



1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
# File 'lib/openhab/dsl/rules/builder.rb', line 1684

def event(topic, source: nil, types: nil, attach: nil)
  types = types.join(",") if types.is_a?(Enumerable)
  # @deprecated OH3.4 - OH3 config uses eventXXX vs OH4 uses `topic`, `source`, and `types`
  # See https://github.com/openhab/openhab-core/pull/3299
  trigger("core.GenericEventTrigger",
          eventTopic: topic,
          eventSource: source,
          eventTypes: types, # @deprecated OH3.4
          topic: topic,
          source: source,
          types: types,
          attach: attach)
end

#every(value, at: nil, attach: nil) ⇒ void

This method returns an undefined value.

Create a rule that executes at the specified interval.

Examples:

rule "Daily" do
  every :day, at: '5:15'
  run do
    Light.on
  end
end

The above rule could also be expressed using LocalTime class as below

rule "Daily" do
  every :day, at: LocalTime.of(5, 15)
  run { Light.on }
end
rule "Weekly" do
  every :monday, at: '5:15'
  run do
    Light.on
  end
end
rule "Often" do
  every :minute
  run do
    Light.on
  end
end
rule "Hourly" do
  every :hour
  run do
    Light.on
  end
end
rule "Often" do
  every 5.minutes
  run do
    Light.on
  end
end
rule 'Every 14th of Feb at 2pm' do
  every '02-14', at: '2pm'
  run { logger.info "Happy Valentine's Day!" }
end
rule "Every day at sunset" do
  every :day, at: Sunset_Time
  run { logger.info "It's getting dark" }
end

Parameters:

  • value (String, Duration, java.time.MonthDay, :second, :minute, :hour, :day, :week, :month, :year, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday)

    When to execute rule.

  • at (LocalTime, String, Core::Items::DateTimeItem, nil) (defaults to: nil)

    What time of day to execute rule If value is :day, at can be a DateTimeItem, and the trigger will run every day at the (time only portion of) current state of the item. If the item is NULL or UNDEF, the trigger will not run.

  • attach (Object) (defaults to: nil)

    Object to be attached to the trigger



1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
# File 'lib/openhab/dsl/rules/builder.rb', line 1332

def every(value, at: nil, attach: nil)
  return every(java.time.MonthDay.parse(value), at: at, attach: attach) if value.is_a?(String)

  @ruby_triggers << [:every, value, { at: at }]

  if value == :day && at.is_a?(Item)
    raise ArgumentError, "Attachments are not supported with dynamic datetime triggers" unless attach.nil?

    return trigger("timer.DateTimeTrigger", itemName: at.name, timeOnly: true)
  end

  cron_expression = case value
                    when Symbol then Cron.from_symbol(value, at)
                    when Duration then Cron.from_duration(value, at)
                    when java.time.MonthDay then Cron.from_monthday(value, at)
                    else raise ArgumentError, "Unknown interval"
                    end
  cron(cron_expression, attach: attach)
end

#inspectString

Returns:

  • (String)


2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
# File 'lib/openhab/dsl/rules/builder.rb', line 2015

def inspect
  <<~TEXT.tr("\n", " ")
    #<OpenHAB::DSL::Rules::Builder: #{uid}
    triggers=#{triggers.inspect},
    run blocks=#{run.inspect},
    on_load=#{!@on_load.nil?},
    Trigger Conditions=#{trigger_conditions.inspect},
    Trigger UIDs=#{triggers.map(&:id).inspect},
    Attachments=#{attachments.inspect}
    >
  TEXT
end

#item_added(attach: nil) ⇒ void

This method returns an undefined value.

Creates an item added trigger

Examples:

rule "item added" do
  item_added
  run do |event|
    logger.info("#{event.item.name} added.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1569
1570
1571
1572
# File 'lib/openhab/dsl/rules/builder.rb', line 1569

def item_added(attach: nil)
  @ruby_triggers << [:item_added]
  event("openhab/items/*/added", types: "ItemAddedEvent", attach: attach)
end

#item_removed(attach: nil) ⇒ void

This method returns an undefined value.

Creates an item removed trigger

Examples:

rule "item removed" do
  item_removed
  run do |event|
    logger.info("#{event.item.name} removed.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1587
1588
1589
1590
# File 'lib/openhab/dsl/rules/builder.rb', line 1587

def item_removed(attach: nil)
  @ruby_triggers << [:item_removed]
  event("openhab/items/*/removed", types: "ItemRemovedEvent", attach: attach)
end

#item_updated(attach: nil) ⇒ void

This method returns an undefined value.

Creates an item updated trigger

Examples:

rule "item updated" do
  item_updated
  run do |event|
    logger.info("#{event.item.name} updated.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1606
1607
1608
1609
# File 'lib/openhab/dsl/rules/builder.rb', line 1606

def item_updated(attach: nil)
  @ruby_triggers << [:item_updated]
  event("openhab/items/*/updated", types: "ItemUpdatedEvent", attach: attach)
end

#name(value) ⇒ void

This method returns an undefined value.

Set the rule's name.

Parameters:

  • value (String)


464
# File 'lib/openhab/dsl/rules/builder.rb', line 464

prop :name

#not_if {|event| ... } ⇒ void

This method returns an undefined value.

Prevents execution of rules when the block's result is true and allows it when it's true.

Examples:

rule "Set OutsideDimmer to 50% if LightSwtich turned on and OtherSwitch is OFF" do
  changed LightSwitch, to: ON
  run { OutsideDimmer << 50 }
  not_if { OtherSwitch.on? }
end

Multiple #not_if statements can be used and if any of them are not satisfied the rule will not run.

rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is OFF and Door is not CLOSED" do
  changed LightSwitch, to: ON
  run { OutsideDimmer << 50 }
  not_if { OtherSwitch.on? }
  not_if { Door.closed? }
end

Yield Parameters:

Yield Returns:

  • (Boolean)

    A value indicating if the rule should not run.



670
671
672
# File 'lib/openhab/dsl/rules/builder.rb', line 670

prop_array(:not_if) do |item|
  raise ArgumentError, "Object passed to not_if must be a proc" unless item.is_a?(Proc)
end

#on_load(delay: nil, attach: nil) ⇒ void

This method returns an undefined value.

Creates a trigger that executes when the script is loaded

Execute the rule whenever the script is first loaded, including on openHAB start up, and on subsequent reloads on file modifications. This is useful to perform initialization routines, especially when combined with other triggers.

Examples:

rule "script startup rule" do
  on_load
  run do
    <calculate some item state>
  end
end
rule "Ensure all security lights are on" do
  on_load
  run { Security_Lights.on }
end

Parameters:

  • delay (Duration) (defaults to: nil)

    The amount of time to wait before executing the rule. When nil, execute immediately.

  • attach (Object) (defaults to: nil)

    Object to be attached to the trigger

Raises:

  • (ArgumentError)


1378
1379
1380
1381
1382
1383
1384
# File 'lib/openhab/dsl/rules/builder.rb', line 1378

def on_load(delay: nil, attach: nil)
  # prevent overwriting @on_load
  raise ArgumentError, "on_load can only be used once within a rule" if @on_load

  @on_load = { module: SecureRandom.uuid, delay: delay }
  attachments[@on_load[:module]] = attach
end

#on_start(at_level: nil, at_levels: nil, attach: nil) ⇒ void

This method returns an undefined value.

Creates a trigger that executes when openHAB reaches a certain start level

This will only trigger once during openHAB start up. It won't trigger on script reloads.

Examples:

rule "Trigger at openHAB system start" do
  on_start # trigger at the default startlevel 100
  run { logger.info "openHAB start up complete." }
end

Trigger at a specific start level

rule "Trigger after things are loaded" do
  on_start at_level: :things
  run { logger.info "Things are ready!" }
end

Trigger at multiple levels

rule "Multiple start up triggers" do
  on_start at_levels: %i[ui things complete]
  run do |event|
    logger.info "openHAB startlevel has reached level #{event.startlevel}"
  end
end

Parameters:

  • at_level (Integer, :rules, :ruleengine, :ui, :things, :complete) (defaults to: nil)

    Zero or more start levels. Note that Startlevels less than 40 are not available as triggers because the rule engine needs to start up first before it can execute any rules

    Symbol Start Level
    :osgi 10
    :model 20
    :state 30
    :rules 40
    :ruleengine 50
    :ui 70
    :things 80
    :complete 100
  • at_levels (Array<Integer,:rules,:ruleengine,:ui,:things,:complete>) (defaults to: nil)

    Fluent alias for at_level

  • attach (Object) (defaults to: nil)

    Object to be attached to the trigger

See Also:



1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
# File 'lib/openhab/dsl/rules/builder.rb', line 1431

def on_start(at_level: nil, at_levels: nil, attach: nil)
  levels = Array.wrap(at_level) | Array.wrap(at_levels)
  levels = [100] if levels.empty?

  levels.map! do |level|
    next level unless level.is_a?(Symbol)

    begin
      klass = org.openhab.core.service.StartLevelService.java_class
      klass.declared_field("STARTLEVEL_#{level.upcase}").get_int(klass)
    rescue java.lang.NoSuchFieldException
      raise ArgumentError, "Invalid symbol for at_level: :#{level}"
    end
  end

  @ruby_triggers << [:on_start, levels]
  levels.each do |level|
    logger.warn "Rule engine doesn't start until start level 40" if level < 40
    config = { startlevel: level }
    logger.trace { "Creating a SystemStartlevelTrigger with startlevel=#{level}" }
    Triggers::Trigger.new(rule_triggers: @rule_triggers)
                     .append_trigger(type: "core.SystemStartlevelTrigger", config: config, attach: attach)
  end
end

#only_every(interval) ⇒ void

This method returns an undefined value.

Executes the rule then ignores subsequent triggers for a given duration.

Additional triggers that occur within the given duration after the rule execution will be ignored. This results in executions happening only at the specified interval or more.

Unlike #throttle_for, this guard will execute the rule as soon as a new trigger occurs instead of waiting for the specified duration. This is ideal for triggers such as a door bell where the rule should run as soon as a new trigger is detected but ignore subsequent triggers if they occur too soon after.

Examples:

Only allow executions every 10 minutes or more

rule "Aircon Vent Control" do
  changed BedRoom_Temperature
  only_every 10.minutes
  run do
    # Adjust BedRoom_Aircon_Vent
  end
end

Run only on the first update and ignore subsequent triggers for the next minute

# They can keep pressing the door bell as often as they like,
# but the bell will only ring at most once every minute
rule do
  updated DoorBell_Button, to: "single"
  only_every 1.minute
  run { Audio.play_stream "doorbell.mp3" }
end

Using symbolic duration

rule "Display update" do
  updated Power_Usage
  only_every :minute
  run { Power_Usage_Display.update "Current power usage: #{Power_Usage.average_since(1.minute.ago)}" }
end

Parameters:

  • interval (Duration, :second, :minute, :hour, :day)

    The period during which subsequent triggers are ignored.

See Also:



849
850
851
852
# File 'lib/openhab/dsl/rules/builder.rb', line 849

def only_every(interval)
  interval = 1.send(interval) if %i[second minute hour day].include?(interval)
  debounce(for: interval, leading: true)
end

#only_if {|event| ... } ⇒ void

This method returns an undefined value.

Allows rule execution when the block's result is true and prevents it when it's false.

Examples:

rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is also ON" do
  changed LightSwitch, to: ON
  run { OutsideDimmer << 50 }
  only_if { OtherSwitch.on? }
end

Multiple #only_if statements can be used and all must be true for the rule to run.

rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is also ON and Door is closed" do
  changed LightSwitch, to: ON
  run { OutsideDimmer << 50 }
  only_if { OtherSwitch.on? }
  only_if { Door.closed? }
end

Guards have access to event information.

rule "Set OutsideDimmer to 50% if any switch in group Switches starting with Outside is switched On" do
  changed Switches.items, to: ON
  run { OutsideDimmer << 50 }
  only_if { |event| event.item.name.start_with?("Outside") }
end

Yield Parameters:

Yield Returns:

  • (Boolean)

    A value indicating if the rule should run.



642
643
644
# File 'lib/openhab/dsl/rules/builder.rb', line 642

prop_array(:only_if) do |item|
  raise ArgumentError, "Object passed to only_if must be a proc" unless item.is_a?(Proc)
end

#otherwise {|event| ... } ⇒ Object

Add a block that will be passed event data, to be run if guards are not satisfied.

The #otherwise property is the automation code that is executed when a rule is triggered and guards are not satisfied. This property accepts a block of code and executes it. The block is automatically passed an event object which can be used to access multiple properties about the triggering event.

Examples:

rule 'Turn switch ON or OFF based on value of another switch' do
  on_load
  run { TestSwitch << ON }
  otherwise { TestSwitch << OFF }
  only_if { OtherSwitch.on? }
end

Yield Parameters:



442
# File 'lib/openhab/dsl/rules/builder.rb', line 442

prop_array :otherwise, array_name: :run_queue, wrapper: Otherwise

#received_command(*items, command: nil, commands: nil, attach: nil) ⇒ void

This method returns an undefined value.

Creates a trigger for when an item or group receives a command.

The command/commands parameters are replicated for DSL fluency.

The event passed to run blocks will be an Core::Events::ItemCommandEvent.

Examples:

rule 'Execute rule when item received command' do
  received_command Alarm_Mode
  run { |event| logger.info("Item received command: #{event.command}" ) }
end
rule 'Execute rule when item receives specific command' do
  received_command Alarm_Mode, command: 7
  run { |event| logger.info("Item received command: #{event.command}" ) }
end
rule 'Execute rule when item receives one of many specific commands' do
  received_command Alarm_Mode, commands: [7,14]
  run { |event| logger.info("Item received command: #{event.command}" ) }
end
rule 'Execute rule when group receives a specific command' do
  received_command AlarmModes
  triggered { |item| logger.info("Group #{item.name} received command")}
end
rule 'Execute rule when member of group receives any command' do
  received_command AlarmModes.members
  triggered { |item| logger.info("Group item #{item.name} received command")}
end
rule 'Execute rule when member of group is changed to one of many states' do
  received_command AlarmModes.members, commands: [7, 14]
  triggered { |item| logger.info("Group item #{item.name} received command")}
end
rule 'Execute rule when item receives a range of commands' do
  received_command Alarm_Mode, commands: 7..14
  run { |event| logger.info("Item received command: #{event.command}" ) }
end

Works with procs

rule 'Execute rule when Alarm Mode command is odd' do
  received_command Alarm_Mode, command: proc { |c| c.odd? }
  run { |event| logger.info("Item received command: #{event.command}" ) }
end

Works with lambdas

rule 'Execute rule when Alarm Mode command is odd' do
  received_command Alarm_Mode, command: -> c { c.odd? }
  run { |event| logger.info("Item received command: #{event.command}" ) }
end

Works with regexes

rule 'Execute rule when Alarm Mode command matches a substring' do
  received_command Alarm_Mode, command: /arm/
  run { |event| logger.info("Item received command: #{event.command}" ) }
end

Parameters:



1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
# File 'lib/openhab/dsl/rules/builder.rb', line 1530

def received_command(*items, command: nil, commands: nil, attach: nil)
  command_trigger = Command.new(rule_triggers: @rule_triggers)

  # if neither command nor commands is specified, ensure that we create
  # a trigger that isn't looking for a specific command.
  commands = [nil] if command.nil? && commands.nil?
  commands = Array.wrap(command) | Array.wrap(commands)

  @ruby_triggers << [:received_command, items, { command: commands }]

  items.each do |item|
    case item
    when Core::Items::Item,
         Core::Items::GroupItem::Members
      nil
    else
      raise ArgumentError, "items must be an Item or GroupItem::Members"
    end
    commands.each do |cmd|
      logger.trace { "Creating received command trigger for items #{item.inspect} and commands #{cmd.inspect}" }

      command_trigger.trigger(item: item, command: cmd, attach: attach)
    end
  end
end

#run {|event| ... } ⇒ void

This method returns an undefined value.

Add a block that will be passed event data.

The run property is the automation code that is executed when a rule is triggered. This property accepts a block of code and executes it. The block is automatically passed an event object which can be used to access multiple properties about the triggering event. The code for the automation can be entirely within the run block and can call methods defined in the Ruby script.

Examples:

{} style used for single line blocks.

rule 'Access Event Properties' do
  changed TestSwitch
  run { |event| logger.info("#{event.item.name} triggered from #{event.was} to #{event.state}") }
end

do/end style used for multi-line blocks.

rule 'Multi Line Run Block' do
  changed TestSwitch
  run do |event|
    logger.info("#{event.item.name} triggered")
    logger.info("from #{event.was}") if event.was?
    logger.info("to #{event.state}") if event.state?
   end
end

Rules can have multiple run blocks and they are executed in order. Useful when used in combination with #delay.

rule 'Multiple Run Blocks' do
  changed TestSwitch
  run { |event| logger.info("#{event.item.name} triggered") }
  run { |event| logger.info("from #{event.was}") if event.was? }
  run { |event| logger.info("to #{event.state}") if event.state?  }
end

Yield Parameters:



321
# File 'lib/openhab/dsl/rules/builder.rb', line 321

prop_array :run, array_name: :run_queue, wrapper: Run

#tags(*tags) ⇒ void

This method returns an undefined value.

Set the rule's tags.

Examples:

rule "tagged rule" do
  tags "lighting", "security"
end

Parameters:



489
# File 'lib/openhab/dsl/rules/builder.rb', line 489

prop :tags

#thing_added(attach: nil) ⇒ void

This method returns an undefined value.

Creates a thing added trigger

Examples:

rule "thing added" do
  thing_added
  run do |event|
    logger.info("#{event.thing.uid} added.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1624
1625
1626
1627
# File 'lib/openhab/dsl/rules/builder.rb', line 1624

def thing_added(attach: nil)
  @ruby_triggers << [:thing_added]
  event("openhab/things/*/added", types: "ThingAddedEvent", attach: attach)
end

#thing_removed(attach: nil) ⇒ void

This method returns an undefined value.

Creates a thing removed trigger

Examples:

rule "thing removed" do
  thing_removed
  run do |event|
    logger.info("#{event.thing.uid} removed.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1642
1643
1644
1645
# File 'lib/openhab/dsl/rules/builder.rb', line 1642

def thing_removed(attach: nil)
  @ruby_triggers << [:thing_removed]
  event("openhab/things/*/removed", types: "ThingRemovedEvent", attach: attach)
end

#thing_updated(attach: nil) ⇒ void

This method returns an undefined value.

Creates a thing updated trigger

Examples:

rule "thing updated" do
  thing_updated
  run do |event|
    logger.info("#{event.thing.uid} updated.")
  end
end

Parameters:

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1661
1662
1663
1664
# File 'lib/openhab/dsl/rules/builder.rb', line 1661

def thing_updated(attach: nil)
  @ruby_triggers << [:thing_updated]
  event("openhab/things/*/updated", types: "ThingUpdatedEvent", attach: attach)
end

#throttle_for(duration) ⇒ void

This method returns an undefined value.

Rate-limits rule executions by delaying triggers and executing the last trigger within the given duration.

When a new trigger occurs, it will hold the execution and start a fixed timer for the given duration. Should more triggers occur during this time, keep holding and once the wait time is over, execute the latest trigger.

#throttle_for will execute rules after it had waited for the given duration, regardless of how frequently the triggers were occuring. In contrast, #debounce_for will wait until there is a minimum interval between two triggers.

#throttle_for is ideal in situations where regular status updates need to be made for frequently changing values. It is also useful when a rule responds to triggers from multiple related items that are updated at around the same time. Instead of executing the rule multiple times, #throttle_for will wait for a pre-set amount of time since the first group of triggers occurred before executing the rule.

Examples:

Perform calculations from multiple items

rule "Update Power Summary " do |rule|
  changed Power_From_Solar, Power_Load, Power_From_Grid
  throttle_for 1.second
  only_if { rule.dependencies.all?(&:state?) } # make sure all items have a state
  run do
    msg = []
    msg << Power_Load.state.negate.to_unit("kW").format("Load: %.2f %unit%")
    msg << Power_From_Solar.state.to_unit("kW").format("PV: %.2f %unit%")
    if Power_From_Grid.positive?
      msg << Power_From_Grid.state.to_unit("kW").format("From Grid: %.1f %unit%")
    else
      msg << Power_From_Grid.state.negate.to_unit("kW").format("To Grid: %.1f %unit%")
    end
    Power_Summary.update(msg.join(", "))
  end
end

Parameters:

  • duration (Duration)

    The minimum amount of time to wait inbetween rule executions.

See Also:



801
802
803
# File 'lib/openhab/dsl/rules/builder.rb', line 801

def throttle_for(duration)
  debounce(for: duration)
end

#time_series_updated(*items, attach: nil) ⇒ void

This method returns an undefined value.

Creates a time series updated trigger

The event passed to run blocks will be a Core::Events::ItemTimeSeriesUpdatedEvent

Examples:

rule 'Execute rule when item time series is updated' do
  time_series_updated MyItem
  run do |event|
    logger.info("Item time series updated: #{event.item.name}.")
    logger.info("  TimeSeries size: #{event.time_series.size}, policy: #{event.time_series.policy}")
    event.time_series.each do |entry|
      timestamp = entry.timestamp.to_time.strftime("%Y-%m-%d %H:%M:%S")
      logger.info("    Entry: #{timestamp}: State: #{entry.state}")
    end
  end
end

Parameters:

  • items (Item)

    Items to create trigger for.

  • attach (Object) (defaults to: nil)

    Object to be attached to the trigger.

See Also:

Since:

  • openHAB 4.1



1904
1905
1906
1907
1908
1909
1910
1911
# File 'lib/openhab/dsl/rules/builder.rb', line 1904

def time_series_updated(*items, attach: nil)
  @ruby_triggers << [:time_series_updated, items]
  items.map do |item|
    raise ArgumentError, "items must be an Item or GroupItem::Members" unless item.is_a?(Core::Items::Item)

    event("openhab/items/#{item.name}/timeseriesupdated", types: "ItemTimeSeriesUpdatedEvent", attach: attach)
  end
end

#trigger(type, attach: nil, **configuration) ⇒ void

This method returns an undefined value.

Create a generic trigger given the trigger type uid and a configuration hash

This provides the ability to create a trigger type not already covered by the other methods.

Examples:

Create a trigger for the PID Controller Automation add-on.

rule 'PID Control' do
  trigger 'pidcontroller.trigger',
    input: InputItem.name,
    setpoint: SetPointItem.name,
    kp: 10,
    ki: 10,
    kd: 10,
    kdTimeConstant: 1,
    loopTime: 1000

  run do |event|
    logger.info("PID controller command: #{event.command}")
    ControlItem << event.command
  end
end

DateTime Trigger

rule 'DateTime Trigger' do
  description 'Triggers at a time specified in MyDateTimeItem'
  trigger 'timer.DateTimeTrigger', itemName: MyDateTimeItem.name
  run do
    logger.info("DateTimeTrigger has been triggered")
  end
end

Parameters:

  • type (String)

    Trigger type UID

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger

  • configuration (Hash)

    A hash containing the trigger configuration entries



1767
1768
1769
1770
1771
# File 'lib/openhab/dsl/rules/builder.rb', line 1767

def trigger(type, attach: nil, **configuration)
  logger.trace { "Creating trigger (#{type}) with configuration(#{configuration})" }
  Triggers::Trigger.new(rule_triggers: @rule_triggers)
                   .append_trigger(type: type, config: configuration, attach: attach)
end

#triggered {|item| ... } ⇒ void

This method returns an undefined value.

Add a block that will be passed the triggering item.

This property is the same as the #run property except rather than passing an event object to the automation block the triggered item is passed. This enables optimizations for simple cases and supports Ruby's pretzel colon &: operator..

Examples:

rule "motion sensor triggered" do
  changed MotionSensor.members, to: :OPEN
  triggered do |item|
    logger.info("#{item.name} detected motion")
  end
end
rule 'Triggered has access directly to item triggered' do
  changed TestSwitch
  triggered { |item| logger.info("#{item.name} triggered") }
end

Triggered items are highly useful when working with groups

# Switches is a group of Switch items
rule 'Triggered item is item changed when a group item is changed.' do
  changed Switches.members
  triggered { |item| logger.info("Switch #{item.name} changed to #{item.state}")}
end

rule 'Turn off any switch that changes' do
  changed Switches.members
  triggered(&:off)
end

Like other execution blocks, multiple triggered blocks are supported in a single rule

rule 'Turn a switch off and log it, 5 seconds after turning it on' do
  changed Switches.members, to: ON
  delay 5.seconds
  triggered(&:off)
  triggered {|item| logger.info("#{item.label} turned off") }
end

Yield Parameters:



371
# File 'lib/openhab/dsl/rules/builder.rb', line 371

prop_array :triggered, array_name: :run_queue, wrapper: Trigger

#uid(id) ⇒ void

This method returns an undefined value.

Set the rule's UID.

Parameters:

  • id (String)


454
# File 'lib/openhab/dsl/rules/builder.rb', line 454

prop(:uid) { |id| Thread.current[:openhab_rule_uid] = id }

#updated(*items, to: nil, attach: nil) ⇒ void

This method returns an undefined value.

Create a trigger when item, group or thing is updated

The event passed to run blocks will be an Core::Events::ItemStateEvent or a Core::Events::ThingStatusInfoEvent depending on if the triggering element was an item or a thing.

Examples:

rule 'Execute rule when item is updated to any value' do
  updated Alarm_Mode
  run { logger.info("Alarm Mode Updated") }
end
rule 'Execute rule when item is updated to specific number' do
  updated Alarm_Mode, to: 7
  run { logger.info("Alarm Mode Updated") }
end
rule 'Execute rule when item is updated to one of many specific states' do
  updated Alarm_Mode, to: [7, 14]
  run { logger.info("Alarm Mode Updated")}
end
rule 'Execute rule when item is within a range' do
  updated Alarm_Mode, to: 7..14
  run { logger.info("Alarm Mode Updated to a value between 7 and 14")}
end
rule 'Execute rule when group is updated to any state' do
  updated AlarmModes
  triggered { |item| logger.info("Group #{item.name} updated")}
end
rule 'Execute rule when member of group is changed to any state' do
  updated AlarmModes.members
  triggered { |item| logger.info("Group item #{item.name} updated")}
end
rule 'Execute rule when member of group is changed to one of many states' do
  updated AlarmModes.members, to: [7, 14]
  triggered { |item| logger.info("Group item #{item.name} updated")}
end

Works with procs

rule 'Execute rule when member of group is changed to an odd state' do
  updated AlarmModes.members, to: proc { |t| t.odd? }
  triggered { |item| logger.info("Group item #{item.name} updated")}
end

Works with lambdas:

rule 'Execute rule when member of group is changed to an odd state' do
  updated AlarmModes.members, to: -> t { t.odd? }
  triggered { |item| logger.info("Group item #{item.name} updated")}
end

Works with regexes:

rule 'Execute rule when member of group is changed to a substring' do
  updated AlarmModes.members, to: /armed/
  triggered { |item| logger.info("Group item #{item.name} updated")}
end

Works with things as well

rule 'Execute rule when thing is updated' do
   updated things['astro:sun:home'], :to => :uninitialized
   run { |event| logger.info("Thing #{event.uid} status <trigger> to #{event.status}") }
end

Parameters:



1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
# File 'lib/openhab/dsl/rules/builder.rb', line 1857

def updated(*items, to: nil, attach: nil)
  updated = Updated.new(rule_triggers: @rule_triggers)
  @ruby_triggers << [:updated, items, { to: to }]
  items.map do |item|
    case item
    when Core::Things::Thing,
         Core::Things::ThingUID,
         Core::Items::Item,
         Core::Items::GroupItem::Members
      nil
    else
      raise ArgumentError, "items must be an Item, GroupItem::Members, Thing, or ThingUID"
    end

    logger.trace { "Creating updated trigger for item(#{item}) to(#{to})" }
    [to].flatten.map do |to_state|
      updated.trigger(item: item, to: to_state, attach: attach)
    end
  end.flatten
end

#watch(path, glob: "*", for: %i[created deleted modified], attach: nil) ⇒ void

This method returns an undefined value.

Create a trigger to watch a path

It provides the ability to create a trigger on file and directory changes.

If a file or a path that does not exist is supplied as the argument to watch, the parent directory will be watched and the file or non-existent part of the supplied path will become the glob. For example, if the path given is /tmp/foo/bar and /tmp/foo exists but bar does not exist inside of of /tmp/foo then the directory /tmp/foo will be watched for any files that match */bar.

If the last part of the path contains any glob characters e.g. /tmp/foo/*bar, the parent directory will be watched and the last part of the path will be treated as if it was passed as the glob argument. In other words, watch '/tmp/foo/*bar' is equivalent to watch '/tmp/foo', glob: '*bar'

#Watching inside subdirectories

Subdirectories aren't watched unless:

  • One of the glob patterns include the recursive match pattern **, or
  • The glob patterns include subdirectories, see examples below.

The event passed to run blocks will be a Events::WatchEvent.

Examples:

Watch items directory inside of the openHAB configuration path and log any changes.

rule 'watch directory' do
  watch OpenHAB::Core.config_folder / 'items'
  run { |event| logger.info("#{event.path.basename} - #{event.type}") }
end

Watch items directory for files that end in *.erb and log any changes

rule 'watch directory' do
  watch OpenHAB::Core.config_folder / 'items', glob: '*.erb'
  run { |event| logger.info("#{event.path.basename} - #{event.type}") }
end

Watch items/foo.items log any changes

rule 'watch directory' do
  watch OpenHAB::Core.config_folder / 'items/foo.items'
  run { |event| logger.info("#{event.path.basename} - #{event.type}") }
end

Watch items/*.items log any changes

rule 'watch directory' do
  watch OpenHAB::Core.config_folder / 'items/*.items'
  run { |event| logger.info("#{event.path.basename} - #{event.type}") }
end

Watch items/*.items for when items files are deleted or created (ignore changes)

rule 'watch directory' do
  watch OpenHAB::Core.config_folder / 'items/*.items', for: [:deleted, :created]
  run { |event| logger.info("#{event.path.basename} - #{event.type}") }
end

Watch for changes inside all subdirs of config_folder/automation/ruby/lib

rule "Watch recursively" do
  watch OpenHAB::Core.config_folder / "automation/ruby/lib/**"
  run { |event| logger.info("#{event.path} - #{event.type}") }
end

Recursively watch using subdirectory in glob

rule "Monitor changes in the list of installed gems" do
  watch ENV['GEM_HOME'], glob: "gems/*"
  run { |event| logger.info("#{event.path} - #{event.type}") }
end

Recursively watch using glob option

rule "Watch recursively" do
  watch OpenHAB::Core.config_folder / "automation/ruby/lib", glob: "**"
  run { |event| logger.info("#{event.path} - #{event.type}") }
end

Parameters:

  • path (String)

    Path to watch. Can be a directory or a file.

  • glob (String) (defaults to: "*")

    Limit events to paths matching this glob. Globs are matched using glob PathMatcher rules.

  • for (Array<:created, :deleted, :modified>, :created, :deleted, :modified) (defaults to: %i[created deleted modified])

    Types of changes to watch for.

  • attach (Object) (defaults to: nil)

    object to be attached to the trigger



1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
# File 'lib/openhab/dsl/rules/builder.rb', line 1999

def watch(path, glob: "*", for: %i[created deleted modified], attach: nil)
  types = [binding.local_variable_get(:for)].flatten

  WatchHandler::WatchTriggerHandlerFactory.instance # ensure it's registered
  trigger(WatchHandler::WATCH_TRIGGER_MODULE_ID,
          path: path.to_s,
          types: types.map(&:to_s),
          glob: glob.to_s,
          attach: attach)
end