openhab-scripting 5.16.0 → 5.18.0

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 (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)