openhab-scripting 5.16.0 → 5.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/entity_lookup.rb +5 -5
  3. data/lib/openhab/core/events/item_state_updated_event.rb +11 -11
  4. data/lib/openhab/core/events/item_time_series_updated_event.rb +23 -0
  5. data/lib/openhab/core/events/timer_event.rb +32 -32
  6. data/lib/openhab/core/items/generic_item.rb +12 -0
  7. data/lib/openhab/core/items/numeric_item.rb +1 -1
  8. data/lib/openhab/core/items/persistence.rb +2 -4
  9. data/lib/openhab/core/items/registry.rb +1 -3
  10. data/lib/openhab/core/items/semantics/enumerable.rb +2 -2
  11. data/lib/openhab/core/items/semantics.rb +1 -4
  12. data/lib/openhab/core/profile_factory.rb +16 -1
  13. data/lib/openhab/core/rules/registry.rb +2 -1
  14. data/lib/openhab/core/rules/rule.rb +1 -0
  15. data/lib/openhab/core/sitemaps/provider.rb +3 -1
  16. data/lib/openhab/core/things/channel.rb +1 -1
  17. data/lib/openhab/core/things/profile_callback.rb +5 -0
  18. data/lib/openhab/core/things/thing.rb +6 -0
  19. data/lib/openhab/core/types/string_type.rb +1 -1
  20. data/lib/openhab/core/types/time_series.rb +131 -0
  21. data/lib/openhab/core.rb +14 -1
  22. data/lib/openhab/core_ext/java/instant.rb +14 -0
  23. data/lib/openhab/dsl/items/builder.rb +0 -1
  24. data/lib/openhab/dsl/items/timed_command.rb +1 -0
  25. data/lib/openhab/dsl/rules/builder.rb +140 -39
  26. data/lib/openhab/dsl/rules/name_inference.rb +16 -8
  27. data/lib/openhab/dsl/rules/property.rb +4 -1
  28. data/lib/openhab/dsl/rules/rule_triggers.rb +5 -3
  29. data/lib/openhab/dsl/rules/terse.rb +32 -15
  30. data/lib/openhab/dsl/rules/triggers/changed.rb +15 -7
  31. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +3 -2
  32. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +98 -100
  33. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +2 -2
  34. data/lib/openhab/dsl/sitemaps/builder.rb +1 -1
  35. data/lib/openhab/dsl/version.rb +1 -1
  36. data/lib/openhab/dsl.rb +30 -4
  37. data/lib/openhab/rspec/mocks/persistence_service.rb +10 -3
  38. data/lib/openhab/rspec/mocks/synchronous_executor.rb +2 -1
  39. data/lib/openhab/rspec/mocks/thing_handler.rb +4 -0
  40. metadata +6 -3
@@ -40,7 +40,17 @@ module OpenHAB
40
40
  # The rule must have at least one trigger and one execution block.
41
41
  # To create a "script" without any triggers, use {OpenHAB::DSL.script script}.
42
42
  #
43
+ # When explicit `id` is not provided, the rule's ID will be inferred from the block's source location,
44
+ # and a suffix will be added to avoid clashing against existing rules.
45
+ #
46
+ # When an explicit `id` is provided and an existing rule with the same id already exists,
47
+ # the rule will not be created, and the method will return nil.
48
+ #
49
+ # To ensure that a rule is created even when the same id already exists, use {OpenHAB::DSL.rule!} or call
50
+ # {Core::Rules::Registry#remove rules.remove} to remove any existing rule prior to creating the new rule.
51
+ #
43
52
  # @param [String] name The rule name
53
+ # @param [String] id The rule's ID. This can also be defined in the block using {Rules::BuilderDSL#uid uid}.
44
54
  # @yield Block executed in the context of a {Rules::BuilderDSL}
45
55
  # @yieldparam [Rules::BuilderDSL] rule
46
56
  # Optional parameter to access the rule configuration from within execution blocks and guards.
@@ -48,20 +58,26 @@ module OpenHAB
48
58
  #
49
59
  # @see OpenHAB::DSL::Rules::BuilderDSL Rule BuilderDSL for details on rule triggers, guards and execution blocks
50
60
  # @see Rules::Terse Terse Rules
61
+ # @see DSL.rule!
51
62
  #
52
63
  # @example
53
- # require "openhab/dsl"
54
- #
55
64
  # rule "name" do
