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,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Items
6
+ # Extensions for {GenericItem} to implement timed commands
7
+ #
8
+ # All items have an implicit timer associated with them, enabling to
9
+ # easily set an item into a specific state for a specified duration and
10
+ # then at the expiration of that duration have the item automatically
11
+ # change to another state. These timed commands are reentrant, meaning
12
+ # if the same timed command is triggered while an outstanding timed
13
+ # command exist, that timed command will be rescheduled rather than
14
+ # creating a distinct timed command.
15
+ #
16
+ # Timed commands are initiated by using the 'for:' argument with the
17
+ # command. This is available on both the 'command' method and any
18
+ # command-specific methods, e.g. {SwitchItem#on}.
19
+ #
20
+ # Any update to the timed command state will result in the timer being
21
+ # cancelled. For example, if you have a Switch on a timer and another
22
+ # rule sends OFF or ON to that item the timer will be automatically
23
+ # canceled. Sending a different duration (for:) value for the timed
24
+ # command will reschedule the timed command for that new duration.
25
+ #
26
+ module TimedCommand
27
+ #
28
+ # Provides information about why the expiration block of a
29
+ # {TimedCommand#command timed command} is being called.
30
+ #
31
+ # @attr [GenericItem] item
32
+ # @!visibility private
33
+ # @attr [Types::Type, Proc] on_expire
34
+ # @!visibility private
35
+ # @attr [Core::Timer] timer
36
+ # @!visibility private
37
+ # @attr [Symbol] resolution
38
+ # @!visibility private
39
+ # @attr [String] rule_uid
40
+ # @!visibility private
41
+ # @attr [Mutex] mutex
42
+ # @!visibility private
43
+ #
44
+ TimedCommandDetails = Struct.new(:item,
45
+ :on_expire,
46
+ :timer,
47
+ :resolution,
48
+ :rule_uid,
49
+ :mutex,
50
+ keyword_init: true) do
51
+ # @return [true, false]
52
+ def expired?
53
+ resolution == :expired
54
+ end
55
+
56
+ # @return [true, false]
57
+ def cancelled?
58
+ resolution == :cancelled
59
+ end
60
+ end
61
+
62
+ @timed_commands = java.util.concurrent.ConcurrentHashMap.new
63
+
64
+ class << self
65
+ # @!visibility private
66
+ attr_reader :timed_commands
67
+ end
68
+
69
+ Core::Items::GenericItem.prepend(self)
70
+
71
+ #
72
+ # Sends command to an item for specified duration, then on timer expiration sends
73
+ # the expiration command to the item
74
+ #
75
+ # @note If a block is provided, and the timer is canceled because the
76
+ # item changed state while it was waiting, the block will still be
77
+ # executed. Be sure to check {TimedCommandDetails#expired? #expired?}
78
+ # and/or {TimedCommandDetails#cancelled? #cancelled?} to determine why
79
+ # the block was called.
80
+ #
81
+ # @param [Command] command to send to object
82
+ # @param [Duration] for duration for item to be in command state
83
+ # @param [Command] on_expire Command to send when duration expires
84
+ # @yield If a block is provided, `on_expire` is ignored and the block
85
+ # is expected to set the item to the desired state or carry out some
86
+ # other action.
87
+ # @yieldparam [TimedCommandDetails] timed_command
88
+ # @return [self]
89
+ #
90
+ # @example
91
+ # Switch.command(ON, for: 5.minutes)
92
+ # @example
93
+ # Switch.on for: 5.minutes
94
+ # @example
95
+ # Dimmer.on for: 5.minutes, on_expire: 50
96
+ # @example
97
+ # Dimmer.on(for: 5.minutes) { |event| Dimmer.off if Light.on? }
98
+ #
99
+ def command(command, for: nil, on_expire: nil, &block)
100
+ duration = binding.local_variable_get(:for)
101
+ return super(command) unless duration
102
+
103
+ on_expire = block if block
104
+
105
+ TimedCommand.timed_commands.compute(self) do |_key, timed_command_details|
106
+ if timed_command_details.nil?
107
+ # no prior timed command
108
+ on_expire ||= default_on_expire(command)
109
+ super(command)
110
+ create_timed_command(duration: duration, on_expire: on_expire)
111
+ else
112
+ timed_command_details.mutex.synchronize do
113
+ if timed_command_details.resolution
114
+ # timed command that finished, but hadn't removed itself from the map yet
115
+ # (it doesn't do so under the mutex to prevent a deadlock).
116
+ # just create a new one
117
+ on_expire ||= default_on_expire(command)
118
+ super(command)
119
+ create_timed_command(duration: duration, on_expire: on_expire)
120
+ else
121
+ # timed command still pending; reset it
122
+ logger.trace "Outstanding Timed Command #{timed_command_details} encountered - rescheduling"
123
+ timed_command_details.on_expire = on_expire unless on_expire.nil?
124
+ timed_command_details.timer.reschedule(duration)
125
+ # disable the cancel rule while we send the new command
126
+ Core.rule_manager.set_enabled(timed_command_details.rule_uid, false)
127
+ super(command)
128
+ Core.rule_manager.set_enabled(timed_command_details.rule_uid, true)
129
+ timed_command_details
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ self
136
+ end
137
+
138
+ private
139
+
140
+ # Creates a new timed command and places it in the TimedCommand hash
141
+ def create_timed_command(duration:, on_expire:)
142
+ timed_command_details = TimedCommandDetails.new(item: self,
143
+ on_expire: on_expire,
144
+ mutex: Mutex.new)
145
+
146
+ timed_command_details.timer = timed_command_timer(timed_command_details, duration)
147
+ cancel_rule = TimedCommandCancelRule.new(timed_command_details)
148
+ timed_command_details.rule_uid = Core.automation_manager
149
+ .add_rule(cancel_rule)
150
+ .uid
151
+ Rules.script_rules[timed_command_details.rule_uid] = cancel_rule
152
+ logger.trace "Created Timed Command #{timed_command_details}"
153
+ timed_command_details
154
+ end
155
+
156
+ # Creates the timer to handle changing the item state when timer expires or invoking user supplied block
157
+ # @param [TimedCommandDetailes] timed_command_details details about the timed command
158
+ # @return [Timer] Timer
159
+ def timed_command_timer(timed_command_details, duration)
160
+ DSL.after(duration) do
161
+ timed_command_details.mutex.synchronize do
162
+ logger.trace "Timed command expired - #{timed_command_details}"
163
+ DSL.remove_rule(timed_command_details.rule_uid)
164
+ timed_command_details.resolution = :expired
165
+ case timed_command_details.on_expire
166
+ when Proc
167
+ logger.trace "Invoking block #{timed_command_details.on_expire} after timed command for #{name} expired"
168
+ timed_command_details.on_expire.call(timed_command_details)
169
+ when Core::Types::UnDefType
170
+ update(timed_command_details.on_expire)
171
+ else
172
+ command(timed_command_details.on_expire)
173
+ end
174
+ end
175
+ TimedCommand.timed_commands.delete(timed_command_details.item)
176
+ end
177
+ end
178
+
179
+ #
180
+ # The default expire for ON/OFF is their inverse
181
+ #
182
+ def default_on_expire(command)
183
+ return !command if command.is_a?(Core::Types::OnOffType)
184
+
185
+ raw_state
186
+ end
187
+
188
+ #
189
+ # Rule to cancel timed commands
190
+ #
191
+ # @!visibility private
192
+ class TimedCommandCancelRule < org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule
193
+ def initialize(timed_command_details)
194
+ super()
195
+ @timed_command_details = timed_command_details
196
+ # Capture rule name if known
197
+ @thread_locals = ThreadLocal.persist
198
+ self.name = "Cancel implicit timer for #{timed_command_details.item.name}"
199
+ self.triggers = [Rules::RuleTriggers.trigger(
200
+ type: Rules::Triggers::Changed::ITEM_STATE_CHANGE,
201
+ config: { "itemName" => timed_command_details.item.name }
202
+ )]
203
+ end
204
+
205
+ # Cleanup the rule; nothing to do here.
206
+ def cleanup; end
207
+
208
+ #
209
+ # Execute the rule
210
+ #
211
+ # @param [Map] _mod map provided by OpenHAB rules engine
212
+ # @param [Map] inputs map provided by OpenHAB rules engine containing event and other information
213
+ #
214
+ def execute(_mod = nil, inputs = nil)
215
+ ThreadLocal.thread_local(**@thread_locals) do
216
+ @timed_command_details.mutex.synchronize do
217
+ logger.trace "Canceling implicit timer #{@timed_command_details.timer} for "\
218
+ "#{@timed_command_details.item.name} because received event #{inputs}"
219
+ @timed_command_details.timer.cancel
220
+ DSL.remove_rule(@timed_command_details.rule_uid)
221
+ @timed_command_details.resolution = :cancelled
222
+ if @timed_command_details.on_expire.is_a?(Proc)
223
+ logger.trace "Executing user supplied block on timed command cancelation"
224
+ @timed_command_details.on_expire.call(@timed_command_details)
225
+ end
226
+ end
227
+ TimedCommand.timed_commands.delete(@timed_command_details.item)
228
+ rescue Exception => e
229
+ logger.log_exception(e)
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,308 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Rules
6
+ #
7
+ # OpenHAB rules engine object
8
+ #
9
+ # @!visibility private
10
+ class AutomationRule < org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule
11
+ field_writer :uid
12
+
13
+ #
14
+ # Create a new Rule
15
+ #
16
+ # @param [Config] config Rule configuration
17
+ #
18
+ # Constructor sets a number of variables, no further decomposition necessary
19
+ def initialize(config:)
20
+ # Metrics disabled because only setters are called or defaults set.
21
+ super()
22
+ set_name(config.name)
23
+ set_description(config.description)
24
+ set_tags(to_string_set(config.tags))
25
+ set_triggers(config.triggers)
26
+ self.uid = config.uid
27
+ @run_context = config.caller
28
+ @run_queue = config.run
29
+ @guard = config.guard
30
+ @between = config.between && DSL.between(config.between)
31
+ @trigger_conditions = config.trigger_conditions
32
+ @attachments = config.attachments
33
+ @thread_locals = ThreadLocal.persist
34
+ end
35
+
36
+ #
37
+ # Execute the rule
38
+ #
39
+ # @param [Map] mod map provided by OpenHAB rules engine
40
+ # @param [Map] inputs map provided by OpenHAB rules engine containing event and other information
41
+ #
42
+ #
43
+ def execute(mod = nil, inputs = nil)
44
+ ThreadLocal.thread_local(**@thread_locals) do
45
+ logger.trace { "Execute called with mod (#{mod&.to_string}) and inputs (#{inputs.inspect})" }
46
+ logger.trace { "Event details #{inputs["event"].inspect}" } if inputs&.key?("event")
47
+ trigger_conditions(inputs).process(mod: mod, inputs: inputs) do
48
+ process_queue(create_queue(inputs), mod, inputs)
49
+ end
50
+ rescue Exception => e
51
+ @run_context.send(:logger).log_exception(e)
52
+ end
53
+ end
54
+
55
+ #
56
+ # Cleanup any resources associated with automation rule
57
+ #
58
+ def cleanup
59
+ @trigger_conditions.each_value(&:cleanup)
60
+ end
61
+
62
+ private
63
+
64
+ #
65
+ # Create the run queue based on guards
66
+ #
67
+ # @param [Map] inputs rule inputs
68
+ # @return [Queue] <description>
69
+ #
70
+ def create_queue(inputs)
71
+ case check_guards(event: extract_event(inputs))
72
+ when true
73
+ @run_queue.dup.grep_v(Builder::Otherwise)
74
+ when false
75
+ @run_queue.dup.grep(Builder::Otherwise)
76
+ end
77
+ end
78
+
79
+ #
80
+ # Extract the event object from inputs
81
+ # and merge other inputs keys/values into the event
82
+ #
83
+ # @param [Map] inputs rule inputs
84
+ #
85
+ # @return [Object] event object
86
+ #
87
+ def extract_event(inputs)
88
+ event = inputs&.dig("event")
89
+ unless event
90
+ event = Struct.new(:event, :attachment, :command).new
91
+ event.command = inputs&.dig("command")
92
+ end
93
+ add_attachment(event, inputs)
94
+ end
95
+
96
+ #
97
+ # Get the trigger_id for the trigger that caused the rule creation
98
+ #
99
+ # @return [Hash] Input hash potentially containing trigger id
100
+ #
101
+ def trigger_id(inputs)
102
+ inputs&.dig("module")
103
+ end
104
+
105
+ #
106
+ # Returns trigger conditions from inputs if it exists
107
+ #
108
+ # @param [Map] inputs map from OpenHAB containing UID
109
+ #
110
+ # @return [Array] Array of trigger conditions that match rule UID
111
+ #
112
+ def trigger_conditions(inputs)
113
+ # Parse this to get the trigger UID:
114
+ # ["72698819-83cb-498a-8e61-5aab8b812623.event", "oldState", "module", \
115
+ # "72698819-83cb-498a-8e61-5aab8b812623.oldState", "event", "newState",\
116
+ # "72698819-83cb-498a-8e61-5aab8b812623.newState"]
117
+ @trigger_conditions[trigger_id(inputs)]
118
+ end
119
+
120
+ # If an attachment exists for the trigger for this event add it to the event object
121
+ # @param [Object] event Event
122
+ # @param [Hash] inputs Inputs into event
123
+ # @return [Object] Event with attachment added
124
+ #
125
+ def add_attachment(event, inputs)
126
+ attachment = @attachments[trigger_id(inputs)]
127
+ return event unless attachment
128
+
129
+ event.attachment = attachment
130
+ event
131
+ end
132
+
133
+ #
134
+ # Check if any guards prevent execution
135
+ #
136
+ # @param [Map] event OpenHAB rule trigger event
137
+ #
138
+ # @return [true,false] True if guards says rule should execute, false otherwise
139
+ #
140
+ # Loggging inflates method length
141
+ def check_guards(event:)
142
+ return true if @guard.nil?
143
+
144
+ if @guard.should_run? event
145
+ return true if @between.nil?
146
+
147
+ now = Time.now
148
+ return true if @between.cover? now
149
+
150
+ logger.trace("Skipped execution of rule '#{name}' because the current time #{now} "\
151
+ "is not between #{@between.begin} and #{@between.end}")
152
+ else
153
+ logger.trace("Skipped execution of rule '#{name}' because of guard #{@guard}")
154
+ end
155
+ false
156
+ end
157
+
158
+ #
159
+ # Process the run queue
160
+ #
161
+ # @param [Array] run_queue array of procs of various types to execute
162
+ # @param [Map] mod OpenHAB map object describing rule trigger
163
+ # @param [Map] inputs OpenHAB map object describing rule trigge
164
+ #
165
+ def process_queue(run_queue, mod, inputs)
166
+ event = extract_event(inputs)
167
+
168
+ while (task = run_queue.shift)
169
+ if task.is_a?(Builder::Delay)
170
+ process_delay_task(inputs, mod, run_queue, task)
171
+ else
172
+ process_task(event, task)
173
+ end
174
+ end
175
+ end
176
+
177
+ #
178
+ # Dispatch execution block tasks to different methods
179
+ #
180
+ # @param [OpenHab Event] event that triggered the rule
181
+ # @param [Task] task task containing otherwise block to execute
182
+ #
183
+ def process_task(event, task)
184
+ ThreadLocal.thread_local(**@thread_locals) do
185
+ case task
186
+ when Builder::Run then process_run_task(event, task)
187
+ when Builder::Script then process_script_task(task)
188
+ when Builder::Trigger then process_trigger_task(event, task)
189
+ when Builder::Otherwise then process_otherwise_task(event, task)
190
+ end
191
+ end
192
+ end
193
+
194
+ #
195
+ # Process an otherwise block
196
+ #
197
+ # @param [OpenHab Event] event that triggered the rule
198
+ # @param [Task] task task containing otherwise block to execute
199
+ #
200
+ #
201
+ def process_otherwise_task(event, task)
202
+ logger.trace { "Executing rule '#{name}' otherwise block with event(#{event})" }
203
+ @run_context.instance_exec(event, &task.block)
204
+ end
205
+
206
+ #
207
+ # Process delay task
208
+ #
209
+ # @param [Map] inputs Rule trigger inputs
210
+ # @param [Map] mod Rule modes
211
+ # @param [Queue] run_queue Queue of tasks for this rule
212
+ # @param [Delay] task to process
213
+ #
214
+ #
215
+ def process_delay_task(inputs, mod, run_queue, task)
216
+ remaining_queue = run_queue.slice!(0, run_queue.length)
217
+ DSL.after(task.duration) { process_queue(remaining_queue, mod, inputs) }
218
+ end
219
+
220
+ #
221
+ # Process a task that is caused by a group item
222
+ #
223
+ # @param [Map] event Rule event map
224
+ # @param [Trigger] task to execute
225
+ #
226
+ #
227
+ def process_trigger_task(event, task)
228
+ return unless event&.item
229
+
230
+ logger.trace { "Executing rule '#{name}' trigger block with item (#{event.item})" }
231
+ @run_context.instance_exec(event.item, &task.block)
232
+ end
233
+
234
+ #
235
+ # Process a run task
236
+ #
237
+ # @param [OpenHab Event] event information
238
+ # @param [Run] task to execute
239
+ #
240
+ #
241
+ def process_run_task(event, task)
242
+ logger.trace { "Executing rule '#{name}' run block with event(#{event})" }
243
+ @run_context.instance_exec(event, &task.block)
244
+ end
245
+
246
+ #
247
+ # Process a script task
248
+ #
249
+ # @param [Script] task to execute
250
+ #
251
+ def process_script_task(task)
252
+ logger.trace { "Executing script '#{name}' run block" }
253
+ @run_context.instance_exec(&task.block)
254
+ end
255
+
256
+ #
257
+ # Convert the given array to a set of strings.
258
+ # Convert Semantics classes to their simple name
259
+ #
260
+ # @example
261
+ # to_string_set("tag1", Semantics::LivingRoom)
262
+ #
263
+ # @param tags [Array] An array of strings or Semantics classes
264
+ #
265
+ # @return [Set] A set of strings
266
+ #
267
+ def to_string_set(*tags)
268
+ tags = tags.flatten.map do |tag|
269
+ if tag.respond_to?(:java_class) && tag < org.openhab.core.semantics.Tag
270
+ tag.java_class.simple_name
271
+ else
272
+ tag.to_s
273
+ end
274
+ end
275
+ Set.new(tags)
276
+ end
277
+
278
+ #
279
+ # Create a new hash in which all elements are converted to strings
280
+ #
281
+ # @param [Map] hash in which all elements should be converted to strings
282
+ #
283
+ # @return [Map] new map with values converted to strings
284
+ #
285
+ def inspect_hash(hash)
286
+ hash.each_with_object({}) do |(key, value), new_hash|
287
+ new_hash[inspect_item(key)] = inspect_item(value)
288
+ end
289
+ end
290
+
291
+ #
292
+ # Convert an individual element into a string based on if it a Ruby or Java object
293
+ #
294
+ # @param [Object] item to convert to a string
295
+ #
296
+ # @return [String] representation of item
297
+ #
298
+ def inspect_item(item)
299
+ if item.respond_to? :to_string
300
+ item.to_string
301
+ elsif item.respond_to? :to_str
302
+ item.to_str
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
308
+ end