openhab-jrubyscripting 5.0.0.rc2 → 5.0.0.rc4

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions.rb +6 -2
  3. data/lib/openhab/core/items/generic_item.rb +13 -5
  4. data/lib/openhab/core/items/metadata/hash.rb +19 -35
  5. data/lib/openhab/core/items/persistence.rb +3 -1
  6. data/lib/openhab/core/items/semantics/enumerable.rb +6 -4
  7. data/lib/openhab/core/items/semantics.rb +18 -22
  8. data/lib/openhab/core/profile_factory.rb +15 -3
  9. data/lib/openhab/core/provider.rb +11 -4
  10. data/lib/openhab/core/rules/module.rb +26 -0
  11. data/lib/openhab/core/rules/provider.rb +40 -0
  12. data/lib/openhab/core/rules/registry.rb +76 -0
  13. data/lib/openhab/core/rules/rule.rb +150 -0
  14. data/lib/openhab/core/rules.rb +25 -0
  15. data/lib/openhab/core/script_handling.rb +32 -3
  16. data/lib/openhab/core/timer.rb +5 -7
  17. data/lib/openhab/core/types.rb +1 -1
  18. data/lib/openhab/core.rb +0 -16
  19. data/lib/openhab/core_ext/java/list.rb +436 -0
  20. data/lib/openhab/core_ext/java/map.rb +66 -0
  21. data/lib/openhab/core_ext/java/month.rb +1 -1
  22. data/lib/openhab/core_ext/java/zoned_date_time.rb +1 -2
  23. data/lib/openhab/core_ext/ruby/date.rb +3 -0
  24. data/lib/openhab/core_ext/ruby/date_time.rb +53 -0
  25. data/lib/openhab/core_ext/ruby/time.rb +88 -86
  26. data/lib/openhab/dsl/events/watch_event.rb +1 -1
  27. data/lib/openhab/dsl/items/builder.rb +8 -3
  28. data/lib/openhab/dsl/items/ensure.rb +6 -2
  29. data/lib/openhab/dsl/items/timed_command.rb +10 -11
  30. data/lib/openhab/dsl/rules/automation_rule.rb +36 -13
  31. data/lib/openhab/dsl/rules/builder.rb +100 -9
  32. data/lib/openhab/dsl/rules/name_inference.rb +0 -5
  33. data/lib/openhab/dsl/rules/rule_triggers.rb +1 -1
  34. data/lib/openhab/dsl/rules/terse.rb +1 -2
  35. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +18 -54
  36. data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +0 -3
  37. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +1 -1
  38. data/lib/openhab/dsl/rules.rb +0 -21
  39. data/lib/openhab/dsl/thread_local.rb +2 -2
  40. data/lib/openhab/dsl/timer_manager.rb +3 -1
  41. data/lib/openhab/dsl/version.rb +1 -1
  42. data/lib/openhab/dsl.rb +12 -105
  43. data/lib/openhab/log.rb +9 -2
  44. data/lib/openhab/rspec/example_group.rb +42 -0
  45. data/lib/openhab/rspec/helpers.rb +31 -8
  46. data/lib/openhab/rspec/hooks.rb +6 -10
  47. data/lib/openhab/rspec/karaf.rb +50 -27
  48. data/lib/openhab/rspec/mocks/synchronous_executor.rb +11 -4
  49. data/lib/openhab/rspec/mocks/timer.rb +2 -1
  50. data/lib/openhab/rspec/suspend_rules.rb +4 -2
  51. metadata +24 -3
  52. data/lib/openhab/dsl/script_handling.rb +0 -6
@@ -2,103 +2,105 @@
2
2
 
3
3
  require "forwardable"
4
4
 
5
- module OpenHAB
6
- module CoreExt
7
- module Ruby
8
- # Extensions to Time and DateTime
9
- module TimeExtensions
10
- extend Forwardable
11
-
12
- # @!visibility private
13
- def self.included(base)
14
- base.send :alias_method, :plus_without_temporal, :+
15
- base.send :alias_method, :+, :plus_with_temporal
16
- base.send :alias_method, :minus_without_temporal, :-
17
- base.send :alias_method, :-, :minus_with_temporal
18
- end
5
+ # Extensions to Time
6
+ class Time
7
+ extend Forwardable
19
8
 
