openhab-jrubyscripting 5.0.0.rc3 → 5.0.0.rc5

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions.rb +19 -3
  3. data/lib/openhab/core/items/metadata/hash.rb +2 -1
  4. data/lib/openhab/core/items/metadata/namespace_hash.rb +1 -1
  5. data/lib/openhab/core/items/persistence.rb +18 -8
  6. data/lib/openhab/core/items/semantics.rb +18 -22
  7. data/lib/openhab/core/items.rb +1 -1
  8. data/lib/openhab/core/profile_factory.rb +13 -3
  9. data/lib/openhab/core/provider.rb +3 -3
  10. data/lib/openhab/core/rules/module.rb +26 -0
  11. data/lib/openhab/core/rules/provider.rb +15 -0
  12. data/lib/openhab/core/script_handling.rb +32 -3
  13. data/lib/openhab/core/things/profile_callback.rb +1 -1
  14. data/lib/openhab/core/timer.rb +21 -10
  15. data/lib/openhab/core/types/point_type.rb +1 -1
  16. data/lib/openhab/core_ext/between.rb +32 -0
  17. data/lib/openhab/core_ext/java/duration.rb +1 -0
  18. data/lib/openhab/core_ext/java/local_date.rb +1 -0
  19. data/lib/openhab/core_ext/java/local_time.rb +1 -0
  20. data/lib/openhab/core_ext/java/month.rb +13 -2
  21. data/lib/openhab/core_ext/java/month_day.rb +2 -0
  22. data/lib/openhab/core_ext/java/zoned_date_time.rb +4 -4
  23. data/lib/openhab/core_ext/ruby/date.rb +4 -1
  24. data/lib/openhab/core_ext/ruby/date_time.rb +1 -0
  25. data/lib/openhab/core_ext/ruby/time.rb +1 -0
  26. data/lib/openhab/dsl/items/timed_command.rb +1 -1
  27. data/lib/openhab/dsl/rules/automation_rule.rb +1 -1
  28. data/lib/openhab/dsl/rules/builder.rb +1 -1
  29. data/lib/openhab/dsl/rules/name_inference.rb +1 -1
  30. data/lib/openhab/dsl/rules/rule_triggers.rb +1 -1
  31. data/lib/openhab/dsl/rules/triggers/changed.rb +1 -1
  32. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +2 -2
  33. data/lib/openhab/dsl/version.rb +1 -1
  34. data/lib/openhab/dsl.rb +2 -0
  35. data/lib/openhab/log.rb +7 -0
  36. data/lib/openhab/rspec/helpers.rb +1 -1
  37. data/lib/openhab/rspec/hooks.rb +3 -4
  38. data/lib/openhab/rspec/karaf.rb +6 -2
  39. data/lib/openhab/rspec/mocks/persistence_service.rb +15 -0
  40. data/lib/openhab/rspec/mocks/thing_handler.rb +2 -2
  41. data/lib/openhab/yard/html_helper.rb +3 -3
  42. metadata +8 -7
  43. data/lib/openhab/dsl/script_handling.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce249607265a008aa969e2edbd56302edd70a67a01dfebaec9ad67b7e5f1ad27
4
- data.tar.gz: ac0c8d25509a8be8374a3f15fecd2e575da8cb08b82268aef41414f64f7a3b38
3
+ metadata.gz: d5963df56b31bb66f4d4cf5afa5927719d61740df847c5d5ff0965a188a73c62
4
+ data.tar.gz: 0011f05f77383e9618844437bc63b55d02e62ed9a402d775a76dbc75d20a8334
5
5
  SHA512:
6
- metadata.gz: 7c082f0949b6bc55ebf48b1489eade3f6bbcf25181d6741e29697dbb5ef5221abf583c78fb8ad46596b5ea10957e9d4b5d2d5dfe35097eea004f005d06683d48
7
- data.tar.gz: ee7e740d0dd1233462521313c31145971bed68e2830c765bad27b8762d064bdf154993a2d733fc7743240bd55aec9e0eecd8cb4f2d569dbd0c599e765140cd06
6
+ metadata.gz: ffdb129b03aac2d44365aa42a7f02889f5d8a20f662391b874cb795547b646d6d4f02d014284a73143862ca25f931f42429be012a25b645d02b1e713a02c073b
7
+ data.tar.gz: ca9804074c3092c6bcff87ed17bffe4543ef95b3ef0c230d4767811a266d368210658f36968d4d42b6ed2f320a8935ab28363a318a6bd0d2a7d393afab67a29a
@@ -66,11 +66,27 @@ module OpenHAB
66
66
  #
67
67
  module Actions
68
68
  OSGi.services("org.openhab.core.model.script.engine.action.ActionService")&.each do |service|
69
- java_import service.action_class.ruby_class
70
- logger.trace("Loaded ACTION: #{service.action_class}")
69
+ action_class = service.action_class
70
+ module_name = action_class.simple_name
71
+ action = if action_class.interface?
72
+ impl = OSGi.service(action_class)
73
+ unless impl
74
+ logger.error("Unable to find an implementation object for action service #{action_class}.")
75
+ next
76
+ end
77
+ const_set(module_name, impl)
78
+ else
79
+ (java_import action_class.ruby_class).first
80
+ end
81
+ logger.trace("Loaded ACTION: #{action_class}")
82
+ Object.const_set(module_name, action)
71
83
  end
84
+
72
85
  # Import common actions
73
- %w[Exec HTTP Ping].each { |action| java_import "org.openhab.core.model.script.actions.#{action}" }
86
+ %w[Exec HTTP Ping].each do |action|
87
+ klass = (java_import "org.openhab.core.model.script.actions.#{action}").first
88
+ Object.const_set(action, klass)
89
+ end
74
90
 
