openhab-scripting 4.28.2 → 4.30.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c9d9745b2590c4152baf18a82dbe0063ac4c73c94f6f452d9d848790d8eb06a
4
- data.tar.gz: 276152f1a79cf44fb539f4bb0c73618263db8d4a944cf367fa0ef1db167d989a
3
+ metadata.gz: 9b902e522a26b84abdefb33be6bfc11a8569f54e25529c9080a7f2a3fc681098
4
+ data.tar.gz: 21129dcbfbd8832a992e7909815b7253e8568a69a9c3f7b398cfa6b0e00cb9a5
5
5
  SHA512:
6
- metadata.gz: d66efa237735b6a754e97eb933a301cb5dd639fa7cc715fa7d36c3e6f7ed14c11a3da874ea707792cb0913bb9e57396b33532746b7a63b08acf50ff15d4fc9f0
7
- data.tar.gz: b2fcbb63e5e9da86909dd7b7b2bcb054b29aea1b80ae59fb26555a7aa678a7f4ebe04ee36dda69413c7cc8fbe08c7beeeaa5ad333e09d4bac08bbe2b769e85e7
6
+ metadata.gz: fbab23e964b911e6aa10d7d808c9b52e4850efa66be3b7e9057a7335414cd4e9ca3436dfc006371b7ea396067b260ef8cf4542f00dd81b3048bfa39844d876b6
7
+ data.tar.gz: f2151e2b63dd6d363cfb6d664c1dd13140aef35c21d7d4c5896a31d7a38617b3865a2e6b2a19367d4604b50d65e31b1381eb7201e88add89c401da75542d2df3
@@ -32,6 +32,13 @@ module OpenHAB
32
32
  group.get_members.to_a
33
33
  end
34
34
 
35
+ # Name of the group
36
+ #
37
+ # @return [String]
38
+ def name
39
+ group.name
40
+ end
41
+
35
42
  # Send a command to each member of the group
36
43
  #
37
44
  # @return [GroupMembers] +self+
@@ -35,7 +35,7 @@ module OpenHAB
35
35
  # Explicit conversion to array
36
36
  # @return [Array]
37
37
  def to_a
38
- $ir.items.grep_v(org.openhab.core.items.GroupItem) # rubocop:disable Style/GlobalVars
38
+ $ir.items.to_a # rubocop:disable Style/GlobalVars
39
39
  end
40
40
  end
41
41
 
@@ -45,7 +45,7 @@ module OpenHAB
45
45
  between = config.between&.yield_self { between(config.between) }
46
46
  @between = between || OpenHAB::DSL::Between::ALL_DAY
47
47
  # Convert between to correct range or nil if not set
48
- @trigger_delays = config.trigger_delays
48
+ @trigger_conditions = config.trigger_conditions
49
49
  @attachments = config.attachments
50
50
  end
51
51
  # rubocop:enable Metrics/MethodLength
@@ -57,18 +57,12 @@ module OpenHAB
57
57
  # @param [Map] inputs map provided by OpenHAB rules engine containing event and other information
58
58
  #
59
59
  #
60
- def execute(mod = nil, inputs = nil) # rubocop:disable Metrics/MethodLength
60
+ def execute(mod = nil, inputs = nil)
61
61
  thread_local(RULE_NAME: name) do
62
62
  logger.trace { "Execute called with mod (#{mod&.to_string}) and inputs (#{inputs&.pretty_inspect})" }
63
63
  logger.trace { "Event details #{inputs['event'].pretty_inspect}" } if inputs&.key?('event')
64
- if trigger_delay inputs
65
- trigger_delay = trigger_delay(inputs)
66
- process_trigger_delay(trigger_delay, mod, inputs)
67
- else
68
- # If guards are satisfied execute the run type blocks
69
- # If they are not satisfied, execute the Othewise blocks
70
- queue = create_queue(inputs)
71
- process_queue(queue, mod, inputs)
64
+ trigger_conditions(inputs).process(mod: mod, inputs: inputs) do
65
+ process_queue(create_queue(inputs), mod, inputs)
72
66
  end
73
67
  end
74
68
  end
@@ -124,18 +118,18 @@ module OpenHAB
124
118
  end
125
119
 
126
120
  #
127
- # Returns trigger delay from inputs if it exists
121
+ # Returns trigger conditions from inputs if it exists
128
122
  #
129
123
  # @param [Map] inputs map from OpenHAB containing UID
130
124
  #
131
- # @return [Array] Array of trigger delays that match rule UID
125
+ # @return [Array] Array of trigger conditions that match rule UID
132
126
  #
133
- def trigger_delay(inputs)
127
+ def trigger_conditions(inputs)
134
128
  # Parse this to get the trigger UID:
135
129
  # ["72698819-83cb-498a-8e61-5aab8b812623.event", "oldState", "module", \
136
130
  # "72698819-83cb-498a-8e61-5aab8b812623.oldState", "event", "newState",\
137
131
  # "72698819-83cb-498a-8e61-5aab8b812623.newState"]
138
- @trigger_delays[trigger_id(inputs)]
132
+ @trigger_conditions[trigger_id(inputs)]
139
133
  end
140
134
 