20
- #
21
- # @!method +(other)
22
- #
23
- # Extends {#+} to allow adding a {java.time.temporal.TemporalAmount TemporalAmount}
24
- #
25
- # @param [java.time.temporal.TemporalAmount] other
26
- # @return [ZonedDateTime] If other is a {java.time.temporal.TemporalAmount TemporalAmount}
27
- # @return [Time] If other is a Numeric
28
- #
29
- def plus_with_temporal(other)
30
- return to_zoned_date_time + other if other.is_a?(java.time.temporal.TemporalAmount)
9
+ #
10
+ # @!method +(other)
11
+ #
12
+ # Extends {#+} to allow adding a {java.time.temporal.TemporalAmount TemporalAmount}
13
+ #
14
+ # @param [java.time.temporal.TemporalAmount] other
15
+ # @return [ZonedDateTime] If other is a {java.time.temporal.TemporalAmount TemporalAmount}
16
+ # @return [Time] If other is a Numeric
17
+ #
18
+ def plus_with_temporal(other)
19
+ return to_zoned_date_time + other if other.is_a?(java.time.temporal.TemporalAmount)
31
20
 
32
- plus_without_temporal(other)
33
- end
21
+ plus_without_temporal(other)
22
+ end
23
+ alias_method :plus_without_temporal, :+
24
+ alias_method :+, :plus_with_temporal
34
25
 
35
- #
36
- # @!method -(other)
37
- #
38
- # Extends {#-} to allow subtracting a {java.time.temporal.TemporalAmount TemporalAmount}
39
- #
40
- # @param [java.time.temporal.TemporalAmount] other
41
- # @return [ZonedDateTime] If other is a {java.time.temporal.TemporalAmount TemporalAmount}
42
- # @return [Time] If other is a Numeric
43
- #
44
- def minus_with_temporal(other)
45
- return to_zoned_date_time - other if other.is_a?(java.time.temporal.TemporalAmount)
26
+ #
27
+ # @!method -(other)
28
+ #
29
+ # Extends {#-} to allow subtracting a {java.time.temporal.TemporalAmount TemporalAmount}
30
+ # or any other date/time class that responds to #to_zoned_date_time.
31
+ #
32
+ # Subtractions with another object of the same class (e.g. Time - Other Time, or DateTime - Other DateTime)
33
+ # remains unchanged from its original behavior.
34
+ #
35
+ # @example Time - Duration -> ZonedDateTime
36
+ # zdt_one_hour_ago = Time.now - 1.hour
37
+ #
38
+ # @example Time - ZonedDateTime -> Duration
39
+ # java_duration = Time.now - 1.hour.ago
40
+ #
41
+ # @example Time - Numeric -> Time
42
+ # time_one_hour_ago = Time - 3600
43
+ #
44
+ # @example Time - Time -> Float
45
+ # one_day_in_secs = Time.new(2002, 10, 31) - Time.new(2002, 10, 30)
46
+ #
47
+ # @param [java.time.temporal.TemporalAmount, #to_zoned_date_time] other
48
+ # @return [ZonedDateTime] If other is a {java.time.temporal.TemporalAmount TemporalAmount}
49
+ # @return [Duration] If other responds to #to_zoned_date_time
50
+ # @return [Time] If other is a Numeric
51
+ # @return [Float] If other is a Time
52
+ #
53
+ def minus_with_temporal(other)
54
+ return to_zoned_date_time - other if other.is_a?(java.time.temporal.TemporalAmount)
46
55
 
47
- minus_without_temporal(other)
48
- end
56
+ # Exclude subtracting against the same class
57
+ if other.respond_to?(:to_zoned_date_time) && !other.is_a?(self.class)
58
+ return to_zoned_date_time - other.to_zoned_date_time
59
+ end
49
60
 
50
- # @return [LocalDate]
51
- def to_local_date(_context = nil)
52
- java.time.LocalDate.of(year, month, day)
53
- end
61
+ minus_without_temporal(other)
62
+ end
63
+ alias_method :minus_without_temporal, :-
64
+ alias_method :-, :minus_with_temporal
54
65
 
55
- # @!method to_local_time
56
- # @return [LocalTime]
57
- def_delegator :to_zoned_date_time, :to_local_time
66
+ # @return [LocalDate]
67
+ def to_local_date(_context = nil)
68
+ java.time.LocalDate.of(year, month, day)
69
+ end
58
70
 
