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.
- 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
- data/lib/openhab/yard/base_helper.rb +46 -0
- data/lib/openhab/yard/markdown_directive.rb +125 -0
- data/lib/openhab/yard/markdown_helper.rb +99 -0
- 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
|
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
|
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
|
#
|