Module: OpenHAB::DSL::Items::TimedCommand

Included in:
Core::Items::GenericItem, Core::Items::GroupItem
Defined in:
lib/openhab/dsl/items/timed_command.rb

Overview

Extensions for Item to implement timed commands

All items have an implicit timer associated with them, enabling to easily set an item into a specific state for a specified duration and then at the expiration of that duration have the item automatically change to another state. These timed commands are reentrant, meaning if the same timed command is triggered while an outstanding timed command exist, that timed command will be rescheduled rather than creating a distinct timed command.

Timed commands are initiated by using the ‘for:’ argument with the command. This is available on both the ‘command’ method and any command-specific methods, e.g. Core::Items::SwitchItem#on.

The timer will be cancelled, and the item’s state will not be changed to the on_expire state if:

  • The item receives any command within the timed command duration.

  • The item is updated to a different state, even if it is then updated back to the same state.

For example, if you have a Switch on a timer and another rule sends a command to that item, even when it’s commanded to the same state, the timer will be automatically canceled.

Sending a different duration (for:) value for the timed command will reschedule the timed command for that new duration.

Defined Under Namespace

Classes: TimedCommandDetails

Instance Method Summary collapse

Instance Method Details

#command(command, for: nil, on_expire: nil, only_when_ensured: false, **kwargs) {|timed_command| ... } ⇒ self

Note:

If a block is provided, and the timer is canceled because the item changed state while it was waiting, the block will still be executed. The timed command can be reinstated by calling #resume or #reschedule.

If the timer expired, the timed command can be rescheduled from inside the block by calling #reschedule.

Be sure to check #expired? and/or #cancelled? to determine why the block was called.

Sends command to an item for specified duration, then on timer expiration sends the expiration command to the item

Examples:

Switch.command(ON, for: 5.minutes)
Switch.on for: 5.minutes
Dimmer.on for: 5.minutes, on_expire: 50
Dimmer.on(for: 5.minutes) { |event| Dimmer.off if Light.on? }

Only start a timed command if the command was ensured

rule "Motion Detected" do
  received_command Motion_Sensor, command: ON
  run do
    # Start the timer only if the command was ensured
    # i.e. the light was off before the command was sent
    # but if the timer was already started, extend it
    FrontPorchLight.ensure.on for: 30.minutes, only_when_ensured: true
  end
end

Parameters:

  • command (Command)

    to send to object

  • for (Duration) (defaults to: nil)

    duration for item to be in command state

  • on_expire (Command) (defaults to: nil)

    Command to send when duration expires

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

    if true, only start the timed command if the command was ensured

Yields:

  • If a block is provided, ‘on_expire` is ignored and the block is expected to set the item to the desired state or carry out some other action.

Yield Parameters:

Returns:

  • (self)


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/openhab/dsl/items/timed_command.rb', line 152

def command(command, for: nil, on_expire: nil, only_when_ensured: false, **kwargs, &block)
  duration = binding.local_variable_get(:for)
  return super(command, **kwargs) unless duration

  on_expire = block if block

  create_ensured_timed_command = proc do
    on_expire ||= default_on_expire(command)
    if only_when_ensured
      DSL.ensure_states do
        create_timed_command(command, duration: duration, on_expire: on_expire) if super(command, **kwargs)
      end
    else
      super(command, **kwargs)
      create_timed_command(command, duration: duration, on_expire: on_expire)
    end
  end

  TimedCommand.timed_commands.compute(self) do |_key, timed_command_details|
    if timed_command_details.nil?
      # no prior timed command
      create_ensured_timed_command.call
    else
      timed_command_details.mutex.synchronize do
        if timed_command_details.resolution
          # timed command that finished, but hadn't removed itself from the map yet
          # (it doesn't do so under the mutex to prevent a deadlock).
          # just create a new one
          create_ensured_timed_command.call
        else
          # timed command still pending; reset it
          logger.trace { "Outstanding Timed Command #{timed_command_details} encountered - rescheduling" }
          timed_command_details.on_expire = on_expire unless on_expire.nil?
          timed_command_details.timer.reschedule(duration)
          # disable the cancel rule while we send the new command
          DSL.rules[timed_command_details.rule_uid].disable
          super(command, **kwargs) # This returns nil when "ensured"
          DSL.rules[timed_command_details.rule_uid].enable
          timed_command_details
        end
      end
    end
  end

  self
end