59
- # @return [Month]
60
- def to_month
61
- java.time.Month.of(month)
62
- end
71
+ # @!method to_local_time
72
+ # @return [LocalTime]
73
+ def_delegator :to_zoned_date_time, :to_local_time
63
74
 
64
- # @return [MonthDay]
65
- def to_month_day
66
- java.time.MonthDay.of(month, day)
67
- end
75
+ # @return [Month]
76
+ def to_month
77
+ java.time.Month.of(month)
78
+ end
68
79
 
69
- # @param [ZonedDateTime, nil] context
70
- # A {ZonedDateTime} used to fill in missing fields
71
- # during conversion. Not used in this class.
72
- # @return [ZonedDateTime]
73
- def to_zoned_date_time(context = nil) # rubocop:disable Lint/UnusedMethodArgument
74
- to_java(ZonedDateTime)
75
- end
80
+ # @return [MonthDay]
81
+ def to_month_day
82
+ java.time.MonthDay.of(month, day)
83
+ end
76
84
 
77
- #
78
- # Converts to a {ZonedDateTime} if `other`
79
- # is also convertible to a ZonedDateTime.
80
- #
81
- # @param [#to_zoned_date_time] other
82
- # @return [Array, nil]
83
- #
84
- def coerce(other)
85
- [other.to_zoned_date_time(to_zoned_date_time), self] if other.respond_to?(:to_zoned_date_time)
86
- end
87
- end
88
- end
85
+ # @param [ZonedDateTime, nil] context
86
+ # A {ZonedDateTime} used to fill in missing fields
87
+ # during conversion. Not used in this class.
88
+ # @return [ZonedDateTime]
89
+ def to_zoned_date_time(context = nil) # rubocop:disable Lint/UnusedMethodArgument
90
+ to_java(java.time.ZonedDateTime)
89
91
  end
90
- end
91
92
 
92
- #
93
- # Extensions to Ruby Time
94
- #
95
- class Time
96
- include(OpenHAB::CoreExt::Ruby::TimeExtensions)
97
- end
93
+ #
94
+ # Converts to a {ZonedDateTime} if `other`
95
+ # is also convertible to a ZonedDateTime.
96
+ #
97
+ # @param [#to_zoned_date_time] other
98
+ # @return [Array, nil]
99
+ #
100
+ def coerce(other)
101
+ return unless other.respond_to?(:to_zoned_date_time)
98
102
 
99
- #
100
- # Extensions to Ruby DateTime
101
- #
102
- class DateTime
103
- include(OpenHAB::CoreExt::Ruby::TimeExtensions)
103
+ zdt = to_zoned_date_time
104
+ [other.to_zoned_date_time(zdt), zdt]
105
+ end
104
106
  end
@@ -4,7 +4,7 @@ module OpenHAB
4
4
  module DSL
5
5
  module Events
6
6
  #
7
- # Event object passed by a {Rules::Builder#watch} trigger.
7
+ # Event object passed by a {Rules::BuilderDSL#watch} trigger.
8
8
  #
9
9
  # @!attribute [r] path
10
10
  # @return [Pathname] The path that had an event
@@ -40,7 +40,7 @@ module OpenHAB
40
40
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
41
41
  def #{method}_item(*args, **kwargs, &block) # def dimmer_item(*args, **kwargs, &block)