141
135
  # If an attachment exists for the trigger for this event add it to the event object
@@ -151,138 +145,6 @@ module OpenHAB
151
145
  event
152
146
  end
153
147
 
154
- #
155
- # Check if trigger guards prevent rule execution
156
- #
157
- # @param [Delay] trigger_delay rules delaying trigger because of
158
- # @param [Map] inputs OpenHAB map object describing rule trigger
159
- #
160
- # @return [Boolean] True if the rule should execute, false if trigger guard prevents execution
161
- #
162
- def check_trigger_guards(trigger_delay, inputs)
163
- new_state, old_state = retrieve_states(inputs)
164
- if check_from(trigger_delay, old_state)
165
- return true if check_to(trigger_delay, new_state)
166
-
167
- logger.trace("Skipped execution of rule '#{name}' because to state #{new_state}"\
168
- " does not equal specified state(#{trigger_delay.to})")
169
- else
170
- logger.trace("Skipped execution of rule '#{name}' because old state #{old_state}"\
171
- " does not equal specified state(#{trigger_delay.from})")
172
- end
173
- end
174
-
175
- #
176
- # Rerieve the newState and oldState, alternatively newStatus and oldStatus
177
- # from the input map
178
- #
179
- # @param [Map] inputs OpenHAB map object describing rule trigger
180
- #
181
- # @return [Array] An array of the values for [newState, oldState] or [newStatus, oldStatus]
182
- #
183
- def retrieve_states(inputs)
184
- old_state = inputs['oldState'] || thing_status_to_sym(inputs['oldStatus'])
185
- new_state = inputs['newState'] || thing_status_to_sym(inputs['newStatus'])
186
-
187
- [new_state, old_state]
188
- end
189
-
190
- #
191
- # Converts a ThingStatus object to a ruby Symbol
192
- #
193
- # @param [Java::OrgOpenhabCoreThing::ThingStatus] status A ThingStatus instance
194
- #
195
- # @return [Symbol] A corresponding symbol, in lower case
196
- #
197
- def thing_status_to_sym(status)
198
- status&.to_s&.downcase&.to_sym
199
- end
200
-
201
- #
202
- # Check the from state against the trigger delay
203
- #
204
- # @param [TriggerDelay] trigger_delay Information about the trigger delay
205
- # @param [Item State] state from state to check
206
- #
207
- # @return [Boolean] true if no from state is defined or defined state equals supplied state
208
- #
209
- def check_from(trigger_delay, state)
210
- trigger_delay.from.nil? || state == trigger_delay.from
211
- end
212
-
213
- #
214
- # Check the to state against the trigger delay
215
- #
216
- # @param [TriggerDelay] trigger_delay Information about the trigger delay
217
- # @param [Item State] state to-state to check
218
- #
219
- # @return [Boolean] true if no to state is defined or defined state equals supplied state
220
- #
221
- def check_to(trigger_delay, state)
222
- trigger_delay.to.nil? || state == trigger_delay.to
223
- end
224
-
225
- #
226
- # Process any matching trigger delays
227
- #
228
- # @param [Map] mod OpenHAB map object describing rule trigger
229
- # @param [Map] inputs OpenHAB map object describing rule trigger
230
- #
231
- #
232
- def process_trigger_delay(trigger_delay, mod, inputs)
233
- if trigger_delay.timer_active?
234
- process_active_timer(inputs, mod, trigger_delay)
235
- elsif check_trigger_guards(trigger_delay, inputs)
236
- logger.trace("Trigger Guards Matched for #{trigger_delay}, delaying rule execution")
237
- # Add timer and attach timer to delay object, and also state being tracked to so timer can be cancelled if
238
- # state changes
239
- # Also another timer should not be created if changed to same value again but instead rescheduled
240
- create_trigger_delay_timer(inputs, mod, trigger_delay)
241
- else
242
- logger.trace("Trigger Guards did not match for #{trigger_delay}, ignoring trigger.")
243
- end
244
- end
245
-
246
- #
247
- # Creatas a timer for trigger delays
248
- #
249
- # @param [Hash] inputs rule trigger inputs
250
- # @param [Hash] mod rule trigger mods
251
- # @param [TriggerDelay] trigger_delay specifications
252
- #
253
- #
254
- def create_trigger_delay_timer(inputs, mod, trigger_delay)
255
- logger.trace("Creating timer for rule #{name} and trigger delay #{trigger_delay}")
256
- trigger_delay.timer = after(trigger_delay.duration) do
257
- logger.trace("Delay Complete for #{trigger_delay}, executing rule")
258
- trigger_delay.timer = nil
259
- queue = create_queue(inputs)
260
- process_queue(queue, mod, inputs)
261
- end
262
- trigger_delay.tracking_to, = retrieve_states(inputs)
263
- end
264
-
265
- #
266
- # Process an active trigger timer
267
- #
268
- # @param [Hash] inputs rule trigger inputs
269
- # @param [Hash] mod rule trigger mods
270
- # @param [TriggerDelay] trigger_delay specifications
271
- #
272
- #
273
- def process_active_timer(inputs, mod, trigger_delay)
274
- state, = retrieve_states(inputs)
275
- if state == trigger_delay.tracking_to
276
- logger.trace("Item changed to #{state} for #{trigger_delay}, rescheduling timer.")
277
- trigger_delay.timer.reschedule(ZonedDateTime.now.plus(trigger_delay.duration))
278
- else
279
- logger.trace("Item changed to #{state} for #{trigger_delay}, canceling timer.")
280
- trigger_delay.timer.cancel
281
- # Reprocess trigger delay after canceling to track new state (if guards matched, etc)
282
- process_trigger_delay(trigger_delay, mod, inputs)
283
- end
284
- end
285
-
286
148
  #
