Module: OpenHAB::DSL

Includes:
Core::Actions, Core::EntityLookup, Core::ScriptHandling, Rules::Terse
Included in:
Items::Builder, Rules::BuilderDSL
Defined in:
lib/openhab/dsl.rb,
lib/openhab/dsl/rules.rb,
lib/openhab/dsl/events.rb,
lib/openhab/dsl/version.rb,
lib/openhab/dsl/debouncer.rb,
lib/openhab/dsl/rules/guard.rb,
lib/openhab/dsl/rules/terse.rb,
lib/openhab/dsl/items/ensure.rb,
lib/openhab/dsl/thread_local.rb,
lib/openhab/dsl/items/builder.rb,
lib/openhab/dsl/rules/builder.rb,
lib/openhab/dsl/timer_manager.rb,
lib/openhab/dsl/rules/property.rb,
lib/openhab/dsl/rules/triggers.rb,
lib/openhab/dsl/things/builder.rb,
lib/openhab/dsl/sitemaps/builder.rb,
lib/openhab/dsl/events/watch_event.rb,
lib/openhab/dsl/items/timed_command.rb,
lib/openhab/dsl/rules/rule_triggers.rb,
lib/openhab/dsl/rules/name_inference.rb,
lib/openhab/dsl/rules/automation_rule.rb,
lib/openhab/dsl/rules/triggers/changed.rb,
lib/openhab/dsl/rules/triggers/channel.rb,
lib/openhab/dsl/rules/triggers/command.rb,
lib/openhab/dsl/rules/triggers/trigger.rb,
lib/openhab/dsl/rules/triggers/updated.rb,
lib/openhab/dsl/rules/triggers/cron/cron.rb,
lib/openhab/dsl/rules/triggers/conditions.rb,
lib/openhab/dsl/rules/triggers/cron/cron_handler.rb,
lib/openhab/dsl/rules/triggers/conditions/generic.rb,
lib/openhab/dsl/rules/triggers/conditions/duration.rb,
lib/openhab/dsl/rules/triggers/watch/watch_handler.rb

Overview

The main DSL available to rules.

Methods on this module are extended onto main, the top level self in any file. You can also access them as class methods on the module for use inside of other classes, or include the module.

Defined Under Namespace

Modules: Events, Items, Rules, Sitemaps, Things Classes: Debouncer, TimerManager

Constant Summary collapse

VERSION =

Version of openHAB helper libraries

Returns:

  • (String)
"5.9.0"

Rule Creation collapse

Rule Support collapse

Object Access collapse

Utilities collapse

Block Modifiers collapse

These methods allow certain operations to be grouped inside the given block to reduce repetitions

Methods included from Rules::Terse

#changed, #channel, #channel_linked, #channel_unlinked, #cron, #every, #item_added, #item_removed, #item_updated, #on_start, #received_command, #thing_added, #thing_removed, #thing_updated, #updated

Methods included from Core::ScriptHandling

script_loaded, script_unloaded

Methods included from Core::Actions

notify

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missingObject (private)

Provide access to the script context / variables see OpenHAB::DSL::Rules::AutomationRule#execute!



985
986
987
988
989
990
991
# File 'lib/openhab/dsl.rb', line 985

ruby2_keywords def method_missing(method, *args)
  return super unless args.empty? && !block_given?
  return super unless (context = Thread.current[:openhab_context]) && context.key?(method)

  logger.trace("DSL#method_missing found context variable: '#{method}'")
  context[method]
end

Class Method Details

.after(duration, id: nil, reschedule: true) {|timer| ... } ⇒ Core::Timer

Create a timer and execute the supplied block after the specified duration

#Reentrant Timers

Timers with an id are reentrant by id. Reentrant means that when the same id is encountered, the timer is rescheduled rather than creating a second new timer. Note that the timer will execute the block provided in the latest call.

This removes the need for the usual boilerplate code to manually keep track of timer objects.

Timers with id can be managed with the built-in timers object.

When a timer is cancelled, it will be removed from the object.

Be sure that your ids are unique. For example, if you're using items as your ids, you either need to be sure you don't use the same item for multiple logical contexts, or you need to make your id more specific, by doing something like embedding the item in array with a symbol of the timer's purpose, like [:vacancy, item]. But also note that assuming default settings, every Ruby file (for file-based rules) or UI rule gets its own instance of the timers object, so you don't need to worry about collisions among different files.

