openhab-scripting 5.13.0 → 5.14.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfe5a24aad596dbbc699c68d39ffec4417b9199a75738a54292386d80e725525
4
- data.tar.gz: 7aa7877384c2df3ee41588660e3db596f807fd5b2b7b781921528f14b0f9fdeb
3
+ metadata.gz: 29d5852ff9f0b047b5e70f69d8ece471770b59a86f4989a3120a7c6027fec1b9
4
+ data.tar.gz: 39c590e109e6c5349fd6b4b5eee9bf2e5a3058f4b8cf875b6b69f02bb89996df
5
5
  SHA512:
6
- metadata.gz: 756ab20dcfdfce97ba6dfdcbf76d1f40c544607312622e0e013ddd38f7a0d201ae0fec69c13017300fba938d7081eb89f5be5d65ad59abcadf17380ae580dad8
7
- data.tar.gz: d9facdcb106fe9fa3e2e51574fa2e52168b9c07eb9c1fad1498a6380e9bc783c8a63535c2a715956a894eb39a2b741ff6f9284fb1de71f9e083262b9f4ed42d0
6
+ metadata.gz: 41707220dfe28a3a7395b4d0b7c8538e8150448934d97f63d5b96a533f859f681b47d68bb83688cfa316737b34622f90c7da670915a454427a9c4c705d9cfa9e
7
+ data.tar.gz: 9c8ae71904b136e9aa2e11027e0de57101721b7096352994a2d636595f69b32a07e0992bf476a0200e0e6de5330a6a70d6e6f363f2e1f4c687ff1b033f013710
@@ -252,10 +252,10 @@ module OpenHAB
252
252
  end
253
253
 
254
254
  # @!attribute thing [r]
255
- # Return the item's thing if this item is linked with a thing. If an item is linked to more than one thing,
255
+ # Return the item's thing if this item is linked with a thing. If an item is linked to more than one channel,
256
256
  # this method only returns the first thing.
257
257
  #
258
- # @return [Thing] The thing associated with this item or nil
258
+ # @return [Things::Thing, nil]
259
259
  def thing
260
260
  all_linked_things.first
261
261
  end
@@ -264,12 +264,46 @@ module OpenHAB
264
264
  # @!attribute things [r]
265
265
  # Returns all of the item's linked things.
266
266
  #
267
- # @return [Array<Thing>] An array of things or an empty array
267
+ # @return [Array<Things::Thing>] An array of things or an empty array
268
268
  def things
269
269
  Things::Links::Provider.registry.get_bound_things(name).map { |thing| Things::Proxy.new(thing) }
270
270
  end
271
271
  alias_method :all_linked_things, :things
272
272
 
273
+ # @!attribute channel_uid [r]
274
+ # Return the UID of the channel this item is linked to. If an item is linked to more than one channel,
275
+ # this method only returns the first channel.
276
+ #
277
+ # @return [Things::ChannelUID, nil]
278
+ def channel_uid
279
+ channel_uids.first
280
+ end
281
+
282
+ # @!attribute channel_uids [r]
283
+ # Return the UIDs of all of the channels this item is linked to.
284
+ #
285
+ # @return [Array<Things::ChannelUID>]
286
+ def channel_uids
287
+ Things::Links::Provider.registry.get_bound_channels(name)
288
+ end
289
+
290
+ # @!attribute channel [r]
291
+ # Return the the channel this item is linked to. If an item is linked to more than one channel,
292
+ # this method only returns the first channel.
293
+ #
294
+ # @return [Things::Channel, nil]
295
+ def channel
296
+ channel_uids.first&.channel
297
+ end
298
+
299
+ # @!attribute channels [r]
300
+ # Return all of the channels this item is linked to.
301
+ #
302
+ # @return [Array<Things::Channel>]
303
+ def channels
304
+ channel_uids.map(&:channel)
305
+ end
306
+
273
307
  #
274
308
  # @!attribute links [r]
275
309
  # Returns all of the item's links (channels and link configurations).
@@ -290,30 +324,43 @@ module OpenHAB
290
324
  end
291
325
 
292
326
  #