75
91
  module_function
76
92
 
@@ -380,6 +380,7 @@ module OpenHAB
380
380
 
381
381
  [value, to_h].inspect
382
382
  end
383
+ remove_method :to_s
383
384
  alias_method :to_s, :inspect
384
385
 
385
386
  #
@@ -399,7 +400,7 @@ module OpenHAB
399
400
  return preferred_provider unless provider
400
401
 
401
402
  unless provider.is_a?(org.openhab.core.common.registry.ManagedProvider)
402
- raise FrozenError, "Cannot modify metadata from provider #{provider.inspect}"
403
+ raise FrozenError, "Cannot modify metadata from provider #{provider.inspect} for #{uid}."
403
404
  end
404
405
 
405
406
  if preferred_provider != provider
@@ -288,7 +288,7 @@ module OpenHAB
288
288
 
289
289
  # @!visibility private
290
290
  def hash
291
- "metadata_namespace_hash".hash + @item_name.hash
291
+ ["metadata_namespace_hash", @item_name.hash]
292
292
  end
293
293
 
294
294
  # @!visibility private
@@ -40,16 +40,26 @@ module OpenHAB
40
40
  module Persistence
41
41
  GenericItem.prepend(self)
42
42
 
43
- # A state class with an added timestamp attribute. This is used to hold OpenHAB's HistoricItem.
43
+ #
44
+ # A state class with an added timestamp attribute.
45
+ #
46
+ # This wraps {org.openhab.core.persistence.HistoricItem HistoricItem}
47
+ # to allow implicitly treating the object as its state, and wrapping of
48
+ # that state into a {QuantityType} as necessary.
49
+ #
44
50
  class HistoricState < SimpleDelegator
45
- attr_reader :timestamp
46
-
47
51
  alias_method :state, :__getobj__
48
52
 
49
- def initialize(state, timestamp)
50
- @timestamp = timestamp
53
+ def initialize(state, historic_item)
54
+ @historic_item = historic_item
51
55
  super(state)
52
56
  end
57
+
58
+ # @!attribute [r] timestamp
59
+ # @return [ZonedDateTime]
60
+ def timestamp
61
+ @historic_item.timestamp
62
+ end
53
63
  end
54
64
 
55
65
  # All persistence methods that could return a QuantityType
@@ -245,7 +255,7 @@ module OpenHAB
245
255
  def previous_state(service = nil, skip_equal: false)
246
256
  service ||= persistence_service
247
257
  result = Actions::PersistenceExtensions.previous_state(self, skip_equal, service&.to_s)
248
- HistoricState.new(quantify(result.state), result.timestamp)
258
+ HistoricState.new(quantify(result.state), result)
249
259
  end
250
260
 
251
261
  PERSISTENCE_METHODS.each do |method|
@@ -286,7 +296,7 @@ module OpenHAB
286
296
  # @return [Object] QuantityType or the original value
287
297
  #
288
298
  def quantify(value)
289
- if value.is_a?(DecimalType) && unit
299
+ if value.is_a?(DecimalType) && respond_to?(:unit) && unit
290
300
  logger.trace("Unitizing #{value} with unit #{unit}")
291
301
  QuantityType.new(value.to_big_decimal, unit)
292
302
  else
@@ -308,7 +318,7 @@ module OpenHAB
308
318
  def wrap_result(result, method)
309
319
  if result.is_a?(org.openhab.core.persistence.HistoricItem)
310
320
  return HistoricState.new(quantify(result.state),
311
- result.timestamp)
321
+ result)
312
322
  end
313
323
  return quantify(result) if QUANTITY_METHODS.include?(method)
314
324
 
@@ -187,11 +187,6 @@ module OpenHAB
187
187
  # end
188
188
  #
189
189
 
190
- # @!visibility private
191
- # import the actual semantics action
192
- SemanticsAction = org.openhab.core.model.script.actions.Semantics
193
- private_constant :SemanticsAction
194
-
195
190
  # import all the semantics constants