Examples:

Create a simple timer

after 5.seconds do
  logger.info("Timer Fired")
end

Timers delegate methods to openHAB timer objects

after 1.second do |timer|
  logger.info("Timer is active? #{timer.active?}")
end

Timers can be rescheduled to run again, waiting the original duration

after 3.seconds do |timer|
  logger.info("Timer Fired")
  timer.reschedule
end

Timers can be rescheduled for different durations

after 3.seconds do |timer|
  logger.info("Timer Fired")
  timer.reschedule 5.seconds
end

Timers can be manipulated through the returned object

mytimer = after 1.minute do
  logger.info("It has been 1 minute")
end

mytimer.cancel

Reentrant timers will automatically reschedule if the same id is encountered again

rule "Turn off closet light after 10 minutes" do
  changed ClosetLights.members, to: ON
  triggered do |item|
    after 10.minutes, id: item do
      item.ensure.off
    end
  end
end

Timers with id can be managed through the built-in timers object

after 1.minute, id: :foo do
  logger.info("managed timer has fired")
end

timers.cancel(:foo)

if timers.include?(:foo)
  logger.info("The timer :foo is not active")
end

Only create a new timer if it isn't already scheduled

after(1.minute, id: :foo, reschedule: false) do
  logger.info("Timer fired")
end

Reentrant timers will execute the block from the most recent call

# In the following example, if Item1 received a command, followed by Item2,
# the timer will execute the block referring to Item2.
rule "Execute The Most Recent Block" do
  received_command Item1, Item2
  run do |event|
    after(10.minutes, id: :common_timer) do
      logger.info "The latest command was received from #{event.item}"
    end
  end
end