42
42
  item(#{method.inspect}, *args, **kwargs, &block) # item(:dimmer, *args, **kwargs, &block)
43
- end
43
+ end # end
44
44
  RUBY
45
45
  end
46
46
  end
@@ -251,7 +251,12 @@ module OpenHAB
251
251
  @autoupdate = autoupdate
252
252
  @channels = []
253
253
  @expire = nil
254
- self.expire(*Array(expire)) if expire
254
+ if expire
255
+ expire = Array(expire)
256
+ expire_config = expire.pop if expire.last.is_a?(Hash)
257
+ expire_config ||= {}
258
+ self.expire(*expire, **expire_config)
259
+ end
255
260
  self.alexa(alexa) if alexa
256
261
  self.ga(ga) if ga
257
262
  self.homekit(homekit) if homekit
@@ -365,7 +370,7 @@ module OpenHAB
365
370
  # end
366
371
  # end
367
372
  #
368
- def channel(channel, **config)
373
+ def channel(channel, config = {})
369
374
  @channels << [channel, config]
370
375
  end
371
376
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ruby2_keywords"
4
+
3
5
  module OpenHAB
4
6
  module DSL
5
7
  module Items
@@ -21,6 +23,9 @@ module OpenHAB
21
23
  end
22
24
 
23
25
  # Extensions for {::GenericItem} to implement {Ensure}'s functionality
26
+ #
27
+ # @see OpenHAB::DSL.ensure ensure
28
+ # @see OpenHAB::DSL.ensure_states ensure_states
24
29
  module GenericItem
25
30
  include Ensurable
26
31
 
@@ -71,14 +76,13 @@ module OpenHAB
71
76
  end
72
77
 
73
78
  # activate `ensure_states` before forwarding to the wrapped object
74
- def method_missing(method, *args, &block)
79
+ ruby2_keywords def method_missing(method, *args, &block)
75
80
  return super unless @item.respond_to?(method)
76
81
 
77
82
  DSL.ensure_states do
78
83
  @item.__send__(method, *args, &block)
79
84
  end
80
85
  end
81
- ruby2_keywords :method_missing if respond_to? :ruby2_keywords
82
86
 
83
87
  # .
84
88
  def respond_to_missing?(method, include_private = false)
@@ -123,9 +123,9 @@ module OpenHAB
123
123
  timed_command_details.on_expire = on_expire unless on_expire.nil?
124
124
  timed_command_details.timer.reschedule(duration)
125
125
  # disable the cancel rule while we send the new command
126
- Core.rule_manager.set_enabled(timed_command_details.rule_uid, false)
126
+ DSL.rules[timed_command_details.rule_uid].disable
127
127
  super(command)
128
- Core.rule_manager.set_enabled(timed_command_details.rule_uid, true)
128
+ DSL.rules[timed_command_details.rule_uid].enable
129
129
  timed_command_details
130
130
  end
131
131
  end
@@ -145,10 +145,9 @@ module OpenHAB
145
145
 
146
146
  timed_command_details.timer = timed_command_timer(timed_command_details, duration)
147
147
  cancel_rule = TimedCommandCancelRule.new(timed_command_details)
148
- timed_command_details.rule_uid = Core.automation_manager
149
- .add_rule(cancel_rule)
150
- .uid
151
- Rules.script_rules[timed_command_details.rule_uid] = cancel_rule
148
+ unmanaged_rule = Core.automation_manager.add_unmanaged_rule(cancel_rule)
149
+ timed_command_details.rule_uid = unmanaged_rule.uid
150
+ Core::Rules::Provider.current.add(unmanaged_rule)
152
151
  logger.trace "Created Timed Command #{timed_command_details}"
153
152
  timed_command_details
154
153
  end
@@ -160,7 +159,7 @@ module OpenHAB
160
159
  DSL.after(duration) do
161
160
  timed_command_details.mutex.synchronize do
162
161
  logger.trace "Timed command expired - #{timed_command_details}"
163
- DSL.remove_rule(timed_command_details.rule_uid)
162
+ DSL.rules.remove(timed_command_details.rule_uid)
164
163
  timed_command_details.resolution = :expired
165
164
  case timed_command_details.on_expire
166
165
  when Proc
@@ -200,11 +199,9 @@ module OpenHAB
200
199
  type: Rules::Triggers::Changed::ITEM_STATE_CHANGE,
201
200
  config: { "itemName" => timed_command_details.item.name }
202
201
  )]
202
+ self.visibility = Core::Rules::Visibility::HIDDEN
203
203
  end
204
204
 
205
- # Cleanup the rule; nothing to do here.
206
- def cleanup; end
207
-
208
205
  #
209
206
  # Execute the rule
210
207
  #
@@ -217,7 +214,7 @@ module OpenHAB
217
214
  logger.trace "Canceling implicit timer #{@timed_command_details.timer} for "\
218
215
  "#{@timed_command_details.item.name} because received event #{inputs}"
219
216
  @timed_command_details.timer.cancel
220
- DSL.remove_rule(@timed_command_details.rule_uid)
217
+ DSL.rules.remove(@timed_command_details.rule_uid)
221
218
  @timed_command_details.resolution = :cancelled
222
219
  if @timed_command_details.on_expire.is_a?(Proc)