196
191
  [org.openhab.core.semantics.model.point.Points,
197
192
  org.openhab.core.semantics.model.property.Properties,
@@ -223,6 +218,7 @@ module OpenHAB
223
218
  # Property = org.openhab.core.semantics.Property
224
219
 
225
220
  # put ourself into the global namespace, replacing the action
221
+ Object.send(:remove_const, :Semantics)
226
222
  ::Semantics = self # rubocop:disable Naming/ConstantName
227
223
 
228
224
  #
@@ -234,7 +230,7 @@ module OpenHAB
234
230
  # @return [true, false]
235
231
  #
236
232
  def location?
237
- SemanticsAction.location?(self)
233
+ Actions::Semantics.location?(self)
238
234
  end
239
235
 
240
236
  #
@@ -246,7 +242,7 @@ module OpenHAB
246
242
  # @return [true, false]
247
243
  #
248
244
  def equipment?
249
- SemanticsAction.equipment?(self)
245
+ Actions::Semantics.equipment?(self)
250
246
  end
251
247
 
252
248
  # Checks if this Item is a {Point}
@@ -257,7 +253,7 @@ module OpenHAB
257
253
  # @return [true, false]
258
254
  #
259
255
  def point?
260
- SemanticsAction.point?(self)
256
+ Actions::Semantics.point?(self)
261
257
  end
262
258
 
263
259
  #
@@ -280,7 +276,7 @@ module OpenHAB
280
276
  # @return [GenericItem, nil]
281
277
  #
282
278
  def location
283
- SemanticsAction.get_location(self)&.then(&Proxy.method(:new))
279
+ Actions::Semantics.get_location(self)&.then(&Proxy.method(:new))
284
280
  end
285
281
 
286
282
  #
@@ -293,7 +289,7 @@ module OpenHAB
293
289
  # @return [Class, nil]
294
290
  #
295
291
  def location_type
296
- SemanticsAction.get_location_type(self)&.ruby_class
292
+ Actions::Semantics.get_location_type(self)&.ruby_class
297
293
  end
298
294
 
299
295
  #
@@ -306,7 +302,7 @@ module OpenHAB
306
302
  # @return [GenericItem, nil]
307
303
  #
308
304
  def equipment
309
- SemanticsAction.get_equipment(self)&.then(&Proxy.method(:new))
305
+ Actions::Semantics.get_equipment(self)&.then(&Proxy.method(:new))
310
306
  end
311
307
 
312
308
  #
@@ -319,7 +315,7 @@ module OpenHAB
319
315
  # @return [Class, nil]
320
316
  #
321
317
  def equipment_type
322
- SemanticsAction.get_equipment_type(self)&.ruby_class
318
+ Actions::Semantics.get_equipment_type(self)&.ruby_class
323
319
  end
324
320
 
325
321
  #
@@ -330,7 +326,7 @@ module OpenHAB
330
326
  # @return [Class, nil]
331
327
  #
332
328
  def point_type
333
- SemanticsAction.get_point_type(self)&.ruby_class
329
+ Actions::Semantics.get_point_type(self)&.ruby_class
334
330
  end
335
331
 
336
332
  #
@@ -341,7 +337,7 @@ module OpenHAB
341
337
  # @return [Class, nil]
342
338
  #
343
339
  def property_type
344
- SemanticsAction.get_property_type(self)&.ruby_class
340
+ Actions::Semantics.get_property_type(self)&.ruby_class
345
341
  end
346
342
 
347
343
  # @!attribute [r] semantic_type
@@ -355,7 +351,7 @@ module OpenHAB
355
351
  # @return [Class, nil]
356
352
  #
357
353
  def semantic_type
358
- SemanticsAction.get_semantic_type(self)&.ruby_class
354
+ Actions::Semantics.get_semantic_type(self)&.ruby_class
359
355
  end
360
356
 
361
357
  #
@@ -409,7 +405,7 @@ module Enumerable
409
405
  # Returns a new array of items that are a semantics Location (optionally of the given type)
410
406
  # @return [Array<GenericItem>]
411
407
  def locations(type = nil)
412
- if type && (!type.is_a?(Module) || !(type < OpenHAB::Core::Items::Semantics::Location))
408
+ if type && (!type.is_a?(Module) || !(type < Semantics::Location))
413
409
  raise ArgumentError, "type must be a subclass of Location"
414
410
  end
415
411
 
@@ -432,7 +428,7 @@ module Enumerable
432
428
  # @example Get all TVs in a room
433
429
  # lGreatRoom.equipments(Semantics::Screen)
434
430
  def equipments(type = nil)
435
- if type && (!type.is_a?(Module) || !(type < OpenHAB::Core::Items::Semantics::Equipment))
431
+ if type && (!type.is_a?(Module) || !(type < Semantics::Equipment))
436
432
  raise ArgumentError, "type must be a subclass of Equipment"
437
433
  end
438
434
 
@@ -457,19 +453,19 @@ module Enumerable
457
453
  end
458
454
  unless point_or_property_types.all? do |tag|
459
455
  tag.is_a?(Module) &&
460
- (tag < OpenHAB::Core::Items::Semantics::Point || tag < OpenHAB::Core::Items::Semantics::Property)
456
+ (tag < Semantics::Point || tag < Semantics::Property)
461
457
  end
462
458
  raise ArgumentError, "point_or_property_types must all be a subclass of Point or Property"
463
459
  end
464
- if point_or_property_types.count { |tag| tag < OpenHAB::Core::Items::Semantics::Point } > 1 ||
465
- point_or_property_types.count { |tag| tag < OpenHAB::Core::Items::Semantics::Property } > 1
460
+ if point_or_property_types.count { |tag| tag < Semantics::Point } > 1 ||
461
+ point_or_property_types.count { |tag| tag < Semantics::Property } > 1
466
462
  raise ArgumentError, "point_or_property_types cannot both be a subclass of Point or Property"
467
463
  end
468
464
 
469
465
  select do |point|
470
466
  point.point? && point_or_property_types.all? do |tag|
471
- (tag < OpenHAB::Core::Items::Semantics::Point && point.point_type <= tag) ||
472
- (tag < OpenHAB::Core::Items::Semantics::Property && point.property_type&.<=(tag))
467
+ (tag < Semantics::Point && point.point_type <= tag) ||
468
+ (tag < Semantics::Property && point.property_type&.<=(tag))
473
469
  end
474
470
  end
475
471
  end
@@ -100,7 +100,7 @@ module OpenHAB
100
100
  constants.map { |c| const_get(c) }
101
101
  .grep(Module)
102
102
  .select { |k| k <= GenericItem && k != GroupItem && k != StringItem }
103
- .sort { |a, b| a < b ? 1 : -1 }
103
+ .sort { |a, b| (a < b) ? 1 : -1 }
104
104
  .each do |klass|
105
105
  klass.field_reader :ACCEPTED_COMMAND_TYPES, :ACCEPTED_DATA_TYPES unless klass == GenericItem
106
106
 
@@ -98,6 +98,19 @@ module OpenHAB
98
98
 
99
99
  def initialize
100
100
  @profiles = {}
101
+
102
+ @registration = OSGi.register_service(self)
103
+ ScriptHandling.script_unloaded { unregister }
104
+ end
105
+
106
+ #
107
+ # Unregister the ProfileFactory OSGi service
108
+ #
109
+ # @!visibility private
110
+ # @return [void]
111
+ #
112
+ def unregister
113
+ @registration.unregister
101
114
  end
102
115
 
103
116
  # @!visibility private
@@ -113,8 +126,5 @@ module OpenHAB
113
126
  @profiles.keys
114
127
  end
115
128
  end
116
-
117
- registration = OSGi.register_service(ProfileFactory.instance)
118
- ScriptHandling.script_unloaded { registration.unregister }
119
129
  end
120
130
  end
@@ -212,11 +212,11 @@ module OpenHAB
212
212
 
213
213
  private
214
214
 
215
- def initialize
216
- super
215
+ def initialize(script_unloaded_before: nil)
216
+ super()
217
217
  @elements = {}
218
218
  self.class.registry.add_provider(self)
219
- ScriptHandling.script_unloaded { unregister }
219
+ ScriptHandling.script_unloaded(before: script_unloaded_before) { unregister }
220
220
  end
221
221
  end
222
222
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module Core
5
+ module Rules
6
+ # @interface
7
+ java_import org.openhab.core.automation.Module
8
+
9
+ # @!visibility private
10
+ module Module
11
+ # @return [String]
12
+ def inspect
13
+ r = "#<OpenHAB::Core::Rules::#{self.class.simple_name} #{id} (#{type_uid})"
14
+ r += " #{label.inspect}" if label
15
+ r += " configuration=#{configuration.properties.to_h}" unless configuration.properties.empty?
16
+ "#{r}>"
17
+ end
18
+
19
+ # @return [String]
20
+ def to_s
21
+ id
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -19,6 +19,21 @@ module OpenHAB
19
19
  $rules
20
20
  end
21
21
  end
22
+
23
+ def initialize
24
+ super(script_unloaded_before: lambda do |callbacks|
25
+ callbacks.index do |cb|
26
+ case cb.binding.receiver
27
+ when Items::Provider,
28
+ Things::Provider,
29
+ DSL::TimerManager
30
+ true
31
+ else
32
+ false
33
+ end
34
+ end
35
+ end)
36
+ end
22
37
  end
23
38
  end
24
39
  end
@@ -29,9 +29,11 @@ module OpenHAB
29
29
  # end
30
30
  #
31
31
  def script_loaded(&block)
32
- Core::ScriptHandlingCallbacks.script_loaded_hooks << block
32
+ ScriptHandlingCallbacks.script_loaded_hooks << block
33
33
  end
34
34
 
35
+ #
36
+ # @!method script_unloaded(&block)
35
37
  #
36
38
  # Add a block of code to be executed when the script is unloaded.
37
39
  #
@@ -47,8 +49,10 @@ module OpenHAB
47
49
  # logger.info 'Hi, this script has been unloaded'
48
50
  # end
49
51
  #
50
- def script_unloaded(&block)
51
- Core::ScriptHandlingCallbacks.script_unloaded_hooks << block
52
+ def script_unloaded(before: nil, &block)
53
+ # `before` is as yet undocumented, because I'm not set on its interface
54
+ index = before.call(ScriptHandlingCallbacks.script_unloaded_hooks) if before
55
+ ScriptHandlingCallbacks.script_unloaded_hooks.insert(index || -1, block)
52
56
  end
53
57
  end
54
58
 
@@ -58,6 +62,13 @@ module OpenHAB
58
62
  # @!visibility private
59
63
  module ScriptHandlingCallbacks
60
64
  class << self
65
+ #
66
+ # Has the script completed loading?
67
+ #
68
+ # @!visibility private
69
+ # @return [true, false]
70
+ attr_accessor :script_loaded
71
+
61
72
  #
62
73
  # Return script_loaded_hooks
63
74
  #
@@ -74,6 +85,7 @@ module OpenHAB
74
85
  @script_unloaded_hooks ||= []
75
86
  end
76
87
  end
88
+ self.script_loaded = false
77
89
 
78
90
  #
79
91
  # Executed when OpenHAB unloads a script file
@@ -85,6 +97,22 @@ module OpenHAB
85
97
  rescue => e
86
98
  logger.error("Failed to call script_unloaded hook #{hook}: #{e}")
87
99
  end
100
+
101
+ return if ScriptHandlingCallbacks.script_loaded
102
+
103
+ # Make sure we terminate the main thread if it's still set up, in case
104
+ # it's timing out and that's why we're unloading.
105
+ #
106
+ # It would seem simpler to just record Thread.current when this file
107
+ # loads, but if the user is using the autorequire feature of the
108
+ # jrubyscripting addon, this file will load before the main script.
109
+ #
110
+ # Note that Thread.list only includes threads that have had Ruby
111
+ # execute in them, so we don't need to worry about accidentally killing
112
+ # a random Java thread.
113
+ #
114
+ main_thread = Thread.list.find { |t| t != Thread.current && t.name.include?("-safeCall-") }
115
+ main_thread&.raise(Interrupt.new)
88
116
  end
89
117
 
90
118
  #
@@ -97,6 +125,7 @@ module OpenHAB
97
125
  rescue => e
98
126
  logger.error("Failed to call script_loaded hook #{hook}: #{e}")
99
127
  end
128
+ ScriptHandlingCallbacks.script_loaded = true
100
129
  end
101
130
  end
102
131
  end
@@ -19,7 +19,7 @@ module OpenHAB
19
19
  def def_state_parsing_method(method, param_name)
20
20
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
21
21
  def #{method}(type) # def handle_command(type)
22
- type = link.item.format_#{param_name == :state ? :update : param_name}(type) # type = link.item.format_command(type)
22
+ type = link.item.format_#{(param_name == :state) ? :update : param_name}(type) # type = link.item.format_command(type)
23
23
  super(type) # super(type)
24
24
  end # end
25
25
  RUBY
@@ -8,8 +8,6 @@ module OpenHAB
8
8
  # Timer allows you to administer the block of code that
9
9
  # has been scheduled to run later with {OpenHAB::DSL.after after}.
10
10
  #
11
- # @!attribute [r] execution_time
12
- # @return [ZonedDateTime] the scheduled execution time, or null if the timer was cancelled
13
11
  class Timer
14
12
  extend Forwardable
15
13
 
@@ -30,7 +28,7 @@ module OpenHAB
30
28
  # @return [true,false]
31
29
 
32
30
  def_delegator :@timer, :has_terminated, :terminated?
33
- def_delegators :@timer, :execution_time, :active?, :cancelled?, :running?
31
+ def_delegators :@timer, :active?, :cancelled?, :running?
34
32
 
35
33
  # @return [Object, nil]
36
34
  attr_accessor :id
@@ -51,13 +49,19 @@ module OpenHAB
51
49
  @id = id
52
50
  @thread_locals = thread_locals
53
51
  @block = block
54
- @timer = org.openhab.core.model.script.actions.ScriptExecution.create_timer(
55
- # create it far enough in the future so it won't execute until we finish setting it up
56
- 1.minute.from_now,
57
- # when running in rspec, it may have troubles finding this class
58
- # for auto-conversion of block to interface, so use .impl
59
- org.eclipse.xtext.xbase.lib.Procedures::Procedure0.impl { execute }
60
- )
52
+ @timer = if defined?(ScriptExecution)
53
+ ScriptExecution.create_timer(1.minute.from_now) { execute }
54
+ else # DEPRECATED: openHAB 3.4.0
55
+ org.openhab.core.model.script.actions.ScriptExecution.create_timer(
56
+ # create it far enough in the future so it won't execute until we finish setting it up
57
+ 1.minute.from_now,
58
+ # when running in rspec, it may have troubles finding this class
59
+ # for auto-conversion of block to interface, so use .impl
60
+ org.eclipse.xtext.xbase.lib.Procedures::Procedure0.impl { execute }
61
+ )
62
+ end
63
+ # DEPRECATED: openHAB 3.4.0.M6
64
+ @timer.class.field_reader :future unless @timer.respond_to?(:future)
61
65
  reschedule(@time)
62
66
  end
63
67
 
@@ -74,6 +78,13 @@ module OpenHAB
74
78
  end
75
79
  alias_method :to_s, :inspect
76
80
 
81
+ # @!attribute [r] execution_time
82
+ # @return [ZonedDateTime, nil] the scheduled execution time, or `nil` if the timer was cancelled
83
+ def execution_time
84
+ # DEPRECATED: openHAB 3.4.0.M6 (just remove the entire method)
85
+ @timer.future.scheduled_time
86
+ end
87
+
77
88
  #
78
89
  # Reschedule timer
79
90
  #
@@ -21,7 +21,7 @@ module OpenHAB
21
21
  if value.is_a?(DecimalType) || value.is_a?(StringType)
22
22
  value
23
23
  elsif value.is_a?(QuantityType)
24
- unit = index == 2 ? DSL.unit(SIUnits::METRE.dimension) || SIUnits::METRE : Units::DEGREE_ANGLE
24
+ unit = (index == 2) ? DSL.unit(SIUnits::METRE.dimension) || SIUnits::METRE : Units::DEGREE_ANGLE
25
25
  DecimalType.new(value.to_unit(unit).to_big_decimal)
26
26
  elsif value.respond_to?(:to_str)
27
27
  StringType.new(value.to_str)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module CoreExt
5
+ # Extensions that apply to both Date and Time classes
6
+ module Between
7
+ #
8
+ # Checks whether the the object falls between the given range.
9
+ #
10
+ # @overload between?(min, max)
11
+ # @param [Object] min The minimum value to check, inclusive
12
+ # @param [Object] max The maximum value to check, inclusive
13
+ # @return [true,false]
14
+ #
15
+ # @overload between?(range)
16
+ # @param [Range] range A range to check
17
+ # @return [true,false]
18
+ #
19
+ def between?(min, max = nil)
20
+ range = if max
21
+ Range.new(min, max, false)
22
+ else
23
+ raise ArgumentError, "Expecting a range when given a single argument" unless min.is_a?(Range)
24
+
25
+ min
26
+ end
27
+
28
+ OpenHAB::DSL.between(range).cover?(self)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -7,6 +7,7 @@ module OpenHAB
7
7
 
8
8
  # Extensions to Duration
9
9
  class Duration
10
+ include Between
10
11
  # @!parse include TemporalAmount
11
12
 
12
13
  alias_method :to_i, :seconds
@@ -10,6 +10,7 @@ module OpenHAB
10
10
  # Extensions to LocalDate
11
11
  class LocalDate
12
12
  include Time
13
+ include Between
13
14
 
14
15
  class << self # rubocop:disable Lint/EmptyClass
15
16
  # @!attribute [r] now
@@ -33,6 +33,7 @@ module OpenHAB
33
33
  # end
34
34
  #
35
35
  class LocalTime
36
+ include Between
36
37
  # @!parse include Time
37
38
 
38
39
  # @!visibility private
@@ -5,12 +5,23 @@ require_relative "time"
5
5
  module OpenHAB
6
6
  module CoreExt
7
7
  module Java
8
- java_import java.time.Month
8
+ Month = java.time.Month
9
9
 
10
10
  # Extensions to Month
11
11
  class Month
12
+ include Between
12
13
  # @!parse include Time
13
14
 
15
+ # @return [Month]
16
+ def +(other)
17
+ plus(other)
18
+ end
19
+
20
+ # @return [Month]
21
+ def -(other)
22
+ minus(other)
23
+ end
24
+
14
25
  #
15
26
  # Returns the next month
16
27
  #
@@ -49,7 +60,7 @@ module OpenHAB
49
60
  # during conversion. {ZonedDateTime.now} is assumed if not given.
50
61
  # @return [ZonedDateTime]
51
62
  def to_zoned_date_time(context = nil)
52
- to_local_date(context).to_zoned_date_time
63
+ to_local_date(context).to_zoned_date_time(context)
53
64
  end
54
65
  end
55
66
  end
@@ -9,6 +9,8 @@ module OpenHAB
9
9
 
10
10
  # Extensions to MonthDay
11
11
  class MonthDay
12
+ include Between
13
+
12
14
  class << self
13
15
  #
14
16
  # Parses strings in the form "M-d"
@@ -10,6 +10,7 @@ module OpenHAB
10
10
  # Extensions to ZonedDateTime
11
11
  class ZonedDateTime
12
12
  include Time
13
+ include Between
13
14
 
14
15
  class << self # rubocop:disable Lint/EmptyClass
15
16
  # @!attribute [r] now
@@ -91,11 +92,10 @@ module OpenHAB
91
92
  # compare instants, otherwise it will differ by timezone, which we don't want
92
93
  # (use eql? if you care about that)
93
94
  if other.respond_to?(:to_zoned_date_time)
94
- return to_instant.compare_to(other.to_zoned_date_time(self).to_instant)
95
+ to_instant.compare_to(other.to_zoned_date_time(self).to_instant)
96
+ elsif other.respond_to?(:coerce) && (lhs, rhs = other.coerce(self))
97
+ lhs <=> rhs
95
98
  end
96
- return nil unless (lhs, rhs = other.coerce(self))
97
-
98
- lhs <=> rhs
99
99
  end
100
100
 
101
101
  #
@@ -4,6 +4,8 @@ require "date"
4
4
 
5
5
  # Extensions to Date
6
6
  class Date
7
+ include OpenHAB::CoreExt::Between
8
+
7
9
  #
8
10
  # Extends {#+} to allow adding a {java.time.temporal.TemporalAmount TemporalAmount}
9
11
  #
@@ -65,7 +67,7 @@ class Date
65
67
 
66
68
  return self <=> other.to_date(self) if other.is_a?(java.time.MonthDay)
67
69
 
68
- if other.respond_to?(:coerce) && (lhs, rhs = coerce(self))
70
+ if other.respond_to?(:coerce) && (lhs, rhs = other.coerce(self))
69
71
  return lhs <=> rhs
70
72
  end
71
73
 
@@ -87,5 +89,6 @@ class Date
87
89
  [other.to_date, self]
88
90
  end
89
91
 
92
+ remove_method :inspect
90
93
  alias_method :inspect, :to_s
91
94
  end
@@ -10,6 +10,7 @@ require "forwardable"
10
10
  # Extensions to DateTime
11
11
  class DateTime < Date
12
12
  extend Forwardable
13
+ include OpenHAB::CoreExt::Between
13
14
 
14
15
  # (see Time#plus_with_temporal)
15
16
  def plus_with_temporal(other)
@@ -5,6 +5,7 @@ require "forwardable"
5
5
  # Extensions to Time
6
6
  class Time
7
7
  extend Forwardable
8
+ include OpenHAB::CoreExt::Between
8
9
 
9
10
  #
10
11
  # @!method +(other)
@@ -211,7 +211,7 @@ module OpenHAB
211
211
  def execute(_mod = nil, inputs = nil)
212
212
  ThreadLocal.thread_local(**@thread_locals) do
213
213
  @timed_command_details.mutex.synchronize do
214
- logger.trace "Canceling implicit timer #{@timed_command_details.timer} for "\
214
+ logger.trace "Canceling implicit timer #{@timed_command_details.timer} for " \
215
215
  "#{@timed_command_details.item.name} because received event #{inputs}"
216
216
  @timed_command_details.timer.cancel
217
217
  DSL.rules.remove(@timed_command_details.rule_uid)
@@ -170,7 +170,7 @@ module OpenHAB
170
170
  now = Time.now
171
171
  return true if @between.cover? now
172
172
 
173
- logger.trace("Skipped execution of rule '#{name}' because the current time #{now} "\
173
+ logger.trace("Skipped execution of rule '#{name}' because the current time #{now} " \
174
174
  "is not between #{@between.begin} and #{@between.end}")
175
175
  else
176
176
  logger.trace("Skipped execution of rule '#{name}' because of guard #{@guard}")
@@ -1417,7 +1417,7 @@ module OpenHAB
1417
1417
  added_rule = add_rule(provider, rule)
1418
1418
  # add config so that MainUI can show the script
1419
1419
  added_rule.actions.first.configuration.put("type", "application/x-ruby")
1420
- added_rule.actions.first.configuration.put("script", script)
1420
+ added_rule.actions.first.configuration.put("script", script) if script
1421
1421
 
1422
1422
  rule.execute(nil, { "event" => Struct.new(:attachment).new(start_attachment) }) if on_start?
1423
1423
  added_rule
@@ -110,7 +110,7 @@ module OpenHAB
110
110
 
111
111
  # formulate a readable rule name from a channel link trigger
112
112
  def infer_rule_name_from_channel_link_trigger(trigger)
113
- trigger == :channel_linked ? "Channel linked to item" : "Channel unlinked from item"
113
+ (trigger == :channel_linked) ? "Channel linked to item" : "Channel unlinked from item"
114
114
  end
115
115
 
116
116
  # formulate a readable rule name from a thing added/updated/remove trigger
@@ -44,7 +44,7 @@ module OpenHAB
44
44
  def append_trigger(type:, config:, attach: nil, conditions: nil)
45
45
  config.transform_keys!(&:to_s)
46
46
  RuleTriggers.trigger(type: type, config: config).tap do |trigger|
47
- logger.trace("Appending trigger (#{trigger}) attach (#{attach}) conditions(#{conditions})")
47
+ logger.trace("Appending trigger (#{trigger.inspect}) attach (#{attach}) conditions(#{conditions})")
48
48
  @triggers << trigger
49
49
  @attachments[trigger.id] = attach if attach
50
50
  @trigger_conditions[trigger.id] = conditions if conditions
@@ -60,7 +60,7 @@ module OpenHAB
60
60
  #
61
61
  def wait_trigger(item:, duration:, to: nil, from: nil, attach: nil)
62
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}) "\
63
+ logger.trace("Creating Changed Wait Change Trigger for Item(#{item_name}) Duration(#{duration}) " \
64
64
  "To(#{to}) From(#{from}) Attach(#{attach})")