293
- # Links the item to a channel.
327
+ # @return [Things::ItemChannelLink, nil]
294
328
  #
295
- # @param [String, Things::Channel, Things::ChannelUID] channel The channel to link to.
296
- # @param [Hash] config The configuration for the link.
329
+ # @overload link
330
+ # Returns the item's link. If an item is linked to more than one channel,
331
+ # this method only returns the first link.
297
332
  #
298
- # @return [Things::ItemChannelLink] The created link.
333
+ # @return [Things::ItemChannelLink, nil]
299
334
  #
300
- # @example Link an item to a channel
301
- # LivingRoom_Light_Power.link("mqtt:topic:livingroom-light:power")
335
+ # @overload link(channel, config = {})
302
336
  #
303
- # @example Link to a Thing's channel
304
- # LivingRoom_Light_Power.link(things["mqtt:topic:livingroom-light"].channels["power"])
337
+ # Links the item to a channel.
305
338
  #
306
- # @example Specify a link configuration
307
- # High_Temperature_Alert.link(
308
- # "mqtt:topic:outdoor-thermometer:temperature",
309
- # profile: "system:hysteresis",
310
- # lower: "29 °C",
311
- # upper: "30 °C")
339
+ # @param [String, Things::Channel, Things::ChannelUID] channel The channel to link to.
340
+ # @param [Hash] config The configuration for the link.
312
341
  #
313
- # @see links
314
- # @see unlink
342
+ # @return [Things::ItemChannelLink] The created link.
343
+ #
344
+ # @example Link an item to a channel
345
+ # LivingRoom_Light_Power.link("mqtt:topic:livingroom-light:power")
315
346
  #
316
- def link(channel, config = {})
347
+ # @example Link to a Thing's channel
348
+ # LivingRoom_Light_Power.link(things["mqtt:topic:livingroom-light"].channels["power"])
349
+ #
350
+ # @example Specify a link configuration
351
+ # High_Temperature_Alert.link(
352
+ # "mqtt:topic:outdoor-thermometer:temperature",
353
+ # profile: "system:hysteresis",
354
+ # lower: "29 °C",
355
+ # upper: "30 °C")
356
+ #
357
+ # @see links
358
+ # @see unlink
359
+ #
360
+ def link(channel = nil, config = nil)
361
+ return Things::Links::Provider.registry.get_links(name).first if channel.nil? && config.nil?
362
+
363
+ config ||= {}
317
364
  Core::Things::Links::Provider.create_link(self, channel, config).tap do |new_link|
318
365
  provider = Core::Things::Links::Provider.current
319
366
  if !(current_link = provider.get(new_link.uid))
@@ -63,7 +63,7 @@ module OpenHAB
63
63
  #
64
64
  # @example
65
65
  # sitemaps.build do
66
- # sitemap "default", "My Residence" do
66
+ # sitemap "default", label: "My Residence" do
67
67
  # frame label: "Control" do
68
68
  # text label: "Climate", icon: "if:mdi:home-thermometer-outline" do
69
69
  # frame label: "Main Floor" do
@@ -33,6 +33,13 @@ module OpenHAB
33
33
  end
34
34
 
35
35
  alias_method :channel_uid, :linked_uid
36
+
37
+ # @return [String]
38
+ def inspect
39
+ r = "#<OpenHAB::Core::Things::ItemChannelLink item_name=#{item_name} channel_uid=#{channel_uid}"
40
+ r += " configuration=#{configuration.properties.to_h}" unless configuration.properties.empty?
41
+ "#{r}>"
42
+ end
36
43
  end
37
44
  end
38
45
  end
@@ -18,11 +18,12 @@ module OpenHAB
18
18
  end
19
19
 
20
20
  # (see SitemapBuilder#initialize)
21
+ # @!method sitemap(name, label: nil, icon: nil, &block)
21
22
  # @yield Block executed in the context of a {SitemapBuilder}
22
23
  # @return [SitemapBuilder]
23
24
  # @!visibility public