Parameters:

  • duration (java.time.temporal.TemporalAmount, #to_zoned_date_time, Proc)

    Duration after which to execute the block

  • id (Object) (defaults to: nil)

    ID to associate with timer. The timer can be managed via timers.

  • reschedule (true, false) (defaults to: true)

    Reschedule the timer if it already exists.

Yields:

  • Block to execute when the timer is elapsed.

Yield Parameters:

Returns:

  • (Core::Timer)

    if reschedule is false, the existing timer. Otherwise the new timer.

Raises:

  • (ArgumentError)

See Also:



354
355
356
357
358
359
360
# File 'lib/openhab/dsl.rb', line 354

def after(duration, id: nil, reschedule: true, &block)
  raise ArgumentError, "Block is required" unless block

  # Carry rule name to timer
  thread_locals = ThreadLocal.persist
  timers.create(duration, id: id, reschedule: reschedule, thread_locals: thread_locals, block: block)
end

.between(range) ⇒ Range

Convert a string based range into a range of LocalTime, LocalDate, MonthDay, or ZonedDateTime depending on the format of the string.

Examples:

Range#cover?

logger.info("Within month-day range") if between('02-20'..'06-01').cover?(MonthDay.now)

Use in a Case

case MonthDay.now
when between('01-01'..'03-31')
  logger.info("First quarter")
when between('04-01'..'06-30')
 logger.info("Second quarter")
end

Create a time range

between('7am'..'12pm').cover?(LocalTime.now)

Returns:

  • (Range)

    converted range object

Raises:

  • (ArgumentError)

See Also:



384
385
386
387
388
389
390
# File 'lib/openhab/dsl.rb', line 384

def between(range)
  raise ArgumentError, "Supplied object must be a range" unless range.is_a?(Range)

  start = try_parse_time_like(range.begin)
  finish = try_parse_time_like(range.end)
  Range.new(start, finish, range.exclude_end?)
end

.debounce_for(debounce_time, id: nil, &block) ⇒ void

This method returns an undefined value.

Waits until calls to this method have stopped firing for a period of time before executing the block.

This method acts as a guard for the given block to ensure that it doesn't get executed too frequently. The debounce_for method can be called as frequently as possible. The given block, however, will only be executed once the debounce_time has passed since the last call to debounce_for.

This method can be used from within a UI rule as well as from a file-based rule.

Examples:

Run a block of code only after an item has stopped changing

# This can be placed inside a UI rule with an Item Change trigger for
# a Door contact sensor.
# If the door state has stopped changing state for 10 minutes,
# execute the block.
debounce_for(10.minutes) do
  if DoorState.open?
    Voice.say("The door has been left open!")
  end
end

Parameters:

  • id (Object) (defaults to: nil)

    ID to associate with this call.

  • block (Block)

    The block to be debounced.

  • 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:



456
457
458
459
# File 'lib/openhab/dsl.rb', line 456

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

.ensure_states { ... } ⇒ Object

Global method that takes a block and for the duration of the block all commands sent will check if the item is in the command's state before sending the command.

Examples:

Turn on several switches only if they're not already on

ensure_states do
  Switch1.on
  Switch2.on
end
# VirtualSwitch is in state `ON`
ensure_states do
  VirtualSwitch << ON       # No command will be sent
  VirtualSwitch.update(ON)  # No update will be posted
  VirtualSwitch << OFF      # Off command will be sent
  VirtualSwitch.update(OFF) # No update will be posted
end
ensure_states do
  rule 'Items in an execution block will have ensure_states applied to them' do
    changed VirtualSwitch
    run do
      VirtualSwitch.on
      VirtualSwitch2.on
    end
  end
end
rule 'ensure_states must be in an execution block' do
  changed VirtualSwitch
  run do
     ensure_states do
        VirtualSwitch.on
        VirtualSwitch2.on
     end
  end
end

Yields:

Returns:

  • (Object)

    The result of the block.



605
606
607
608
609
610
611
# File 'lib/openhab/dsl.rb', line 605

def ensure_states
  old = Thread.current[:openhab_ensure_states]
  Thread.current[:openhab_ensure_states] = true
  yield
ensure
  Thread.current[:openhab_ensure_states] = old
end

.holiday_file(file) { ... } ⇒ Object .holiday_fileString?

Overloads:

  • .holiday_file(file) { ... } ⇒ Object

    Sets a thread local variable to use a specific holiday file for ephemeris calls inside the block.

    Examples:

    Set a specific holiday configuration file temporarily

    holiday_file("/home/cody/holidays.xml") do
      Time.now.next_holiday
    end

    Parameters:

    • file (String, nil)

      Path to a file defining holidays; nil to reset to default.

    Yields:

    • [] Block executed in context of the supplied holiday file

    Returns:

    • (Object)

      The return value from the block.

    See Also:

  • .holiday_fileString?

    Returns the current thread local value for the holiday file.

    Returns:

    • (String, nil)

      the current holiday file

See Also:



936
937
938
939
940
941
942
943
944
945
946
# File 'lib/openhab/dsl.rb', line 936

def holiday_file(*args)
  raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" if args.length > 1

  old = Thread.current[:openhab_holiday_file]
  return old if args.empty?

  holiday_file!(args.first)
  yield
ensure
  holiday_file!(old)
end

.holiday_file!(file = nil) ⇒ Symbol?

Sets a thread local variable to set the default holiday file.

Examples:

holiday_file!("/home/cody/holidays.xml")
Time.now.next_holiday

Parameters:

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

    Path to a file defining holidays; nil to reset to default.

Returns:

  • (Symbol, nil)

    the new holiday file

See Also:



961
962
963
# File 'lib/openhab/dsl.rb', line 961

def holiday_file!(file = nil)
  Thread.current[:openhab_holiday_file] = file
end

.itemsCore::Items::Registry

Fetches all items from the item registry

The examples all assume the following items exist.

Dimmer DimmerTest "Test Dimmer"
Switch SwitchTest "Test Switch"

Examples:

logger.info("Item Count: #{items.count}")  # Item Count: 2
logger.info("Items: #{items.map(&:label).sort.join(', ')}")  # Items: Test Dimmer, Test Switch'
logger.info("DimmerTest exists? #{items.key?('DimmerTest')}") # DimmerTest exists? true
logger.info("StringTest exists? #{items.key?('StringTest')}") # StringTest exists? false
rule 'Use dynamic item lookup to increase related dimmer brightness when switch is turned on' do
  changed SwitchTest, to: ON
  triggered { |item| items[item.name.gsub('Switch','Dimmer')].brighten(10) }
end
rule 'search for a suitable item' do
  on_load
  triggered do
    # Send ON to DimmerTest if it exists, otherwise send it to SwitchTest
    (items['DimmerTest'] || items['SwitchTest'])&.on
  end
end

Returns:



213
214
215
# File 'lib/openhab/dsl.rb', line 213

def items
  Core::Items::Registry.instance
end

.only_every(interval, id: nil, &block) ⇒ void

This method returns an undefined value.

Limit how often the given block executes to the specified interval.

only_every will execute the given block but prevents further executions until the given interval has passed. In contrast, throttle_for will not execute the block immediately, and will wait until the end of the interval.

Examples:

Prevent door bell from ringing repeatedly

# This can be called from a UI rule.
# For file based rule, use the `only_every` rule guard
only_every(30.seconds) { Audio.play_sound("doorbell.mp3") }

Parameters:

  • id (Object) (defaults to: nil)

    ID to associate with this call.

  • block (Block)

    The block to be throttled.

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

    The period during which subsequent triggers are ignored.

See Also:



519
520
521
522
# File 'lib/openhab/dsl.rb', line 519

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

.persistence(service) { ... } ⇒ Object

Sets a thread local variable to set the default persistence service for method calls inside the block

Examples:

persistence(:influxdb) do
  Item1.persist
  Item1.changed_since(1.hour)
  Item1.average_since(12.hours)
end

Parameters:

  • service (Object)

    Persistence service either as a String or a Symbol

Yields:

  • [] Block executed in context of the supplied persistence service

Returns:

  • (Object)

    The return value from the block.

See Also:



631
632
633
634
635
636
# File 'lib/openhab/dsl.rb', line 631

def persistence(service)
  old = persistence!(service)
  yield
ensure
  persistence!(old)
end

.persistence!(service = nil) ⇒ Object?

Note:

This method is only intended for use at the top level of rule scripts. If it's used within library methods, or hap-hazardly within rules, things can get very confusing because the prior state won't be properly restored.

Permanently sets the default persistence service for the current thread

Parameters:

  • service (Object) (defaults to: nil)

    Persistence service either as a String or a Symbol. When nil, use the system's default persistence service.

Returns:

  • (Object, nil)

    The previous persistence service settings, or nil when using the system's default.

See Also:



653
654
655
656
657
# File 'lib/openhab/dsl.rb', line 653

def persistence!(service = nil)
  old = Thread.current[:openhab_persistence_service]
  Thread.current[:openhab_persistence_service] = service
  old
end

.profile(id) {|event, command: nil, state: nil, callback:, link:, item:, channel_uid:, configuration:, context:| ... } ⇒ void

This method returns an undefined value.

Defines a new profile that can be applied to item channel links.

Examples:

Vetoing a command

profile(:veto_closing_shades) do |event, item:, command:|
  next false if command&.down?

  true
end

items.build do
  rollershutter_item "MyShade" do
    channel "thing:rollershutter", profile: "ruby:veto_closing_shades"
  end
end
# can also be referenced from an `.items` file:
# Rollershutter MyShade { channel="thing:rollershutter"[profile="ruby:veto_closing_shades"] }

Overriding units from a binding

profile(:set_uom) do |event, callback:, configuration:, state:, command:|
  unless configuration["unit"]
    logger.warn("Unit configuration not provided for set_uom profile")
     next true
  end

  case event
  when :state_from_handler
    next true unless state.is_a?(DecimalType) || state.is_a?(QuantityType) # what is it then?!

    state = state.to_d if state.is_a?(QuantityType) # ignore the units if QuantityType was given
    callback.send_update(state | configuration["unit"])
    false
  when :command_from_item
    # strip the unit from the command, as the binding likely can't handle it
    next true unless command.is_a?(QuantityType)

    callback.handle_command(DecimalType.new(command.to_d))
    false
  else
    true # pass other events through as normal
  end
end
# can also be referenced from an `.items` file:
# Number:Temperature MyTempWithNonUnitValueFromBinding "I prefer Celsius [%d °C]" { channel="something_that_returns_F"[profile="ruby:set_uom", unit="°F"] }

Parameters:

  • id (String, Symbol)

    The id for the profile.

Yields:

  • (event, command: nil, state: nil, callback:, link:, item:, channel_uid:, configuration:, context:)

    All keyword params are optional. Any that aren't defined won't be passed.

Yield Parameters:

  • event (:command_from_item, :state_from_item, :command_from_handler, :state_from_handler)

    The event that needs to be processed.

  • command (Command, nil)

    The command being sent for :command_from_item and :command_from_handler events.

  • state (State, nil)

    The state being sent for :state_from_item and :state_from_handler events.

  • callback (Core::Things::ProfileCallback)

    The callback to be used to customize the action taken.

  • link (Core::Things::ItemChannelLink)

    The link between the item and the channel, including its configuration.

  • item (Item)

    The linked item.

  • channel_uid (Core::Things::ChannelUID)

    The linked channel.

  • configuration (Hash)

    The profile configuration.

  • context (org.openhab.core.thing.profiles.ProfileContext)

    The profile context.

Yield Returns:

  • (Boolean)

    Return true from the block in order to have default processing.

Raises:

  • (ArgumentError)

See Also:

  • org.openhab.thing.Profile
  • org.openhab.thing.StateProfile


145
146
147
148
149
150
151
152
153
154
# File 'lib/openhab/dsl.rb', line 145

def profile(id, &block)
  raise ArgumentError, "Block is required" unless block

  id = id.to_s
  uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)

  ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
    Core::ProfileFactory.instance.register(uid, block)
  end
