openhab-jrubyscripting 5.0.0.rc5 → 5.0.0.rc8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) 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. metadata +7 -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 unless defined?($ir)
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
  #