24
- def sitemap(name, label = nil, icon: nil, &block)
25
- sitemap = SitemapBuilder.new(name, label, icon: icon)
25
+ def sitemap(name, label: nil, icon: nil, &block)
26
+ sitemap = SitemapBuilder.new(name, label: label, icon: icon)
26
27
  sitemap.instance_eval_with_dummy_items(&block) if block
27
28
  sitemap = sitemap.build
28
29
  if @update && @provider.get(sitemap.uid)
@@ -1110,7 +1111,7 @@ module OpenHAB
1110
1111
  # @param label [String, nil]
1111
1112
  # @param icon [String, nil]
1112
1113
  # @!visibility private
1113
- def initialize(name, label = nil, icon: nil)
1114
+ def initialize(name, label: nil, icon: nil)
1114
1115
  super(:sitemap, label: label, icon: icon)
1115
1116
 
1116
1117
  @name = name
@@ -4,6 +4,6 @@ module OpenHAB
4
4
  module DSL
5
5
  # Version of openHAB helper libraries
6
6
  # @return [String]
7
- VERSION = "5.13.0"
7
+ VERSION = "5.14.0"
8
8
  end
9
9
  end
data/lib/openhab/osgi.rb CHANGED
@@ -33,20 +33,25 @@ module OpenHAB
33
33
  # Register a new service instance with OSGi
34
34
  #
35
35
  # @param [Object] instance The service instance
36
- # @param [Module] interfaces The interfaces to register this service for.
36
+ # @param [Module, String] interfaces The interfaces to register this service for.
37
37
  # If not provided, it will default to all Java interfaces the instance
38
38
  # implements.
39
+ # @param [org.osgi.framework.Bundle, nil] bundle The bundle to register
40
+ # the service from. If not provided, it will default to the bundle of the first
41
+ # interface.
39
42
  # @param [Hash] properties The service registration properties.
40
43
  # @return [org.osgi.framework.ServiceRegistration]
41
44
  #
42
- def register_service(instance, *interfaces, **properties)
45
+ def register_service(instance, *interfaces, bundle: nil, **properties)
43
46
  if interfaces.empty?
44
47
  interfaces = instance.class.ancestors.select { |k| k.respond_to?(:java_class) && k.java_class&.interface? }
45
48
  end
46
49
 
47
- bundle = org.osgi.framework.FrameworkUtil.get_bundle(interfaces.first.java_class)
50
+ bundle_class = interfaces.first.is_a?(Module) ? interfaces.first : instance
51
+ bundle ||= org.osgi.framework.FrameworkUtil.get_bundle(bundle_class.java_class)
52
+ interfaces.map! { |i| i.is_a?(String) ? i : i.java_class.name }
48
53
  bundle.bundle_context.register_service(
49
- interfaces.map { |i| i.java_class.name }.to_java(java.lang.String),
54
+ interfaces.to_java(java.lang.String),
50
55
  instance,
51
56
  java.util.Hashtable.new(properties)
52
57
  )
@@ -63,6 +68,32 @@ module OpenHAB
63
68
  def bundle
64
69
  @bundle ||= org.osgi.framework.FrameworkUtil.getBundle($scriptExtension.java_class)
65
70
  end
71
+
72
+ # @!visibility private
73
+ SCR_NAMESPACE = "http://www.osgi.org/xmlns/scr/v1.4.0"
74
+ private_constant :SCR_NAMESPACE
75
+
76
+ # @!visibility private
77
+ def service_component_classes(bundle)
78
+ require "nokogiri"
79
+
80
+ component_paths = bundle.headers.get(
81
+ org.osgi.service.component.ComponentConstants::SERVICE_COMPONENT
82
+ )&.split(",") || []
83
+ component_paths.filter_map do |path|
84
+ stream = bundle.get_entry(path).open_stream
85
+ xml = Nokogiri::XML(String.from_java_bytes(stream.read_all_bytes))
86
+
87
+ class_name = xml.at_xpath("scr:component/implementation", scr: SCR_NAMESPACE)&.[]("class")
88
+ next unless class_name
89
+
90
+ services = xml.xpath("scr:component/service/provide", scr: SCR_NAMESPACE).map { |p| p["interface"] }
91
+
92
+ [bundle.load_class(class_name), services]
93
+ ensure
94
+ stream&.close
95
+ end.to_h
96
+ end
66
97
  end
