openhab-jrubyscripting 5.0.0.rc9 → 5.0.0.rc11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|