65
65
  conditions = Conditions::Duration.new(to: to, from: from, duration: duration)
66
66
  changed_trigger(item: item, to: nil, from: nil, attach: attach, conditions: conditions)
@@ -24,7 +24,7 @@ module OpenHAB
24
24
  @conditions = Conditions::Proc.new(to: to, from: from)
25
25
  @duration = duration
26
26
  @timer = nil
27
- logger.trace "Created Duration Condition To(#{to}) From(#{from}) "\
27
+ logger.trace "Created Duration Condition To(#{to}) From(#{from}) " \
28
28
  "Conditions(#{@conditions}) Duration(#{@duration})"
29
29
  end
30
30
 
@@ -109,7 +109,7 @@ module OpenHAB
109
109
  state, = retrieve_states(inputs)
110
110
  if state == @tracking_to
111
111
  logger.trace("Item changed to #{state} for #{self}, rescheduling timer.")
112
- @timer.reschedule(ZonedDateTime.now.plus(@duration))
112
+ @timer.reschedule(@duration)
113
113
  else
114
114
  logger.trace("Item changed to #{state} for #{self}, canceling timer.")
115
115
  @timer.cancel
@@ -4,6 +4,6 @@ module OpenHAB
4
4
  module DSL
5
5
  # Version of OpenHAB helper libraries