67
98
  end
68
99
  end
@@ -77,23 +77,37 @@ module OpenHAB
77
77
  #
78
78
  # @return [void]
79
79
  #
80
+ # @deprecated
80
81
  def autoupdate_all_items
81
- if instance_variable_defined?(:@autoupdated_items)
82
- raise RuntimeError "You should only call `autoupdate_all_items` once per spec"
83
- end
84
-
85
- @autoupdated_items = []
86
- @spec_metadata_provider = Core::Items::Metadata::Provider.current
82
+ # no-op
83
+ end
87
84
 
88
- $ir.for_each do |_provider, item|
89
- if (hash = item.metadata["autoupdate"])
90
- provider = Core::Items::Metadata::Provider.registry.provider_for(hash.uid)
91
- provider.remove(hash.uid)
92
- @autoupdated_items << [provider, hash]
93
- provider(@spec_metadata_provider) do
94
- item.metadata["autoupdate"] = "true"
95
- end
85
+ #
86
+ # Force things to come online that are missing their thing type
87
+ #
88
+ # As of openHAB 4.0, things that are missing their thing type will not
89
+ # come online immediately. This especially impacts bindings that
90
+ # dynamically generate their thing types, but don't persist those
91
+ # thing types. You can use this method to force them to come online
92
+ # immediately.
93
+ #
94
+ # @return [void]
95
+ #
96
+ def initialize_missing_thing_types
97
+ thing_manager = OpenHAB::OSGi.service("org.openhab.core.thing.ThingManager")
98
+ thing_manager.class.field_reader :missingPrerequisites
99
+ first = true
100
+ thing_manager.missingPrerequisites.each_value do |prereq|
101
+ if first
102
+ prereq.class.field_accessor :timesChecked
103
+ first = false
96
104
  end
105
+ prereq.timesChecked = 60
106
+ end
107
+ m = thing_manager.class.java_class.get_declared_method(:checkMissingPrerequisites)
108
+ m.accessible = true
109
+ suspend_rules do
110
+ m.invoke(thing_manager)
97
111
  end
98
112
  end
99
113
 
@@ -226,8 +240,11 @@ module OpenHAB
226
240
  require_relative "openhab/core/actions"
227
241
 
228
242
  ps = Mocks::PersistenceService.instance
229
- bundle = org.osgi.framework.FrameworkUtil.get_bundle(org.openhab.core.persistence.PersistenceService.java_class)
230
- bundle.bundle_context.register_service(org.openhab.core.persistence.PersistenceService.java_class, ps, nil)
243
+ persistence_bundle = org.osgi.framework.FrameworkUtil
244
+ .get_bundle(org.openhab.core.persistence.PersistenceService.java_class)
245
+ persistence_bundle.bundle_context.register_service(org.openhab.core.persistence.PersistenceService.java_class,
246
+ ps,
247
+ nil)
231
248
 
232
249
  rs = OSGi.service("org.openhab.core.service.ReadyService")
233
250
 
@@ -240,6 +257,33 @@ module OpenHAB
240
257
  rs.register_tracker(org.openhab.core.service.ReadyService::ReadyTracker.impl { continue.call }, filter)
241
258
  end
242
259
 
260
+ begin
261
+ # load storage based type providers
262
+ ast = org.openhab.core.thing.binding.AbstractStorageBasedTypeProvider
263
+ ast_bundle = org.osgi.framework.FrameworkUtil.get_bundle(ast.java_class)
264
+ storage_service = OSGi.service("org.openhab.core.storage.StorageService")
265
+ require_relative "mocks/abstract_storage_based_type_provider_wrapped_storage_service"
266
+
267
+ OSGi.bundle_context.bundles.each do |bundle|
268
+ OSGi.service_component_classes(bundle)
269
+ .select { |klass, _services| klass.ancestors.include?(ast.java_class) }
270
+ .each do |klass, services|
271
+ new_ast_klass = Class.new(ast)
272
+ new_ast_klass.become_java!
273
+ wrapped_storage_service = Mocks::AbstractStorageBasedTypeProviderWrappedStorageService
274
+ .new(storage_service,
275
+ new_ast_klass.java_class,
276
+ klass)
277
+ new_ast = new_ast_klass.new(wrapped_storage_service)
278
+
279
+ services -= [klass.name]
280
+ OSGi.register_service(new_ast, *services, bundle: ast_bundle)
281
+ end
282
+ end
283
+ rescue NameError
284
+ # @deprecated OH 4.0
285
+ end
286
+
243
287
  # RSpec additions
