openhab-scripting 4.27.1 → 4.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/dsl/rules/automation_rule.rb +10 -158
  3. data/lib/openhab/dsl/rules/rule.rb +4 -46
  4. data/lib/openhab/dsl/rules/rule_config.rb +4 -3
  5. data/lib/openhab/dsl/rules/triggers/changed.rb +30 -16
  6. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +184 -0
  7. data/lib/openhab/dsl/rules/triggers/conditions/none.rb +32 -0
  8. data/lib/openhab/dsl/rules/triggers/conditions/range.rb +74 -0
  9. data/lib/openhab/dsl/rules/triggers/cron.rb +118 -0
  10. data/lib/openhab/dsl/rules/triggers/trigger.rb +2 -1
  11. data/lib/openhab/dsl/rules/triggers/updated.rb +47 -16
  12. data/lib/openhab/dsl/rules/triggers/watch.rb +1 -3
  13. data/lib/openhab/dsl/timers/manager.rb +14 -1
  14. data/lib/openhab/dsl/types/date_time_type.rb +2 -2
  15. data/lib/openhab/dsl/types/decimal_type.rb +1 -1
  16. data/lib/openhab/dsl/types/hsb_type.rb +1 -1
  17. data/lib/openhab/dsl/types/increase_decrease_type.rb +1 -1
  18. data/lib/openhab/dsl/types/next_previous_type.rb +1 -1
  19. data/lib/openhab/dsl/types/on_off_type.rb +1 -1
  20. data/lib/openhab/dsl/types/open_closed_type.rb +1 -1
  21. data/lib/openhab/dsl/types/percent_type.rb +1 -1
  22. data/lib/openhab/dsl/types/play_pause_type.rb +1 -1
  23. data/lib/openhab/dsl/types/point_type.rb +1 -1
  24. data/lib/openhab/dsl/types/quantity_type.rb +1 -1
  25. data/lib/openhab/dsl/types/refresh_type.rb +1 -1
  26. data/lib/openhab/dsl/types/rewind_fastforward_type.rb +1 -1
  27. data/lib/openhab/dsl/types/stop_move_type.rb +1 -1
  28. data/lib/openhab/dsl/types/string_type.rb +1 -1
  29. data/lib/openhab/dsl/types/un_def_type.rb +1 -1
  30. data/lib/openhab/dsl/types/up_down_type.rb +1 -1
  31. data/lib/openhab/log/configuration.rb +1 -1
  32. data/lib/openhab/log/logger.rb +10 -0
  33. data/lib/openhab/version.rb +1 -1
  34. metadata +5 -3
  35. data/lib/openhab/dsl/rules/cron_trigger_rule.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7281829b6759d5f975fc84086826ed6a2712b841a41c47b1305eb37df54aa264
4
- data.tar.gz: c2002b1ba4a7f207c89355775fef67b30d28fa7c0dc8b0131785b8c5527988a5
3
+ metadata.gz: 346933e8c00b9208f18ce61d4a87911cdc315a7b7ee1698aec1977dc47ba9bea
4
+ data.tar.gz: 5a94e6d5a1d43a4fd3e6586bd70a9f3286918bee1c300fe490d2460dbdce3494
5
5
  SHA512:
6
- metadata.gz: 2f50fa017b7b74d18cf4419b9a28b25134c2d52b5e5871864b97dc1698b467e211dfc85a91a24f6b391584e2ca130c5596da7f894681fd0c2f85b3a446c983d3
7
- data.tar.gz: 658dd4e19e86c5629998023b415269c2c11891796020872e590f7cb85e69491046194db90ab2914cfbc38cbed77b0dec273040d107167239a7efa03d97de0ef1
6
+ metadata.gz: dcd2f7d6aa7b11074c059296b61b0fa22b691184915afe227ecf41d865690b6a81c8ea431c3b83407f05520aedca8238be4636f8da62217a0caf090d63bd8488
7
+ data.tar.gz: 1614e96cd679ebab6dfe811e7818e77e9e512603b126223f6a870f7b6550a2101cb86fe79f9856fe6559cc926cecf212afe15eb8a50892863c208b32d00f6386
@@ -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
  #
@@ -304,7 +166,7 @@ module OpenHAB
304
166
  end
305
167
  false
306
168
  rescue StandardError => e