56
65
  # <one or more triggers>
57
66
  # <one or more execution blocks>
58
67
  # <zero or more guards>
59
68
  # end
60
69
  #
61
- def rule(name = nil, id: nil, script: nil, binding: nil, &block)
70
+ # @example Create a rule with an explicit id, deleting any existing rule with the same id
71
+ # rule! "name", id: "my_happy_day_reminder" do
72
+ # every :day
73
+ # run { logger.info "Happy new day!" }
74
+ # end
75
+ #
76
+ def rule(name = nil, id: nil, replace: false, script: nil, binding: nil, &block)
62
77
  raise ArgumentError, "Block is required" unless block
63
78
 
64
- id ||= NameInference.infer_rule_id_from_block(block)
79
+ inferred_id = nil
80
+ id ||= inferred_id = NameInference.infer_rule_id_from_block(block)
65
81
  script ||= block.source rescue nil # rubocop:disable Style/RescueModifier
66
82
 
67
83
  builder = nil
@@ -70,6 +86,18 @@ module OpenHAB
70
86
  builder = BuilderDSL.new(binding || block.binding)
71
87
  builder.uid(id)
72
88
  builder.instance_exec(builder, &block)
89
+
90
+ if replace
91
+ logger.debug { "Removing existing rule '#{builder.uid}'." } if DSL.rules.remove(builder.uid)
92
+ else
93
+ id_not_inferred = inferred_id.nil? || inferred_id != builder.uid
94
+ if id_not_inferred && (existing_rule = $rules.get(builder.uid))
95
+ logger.warn "Rule '#{builder.uid}' is not created because " \
96
+ "another rule/script/scene with the same id already exists: #{existing_rule.inspect}."
97
+ return nil
98
+ end
99
+ end
100
+
73
101
  builder.guard = Guard.new(run_context: builder.caller,
74
102
  only_if: builder.only_if,
75
103
  not_if: builder.not_if)
@@ -101,7 +129,7 @@ module OpenHAB
101
129
  # @param [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil] tags
102
130
  # Fluent alias for `tag`
103
131
  # @yield [] Block executed when the script is executed.
104
- # @return [Core::Rules::Rule]
132
+ # @return [Core::Rules::Rule, nil]
105
133
  #
106
134
  # @example A simple script
107
135
  # # return the script object into a variable
@@ -123,15 +151,26 @@ module OpenHAB
123
151
  #
124
152
  # rules.scripts["send_alert"].run(message: "The door is open!")
125
153
  #
154
+ # @see DSL.rule
126
155
  # @see Core::Rules::Rule#trigger
156
+ # @see DSL.script!
127
157
  #
128
- def script(name = nil, description: nil, id: nil, tag: nil, tags: nil, script: nil, &block)
158
+ def script(name = nil, description: nil, id: nil, tag: nil, tags: nil, replace: false, script: nil, &block)
129
159
  raise ArgumentError, "Block is required" unless block
130
160
 
131
- id ||= NameInference.infer_rule_id_from_block(block)
161
+ inferred_id = nil # rubocop:disable Lint/UselessAssignment it is used below
162
+ id ||= inferred_id = NameInference.infer_rule_id_from_block(block)
132
163
  name ||= id
133
164
  script ||= block.source rescue nil # rubocop:disable Style/RescueModifier
134
165
 
166
+ if replace
167
+ logger.debug { "Removing existing rule '#{id}'." } if DSL.rules.remove(id)
168
+ elsif inferred_id.nil? && (existing_rule = $rules.get(id))
169
+ logger.warn "Script '#{id}' is not created because " \
170
+ "another script/scene/rule with the same id already exists: #{existing_rule.inspect}."
171
+ return nil
172
+ end
173
+
135
174
  builder = nil
136
175
  ThreadLocal.thread_local(openhab_rule_type: "script", openhab_rule_uid: id) do
137
176
  builder = BuilderDSL.new(block.binding)
@@ -159,15 +198,27 @@ module OpenHAB
159
198
  # @param [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil] tags
160
199
  # Fluent alias for `tag`
161
200
  # @yield [] Block executed when the script is executed.
162
- # @return [Core::Rules::Rule]
201
+ # @return [Core::Rules::Rule, nil]
163
202
  #
