Skip to content
This repository has been archived by the owner on Feb 14, 2023. It is now read-only.

Commit

Permalink
add only_if feature
Browse files Browse the repository at this point in the history
  • Loading branch information
jimtng committed Dec 18, 2022
1 parent bf819f3 commit ec136f2
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ here is a non-exhaustive list of significant departures from the original gem:
* {OpenHAB::DSL::Rules::BuilderDSL#on_load #on_load} supports delay
* Various Ephemeris methods on {ZonedDateTime}.
* {OpenHAB::DSL::Rules::BuilderDSL#debounce debounce guard} for file-based rules and {OpenHAB::DSL.debounce debounce DSL method} for UI rules
* {OpenHAB::DSL::Rules::BuilderDSL#only_if only_if guard} for file-based rules and {OpenHAB::DSL.only_if only_if DSL method} for UI rules

### Bug Fixes

Expand Down
18 changes: 18 additions & 0 deletions lib/openhab/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,24 @@ def debounce(for:, leading: false, idle_time: nil, &block)
end
end

#
# Limit how often the given block executes to the specified interval.
#
# {only_every} allows block executions when the previous one occurred at or before the specified interval.
# This is a simplified version of a leading edge {debounce}.
#
# @param [Duration,:second,:minute,:hour,:day] interval The block is not allowed to run more often than
# the specified interval.
# @return [void]
#
# @see Debouncer Debouncer class
# @see debounce
#
def only_every(interval, &block)
interval = 1.send(interval) if %i[second minute hour day].include?(interval)
debounce(for: interval, leading: true, &block)
end

#
# Store states of supplied items
#
Expand Down
35 changes: 33 additions & 2 deletions lib/openhab/dsl/rules/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ def dependencies(trigger_types = %i[changed updated])
# # instead of waiting, the bell will not ring a second time.
# # They will have to stop and wait for at least 30 seconds, and after 1 minute
# # before pressing the button again in order for the bell to ring a second time.
# rule do
# rule "Limit excessive doorbell press" do
# updated DoorBell_Button, to: "single"
# debounce for: 1.minute, leading: true, idle_time: 30.seconds
# run { Audio.play_stream "doorbell.mp3" }
Expand All @@ -604,13 +604,44 @@ def dependencies(trigger_types = %i[changed updated])
# @see OpenHAB::DSL.debounce DSL.debounce method
#
def debounce(for: 1.second, leading: false, idle_time: nil)
raise ArgumentError, "Debounce can only be specified once" if @debounce_settings
raise ArgumentError, "Debounce guard can only be specified once" if @debounce_settings

interval = binding.local_variable_get(:for)
# This hash structure must match the parameter signature for Debouncer.new
@debounce_settings = { for: interval, leading: leading, idle_time: idle_time }
end

#
# Limit how often the rule executes to the specified interval.
#
# This guard only allows rule executions when the previous one occurred at or before the specified interval.
# This is a simplified version of a leading edge {debounce} guard.
#
# @param [Duration,:second,:minute,:hour,:day] interval The rule is not allowed to run more often than
# the specified interval.
# @return [void]
#
# @example Only allow executions every 10 minutes
# rule "Aircon Vent Control" do
# changed BedRoom_Temperature
# only_every 10.minutes
# run do
# # Adjust BedRoom_Aircon_Vent
# end
# end
#
# @example 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
#
def only_every(interval)
interval = 1.send(interval) if %i[second minute hour day].include?(interval)
debounce(for: interval, leading: true)
end

# @!endgroup

# @!visibility private
Expand Down
33 changes: 33 additions & 0 deletions spec/openhab/dsl/rules/builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,39 @@ def self.test_it(range, expected)
expect(captured).to eq [5]
end
end

describe "#only_every" do
let(:item) { items.build { number_item "Item1" } }

it "works" do
counter = 0
rule do
updated Item1
only_every :second
run { counter += 1 }
end

30.times do
item.update 1
time_travel_and_execute_timers(100.ms)
end
expect(counter).to eq 3
end

it "accepts symbolic duration" do
def test_symbol(symbol)
rule do
updated Item1
only_every symbol
run { nil }
end
end

%i[second minute hour day].each do |sym|
expect { test_symbol sym }.not_to raise_exception
end
end
end
end
# rubocop:enable RSpec/InstanceVariable
end
Expand Down
17 changes: 17 additions & 0 deletions spec/openhab/dsl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@
end
end

describe "#only_every" do
it "works" do
counter = 0
20.times do
only_every(100.ms) { counter += 1 }
time_travel_and_execute_timers(10.ms)
end
expect(counter).to eq 2
end

it "accepts symbolic duration" do
%i[second minute hour day].each do |sym|
expect { only_every(sym) { nil } }.not_to raise_exception
end
end
end

describe "#unit" do
it "converts all units and numbers to specific unit for all operations" do
c = 23 | "°C"
Expand Down

0 comments on commit ec136f2

Please sign in to comment.