end

.provider(*providers, **providers_by_type) { ... } ⇒ Object

Sets the implicit provider(s) for operations inside the block.

Examples:

provider(metadata: :persistent) do
  Switch1.metadata[:last_status_from_service] = status
end

provider!(metadata: { last_status_from_service: :persistent }, Switch2: :persistent)
Switch1.metadata[:last_status_from_service] = status # this will persist in JSONDB
Switch1.metadata[:homekit] = "Lightbulb" # this will be removed when the script is deleted
Switch2.metadata[:homekit] = "Lightbulb" # this will persist in JSONDB

Parameters:

  • providers (Core::Provider, org.openhab.core.common.registry.ManagedProvider, :persistent, :transient, Proc)

    An explicit provider to use. If it's a Core::Provider, the type will be inferred automatically. Otherwise it's applied to all types.

  • providers_by_type (Hash)

    A list of providers by type. Type can be :items, :metadata, :things, :links, an Item applying the provider to all metadata on that item, or a String or Symbol applying the provider to all metadata of that namespace.

    The provider can be a Provider, :persistent, :transient, or a Proc returning one of those types. When the Proc is called for metadata elements, the Core::Items::Metadata::Hash will be passed as an argument. Therefore it's recommended that you use a Proc, not a Lambda, for permissive argument matching.

