openhab-jrubyscripting 5.0.0.rc5 → 5.0.0.rc6

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions.rb +6 -6
  3. data/lib/openhab/core/dependency_tracking.rb +34 -0
  4. data/lib/openhab/core/entity_lookup.rb +132 -78
  5. data/lib/openhab/core/events/item_channel_link.rb +2 -2
  6. data/lib/openhab/core/events/item_command_event.rb +1 -1
  7. data/lib/openhab/core/events/item_event.rb +2 -2
  8. data/lib/openhab/core/events/item_state_changed_event.rb +1 -1
  9. data/lib/openhab/core/events/thing.rb +1 -1
  10. data/lib/openhab/core/items/accepted_data_types.rb +2 -2
  11. data/lib/openhab/core/items/contact_item.rb +1 -1
  12. data/lib/openhab/core/items/dimmer_item.rb +2 -2
  13. data/lib/openhab/core/items/generic_item.rb +45 -224
  14. data/lib/openhab/core/items/group_item.rb +5 -3
  15. data/lib/openhab/core/items/image_item.rb +2 -2
  16. data/lib/openhab/core/items/item.rb +219 -0
  17. data/lib/openhab/core/items/metadata/hash.rb +1 -1
  18. data/lib/openhab/core/items/persistence.rb +4 -5
  19. data/lib/openhab/core/items/provider.rb +2 -2
  20. data/lib/openhab/core/items/proxy.rb +68 -7
  21. data/lib/openhab/core/items/registry.rb +6 -6
  22. data/lib/openhab/core/items/semantics/enumerable.rb +6 -6
  23. data/lib/openhab/core/items/semantics.rb +8 -7
  24. data/lib/openhab/core/items.rb +2 -1
  25. data/lib/openhab/core/provider.rb +14 -7
  26. data/lib/openhab/core/rules/registry.rb +2 -2
  27. data/lib/openhab/core/rules.rb +1 -1
  28. data/lib/openhab/core/script_handling.rb +6 -6
  29. data/lib/openhab/core/things/channel.rb +1 -1
  30. data/lib/openhab/core/things/channel_uid.rb +2 -2
  31. data/lib/openhab/core/things/item_channel_link.rb +2 -2
  32. data/lib/openhab/core/things/links/provider.rb +2 -2
  33. data/lib/openhab/core/things/registry.rb +1 -1
  34. data/lib/openhab/core/things/thing.rb +1 -1
  35. data/lib/openhab/core/types/date_time_type.rb +4 -4
  36. data/lib/openhab/core/types/hsb_type.rb +2 -2
  37. data/lib/openhab/core/types/quantity_type.rb +1 -1
  38. data/lib/openhab/core/types.rb +1 -1
  39. data/lib/openhab/core/uid.rb +1 -1
  40. data/lib/openhab/core/value_cache.rb +188 -0
  41. data/lib/openhab/core.rb +57 -15
  42. data/lib/openhab/core_ext/ruby/symbol.rb +7 -0
  43. data/lib/openhab/dsl/items/builder.rb +17 -10
  44. data/lib/openhab/dsl/items/ensure.rb +5 -5
  45. data/lib/openhab/dsl/items/timed_command.rb +4 -4
  46. data/lib/openhab/dsl/rules/automation_rule.rb +53 -39
  47. data/lib/openhab/dsl/rules/builder.rb +128 -79
  48. data/lib/openhab/dsl/rules/guard.rb +5 -5
  49. data/lib/openhab/dsl/rules/name_inference.rb +20 -1
  50. data/lib/openhab/dsl/rules/rule_triggers.rb +3 -3
  51. data/lib/openhab/dsl/rules/terse.rb +1 -0
  52. data/lib/openhab/dsl/rules/triggers/changed.rb +26 -23
  53. data/lib/openhab/dsl/rules/triggers/command.rb +6 -5
  54. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +2 -2
  55. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +2 -2
  56. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +6 -6
  57. data/lib/openhab/dsl/rules/triggers/updated.rb +5 -5
  58. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +11 -12
  59. data/lib/openhab/dsl/things/builder.rb +73 -14
  60. data/lib/openhab/dsl/version.rb +2 -2
  61. data/lib/openhab/dsl.rb +43 -17
  62. data/lib/openhab/log.rb +5 -5
  63. data/lib/openhab/rspec/configuration.rb +5 -5
  64. data/lib/openhab/rspec/example_group.rb +1 -1
  65. data/lib/openhab/rspec/helpers.rb +4 -4
  66. data/lib/openhab/rspec/hooks.rb +19 -1
  67. data/lib/openhab/rspec/karaf.rb +12 -19
  68. data/lib/openhab/rspec/suspend_rules.rb +2 -1
  69. data/lib/openhab/yard/base_helper.rb +46 -0
  70. data/lib/openhab/yard/markdown_directive.rb +125 -0
  71. data/lib/openhab/yard/markdown_helper.rb +99 -0
  72. metadata +10 -3
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ OpenHAB::Core.import_preset("cache")
4
+ unless defined?($sharedCache)
5
+ $sharedCache = nil
6
+ return
7
+ end
8
+
9
+ module OpenHAB
10
+ module Core
11
+ # @interface
12
+ java_import org.openhab.core.automation.module.script.rulesupport.shared.ValueCache
13
+
14
+ #
15
+ # ValueCache is the interface used to access a
16
+ # {OpenHAB::DSL.shared_cache shared cache} available between scripts and/or
17
+ # rule executions.
18
+ #
19
+ # While ValueCache looks somewhat like a Hash, it does not support
20
+ # iteration of the contained elements. So it's limited to strictly storing,
21
+ # fetching, or removing known elements.
22
+ #
23
+ # Shared caches are _not_ persisted between openHAB restarts. And in fact,
24
+ # if all scripts are unloaded that reference a particular key, that key is
25
+ # removed.
26
+ #
27
+ # @note Only the {OpenHAB::DSL.shared_cache sharedCache} is exposed in Ruby.
28
+ # For a private cache, simply use an instance variable. See
29
+ # {file:docs/ruby-basics.md#Variables Instance Variables}.
30
+ #
31
+ # @note Because every script or UI rule gets it own JRuby engine instance,
32
+ # you cannot rely on being able to access Ruby objects between them. Only
33
+ # objects that implement a Java interface that's part of Java or openHAB
34
+ # Core (such as Hash implements {java.util.Map}, or other basic
35
+ # datatypes) can be reliably stored and accessed from the shared cache.
36
+ # Likewise, you can use the cache to access data from other scripting
37
+ # languages, but they'll be all but useless in Ruby. It's best to stick
38
+ # to simple data types. If you're having troubles, serializing to_json
39
+ # before storing may help.
40
+ #
41
+ # @see https://www.openhab.org/docs/configuration/jsr223.html#cache-preset openHAB Cache Preset
42
+ #
43
+ # @example
44
+ # shared_cache.compute_if_absent(:execution_count) { 0 }
45
+ # shared_cache[:execution_count] += 1
46
+ #
47
+ module ValueCache
48
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-5B-5D Hash#[]
49
+ def [](key)
50
+ get(key.to_s)
51
+ end
52
+
53
+ #
54
+ # Compute and store new value for key if the key is absent. This method is atomic.
55
+ #
56
+ # @param [String] key
57
+ # @yieldreturn [Object] new value
58
+ # @return [Object] new value or current value
59
+ #
60
+ def compute_if_absent(key, &block)
61
+ get(key.to_s, &block)
62
+ end
63
+
64
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-5B-5D-3D Hash#[]=
65
+ def []=(key, value)
66
+ put(key.to_s, value)
67
+ end
68
+ alias_method :store, :[]
69
+
70
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-delete Hash#delete
71
+ def delete(key)
72
+ key = key.to_s
73
+ if block_given?
74
+ fetch(key) do
75
+ return yield(key)
76
+ end
77
+ end
78
+ remove(key)
79
+ end
80
+
81
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-fetch Hash#fetch
82
+ #
83
+ # @example
84
+ # shared_cache.fetch(:key_from_another_script) # raises NoKeyError
85
+ #
86
+ def fetch(key, *default_value)
87
+ if default_value.length > 1
88
+ raise ArgumentError,
89
+ "wrong number of arguments (given #{default_value.length + 1}, expected 0..1)"
90
+ end
91
+
92
+ key = key.to_s
93
+ if default_value.empty?
94
+ if block_given?
95
+ get(key) do
96
+ return yield(key)
97
+ end
98
+ else
99
+ get(key) do
100
+ raise KeyError.new("key not found: #{key.inspect}", key: key)
101
+ end
102
+ end
103
+ else
104
+ get(key) { return default_value.first }
105
+ end
106
+ end
107
+
108
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-assoc Hash#assoc
109
+ def assoc(key)
110
+ [key, fetch(key) do
111
+ # return nil directly, without storing a value to the cache
112
+ return nil
113
+ end]
114
+ end
115
+
116
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-dig Hash#dig
117
+ def dig(key, *identifiers)
118
+ r = fetch(key) { return nil }
119
+ r&.dig(*identifiers)
120
+ end
121
+
122
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-fetch_values Hash#fetch_values
123
+ def fetch_values(*keys, &block)
124
+ result = []
125
+ keys.each do |key|
126
+ if block
127
+ result << fetch(key, &block)
128
+ else
129
+ has_value = true
130
+ value = fetch(key) { has_value = false }
131
+ result << value if has_value
132
+ end
133
+ end
134
+ result
135
+ end
136
+
137
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-key-3F Hash#key?
138
+ def key?(key)
139
+ !!fetch(key) { return false }
140
+ end
141
+ alias_method :has_key?, :key?
142
+ alias_method :include?, :key?
143
+ alias_method :member?, :key?
144
+
145
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-merge-21 Hash#merge!
146
+ def merge!(*other_hashes)
147
+ other_hashes.each do |hash|
148
+ hash.each do |(k, v)|
149
+ k = k.to_s
150
+ if block_given?
151
+ dup = true
152
+ old_value = fetch(k) do
153
+ dup = false
154
+ end
155
+ self[k] = dup ? yield(k, old_value, v) : v
156
+ else
157
+ self[k] = v
158
+ end
159
+ end
160
+ end
161
+ self
162
+ end
163
+ alias_method :update, :merge!
164
+
165
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-slice Hash#slice
166
+ def slice(*keys)
167
+ result = {}
168
+ keys.each do |k|
169
+ k = k.to_s
170
+ result[k] = self[k] if key?(k)
171
+ end
172
+ result
173
+ end
174
+
175
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-to_proc Hash#to_proc
176
+ def to_proc
177
+ @to_proc ||= ->(k) { self[k] }
178
+ end
179
+
180
+ # @see https://ruby-doc.org/core-3.1.2/Hash.html#method-i-values_at Hash#values_at
181
+ def values_at(*keys)
182
+ keys.map do |k|
183
+ self[k]
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
data/lib/openhab/core.rb CHANGED
@@ -1,21 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # several classes rely on this, so force it to load earlier
4
- require_relative "core/provider"
5
-
6
- Dir[File.expand_path("core/**/*.rb", __dir__)].sort.each do |f|
7
- require f
8
- end
9
-
10
3
  module OpenHAB