164
- def scene(name = nil, description: nil, id: nil, tag: nil, tags: nil, script: nil, &block)
203
+ # @see DSL.rule
204
+ # @see DSL.scene!
205
+ #
206
+ def scene(name = nil, description: nil, id: nil, tag: nil, tags: nil, replace: false, script: nil, &block)
165
207
  raise ArgumentError, "Block is required" unless block
166
208
 
167
- id ||= NameInference.infer_rule_id_from_block(block)
209
+ inferred_id = nil # rubocop:disable Lint/UselessAssignment
210
+ id ||= inferred_id = NameInference.infer_rule_id_from_block(block)
168
211
  name ||= id
169
212
  script ||= block.source rescue nil # rubocop:disable Style/RescueModifier
170
213
 
214
+ if replace
215
+ logger.debug { "Removing existing rule '#{id}'." } if DSL.rules.remove(id)
216
+ elsif inferred_id.nil? && (existing_rule = $rules.get(id))
217
+ logger.warn "Scene '#{id}' is not created because " \
218
+ "another script/scene/rule with the same id already exists: #{existing_rule.inspect}."
219
+ return nil
220
+ end
221
+
171
222
  builder = nil
172
223
  ThreadLocal.thread_local(openhab_rule_type: "script", openhab_rule_uid: id) do
173
224
  builder = BuilderDSL.new(block.binding)
@@ -400,7 +451,7 @@ module OpenHAB
400
451
  # @param [String] id
401
452
  # @return [void]
402
453
  #
403
- prop :uid
454
+ prop(:uid) { |id| Thread.current[:openhab_rule_uid] = id }
404
455
 
405
456
  #
406
457
  # @!method name(value)
@@ -994,13 +1045,20 @@ module OpenHAB
994
1045
  # @param [Item, GroupItem::Members, Thing] items Objects to create trigger for.
995
1046
  # @param [State, Array<State>, #===, nil] from
996
1047
  # Only execute rule if previous state matches `from` state(s).
997
- # @param [State, Array<State>, #===, nil] to State(s) for
1048
+ # @param [State, Array<State>, #===, nil] to
998
1049
  # Only execute rule if new state matches `to` state(s).
999
- # @param [java.time.temporal.TemporalAmount] for
1000
- # Duration item must remain in the same state before executing the execution blocks.
1001
- # @param [Object] attach object to be attached to the trigger
1050
+ # @param [java.time.temporal.TemporalAmount, Proc, nil] for
1051
+ # Duration for which the item/thing must remain in the same state before executing the execution blocks.
1052
+ # When a proc is provided, it will be called when the rule is triggered to get the duration.
1053
+ # @param [Object, nil] attach object to be attached to the trigger
1002
1054
  # @return [void]
1003
1055
  #
1056
+ # @example Single item trigger
1057
+ # rule "Execute rule when a sensor changed" do
1058
+ # changed FrontMotion_Sensor
1059
+ # run { |event| logger.info("Motion detected by #{event.item.name}") }
1060
+ # end
1061
+ #
1004
1062
  # @example Multiple items can be separated with a comma:
1005
1063
  # rule "Execute rule when either sensor changed" do
1006
1064
  # changed FrontMotion_Sensor, RearMotion_Sensor
@@ -1013,23 +1071,15 @@ module OpenHAB
1013
1071
  # run { |event| logger.info("Motion detected by #{event.item.name}") }
1014
1072
  # end
1015
1073
  #
1016
- # @example `for` parameter can be a proc too:
1017
- # Alarm_Delay << 20
1018
- #
1019
- # rule "Execute rule when item is changed for specified duration" do
1020
- # changed Alarm_Mode, for: -> { Alarm_Delay.state }
1021
- # run { logger.info("Alarm Mode Updated") }
1022
- # end
1023
- #
1024
- # @example You can optionally provide `from` and `to` states to restrict the cases in which the rule executes:
1025
- # rule "Execute rule when item is changed to specific number, from specific number, for specified duration" do
1026
- # changed Alarm_Mode, from: 8, to: [14,12], for: 12.seconds
1074
+ # @example You can optionally provide `from` and/or `to` states to restrict the cases in which the rule executes:
1075
+ # rule "Execute rule when item is changed to specific number, from specific number" do
1076
+ # changed Alarm_Mode, from: 8, to: [14,12]
1027
1077
  # run { logger.info("Alarm Mode Updated") }
1028
1078
  # end
1029
1079
  #
1030
1080
  # @example Works with ranges:
1031
- # rule "Execute when item changed to a range of numbers, from a range of numbers, for specified duration" do
1032
- # changed Alarm_Mode, from: 8..10, to: 12..14, for: 12.seconds
1081
+ # rule "Execute when item changed to a range of numbers, from a range of numbers" do
1082
+ # changed Alarm_Mode, from: 8..10, to: 12..14
1033
1083
  # run { logger.info("Alarm Mode Updated") }
1034
1084
  # end
1035
1085
  #
@@ -1040,14 +1090,14 @@ module OpenHAB
1040
1090
  # end
1041
1091
  #
1042
1092
  # @example Works with procs:
1043
- # rule "Execute when item state is changed from an odd number, to an even number, for specified duration" do
1044
- # changed Alarm_Mode, from: proc { |from| from.odd? }, to: proc {|to| to.even? }, for: 12.seconds
1093
+ # rule "Execute when item state is changed from an odd number, to an even number" do
1094
+ # changed Alarm_Mode, from: proc { |from| from.odd? }, to: proc {|to| to.even? }
1045
1095
  # run { logger.info("Alarm Mode Updated") }
1046
1096
  # end
1047
1097
  #
1048
1098
  # @example Works with lambdas:
1049
- # rule "Execute when item state is changed from an odd number, to an even number, for specified duration" do
1050
- # changed Alarm_Mode, from: -> from { from.odd? }, to: -> to { to.even? }, for: 12.seconds
1099
+ # rule "Execute when item state is changed from an odd number, to an even number" do
1100
+ # changed Alarm_Mode, from: -> from { from.odd? }, to: -> to { to.even? }
1051
1101
  # run { logger.info("Alarm Mode Updated") }
1052
1102
  # end
1053
1103
  #
@@ -1057,6 +1107,22 @@ module OpenHAB
1057
1107
  # run { logger.info("Alarm armed") }
1058
1108
  # end
1059
1109
  #
1110
+ # @example Delay the trigger until the item has been in the same state for 10 seconds
1111
+ # rule "Execute rule when item is changed for specified duration" do
1112
+ # changed Closet_Door, to: CLOSED, for: 10.seconds
1113
+ # run do
1114
+ # Closet_Light.off
1115
+ # end
1116
+ # end
1117
+ #
1118
+ # @example `for` parameter can be a proc that returns a duration:
1119
+ # Alarm_Delay << 20
1120
+ #
1121
+ # rule "Execute rule when item is changed for specified duration" do
1122
+ # changed Alarm_Mode, for: -> { Alarm_Delay.state.to_i.seconds }
1123
+ # run { logger.info("Alarm Mode Updated") }
1124
+ # end
1125
+ #
1060
1126
  # @example Works with Things:
1061
1127
  # rule "Execute rule when thing is changed" do
1062
1128
  # changed things["astro:sun:home"], :from => :online, :to => :uninitialized
@@ -1089,7 +1155,7 @@ module OpenHAB
1089
1155
  raise ArgumentError, "items must be an Item, GroupItem::Members, Thing, or ThingUID"
1090
1156
  end
1091
1157
 
1092
- logger.trace("Creating changed trigger for entity(#{item}), to(#{to.inspect}), from(#{from.inspect})")
1158
+ logger.trace { "Creating changed trigger for entity(#{item}), to(#{to.inspect}), from(#{from.inspect})" }
1093
1159
 
1094
1160
  Array.wrap(from).each do |from_state|
1095
1161
  Array.wrap(to).each do |to_state|
@@ -1381,7 +1447,7 @@ module OpenHAB
1381
1447
  levels.each do |level|
1382
1448
  logger.warn "Rule engine doesn't start until start level 40" if level < 40
1383
1449
  config = { startlevel: level }
1384
- logger.trace("Creating a SystemStartlevelTrigger with startlevel=#{level}")
1450
+ logger.trace { "Creating a SystemStartlevelTrigger with startlevel=#{level}" }
1385
1451
  Triggers::Trigger.new(rule_triggers: @rule_triggers)
1386
1452
  .append_trigger(type: "core.SystemStartlevelTrigger", config: config, attach: attach)
1387
1453
  end
@@ -1480,7 +1546,7 @@ module OpenHAB
1480
1546
  raise ArgumentError, "items must be an Item or GroupItem::Members"
1481
1547
  end
1482
1548
  commands.each do |cmd|