307
- print_backtrace(e)
169
+ logger.log_exception(e, name)
308
170
  end
309
171
  # rubocop:enable Metrics/MethodLength
310
172
 
@@ -328,7 +190,7 @@ module OpenHAB
328
190
  end
329
191
  end
330
192
  rescue StandardError => e
331
- print_backtrace(e)
193
+ logger.log_exception(e, name)
332
194
  end
333
195
 
334
196
  #
@@ -399,16 +261,6 @@ module OpenHAB
399
261
  @run_context.instance_exec(event, &task.block)
400
262
  end
401
263
 
402
- #
403
- # Print error and stack trace without calls to internal classes
404
- #
405
- # @param [Exception] error A rescued error
406
- #
407
- def print_backtrace(error)
408
- error = logger.clean_backtrace(error)
409
- logger.error { "#{error.message} (#{error.class})\nIn rule: #{name}\n#{error.backtrace&.join("\n")}" }
410
- end
411
-
412
264
  #
413
265
  # Create a new hash in which all elements are converted to strings
414
266
  #
@@ -5,7 +5,6 @@ require 'openhab/core/services'
5
5
  require 'openhab/log/logger'
6
6
  require_relative 'rule_config'
7
7
  require_relative 'automation_rule'
8
- require_relative 'cron_trigger_rule'
9
8
  require_relative 'guard'
10
9
 
11
10
  module OpenHAB
@@ -26,7 +25,7 @@ module OpenHAB
26
25
  @registry = OpenHAB::Core.rule_registry
27
26
  class << self
28
27
  attr_reader :script_rules, :automation_manager, :registry
29
- end
28
+ end
30
29
 
31
30
  #
32
31
  # Create a new rule
@@ -35,7 +34,7 @@ end
35
34
  # @yield [] Block executed in context of a RuleConfig
36
35
  #
37
36
  #
38
- # rubocop: disable Metrics
37
+ # rubocop: disable Metrics/MethodLength
39
38
  def rule(rule_name, &block)
40
39
  thread_local(RULE_NAME: rule_name) do
41
40
  @rule_name = rule_name
@@ -47,10 +46,9 @@ end
47
46
  nil # Must return something other than the rule object. See https://github.com/boc-tothefuture/openhab-jruby/issues/438
48
47
  end
49
48
  rescue StandardError => e
50
- puts "#{e.class}: #{e.message}"
51
- re_raise_with_backtrace(e)
49
+ logger.log_exception(e, @rule_name)
52
50
  end
53
- # rubocop: enable Metrics
51
+ # rubocop: enable Metrics/MethodLength
54
52
 
55
53
  #
56
54
  # Cleanup rules in this script file
@@ -62,16 +60,6 @@ end
62
60
 
63
61
  private
64
62
 
65
- #
66
- # Re-raises a rescued error to OpenHAB with added rule name and stack trace
67
- #
68
- # @param [Exception] error A rescued error
69
- #
70
- def re_raise_with_backtrace(error)
71
- error = logger.clean_backtrace(error)
72
- raise error, "#{error.message}\nIn rule: #{@rule_name}\n#{error.backtrace.join("\n")}"
73
- end
74
-
75
63
  #
76
64
  # Process a rule based on the supplied configuration
77
65
  #
@@ -81,44 +69,14 @@ end
81
69
  def process_rule_config(config)
82
70
  return unless create_rule?(config)
83
71
 
84
- cron_attach_triggers, other_triggers = partition_triggers(config)
85
- logger.trace("Cron triggers: #{cron_attach_triggers} - Other triggers: #{other_triggers}")
86
- config.triggers = other_triggers
87
-
88
72
  rule = AutomationRule.new(config: config)
89
73
  Rules.script_rules << rule
90
74
  add_rule(rule)
91
75
 
92
- process_cron_attach(cron_attach_triggers, config, rule)
93
-
94
76
  rule.execute(nil, { 'event' => Struct.new(:attachment).new(config.start_attachment) }) if config.on_start?
95
77
  rule
96
78
  end
97
79
 