Yields:

  • [] The block will be executed using the specified provider(s).

Returns:

  • (Object)

    the result of the block

Raises:

  • (ArgumentError)

See Also:



807
808
809
810
811
812
813
814
815
816
# File 'lib/openhab/dsl.rb', line 807

def provider(*providers, **providers_by_type)
  raise ArgumentError, "You must give a block to set the provider for the duration of" unless block_given?

  begin
    old_providers = provider!(*providers, **providers_by_type)
    yield
  ensure
    Thread.current[:openhab_providers] = old_providers
  end
end

.provider!(things: nil, items: nil, metadata: nil, links: nil, **metadata_items) ⇒ void

Note:

This method is only intended for use at the top level of rule scripts. If it's used within library methods, or hap-hazardly within rules, things can get very confusing because the prior state won't be properly restored.

This method returns an undefined value.

Permanently set the implicit provider(s) for this thread.

provider! calls are cumulative - additional calls will not erase the effects of previous calls unless they are for the same provider type.

Parameters:

  • providers (Core::Provider, org.openhab.core.common.registry.ManagedProvider, :persistent, :transient, Proc)

    An explicit provider to use. If it's a Core::Provider, the type will be inferred automatically. Otherwise it's applied to all types.

  • providers_by_type (Hash)

    A list of providers by type. Type can be :items, :metadata, :things, :links, an Item applying the provider to all metadata on that item, or a String or Symbol applying the provider to all metadata of that namespace.

    The provider can be a Provider, :persistent, :transient, or a Proc returning one of those types. When the Proc is called for metadata elements, the Core::Items::Metadata::Hash will be passed as an argument. Therefore it's recommended that you use a Proc, not a Lambda, for permissive argument matching.

See Also:



849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
# File 'lib/openhab/dsl.rb', line 849