11
- # Contains classes and modules that wrap actual OpenHAB objects
4
+ # Contains classes and modules that wrap actual openHAB objects
12
5
  module Core
13
- # The OpenHAB Version. >= 3.3.0 is required.
6
+ # The openHAB Version. >= 3.3.0 is required.
14
7
  # @return [String]
15
8
  VERSION = org.openhab.core.OpenHAB.version.freeze
16
9
 
17
10
  unless Gem::Version.new(VERSION) >= Gem::Version.new("3.3.0")
18
- raise "`openhab-jrubyscripting` requires OpenHAB >= 3.3.0"
11
+ raise "`openhab-jrubyscripting` requires openHAB >= 3.3.0"
19
12
  end
20
13
 
21
14
  # @return [Integer] Number of seconds to wait between checks for automation manager
@@ -23,18 +16,18 @@ module OpenHAB
23
16
  private_constant :CHECK_DELAY
24
17
  class << self
25
18
  #
26
- # Wait until OpenHAB engine ready to process
19
+ # Wait until openHAB engine ready to process
27
20
  #
28
21
  # @return [void]
29
22
  #
30
23
  # @!visibility private
31
24
  def wait_till_openhab_ready
32
- logger.trace("Checking readiness of OpenHAB")
25
+ logger.trace("Checking readiness of openHAB")
33
26
  until automation_manager
