openhab-scripting 5.13.0 → 5.14.0

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