openhab-jrubyscripting 5.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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