34
27
  logger.trace("Automation manager not loaded, checking again in #{CHECK_DELAY} seconds.")
35
28
  sleep CHECK_DELAY
36
29
  end
37
- logger.trace "Automation manager instantiated, OpenHAB ready for rule processing."
30
+ logger.trace "Automation manager instantiated, openHAB ready for rule processing."
38
31
  end
39
32
 
40
33
  #
@@ -48,11 +41,60 @@ module OpenHAB
48
41
  #
49
42
  # @!attribute [r] automation_manager
50
43
  # @return [org.openhab.core.automation.module.script.rulesupport.shared.ScriptedAutomationManager]
51
- # The OpenHAB Automation manager.
44
+ # The openHAB Automation manager.
52
45
  #
53
46
  def automation_manager
54
- $scriptExtension.get("automationManager")
47
+ $se.get("automationManager")
48
+ end
49
+
50
+ #
51
+ # Imports a specific script extension preset into the global namespace
52
+ #
53
+ # @param [String] preset
54
+ # @return [void]
55
+ #
56
+ def import_preset(preset)
57
+ import_scope_values($se.import_preset(preset))
58
+ end
59
+
60
+ #
61
+ # Imports all default script extension presets into the global namespace
62
+ #
63
+ # @!visibility private
64
+ # @return [void]
65
+ #
66
+ def import_default_presets
67
+ $se.default_presets.each { |preset| import_preset(preset) }
68
+ end
69
+
70
+ #
71
+ # Imports concrete scope values into the global namespace
72
+ #
73
+ # @param [java.util.Map<String, Object>] scope_values
74
+ # @!visibility private
75
+ # @return [void]
76
+ #
77
+ def import_scope_values(scope_values)
78
+ scope_values.for_each do |key, value|
79
+ # convert Java classes to Ruby classes
80
+ value = value.ruby_class if value.is_a?(java.lang.Class) # rubocop:disable Lint/UselessAssignment
81
+ # variables are globals; constants go into the global namespace
82
+ key = case key[0]
83
+ when "a".."z" then "$#{key}"
84
+ when "A".."Z" then "::#{key}"
85
+ end
86
+ eval("#{key} = value unless defined?(#{key})", nil, __FILE__, __LINE__) # rubocop:disable Security/Eval
87
+ end
55
88
  end