1483
- logger.trace "Creating received command trigger for items #{item.inspect} and commands #{cmd.inspect}"
1549
+ logger.trace { "Creating received command trigger for items #{item.inspect} and commands #{cmd.inspect}" }
1484
1550
 
1485
1551
  command_trigger.trigger(item: item, command: cmd, attach: attach)
1486
1552
  end
@@ -1699,7 +1765,7 @@ module OpenHAB
1699
1765
  # end
1700
1766
  #
1701
1767
  def trigger(type, attach: nil, **configuration)
1702
- logger.trace("Creating trigger (#{type}) with configuration(#{configuration})")
1768
+ logger.trace { "Creating trigger (#{type}) with configuration(#{configuration})" }
1703
1769
  Triggers::Trigger.new(rule_triggers: @rule_triggers)
1704
1770
  .append_trigger(type: type, config: configuration, attach: attach)
1705
1771
  end
@@ -1802,13 +1868,48 @@ module OpenHAB
1802
1868
  raise ArgumentError, "items must be an Item, GroupItem::Members, Thing, or ThingUID"
1803
1869
  end
1804
1870
 
1805
- logger.trace("Creating updated trigger for item(#{item}) to(#{to})")
1871
+ logger.trace { "Creating updated trigger for item(#{item}) to(#{to})" }
1806
1872
  [to].flatten.map do |to_state|
1807
1873
  updated.trigger(item: item, to: to_state, attach: attach)
1808
1874
  end
1809
1875
  end.flatten
1810
1876
  end
1811
1877
 
1878
+ #
1879
+ # Creates a time series updated trigger
1880
+ #
1881
+ # The `event` passed to run blocks will be a {OpenHAB::Core::Events::ItemTimeSeriesUpdatedEvent}
1882
+ #
1883
+ # @param [Item] items Items to create trigger for.
1884
+ # @param [Object] attach Object to be attached to the trigger.
1885
+ # @return [void]
1886
+ #
1887
+ # @since openHAB 4.1
1888
+ # @see Core::Types::TimeSeries TimeSeries
1889
+ # @see Core::Items::GenericItem#time_series= GenericItem#time_series=
1890
+ #
1891
+ # @example
1892
+ # rule 'Execute rule when item time series is updated' do
1893
+ # time_series_updated MyItem
1894
+ # run do |event|
1895
+ # logger.info("Item time series updated: #{event.item.name}.")
1896
+ # logger.info(" TimeSeries size: #{event.time_series.size}, policy: #{event.time_series.policy}")
1897
+ # event.time_series.each do |entry|
1898
+ # timestamp = entry.timestamp.to_time.strftime("%Y-%m-%d %H:%M:%S")
1899
+ # logger.info(" Entry: #{timestamp}: State: #{entry.state}")
1900
+ # end
1901
+ # end
1902
+ # end
1903
+ #
1904
+ def time_series_updated(*items, attach: nil)
1905
+ @ruby_triggers << [:time_series_updated, items]
1906
+ items.map do |item|
1907
+ raise ArgumentError, "items must be an Item or GroupItem::Members" unless item.is_a?(Core::Items::Item)
1908
+
1909
+ event("openhab/items/#{item.name}/timeseriesupdated", types: "ItemTimeSeriesUpdatedEvent", attach: attach)
1910
+ end
1911
+ end
1912
+
1812
1913
  #
1813
1914
  # Create a trigger to watch a path
1814
1915
  #
@@ -1987,7 +2088,7 @@ module OpenHAB
1987
2088
  elsif !execution_blocks?
1988
2089
  logger.warn "Rule '#{uid}' has no execution blocks, not creating rule"
1989
2090
  elsif !enabled
1990
- logger.trace "Rule '#{uid}' marked as disabled, not creating rule."
2091
+ logger.trace { "Rule '#{uid}' marked as disabled, not creating rule." }
1991
2092
  else
1992
2093
  return true
1993
2094
  end
@@ -2024,7 +2125,7 @@ module OpenHAB
2024
2125
  duplicate_index += 1
2025
2126
  rule.uid = "#{base_uid} (#{duplicate_index})"
2026
2127
  end
2027
- logger.trace("Adding rule: #{rule}")
2128
+ logger.trace { "Adding rule: #{rule}" }
2028
2129
  unmanaged_rule = Core.automation_manager.add_unmanaged_rule(rule)
2029
2130
  provider.add(unmanaged_rule)
2030
2131
  unmanaged_rule