6
6
  # @return [String]
7
- VERSION = "5.0.0.rc3"
7
+ VERSION = "5.0.0.rc5"
8
8
  end
9
9
  end
data/lib/openhab/dsl.rb CHANGED
@@ -299,6 +299,8 @@ module OpenHAB
299
299
  # @example Create a time range
300
300
  # between('7am'..'12pm').cover?(LocalTime.now)
301
301
  #
302
+ # @see CoreExt::Between#between? #between?
303
+ #
302
304
  def between(range)
303
305
  raise ArgumentError, "Supplied object must be a range" unless range.is_a?(Range)
304
306
 
data/lib/openhab/log.rb CHANGED
@@ -235,12 +235,19 @@ module OpenHAB
235
235
  alias_method :to_s, :inspect
236
236
 
237
237
  # @!attribute [rw] level
238
+ #
239
+ # @note When a logger's level is modified, the logging infrastructure has
240
+ # to reload, and logging may be completely unavailable for a short time.
241
+ #
238
242
  # @return [:error,:warn,:info,:debug,:trace] The current log level
243
+ #
239
244
  def level
240
245
  Logger.log_service.get_level(name)[name]&.downcase&.to_sym
241
246
  end
242
247
 
243
248
  def level=(level)
249
+ return if self.level == level
250
+
244
251
  Logger.log_service.set_level(name, level.to_s)