56
89
  end
90
+
91
+ import_default_presets
57
92
  end
58
93
  end
94
+
95
+ # several classes rely on this, so force it to load earlier
96
+ require_relative "core/provider"
97
+
98
+ Dir[File.expand_path("core/**/*.rb", __dir__)].sort.each do |f|
99
+ require f
100
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extensions to Symbol
4
+ class Symbol
5
+ # Ruby 3.0 already has #name
6
+ alias_method :name, :to_s unless instance_methods.include?(:name)
7
+ end
@@ -3,36 +3,40 @@
3
3
  module OpenHAB
4
4
  module DSL
5
5
  #
6
- # Contains extensions to simplify working with [GenericItem]s.
6
+ # Contains extensions to simplify working with {Item Items}.
7
7
  #
8
8
  module Items
9
- # An item builder allows you to dynamically create OpenHAB items at runtime.
9
+ # An item builder allows you to dynamically create openHAB items at runtime.
10
10
  # This can be useful either to create items as soon as the script loads,
11
11
  # or even later based on a rule executing.
12
12
  #
13
13
  # @example
14
14
  # items.build do
15
- # switch_item "MySwitch", "My Switch"
16
- # switch_item "NotAutoupdating", autoupdate: false, channel: "mqtt:topic:1#light"
17
- # group_item "MyGroup" do
18
- # contact_item "ItemInGroup", channel: "binding:thing#channel"
15
+ # switch_item MySwitch, "My Switch"
16
+ # switch_item NotAutoupdating, autoupdate: false, channel: "mqtt:topic:1#light"
17
+ # group_item MyGroup do
18
+ # contact_item ItemInGroup, channel: "binding:thing#channel"
19
19
  # end
20
20
  # # passing `thing` to a group item will automatically use it as the base
21
21
  # # for item channels
