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/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/proc.rb,
lib/openhab/dsl/rules/triggers/cron/cron_handler.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, Things Classes: Debouncer, TimerManager

Constant Summary collapse

VERSION =

Version of openHAB helper libraries

Returns:

  • (String)
"5.0.1"

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

Methods included from Core::EntityLookup

#method_missing

Dynamic Method Handling

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

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:



343
344
345
346
347
348
349
# File 'lib/openhab/dsl.rb', line 343

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:



373
374
375
376
377
378
379
# File 'lib/openhab/dsl.rb', line 373

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:



445
446
447
448
# File 'lib/openhab/dsl.rb', line 445

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.



594
595
596
597
598
599
600
# File 'lib/openhab/dsl.rb', line 594

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:



925
926
927
928
929
930
931
932
933
934
935
# File 'lib/openhab/dsl.rb', line 925

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:



950
951
952
# File 'lib/openhab/dsl.rb', line 950

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:



207
208
209
# File 'lib/openhab/dsl.rb', line 207

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:



508
509
510
511
# File 'lib/openhab/dsl.rb', line 508

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:



620
621
622
623
624
625
# File 'lib/openhab/dsl.rb', line 620

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:



642
643
644
645
646
# File 'lib/openhab/dsl.rb', line 642

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, 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, 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.send_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, link:, item:, channel_uid:, configuration:, context:)

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

Yield Parameters:

  • callback (Core::Things::ProfileCallback)

    The callback to be used to customize the action taken.

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

  • 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


139
140
141
142
143
144
145
146
147
148
# File 'lib/openhab/dsl.rb', line 139

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(*args, **kwargs) { ... } ⇒ 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 in the context of the specified unit(s).

Returns:

  • (Object)

    the result of the block

Raises:

  • (ArgumentError)

See Also:



796
797
798
799
800
801
802
803
804
805
# File 'lib/openhab/dsl.rb', line 796

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

  begin
    old_providers = provider!(*args, **kwargs)
    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:



838
839
840
841
842
843
844
845
846
847
848
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
# File 'lib/openhab/dsl.rb', line 838

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:



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

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

.rulesCore::Rules::Registry

Fetches all rules from the rule registry.



170
171
172
# File 'lib/openhab/dsl.rb', line 170

def rules
  Core::Rules::Registry.instance
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:



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

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



161
162
163
# File 'lib/openhab/dsl.rb', line 161

def shared_cache
  $sharedCache
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:



534
535
536
537
538
539
540
541
# File 'lib/openhab/dsl.rb', line 534

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:



225
226
227
# File 'lib/openhab/dsl.rb', line 225

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:



478
479
480
# File 'lib/openhab/dsl.rb', line 478

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

.timersTimerManager

Provides access to timers created by after

Returns:



233
234
235
# File 'lib/openhab/dsl.rb', line 233

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



483
484
485
# File 'lib/openhab/dsl.rb', line 483

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)


709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/openhab/dsl.rb', line 709

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:



765
766
767
768
769
770
771
772
773
774
# File 'lib/openhab/dsl.rb', line 765

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