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.
- checksums.yaml +4 -4
- data/lib/openhab/core/actions.rb +6 -6
- data/lib/openhab/core/dependency_tracking.rb +34 -0
- data/lib/openhab/core/entity_lookup.rb +132 -78
- data/lib/openhab/core/events/item_channel_link.rb +2 -2
- data/lib/openhab/core/events/item_command_event.rb +1 -1
- data/lib/openhab/core/events/item_event.rb +2 -2
- data/lib/openhab/core/events/item_state_changed_event.rb +1 -1
- data/lib/openhab/core/events/thing.rb +1 -1
- data/lib/openhab/core/items/accepted_data_types.rb +2 -2
- data/lib/openhab/core/items/contact_item.rb +1 -1
- data/lib/openhab/core/items/dimmer_item.rb +2 -2
- data/lib/openhab/core/items/generic_item.rb +45 -224
- data/lib/openhab/core/items/group_item.rb +5 -3
- data/lib/openhab/core/items/image_item.rb +2 -2
- data/lib/openhab/core/items/item.rb +219 -0
- data/lib/openhab/core/items/metadata/hash.rb +1 -1
- data/lib/openhab/core/items/persistence.rb +4 -5
- data/lib/openhab/core/items/provider.rb +2 -2
- data/lib/openhab/core/items/proxy.rb +68 -7
- data/lib/openhab/core/items/registry.rb +6 -6
- data/lib/openhab/core/items/semantics/enumerable.rb +6 -6
- data/lib/openhab/core/items/semantics.rb +8 -7
- data/lib/openhab/core/items.rb +2 -1
- data/lib/openhab/core/provider.rb +14 -7
- data/lib/openhab/core/rules/registry.rb +2 -2
- data/lib/openhab/core/rules.rb +1 -1
- data/lib/openhab/core/script_handling.rb +6 -6
- data/lib/openhab/core/things/channel.rb +1 -1
- data/lib/openhab/core/things/channel_uid.rb +2 -2
- data/lib/openhab/core/things/item_channel_link.rb +2 -2
- data/lib/openhab/core/things/links/provider.rb +2 -2
- data/lib/openhab/core/things/registry.rb +1 -1
- data/lib/openhab/core/things/thing.rb +1 -1
- data/lib/openhab/core/types/date_time_type.rb +4 -4
- data/lib/openhab/core/types/hsb_type.rb +2 -2
- data/lib/openhab/core/types/quantity_type.rb +1 -1
- data/lib/openhab/core/types.rb +1 -1
- data/lib/openhab/core/uid.rb +1 -1
- data/lib/openhab/core/value_cache.rb +188 -0
- data/lib/openhab/core.rb +57 -15
- data/lib/openhab/core_ext/ruby/symbol.rb +7 -0
- data/lib/openhab/dsl/items/builder.rb +17 -10
- data/lib/openhab/dsl/items/ensure.rb +5 -5
- data/lib/openhab/dsl/items/timed_command.rb +4 -4
- data/lib/openhab/dsl/rules/automation_rule.rb +53 -39
- data/lib/openhab/dsl/rules/builder.rb +128 -79
- data/lib/openhab/dsl/rules/guard.rb +5 -5
- data/lib/openhab/dsl/rules/name_inference.rb +20 -1
- data/lib/openhab/dsl/rules/rule_triggers.rb +3 -3
- data/lib/openhab/dsl/rules/terse.rb +1 -0
- data/lib/openhab/dsl/rules/triggers/changed.rb +26 -23
- data/lib/openhab/dsl/rules/triggers/command.rb +6 -5
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +2 -2
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +2 -2
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +6 -6
- data/lib/openhab/dsl/rules/triggers/updated.rb +5 -5
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +11 -12
- data/lib/openhab/dsl/things/builder.rb +73 -14
- data/lib/openhab/dsl/version.rb +2 -2
- data/lib/openhab/dsl.rb +43 -17
- data/lib/openhab/log.rb +5 -5
- data/lib/openhab/rspec/configuration.rb +5 -5
- data/lib/openhab/rspec/example_group.rb +1 -1
- data/lib/openhab/rspec/helpers.rb +4 -4
- data/lib/openhab/rspec/hooks.rb +19 -1
- data/lib/openhab/rspec/karaf.rb +12 -19
- data/lib/openhab/rspec/suspend_rules.rb +2 -1
- 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
|
4
|
+
# Contains classes and modules that wrap actual openHAB objects
|
12
5
|
module Core
|
13
|
-
# The
|
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
|
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
|
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
|
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,
|
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
|
44
|
+
# The openHAB Automation manager.
|
52
45
|
#
|
53
46
|
def automation_manager
|
54
|
-
$
|
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
|
@@ -3,36 +3,40 @@
|
|
3
3
|
module OpenHAB
|
4
4
|
module DSL
|
5
5
|
#
|
6
|
-
# Contains extensions to simplify working with
|
6
|
+
# Contains extensions to simplify working with {Item Items}.
|
7
7
|
#
|
8
8
|
module Items
|
9
|
-
# An item builder allows you to dynamically create
|
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
|
16
|
-
# switch_item
|
17
|
-
# group_item
|
18
|
-
# contact_item
|
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
|
23
|
-
# string_item
|
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
|
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 {
|
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
|
-
|
21
|
+
ItemDelegate.new(self)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
# Extensions for {::
|
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
|
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
|
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 {
|
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 [
|
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
|
209
|
-
# @param [Map] inputs map provided by
|
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
|
45
|
-
# @param [Map] inputs map provided by
|
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
|
-
|
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]
|
105
|
+
# @param [Map] event Event object
|
91
106
|
# @return [Queue] <description>
|
92
107
|
#
|
93
|
-
def create_queue(
|
94
|
-
case check_guards(event:
|
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
|
-
|
112
|
-
|
113
|
-
event =
|
114
|
-
event
|
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
|
-
|
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
|
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 [
|
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
|
186
|
-
# @param [
|
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,
|
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(
|
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]
|
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(
|
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,
|
254
|
+
DSL.after(task.duration) { process_queue(remaining_queue, mod, event) }
|
241
255
|
end
|
242
256
|
|
243
257
|
#
|