22
- # group_item "Equipment", tags: Semantics::HVAC, thing: "binding:thing"
23
- # string_item "Mode", tags: Semantics::Control, channel: "mode"
22
+ # group_item Equipment, tags: Semantics::HVAC, thing: "binding:thing"
23
+ # string_item Mode, tags: Semantics::Control, channel: "mode"
24
24
  # end
25
25
  # end
26
+ #
26
27
  module Builder
27
28
  include Core::EntityLookup
28
29
 
30
+ self.create_dummy_items = true
31
+
29
32
  class << self
30
33
  private
31
34
 
32
35
  # @!macro def_item_method
33
36
  # @!method $1_item(name, label = nil, **kwargs)
34
37
  # Create a new $1 item
35
- # @param name [String] The name for the new item
38
+ # @param name [String, Symbol, Core::Items::Proxy] The name for the new item.
39
+ # Note that you can use a string, a symbol, or even a literal constant name
36
40
  # @param label [String] The item label
37
41
  # @yieldparam [ItemBuilder] builder Item for further customization
38
42
  # @see ItemBuilder#initialize ItemBuilder#initialize for additional arguments.
@@ -233,6 +237,7 @@ module OpenHAB
233
237
  raise ArgumentError, "`name` cannot be nil" if name.nil?
234
238
  raise ArgumentError, "`dimension` can only be specified with NumberItem" if dimension && type != :number
235
239
 
240
+ name = name.name if name.respond_to?(:name)
236
241
  if provider.is_a?(GroupItemBuilder)
237
242
  name = "#{provider.name_base}#{name}"
238
243
  label = "#{provider.label_base}#{label}".strip if label
@@ -365,7 +370,7 @@ module OpenHAB
365
370
  #
366
371
  # @example
367
372
  # items.build do
368
- # date_time_item "Bedroom_Light_Updated" do
373
+ # date_time_item Bedroom_Light_Updated do
369
374
  # channel "hue:0210:1:bulb1:color", profile: "system:timestamp-update"
370
375
  # end
371
376
  # end
@@ -469,6 +474,8 @@ module OpenHAB
469
474
  include Builder
470
475
 
471
476
  Builder.public_instance_methods.each do |m|
477
+ next unless Builder.instance_method(m).owner == Builder
478
+
472
479
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
473
480
  def #{m}(*args, groups: nil, **kwargs) # def dimmer_item(*args, groups: nil, **kwargs)
474
481
  groups ||= [] # groups ||= []
@@ -7,7 +7,7 @@ module OpenHAB
7
7
  module Items
8
8
  # Functionality to implement `ensure`/`ensure_states`
9
9
  module Ensure
10
- # Contains the `ensure` method mixed into {GenericItem} and {GroupItem::Members}
10
+ # Contains the `ensure` method mixed into {Item} and {GroupItem::Members}
11
11
  module Ensurable
12
12
  # Fluent method call that you can chain commands on to, that will
13
13
  # then automatically ensure that the item is not in the command's
@@ -18,15 +18,15 @@ module OpenHAB
18
18
  # @example Turn on all switches in a group that aren't already on
19
19
  # MySwitchGroup.members.ensure.on
20
20
  def ensure
21
- GenericItemDelegate.new(self)
21
+ ItemDelegate.new(self)
22
22
  end
23
23
  end
24
24
 
25
- # Extensions for {::GenericItem} to implement {Ensure}'s functionality
25
+ # Extensions for {::Item} to implement {Ensure}'s functionality
26
26
  #
27
27
  # @see OpenHAB::DSL.ensure ensure
28
28
  # @see OpenHAB::DSL.ensure_states ensure_states
29
- module GenericItem
29
+ module Item
30
30
  include Ensurable
31
31
 
32
32
  Core::Items::GenericItem.prepend(self)
@@ -63,7 +63,7 @@ module OpenHAB
63
63
  # "anonymous" class that wraps any method call in `ensure_states`
64
64
  # before forwarding to the wrapped object
65
65
  # @!visibility private
66
- class GenericItemDelegate
66
+ class ItemDelegate
67
67
  def initialize(item)
68
68
  @item = item
69
69
  end
@@ -3,7 +3,7 @@
3
3
  module OpenHAB
4
4
  module DSL
5
5
  module Items
6
- # Extensions for {GenericItem} to implement timed commands
6
+ # Extensions for {Item} to implement timed commands
7
7
  #