245
252
  end
246
253
 
@@ -124,7 +124,7 @@ module OpenHAB
124
124
  #
125
125
  def time_travel_and_execute_timers(duration)
126
126
  if self.class.mock_timers?
127
- Timecop.travel(duration)
127
+ Timecop.frozen? ? Timecop.freeze(duration) : Timecop.travel(duration)
128
128
  execute_timers
129
129
  else
130
130
  sleep duration
@@ -57,9 +57,8 @@ module OpenHAB
57
57
  tm.class.field_reader :storage
58
58
  tm.storage.keys.each { |k| tm.storage.remove(k) } # rubocop:disable Style/HashEachMethods not a hash
59
59
 
60
- profile_factory = Core::ProfileFactory.send(:new)
61
- @profile_factory_registration = OSGi.register_service(profile_factory)
62
- allow(Core::ProfileFactory).to receive(:instance).and_return(profile_factory)
60
+ @profile_factory = Core::ProfileFactory.send(:new)
61
+ allow(Core::ProfileFactory).to receive(:instance).and_return(@profile_factory)
63
62
 
64
63
  stub_const("OpenHAB::Core::Timer", Mocks::Timer) if self.class.mock_timers?
65
64
 
@@ -72,7 +71,7 @@ module OpenHAB
72
71
  config.after do