def provider!(*providers, **providers_by_type)
  thread_providers = Thread.current[:openhab_providers] ||= {}
  old_providers = thread_providers.dup

  providers.each do |provider|
    case provider
    when Core::Provider
      thread_providers[provider.class.type] = provider
    when org.openhab.core.common.registry.ManagedProvider
      type = provider.type
      unless type
        raise ArgumentError, "#{provider.inspect} is for objects which are not supported by openhab-scripting"
      end

      thread_providers[type] = provider
    when Proc,
      :transient,
      :persistent
      Core::Provider::KNOWN_TYPES.each do |known_type|
        thread_providers[known_type] = provider
      end
    when Hash
      # non-symbols can't be used as kwargs, so Item keys show up as a separate hash here
      # just merge it in, and allow it to be handled below
      providers_by_type.merge!(provider)
    else
      raise ArgumentError, "#{provider.inspect} is not a valid provider"
    end
  end

  providers_by_type.each do |type, provider|
    case provider
    when Proc,
      org.openhab.core.common.registry.ManagedProvider,
      :transient,
      :persistent,
      nil
      nil
    else
      raise ArgumentError, "#{provider.inspect} is not a valid provider"
    end

    case type
    when :items, :metadata, :things, :links
      if provider.is_a?(org.openhab.core.common.registry.ManagedProvider) && provider.type != type
        raise ArgumentError, "#{provider.inspect} is not a provider for #{type}"
      end

      thread_providers[type] = provider
    when Symbol, String
      (thread_providers[:metadata_namespaces] ||= {})[type.to_s] = provider
    when Item
      (thread_providers[:metadata_items] ||= {})[type.name] = provider
    else
      raise ArgumentError, "#{type.inspect} is not provider type"
    end
  end

  old_providers
end

.rule(name = nil, **kwargs) {|rule| ... } ⇒ Core::Rules::Rule?

Create a new rule

The rule must have at least one trigger and one execution block. To create a "script" without any triggers, use #script.

Examples:

require "openhab/dsl"

rule "name" do
  <one or more triggers>
  <one or more execution blocks>
  <zero or more guards>
end

Parameters:

  • name (String) (defaults to: nil)

    The rule name

Yields:

Yield Parameters:

  • rule (Rules::BuilderDSL)

    Optional parameter to access the rule configuration from within execution blocks and guards.

Returns:

See Also:



59
60
61
# File 'lib/openhab/dsl.rb', line 59

def rule(name = nil, **kwargs, &block)
  rules.build { rule(name, **kwargs, &block) }
end

.rulesCore::Rules::Registry

Fetches all rules from the rule registry.



176
177
178
# File 'lib/openhab/dsl.rb', line 176

def rules
  Core::Rules::Registry.instance
end

.scene(name = nil, id: nil, **kwargs) { ... } ⇒ Core::Rules::Rule

Create a new scene

A scene is a rule with no triggers. It can be called by various other actions, such as the Run Rules action.

Parameters:

  • name (String) (defaults to: nil)

    A descriptive name

  • id (String) (defaults to: nil)

    The script's ID

Yields:

  • [] Block executed when the script is executed.

Returns:



64
65
66
# File 'lib/openhab/dsl.rb', line 64

def scene(name = nil, id: nil, **kwargs, &block)
  rules.build { scene(name, id: id, **kwargs, &block) }
end

.script(name = nil, id: nil, **kwargs) { ... } ⇒ Core::Rules::Rule

Create a new script

A script is a rule with no triggers. It can be called by various other actions, such as the Run Rules action.

Parameters:

  • name (String) (defaults to: nil)

    A descriptive name

  • id (String) (defaults to: nil)

    The script's ID

Yields:

  • [] Block executed when the script is executed.

Returns:



69
70
71
# File 'lib/openhab/dsl.rb', line 69

def script(name = nil, id: nil, **kwargs, &block)
  rules.build { script(name, id: id, **kwargs, &block) }
end

.shared_cacheCore::ValueCache

Note:

Only the sharedCache is exposed in Ruby. For a private cache, simply use an instance variable. See Instance Variables.

Note:

Because every script or UI rule gets its own JRuby engine instance, you cannot rely on being able to access Ruby objects between them. Only objects that implement a Java interface that's part of Java or openHAB Core (such as Hash implements java.util.Map, or other basic datatypes) can be reliably stored and accessed from the shared cache. Likewise, you can use the cache to access data from other scripting languages, but they'll be all but useless in Ruby. It's best to stick to simple data types. If you're having troubles, serializing to_json before storing may help.

ValueCache is the interface used to access a shared cache available between scripts and/or rule executions.

While ValueCache looks somewhat like a Hash, it does not support iteration of the contained elements. So it's limited to strictly storing, fetching, or removing known elements.

Shared caches are not persisted between openHAB restarts. And in fact, if all scripts are unloaded that reference a particular key, that key is removed.