8
8
  # All items have an implicit timer associated with them, enabling to
9
9
  # easily set an item into a specific state for a specified duration and
@@ -28,7 +28,7 @@ module OpenHAB
28
28
  # Provides information about why the expiration block of a
29
29
  # {TimedCommand#command timed command} is being called.
30
30
  #
31
- # @attr [GenericItem] item
31
+ # @attr [Item] item
32
32
  # @!visibility private
33
33
  # @attr [Types::Type, Proc] on_expire
34
34
  # @!visibility private
@@ -205,8 +205,8 @@ module OpenHAB
205
205
  #
206
206
  # Execute the rule
207
207
  #
208
- # @param [Map] _mod map provided by OpenHAB rules engine
209
- # @param [Map] inputs map provided by OpenHAB rules engine containing event and other information
208
+ # @param [java.util.Map] _mod map provided by openHAB rules engine
209
+ # @param [java.util.Map] inputs map provided by openHAB rules engine containing event and other information
210
210
  #
211
211
  def execute(_mod = nil, inputs = nil)
212
212
  ThreadLocal.thread_local(**@thread_locals) do
@@ -8,6 +8,20 @@ module OpenHAB
8
8
  #
9
9
  # @!visibility private
10
10
  class AutomationRule < org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule
11
+ # @!visibility private
12
+ INPUT_KEY_PATTERN = /^[a-z_]+[a-zA-Z0-9_]*$/.freeze
13
+
14
+ class << self
15
+ #
16
+ # Caches dynamically generated Struct classes so that they don't have
17
+ # to be re-generated for every event, blowing the method cache.
18
+ #
19
+ # @!visibility private
20
+ # @return [java.util.Map<Array<Symbol>, Class>]
21
+ attr_reader :event_structs
22
+ end
23
+ @event_structs = java.util.concurrent.ConcurrentHashMap.new
24
+
11
25
  field_writer :uid
12
26
 
13
27
  #
@@ -41,8 +55,8 @@ module OpenHAB
41
55
  #
42
56
  # Execute the rule
43
57
  #
44
- # @param [Map] mod map provided by OpenHAB rules engine
45
- # @param [Map] inputs map provided by OpenHAB rules engine containing event and other information
58
+ # @param [java.util.Map] mod map provided by openHAB rules engine
59
+ # @param [java.util.Map] inputs map provided by openHAB rules engine containing event and other information
46
60
  #
47
61
  #
48
62
  def execute(mod = nil, inputs = nil)
@@ -50,7 +64,8 @@ module OpenHAB
50
64
  logger.trace { "Execute called with mod (#{mod&.to_string}) and inputs (#{inputs.inspect})" }
51
65
  logger.trace { "Event details #{inputs["event"].inspect}" } if inputs&.key?("event")
52
66
  trigger_conditions(inputs).process(mod: mod, inputs: inputs) do
53
- process_queue(create_queue(inputs), mod, inputs)
67
+ event = extract_event(inputs)
68
+ process_queue(create_queue(event), mod, event)
54
69
  end
55
70
  rescue Exception => e
56
71
  raise if defined?(::RSpec)
@@ -87,11 +102,11 @@ module OpenHAB
87
102
  #
88
103
  # Create the run queue based on guards
89
104
  #
90
- # @param [Map] inputs rule inputs
105
+ # @param [Map] event Event object
91
106
  # @return [Queue] <description>
92
107
  #
93
- def create_queue(inputs)
94
- case check_guards(event: extract_event(inputs))
108
+ def create_queue(event)
109
+ case check_guards(event: event)
95
110
  when true
96
111
  @run_queue.dup.grep_v(BuilderDSL::Otherwise)
97
112
  when false
@@ -108,12 +123,30 @@ module OpenHAB
108
123
  # @return [Object] event object
109
124
  #
110
125
  def extract_event(inputs)
111
- event = inputs&.dig("event")
112
- unless event
113
- event = Struct.new(:event, :attachment, :command).new
114
- event.command = inputs&.dig("command")
126
+ attachment = @attachments[trigger_id(inputs)]
127
+ if inputs&.key?("event")
128
+ event = inputs["event"]
129
+ unless event
130
+ if attachment
131
+ logger.warn("Unable to attach #{attachment.inspect} to event " \
132
+ "object for rule #{uid} since the event is nil.")
133
+ end
134
+ return nil
135
+ end
136
+
137
+ event.attachment = attachment
138
+ return event
115
139
  end