223
220
  logger.trace "Executing user supplied block on timed command cancelation"
@@ -226,6 +223,8 @@ module OpenHAB
226
223
  end
227
224
  TimedCommand.timed_commands.delete(@timed_command_details.item)
228
225
  rescue Exception => e
226
+ raise if defined?(::RSpec)
227
+
229
228
  logger.log_exception(e)
230
229
  end
231
230
  end
@@ -16,7 +16,7 @@ module OpenHAB
16
16
  # @param [Config] config Rule configuration
17
17
  #
18
18
  # Constructor sets a number of variables, no further decomposition necessary
19
- def initialize(config:)
19
+ def initialize(config)
20
20
  # Metrics disabled because only setters are called or defaults set.
21
21
  super()
22
22
  set_name(config.name)
@@ -29,8 +29,13 @@ module OpenHAB
29
29
  @guard = config.guard
30
30
  @between = config.between && DSL.between(config.between)
31
31
  @trigger_conditions = config.trigger_conditions
32
+ @trigger_conditions.each_value do |condition|
33
+ condition.rule = self if condition.respond_to?(:rule=)
34
+ end
32
35
  @attachments = config.attachments
33
36
  @thread_locals = ThreadLocal.persist
37
+ @cleanup_hooks = Set.new
38
+ @listener = nil
34
39
  end
35
40
 
36
41
  #
@@ -48,19 +53,37 @@ module OpenHAB
48
53
  process_queue(create_queue(inputs), mod, inputs)
49
54
  end
50
55
  rescue Exception => e
56
+ raise if defined?(::RSpec)
57
+
51
58
  @run_context.send(:logger).log_exception(e)
52
59
  end
53
60
  end
54
61
 
55
- #
56
- # Cleanup any resources associated with automation rule
57
- #
58
- def cleanup
59
- @trigger_conditions.each_value(&:cleanup)
62
+ # @!visibility private
63
+ def on_removal(listener)
64
+ @cleanup_hooks << listener
65
+ listen_for_removal unless @listener
60
66
  end
61
67
 
62
68
  private
63
69
 
70
+ def cleanup
71
+ @cleanup_hooks.each(&:cleanup)
72
+ end
73
+
74
+ def listen_for_removal
75
+ @listener ||= org.openhab.core.common.registry.RegistryChangeListener.impl do |method, element|
76
+ next unless method == :removed
77
+
78
+ logger.trace("Rule #{element.inspect} removed from registry")
79
+ next unless element.uid == uid
80
+
81
+ cleanup
82
+ $rules.remove_registry_change_listener(@listener)
83
+ end
84
+ $rules.add_registry_change_listener(@listener)
85
+ end
86
+
64
87
  #
65
88
  # Create the run queue based on guards
66
89
  #
@@ -70,9 +93,9 @@ module OpenHAB
70
93
  def create_queue(inputs)
71
94
  case check_guards(event: extract_event(inputs))
72
95
  when true
73
- @run_queue.dup.grep_v(Builder::Otherwise)
96
+ @run_queue.dup.grep_v(BuilderDSL::Otherwise)
74
97
  when false
75
- @run_queue.dup.grep(Builder::Otherwise)
98
+ @run_queue.dup.grep(BuilderDSL::Otherwise)
76
99
  end
77
100
  end
78
101
 
@@ -166,7 +189,7 @@ module OpenHAB
166
189
  event = extract_event(inputs)
167
190
 
168
191
  while (task = run_queue.shift)
169
- if task.is_a?(Builder::Delay)
192
+ if task.is_a?(BuilderDSL::Delay)
170
193
  process_delay_task(inputs, mod, run_queue, task)
171
194
  else
172
195
  process_task(event, task)
@@ -183,10 +206,10 @@ module OpenHAB
183
206
  def process_task(event, task)
184
207
  ThreadLocal.thread_local(**@thread_locals) do
185
208
  case task
186
- when Builder::Run then process_run_task(event, task)
187
- when Builder::Script then process_script_task(task)
188
- when Builder::Trigger then process_trigger_task(event, task)
189
- when Builder::Otherwise then process_otherwise_task(event, task)
209
+ when BuilderDSL::Run then process_run_task(event, task)
210
+ when BuilderDSL::Script then process_script_task(task)
211
+ when BuilderDSL::Trigger then process_trigger_task(event, task)
212
+ when BuilderDSL::Otherwise then process_otherwise_task(event, task)
190
213
  end
