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,263 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ #
6
+ # Contains extensions to simplify working with {Core::Things::Thing Thing}s.
7
+ #
8
+ module Things
9
+ # Stores all things created in scripts, and notifies the ThingRegistry
10
+ # of their existence
11
+ # aa@!visibility private
12
+ class ThingProvider < org.openhab.core.common.registry.AbstractProvider
13
+ include org.openhab.core.thing.ThingProvider
14
+ include Singleton
15
+
16
+ def initialize
17
+ super
18
+
19
+ @things = []
20
+
21
+ $things.add_provider(self)
22
+ ScriptHandling.script_unloaded { $things.remove_provider(self) }
23
+ end
24
+
25
+ # Add a thing to this provider
26
+ def add(thing)
27
+ thing = thing.build
28
+ @things << thing
29
+ notify_listeners_about_added_element(thing)
30
+ thing
31
+ end
32
+
33
+ # Get all items in this provider
34
+ def getAll # rubocop:disable Naming/MethodName required by java interface
35
+ @things
36
+ end
37
+ end
38
+
39
+ # A thing builder allows you to dynamically create OpenHAB thing at runtime.
40
+ # This can be useful either to create things as soon as the script loads,
41
+ # or even later based on a rule executing.
42
+ #
43
+ # @example
44
+ # things.build do
45
+ # thing "astro:sun:home", "Astro Sun Data", config: { "geolocation" => "0,0" }
46
+ # end
47
+ class Builder
48
+ # Create a new Bridge
49
+ # @see BridgeBuilder#initialize
50
+ def bridge(*args, **kwargs, &block)
51
+ build(BridgeBuilder, *args, **kwargs, &block)
52
+ end
53
+
54
+ # Create a new Thing
55
+ # @see ThingBuilder#initialize
56
+ def thing(*args, **kwargs, &block)
57
+ build(ThingBuilder, *args, **kwargs, &block)
58
+ end
59
+
60
+ private
61
+
62
+ def build(klass, *args, **kwargs, &block)
63
+ builder = klass.new(*args, **kwargs)
64
+ builder.instance_eval(&block) if block
65
+ thing = ThingProvider.instance.add(builder)
66
+ thing = Core::Things::Proxy.new(thing)
67
+ thing.enable(enabled: builder.enabled) unless builder.enabled.nil?
68
+ thing
69
+ end
70
+ end
71
+
72
+ # The ThingBuilder DSL allows you to customize a thing
73
+ class ThingBuilder
74
+ # The label for this thing
75
+ # @return [String, nil]
76
+ attr_accessor :label
77
+ # The location for this thing
78
+ # @return [String, nil]
79
+ attr_accessor :location
80
+ # The id for this thing
81
+ # @return [Core::Things::ThingUID]
82
+ attr_reader :uid
83
+ # The type of this thing
84
+ # @return [ThingTypeUID]
85
+ attr_reader :thing_type_uid
86
+ # The bridge of this thing
87
+ # @return [Core::Things::ThingUID, nil]
88
+ attr_reader :bridge_uid
89
+ # The config for this thing
90
+ # @return [Hash, nil]
91
+ attr_reader :config
92
+ # If the thing should be enabled after it is created
93
+ # @return [true, false, nil]
94
+ attr_reader :enabled
95
+ # Explicitly configured channels on this thing
96
+ # @return [Array<ChannelBuilder>]
97
+ attr_reader :channels
98
+
99
+ class << self
100
+ # @!visibility private
101
+ def thing_type_registry
102
+ @thing_type_registry ||= OSGi.service("org.openhab.core.thing.type.ThingTypeRegistry")
103
+ end
104
+
105
+ # @!visibility private
106
+ def config_description_registry
107
+ @config_description_registry ||=
108
+ OSGi.service("org.openhab.core.config.core.ConfigDescriptionRegistry")
109
+ end
110
+
111
+ # @!visibility private
112
+ def thing_factory_helper
113
+ @thing_factory_helper ||= begin
114
+ # this is an internal class, so OSGi doesn't put it on the main class path,
115
+ # so we have to go find it ourselves manually
116
+ bundle = org.osgi.framework.FrameworkUtil.get_bundle(org.openhab.core.thing.Thing)
117
+ bundle.load_class("org.openhab.core.thing.internal.ThingFactoryHelper").ruby_class
118
+ end
119
+ end
120
+ end
121
+
122
+ def initialize(uid, label = nil, binding: nil, type: nil, bridge: nil, location: nil, config: {}, enabled: nil)
123
+ @channels = []
124
+ uid = uid.to_s
125
+ uid_segments = uid.split(org.openhab.core.common.AbstractUID::SEPARATOR)
126
+ @bridge_uid = nil
127
+ bridge = bridge.uid if bridge.is_a?(org.openhab.core.thing.Bridge) || bridge.is_a?(BridgeBuilder)
128
+ bridge = bridge&.to_s
129
+ bridge_segments = bridge&.split(org.openhab.core.common.AbstractUID::SEPARATOR) || []
130
+ type = type&.to_s
131
+
132
+ # infer missing components
133
+ type ||= uid_segments[0] if uid_segments.length == 2
134
+ type ||= uid_segments[1] if uid_segments.length > 2
135
+ binding ||= uid_segments[0] if uid_segments.length > 2
136
+ binding ||= bridge_segments[0] if bridge_segments && bridge_segments.length > 2
137
+
138
+ if bridge
139
+ bridge_segments.unshift(binding) if bridge_segments.length < 3
140
+ @bridge_uid = org.openhab.core.thing.ThingUID.new(*bridge_segments)
141
+ end
142
+
143
+ @uid = org.openhab.core.thing.ThingUID.new(*[binding, type, @bridge_uid&.id,
144
+ uid_segments.last].compact)
145
+ @thing_type_uid = org.openhab.core.thing.ThingTypeUID.new(*@uid.all_segments[0..1])
146
+ @label = label
147
+ @location = location
148
+ @location = location.label if location.is_a?(GenericItem)
149
+ @config = config
150
+ @enabled = enabled
151
+ end
152
+
153
+ # Add an explicitly configured channel to this item
154
+ # @see ChannelBuilder#initialize
155
+ def channel(*args, **kwargs, &block)
156
+ channel = ChannelBuilder.new(*args, thing: self, **kwargs)
157
+ channel.instance_eval(&block) if block
158
+ @channels << channel
159
+ end
160
+
161
+ # @!visibility private
162
+ def build
163
+ configuration = org.openhab.core.config.core.Configuration.new(config)
164
+ if thing_type
165
+ self.class.thing_factory_helper.apply_default_configuration(
166
+ configuration, thing_type,
167
+ self.class.config_description_registry
168
+ )
169
+ end
170
+ builder = org.openhab.core.thing.binding.builder.ThingBuilder
171
+ .create(thing_type_uid, uid)
172
+ .with_label(label)
173
+ .with_configuration(configuration)
174
+ .with_bridge(bridge_uid)
175
+ .with_channels(channels)
176
+
177
+ if thing_type
178
+ # can't use with_channels, or it will wipe out custom channels from above
179
+ self.class.thing_factory_helper.create_channels(thing_type, uid,
180
+ self.class.config_description_registry).each do |channel|
181
+ builder.with_channel(channel)
182
+ end
183
+ builder.with_properties(thing_type.properties)
184
+ end
185
+
186
+ thing = builder.build
187
+ Core::Things.manager.set_enabled(uid, enabled) unless enabled.nil?
188
+ thing
189
+ end
190
+
191
+ private
192
+
193
+ def thing_type
194
+ @thing_type ||= self.class.thing_type_registry.get_thing_type(thing_type_uid)
195
+ end
196
+ end
197
+
198
+ # The BridgeBuilder DSL allows you to customize a thing
199
+ class BridgeBuilder < ThingBuilder
200
+ # Create a new Bridge with this Bridge as its Bridge
201
+ # @see BridgeBuilder#initialize
202
+ def bridge(*args, **kwargs, &block)
203
+ super(*args, bridge: self, **kwargs, &block)
204
+ end
205
+
206
+ # Create a new Thing with this Bridge as its Bridge
207
+ # @see ThingBuilder#initialize
208
+ def thing(*args, **kwargs, &block)
209
+ super(*args, bridge: self, **kwargs, &block)
210
+ end
211
+ end
212
+
213
+ # The ChannelBuilder DSL allows you to customize a channel
214
+ class ChannelBuilder
215
+ attr_accessor :label
216
+ attr_reader :uid, :parameters
217
+
218
+ def initialize(uid, type, label, thing:, group: nil, **parameters)
219
+ @thing = thing
220
+
221
+ uid = uid.to_s
222
+ uid_segments = uid.split(org.openhab.core.common.AbstractUID::SEPARATOR)
223
+ group_segments = uid_segments.last.split(org.openhab.core.thing.ChannelUID::CHANNEL_GROUP_SEPARATOR)
224
+ if group
225
+ if group_segments.length == 2
226
+ group_segments[0] = group
227
+ else
228
+ group_segments.unshift(group)
229
+ end
230
+ uid_segments[-1] = group_segments.join(org.openhab.core.thing.ChannelUID::CHANNEL_GROUP_SEPARATOR)
231
+ end
232
+ @uid = org.openhab.core.thing.ChannelUID.new(thing.uid, uid_segments.last)
233
+ unless type.is_a?(org.openhab.core.thing.ChannelTypeUID)
234
+ type = org.openhab.core.thing.ChannelTypeUID.new(thing.uid.binding_id,
235
+ type)
236
+ end
237
+ @type = type
238
+ @label = label
239
+ @parameters = parameters
240
+ end
241
+
242
+ # @!visibility private
243
+ def build
244
+ org.openhab.core.thing.binding.builder.ChannelBuilder.create(uid)
245
+ .with_kind(kind)
246
+ .with_type(type)
247
+ .with_configuration(org.openhab.core.config.core.Configuration.new(parameters))
248
+ .build
249
+ end
250
+
251
+ private
252
+
253
+ def kind
254
+ if @type == :trigger
255
+ org.openhab.core.thing.type.ChannelKind::TRIGGER
256
+ else
257
+ org.openhab.core.thing.type.ChannelKind::STATE
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ #
6
+ # Manages thread local variables for access inside of blocks
7
+ #
8
+ # @!visibility private
9
+ module ThreadLocal
10
+ module_function
11
+
12
+ # Keys to persist
13
+ KNOWN_KEYS = %i[
14
+ openhab_rule_uid
15
+ openhab_rule_type
16
+ openhab_units
17
+ openhab_persistence_service
18
+ openhab_ensure_states
19
+ ].freeze
20
+
21
+ #
22
+ # Fetch all {KNOWN_KEYS} from thread local storage.
23
+ #
24
+ # @return [Hash<Symbol=>Object>]
25
+ #
26
+ def persist
27
+ KNOWN_KEYS.to_h do |key|
28
+ [key, Thread.current[key]]
29
+ end
30
+ end
31
+
32
+ #
33
+ # Execute the supplied block with the supplied values set for the currently running thread
34
+ # The previous values for each key are restored after the block is executed
35
+ #
36
+ # @param [Hash] values Keys and values to set for running thread, if hash is nil no values are set
37
+ #
38
+ def thread_local(**values)
39
+ old_values = values.to_h { |key, _value| [key, Thread.current[key]] }
40
+ values.each { |key, value| Thread.current[key] = value }
41
+ logger.trace "Executing block with thread local context: #{values} - old context: #{old_values}"
42
+ yield
43
+ ensure
44
+ old_values.each { |key, value| Thread.current[key] = value }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "singleton"
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ #
9
+ # Manages timers created by {OpenHAB::DSL.after after}, or {#schedule}.
10
+ #
11
+ class TimerManager
12
+ include Singleton
13
+
14
+ ScriptHandling.script_unloaded { instance.cancel_all }
15
+
16
+ # @!visibility private
17
+ def initialize
18
+ # Tracks active timers
19
+ @timers = java.util.concurrent.ConcurrentHashMap.new
20
+ @timers_by_id = java.util.concurrent.ConcurrentHashMap.new
21
+ end
22
+
23
+ #
24
+ # Create a new timer, managing ids if necessary
25
+ #
26
+ # @!visibility private
27
+ def create(duration, thread_locals:, block:, id:, reschedule:)
28
+ if id
29
+ return @timers_by_id.compute(id) do |_key, old_timer|
30
+ # return the existing timer if we're only supposed to create a new
31
+ # timer when one doesn't already exist
32
+ next old_timer if !reschedule && old_timer
33
+
34
+ old_timer&.cancel
35
+ Core::Timer.new(duration, id: id, thread_locals: thread_locals, block: block)
36
+ end
37
+ end
38
+
39
+ Core::Timer.new(duration, id: id, thread_locals: thread_locals, block: block)
40
+ end
41
+
42
+ # Add a timer that is now active
43
+ # @!visibility private
44
+ def add(timer)
45
+ logger.trace("Adding #{timer} to timers")
46
+ @timers[timer] = 1
47
+ end
48
+
49
+ #
50
+ # Delete a timer that is no longer active
51
+ #
52
+ # @!visibility private
53
+ def delete(timer)
54
+ logger.trace("Removing #{timer} from timers")
55
+ @timers.remove(timer)
56
+ return unless timer.id
57
+
58
+ @timers_by_id.delete(timer.id)
59
+ end
60
+
61
+ #
62
+ # Cancel a single timer by id
63
+ #
64
+ # @param [Object] id
65
+ # @return [true, false] if the timer was able to be cancelled
66
+ #
67
+ def cancel(id)
68
+ result = false
69
+ @timers_by_id.compute_if_present(id) do |_key, timer|
70
+ result = timer.cancel
71
+ nil
72
+ end
73
+ result
74
+ end
75
+
76
+ #
77
+ # Reschedule a single timer by id
78
+ #
79
+ # @param [Object] id
80
+ # @param [java.time.temporal.TemporalAmount, #to_zoned_date_time, Proc, nil] duration
81
+ # When to reschedule the timer for. `nil` to retain its current interval.
82
+ # @return [Timer, nil] the timer if it was rescheduled, otherwise `nil`
83
+ #
84
+ def reschedule(id, duration = nil)
85
+ @timers_by_id.compute_if_present(id) do |_key, timer|
86
+ timer.reschedule(duration)
87
+ end
88
+ end
89
+
90
+ #
91
+ # Schedule a timer by id
92
+ #
93
+ # Schedules a timer by id, but passes the current timer -- if it exists --
94
+ # to the block so that you can decide how you want to proceed based on that
95
+ # state. The timer is created in a thread-safe manner.
96
+ #
97
+ # @param [Object] id
98
+ # @yieldparam [Timer, nil] timer The existing timer with this id, if one exists.
99
+ # @yieldreturn [Timer, nil] A new timer to associate with this id, the existing
100
+ # timer, or nil. If nil, any existing timer will be cancelled.
101
+ # @return [Timer, nil]
102
+ #
103
+ # @example Extend an existing timer, or schedule a new one
104
+ # # This is technically the same functionality as just calling `after()` with an `id`,
105
+ # # but allows you to performa extra steps if the timer is actually scheduled.
106
+ # timers.schedule(item) do |timer|
107
+ # next timer.tap(&:reschedule) if timer
108
+ #
109
+ # notify("The lights were turned on")
110
+ #
111
+ # after(30.seconds) { item.off }
112
+ # end
113
+ #
114
+ # @example Keep trying to turn something on, up to 5 times
115
+ # timers.schedule(item) do |timer|
116
+ # next if timer # don't interrupt a retry cycle if it already exists
117
+ #
118
+ # retries = 5
119
+ # after(2.seconds) do |inner_timer|
120
+ # next if (retries -= 1).zero?
121
+ # next inner_timer.reschedule unless item.on?
122
+ #
123
+ # item.on
124
+ # end
125
+ # end
126
+ #
127
+ def schedule(id)
128
+ @timers_by_id.compute(id) do |_key, timer|
129
+ new_timer = yield timer
130
+ raise ArgumentError, "Block must return a timer or nil" unless timer.is_a?(Core::Timer) || timer.nil?
131
+
132
+ if !new_timer.equal?(timer) && new_timer&.id
133
+ raise ArgumentError,
134
+ "Do not schedule a new timer with an ID inside a #schedule block"
135
+ end
136
+
137
+ if timer&.cancelled?
138
+ new_timer = nil
139
+ elsif new_timer.nil? && !timer&.cancelled?
140
+ timer&.cancel
141
+ end
142
+ next unless new_timer
143
+
144
+ new_timer.id ||= id
145
+ if new_timer.id != id
146
+ raise ArgumentError,
147
+ "The new timer cannot have a different ID than what you're attempting to schedule"
148
+ end
149
+
150
+ new_timer
151
+ end
152
+ end
153
+
154
+ #
155
+ # Checks if a timer exists by id
156
+ #
157
+ # @note This method is not recommended for normal use in rules.
158
+ # Timers are prone to race conditions if accessed from multiple rules,
159
+ # or from timers themselves. Rescheduling, canceling, or scheduling
160
+ # a new timer based on the results of this method may cause problems,
161
+ # since the state may have changed in the meantime. Instead, use
162
+ # {OpenHAB::DSL.after after} with an `id`, {#cancel}, {#reschedule}, or
163
+ # {#schedule} to perform those actions atomically.
164
+ #
165
+ # @param [Object] id
166
+ # @return [true, false]
167
+ #
168
+ def include?(id)
169
+ @timers_by_id.key?(id)
170
+ end
171
+ alias_method :key?, :include?
172
+ alias_method :member?, :include?
173
+
174
+ #
175
+ # Cancels all active timers in the current script/UI rule
176
+ #
177
+ # Including timers with or without an id.
178
+ #
179
+ # @return [void]
180
+ #
181
+ def cancel_all
182
+ logger.trace("Canceling #{@timers.length} timers")
183
+ # don't use #each, in case timers are scheduling more timers
184
+ until @timers.empty?
185
+ timer = @timers.keys.first
186
+ timer.cancel
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ # Version of OpenHAB helper libraries
6
+ # @return [String]
7
+ VERSION = "5.0.0.rc1"
8
+ end
9
+ end