@@ -26,8 +26,9 @@ module OpenHAB
26
26
  class << self
27
27
  # get the block's source location, and simplify to a simple filename
28
28
  def infer_rule_id_from_block(block)
29
- file = File.basename(block.source_location.first)
30
- "#{file}:#{block.source_location.last}"
29
+ file = File.basename(block.source_location.first, ".rb")
30
+ file = "script:#{$ctx["ruleUID"]}" if $ctx&.key?("ruleUID") && file == "<script>"
31
+ "#{file}:#{block.source_location.last}".tr(".", "_")
31
32
  end
32
33
 
33
34
  # formulate a readable rule name such as "TestSwitch received command ON" if possible
@@ -47,8 +48,6 @@ module OpenHAB
47
48
  infer_rule_name_from_trigger(*config.ruby_triggers.first)
48
49
  end
49
50
 
50
- private
51
-
52
51
  # formulate a readable rule name from a single trigger if possible
53
52
  def infer_rule_name_from_trigger(trigger, items = nil, kwargs = {})
54
53
  case trigger
@@ -64,11 +63,15 @@ module OpenHAB
64
63
  infer_rule_name_from_item_registry_trigger(trigger)
65
64
  when :thing_added, :thing_removed, :thing_updated
66
65
  infer_rule_name_from_thing_trigger(trigger)
66
+ when :time_series_updated
67
+ infer_rule_name_from_time_series_trigger(items)
67
68
  when :on_start
68
69
  infer_rule_name_from_on_start_trigger(items)
69
70
  end
70
71
  end
71
72
 
73
+ private
74
+
72
75
  # formulate a readable rule name from an item-type trigger
73
76
  def infer_rule_name_from_item_trigger(trigger, items, kwargs)
74
77
  kwargs.delete(:command) if kwargs[:command] == [nil]
@@ -83,10 +86,10 @@ module OpenHAB
83
86
 
84
87
  trigger_name = trigger.to_s.tr("_", " ")
85
88
  item_names = items.map do |item|
86
- if item.is_a?(GroupItem::Members)
87
- "#{item.group.name}.members"
88
- else
89
- item.name
89
+ case item
90
+ when GroupItem::Members then "#{item.group.name}.members"
91
+ when Core::Items::Item then item.name
92
+ else item.to_s
90
93
  end
91
94
  end
92
95
  name = "#{format_array(item_names)} #{trigger_name}"
@@ -136,6 +139,11 @@ module OpenHAB
136
139
  }[trigger]
137
140
  end
138
141
 
142
+ # formulate a readable rule name from a time series updated trigger
143
+ def infer_rule_name_from_time_series_trigger(items)
144
+ "#{format_array(items.map(&:name))} time series updated"
145
+ end
146
+
139
147
  # formulate a readable rule name from an on_start trigger
140
148
  def infer_rule_name_from_on_start_trigger(levels)
141
149
  levels = levels.map { |level| "#{level} (#{start_level_description(level)})" }
@@ -13,8 +13,10 @@ module OpenHAB
13
13
  # and a setter with any number of arguments or a block.
14
14
  #
15
15
  # @param [String] name of the property
16
+ # @yield Block to call when the property is set
17
+ # @yieldparam [Object] value the value being set
16
18
  #
17
- def prop(name)
19
+ def prop(name, &assignment_block)
18
20
  # rubocop rules are disabled because this method is dynamically defined on the calling
19
21
  # object making calls to other methods in this module impossible, or if done on methods
20
22
  # in this module than instance variable belong to the module not the calling class
@@ -30,6 +32,7 @@ module OpenHAB
30
32
  elsif block
31
33
  instance_variable_set(:"@#{name}", block)
32
34
  end
35
+ assignment_block&.call(instance_variable_get(:"@#{name}"))
33
36
  end
34
37
  end
35
38
  end
@@ -41,9 +41,9 @@ module OpenHAB
41
41
  #
42
42
  # @return [org.openhab.core.automation.Trigger] openHAB trigger
43
43
  #
44
- def append_trigger(type:, config:, attach: nil, conditions: nil)
44
+ def append_trigger(type:, config:, attach: nil, conditions: nil, label: nil)
45
45
  config.transform_keys!(&:to_s)
46
- RuleTriggers.trigger(type: type, config: config).tap do |trigger|
46
+ RuleTriggers.trigger(type: type, config: config, label: label).tap do |trigger|
47
47
  logger.trace("Appending trigger (#{trigger.inspect}) attach (#{attach}) conditions(#{conditions})")
