openhab-jrubyscripting 5.0.0.rc10 → 5.0.0.rc12
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/date_time_item.rb +3 -2
- data/lib/openhab/core/items/generic_item.rb +92 -1
- data/lib/openhab/core/items/item.rb +9 -8
- data/lib/openhab/core/items/metadata/hash.rb +1 -1
- data/lib/openhab/core/items/metadata/namespace_hash.rb +10 -2
- data/lib/openhab/core/items/metadata/provider.rb +2 -2
- data/lib/openhab/core/items/persistence.rb +99 -21
- 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 +12 -1
- data/lib/openhab/core/items/state_storage.rb +2 -2
- data/lib/openhab/core/items.rb +3 -3
- data/lib/openhab/core/profile_factory.rb +3 -1
- data/lib/openhab/core/proxy.rb +130 -0
- data/lib/openhab/core/registry.rb +12 -2
- data/lib/openhab/core/rules.rb +1 -1
- data/lib/openhab/core/things/links/provider.rb +39 -1
- data/lib/openhab/core/things/proxy.rb +8 -0
- data/lib/openhab/core/things/registry.rb +4 -0
- data/lib/openhab/core/timer.rb +3 -19
- data/lib/openhab/core/types/date_time_type.rb +3 -2
- 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.rb +3 -3
- 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 +27 -1
- data/lib/openhab/core_ext/java/local_date.rb +17 -7
- data/lib/openhab/core_ext/java/local_time.rb +13 -3
- data/lib/openhab/core_ext/java/month.rb +1 -1
- data/lib/openhab/core_ext/java/month_day.rb +15 -3
- data/lib/openhab/core_ext/java/period.rb +1 -1
- data/lib/openhab/core_ext/java/temporal_amount.rb +1 -1
- data/lib/openhab/core_ext/java/time.rb +5 -1
- data/lib/openhab/core_ext/java/zoned_date_time.rb +100 -2
- data/lib/openhab/core_ext/ruby/date.rb +4 -2
- data/lib/openhab/core_ext/ruby/date_time.rb +1 -0
- data/lib/openhab/core_ext/ruby/numeric.rb +6 -1
- 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 +29 -14
- data/lib/openhab/dsl/items/timed_command.rb +31 -13
- data/lib/openhab/dsl/rules/automation_rule.rb +30 -44
- data/lib/openhab/dsl/rules/builder.rb +404 -39
- 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 +251 -14
- data/lib/openhab/rspec/helpers.rb +3 -2
- data/lib/openhab/rspec/hooks.rb +6 -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/mocks/timer.rb +33 -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/rspec.rb +9 -0
- 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
@@ -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
|
|
@@ -355,7 +358,7 @@ module OpenHAB
|
|
355
358
|
#
|
356
359
|
# Set the rule's tags.
|
357
360
|
#
|
358
|
-
# @param [String,
|
361
|
+
# @param [String, Symbol, Semantics::Tag] tags A list of tags to assign to the rule.
|
359
362
|
# @return [void]
|
360
363
|
#
|
361
364
|
# @example
|
@@ -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
|
#
|
@@ -899,7 +1101,10 @@ module OpenHAB
|
|
899
1101
|
# :saturday,
|
900
1102
|
# :sunday] value
|
901
1103
|
# When to execute rule.
|
902
|
-
# @param [LocalTime, String, nil] at What time of day to execute rule
|
1104
|
+
# @param [LocalTime, String, Core::Items::DateTimeItem, nil] at What time of day to execute rule
|
1105
|
+
# If `value` is `:day`, `at` can be a {Core::Items::DateTimeItem DateTimeItem}, and
|
1106
|
+
# the trigger will run every day at the (time only portion of) current state of the
|
1107
|
+
# item. If the item is {NULL} or {UNDEF}, the trigger will not run.
|
903
1108
|
# @param [Object] attach Object to be attached to the trigger
|
904
1109
|
# @return [void]
|
905
1110
|
#
|
@@ -955,11 +1160,23 @@ module OpenHAB
|
|
955
1160
|
# run { logger.info "Happy Valentine's Day!" }
|
956
1161
|
# end
|
957
1162
|
#
|
1163
|
+
# @example
|
1164
|
+
# rule "Every day at sunset" do
|
1165
|
+
# every :day, at: Sunset_Time
|
1166
|
+
# run { logger.info "It's getting dark" }
|
1167
|
+
# end
|
1168
|
+
#
|
958
1169
|
def every(value, at: nil, attach: nil)
|
959
1170
|
return every(java.time.MonthDay.parse(value), at: at, attach: attach) if value.is_a?(String)
|
960
1171
|
|
961
1172
|
@ruby_triggers << [:every, value, { at: at }]
|
962
1173
|
|
1174
|
+
if value == :day && at.is_a?(Item)
|
1175
|
+
raise ArgumentError, "Attachments are not supported with dynamic datetime triggers" unless attach.nil?
|
1176
|
+
|
1177
|
+
return trigger("timer.DateTimeTrigger", itemName: at.name, timeOnly: true)
|
1178
|
+
end
|
1179
|
+
|
963
1180
|
cron_expression = case value
|
964
1181
|
when Symbol then Cron.from_symbol(value, at)
|
965
1182
|
when Duration then Cron.from_duration(value, at)
|
@@ -976,6 +1193,8 @@ module OpenHAB
|
|
976
1193
|
# and on subsequent reloads on file modifications.
|
977
1194
|
# This is useful to perform initialization routines, especially when combined with other triggers.
|
978
1195
|
#
|
1196
|
+
# @param [Duration] delay The amount of time to wait before executing the rule.
|
1197
|
+
# When nil, execute immediately.
|
979
1198
|
# @param [Object] attach Object to be attached to the trigger
|
980
1199
|
# @return [void]
|
981
1200
|
#
|
@@ -993,12 +1212,12 @@ module OpenHAB
|
|
993
1212
|
# run { Security_Lights.on }
|
994
1213
|
# end
|
995
1214
|
#
|
996
|
-
def on_load(attach: nil)
|
997
|
-
# prevent overwriting @
|
998
|
-
raise ArgumentError, "on_load can only be used once within a rule" if @
|
1215
|
+
def on_load(delay: nil, attach: nil)
|
1216
|
+
# prevent overwriting @on_load
|
1217
|
+
raise ArgumentError, "on_load can only be used once within a rule" if @on_load
|
999
1218
|
|
1000
|
-
@
|
1001
|
-
attachments[@
|
1219
|
+
@on_load = { module: SecureRandom.uuid, delay: delay }
|
1220
|
+
attachments[@on_load[:module]] = attach
|
1002
1221
|
end
|
1003
1222
|
|
1004
1223
|
#
|
@@ -1165,6 +1384,61 @@ module OpenHAB
|
|
1165
1384
|
end
|
1166
1385
|
end
|
1167
1386
|
|
1387
|
+
#
|
1388
|
+
# Creates an item added trigger
|
1389
|
+
#
|
1390
|
+
# @param [Object] attach object to be attached to the trigger
|
1391
|
+
# @return [void]
|
1392
|
+
#
|
1393
|
+
# @example
|
1394
|
+
# rule "item added" do
|
1395
|
+
# item_added
|
1396
|
+
# run do |event|
|
1397
|
+
# logger.info("#{event.item.name} added.")
|
1398
|
+
# end
|
1399
|
+
# end
|
1400
|
+
def item_added(attach: nil)
|
1401
|
+
@ruby_triggers << [:item_added]
|
1402
|
+
event("openhab/items/*/added", types: "ItemAddedEvent", attach: attach)
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
#
|
1406
|
+
# Creates an item removed trigger
|
1407
|
+
#
|
1408
|
+
# @param [Object] attach object to be attached to the trigger
|
1409
|
+
# @return [void]
|
1410
|
+
#
|
1411
|
+
# @example
|
1412
|
+
# rule "item removed" do
|
1413
|
+
# item_removed
|
1414
|
+
# run do |event|
|
1415
|
+
# logger.info("#{event.item.name} removed.")
|
1416
|
+
# end
|
1417
|
+
# end
|
1418
|
+
def item_removed(attach: nil)
|
1419
|
+
@ruby_triggers << [:item_removed]
|
1420
|
+
event("openhab/items/*/removed", types: "ItemRemovedEvent", attach: attach)
|
1421
|
+
end
|
1422
|
+
|
1423
|
+
#
|
1424
|
+
# Creates an item updated trigger
|
1425
|
+
#
|
1426
|
+
# @param [Object] attach object to be attached to the trigger
|
1427
|
+
# @return [void]
|
1428
|
+
#
|
1429
|
+
# @example
|
1430
|
+
# rule "item updated" do
|
1431
|
+
# item_updated
|
1432
|
+
# run do |event|
|
1433
|
+
# logger.info("#{event.item.name} updated.")
|
1434
|
+
# end
|
1435
|
+
# end
|
1436
|
+
#
|
1437
|
+
def item_updated(attach: nil)
|
1438
|
+
@ruby_triggers << [:item_updated]
|
1439
|
+
event("openhab/items/*/updated", types: "ItemUpdatedEvent", attach: attach)
|
1440
|
+
end
|
1441
|
+
|
1168
1442
|
#
|
1169
1443
|
# Creates a thing added trigger
|
1170
1444
|
#
|
@@ -1180,8 +1454,7 @@ module OpenHAB
|
|
1180
1454
|
# end
|
1181
1455
|
def thing_added(attach: nil)
|
1182
1456
|
@ruby_triggers << [:thing_added]
|
1183
|
-
|
1184
|
-
eventTypes: "ThingAddedEvent", attach: attach)
|
1457
|
+
event("openhab/things/*/added", types: "ThingAddedEvent", attach: attach)
|
1185
1458
|
end
|
1186
1459
|
|
1187
1460
|
#
|
@@ -1199,8 +1472,7 @@ module OpenHAB
|
|
1199
1472
|
# end
|
1200
1473
|
def thing_removed(attach: nil)
|
1201
1474
|
@ruby_triggers << [:thing_removed]
|
1202
|
-
|
1203
|
-
eventTypes: "ThingRemovedEvent", attach: attach)
|
1475
|
+
event("openhab/things/*/removed", types: "ThingRemovedEvent", attach: attach)
|
1204
1476
|
end
|
1205
1477
|
|
1206
1478
|
#
|
@@ -1210,17 +1482,69 @@ module OpenHAB
|
|
1210
1482
|
# @return [void]
|
1211
1483
|
#
|
1212
1484
|
# @example
|
1213
|
-
#
|
1214
|
-
#
|
1215
|
-
#
|
1216
|
-
#
|
1217
|
-
#
|
1218
|
-
#
|
1485
|
+
# rule "thing updated" do
|
1486
|
+
# thing_updated
|
1487
|
+
# run do |event|
|
1488
|
+
# logger.info("#{event.thing.uid} updated.")
|
1489
|
+
# end
|
1490
|
+
# end
|
1219
1491
|
#
|
1220
1492
|
def thing_updated(attach: nil)
|
1221
|
-
@ruby_triggers << [:
|
1222
|
-
|
1223
|
-
|
1493
|
+
@ruby_triggers << [:thing_updated]
|
1494
|
+
event("openhab/things/*/updated", types: "ThingUpdatedEvent", attach: attach)
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
#
|
1498
|
+
# Creates a trigger on events coming through the event bus
|
1499
|
+
#
|
1500
|
+
# @param [String] topic The topic to trigger on; can contain the wildcard `*`.
|
1501
|
+
# @param [String, nil] source The sender of the event to trigger on.
|
1502
|
+
# Default does not filter on source.
|
1503
|
+
# @param [String, Array<String>, nil] types Only subscribe to certain event types.
|
1504
|
+
# Default does not filter on event types.
|
1505
|
+
# @return [void]
|
1506
|
+
#
|
1507
|
+
# @example
|
1508
|
+
# rule "thing updated" do
|
1509
|
+
# event("openhab/things/*/updated", types: "ThingUpdatedEvent")
|
1510
|
+
# run do |event|
|
1511
|
+
# logger.info("#{event.thing.uid} updated")
|
1512
|
+
# end
|
1513
|
+
# end
|
1514
|
+
#
|
1515
|
+
def event(topic, source: nil, types: nil, attach: nil)
|
1516
|
+
types = types.join(",") if types.is_a?(Enumerable)
|
1517
|
+
trigger("core.GenericEventTrigger", eventTopic: topic, eventSource: source, eventTypes: types, attach: attach)
|
1518
|
+
end
|
1519
|
+
|
1520
|
+
#
|
1521
|
+
# Creates a trigger based on the time stored in a {DateTimeItem}
|
1522
|
+
#
|
1523
|
+
# The trigger will dynamically update any time the state of the item
|
1524
|
+
# changes. If the item is {NULL} or {UNDEF}, the trigger will not run.
|
1525
|
+
#
|
1526
|
+
# @param [Item, String, Symbol] item The item (or it's name)
|
1527
|
+
# @return [void]
|
1528
|
+
#
|
1529
|
+
# @example
|
1530
|
+
# rule "say hello when the kids get home from school" do
|
1531
|
+
# at HomeFromSchool_Time
|
1532
|
+
# run do
|
1533
|
+
# KitchenEcho_TTS << "hi kids! how was school?"
|
1534
|
+
# end
|
1535
|
+
# end
|
1536
|
+
#
|
1537
|
+
# rule "set home from school time" do
|
1538
|
+
# on_load
|
1539
|
+
# every :day, at: "5:00am" do
|
1540
|
+
# run do
|
1541
|
+
# HomeFromSchool_Time.ensure.update(school_day? ? LocalTime.parse("3:30pm") : NULL)
|
1542
|
+
# end
|
1543
|
+
# end
|
1544
|
+
#
|
1545
|
+
def at(item)
|
1546
|
+
item = item.name if item.is_a?(Item)
|
1547
|
+
trigger("timer.DateTimeTrigger", itemName: item.to_s)
|
1224
1548
|
end
|
1225
1549
|
|
1226
1550
|
#
|
@@ -1445,7 +1769,7 @@ module OpenHAB
|
|
1445
1769
|
#<OpenHAB::DSL::Rules::Builder: #{uid}
|
1446
1770
|
triggers=#{triggers.inspect},
|
1447
1771
|
run blocks=#{run.inspect},
|
1448
|
-
on_load=#{!@
|
1772
|
+
on_load=#{!@on_load.nil?},
|
1449
1773
|
Trigger Conditions=#{trigger_conditions.inspect},
|
1450
1774
|
Trigger UIDs=#{triggers.map(&:id).inspect},
|
1451
1775
|
Attachments=#{attachments.inspect}
|
@@ -1468,12 +1792,26 @@ module OpenHAB
|
|
1468
1792
|
added_rule.actions.first.configuration.put("type", "application/x-ruby")
|
1469
1793
|
added_rule.actions.first.configuration.put("script", script) if script
|
1470
1794
|
|
1471
|
-
rule.execute(nil, { "module" =>
|
1795
|
+
process_on_load { |module_id| rule.execute(nil, { "module" => module_id }) }
|
1796
|
+
|
1472
1797
|
added_rule
|
1473
1798
|
end
|
1474
1799
|
|
1475
1800
|
private
|
1476
1801
|
|
1802
|
+
# Calls the on_load block, with a delay if specified
|
1803
|
+
# @yield block to execute on load time
|
1804
|
+
# @yieldparam [String] module The module ID that identifies this on_load event
|
1805
|
+
def process_on_load
|
1806
|
+
return unless @on_load
|
1807
|
+
|
1808
|
+
if @on_load[:delay]
|
1809
|
+
after(@on_load[:delay]) { yield @on_load[:module] }
|
1810
|
+
else
|
1811
|
+
yield @on_load[:module]
|
1812
|
+
end
|
1813
|
+
end
|
1814
|
+
|
1477
1815
|
# delegate to the caller's logger
|
1478
1816
|
def logger
|
1479
1817
|
@caller.send(:logger)
|
@@ -1505,7 +1843,7 @@ module OpenHAB
|
|
1505
1843
|
# @return [true,false] True if rule has triggers, false otherwise
|
1506
1844
|
#
|
1507
1845
|
def triggers?
|
1508
|
-
@
|
1846
|
+
!(@on_load.nil? && triggers.empty?)
|
1509
1847
|
end
|
1510
1848
|
|
1511
1849
|
#
|
@@ -1534,6 +1872,33 @@ module OpenHAB
|
|
1534
1872
|
provider.add(unmanaged_rule)
|
1535
1873
|
unmanaged_rule
|
1536
1874
|
end
|
1875
|
+
|
1876
|
+
#
|
1877
|
+
# Prevents or delays executions of rules to within a specified interval.
|
1878
|
+
# Debounce handling is done after the from/to/command types are filtered, but before only_if/not_if
|
1879
|
+
# guards.
|
1880
|
+
#
|
1881
|
+
# For a more detailed timing diagram, see {Debouncer}.
|
1882
|
+
#
|
1883
|
+
# Note the trailing edge debouncer delays the triggers so that they were postponed for the given interval after
|
1884
|
+
# the first detection of the trigger.
|
1885
|
+
#
|
1886
|
+
# @param (see Debouncer#initialize)
|
1887
|
+
#
|
1888
|
+
# @return [void]
|
1889
|
+
#
|
1890
|
+
# @see Debouncer Debouncer class
|
1891
|
+
# @see OpenHAB::DSL.debounce DSL.debounce method
|
1892
|
+
# @see only_every
|
1893
|
+
#
|
1894
|
+
# @!visibility private
|
1895
|
+
def debounce(for: 1.second, leading: false, idle_time: nil)
|
1896
|
+
raise ArgumentError, "Debounce guard can only be specified once" if @debounce_settings
|
1897
|
+
|
1898
|
+
interval = binding.local_variable_get(:for)
|
1899
|
+
# This hash structure must match the parameter signature for Debouncer.new
|
1900
|
+
@debounce_settings = { for: interval, leading: leading, idle_time: idle_time }
|
1901
|
+
end
|
1537
1902
|
end
|
1538
1903
|
end
|
1539
1904
|
end
|