Examples:

shared_cache.compute_if_absent(:execution_count) { 0 }
shared_cache[:execution_count] += 1

Returns:

  • (Core::ValueCache)

    the cache shared among all scripts and UI rules in all languages.

See Also:



167
168
169
# File 'lib/openhab/dsl.rb', line 167

def shared_cache
  $sharedCache
end

.sitemapsCore::Sitemaps::Provider



218
219
220
# File 'lib/openhab/dsl.rb', line 218

def sitemaps
  Core::Sitemaps::Provider.instance
end

.store_states(*items) ⇒ Core::Items::StateStorage

Store states of supplied items

Takes one or more items and returns a map {Item => State} with the current state of each item. It is implemented by calling openHAB's events.storeStates().

Examples:

states = store_states Item1, Item2
...
states.restore

With a block

store_states Item1, Item2 do
  ...
end # the states will be restored here

Parameters:

  • items (Item)

    Items to store states of.

Returns:



545
546
547
548
549
550
551
552
# File 'lib/openhab/dsl.rb', line 545

def store_states(*items)
  states = Core::Items::StateStorage.from_items(*items)
  if block_given?
    yield
    states.restore
  end
  states
end

.thingsCore::Things::Registry

Get all things known to openHAB

Examples:

things.each { |thing| logger.info("Thing: #{thing.uid}")}
logger.info("Thing: #{things['astro:sun:home'].uid}")
homie_things = things.select { |t| t.thing_type_uid == "mqtt:homie300" }
zwave_things = things.select { |t| t.binding_id == "zwave" }
homeseer_dimmers = zwave_things.select { |t| t.thing_type_uid.id == "homeseer_hswd200_00_000" }
things['zwave:device:512:node90'].uid.bridge_ids # => ["512"]
things['mqtt:topic:4'].uid.bridge_ids # => []

Returns:



236
237
238
# File 'lib/openhab/dsl.rb', line 236

def things
  Core::Things::Registry.instance
end

.throttle_for(duration, id: nil, &block) ⇒ void

This method returns an undefined value.

Rate-limits block executions by delaying calls and only executing the last call within the given duration.

When throttle_for is called, it will hold from executing the block and start a fixed timer for the given duration. Should more calls occur during this time, keep holding and once the wait time is over, execute the block.

throttle_for will execute the block after it had waited for the given duration, regardless of how frequently throttle_for was called. 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.

Parameters:

  • id (Object) (defaults to: nil)

    ID to associate with this call.

  • block (Block)

    The block to be throttled.

  • duration (Duration)

    The minimum amount of time to wait inbetween rule executions.

See Also:



489
490
491
# File 'lib/openhab/dsl.rb', line 489

def throttle_for(duration, id: nil, &block)
  debounce(for: duration, id: id, &block)
end

.timersTimerManager

Provides access to timers created by after

Returns:



244
245
246
# File 'lib/openhab/dsl.rb', line 244

def timers
  TimerManager.instance
end

.transform(type, function, value) ⇒ String

Applies a transformation of a given type with some function to a value.

Examples:

Run a transformation

transform(:map, "myfan.map", 0)

Parameters:

  • type (String, Symbol)

    The transformation type, e.g. REGEX or MAP

  • function (String, Symbol)

    The function to call. This value depends on the transformation type

  • value (String)

    The value to apply the transformation to

Returns:

  • (String)

    the transformed value, or the original value if an error occurred



494
495
496
# File 'lib/openhab/dsl.rb', line 494

def transform(type, function, value)
  Transformation.transform(type, function, value)
end

.unit(*units) { ... } ⇒ Object .unit(dimension) ⇒ javax.measure.Unit

Sets the implicit unit(s) for operations inside the block.