48
48
  @triggers << trigger
49
49
  @attachments[trigger.id] = attach if attach
@@ -56,15 +56,17 @@ module OpenHAB
56
56
  #
57
57
  # @param [String] type of trigger
58
58
  # @param [Map] config map
59
+ # @param [String] label for the trigger
59
60
  #
60
61
  # @return [org.openhab.core.automation.Trigger] configured by type and supplied config
61
62
  #
62
- def self.trigger(type:, config:)
63
+ def self.trigger(type:, config:, label: nil)
63
64
  logger.trace("Creating trigger of type '#{type}' config: #{config}")
64
65
  org.openhab.core.automation.util.TriggerBuilder.create
65
66
  .with_id(uuid)
66
67
  .with_type_uid(type)
67
68
  .with_configuration(Core::Configuration.new(config))
69
+ .with_label(label)
68
70
  .build
69
71
  end
70
72
 
@@ -20,29 +20,46 @@ module OpenHAB
20
20
  class << self
21
21
  # @!visibility private
22
22
  # @!macro def_terse_rule
23
- # @!method $1(*args, name :nil, id: nil, on_load: false, **kwargs, &block)
23
+ # @!method $1(
24
+ # *args,
25
+ # id: nil,
26
+ # name :nil,
27
+ # description: nil,
28
+ # tag: nil,
29
+ # tags: nil,
30
+ # on_load: false,
31
+ # **kwargs,
32
+ # &block)
24
33
  # Create a new rule with a $1 trigger.
25
- # @param name [String] The name for the rule.
26
34
  # @param id [String] The ID for the rule.
35
+ # @param name [String] The name for the rule.
36
+ # @param description [String, nil] The description for the rule.
37
+ # @param tag [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil]
38
+ # Tags to assign to the script
39
+ # @param tags [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil]
40
+ # Fluent alias for `tag`
27
41
  # @param on_load [true, false] If the rule should _also_ trigger immediately when the script loads.
28
42
  # @yield The execution block for the rule.
29
43
  # @return [void]
30
44
  # @see BuilderDSL#$1
31
45
  def def_terse_rule(trigger)
32
46
  class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
33
- def #{trigger}(*args, name: nil, id: nil, on_load: false, **kwargs, &block) # def changed(*args, name: nil, id: nil, on_load: false, **kwargs, &block)
34
- raise ArgumentError, "Block is required" unless block # raise ArgumentError, "Block is required" unless block
35
- #
36
- id ||= NameInference.infer_rule_id_from_block(block) # id ||= NameInference.infer_rule_id_from_block(block)
37
- script = block.source rescue nil # script = block.source rescue nil
38
- caller_binding = block.binding # caller_binding = block.binding
39
- rule name, id: id, script: script, binding: caller_binding do # rule name, id: id, script: script, binding: caller_binding do
40
- self.on_load if on_load # self.on_load if on_load
41
- #{trigger}(*args, **kwargs) # changed(*args, **kwargs)
42
- run(&block) # run(&block)
43
- end # end
44
- end # end
45
- module_function #{trigger.inspect} # module_function :changed
47
+ def #{trigger}(*args, id: nil, name: nil, description: nil, # def changed(*args, id: nil, name: nil, description: nil,
48
+ tag: nil, tags: nil, on_load: false, **kwargs, &block) # tag: nil, tags: nil, on_load: false, **kwargs, &block)
49
+ raise ArgumentError, "Block is required" unless block # raise ArgumentError, "Block is required" unless block
50
+ #
51
+ id ||= NameInference.infer_rule_id_from_block(block) # id ||= NameInference.infer_rule_id_from_block(block)
52
+ script = block.source rescue nil # script = block.source rescue nil
53
+ caller_binding = block.binding # caller_binding = block.binding
54
+ rule name, id: id, script: script, binding: caller_binding do # rule name, id: id, script: script, binding: caller_binding do
55
+ self.on_load if on_load # self.on_load if on_load
56
+ self.description(description) if description # self.description(description) if description
57
+ self.tags(*Array.wrap(tag), *Array.wrap(tags)) # self.tags(*Array.wrap(tag), *Array.wrap(tags))
58
+ #{trigger}(*args, **kwargs) # changed(*args, **kwargs)
59
+ run(&block) # run(&block)
60
+ end # end
61
+ end # end
62
+ module_function #{trigger.inspect} # module_function :changed
46
63
  RUBY