116
- add_attachment(event, inputs)
140
+
141
+ inputs = inputs.to_h
142
+ .select { |key, _value| key != "module" && INPUT_KEY_PATTERN.match?(key) }
143
+ .transform_keys(&:to_sym)
144
+ inputs[:attachment] = attachment
145
+ keys = inputs.keys.sort
146
+ struct_class = self.class.event_structs.compute_if_absent(keys) do
147
+ Struct.new(*keys, keyword_init: true)
148
+ end
149
+ struct_class.new(**inputs)
117
150
  end
118
151
 
119
152
  #
@@ -128,35 +161,18 @@ module OpenHAB
128
161
  #
129
162
  # Returns trigger conditions from inputs if it exists
130
163
  #
131
- # @param [Map] inputs map from OpenHAB containing UID
164
+ # @param [java.util.Map] inputs map from openHAB containing UID
132
165
  #
133
166
  # @return [Array] Array of trigger conditions that match rule UID
134
167
  #
135
168
  def trigger_conditions(inputs)
136
- # Parse this to get the trigger UID:
137
- # ["72698819-83cb-498a-8e61-5aab8b812623.event", "oldState", "module", \
138
- # "72698819-83cb-498a-8e61-5aab8b812623.oldState", "event", "newState",\
139
- # "72698819-83cb-498a-8e61-5aab8b812623.newState"]
140
169
  @trigger_conditions[trigger_id(inputs)]
141
170
  end
142
171
 
143
- # If an attachment exists for the trigger for this event add it to the event object
144
- # @param [Object] event Event
145
- # @param [Hash] inputs Inputs into event
146
- # @return [Object] Event with attachment added
147
- #
148
- def add_attachment(event, inputs)
149
- attachment = @attachments[trigger_id(inputs)]
150
- return event unless attachment
151
-
152
- event.attachment = attachment
153
- event
154
- end
155
-
156
172
  #
157
173
  # Check if any guards prevent execution
158
174
  #
159
- # @param [Map] event OpenHAB rule trigger event
175
+ # @param [Object] event openHAB rule trigger event
160
176
  #
161
177
  # @return [true,false] True if guards says rule should execute, false otherwise
162
178
  #
@@ -182,15 +198,13 @@ module OpenHAB
182
198
  # Process the run queue
183
199
  #
184
200
  # @param [Array] run_queue array of procs of various types to execute
185
- # @param [Map] mod OpenHAB map object describing rule trigger
186
- # @param [Map] inputs OpenHAB map object describing rule trigge
201
+ # @param [java.util/Map] mod openHAB map object describing rule trigger
202
+ # @param [Object] event openHAB map object describing rule trigger
187
203
  #
188
- def process_queue(run_queue, mod, inputs)
189
- event = extract_event(inputs)
190
-
204
+ def process_queue(run_queue, mod, event)
191
205
  while (task = run_queue.shift)
192
206
  if task.is_a?(BuilderDSL::Delay)
193
- process_delay_task(inputs, mod, run_queue, task)
207
+ process_delay_task(event, mod, run_queue, task)
194
208
  else
195
209
  process_task(event, task)
196
210
  end
@@ -229,15 +243,15 @@ module OpenHAB
229
243
  #
230
244
  # Process delay task
231
245
  #
232
- # @param [Map] inputs Rule trigger inputs
246
+ # @param [Map] event Rule trigger event
233
247
  # @param [Map] mod Rule modes
234
248
  # @param [Queue] run_queue Queue of tasks for this rule
235
249
  # @param [Delay] task to process
236
250
  #
237
251
  #
238
- def process_delay_task(inputs, mod, run_queue, task)
252
+ def process_delay_task(event, mod, run_queue, task)
239
253
  remaining_queue = run_queue.slice!(0, run_queue.length)
240
- DSL.after(task.duration) { process_queue(remaining_queue, mod, inputs) }
254
+ DSL.after(task.duration) { process_queue(remaining_queue, mod, event) }
241
255
  end
242
256
 
243
257
  #