287
149
  # Check if any guards prevent execution
288
150
  #
@@ -10,6 +10,7 @@ require_relative 'triggers/command'
10
10
  require_relative 'triggers/updated'
11
11
  require_relative 'triggers/generic'
12
12
  require_relative 'triggers/watch'
13
+ require_relative 'triggers/conditions/proc'
13
14
  require_relative 'guard'
14
15
  require 'openhab/core/entity_lookup'
15
16
  require 'openhab/dsl/between'
@@ -37,7 +38,7 @@ module OpenHAB
37
38
  attr_accessor :triggers
38
39
 
39
40
  # @return [Array] Of trigger delays
40
- attr_reader :trigger_delays
41
+ attr_reader :trigger_conditions
41
42
 
42
43
  # @return [Hash] Hash of trigger UIDs to attachments
43
44
  attr_reader :attachments
@@ -86,7 +87,7 @@ module OpenHAB
86
87
  #
87
88
  def initialize(rule_name, caller_binding)
88
89
  @triggers = []
89
- @trigger_delays = {}
90
+ @trigger_conditions = Hash.new(OpenHAB::DSL::Rules::Triggers::Conditions::Proc::ANY)
90
91
  @attachments = {}
91
92
  @caller = caller_binding.eval 'self'
92
93
  name(rule_name)
@@ -145,7 +146,7 @@ module OpenHAB
145
146
  "Triggers: (#{triggers}) " \
146
147
  "Run blocks: (#{run}) " \
147
148
  "on_start: (#{on_start?}) " \
148
- "Trigger Waits: #{trigger_delays} " \
149
+ "Trigger Conditions: #{trigger_conditions} " \
149
150
  "Trigger UIDs: #{triggers.map(&:id).join(', ')}" \
150
151
  "Attachments: #{attachments} "
151
152
  end
@@ -1,25 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openhab/log/logger'
4
+ require_relative 'conditions/duration'
5
+ require_relative 'conditions/proc'
4
6
 
5
7
  module OpenHAB
6
8
  module DSL
7
9
  module Rules
8
10
  #
9
- # Module holds rule triggers
11
+ # Module for changed triggers
10
12
  #
13
+
11
14
  module Triggers
12
15
  include OpenHAB::Log
13
16
 
14
- #
15
- # Struct capturing data necessary for a conditional trigger
16
- #
17
- TriggerDelay = Struct.new(:to, :from, :duration, :timer, :tracking_to, keyword_init: true) do
18
- def timer_active?
19
- timer&.is_active
20
- end
21
- end
22
-
23
17
  #
24
18
  # Creates a trigger item, group and thing changed
25
19
  #
@@ -38,7 +32,7 @@ module OpenHAB
38
32
  wait_duration = binding.local_variable_get(:for)
39
33
 
40
34
  each_state(from, to) do |from_state, to_state|
41
- changed_or_wait(item, from_state, to_state, wait_duration, attach)
35
+ changed_trigger(item, from_state, to_state, wait_duration, attach)
42
36
  end
43
37
  end.flatten
44
38
  end
@@ -65,7 +59,7 @@ module OpenHAB
65
59
  end
66
60
 
67
61
  #
68
- # Create regular or delayed trigger based on duration
62
+ # Create the trigger
69
63
  #
70
64
  # @param [Object] item item to create trigger for
71
65
  # @param [Item State] from state to restrict trigger to
@@ -75,9 +69,13 @@ module OpenHAB
75
69
  #
76
70
  # @return [Trigger] OpenHAB triggers
77
71
  #
78
- def changed_or_wait(item, from, to, duration, attach)
72
+ def changed_trigger(item, from, to, duration, attach)
79
73
  if duration
80
74
  changed_wait(item, from: from, to: to, duration: duration, attach: attach)
75
+ elsif [to, from].grep(Range).any?
76
+ create_changed_range_trigger(item, from: from, to: to, attach: attach)
77
+ elsif [to, from].grep(Proc).any?
78
+ create_changed_proc_trigger(item, from: from, to: to, attach: attach)
81
79
  else
82
80
  create_changed_trigger(item, from, to, attach)
83
81
  end
@@ -90,13 +88,48 @@ module OpenHAB
90
88
  # @param [OpenHAB::Core::Duration] duration to delay trigger for until condition is met
91
89
  # @param [Item State] to OpenHAB Item State item or group needs to change to
92
90
  # @param [Item State] from OpenHAB Item State item or group needs to be coming from
91
+ # @param [Object] attach to trigger
93
92
  #
94
93
  # @return [Trigger] OpenHAB trigger
95
94
  #
