openhab-jrubyscripting 5.0.0.rc3 → 5.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
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