244
288
  require_relative "suspend_rules"
245
289
 
@@ -422,32 +466,6 @@ module OpenHAB
422
466
  .gsub(%r{(?:_|(/))([a-z\d]*)}) { "#{$1}#{$2.capitalize}" }
423
467
  .split("/")
424
468
  end
425
-
426
- # need to transfer autoupdate metadata from GenericMetadataProvider to ManagedMetadataProvider
427
- # so that we can mutate it in the future
428
- def set_up_autoupdates
429
- registry = Core::Items::Metadata::Provider.registry
430
- registry.class.field_reader :identifierToElement
431
-
432
- autoupdate_provider = Core::Items::Metadata::Provider.send(:new)
433
- registry.all.each do |metadata|
434
- next unless metadata.uid.namespace == "autoupdate"
435
-
436
- # tweak the registry to allow us to overwrite this element
437
- registry.identifierToElement.delete(metadata.uid)
438
- autoupdate_provider.add(metadata)
439
- end
440
- end
441
-
442
- def restore_autoupdate_items
443
- return unless instance_variable_defined?(:@autoupdated_items)
444
-
445
- @autoupdated_items.each do |(provider, hash)|
446
- @spec_metadata_provider.remove(hash.uid)
447
- provider.add(hash.instance_variable_get(:@metadata))
448
- end
449
- @autoupdated_items = nil
450
- end
451
469
  end
452
470
 
453
471
  if defined?(::RSpec)
@@ -38,7 +38,6 @@ module OpenHAB
38
38
  end
39
39
 
40
40
  Helpers.autorequires unless Configuration.private_confdir
41
- Helpers.send(:set_up_autoupdates)
42
41
  Helpers.load_transforms
43
42
  Helpers.load_rules
44
43
 
@@ -52,6 +51,10 @@ module OpenHAB
52
51
  end
53
52
  end
54
53
 
54
+ config.around do |example|
55
+ Mocks::Timer.mock_timers(self.class.mock_timers?, &example)
56
+ end
57
+
55
58
  config.before do
56
59
  suspend_rules do
57
60
  $ir.for_each do |_provider, item|
@@ -88,8 +91,6 @@ module OpenHAB
88
91
  @profile_factory = Core::ProfileFactory.send(:new)
89
92
  allow(Core::ProfileFactory).to receive(:instance).and_return(@profile_factory)
90
93
 
91
- stub_const("OpenHAB::Core::Timer", Mocks::Timer) if self.class.mock_timers?
92
-
93
94
  log_line = "rspec #{example.location} # #{example.full_description}"
94
95
  logger.info(log_line)
95
96
  Logger.events.info(log_line)
@@ -103,7 +104,6 @@ module OpenHAB
103
104
  # wipe this
104
105
  DSL::Items::TimedCommand.timed_commands.clear
105
106
  Timecop.return
106
- restore_autoupdate_items
107
107
  Mocks::PersistenceService.instance.reset
108
108
  Hooks.cache_script_extension.sharedCache.clear if DSL.shared_cache
109
109
  DSL.persistence!(nil)
@@ -537,6 +537,10 @@ module OpenHAB
537
537
 
538
538
  def add_class_loader(bundle)
539
539
  return if @class_loaders.include?(bundle.symbolic_name)
540
+ # this bundle sometimes has an invalid internal jar (geronimo-osgi-locator.jar) which will
541
+ # spam the logs with useless errors. it's doubtful we'll need to access a class from this
542
+ # bundle directly
543
+ return if bundle.symbolic_name == "org.apache.aries.javax.jax.rs-api"
540
544
 