191
214
  end
192
215
  end
@@ -5,6 +5,7 @@ require "forwardable"
5
5
  require_relative "property"
6
6
  require_relative "guard"
7
7
  require_relative "rule_triggers"
8
+ require_relative "terse"
8
9
 
9
10
  Dir[File.expand_path("triggers/*.rb", __dir__)].sort.each do |f|
10
11
  require f
@@ -16,10 +17,100 @@ module OpenHAB
16
17
  # Creates and manages OpenHAB Rules
17
18
  #
18
19
  module Rules
20
+ # A rules builder allows you to create OpenHAB rules.
19
21
  #
20
- # Rule configuration for OpenHAB Rules engine
22
+ # Note that all methods on this module are also availabe directly on {OpenHAB::DSL}.
21
23
  #
22
24
  class Builder
25
+ include Terse
26
+
27
+ # @return [org.openhab.core.automation.RuleProvider]
28
+ attr_reader :provider
29
+
30
+ def initialize(provider)
31
+ @provider = Core::Rules::Provider.current(provider)
32
+ end
33
+
34
+ #
35
+ # Create a new rule
36
+ #
37
+ # @param [String] name The rule name
38
+ # @yield Block executed in the context of a {Rules::Builder}
39
+ # @yieldparam [Rules::Builder] rule
40
+ # Optional parameter to access the rule configuration from within execution blocks and guards.
41
+ # @return [Rule]
42
+ #
43
+ # @see OpenHAB::DSL::Rules::Builder Rule builder for details on rule triggers, guards and execution blocks
44
+ # @see Rules::Terse Terse Rules
45
+ #
46
+ # @example
47
+ # require "openhab/dsl"
48
+ #
49
+ # rule "name" do
50
+ # <zero or more triggers>
51
+ # <zero or more execution blocks>
52
+ # <zero or more guards>
53
+ # end
54
+ #
55
+ def rule(name = nil, id: nil, script: nil, binding: nil, &block)
56
+ raise ArgumentError, "Block is required" unless block
57
+
58
+ id ||= NameInference.infer_rule_id_from_block(block)
59
+ script ||= block.source rescue nil # rubocop:disable Style/RescueModifier
60
+
61
+ builder = nil
62
+
63
+ ThreadLocal.thread_local(openhab_rule_type: "rule", openhab_rule_uid: id) do
64
+ builder = BuilderDSL.new(binding || block.binding)
65
+ builder.uid(id)
66
+ builder.instance_exec(builder, &block)
67
+ builder.guard = Guard.new(run_context: builder.caller, only_if: builder.only_if,
68
+ not_if: builder.not_if)
69
+
70
+ name ||= NameInference.infer_rule_name(builder)
71
+ name ||= id
72
+
73
+ builder.name(name)
74
+ logger.trace { builder.inspect }
75
+ builder.build(provider, script)
76
+ end
77
+ end
78
+
79
+ #
80
+ # Create a new script
81
+ #
82
+ # A script is a rule with no triggers. It can be called by various other actions,
83
+ # such as the Run Rules action.
84
+ #
85
+ # @param [String] name A descriptive name
86
+ # @param [String] id The script's ID
87
+ # @yield [] Block executed when the script is executed.
88
+ # @return [Rule]
89
+ #
90
+ def script(name = nil, id: nil, script: nil, &block)
91
+ raise ArgumentError, "Block is required" unless block
92
+
93
+ id ||= NameInference.infer_rule_id_from_block(block)
94
+ name ||= id
95
+ script ||= block.source rescue nil # rubocop:disable Style/RescueModifier
96
+
97
+ builder = nil
98
+ ThreadLocal.thread_local(openhab_rule_type: "script", openhab_rule_uid: id) do
99
+ builder = BuilderDSL.new(block.binding)
100
+ builder.uid(id)
101
+ builder.tags(["Script"])
102
+ builder.name(name)
103
+ builder.script(&block)
104
+ logger.trace { builder.inspect }
105
+ builder.build(provider, script)
106
+ end
107
+ end
108
+ end
109
+
110
+ #
111
+ # Rule configuration for OpenHAB Rules engine
112
+ #
113
+ class BuilderDSL
23
114
  include Core::EntityLookup