98
- #
99
- # Add cron triggers with attachments to rules
100
- # @param [Array] cron_attach_triggers cron type triggers with attachments
101
- #
102
- def process_cron_attach(cron_attach_triggers, config, rule)
103
- cron_attach_triggers&.map { |trigger| CronTriggerRule.new(rule_config: config, rule: rule, trigger: trigger) }
104
- &.each { |trigger| add_rule(trigger) }
105
- end
106
-
107
- #
108
- # Partitions triggers in a config, removing cron triggers with a corresponding attachment
109
- # so they can be used with CronTriggerRules to support attachments
110
- # @return [Array] Two element array the first element is cron triggers with attachments,
111
- # second element is other triggers
112
- #
113
- def partition_triggers(config)
114
- config
115
- .triggers
116
- .partition do |trigger|
117
- trigger.typeUID == OpenHAB::DSL::Rules::Triggers::Trigger::CRON &&
118
- config.attachments.key?(trigger.id)
119
- end
120
- end
121
-
122
80
  #
123
81
  # Should a rule be created based on rule configuration
124
82
  #
@@ -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/none'
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::None.instance)
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/range'
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,11 @@ 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)
81
77
  else
82
78
  create_changed_trigger(item, from, to, attach)
83
79
  end
@@ -90,13 +86,31 @@ module OpenHAB
90
86
  # @param [OpenHAB::Core::Duration] duration to delay trigger for until condition is met
91
87
  # @param [Item State] to OpenHAB Item State item or group needs to change to
92
88
  # @param [Item State] from OpenHAB Item State item or group needs to be coming from
89
+ # @param [Object] attach to trigger
93
90
  #
94
91
  # @return [Trigger] OpenHAB trigger
95
92
  #
96
93
  def changed_wait(item, duration:, to: nil, from: nil, attach: nil)
97
94
  trigger = create_changed_trigger(item, nil, nil, attach)
98
95
  logger.trace("Creating Changed Wait Change Trigger for #{item}")
99
- @trigger_delays[trigger.id] = TriggerDelay.new(to: to, from: from, duration: duration)
96
+ @trigger_conditions[trigger.id] = Conditions::Duration.new(to: to, from: from, duration: duration)
97
+ trigger
98
+ end
99
+
100
+ #
101
+ # Creates a trigger with a range condition on either 'from' or 'to' field
102
+ # @param [Object] item to create changed trigger on
103
+ # @param [Object] from state to restrict trigger to
104
+ # @param [Object] to state restrict trigger to
105
+ # @param [Object] attach to trigger
106
+ # @return [Trigger] OpenHAB trigger
107
+ #
108
+ def create_changed_range_trigger(item, from:, to:, attach:)
109
+ # swap range w/ nil if from or to is a range
110
+ from_range, from = from, from_range if from.is_a? Range
111
+ to_range, to = to, to_range if to.is_a? Range
112
+ trigger = create_changed_trigger(item, from, to, attach)
113
+ @trigger_conditions[trigger.id] = Conditions::Range.new(to: to_range, from: from_range)
100
114
  trigger
101
115
  end
102
116
 
@@ -104,8 +118,8 @@ module OpenHAB
104
118
  # Create a changed trigger
105
119
  #
106
120
  # @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
121
+ # @param [Object] from state to restrict trigger to
122
+ # @param [Object] to state restrict trigger to
109
123
  #
110
124
  #
111
125
  def create_changed_trigger(item, from, to, attach)