96
95
  def changed_wait(item, duration:, to: nil, from: nil, attach: nil)
97
- trigger = create_changed_trigger(item, nil, nil, attach)
98
- logger.trace("Creating Changed Wait Change Trigger for #{item}")
99
- @trigger_delays[trigger.id] = TriggerDelay.new(to: to, from: from, duration: duration)
96
+ item_name = item.respond_to?(:name) ? item.name : item.to_s
97
+ logger.trace("Creating Changed Wait Change Trigger for Item(#{item_name}) Duration(#{duration}) "\
98
+ "To(#{to}) From(#{from}) Attach(#{attach})")
99
+ create_changed_trigger(item, nil, nil, attach).tap do |trigger|
100
+ @trigger_conditions[trigger.id] = Conditions::Duration.new(to: to, from: from, duration: duration)
101
+ end
102
+ end
103
+
104
+ #
105
+ # Creates a trigger with a range condition on either 'from' or 'to' field
106
+ # @param [Object] item to create changed trigger on
107
+ # @param [Object] from state to restrict trigger to
108
+ # @param [Object] to state restrict trigger to
109
+ # @param [Object] attach to trigger
110
+ # @return [Trigger] OpenHAB trigger
111
+ #
112
+ def create_changed_range_trigger(item, from:, to:, attach:)
113
+ from, to = Conditions::Proc.range_procs(from, to)
114
+ create_changed_proc_trigger(item, from: from, to: to, attach: attach)
115
+ end
116
+
117
+ #
118
+ # Creates a trigger with a proc condition on either 'from' or 'to' field
119
+ # @param [Object] item to create changed trigger on
120
+ # @param [Object] from state to restrict trigger to
121
+ # @param [Object] to state restrict trigger to
122
+ # @param [Object] attach to trigger
123
+ # @return [Trigger] OpenHAB trigger
124
+ #
125
+ def create_changed_proc_trigger(item, from:, to:, attach:)
126
+ # swap from/to w/ nil if from/to is a proc
127
+ # rubocop:disable Style/ParallelAssignment
128
+ from_proc, from = from, nil if from.is_a? Proc
129
+ to_proc, to = to, nil if to.is_a? Proc
130
+ # rubocop:enable Style/ParallelAssignment
131
+ trigger = create_changed_trigger(item, from, to, attach)
132
+ @trigger_conditions[trigger.id] = Conditions::Proc.new(to: to_proc, from: from_proc)
100
133
  trigger
101
134
  end
102
135
 
@@ -104,8 +137,8 @@ module OpenHAB
104
137
  # Create a changed trigger
105
138
  #
106
139
  # @param [Object] item to create changed trigger on
107
- # @param [String] from state to restrict trigger to
108
- # @param [String] to state restrict trigger to
140
+ # @param [Object] from state to restrict trigger to
141
+ # @param [Object] to state restrict trigger to
109
142
  #
110
143
  #
