openhab-jrubyscripting 5.0.0.rc9 → 5.0.0.rc11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/openhab/core/actions/audio.rb +47 -0
- data/lib/openhab/core/actions/ephemeris.rb +39 -0
- data/lib/openhab/core/actions/exec.rb +51 -0
- data/lib/openhab/core/actions/http.rb +80 -0
- data/lib/openhab/core/actions/ping.rb +30 -0
- data/lib/openhab/core/actions/transformation.rb +32 -0
- data/lib/openhab/core/actions/voice.rb +36 -0
- data/lib/openhab/core/actions.rb +23 -120
- data/lib/openhab/core/{events → dto}/item_channel_link.rb +1 -4
- data/lib/openhab/core/{events → dto}/thing.rb +10 -12
- data/lib/openhab/core/dto.rb +11 -0
- data/lib/openhab/core/entity_lookup.rb +1 -1
- data/lib/openhab/core/events/abstract_event.rb +1 -0
- data/lib/openhab/core/events/abstract_item_registry_event.rb +36 -0
- data/lib/openhab/core/events/abstract_thing_registry_event.rb +40 -0
- data/lib/openhab/core/events/item_command_event.rb +1 -1
- data/lib/openhab/core/events/item_state_changed_event.rb +6 -6
- data/lib/openhab/core/events/item_state_event.rb +6 -6
- data/lib/openhab/core/events/thing_status_info_event.rb +8 -6
- data/lib/openhab/core/items/generic_item.rb +2 -1
- data/lib/openhab/core/items/persistence.rb +52 -18
- data/lib/openhab/core/items/player_item.rb +1 -1
- data/lib/openhab/core/items/proxy.rb +20 -14
- data/lib/openhab/core/items/registry.rb +2 -0
- data/lib/openhab/core/items.rb +3 -3
- data/lib/openhab/core/profile_factory.rb +3 -1
- data/lib/openhab/core/proxy.rb +125 -0
- data/lib/openhab/core/things/links/provider.rb +1 -1
- data/lib/openhab/core/things/proxy.rb +8 -0
- data/lib/openhab/core/types/date_time_type.rb +2 -1
- data/lib/openhab/core/types/decimal_type.rb +1 -1
- data/lib/openhab/core/types/un_def_type.rb +2 -2
- data/lib/openhab/core/value_cache.rb +1 -1
- data/lib/openhab/core_ext/ephemeris.rb +53 -0
- data/lib/openhab/core_ext/java/class.rb +1 -1
- data/lib/openhab/core_ext/java/duration.rb +25 -1
- data/lib/openhab/core_ext/java/local_date.rb +2 -0
- data/lib/openhab/core_ext/java/month_day.rb +2 -0
- data/lib/openhab/core_ext/java/zoned_date_time.rb +85 -0
- data/lib/openhab/core_ext/ruby/date.rb +2 -0
- data/lib/openhab/core_ext/ruby/date_time.rb +1 -0
- data/lib/openhab/core_ext/ruby/time.rb +1 -0
- data/lib/openhab/dsl/debouncer.rb +259 -0
- data/lib/openhab/dsl/items/builder.rb +4 -2
- data/lib/openhab/dsl/items/timed_command.rb +31 -13
- data/lib/openhab/dsl/rules/automation_rule.rb +28 -21
- data/lib/openhab/dsl/rules/builder.rb +357 -37
- data/lib/openhab/dsl/rules/guard.rb +12 -54
- data/lib/openhab/dsl/rules/name_inference.rb +11 -0
- data/lib/openhab/dsl/rules/property.rb +3 -4
- data/lib/openhab/dsl/rules/terse.rb +4 -1
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +5 -6
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +1 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +19 -31
- data/lib/openhab/dsl/rules/triggers/watch/watch.rb +1 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +22 -30
- data/lib/openhab/dsl/things/builder.rb +1 -1
- data/lib/openhab/dsl/thread_local.rb +1 -0
- data/lib/openhab/dsl/version.rb +1 -1
- data/lib/openhab/dsl.rb +224 -3
- data/lib/openhab/rspec/hooks.rb +5 -2
- data/lib/openhab/rspec/karaf.rb +7 -0
- data/lib/openhab/rspec/mocks/instance_method_stasher.rb +22 -0
- data/lib/openhab/rspec/mocks/space.rb +23 -0
- data/lib/openhab/rspec/openhab/core/actions.rb +16 -4
- data/lib/openhab/rspec/openhab/core/items/proxy.rb +1 -13
- data/lib/openhab/rspec/suspend_rules.rb +1 -14
- data/lib/openhab/yard/base_helper.rb +19 -0
- data/lib/openhab/yard/code_objects/group_object.rb +9 -3
- data/lib/openhab/yard/coderay.rb +17 -0
- data/lib/openhab/yard/handlers/jruby/base.rb +10 -1
- data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +3 -0
- data/lib/openhab/yard/html_helper.rb +49 -15
- data/lib/openhab/yard/markdown_helper.rb +135 -0
- data/lib/openhab/yard.rb +6 -0
- metadata +36 -4
@@ -30,8 +30,8 @@ module OpenHAB
|
|
30
30
|
# @param [Config] config Rule configuration
|
31
31
|
#
|
32
32
|
# Constructor sets a number of variables, no further decomposition necessary
|
33
|
+
#
|
33
34
|
def initialize(config)
|
34
|
-
# Metrics disabled because only setters are called or defaults set.
|
35
35
|
super()
|
36
36
|
set_name(config.name)
|
37
37
|
set_description(config.description)
|
@@ -50,6 +50,8 @@ module OpenHAB
|
|
50
50
|
@thread_locals = ThreadLocal.persist
|
51
51
|
@cleanup_hooks = Set.new
|
52
52
|
@listener = nil
|
53
|
+
debounce_settings = config.debounce_settings || { for: nil }
|
54
|
+
@debouncer = Debouncer.new(**debounce_settings)
|
53
55
|
end
|
54
56
|
|
55
57
|
#
|
@@ -58,30 +60,34 @@ module OpenHAB
|
|
58
60
|
# @param [java.util.Map] mod map provided by openHAB rules engine
|
59
61
|
# @param [java.util.Map] inputs map provided by openHAB rules engine containing event and other information
|
60
62
|
#
|
61
|
-
#
|
62
63
|
def execute(mod = nil, inputs = nil)
|
64
|
+
execute!(mod, inputs)
|
65
|
+
end
|
66
|
+
|
67
|
+
# @!visibility private
|
68
|
+
def on_removal(listener)
|
69
|
+
@cleanup_hooks << listener
|
70
|
+
listen_for_removal unless @listener
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# This method gets called in rspec's SuspendRules as well
|
76
|
+
def execute!(mod, inputs)
|
63
77
|
ThreadLocal.thread_local(**@thread_locals) do
|
64
78
|
logger.trace { "Execute called with mod (#{mod&.to_string}) and inputs (#{inputs.inspect})" }
|
65
79
|
logger.trace { "Event details #{inputs["event"].inspect}" } if inputs&.key?("event")
|
66
80
|
trigger_conditions(inputs).process(mod: mod, inputs: inputs) do
|
67
81
|
event = extract_event(inputs)
|
68
|
-
process_queue(create_queue(event), mod, event)
|
82
|
+
@debouncer.call { process_queue(create_queue(event), mod, event) }
|
69
83
|
end
|
70
84
|
rescue Exception => e
|
71
|
-
raise if defined?(::RSpec)
|
85
|
+
raise if defined?(::RSpec) && ::RSpec.current_example.example_group.propagate_exceptions?
|
72
86
|
|
73
87
|
@run_context.send(:logger).log_exception(e)
|
74
88
|
end
|
75
89
|
end
|
76
90
|
|
77
|
-
# @!visibility private
|
78
|
-
def on_removal(listener)
|
79
|
-
@cleanup_hooks << listener
|
80
|
-
listen_for_removal unless @listener
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
91
|
def cleanup
|
86
92
|
@cleanup_hooks.each(&:cleanup)
|
87
93
|
end
|
@@ -180,17 +186,18 @@ module OpenHAB
|
|
180
186
|
def check_guards(event:)
|
181
187
|
return true if @guard.nil?
|
182
188
|
|
183
|
-
|
184
|
-
return true if @between.nil?
|
185
|
-
|
189
|
+
unless @between.nil?
|
186
190
|
now = Time.now
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
logger.trace("Skipped execution of rule '#{name}' because of guard #{@guard}")
|
191
|
+
unless @between.cover?(now)
|
192
|
+
logger.trace("Skipped execution of rule '#{name}' because the current time #{now} " \
|
193
|
+
"is not between #{@between.begin} and #{@between.end}")
|
194
|
+
return false
|
195
|
+
end
|
193
196
|
end
|
197
|
+
|
198
|
+
return true if @guard.should_run?(event)
|
199
|
+
|
200
|
+
logger.trace("Skipped execution of rule '#{name}' because of guard #{@guard}")
|
194
201
|
false
|
195
202
|
end
|
196
203
|
|
@@ -139,6 +139,9 @@ module OpenHAB
|
|
139
139
|
# @return [Array] Of trigger definitions as passed in Ruby
|
140
140
|
attr_reader :ruby_triggers
|
141
141
|
|
142
|
+
# @!visibility private
|
143
|
+
attr_reader :debounce_settings
|
144
|
+
|
142
145
|
# @!visibility private
|
143
146
|
Run = Struct.new(:block)
|
144
147
|
|
@@ -380,6 +383,30 @@ module OpenHAB
|
|
380
383
|
#
|
381
384
|
prop :enabled
|
382
385
|
|
386
|
+
#
|
387
|
+
# Returns all {Item Items} (or {GroupItem::Members GroupItem::Members}) referenced
|
388
|
+
# by the specified trigger types in this rule.
|
389
|
+
#
|
390
|
+
# @param [Symbol, Array<Symbol>] trigger_types Trigger types to search for dependencies
|
391
|
+
# @return [Array<Item, GroupItem::Members>]
|
392
|
+
#
|
393
|
+
# @example Ensure all dependencies have a state when executing a rule
|
394
|
+
# rule do |rule|
|
395
|
+
# changed Item1, Item2, Item3
|
396
|
+
# only_if { rule.dependencies.all?(&:state?) }
|
397
|
+
# run { FormulaItem.update(Item3.state - (Item1.state + Item2.state)) }
|
398
|
+
# end
|
399
|
+
#
|
400
|
+
def dependencies(trigger_types = %i[changed updated])
|
401
|
+
trigger_types = Array.wrap(trigger_types)
|
402
|
+
|
403
|
+
ruby_triggers.flat_map do |t|
|
404
|
+
next [] unless trigger_types.include?(t.first)
|
405
|
+
|
406
|
+
t[1].select { |i| i.is_a?(Item) || i.is_a?(GroupItem::Members) }
|
407
|
+
end.uniq
|
408
|
+
end
|
409
|
+
|
383
410
|
# @!group Guards
|
384
411
|
# Guards exist to only permit rules to run if certain conditions are
|
385
412
|
# satisfied. Think of these as declarative `if` statements that keep
|
@@ -388,8 +415,8 @@ module OpenHAB
|
|
388
415
|
#
|
389
416
|
# ### Guard Combination
|
390
417
|
#
|
391
|
-
#
|
392
|
-
#
|
418
|
+
# Multiple guards can be used on the same rule. All must be satisfied
|
419
|
+
# for a rule to execute.
|
393
420
|
#
|
394
421
|
# @example
|
395
422
|
# rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is OFF and Door is CLOSED" do
|
@@ -403,7 +430,7 @@ module OpenHAB
|
|
403
430
|
#
|
404
431
|
# @!method between(range)
|
405
432
|
#
|
406
|
-
# Only execute rule if current time is between supplied time ranges.
|
433
|
+
# Only execute rule if the current time is between the supplied time ranges.
|
407
434
|
#
|
408
435
|
# If the range is of strings, it will be parsed to an appropriate time class.
|
409
436
|
#
|
@@ -464,7 +491,7 @@ module OpenHAB
|
|
464
491
|
#
|
465
492
|
# @!method only_if
|
466
493
|
#
|
467
|
-
#
|
494
|
+
# Allows rule execution when the block's result is true and prevents it when it's false.
|
468
495
|
#
|
469
496
|
# @yieldparam [Core::Events::AbstractEvent] event The event data that is about to trigger the rule.
|
470
497
|
# @yieldreturn [Boolean] A value indicating if the rule should run.
|
@@ -493,15 +520,13 @@ module OpenHAB
|
|
493
520
|
# end
|
494
521
|
#
|
495
522
|
prop_array(:only_if) do |item|
|
496
|
-
|
497
|
-
raise ArgumentError, "Object passed to only_if must be a proc"
|
498
|
-
end
|
523
|
+
raise ArgumentError, "Object passed to only_if must be a proc" unless item.is_a?(Proc)
|
499
524
|
end
|
500
525
|
|
501
526
|
#
|
502
527
|
# @!method not_if
|
503
528
|
#
|
504
|
-
#
|
529
|
+
# Prevents execution of rules when the block's result is true and allows it when it's true.
|
505
530
|
#
|
506
531
|
# @yieldparam [Core::Events::AbstractEvent] event The event data that is about to trigger the rule.
|
507
532
|
# @yieldreturn [Boolean] A value indicating if the rule should _not_ run.
|
@@ -523,9 +548,187 @@ module OpenHAB
|
|
523
548
|
# end
|
524
549
|
#
|
525
550
|
prop_array(:not_if) do |item|
|
526
|
-
|
527
|
-
|
528
|
-
|
551
|
+
raise ArgumentError, "Object passed to not_if must be a proc" unless item.is_a?(Proc)
|
552
|
+
end
|
553
|
+
|
554
|
+
# rubocop:disable Layout/LineLength
|
555
|
+
|
556
|
+
#
|
557
|
+
# Waits until triggers have stopped firing for a period of time before executing the rule.
|
558
|
+
#
|
559
|
+
# It ignores triggers that are "bouncing around" (rapidly firing) by ignoring
|
560
|
+
# them until they have quiesced (stopped triggering for a while).
|
561
|
+
#
|
562
|
+
# ## Comparison Table
|
563
|
+
# | Guard | Triggers Immediately | Description |
|
564
|
+
# | -------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
565
|
+
# | {debounce_for} | No | Waits until there is a minimum interval between triggers. |
|
566
|
+
# | {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. |
|
567
|
+
# | {only_every} | Yes | Rate-limits the executions to a minimum interval. Immediately executes the first trigger, then ignores subsequent triggers for the period. |
|
568
|
+
#
|
569
|
+
# ## Timing Diagram
|
570
|
+
# The following timing diagram illustrates the difference between {debounce_for},
|
571
|
+
# {throttle_for}, and {only_every} guards:
|
572
|
+
#
|
573
|
+
# ```
|
574
|
+
# TIME INDEX ===> 1 1 2 2 3 3 4 4
|
575
|
+
# 0 5 0 5 0 5 0 5 0 5
|
576
|
+
# Triggers : "X.X...X...X..XX.X.X....X.XXXXXXXXXXX....X....."
|
577
|
+
# debounce_for 5 : "|......................X.|..............X....."
|
578
|
+
# debounce_for 5..5 : "|....X|....X.|....X....|....X|....X|....X....X"
|
579
|
+
# debounce_for 5..6 : "|.....X...|.....X.|....X.|.....X|.....X.|....X"
|
580
|
+
# debounce_for 5..7 : "|......X..|......X|....X.|......X|......X....."
|
581
|
+
# debounce_for 5..8 : "|.......X.|.......X....|.......X|.......X....."
|
582
|
+
# debounce_for 5..20: "|...................X..|................X....."
|
583
|
+
# # throttle_for will fire every 5 intervals after the "first" trigger
|
584
|
+
# throttle_for 5 : "|....X|....X.|....X....|....X|....X|....X....."
|
585
|
+
# only_every 5 : "X.....X......X....X....X....X....X......X....."
|
586
|
+
#
|
587
|
+
# Triggers : "X.X...X...X..XX.X.X..X...XXXXXXXXXXX.X..X.X..."
|
588
|
+
# debounce_for 5..44: "|...........................................X."
|
589
|
+
#
|
590
|
+
# # Notice above, triggers keep firing with intervals less than 5, so
|
591
|
+
# # debouncer keeps waiting, but puts a stop at 44 (the end of range).
|
592
|
+
# ```
|
593
|
+
#
|
594
|
+
# @param [Duration,Range] debounce_time The minimum interval between two consecutive
|
595
|
+
# triggers before the rules are allowed to run.
|
596
|
+
#
|
597
|
+
# When specified just as a Duration or an endless range, it sets the minimum interval
|
598
|
+
# between two consecutive triggers before rules are executed. It will
|
599
|
+
# wait endlessly unless this condition is met or an end of range was specified.
|
600
|
+
#
|
601
|
+
# When the end of the range is specified, it sets the maximum amount of time to wait
|
602
|
+
# from the first trigger before the rule will execute, even when triggers continue
|
603
|
+
# to occur more frequently than the minimum interval.
|
604
|
+
#
|
605
|
+
# When an equal beginning and ending values are given, it will behave just like
|
606
|
+
# {throttle_for}.
|
607
|
+
#
|
608
|
+
# @return [void]
|
609
|
+
#
|
610
|
+
# @see throttle_for
|
611
|
+
# @see only_every
|
612
|
+
#
|
613
|
+
# @example Wait until item stopped changing for at least 1 minute before running the rule
|
614
|
+
# rule do
|
615
|
+
# changed Item1
|
616
|
+
# debounce_for 1.minute
|
617
|
+
# run { ... }
|
618
|
+
# end
|
619
|
+
#
|
620
|
+
# @example Alert when door is open for a while
|
621
|
+
# # Note: When combined with a state check (only_if), this becomes functionally
|
622
|
+
# # equivalent to the changed duration feature.
|
623
|
+
# rule "Door alert" do
|
624
|
+
# changed Door_State
|
625
|
+
# debounce_for 10.minutes
|
626
|
+
# only_if { Door_State.open? }
|
627
|
+
# run { notify("The Door has been open for 10 minutes!") }
|
628
|
+
# end
|
629
|
+
#
|
630
|
+
def debounce_for(debounce_time)
|
631
|
+
idle_time = debounce_time.is_a?(Range) ? debounce_time.begin : debounce_time
|
632
|
+
debounce(for: debounce_time, idle_time: idle_time)
|
633
|
+
end
|
634
|
+
# rubocop:enable Layout/LineLength
|
635
|
+
|
636
|
+
#
|
637
|
+
# Rate-limits rule executions by delaying triggers and executing the last
|
638
|
+
# trigger within the given duration.
|
639
|
+
#
|
640
|
+
# When a new trigger occurs, it will hold the execution and start a fixed timer for
|
641
|
+
# the given duration. Should more triggers occur during this time, keep holding
|
642
|
+
# and once the wait time is over, execute the latest trigger.
|
643
|
+
#
|
644
|
+
# {throttle_for} will execute rules after it had waited
|
645
|
+
# for the given duration, regardless of how frequently the triggers were occuring.
|
646
|
+
# In contrast, {debounce_for} will wait until there is a minimum interval
|
647
|
+
# between two triggers.
|
648
|
+
#
|
649
|
+
# {throttle_for} is ideal in situations where regular status updates need to be made
|
650
|
+
# for frequently changing values. It is also useful when a rule responds to triggers
|
651
|
+
# from multiple related items that are updated at around the same time. Instead of
|
652
|
+
# executing the rule multiple times, {throttle_for} will wait for a pre-set amount
|
653
|
+
# of time since the first group of triggers occurred before executing the rule.
|
654
|
+
#
|
655
|
+
# @param [Duration] duration The minimum amount of time to wait inbetween rule
|
656
|
+
# executions.
|
657
|
+
#
|
658
|
+
# @return [void]
|
659
|
+
#
|
660
|
+
# @see debounce_for
|
661
|
+
# @see only_every
|
662
|
+
#
|
663
|
+
# @example Perform calculations from multiple items
|
664
|
+
# rule "Update Power Summary " do |rule|
|
665
|
+
# changed Power_From_Solar, Power_Load, Power_From_Grid
|
666
|
+
# throttle_for 1.second
|
667
|
+
# only_if { rule.dependencies.all?(&:state?) } # make sure all items have a state
|
668
|
+
# run do
|
669
|
+
# msg = []
|
670
|
+
# msg << Power_Load.state.negate.to_unit("kW").format("Load: %.2f %unit%")
|
671
|
+
# msg << Power_From_Solar.state.to_unit("kW").format("PV: %.2f %unit%")
|
672
|
+
# if Power_From_Grid.positive?
|
673
|
+
# msg << Power_From_Grid.state.to_unit("kW").format("From Grid: %.1f %unit%")
|
674
|
+
# else
|
675
|
+
# msg << Power_From_Grid.state.negate.to_unit("kW").format("To Grid: %.1f %unit%")
|
676
|
+
# end
|
677
|
+
# Power_Summary.update(msg.join(", "))
|
678
|
+
# end
|
679
|
+
# end
|
680
|
+
#
|
681
|
+
def throttle_for(duration)
|
682
|
+
debounce(for: duration)
|
683
|
+
end
|
684
|
+
|
685
|
+
#
|
686
|
+
# Executes the rule then ignores subsequent triggers for a given duration.
|
687
|
+
#
|
688
|
+
# Additional triggers that occur within the given duration after the rule execution
|
689
|
+
# will be ignored. This results in executions happening only at the specified interval or
|
690
|
+
# more.
|
691
|
+
#
|
692
|
+
# Unlike {throttle_for}, this guard will execute the rule as soon as a new trigger
|
693
|
+
# occurs instead of waiting for the specified duration. This is ideal for triggers
|
694
|
+
# such as a door bell where the rule should run as soon as a new trigger is detected
|
695
|
+
# but ignore subsequent triggers if they occur too soon after.
|
696
|
+
#
|
697
|
+
# @param [Duration,:second,:minute,:hour,:day] interval The period during which
|
698
|
+
# subsequent triggers are ignored.
|
699
|
+
# @return [void]
|
700
|
+
#
|
701
|
+
# @example Only allow executions every 10 minutes or more
|
702
|
+
# rule "Aircon Vent Control" do
|
703
|
+
# changed BedRoom_Temperature
|
704
|
+
# only_every 10.minutes
|
705
|
+
# run do
|
706
|
+
# # Adjust BedRoom_Aircon_Vent
|
707
|
+
# end
|
708
|
+
# end
|
709
|
+
#
|
710
|
+
# @example Run only on the first update and ignore subsequent triggers for the next minute
|
711
|
+
# # They can keep pressing the door bell as often as they like,
|
712
|
+
# # but the bell will only ring at most once every minute
|
713
|
+
# rule do
|
714
|
+
# updated DoorBell_Button, to: "single"
|
715
|
+
# only_every 1.minute
|
716
|
+
# run { Audio.play_stream "doorbell.mp3" }
|
717
|
+
# end
|
718
|
+
#
|
719
|
+
# @example Using symbolic duration
|
720
|
+
# rule "Display update" do
|
721
|
+
# updated Power_Usage
|
722
|
+
# only_every :minute
|
723
|
+
# run { Power_Usage_Display.update "Current power usage: #{Power_Usage.average_since(1.minute.ago)}" }
|
724
|
+
# end
|
725
|
+
#
|
726
|
+
# @see debounce_for
|
727
|
+
# @see throttle_for
|
728
|
+
#
|
729
|
+
def only_every(interval)
|
730
|
+
interval = 1.send(interval) if %i[second minute hour day].include?(interval)
|
731
|
+
debounce(for: interval, leading: true)
|
529
732
|
end
|
530
733
|
|
531
734
|
# @!endgroup
|
@@ -541,7 +744,8 @@ module OpenHAB
|
|
541
744
|
@rule_triggers = RuleTriggers.new
|
542
745
|
@caller = caller_binding.eval "self"
|
543
746
|
@ruby_triggers = []
|
544
|
-
@
|
747
|
+
@on_load = nil
|
748
|
+
@debounce_settings = nil
|
545
749
|
enabled(true)
|
546
750
|
tags([])
|
547
751
|
end
|
@@ -680,8 +884,7 @@ module OpenHAB
|
|
680
884
|
# end
|
681
885
|
def channel_linked(attach: nil)
|
682
886
|
@ruby_triggers << [:channel_linked]
|
683
|
-
|
684
|
-
eventTypes: "ItemChannelLinkAddedEvent", attach: attach)
|
887
|
+
event("openhab/links/*/added", types: "ItemChannelLinkAddedEvent", attach: attach)
|
685
888
|
end
|
686
889
|
|
687
890
|
#
|
@@ -702,8 +905,7 @@ module OpenHAB
|
|
702
905
|
# end
|
703
906
|
def channel_unlinked(attach: nil)
|
704
907
|
@ruby_triggers << [:channel_linked]
|
705
|
-
|
706
|
-
eventTypes: "ItemChannelLinkRemovedEvent", attach: attach)
|
908
|
+
event(topic: "openhab/links/*/removed", types: "ItemChannelLinkRemovedEvent", attach: attach)
|
707
909
|
end
|
708
910
|
|
709
911
|
#
|
@@ -976,6 +1178,8 @@ module OpenHAB
|
|
976
1178
|
# and on subsequent reloads on file modifications.
|
977
1179
|
# This is useful to perform initialization routines, especially when combined with other triggers.
|
978
1180
|
#
|
1181
|
+
# @param [Duration] delay The amount of time to wait before executing the rule.
|
1182
|
+
# When nil, execute immediately.
|
979
1183
|
# @param [Object] attach Object to be attached to the trigger
|
980
1184
|
# @return [void]
|
981
1185
|
#
|
@@ -993,12 +1197,12 @@ module OpenHAB
|
|
993
1197
|
# run { Security_Lights.on }
|
994
1198
|
# end
|
995
1199
|
#
|
996
|
-
def on_load(attach: nil)
|
997
|
-
# prevent overwriting @
|
998
|
-
raise ArgumentError, "on_load can only be used once within a rule" if @
|
1200
|
+
def on_load(delay: nil, attach: nil)
|
1201
|
+
# prevent overwriting @on_load
|
1202
|
+
raise ArgumentError, "on_load can only be used once within a rule" if @on_load
|
999
1203
|
|
1000
|
-
@
|
1001
|
-
attachments[@
|
1204
|
+
@on_load = { module: SecureRandom.uuid, delay: delay }
|
1205
|
+
attachments[@on_load[:module]] = attach
|
1002
1206
|
end
|
1003
1207
|
|
1004
1208
|
#
|
@@ -1165,6 +1369,61 @@ module OpenHAB
|
|
1165
1369
|
end
|
1166
1370
|
end
|
1167
1371
|
|
1372
|
+
#
|
1373
|
+
# Creates an item added trigger
|
1374
|
+
#
|
1375
|
+
# @param [Object] attach object to be attached to the trigger
|
1376
|
+
# @return [void]
|
1377
|
+
#
|
1378
|
+
# @example
|
1379
|
+
# rule "item added" do
|
1380
|
+
# item_added
|
1381
|
+
# run do |event|
|
1382
|
+
# logger.info("#{event.item.name} added.")
|
1383
|
+
# end
|
1384
|
+
# end
|
1385
|
+
def item_added(attach: nil)
|
1386
|
+
@ruby_triggers << [:item_added]
|
1387
|
+
event("openhab/items/*/added", types: "ItemAddedEvent", attach: attach)
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
#
|
1391
|
+
# Creates an item removed trigger
|
1392
|
+
#
|
1393
|
+
# @param [Object] attach object to be attached to the trigger
|
1394
|
+
# @return [void]
|
1395
|
+
#
|
1396
|
+
# @example
|
1397
|
+
# rule "item removed" do
|
1398
|
+
# item_removed
|
1399
|
+
# run do |event|
|
1400
|
+
# logger.info("#{event.item.name} removed.")
|
1401
|
+
# end
|
1402
|
+
# end
|
1403
|
+
def item_removed(attach: nil)
|
1404
|
+
@ruby_triggers << [:item_removed]
|
1405
|
+
event("openhab/items/*/removed", types: "ItemRemovedEvent", attach: attach)
|
1406
|
+
end
|
1407
|
+
|
1408
|
+
#
|
1409
|
+
# Creates an item updated trigger
|
1410
|
+
#
|
1411
|
+
# @param [Object] attach object to be attached to the trigger
|
1412
|
+
# @return [void]
|
1413
|
+
#
|
1414
|
+
# @example
|
1415
|
+
# rule "item updated" do
|
1416
|
+
# item_updated
|
1417
|
+
# run do |event|
|
1418
|
+
# logger.info("#{event.item.name} updated.")
|
1419
|
+
# end
|
1420
|
+
# end
|
1421
|
+
#
|
1422
|
+
def item_updated(attach: nil)
|
1423
|
+
@ruby_triggers << [:item_updated]
|
1424
|
+
event("openhab/items/*/updated", types: "ItemUpdatedEvent", attach: attach)
|
1425
|
+
end
|
1426
|
+
|
1168
1427
|
#
|
1169
1428
|
# Creates a thing added trigger
|
1170
1429
|
#
|
@@ -1180,8 +1439,7 @@ module OpenHAB
|
|
1180
1439
|
# end
|
1181
1440
|
def thing_added(attach: nil)
|
1182
1441
|
@ruby_triggers << [:thing_added]
|
1183
|
-
|
1184
|
-
eventTypes: "ThingAddedEvent", attach: attach)
|
1442
|
+
event("openhab/things/*/added", types: "ThingAddedEvent", attach: attach)
|
1185
1443
|
end
|
1186
1444
|
|
1187
1445
|
#
|
@@ -1199,8 +1457,7 @@ module OpenHAB
|
|
1199
1457
|
# end
|
1200
1458
|
def thing_removed(attach: nil)
|
1201
1459
|
@ruby_triggers << [:thing_removed]
|
1202
|
-
|
1203
|
-
eventTypes: "ThingRemovedEvent", attach: attach)
|
1460
|
+
event("openhab/things/*/removed", types: "ThingRemovedEvent", attach: attach)
|
1204
1461
|
end
|
1205
1462
|
|
1206
1463
|
#
|
@@ -1210,17 +1467,39 @@ module OpenHAB
|
|
1210
1467
|
# @return [void]
|
1211
1468
|
#
|
1212
1469
|
# @example
|
1213
|
-
#
|
1214
|
-
#
|
1215
|
-
#
|
1216
|
-
#
|
1217
|
-
#
|
1218
|
-
#
|
1470
|
+
# rule "thing updated" do
|
1471
|
+
# thing_updated
|
1472
|
+
# run do |event|
|
1473
|
+
# logger.info("#{event.thing.uid} updated.")
|
1474
|
+
# end
|
1475
|
+
# end
|
1219
1476
|
#
|
1220
1477
|
def thing_updated(attach: nil)
|
1221
|
-
@ruby_triggers << [:
|
1222
|
-
|
1223
|
-
|
1478
|
+
@ruby_triggers << [:thing_updated]
|
1479
|
+
event("openhab/things/*/updated", types: "ThingUpdatedEvent", attach: attach)
|
1480
|
+
end
|
1481
|
+
|
1482
|
+
#
|
1483
|
+
# Creates a trigger on events coming through the event bus
|
1484
|
+
#
|
1485
|
+
# @param [String] topic The topic to trigger on; can contain the wildcard `*`.
|
1486
|
+
# @param [String, nil] source The sender of the event to trigger on.
|
1487
|
+
# Default does not filter on source.
|
1488
|
+
# @param [String, Array<String>, nil] types Only subscribe to certain event types.
|
1489
|
+
# Default does not filter on event types.
|
1490
|
+
# @return [void]
|
1491
|
+
#
|
1492
|
+
# @example
|
1493
|
+
# rule "thing updated" do
|
1494
|
+
# event("openhab/things/*/updated", types: "ThingUpdatedEvent")
|
1495
|
+
# run do |event|
|
1496
|
+
# logger.info("#{event.thing.uid} updated")
|
1497
|
+
# end
|
1498
|
+
# end
|
1499
|
+
#
|
1500
|
+
def event(topic, source: nil, types: nil, attach: nil)
|
1501
|
+
types = types.join(",") if types.is_a?(Enumerable)
|
1502
|
+
trigger("core.GenericEventTrigger", eventTopic: topic, eventSource: source, eventTypes: types, attach: attach)
|
1224
1503
|
end
|
1225
1504
|
|
1226
1505
|
#
|
@@ -1445,7 +1724,7 @@ module OpenHAB
|
|
1445
1724
|
#<OpenHAB::DSL::Rules::Builder: #{uid}
|
1446
1725
|
triggers=#{triggers.inspect},
|
1447
1726
|
run blocks=#{run.inspect},
|
1448
|
-
on_load=#{!@
|
1727
|
+
on_load=#{!@on_load.nil?},
|
1449
1728
|
Trigger Conditions=#{trigger_conditions.inspect},
|
1450
1729
|
Trigger UIDs=#{triggers.map(&:id).inspect},
|
1451
1730
|
Attachments=#{attachments.inspect}
|
@@ -1468,12 +1747,26 @@ module OpenHAB
|
|
1468
1747
|
added_rule.actions.first.configuration.put("type", "application/x-ruby")
|
1469
1748
|
added_rule.actions.first.configuration.put("script", script) if script
|
1470
1749
|
|
1471
|
-
rule.execute(nil, { "module" =>
|
1750
|
+
process_on_load { |module_id| rule.execute(nil, { "module" => module_id }) }
|
1751
|
+
|
1472
1752
|
added_rule
|
1473
1753
|
end
|
1474
1754
|
|
1475
1755
|
private
|
1476
1756
|
|
1757
|
+
# Calls the on_load block, with a delay if specified
|
1758
|
+
# @yield block to execute on load time
|
1759
|
+
# @yieldparam [String] module The module ID that identifies this on_load event
|
1760
|
+
def process_on_load
|
1761
|
+
return unless @on_load
|
1762
|
+
|
1763
|
+
if @on_load[:delay]
|
1764
|
+
after(@on_load[:delay]) { yield @on_load[:module] }
|
1765
|
+
else
|
1766
|
+
yield @on_load[:module]
|
1767
|
+
end
|
1768
|
+
end
|
1769
|
+
|
1477
1770
|
# delegate to the caller's logger
|
1478
1771
|
def logger
|
1479
1772
|
@caller.send(:logger)
|
@@ -1505,7 +1798,7 @@ module OpenHAB
|
|
1505
1798
|
# @return [true,false] True if rule has triggers, false otherwise
|
1506
1799
|
#
|
1507
1800
|
def triggers?
|
1508
|
-
@
|
1801
|
+
!(@on_load.nil? && triggers.empty?)
|
1509
1802
|
end
|
1510
1803
|
|
1511
1804
|
#
|
@@ -1534,6 +1827,33 @@ module OpenHAB
|
|
1534
1827
|
provider.add(unmanaged_rule)
|
1535
1828
|
unmanaged_rule
|
1536
1829
|
end
|
1830
|
+
|
1831
|
+
#
|
1832
|
+
# Prevents or delays executions of rules to within a specified interval.
|
1833
|
+
# Debounce handling is done after the from/to/command types are filtered, but before only_if/not_if
|
1834
|
+
# guards.
|
1835
|
+
#
|
1836
|
+
# For a more detailed timing diagram, see {Debouncer}.
|
1837
|
+
#
|
1838
|
+
# Note the trailing edge debouncer delays the triggers so that they were postponed for the given interval after
|
1839
|
+
# the first detection of the trigger.
|
1840
|
+
#
|
1841
|
+
# @param (see Debouncer#initialize)
|
1842
|
+
#
|
1843
|
+
# @return [void]
|
1844
|
+
#
|
1845
|
+
# @see Debouncer Debouncer class
|
1846
|
+
# @see OpenHAB::DSL.debounce DSL.debounce method
|
1847
|
+
# @see only_every
|
1848
|
+
#
|
1849
|
+
# @!visibility private
|
1850
|
+
def debounce(for: 1.second, leading: false, idle_time: nil)
|
1851
|
+
raise ArgumentError, "Debounce guard can only be specified once" if @debounce_settings
|
1852
|
+
|
1853
|
+
interval = binding.local_variable_get(:for)
|
1854
|
+
# This hash structure must match the parameter signature for Debouncer.new
|
1855
|
+
@debounce_settings = { for: interval, leading: leading, idle_time: idle_time }
|
1856
|
+
end
|
1537
1857
|
end
|
1538
1858
|
end
|
1539
1859
|
end
|