24
115
  include DSL
25
116
  prepend Triggers
@@ -1319,15 +1410,14 @@ module OpenHAB
1319
1410
  # @param [String] script The source code of the rule
1320
1411
  #
1321
1412
  # @!visibility private
1322
- def build(script)
1413
+ def build(provider, script)
1323
1414
  return unless create_rule?
1324
1415
 
1325
- rule = AutomationRule.new(config: self)
1326
- added_rule = add_rule(rule)
1327
- Rules.script_rules[rule.uid] = rule
1416
+ rule = AutomationRule.new(self)
1417
+ added_rule = add_rule(provider, rule)
1328
1418
  # add config so that MainUI can show the script
1329
1419
  added_rule.actions.first.configuration.put("type", "application/x-ruby")
1330
- added_rule.actions.first.configuration.put("script", script)
1420
+ added_rule.actions.first.configuration.put("script", script) if script
1331
1421
 
1332
1422
  rule.execute(nil, { "event" => Struct.new(:attachment).new(start_attachment) }) if on_start?
1333
1423
  added_rule
@@ -1383,8 +1473,7 @@ module OpenHAB
1383
1473
  #
1384
1474
  # @param [org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule] rule to add
1385
1475
  #
1386
- #
1387
- def add_rule(rule)
1476
+ def add_rule(provider, rule)
1388
1477
  base_uid = rule.uid
1389
1478
  duplicate_index = 1
1390
1479
  while $rules.get(rule.uid)
@@ -1392,7 +1481,9 @@ module OpenHAB
1392
1481
  rule.uid = "#{base_uid} (#{duplicate_index})"
1393
1482
  end
1394
1483
  logger.trace("Adding rule: #{rule}")
1395
- Core.automation_manager.add_rule(rule)
1484
+ unmanaged_rule = Core.automation_manager.add_unmanaged_rule(rule)
1485
+ provider.add(unmanaged_rule)
1486
+ unmanaged_rule
1396
1487
  end
1397
1488
  end
1398
1489
  end
@@ -23,11 +23,6 @@ module OpenHAB
23
23
  private_constant :KNOWN_TRIGGER_TYPES
24
24
 
25
25
  class << self
26
- # takes a freeform rule name and makes it more palatable as an id
27
- def infer_rule_id_from_name(name)
28
- name.downcase.delete("'\"").gsub(/\W+/, "_")
29
- end
30
-
31
26
  # get the block's source location, and simplify to a simple filename
32
27
  def infer_rule_id_from_block(block)
33
28
  file = File.basename(block.source_location.first)
@@ -44,7 +44,7 @@ module OpenHAB
44
44
  def append_trigger(type:, config:, attach: nil, conditions: nil)
45
45
  config.transform_keys!(&:to_s)
46
46
  RuleTriggers.trigger(type: type, config: config).tap do |trigger|
47
- logger.trace("Appending trigger (#{trigger}) attach (#{attach}) conditions(#{conditions})")
47
+ logger.trace("Appending trigger (#{trigger.inspect}) attach (#{attach}) conditions(#{conditions})")
48
48
  @triggers << trigger
49
49
  @attachments[trigger.id] = attach if attach
50
50
  @trigger_conditions[trigger.id] = conditions if conditions
@@ -26,13 +26,12 @@ module OpenHAB
26
26
  # @param id [String] The ID for the rule.
27
27
  # @yield The execution block for the rule.
28
28
  # @return [void]
29
- # @see Builder#$1
29
+ # @see BuilderDSL#$1
30
30
  def def_terse_rule(trigger)
31
31
  class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
32
32
  def #{trigger}(*args, name: nil, id: nil, **kwargs, &block) # def changed(*args, name: nil, id: nil, **kwargs, &block)
33
33
  raise ArgumentError, "Block is required" unless block # raise ArgumentError, "Block is required" unless block
34
34
  #
35
- id ||= NameInference.infer_rule_id_from_name(name) if name # id ||= NameInference.infer_rule_id_from_name(name) if name
36
35
  id ||= NameInference.infer_rule_id_from_block(block) # id ||= NameInference.infer_rule_id_from_block(block)
37
36
  script = block.source rescue nil # script = block.source rescue nil
38
37
  caller_binding = block.binding # caller_binding = block.binding