111
144
  def create_changed_trigger(item, from, to, attach)
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openhab/log/logger'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ module Rules
8
+ module Triggers
9
+ #
10
+ # Module for conditions for triggers
11
+ #
12
+ module Conditions
13
+ include OpenHAB::Log
14
+ #
15
+ # this is a no-op condition which simply executes the provided block
16
+ #
17
+
18
+ #
19
+ # Struct capturing data necessary for a conditional trigger
20
+ #
21
+ # TriggerDelay = Struct.new(:to, :from, :duration, :timer, :tracking_to, keyword_init: true) do
22
+ # def timer_active?
23
+ # timer&.is_active
24
+ # end
25
+ # end
26
+ class Duration
27
+ #
28
+ # Create a new duration condition
29
+ # @param [Object] to optional condition on to state
30
+ # @param [Object] from optional condition on from state
31
+ # @param [Duration] Duration to state must stay at specific value before triggering
32
+ #
33
+ def initialize(to:, from:, duration:)
34
+ to = Conditions::Proc.from_value(to)
35
+ from = Conditions::Proc.from_value(from)
36
+ @conditions = Conditions::Proc.new(to: to, from: from)
37
+ @duration = duration
38
+ logger.trace "Created Duration Condition To(#{to}) From(#{from}) "\
39
+ "Conditions(#{@conditions}) Duration(#{@duration})"
40
+ end
41
+
42
+ # Process rule
43
+ # @param [Hash] inputs inputs from trigger
44
+ #
45
+ def process(mod:, inputs:, &block)
46
+ process_trigger_delay(mod, inputs, &block)
47
+ end
48
+
49
+ private
50
+
51
+ #
52
+ # Checks if there is an active timer
53
+ # @return [true, false] true if the timer exists and is active, false otherwise
54
+ def timer_active?
55
+ @timer&.is_active
56
+ end
57
+
58
+ #
59
+ # Check if trigger guards prevent rule execution
60
+ #
61
+ # @param [Map] inputs OpenHAB map object describing rule trigger
62
+ #
63
+ # @return [Boolean] True if the rule should execute, false if trigger guard prevents execution
64
+ #
65
+ def check_trigger_guards(inputs)
66
+ new_state, old_state = retrieve_states(inputs)
67
+ @conditions.check_from(state: old_state) && @conditions.check_to(state: new_state)
68
+ end
69
+
70
+ #
71
+ # Rerieve the newState and oldState, alternatively newStatus and oldStatus
72
+ # from the input map
73
+ #
74
+ # @param [Map] inputs OpenHAB map object describing rule trigger
75
+ #
76
+ # @return [Array] An array of the values for [newState, oldState] or [newStatus, oldStatus]
77
+ #
78
+ def retrieve_states(inputs)
79
+ new_state = inputs['newState'] || thing_status_to_sym(inputs['newStatus'])
80
+ old_state = inputs['oldState'] || thing_status_to_sym(inputs['oldStatus'])
81
+
82
+ [new_state, old_state]
83
+ end
84
+
85
+ #
86
+ # Converts a ThingStatus object to a ruby Symbol
87
+ #
88
+ # @param [Java::OrgOpenhabCoreThing::ThingStatus] status A ThingStatus instance
89
+ #
90
+ # @return [Symbol] A corresponding symbol, in lower case
91
+ #
92
+ def thing_status_to_sym(status)
93
+ status&.to_s&.downcase&.to_sym
94
+ end
95
+
96
+ #
97
+ # Process any matching trigger delays
98
+ #
99
+ # @param [Map] mod OpenHAB map object describing rule trigger
100
+ # @param [Map] inputs OpenHAB map object describing rule trigger
101
+ #
102
+ #
103
+ def process_trigger_delay(mod, inputs, &block)
104
+ if timer_active?
105
+ process_active_timer(inputs, mod, &block)
106
+ elsif check_trigger_guards(inputs)
107
+ logger.trace("Trigger Guards Matched for #{self}, delaying rule execution")
108
+ # Add timer and attach timer to delay object, and also state being tracked to so
109
+ # timer can be cancelled if state changes
110
+ # Also another timer should not be created if changed to same value again but instead rescheduled
111
+ create_trigger_delay_timer(inputs, mod, &block)
112
+ else
113
+ logger.trace("Trigger Guards did not match for #{self}, ignoring trigger.")
114
+ end
115
+ end
116
+
117
+ #
118
+ # Creates a timer for trigger delays
119
+ #
120
+ # @param [Hash] inputs rule trigger inputs
121
+ # @param [Hash] mod rule trigger mods
122
+ #
123
+ #
124
+ def create_trigger_delay_timer(inputs, _mod)
125
+ logger.trace("Creating timer for trigger delay #{self}")
126
+ @timer = after(@duration) do
127
+ logger.trace("Delay Complete for #{self}, executing rule")
128
+ @timer = nil
129
+ yield
130
+ end
131
+ @tracking_to, = retrieve_states(inputs)
132
+ end
133
+
134
+ #
135
+ # Process an active trigger timer
136
+ #
137
+ # @param [Hash] inputs rule trigger inputs
138
+ # @param [Hash] mod rule trigger mods
139
+ #
140
+ #
141
+ def process_active_timer(inputs, mod, &block)
142
+ state, = retrieve_states(inputs)
143
+ if state == @tracking_to
144
+ logger.trace("Item changed to #{state} for #{self}, rescheduling timer.")
145
+ @timer.reschedule(ZonedDateTime.now.plus(@duration))
146
+ else
147
+ logger.trace("Item changed to #{state} for #{self}, canceling timer.")
148
+ @timer.cancel
149
+ # Reprocess trigger delay after canceling to track new state (if guards matched, etc)
150
+ process_trigger_delay(mod, inputs, &block)
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openhab/log/logger'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ module Rules
8
+ module Triggers
9
+ #
10
+ # Module for conditions for triggers
11
+ #
12
+ module Conditions
13
+ include OpenHAB::Log
14
+
15
+ #
16
+ # This creates trigger conditions that work on procs
17
+ # @param [Proc] from Proc
18
+ # @param [Proc] to Proc
19
+ #
20
+ class Proc
21
+ include OpenHAB::Log
22
+
23
+ # Proc that doesn't check any fields
24
+ ANY = Proc.new.freeze
25
+
26
+ #
27
+ # Converts supplied ranges to procs that check range
28
+ # @param [Array] ranges objects to convert to range proc if they are ranges
29
+ # @return [Array] of procs or supplied arguments if argument was not of type Range
30
+ #
31
+ def self.range_procs(*ranges)
32
+ ranges.map { |range| range.is_a?(Range) ? range_proc(range) : range }
33
+ end
34
+
35
+ #
36
+ # Create a range proc for the supplied range object
37
+ # @param [Range] range to build proc for
38
+ #
39
+ def self.range_proc(range)
40
+ logger.trace("Creating range proc for #{range}")
41
+ lambda do |state|
42
+ logger.trace("Range proc checking if #{state} is in #{range}")
43
+ range.include? state
44
+ end
45
+ end
46
+
47
+ #
48
+ # Create a range proc for the supplied range object
49
+ # @param [Range] range to build proc for
50
+ #
51
+ def self.equality_proc(value)
52
+ logger.trace("Creating equality proc for #{value}")
53
+ lambda do |state|
54
+ logger.trace("Equality proc comparing #{value} against #{state}")
55
+ value == state
56
+ end
57
+ end
58
+
59
+ #
60
+ # Constructs a proc for the specific value type
61
+ # if the value is a proc return the proc
62
+ # if the value is a range create a range proc
63
+ # if the value is nil, return nil
64
+ # otherwise create an equality proc
65
+ # @param [Object] value to construct proc from
66
+ def self.from_value(value)
67
+ logger.trace("Creating proc for Value(#{value})")
68
+ return value if value.nil?
69
+ return value if value.is_a? ::Proc
70
+ return range_proc(value) if value.is_a? Range
71
+
72
+ equality_proc(value)
73
+ end
74
+
75
+ #
76
+ # Create a new Proc Condition that executes only if procs return true
77
+ # @param [Proc] from Proc to check against from value
78
+ # @param [Proc] to Proc to check against to value
79
+ #
80
+ def initialize(from: nil, to: nil)
81
+ @from = from
82
+ @to = to
83
+ end
84
+
85
+ #
86
+ # Process rule
87
+ # @param [Hash] inputs inputs from trigger
88
+ #
89
+ def process(mod:, inputs:) # rubocop:disable Lint/UnusedMethodArgument - mod is unused here but required
90
+ logger.trace("Checking #{inputs} against condition trigger #{self}")
91
+ yield if check_from(inputs: inputs) && check_to(inputs: inputs)
92
+ end
93
+
94
+ #
95
+ # Check if from condition match the proc
96
+ # @param [Hash] inputs from trigger must be supplied if state is not supplied
97
+ # @param [String] state if supplied proc will be passed state value for comparision
98
+ # @return [true/false] depending on if from is set and matches supplied conditions
99
+ #
100
+ def check_from(inputs: nil, state: nil)
101
+ state ||= input_state(inputs, 'oldState')
102
+ logger.trace "Checking from(#{@from}) against state(#{state})"
103
+ check_proc(proc: @from, state: state)
104
+ end
105
+
106
+ #
107
+ # Check if to conditions match the proc
108
+ # @param [Hash] inputs from trigger must be supplied if state is not supplied
109
+ # @param [String] state if supplied proc will be passed state value for comparision
110
+ # @return [true/false] depending on if from is set and matches supplied conditions
111
+ #
112
+ def check_to(inputs: nil, state: nil)
113
+ state ||= input_state(inputs, 'newState', 'state')
114
+ logger.trace "Checking to(#{@to}) against state(#{state})"
115
+ check_proc(proc: @to, state: state)
116
+ end
117
+
118
+ # Describe the Proc Condition as a string
119
+ # @return [String] string representation of proc condition
120
+ #
121
+ def to_s
122
+ "From:(#{@from}) To:(#{@to})"
123
+ end
124
+
125
+ private
126
+
127
+ # Check if a field matches the proc condition
128
+ # @param [Proc] proc to call
129
+ # @param [Hash] inputs containing fields
130
+ # @param [Array] fields array of fields to extract from inputs, first one found is passed to proc
131
+ # @return [true,false] true if proc is nil or proc.call returns true, false otherwise
132
+ def check_proc(proc:, state:)
133
+ return true if proc.nil? || proc.call(state)
134
+
135
+ logger.trace("Skipped execution of rule because state #{state} evalulated false for (#{proc})")
136
+ false
137
+ end
138
+
139
+ # Get the first field from supplied fields in inputs
140
+ # @param [Hash] inputs containing fields
141
+ # @param [Array] fields array of fields to extract from inputs, first one found is returned
142
+ def input_state(inputs, *fields)
143
+ fields.map { |f| inputs[f] }.compact.first
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openhab/log/logger'
4
+ require_relative 'trigger'
4
5
 