73
72
  Core::Items::Proxy.reset_cache
74
73
  Core::Things::Proxy.reset_cache
75
- @profile_factory_registration.unregister
74
+ @profile_factory.unregister
76
75
  timers.cancel_all
77
76
  # timers and rules have already been canceled, so we can safely just
78
77
  # wipe this
@@ -351,8 +351,7 @@ module OpenHAB
351
351
  }.freeze
352
352
  private_constant :BLOCKED_COMPONENTS
353
353
 
354
- START_LEVEL_OVERRIDES = {
355
- }.freeze
354
+ START_LEVEL_OVERRIDES = {}.freeze
356
355
  private_constant :START_LEVEL_OVERRIDES
357
356
 
358
357
  def set_up_bundle_listener
@@ -429,6 +428,11 @@ module OpenHAB
429
428
 
430
429
  tm.bundleResolver = Mocks::BundleResolver.instance
431
430
 
431
+ require_relative "mocks/safe_caller"
432
+ field = tm.class.java_class.declared_field :safeCaller
433
+ field.accessible = true
434
+ field.set(tm, Mocks::SafeCaller.instance)
435
+
432
436
  require_relative "mocks/thing_handler"
433
437
  thf = Mocks::ThingHandlerFactory.instance
434
438
  bundle = org.osgi.framework.FrameworkUtil.get_bundle(org.openhab.core.thing.Thing)