Overloads:

  • .unit(*units) { ... } ⇒ Object

    Sets the implicit unit(s) for this thread such that classes operating inside the block can perform automatic conversions to the supplied unit for QuantityType.

    To facilitate conversion of multiple dimensioned and dimensionless numbers the unit block may be used. The unit block attempts to do the right thing based on the mix of dimensioned and dimensionless items within the block. Specifically all dimensionless items are converted to the supplied unit, except when they are used for multiplication or division.

    Examples:

    Arithmetic Operations Between QuantityType and Numeric

    # Number:Temperature NumberC = 23 °C
    # Number:Temperature NumberF = 70 °F
    # Number Dimensionless = 2
    unit('°F') { NumberC.state - NumberF.state < 4 }                                      # => true
    unit('°F') { NumberC.state - 24 | '°C' < 4 }                                          # => true
    unit('°F') { (24 | '°C') - NumberC.state < 4 }                                        # => true
    unit('°C') { NumberF.state - 20 < 2 }                                                 # => true
    unit('°C') { NumberF.state - Dimensionless.state }                                    # => 19.11 °C
    unit('°C') { NumberF.state - Dimensionless.state < 20 }                               # => true
    unit('°C') { Dimensionless.state + NumberC.state == 25 }                              # => true
    unit('°C') { 2 + NumberC.state == 25 }                                                # => true
    unit('°C') { Dimensionless.state * NumberC.state == 46 }                              # => true
    unit('°C') { 2 * NumberC.state == 46 }                                                # => true
    unit('°C') { ( (2 * (NumberF.state + NumberC.state) ) / Dimensionless.state ) < 45 }  # => true
    unit('°C') { [NumberC.state, NumberF.state, Dimensionless.state].min }                # => 2

    Commands and Updates inside a unit block

    unit('°F') { NumberC << 32 }; NumberC.state                                           # => 0 °C
    # Equivalent to
    NumberC << "32 °F"
    # or
    NumberC << 32 | "°F"

    Specifying Multiple Units

    unit("°C", "kW") do
      TemperatureItem.update("50 °F")
      TemperatureItem.state < 20          # => true. TemperatureItem.state < 20 °C
      PowerUsage.update("3000 W")
      PowerUsage.state < 10               # => true. PowerUsage.state < 10 kW
    end

    Parameters:

    Yields:

    • [] The block will be executed in the context of the specified unit(s).

    Returns:

    • (Object)

      the result of the block

  • .unit(dimension) ⇒ javax.measure.Unit

    Returns The current unit for the thread of the specified dimensions.

    Examples:

    unit(SIUnits::METRE.dimension) # => ImperialUnits::FOOT

    Parameters:

    Returns:

Yields:

Raises:

  • (ArgumentError)


720
721
722
723
724
725
726
727
728
729
730
731
732
733
# File 'lib/openhab/dsl.rb', line 720

def unit(*units)
  if units.length == 1 && units.first.is_a?(javax.measure.Dimension)
    return Thread.current[:openhab_units]&.[](units.first)
  end

  raise ArgumentError, "You must give a block to set the unit for the duration of" unless block_given?

  begin
    old_units = unit!(*units)
    yield
  ensure
    Thread.current[:openhab_units] = old_units
  end
end

.unit!(*units) ⇒ Hash<javax.measure.Dimension=>javax.measure.Unit> .unit!Hash<javax.measure.Dimension=>javax.measure.Unit>

Note:

This method is only intended for use at the top level of rule scripts. If it's used within library methods, or hap-hazardly within rules, things can get very confusing because the prior state won't be properly restored.

Permanently sets the implicit unit(s) for this thread

unit! calls are cumulative - additional calls will not erase the effects of previous calls unless they are for the same dimension.

Overloads:

  • .unit!(*units) ⇒ Hash<javax.measure.Dimension=>javax.measure.Unit>

    Examples:

    Set several defaults at once

    unit!("°F", "ft", "lbs")
    (50 | "°F") == 50 # => true

    Calls are cumulative

    unit!("°F")
    unit!("ft")
    (50 | "°F") == 50 # => true
    (2 | "yd") == 6 # => true

    Subsequent calls override the same dimension from previous calls

    unit!("yd")
    unit!("ft")
    (2 | "yd") == 6 # => true

    Parameters:

  • .unit!Hash<javax.measure.Dimension=>javax.measure.Unit>

    Clear all unit settings

    Examples:

    Clear all unit settings

    unit!("ft")
    unit!
    (2 | "yd") == 6 # => false

Returns:



776
777
778
779
780
781
782
783
784
785
# File 'lib/openhab/dsl.rb', line 776

def unit!(*units)
  units = units.each_with_object({}) do |unit, r|
    unit = org.openhab.core.types.util.UnitUtils.parse_unit(unit) if unit.is_a?(String)
    r[unit.dimension] = unit
  end

  old_units = Thread.current[:openhab_units] || {}
  Thread.current[:openhab_units] = units.empty? ? {} : old_units.merge(units)
  old_units
end