5
6
  module OpenHAB
6
7
  module DSL
@@ -23,29 +24,71 @@ module OpenHAB
23
24
  separate_groups(items).map do |item|
24
25
  logger.trace("Creating updated trigger for item(#{item}) to(#{to})")
25
26
  [to].flatten.map do |to_state|
26
- trigger, config = create_update_trigger(item, to_state)
27
- append_trigger(trigger, config, attach: attach)
27
+ update_trigger(item: item, to: to_state, attach: attach)
28
28
  end
29
29
  end.flatten
30
30
  end
31
31
 
32
32
  private
33
33
 
34
+ #
35
+ # Create the trigger
36
+ #
37
+ # @param [Object] item item to create trigger for
38
+ # @param [Item State] from state to restrict trigger to
39
+ # @param [Item State] to state to restrict trigger to
40
+ # @param attach attachment
41
+ #
42
+ # @return [Trigger] OpenHAB triggers
43
+ #
44
+ def update_trigger(item:, to:, attach:)
45
+ case to
46
+ when Range then create_update_range_trigger(item: item, to: to, attach: attach)
47
+ when Proc then create_update_proc_trigger(item: item, to: to, attach: attach)
48
+ else create_update_trigger(item: item, to: to, attach: attach)
49
+ end
50
+ end
51
+
52
+ #
53
+ # Creates a trigger with a range condition on the 'to' field
54
+ # @param [Object] item to create changed trigger on
55
+ # @param [Object] to state restrict trigger to
56
+ # @param [Object] attach to trigger
57
+ # @return [Trigger] OpenHAB trigger
58
+ #
59
+ def create_update_range_trigger(item:, to:, attach:)
60
+ to, * = Conditions::Proc.range_procs(to)
61
+ create_update_proc_trigger(item: item, to: to, attach: attach)
62
+ end
63
+
64
+ #
65
+ # Creates a trigger with a proc condition on the 'to' field
66
+ # @param [Object] item to create changed trigger on
67
+ # @param [Object] to state restrict trigger to
68
+ # @param [Object] attach to trigger
69
+ # @return [Trigger] OpenHAB trigger
70
+ #
71
+ def create_update_proc_trigger(item:, to:, attach:)
72
+ create_update_trigger(item: item, to: nil, attach: attach).tap do |trigger|
73
+ @trigger_conditions[trigger.id] = Conditions::Proc.new(to: to)
74
+ end
75
+ end
76
+
34
77
  #