@@ -19,6 +19,21 @@ module OpenHAB
19
19
  end
20
20
  end
21
21
 
22
+ module HistoricState
23
+ def timestamp
24
+ # PersistenceExtensions uses an anonymous class to wrap the current
25
+ # state if that happens to be an answer. Except it calls
26
+ # ZonedDateTime.now in Java land, bypassing Timecop.
27
+ # Detect that and make the call in Ruby
28
+ #
29
+ jc = @historic_item.class.java_class
30
+ return ZonedDateTime.now if jc.anonymous? && jc.enclosing_class == PersistenceExtensions.java_class
31
+
32
+ super
33
+ end
34
+ end
35
+ Core::Items::Persistence::HistoricState.prepend(HistoricState)
36
+
22
37
  attr_reader :id
23
38
 
24
39
  def initialize
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Naming have to follow java interface names
3
+ # rubocop:disable Naming/MethodName, Naming/AccessorMethodName
4
4
 
5
5
  require "singleton"
6
6
 
@@ -73,4 +73,4 @@ module OpenHAB
73
73
  end
74
74
  end
75
75
  end
76
- # rubocop:enable Naming
76
+ # rubocop:enable Naming/MethodName, Naming/AccessorMethodName
@@ -16,8 +16,8 @@ module OpenHAB
16
16
 
17
17
  # have to completely replace this method. only change is the regex splitting
18
18
  # into parts now allows `.` as part of the identifier
19
- # rubocop:disable Style
20
- def format_types(typelist, brackets = true)
19
+ # rubocop:disable Style/NestedTernaryOperator, Style/StringConcatenation, Style/TernaryParentheses
20
+ def format_types(typelist, brackets = true) # rubocop:disable Style/OptionalBooleanParameter
21
21
  return unless typelist.is_a?(Array)
22
22
 
23
23
  list = typelist.map do |type|
@@ -27,7 +27,7 @@ module OpenHAB
27
27
  end
28
28
  list.empty? ? "" : (brackets ? "(#{list.join(", ")})" : list.join(", "))
29
29
  end
30
- # rubocop:enable Style
30
+ # rubocop:enable Style/NestedTernaryOperator, Style/StringConcatenation, Style/TernaryParentheses
31
31
 
32
32
  def link_object(obj, title = nil, *)
33
33
  ::YARD::Handlers::JRuby::Base.infer_java_class(obj) if obj.is_a?(String)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-jrubyscripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.rc3
4
+ version: 5.0.0.rc5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-26 00:00:00.000000000 Z
11
+ date: 2022-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '7.1'
75
+ version: '8.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '7.1'
82
+ version: '8.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: cuke_linter
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -198,14 +198,14 @@ dependencies:
198
198
  requirements:
199
199
  - - "~>"
200
200
  - !ruby/object:Gem::Version
201
- version: '12.0'
201
+ version: '13.0'
202
202
  type: :development
203
203
  prerelease: false
204
204
  version_requirements: !ruby/object:Gem::Requirement
205
205
  requirements:
206
206
  - - "~>"
207
207
  - !ruby/object:Gem::Version
208
- version: '12.0'
208
+ version: '13.0'
209
209
  - !ruby/object:Gem::Dependency
210
210
  name: rspec
211
211
  requirement: !ruby/object:Gem::Requirement
@@ -369,6 +369,7 @@ files:
369
369
  - lib/openhab/core/provider.rb
370
370
  - lib/openhab/core/registry.rb
371
371
  - lib/openhab/core/rules.rb
372
+ - lib/openhab/core/rules/module.rb
372
373
  - lib/openhab/core/rules/provider.rb
373
374
  - lib/openhab/core/rules/registry.rb
374
375
  - lib/openhab/core/rules/rule.rb
@@ -408,6 +409,7 @@ files:
408
409
  - lib/openhab/core/types/up_down_type.rb
409
410
  - lib/openhab/core/uid.rb
410
411
  - lib/openhab/core_ext.rb
412
+ - lib/openhab/core_ext/between.rb
411
413
  - lib/openhab/core_ext/java/class.rb
412
414
  - lib/openhab/core_ext/java/duration.rb
413
415
  - lib/openhab/core_ext/java/list.rb
@@ -455,7 +457,6 @@ files:
455
457
  - lib/openhab/dsl/rules/triggers/updated.rb
456
458
  - lib/openhab/dsl/rules/triggers/watch/watch.rb
457
459
  - lib/openhab/dsl/rules/triggers/watch/watch_handler.rb
458
- - lib/openhab/dsl/script_handling.rb
459
460
  - lib/openhab/dsl/things/builder.rb
460
461
  - lib/openhab/dsl/thread_local.rb
461
462
  - lib/openhab/dsl/timer_manager.rb
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OpenHAB
4
- module DSL
5
- end
6
- end