47
64
  end
48
65
  end
@@ -19,18 +19,26 @@ module OpenHAB
19
19
  # @param [Core::Items::Item, Core::Items::GroupItem::Members] item item to create trigger for
20
20
  # @param [Core::Types::State, Symbol, #===, nil] from state to restrict trigger to
21
21
  # @param [Core::Types::State, Symbol, #===, nil] to state to restrict trigger to
22
- # @param [Duration, nil] duration duration to delay trigger until to state is met
22
+ # @param [Duration, Proc, nil] duration duration to delay trigger until to state is met
23
23
  # @param [Object] attach object to be attached to the trigger
24
24
  #
25
25
  # @return [org.openhab.core.automation.Trigger] openHAB triggers
26
26
  #
27
27
  def trigger(item:, from:, to:, duration:, attach:)
28
28
  if duration
29
- item_name = item.respond_to?(:name) ? item.name : item.to_s
30
- logger.trace("Creating Changed Wait Change Trigger for Item(#{item_name}) Duration(#{duration}) " \
31
- "To(#{to}) From(#{from}) Attach(#{attach})")
29
+ if logger.trace?
30
+ item_name = item.respond_to?(:name) ? item.name : item.to_s
31
+ logger.trace("Creating Changed Wait Change Trigger for Item(#{item_name}) Duration(#{duration}) " \
32
+ "To(#{to}) From(#{from}) Attach(#{attach})")
33
+ end
32
34
  conditions = Conditions::Duration.new(to: to, from: from, duration: duration)
33
- changed_trigger(item: item, from: nil, to: nil, attach: attach, conditions: conditions)
35
+ label = NameInference.infer_rule_name_from_trigger(:changed,
36
+ [item],
37
+ from: from,
38
+ to: to,
39
+ duration: duration)
40
+
41
+ changed_trigger(item: item, from: nil, to: nil, attach: attach, conditions: conditions, label: label)
34
42
  else
35
43
  # swap from/to w/ nil if from/to need to be processed in Ruby
36
44
  # rubocop:disable Style/ParallelAssignment
@@ -64,7 +72,7 @@ module OpenHAB
64
72
  # @param [Object] attach object to be attached to the trigger
65
73
  # @return [org.openhab.core.automation.Trigger]
66
74
  #
67
- def changed_trigger(item:, from:, to:, attach: nil, conditions: nil)
75
+ def changed_trigger(item:, from:, to:, attach: nil, conditions: nil, label: nil)
68
76
  type, config = case item
69
77
  when GroupItem::Members
70
78
  group(group: item, from: from, to: to)
@@ -74,7 +82,7 @@ module OpenHAB
74
82
  else
75
83
  item(item: item, from: from, to: to)
76
84
  end
77
- append_trigger(type: type, config: config, attach: attach, conditions: conditions)
85
+ append_trigger(type: type, config: config, attach: attach, conditions: conditions, label: label)
78
86
  end
79
87
 
80
88
  #
@@ -12,7 +12,8 @@ module OpenHAB
12
12
  #
13
13
  class Cron < Trigger
14
14
  # Trigger ID for Cron Triggers
15
- CRON_TRIGGER_MODULE_ID = if Gem::Version.new(OpenHAB::Core::VERSION) >= Gem::Version.new("4.0.0")
15
+ # @deprecated OH 3.4
16
+ CRON_TRIGGER_MODULE_ID = if OpenHAB::Core.version >= OpenHAB::Core::V4_0
16
17
  "timer.GenericCronTrigger"
17
18
  else
18
19
  # @deprecated OH3.4 We need to use a custom CronTrigger handler
@@ -211,7 +212,7 @@ module OpenHAB
211
212
  #
212
213
  def trigger(config:, attach:)
213
214
  # @deprecated OH3.4 needs a custom CronTriggerHandlerFactory
214
- if Gem::Version.new(OpenHAB::Core::VERSION) < Gem::Version.new("4.0.0")
215
+ if OpenHAB::Core.version < OpenHAB::Core::V4_0
215
216
  CronHandler::CronTriggerHandlerFactory.instance # ensure it's registered
216
217
  end
217
218
  append_trigger(type: CRON_TRIGGER_MODULE_ID, config: config, attach: attach)