35
78
  # Create a trigger for updates
36
79
  #
37
80
  # @param [Object] item Type of item [Group,Thing,Item] to create update trigger for
38
81
  # @param [State] to_state state restriction on trigger
39
82
  #
40
- # @return [Array<Hash,String>] first element is a String specifying trigger type
41
- # second element is a Hash configuring trigger
83
+ # @return [Trigger] OpenHAB triggers
42
84
  #
43
- def create_update_trigger(item, to_state)
44
- case item
45
- when OpenHAB::DSL::Items::GroupItem::GroupMembers then group_update(item, to_state)
46
- when Thing then thing_update(item, to_state)
47
- else item_update(item, to_state)
48
- end
85
+ def create_update_trigger(item:, to:, attach:)
86
+ trigger, config = case item
87
+ when OpenHAB::DSL::Items::GroupItem::GroupMembers then group_update(item: item, to: to)
88
+ when Thing then thing_update(thing: item, to: to)
89
+ else item_update(item: item, to: to)
90
+ end
91
+ append_trigger(trigger, config, attach: attach)
49
92
  end
50
93
 
51
94
  #
@@ -57,9 +100,9 @@ module OpenHAB
57
100
  # @return [Array<Hash,String>] first element is a String specifying trigger type
58
101
  # second element is a Hash configuring trigger
59
102
  #
60
- def item_update(item, to_state)
103
+ def item_update(item:, to:)
61
104
  config = { 'itemName' => item.name }
62
- config['state'] = to_state.to_s unless to_state.nil?
105
+ config['state'] = to.to_s unless to.nil?
63
106
  trigger = Trigger::ITEM_STATE_UPDATE
64
107
  [trigger, config]
65
108
  end
@@ -73,9 +116,9 @@ module OpenHAB
73
116
  # @return [Array<Hash,String>] first element is a String specifying trigger type
74
117
  # second element is a Hash configuring trigger
75
118
  #
76
- def group_update(item, to_state)
119
+ def group_update(item:, to:)
77
120
  config = { 'groupName' => item.group.name }
78
- config['state'] = to_state.to_s unless to_state.nil?
121
+ config['state'] = to.to_s unless to.nil?
79
122
  trigger = Trigger::GROUP_STATE_UPDATE
80
123
  [trigger, config]
81
124
  end
@@ -89,8 +132,8 @@ module OpenHAB
89
132
  # @return [Array<Hash,String>] first element is a String specifying trigger type
90
133
  # second element is a Hash configuring trigger
91
134
  #
92
- def thing_update(thing, to_state)
93
- trigger_for_thing(thing, Trigger::THING_UPDATE, to_state)
135
+ def thing_update(thing:, to:)
136
+ trigger_for_thing(thing, Trigger::THING_UPDATE, to)
94
137
  end
95
138
  end
96
139
  end
@@ -10,8 +10,8 @@ module OpenHAB
10
10
  DateTimeType = org.openhab.core.library.types.DateTimeType
11
11
  java_import java.time.ZonedDateTime # This is needed for the addon prior to ruby_class fix (OH 3.2.0)
12
12
 
13
- # global alias
14
- ::DateTimeType = DateTimeType
13
+ # global alias - required for jrubyscripting addon <= OH3.2.0
14
+ ::DateTimeType = DateTimeType if ::DateTimeType.is_a?(Java::JavaLang::Class)
15
15
 
16
16
  # @deprecated
17
17
  # Backwards-compatible alias
@@ -8,8 +8,8 @@ module OpenHAB
8
8
  module Types
9
9
  HSBType = org.openhab.core.library.types.HSBType
10
10
 
11
- # global alias
12
- ::HSBType = HSBType
11
+ # global alias - required for jrubyscripting addon <= OH3.2.0
12
+ ::HSBType = HSBType if ::HSBType.is_a?(Java::JavaLang::Class)
13
13
 
14
14
  # Adds methods to core OpenHAB HSBType to make it more natural in Ruby
15
15
  class HSBType < PercentType
@@ -7,8 +7,8 @@ module OpenHAB
7
7
  module Types
8
8
  PercentType = org.openhab.core.library.types.PercentType
9
9
 
10
- # global alias
11
- ::PercentType = PercentType
10
+ # global alias - required for jrubyscripting addon <= OH3.2.0
11
+ ::PercentType = PercentType if ::PercentType.is_a?(Java::JavaLang::Class)
12
12
 