@@ -0,0 +1,184 @@
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
+
27
+ class Duration
28
+ def initialize(to:, from:, duration:)
29
+ @to = to
30
+ @from = from
31
+ @duration = duration
32
+ end
33
+
34
+ # Process rule
35
+ # @param [Hash] inputs inputs from trigger
36
+ #
37
+ def process(mod:, inputs:, &block)
38
+ process_trigger_delay(mod, inputs, &block)
39
+ end
40
+
41
+ private
42
+
43
+ #
44
+ # Checks if there is an active timer
45
+ # @return [true, false] true if the timer exists and is active, false otherwise
46
+ def timer_active?
47
+ @timer&.is_active
48
+ end
49
+
50
+ #
51
+ # Check if trigger guards prevent rule execution
52
+ #
53
+ # @param [Map] inputs OpenHAB map object describing rule trigger
54
+ #
55
+ # @return [Boolean] True if the rule should execute, false if trigger guard prevents execution
56
+ #
57
+ def check_trigger_guards(inputs)
58
+ new_state, old_state = retrieve_states(inputs)
59
+ if check_from(old_state)
60
+ return true if check_to(new_state)
61
+
62
+ logger.trace("Skipped execution of rule because to state #{new_state}"\
63
+ " does not equal specified state(#{@to})")
64
+ else
65
+ logger.trace("Skipped execution of rule because old state #{old_state}"\
66
+ " does not equal specified state(#{@from})")
67
+ end
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
+ old_state = inputs['oldState'] || thing_status_to_sym(inputs['oldStatus'])
80
+ new_state = inputs['newState'] || thing_status_to_sym(inputs['newStatus'])
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
+ # Check the from state against the trigger delay
98
+ #
99
+ # @param [Item State] state from state to check
100
+ #
101
+ # @return [Boolean] true if no from state is defined or defined state equals supplied state
102
+ #
103
+ def check_from(state)
104
+ return @from.include?(state) if @from.is_a?(::Range)
105
+
106
+ @from.nil? || @from == state
107
+ end
108
+
109
+ #
110
+ # Check the to state against the trigger delay
111
+ #
112
+ # @param [Item State] state to-state to check
113
+ #
114
+ # @return [Boolean] true if no to state is defined or defined state equals supplied state
115
+ #
116
+ def check_to(state)
117
+ return @to.include?(state) if @to.is_a?(::Range)
118
+
119
+ @to.nil? || @to == state
120
+ end
121
+
122
+ #
123
+ # Process any matching trigger delays
124
+ #
125
+ # @param [Map] mod OpenHAB map object describing rule trigger
126
+ # @param [Map] inputs OpenHAB map object describing rule trigger
127
+ #
128
+ #
129
+ def process_trigger_delay(mod, inputs, &block)
130
+ if timer_active?
131
+ process_active_timer(inputs, mod, &block)
132
+ elsif check_trigger_guards(inputs)
133
+ logger.trace("Trigger Guards Matched for #{self}, delaying rule execution")
134
+ # Add timer and attach timer to delay object, and also state being tracked to so
135
+ # timer can be cancelled if state changes
136
+ # Also another timer should not be created if changed to same value again but instead rescheduled
137
+ create_trigger_delay_timer(inputs, mod, &block)
138
+ else
139
+ logger.trace("Trigger Guards did not match for #{self}, ignoring trigger.")
140
+ end
141
+ end
142
+
143
+ #
144
+ # Creates a timer for trigger delays
145
+ #
146
+ # @param [Hash] inputs rule trigger inputs
147
+ # @param [Hash] mod rule trigger mods
148
+ #
149
+ #
150
+ def create_trigger_delay_timer(inputs, _mod)
151
+ logger.trace("Creating timer for trigger delay #{self}")
152
+ @timer = after(@duration) do
153
+ logger.trace("Delay Complete for #{self}, executing rule")
154
+ @timer = nil
155
+ yield
156
+ end
157
+ @tracking_to, = retrieve_states(inputs)
158
+ end
159
+
160
+ #
161
+ # Process an active trigger timer
162
+ #
163
+ # @param [Hash] inputs rule trigger inputs
164
+ # @param [Hash] mod rule trigger mods
165
+ #
166
+ #
167
+ def process_active_timer(inputs, mod, &block)
168
+ state, = retrieve_states(inputs)
169
+ if state == @tracking_to
170
+ logger.trace("Item changed to #{state} for #{self}, rescheduling timer.")
171
+ @timer.reschedule(ZonedDateTime.now.plus(@duration))
172
+ else
173
+ logger.trace("Item changed to #{state} for #{self}, canceling timer.")
174
+ @timer.cancel
175
+ # Reprocess trigger delay after canceling to track new state (if guards matched, etc)
176
+ process_trigger_delay(mod, inputs, &block)
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openhab/log/logger'
4
+ require 'singleton'
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ module Rules
9
+ module Triggers
10
+ #
11
+ # Module for conditions for triggers
12
+ #
13
+ module Conditions
14
+ include OpenHAB::Log
15
+ #
16
+ # this is a no-op condition which simply executes the provided block
17
+ #
18
+ class None
19
+ include Singleton
20
+
21
+ # Process rule
22
+ # @param [Hash] inputs inputs from trigger
23
+ #
24
+ def process(*)
25
+ yield
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end