541
545
  @class_loaders << bundle.symbolic_name
542
546
  ::JRuby.runtime.instance_config.add_loader(JRuby::OSGiBundleClassLoader.new(bundle))
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module OpenHAB
6
+ module RSpec
7
+ # @!visibility private
8
+ module Mocks
9
+ class AbstractStorageBasedTypeProviderWrappedStorageService < SimpleDelegator
10
+ include org.openhab.core.storage.StorageService
11
+
12
+ def initialize(parent, ruby_klass, java_klass)
13
+ super(parent)
14
+ @ruby_klass = ruby_klass
15
+ @java_klass = java_klass
16
+ end
17
+
18
+ def getStorage(name, _class_loader) # rubocop:disable Naming/MethodName
19
+ super(name.sub(@ruby_klass.name, @java_klass.name), @java_klass.class_loader)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -29,7 +29,13 @@ module OpenHAB
29
29
  @thing = thing
30
30
  end
31
31
 
32
- def handle_command(channel, command); end
32
+ def handle_command(channel_uid, command)
33
+ channel = thing.get_channel(channel_uid)
34
+ return unless channel
35
+ return if channel.auto_update_policy == org.openhab.core.thing.type.AutoUpdatePolicy::VETO
36
+
37
+ callback&.state_updated(channel_uid, command) if command.is_a?(Core::Types::State)
38
+ end
33
39
 
34
40
  def set_callback(callback)
35
41
  @callback = callback
@@ -72,6 +72,42 @@ module OpenHAB
72
72
  end
73
73
  Timecop::TimeStackItem.prepend(TimeCopStackItem)
74
74
 
75
+ class << self
76
+ # If timers are currently mocked
77
+ # @return [true, false]
78
+ def mock_timers?
79
+ @mock_timers
80
+ end
81
+
82
+ #
83
+ # Temporarily mock or unmock timers
84
+ #
85
+ # @param [true, false] mock_timers if timers should be mocked
86
+ # @yield
87
+ # @return [Object] the block's return value
88
+ def mock_timers(mock_timers)
89
+ old_mock_timers = @mock_timers
90
+ @mock_timers = mock_timers
91
+ yield
92
+ ensure
93
+ @mock_timers = old_mock_timers
94
+ end
95
+ end
96
+
97
+ @mock_timers = true
98
+
99
+ # @!visibility private
100
+ module ClassMethods
101
+ # @!visibility private
102
+ def new(*args, **kwargs)
103
+ return super if self == Timer
104
+ return Timer.new(*args, **kwargs) if Timer.mock_timers?
105
+
106
+ super
107
+ end
108
+ end
109
+ Core::Timer.singleton_class.prepend(ClassMethods)
110
+
75
111
  attr_reader :execution_time, :id, :block
76
112
 
77
113
  def initialize(time, id:, thread_locals:, block:) # rubocop:disable Lint/MissingSuper
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.13.0
4
+ version: 5.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian O'Connell
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-01-04 00:00:00.000000000 Z
13
+ date: 2024-01-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -546,6 +546,7 @@ files:
546
546
  - lib/openhab/rspec/hooks.rb
547
547
  - lib/openhab/rspec/jruby.rb
548
548
  - lib/openhab/rspec/karaf.rb
549
+ - lib/openhab/rspec/mocks/abstract_storage_based_type_provider_wrapped_storage_service.rb
549
550
  - lib/openhab/rspec/mocks/bundle_install_support.rb
550
551
  - lib/openhab/rspec/mocks/bundle_resolver.rb
551
552
  - lib/openhab/rspec/mocks/event_admin.rb
@@ -607,7 +608,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
607
608
  - !ruby/object:Gem::Version
608
609
  version: '0'
609
610
  requirements: []
610
- rubygems_version: 3.3.26
611
+ rubygems_version: 3.5.4
611
612
  signing_key:
612
613
  specification_version: 4
613
614
  summary: JRuby Helper Libraries for openHAB Scripting