13
13
  # Adds methods to core OpenHAB PercentType to make it more natural in Ruby
14
14
  class PercentType < DecimalType
@@ -5,9 +5,9 @@ module OpenHAB
5
5
  module Types
6
6
  PointType = org.openhab.core.library.types.PointType
7
7
 
8
- # global scope
8
+ # global scope - required for jrubyscripting addon <= OH3.2.0
9
9
  # @!visibility private
10
- ::PointType = PointType
10
+ ::PointType = PointType if ::PointType.is_a?(Java::JavaLang::Class)
11
11
 
12
12
  # Adds methods to core OpenHAB PointType to make it more natural in Ruby
13
13
  class PointType
@@ -7,8 +7,8 @@ module OpenHAB
7
7
  module Types
8
8
  QuantityType = org.openhab.core.library.types.QuantityType
9
9
 
10
- # global alias
11
- ::QuantityType = QuantityType
10
+ # global alias - required for jrubyscripting addon <= OH3.2.0
11
+ ::QuantityType = QuantityType if ::QuantityType.is_a?(Java::JavaLang::Class)
12
12
 
13
13
  # @deprecated
14
14
  # Backwards-compatible alias
@@ -20,8 +20,8 @@ module OpenHAB
20
20
  include NumericType
21
21
 
22
22
  # private alias
23
- ONE = org.openhab.core.library.unit.Units::ONE
24
- private_constant :ONE
23
+ ONE_UNIT = org.openhab.core.library.unit.Units::ONE
24
+ private_constant :ONE_UNIT
25
25
 
26
26
  #
27
27
  # Convert this quantity into a another unit
@@ -46,8 +46,8 @@ module OpenHAB
46
46
  def <=>(other) # rubocop:disable Metrics
47
47
  logger.trace("(#{self.class}) #{self} <=> #{other} (#{other.class})")
48
48
  if other.is_a?(self.class)
49
- return unitize(other.unit).compare_to(other) if unit == ONE
50
- return compare_to(other.unitize(unit)) if other.unit == ONE
49
+ return unitize(other.unit).compare_to(other) if unit == ONE_UNIT
50
+ return compare_to(other.unitize(unit)) if other.unit == ONE_UNIT
51
51
 
52
52
  compare_to(other)
53
53
  elsif other.is_a?(Items::NumericItem) ||
@@ -88,7 +88,7 @@ module OpenHAB
88
88
  elsif other.is_a?(Type)
89
89
  [other, as(other.class)]
90
90
  elsif other.respond_to?(:to_d)
91
- [QuantityType.new(other.to_d.to_java, ONE), self]
91
+ [QuantityType.new(other.to_d.to_java, ONE_UNIT), self]
92
92
  elsif other.is_a?(String)
93
93
  [QuantityType.new(other), self]
94
94
  end
@@ -215,7 +215,7 @@ module OpenHAB
215
215
  logger.trace("Converting #{self} to #{other_unit}")
216
216
 
217
217
  case unit
218
- when ONE
218
+ when ONE_UNIT
219
219
  QuantityType.new(to_big_decimal, other_unit)
220
220
  when other_unit
221
221
  self
@@ -224,10 +224,10 @@ module OpenHAB
224
224
  end
225
225
  end
226
226
 
227
- # if unit is +ONE+, return a plain Java BigDecimal
227
+ # if unit is +ONE_UNIT+, return a plain Java BigDecimal
228
228
  # @!visibility private
229
229
  def deunitize
230
- return to_big_decimal if unit == ONE
230
+ return to_big_decimal if unit == ONE_UNIT
231
231
 
232
232
  self
233
233
  end
@@ -245,9 +245,8 @@ module OpenHAB
245
245
  #
246
246
  def log_caller
247
247
  caller_locations.map(&:path)
248
- .grep_v(%r{openhab/core/})
249
248
  .grep_v(/rubygems/)
250
- .grep_v(%r{lib/ruby})
249
+ .grep_v(/openhab-scripting/)
251
250
  .first
252
251
  .then { |caller| File.basename(caller, '.*') if caller }
253
252
  end
@@ -5,5 +5,5 @@
5
5
  #
6
6
  module OpenHAB
7
7
  # @return [String] Version of OpenHAB helper libraries
8
- VERSION = '4.28.2'
8
+ VERSION = '4.30.2'
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.28.2
4
+ version: 4.30.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian O'Connell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-17 00:00:00.000000000 Z
11
+ date: 2022-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -106,6 +106,8 @@ files:
106
106
  - lib/openhab/dsl/rules/triggers/changed.rb
107
107
  - lib/openhab/dsl/rules/triggers/channel.rb
108
108
  - lib/openhab/dsl/rules/triggers/command.rb
109
+ - lib/openhab/dsl/rules/triggers/conditions/duration.rb
110
+ - lib/openhab/dsl/rules/triggers/conditions/proc.rb
109
111
  - lib/openhab/dsl/rules/triggers/cron.rb
110
112
  - lib/openhab/dsl/rules/triggers/generic.rb
111
113
  - lib/openhab/dsl/rules/triggers/trigger.rb