openhab-jrubyscripting 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/lib/openhab/core/actions.rb +163 -0
  3. data/lib/openhab/core/entity_lookup.rb +144 -0
  4. data/lib/openhab/core/events/abstract_event.rb +17 -0
  5. data/lib/openhab/core/events/item_channel_link.rb +36 -0
  6. data/lib/openhab/core/events/item_command_event.rb +78 -0
  7. data/lib/openhab/core/events/item_event.rb +22 -0
  8. data/lib/openhab/core/events/item_state_changed_event.rb +52 -0
  9. data/lib/openhab/core/events/item_state_event.rb +51 -0
  10. data/lib/openhab/core/events/thing.rb +29 -0
  11. data/lib/openhab/core/events/thing_status_info_event.rb +53 -0
  12. data/lib/openhab/core/events.rb +10 -0
  13. data/lib/openhab/core/items/accepted_data_types.rb +29 -0
  14. data/lib/openhab/core/items/color_item.rb +52 -0
  15. data/lib/openhab/core/items/contact_item.rb +52 -0
  16. data/lib/openhab/core/items/date_time_item.rb +58 -0
  17. data/lib/openhab/core/items/dimmer_item.rb +148 -0
  18. data/lib/openhab/core/items/generic_item.rb +344 -0
  19. data/lib/openhab/core/items/group_item.rb +174 -0
  20. data/lib/openhab/core/items/image_item.rb +109 -0
  21. data/lib/openhab/core/items/location_item.rb +34 -0
  22. data/lib/openhab/core/items/metadata/hash.rb +390 -0
  23. data/lib/openhab/core/items/metadata/namespace_hash.rb +469 -0
  24. data/lib/openhab/core/items/metadata.rb +11 -0
  25. data/lib/openhab/core/items/number_item.rb +62 -0
  26. data/lib/openhab/core/items/numeric_item.rb +22 -0
  27. data/lib/openhab/core/items/persistence.rb +327 -0
  28. data/lib/openhab/core/items/player_item.rb +66 -0
  29. data/lib/openhab/core/items/proxy.rb +59 -0
  30. data/lib/openhab/core/items/registry.rb +66 -0
  31. data/lib/openhab/core/items/rollershutter_item.rb +68 -0
  32. data/lib/openhab/core/items/semantics/enumerable.rb +152 -0
  33. data/lib/openhab/core/items/semantics.rb +476 -0
  34. data/lib/openhab/core/items/state_storage.rb +53 -0
  35. data/lib/openhab/core/items/string_item.rb +28 -0
  36. data/lib/openhab/core/items/switch_item.rb +78 -0
  37. data/lib/openhab/core/items.rb +114 -0
  38. data/lib/openhab/core/lazy_array.rb +52 -0
  39. data/lib/openhab/core/profile_factory.rb +118 -0
  40. data/lib/openhab/core/script_handling.rb +55 -0
  41. data/lib/openhab/core/things/channel.rb +48 -0
  42. data/lib/openhab/core/things/channel_uid.rb +51 -0
  43. data/lib/openhab/core/things/item_channel_link.rb +33 -0
  44. data/lib/openhab/core/things/profile_callback.rb +52 -0
  45. data/lib/openhab/core/things/proxy.rb +69 -0
  46. data/lib/openhab/core/things/registry.rb +46 -0
  47. data/lib/openhab/core/things/thing.rb +194 -0
  48. data/lib/openhab/core/things.rb +22 -0
  49. data/lib/openhab/core/timer.rb +128 -0
  50. data/lib/openhab/core/types/comparable_type.rb +23 -0
  51. data/lib/openhab/core/types/date_time_type.rb +259 -0
  52. data/lib/openhab/core/types/decimal_type.rb +192 -0
  53. data/lib/openhab/core/types/hsb_type.rb +183 -0
  54. data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
  55. data/lib/openhab/core/types/next_previous_type.rb +34 -0
  56. data/lib/openhab/core/types/numeric_type.rb +52 -0
  57. data/lib/openhab/core/types/on_off_type.rb +46 -0
  58. data/lib/openhab/core/types/open_closed_type.rb +41 -0
  59. data/lib/openhab/core/types/percent_type.rb +95 -0
  60. data/lib/openhab/core/types/play_pause_type.rb +38 -0
  61. data/lib/openhab/core/types/point_type.rb +117 -0
  62. data/lib/openhab/core/types/quantity_type.rb +327 -0
  63. data/lib/openhab/core/types/raw_type.rb +26 -0
  64. data/lib/openhab/core/types/refresh_type.rb +27 -0
  65. data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
  66. data/lib/openhab/core/types/stop_move_type.rb +34 -0
  67. data/lib/openhab/core/types/string_type.rb +76 -0
  68. data/lib/openhab/core/types/type.rb +117 -0
  69. data/lib/openhab/core/types/un_def_type.rb +38 -0
  70. data/lib/openhab/core/types/up_down_type.rb +50 -0
  71. data/lib/openhab/core/types.rb +69 -0
  72. data/lib/openhab/core/uid.rb +36 -0
  73. data/lib/openhab/core.rb +85 -0
  74. data/lib/openhab/core_ext/java/duration.rb +115 -0
  75. data/lib/openhab/core_ext/java/local_date.rb +93 -0
  76. data/lib/openhab/core_ext/java/local_time.rb +106 -0
  77. data/lib/openhab/core_ext/java/month.rb +59 -0
  78. data/lib/openhab/core_ext/java/month_day.rb +105 -0
  79. data/lib/openhab/core_ext/java/period.rb +103 -0
  80. data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
  81. data/lib/openhab/core_ext/java/time.rb +58 -0
  82. data/lib/openhab/core_ext/java/unit.rb +15 -0
  83. data/lib/openhab/core_ext/java/zoned_date_time.rb +116 -0
  84. data/lib/openhab/core_ext/ruby/array.rb +21 -0
  85. data/lib/openhab/core_ext/ruby/class.rb +15 -0
  86. data/lib/openhab/core_ext/ruby/date.rb +89 -0
  87. data/lib/openhab/core_ext/ruby/numeric.rb +190 -0
  88. data/lib/openhab/core_ext/ruby/range.rb +70 -0
  89. data/lib/openhab/core_ext/ruby/time.rb +104 -0
  90. data/lib/openhab/core_ext.rb +18 -0
  91. data/lib/openhab/dsl/events/watch_event.rb +18 -0
  92. data/lib/openhab/dsl/events.rb +9 -0
  93. data/lib/openhab/dsl/gems.rb +3 -0
  94. data/lib/openhab/dsl/items/builder.rb +618 -0
  95. data/lib/openhab/dsl/items/ensure.rb +93 -0
  96. data/lib/openhab/dsl/items/timed_command.rb +236 -0
  97. data/lib/openhab/dsl/rules/automation_rule.rb +308 -0
  98. data/lib/openhab/dsl/rules/builder.rb +1373 -0
  99. data/lib/openhab/dsl/rules/guard.rb +115 -0
  100. data/lib/openhab/dsl/rules/name_inference.rb +160 -0
  101. data/lib/openhab/dsl/rules/property.rb +76 -0
  102. data/lib/openhab/dsl/rules/rule_triggers.rb +96 -0
  103. data/lib/openhab/dsl/rules/terse.rb +63 -0
  104. data/lib/openhab/dsl/rules/triggers/changed.rb +169 -0
  105. data/lib/openhab/dsl/rules/triggers/channel.rb +57 -0
  106. data/lib/openhab/dsl/rules/triggers/command.rb +107 -0
  107. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +161 -0
  108. data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +164 -0
  109. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +195 -0
  110. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +127 -0
  111. data/lib/openhab/dsl/rules/triggers/trigger.rb +56 -0
  112. data/lib/openhab/dsl/rules/triggers/updated.rb +130 -0
  113. data/lib/openhab/dsl/rules/triggers/watch/watch.rb +55 -0
  114. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +155 -0
  115. data/lib/openhab/dsl/rules/triggers.rb +12 -0
  116. data/lib/openhab/dsl/rules.rb +29 -0
  117. data/lib/openhab/dsl/script_handling.rb +55 -0
  118. data/lib/openhab/dsl/things/builder.rb +263 -0
  119. data/lib/openhab/dsl/thread_local.rb +48 -0
  120. data/lib/openhab/dsl/timer_manager.rb +191 -0
  121. data/lib/openhab/dsl/version.rb +9 -0
  122. data/lib/openhab/dsl.rb +686 -0
  123. data/lib/openhab/log.rb +348 -0
  124. data/lib/openhab/osgi.rb +70 -0
  125. data/lib/openhab/rspec/configuration.rb +56 -0
  126. data/lib/openhab/rspec/example_group.rb +90 -0
  127. data/lib/openhab/rspec/helpers.rb +439 -0
  128. data/lib/openhab/rspec/hooks.rb +93 -0
  129. data/lib/openhab/rspec/jruby.rb +46 -0
  130. data/lib/openhab/rspec/karaf.rb +811 -0
  131. data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
  132. data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
  133. data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
  134. data/lib/openhab/rspec/mocks/metadata_provider.rb +75 -0
  135. data/lib/openhab/rspec/mocks/persistence_service.rb +140 -0
  136. data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
  137. data/lib/openhab/rspec/mocks/synchronous_executor.rb +56 -0
  138. data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
  139. data/lib/openhab/rspec/mocks/timer.rb +95 -0
  140. data/lib/openhab/rspec/openhab/core/actions.rb +26 -0
  141. data/lib/openhab/rspec/openhab/core/items/proxy.rb +27 -0
  142. data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
  143. data/lib/openhab/rspec/shell.rb +31 -0
  144. data/lib/openhab/rspec/suspend_rules.rb +60 -0
  145. data/lib/openhab/rspec.rb +17 -0
  146. data/lib/openhab/yard/cli/stats.rb +23 -0
  147. data/lib/openhab/yard/code_objects/group_object.rb +17 -0
  148. data/lib/openhab/yard/code_objects/java/base.rb +31 -0
  149. data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
  150. data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
  151. data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
  152. data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
  153. data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
  154. data/lib/openhab/yard/handlers/jruby/base.rb +49 -0
  155. data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
  156. data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
  157. data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +27 -0
  158. data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
  159. data/lib/openhab/yard/html_helper.rb +44 -0
  160. data/lib/openhab/yard/tags/constant_directive.rb +20 -0
  161. data/lib/openhab/yard/tags/group_directive.rb +24 -0
  162. data/lib/openhab/yard/tags/library.rb +3 -0
  163. data/lib/openhab/yard.rb +32 -0
  164. metadata +504 -0
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "property"
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ module Rules
8
+ #
9
+ # Guard that can prevent execution of a rule if not satisfied
10
+ #
11
+ # @!visibility private
12
+ class Guard
13
+ #
14
+ # Create a new Guard
15
+ #
16
+ # @param [Object] only_if Item or Proc to use as guard
17
+ # @param [Object] not_if Item or Proc to use as guard
18
+ #
19
+ def initialize(run_context:, only_if: nil, not_if: nil)
20
+ @run_context = run_context
21
+ @only_if = only_if
22
+ @not_if = not_if
23
+ end
24
+
25
+ #
26
+ # Convert the guard into a string
27
+ #
28
+ # @return [String] describing the only_of and not_if guards
29
+ #
30
+ def to_s
31
+ "only_if: #{@only_if}, not_if: #{@not_if}"
32
+ end
33
+
34
+ #
35
+ # Checks if a guard should run
36
+ #
37
+ # @param [OpenHAB Trigger Event] event OpenHAB Trigger Event
38
+ #
39
+ # @return [true,false] True if guard is satisfied, false otherwise
40
+ #
41
+ def should_run?(event)
42
+ logger.trace("Checking guards #{self}")
43
+ check(@only_if, check_type: :only_if,
44
+ event: event) && check(@not_if, check_type: :not_if, event: event)
45
+ end
46
+
47
+ private
48
+
49
+ #
50
+ # Check if guard is satisfied
51
+ #
52
+ # @param [Array] conditions to check
53
+ # @param [Symbol] check_type type of check to perform (:only_if or :not_if)
54
+ # @param [Event] event OpenHAB event to see if it satisfies the guard
55
+ #
56
+ # @return [true,false] True if guard is satisfied, false otherwise
57
+ #
58
+ def check(conditions, check_type:, event:)
59
+ return true if conditions.nil? || conditions.empty?
60
+
61
+ procs, items = conditions.flatten.partition { |condition| condition.is_a?(Proc) }
62
+ logger.trace("Procs: #{procs} Items: #{items}")
63
+
64
+ items.each { |item| logger.trace { "#{item} truthy? #{item.truthy?}" } }
65
+
66
+ process_check(check_type: check_type, event: event, items: items, procs: procs)
67
+ end
68
+
69
+ #
70
+ # Execute the guard check
71
+ #
72
+ # @param [Symbol] check_type :only_if or :not_if to check
73
+ # @param [OpenHAB Event] event event to check if meets guard
74
+ # @param [Array<Item>] items to check if satisfy criteria
75
+ # @param [Array] procs to check if satisfy criteria
76
+ #
77
+ # @return [true,false] True if criteria are satisfied, false otherwise
78
+ #
79
+ def process_check(check_type:, event:, items:, procs:)
80
+ case check_type
81
+ when :only_if then process_only_if(event, items, procs)
82
+ when :not_if then process_not_if(event, items, procs)
83
+ else raise ArgumentError, "Unexpected check type: #{check_type}"
84
+ end
85
+ end
86
+
87
+ #
88
+ # Check not_if guard
89
+ #
90
+ # @param [OpenHAB Event] event to check if meets guard
91
+ # @param [Array<Item>] items to check if satisfy criteria
92
+ # @param [Array] procs to check if satisfy criteria
93
+ #
94
+ # @return [true,false] True if criteria are satisfied, false otherwise
95
+ #
96
+ def process_not_if(event, items, procs)
97
+ items.flatten.none?(&:truthy?) && procs.none? { |proc| @run_context.instance_exec(event, &proc) }
98
+ end
99
+
100
+ #
101
+ # Check only_if guard
102
+ #
103
+ # @param [OpenHAB Event] event to check if meets guard
104
+ # @param [Array<Item>] items to check if satisfy criteria
105
+ # @param [Array] procs to check if satisfy criteria
106
+ #
107
+ # @return [true,false] True if criteria are satisfied, false otherwise
108
+ #
109
+ def process_only_if(event, items, procs)
110
+ items.flatten.all?(&:truthy?) && procs.all? { |proc| @run_context.instance_exec(event, &proc) }
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "triggers/cron/cron"
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ module Rules
8
+ # Contains helper methods for inferring a rule name from its triggers
9
+ # @!visibility private
10
+ module NameInference
11
+ # Trigger Type UIDs that we know how to generate a name for
12
+ KNOWN_TRIGGER_TYPES = [
13
+ "core.ChannelEventTrigger",
14
+ "core.GenericEventTrigger",
15
+ "core.GroupCommandTrigger",
16
+ "core.GroupStateChangeTrigger",
17
+ "core.GroupStateUpdateTrigger",
18
+ "core.ItemCommandTrigger",
19
+ "core.ItemStateChangeTrigger",
20
+ "core.ItemStateUpdateTrigger",
21
+ Triggers::Cron::CRON_TRIGGER_MODULE_ID
22
+ ].freeze
23
+ private_constant :KNOWN_TRIGGER_TYPES
24
+
25
+ class << self
26
+ # takes a freeform rule name and makes it more palatable as an id
27
+ def infer_rule_id_from_name(name)
28
+ name.downcase.delete("'\"").gsub(/\W+/, "_")
29
+ end
30
+
31
+ # get the block's source location, and simplify to a simple filename
32
+ def infer_rule_id_from_block(block)
33
+ file = File.basename(block.source_location.first)
34
+ "#{file}:#{block.source_location.last}"
35
+ end
36
+
37
+ # formulate a readable rule name such as "TestSwitch received command ON" if possible
38
+ def infer_rule_name(config)
39
+ known_triggers, unknown_triggers = config.triggers.partition do |t|
40
+ KNOWN_TRIGGER_TYPES.include?(t.type_uid)
41
+ end
42
+ return nil unless unknown_triggers.empty?
43
+
44
+ cron_triggers = known_triggers.select { |t| t.type_uid == "jsr223.jruby.CronTrigger" }
45
+ ruby_every_triggers = config.ruby_triggers.select { |t| t.first == :every }
46
+
47
+ # makes sure there aren't any true cron triggers cause we can't format them
48
+ return nil unless cron_triggers.length == ruby_every_triggers.length
49
+ return nil unless config.ruby_triggers.length == 1
50
+
51
+ infer_rule_name_from_trigger(*config.ruby_triggers.first)
52
+ end
53
+
54
+ private
55
+
56
+ # formulate a readable rule name from a single trigger if possible
57
+ def infer_rule_name_from_trigger(trigger, items = nil, kwargs = {})
58
+ case trigger
59
+ when :every
60
+ infer_rule_name_from_every_trigger(items, **kwargs)
61
+ when :channel
62
+ infer_rule_name_from_channel_trigger(items, **kwargs)
63
+ when :changed, :updated, :received_command
64
+ infer_rule_name_from_item_trigger(trigger, items, kwargs)
65
+ when :channel_linked, :channel_unlinked
66
+ infer_rule_name_from_channel_link_trigger(trigger)
67
+ when :thing_added, :thing_removed, :thing_updated
68
+ infer_rule_name_from_thing_trigger(trigger)
69
+ end
70
+ end
71
+
72
+ # formulate a readable rule name from an item-type trigger
73
+ def infer_rule_name_from_item_trigger(trigger, items, kwargs)
74
+ kwargs.delete(:command) if kwargs[:command] == [nil]
75
+ return unless items.length <= 3 &&
76
+ (kwargs.keys - %i[from to command duration]).empty?
77
+ return if kwargs.values_at(:from, :to, :command).compact.any? do |v|
78
+ next false if v.is_a?(Array) && v.length <= 4 # arbitrary length
79
+ next false if v.is_a?(Range)
80
+
81
+ v.is_a?(Proc) || v.is_a?(Enumerable)
82
+ end
83
+
84
+ trigger_name = trigger.to_s.tr("_", " ")
85
+ item_names = items.map do |item|
86
+ if item.is_a?(GroupItem::Members)
87
+ "#{item.group.name}.members"
88
+ else
89
+ item.name
90
+ end
91
+ end
92
+ name = "#{format_beginning_of_sentence_array(item_names)} #{trigger_name}"
93
+
94
+ name += " from #{format_inspected_array(kwargs[:from])}" if kwargs[:from]
95
+ name += " to #{format_inspected_array(kwargs[:to])}" if kwargs[:to]
96
+ name += " #{format_inspected_array(kwargs[:command])}" if kwargs[:command]
97
+ name += " for #{kwargs[:duration]}" if kwargs[:duration]
98
+ name.freeze
99
+ end
100
+
101
+ # formulate a readable rule name from an every-style cron trigger
102
+ def infer_rule_name_from_every_trigger(value, at:)
103
+ name = "Every #{value}"
104
+ name += " at #{at}" if at
105
+ name
106
+ end
107
+
108
+ # formulate a readable rule name from a channel trigger
109
+ def infer_rule_name_from_channel_trigger(channels, triggers:)
110
+ triggers = [] if triggers == [nil]
111
+ name = "#{format_beginning_of_sentence_array(channels)} triggered"
112
+ name += " #{format_inspected_array(triggers)}" unless triggers.empty?
113
+ name
114
+ end
115
+
116
+ # formulate a readable rule name from a channel link trigger
117
+ def infer_rule_name_from_channel_link_trigger(trigger)
118
+ trigger == :channel_linked ? "Channel linked to item" : "Channel unlinked from item"
119
+ end
120
+
121
+ # formulate a readable rule name from a thing added/updated/remove trigger
122
+ def infer_rule_name_from_thing_trigger(trigger)
123
+ {
124
+ thing_added: "Thing Added",
125
+ thing_updated: "Thing updated",
126
+ thing_removed: "Thing removed"
127
+ }[trigger]
128
+ end
129
+
130
+ # format an array of words that will be the beginning of a sentence
131
+ def format_beginning_of_sentence_array(array)
132
+ result = format_array(array)
133
+ if array.length > 2
134
+ result = result.dup
135
+ result[0] = "A"
136
+ result.freeze
137
+ end
138
+ result
139
+ end
140
+
141
+ # format an array of items that need to be inspected individually
142
+ def format_inspected_array(array)
143
+ return array.inspect if array.is_a?(Range)
144
+
145
+ array = [array] unless array.is_a?(Array)
146
+ format_array(array.map(&:inspect))
147
+ end
148
+
149
+ # format an array of words in a friendly way
150
+ def format_array(array)
151
+ return array[0] if array.length == 1
152
+ return "#{array[0]} or #{array[1]}" if array.length == 2
153
+
154
+ "any of #{array.join(", ")}"
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Rules
6
+ #
7
+ # Provides methods to support DSL properties
8
+ #
9
+ # @visibility private
10
+ module Property
11
+ #
12
+ # Dynamically creates a property that acts and an accessor with no arguments
13
+ # and a setter with any number of arguments or a block.
14
+ #
15
+ # @param [String] name of the property
16
+ #
17
+ #
18
+ def prop(name)
19
+ # rubocop rules are disabled because this method is dynamically defined on the calling
20
+ # object making calls to other methods in this module impossible, or if done on methods
21
+ # in this module than instance variable belong to the module not the calling class
22
+ define_method(name) do |*args, &block|
23
+ if args.length.zero? && block.nil? == true
24
+ instance_variable_get("@#{name}")
25
+ else
26
+ logger.trace("Property '#{name}' called with args(#{args}) and block(#{block})")
27
+ if args.length == 1
28
+ instance_variable_set("@#{name}", args.first)
29
+ elsif args.length > 1
30
+ instance_variable_set("@#{name}", args)
31
+ elsif block
32
+ instance_variable_set("@#{name}", block)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ #
39
+ # Dynamically creates a property array acts and an accessor with no arguments
40
+ # and a pushes any number of arguments or a block onto they property array
41
+ # You can provide a block to this method which can be used to check if the provided value is acceptable.
42
+ #
43
+ # @param [String] name of the property
44
+ # @param [String] array_name name of the array to use, defaults to name of property
45
+ # @param [Class] wrapper object to put around elements added to the array
46
+ #
47
+ def prop_array(name, array_name: nil, wrapper: nil)
48
+ define_method(name) do |*args, &block|
49
+ array_name ||= name
50
+ if args.length.zero? && block.nil? == true
51
+ instance_variable_get("@#{array_name}")
52
+ else
53
+ logger.trace("Property '#{name}' called with args(#{args}) and block(#{block})")
54
+ if args.length == 1
55
+ insert = args.first
56
+ elsif args.length > 1
57
+ insert = args
58
+ elsif block
59
+ insert = block
60
+ end
61
+ yield insert if block_given?
62
+ insert = wrapper.new(insert) if wrapper
63
+ instance_variable_set("@#{array_name}", (instance_variable_get("@#{array_name}") || []) << insert)
64
+ end
65
+ end
66
+
67
+ return unless array_name
68
+
69
+ define_method(array_name) do
70
+ instance_variable_get("@#{array_name}")
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ require "securerandom"
6
+
7
+ require_relative "triggers/conditions/proc"
8
+
9
+ module OpenHAB
10
+ module DSL
11
+ module Rules
12
+ #
13
+ # Rule configuration for OpenHAB Rules engine
14
+ #
15
+ # @!visibility private
16
+ class RuleTriggers
17
+ # @return [Array] Of triggers
18
+ attr_accessor :triggers
19
+
20
+ # @return [Hash] Of trigger conditions
21
+ attr_reader :trigger_conditions
22
+
23
+ # @return [Hash] Hash of trigger UIDs to attachments
24
+ attr_reader :attachments
25
+
26
+ #
27
+ # Create a new RuleTrigger
28
+ #
29
+ def initialize
30
+ @triggers = []
31
+ @trigger_conditions = Hash.new(Triggers::Conditions::Proc::ANY)
32
+ @attachments = {}
33
+ end
34
+
35
+ #
36
+ # Append a trigger to the list of triggers
37
+ #
38
+ # @param [String] type of trigger to create
39
+ # @param [Map] config map describing trigger configuration
40
+ # @param [Object] attach object to be attached to the trigger
41
+ #
42
+ # @return [Trigger] OpenHAB trigger
43
+ #
44
+ def append_trigger(type:, config:, attach: nil, conditions: nil)
45
+ config.transform_keys!(&:to_s)
46
+ RuleTriggers.trigger(type: type, config: config).tap do |trigger|
47
+ logger.trace("Appending trigger (#{trigger}) attach (#{attach}) conditions(#{conditions})")
48
+ @triggers << trigger
49
+ @attachments[trigger.id] = attach if attach
50
+ @trigger_conditions[trigger.id] = conditions if conditions
51
+ end
52
+ end
53
+
54
+ #
55
+ # Create a trigger
56
+ #
57
+ # @param [String] type of trigger
58
+ # @param [Map] config map
59
+ #
60
+ # @return [OpenHAB Trigger] configured by type and supplied config
61
+ #
62
+ def self.trigger(type:, config:)
63
+ logger.trace("Creating trigger of type '#{type}' config: #{config}")
64
+ org.openhab.core.automation.util.TriggerBuilder.create
65
+ .with_id(uuid)
66
+ .with_type_uid(type)
67
+ .with_configuration(org.openhab.core.config.core.Configuration.new(config))
68
+ .build
69
+ end
70
+
71
+ #
72
+ # Generate a UUID for triggers
73
+ #
74
+ # @return [String] UUID
75
+ #
76
+ def self.uuid
77
+ SecureRandom.uuid
78
+ end
79
+
80
+ #
81
+ # Inspect the config object
82
+ #
83
+ # @return [String] details of the config object
84
+ #
85
+ def inspect
86
+ <<~TEXT.tr("\n", " ")
87
+ #<RuleTriggers #{triggers.inspect}
88
+ Conditions: #{trigger_conditions.inspect}
89
+ UIDs: #{triggers.map(&:id).inspect}
90
+ Attachments: #{attachments.inspect}>
91
+ TEXT
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Rules
6
+ # If you have a single trigger and execution block, you can use a terse rule:
7
+ # All parameters to the trigger are passed through, and an optional `name:` parameter is added.
8
+ #
9
+ # @example
10
+ # changed TestSwitch do |event|
11
+ # logger.info("TestSwitch changed to #{event.state}")
12
+ # end
13
+ #
14
+ # @example
15
+ # received_command TestSwitch, name: "My Test Switch Rule", command: ON do
16
+ # logger.info("TestSwitch received command ON")
17
+ # end
18
+ #
19
+ module Terse
20
+ class << self
21
+ # @!visibility private
22
+ # @!macro def_terse_rule
23
+ # @!method $1(*args, name :nil, id: nil, **kwargs, &block)
24
+ # Create a new rule with a $1 trigger.
25
+ # @param name [String] The name for the rule.
26
+ # @param id [String] The ID for the rule.
27
+ # @yield The execution block for the rule.
28
+ # @return [void]
29
+ # @see Builder#$1
30
+ def def_terse_rule(trigger)
31
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
32
+ def #{trigger}(*args, name: nil, id: nil, **kwargs, &block) # def changed(*args, name: nil, id: nil, **kwargs, &block)
33
+ raise ArgumentError, "Block is required" unless block # raise ArgumentError, "Block is required" unless block
34
+ #
35
+ id ||= NameInference.infer_rule_id_from_name(name) if name # id ||= NameInference.infer_rule_id_from_name(name) if name
36
+ id ||= NameInference.infer_rule_id_from_block(block) # id ||= NameInference.infer_rule_id_from_block(block)
37
+ script = block.source rescue nil # script = block.source rescue nil
38
+ caller_binding = block.binding # caller_binding = block.binding
39
+ rule name, id: id, script: script, binding: caller_binding do # rule name, id: id, script: script, binding: caller_binding do
40
+ #{trigger}(*args, **kwargs) # changed(*args, **kwargs)
41
+ run(&block) # run(&block)
42
+ end # end
43
+ end # end
44
+ module_function #{trigger.inspect} # module_function :changed
45
+ RUBY
46
+ end
47
+ end
48
+
49
+ def_terse_rule(:changed)
50
+ def_terse_rule(:channel)
51
+ def_terse_rule(:channel_linked)
52
+ def_terse_rule(:channel_unlinked)
53
+ def_terse_rule(:cron)
54
+ def_terse_rule(:every)
55
+ def_terse_rule(:received_command)
56
+ def_terse_rule(:thing_added)
57
+ def_terse_rule(:thing_updated)
58
+ def_terse_rule(:thing_removed)
59
+ def_terse_rule(:updated)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "conditions/duration"
4
+ require_relative "conditions/proc"
5
+ require_relative "trigger"
6
+
7
+ module OpenHAB
8
+ module DSL
9
+ module Rules
10
+ module Triggers
11
+ # @!visibility private
12
+ #
13
+ # Creates changed triggers
14
+ #
15
+ class Changed < Trigger
16
+ #
17
+ # Create the trigger
18
+ #
19
+ # @param [Object] item item to create trigger for
20
+ # @param [Item State] from state to restrict trigger to
21
+ # @param [Item State] to state to restrict trigger to
22
+ # @param [Duration, nil] duration ruration to delay trigger until to state is met
23
+ # @param [Object] attach object to be attached to the trigger
24
+ #
25
+ # @return [Trigger] OpenHAB triggers
26
+ #
27
+ def trigger(item:, from:, to:, duration:, attach:)
28
+ if duration
29
+ wait_trigger(item: item, from: from, to: to, duration: duration, attach: attach)
30
+ elsif [to, from].grep(Range).any?
31
+ range_trigger(item: item, from: from, to: to, attach: attach)
32
+ elsif [to, from].grep(Proc).any?
33
+ proc_trigger(item: item, from: from, to: to, attach: attach)
34
+ else
35
+ changed_trigger(item: item, from: from, to: to, attach: attach)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # @return [String] A thing status Change trigger
42
+ THING_CHANGE = "core.ThingStatusChangeTrigger"
43
+
44
+ # @return [String] An item state change trigger
45
+ ITEM_STATE_CHANGE = "core.ItemStateChangeTrigger"
46
+
47
+ # @return [String] A group state change trigger for items in the group
48
+ GROUP_STATE_CHANGE = "core.GroupStateChangeTrigger"
49
+
50
+ #
51
+ # Create a TriggerDelay for for an item or group that is changed for a specific duration
52
+ #
53
+ # @param [Object] item to create trigger delay for
54
+ # @param [Duration] duration to delay trigger for until condition is met
55
+ # @param [Item State] to OpenHAB Item State item or group needs to change to
56
+ # @param [Item State] from OpenHAB Item State item or group needs to be coming from
57
+ # @param [Object] attach object to be attached to the trigger
58
+ #
59
+ # @return [Trigger] OpenHAB trigger
60
+ #
61
+ def wait_trigger(item:, duration:, to: nil, from: nil, attach: nil)
62
+ item_name = item.respond_to?(:name) ? item.name : item.to_s
63
+ logger.trace("Creating Changed Wait Change Trigger for Item(#{item_name}) Duration(#{duration}) "\
64
+ "To(#{to}) From(#{from}) Attach(#{attach})")
65
+ conditions = Conditions::Duration.new(to: to, from: from, duration: duration)
66
+ changed_trigger(item: item, to: nil, from: nil, attach: attach, conditions: conditions)
67
+ end
68
+
69
+ #
70
+ # Creates a trigger with a range condition on either 'from' or 'to' field
71
+ # @param [Object] item to create changed trigger on
72
+ # @param [Object] from state to restrict trigger to
73
+ # @param [Object] to state restrict trigger to
74
+ # @param [Object] attach object to be attached to the trigger
75
+ # @return [Trigger] OpenHAB trigger
76
+ #
77
+ def range_trigger(item:, from:, to:, attach:)
78
+ from, to = Conditions::Proc.range_procs(from, to)
79
+ proc_trigger(item: item, from: from, to: to, attach: attach)
80
+ end
81
+
82
+ #
83
+ # Creates a trigger with a proc condition on either 'from' or 'to' field
84
+ # @param [Object] item to create changed trigger on
85
+ # @param [Object] from state to restrict trigger to
86
+ # @param [Object] to state restrict trigger to
87
+ # @param [Object] attach object to be attached to the trigger
88
+ # @return [Trigger] OpenHAB trigger
89
+ #
90
+ def proc_trigger(item:, from:, to:, attach:)
91
+ # swap from/to w/ nil if from/to is a proc
92
+ # rubocop:disable Style/ParallelAssignment
93
+ from_proc, from = from, nil if from.is_a?(Proc)
94
+ to_proc, to = to, nil if to.is_a?(Proc)
95
+ # rubocop:enable Style/ParallelAssignment
96
+ conditions = Conditions::Proc.new(to: to_proc, from: from_proc)
97
+ changed_trigger(item: item, from: from, to: to, attach: attach, conditions: conditions)
98
+ end
99
+
100
+ #
101
+ # Create a changed trigger
102
+ #
103
+ # @param [Object] item to create changed trigger on
104
+ # @param [Object] from state to restrict trigger to
105
+ # @param [Object] to state restrict trigger to
106
+ # @param [Object] attach object to be attached to the trigger
107
+ #
108
+ def changed_trigger(item:, from:, to:, attach: nil, conditions: nil)
109
+ type, config = case item
110
+ when GroupItem::Members then group(group: item, from: from,
111
+ to: to)
112
+ when Core::Things::Thing then thing(thing: item, from: from, to: to)
113
+ else item(item: item, from: from, to: to)
114
+ end
115
+ append_trigger(type: type, config: config, attach: attach, conditions: conditions)
116
+ end
117
+
118
+ #
119
+ # Create a changed trigger for a thing
120
+ #
121
+ # @param [Thing] thing to detected changed states on
122
+ # @param [String] from state to restrict trigger to
123
+ # @param [String] to state to restrict trigger to
124
+ #
125
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
126
+ # second element is a Hash configuring trigger
127
+ #
128
+ def thing(thing:, from:, to:)
129
+ trigger_for_thing(thing: thing, type: THING_CHANGE, to: to, from: from)
130
+ end
131
+
132
+ #
133
+ # Create a changed trigger for an item
134
+ #
135
+ # @param [Item] item to detected changed states on
136
+ # @param [String] from state to restrict trigger to
137
+ # @param [String] to to restrict trigger to
138
+ #
139
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
140
+ # second element is a Hash configuring trigger
141
+ #
142
+ def item(item:, from:, to:)
143
+ config = { "itemName" => item.name }
144
+ config["state"] = to.to_s if to
145
+ config["previousState"] = from.to_s if from
146
+ [ITEM_STATE_CHANGE, config]
147
+ end
148
+
149
+ #
150
+ # Create a changed trigger for group items
151
+ #
152
+ # @param [Group] group to detected changed states on
153
+ # @param [String] from state to restrict trigger to
154
+ # @param [String] to to restrict trigger to
155
+ #
156
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
157
+ # second element is a Hash configuring trigger
158
+ #
159
+ def group(group:, from:, to:)
160
+ config = { "groupName" => group.group.name }
161
+ config["state"] = to.to_s if to
162
+ config["previousState"] = from.to_s if from
163
+ [GROUP_STATE_CHANGE, config]
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end