openhab-jrubyscripting 5.0.0.rc11 → 5.0.0.rc.13

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions/exec.rb +41 -41
  3. data/lib/openhab/core/actions/transformation.rb +3 -3
  4. data/lib/openhab/core/actions.rb +1 -1
  5. data/lib/openhab/core/events/item_command_event.rb +31 -31
  6. data/lib/openhab/core/events/item_state_changed_event.rb +41 -18
  7. data/lib/openhab/core/events/item_state_event.rb +46 -18
  8. data/lib/openhab/core/items/date_time_item.rb +3 -2
  9. data/lib/openhab/core/items/generic_item.rb +119 -1
  10. data/lib/openhab/core/items/item.rb +63 -9
  11. data/lib/openhab/core/items/metadata/hash.rb +1 -1
  12. data/lib/openhab/core/items/metadata/namespace_hash.rb +10 -2
  13. data/lib/openhab/core/items/metadata/provider.rb +2 -2
  14. data/lib/openhab/core/items/persistence.rb +48 -4
  15. data/lib/openhab/core/items/provider.rb +4 -0
  16. data/lib/openhab/core/items/registry.rb +10 -1
  17. data/lib/openhab/core/items/semantics/enumerable.rb +29 -6
  18. data/lib/openhab/core/items/state_storage.rb +2 -2
  19. data/lib/openhab/core/items.rb +6 -13
  20. data/lib/openhab/core/provider.rb +2 -2
  21. data/lib/openhab/core/proxy.rb +5 -0
  22. data/lib/openhab/core/registry.rb +12 -2
  23. data/lib/openhab/core/rules/provider.rb +0 -15
  24. data/lib/openhab/core/rules.rb +1 -1
  25. data/lib/openhab/core/script_handling.rb +8 -8
  26. data/lib/openhab/core/things/links/provider.rb +38 -0
  27. data/lib/openhab/core/things/provider.rb +4 -0
  28. data/lib/openhab/core/things/registry.rb +4 -0
  29. data/lib/openhab/core/timer.rb +3 -19
  30. data/lib/openhab/core/types/date_time_type.rb +1 -1
  31. data/lib/openhab/core/types/decimal_type.rb +4 -9
  32. data/lib/openhab/core/types/hsb_type.rb +2 -2
  33. data/lib/openhab/core/types/quantity_type.rb +4 -9
  34. data/lib/openhab/core/types/string_type.rb +1 -1
  35. data/lib/openhab/core/types/type.rb +8 -28
  36. data/lib/openhab/core/types.rb +16 -3
  37. data/lib/openhab/core.rb +3 -3
  38. data/lib/openhab/core_ext/java/duration.rb +2 -0
  39. data/lib/openhab/core_ext/java/local_date.rb +15 -7
  40. data/lib/openhab/core_ext/java/local_time.rb +13 -3
  41. data/lib/openhab/core_ext/java/month.rb +1 -1
  42. data/lib/openhab/core_ext/java/month_day.rb +13 -3
  43. data/lib/openhab/core_ext/java/period.rb +1 -1
  44. data/lib/openhab/core_ext/java/temporal_amount.rb +1 -1
  45. data/lib/openhab/core_ext/java/time.rb +5 -1
  46. data/lib/openhab/core_ext/java/zoned_date_time.rb +15 -2
  47. data/lib/openhab/core_ext/ruby/date.rb +2 -2
  48. data/lib/openhab/core_ext/ruby/{class.rb → module.rb} +3 -3
  49. data/lib/openhab/core_ext/ruby/numeric.rb +6 -1
  50. data/lib/openhab/dsl/items/builder.rb +25 -12
  51. data/lib/openhab/dsl/items/ensure.rb +8 -6
  52. data/lib/openhab/dsl/rules/automation_rule.rb +2 -23
  53. data/lib/openhab/dsl/rules/builder.rb +47 -2
  54. data/lib/openhab/dsl/rules/terse.rb +15 -13
  55. data/lib/openhab/dsl/timer_manager.rb +1 -1
  56. data/lib/openhab/dsl/version.rb +1 -1
  57. data/lib/openhab/dsl.rb +38 -11
  58. data/lib/openhab/osgi.rb +1 -3
  59. data/lib/openhab/rspec/helpers.rb +3 -5
  60. data/lib/openhab/rspec/hooks.rb +1 -0
  61. data/lib/openhab/rspec/mocks/timer.rb +33 -0
  62. data/lib/openhab/rspec.rb +9 -0
  63. metadata +23 -9
@@ -110,20 +110,15 @@ module OpenHAB
110
110
  #
111
111
  # Coerce object to a {DecimalType DecimalType}
112
112
  #
113
- # @param [Numeric, Type] other object to coerce to a {DecimalType DecimalType}
114
- #
115
- # If `other` is a {Type}, `self` will instead be coerced
116
- # to that type to accomodate comparison with things such as {OnOffType}.
113
+ # @param [Numeric] other object to coerce to a {DecimalType DecimalType}
117
114
  #
118
115
  # @return [Array<(DecimalType, DecimalType)>, nil]
119
116
  #
120
117
  def coerce(other)
121
118
  logger.trace("Coercing #{self} as a request from #{other.class}")
122
- if other.is_a?(Type)
123
- [other, as(other.class)]
124
- elsif other.respond_to?(:to_d)
125
- [self.class.new(other.to_d), self]
126
- end
119
+ return unless other.respond_to?(:to_d)
120
+
121
+ [self.class.new(other.to_d), self]
127
122
  end
128
123
 
129
124
  #
@@ -142,7 +142,7 @@ module OpenHAB
142
142
  end
143
143
 
144
144
  # Convert to an HTML-style string of 6 hex characters in the default sRGB color model.
145
- # @return [String] +'#xxxxxx'+
145
+ # @return [String] `"#xxxxxx"`
146
146
  def to_hex
147
147
  Kernel.format("#%06x", rgb)
148
148
  end
@@ -174,7 +174,7 @@ module OpenHAB
174
174
 
175
175
  # @!method to_xy
176
176
  # Convert to the xyY values representing this object's color in CIE XY color model
177
- # @return [[PercentType, PercentType]]
177
+ # @return [[PercentType, PercentType, PercentType]]
178
178
  end
179
179
  end
180
180
  end
@@ -157,19 +157,14 @@ module OpenHAB
157
157
  #
158
158
  # Coerce object to a {QuantityType}
159
159
  #
160
- # @param [Numeric, Type] other object to coerce to a {QuantityType}
161
- #
162
- # if `other` is a {Type}, `self` will instead be coerced
163
- # to that type to accomodate comparison with things such as {OnOffType}
160
+ # @param [Numeric] other object to coerce to a {QuantityType}
164
161
  #
165
162
  # @return [Array<(QuantityType, QuantityType)>, nil]
166
163
  def coerce(other)
167
164
  logger.trace("Coercing #{self} as a request from #{other.class}")
168
- if other.is_a?(Type)
169
- [other, as(other.class)]
170
- elsif other.respond_to?(:to_d)
171
- [QuantityType.new(other.to_d.to_java, Units::ONE), self]
172
- end
165
+ return unless other.respond_to?(:to_d)
166
+
167
+ [QuantityType.new(other.to_d.to_java, Units::ONE), self]
173
168
  end
174
169
 
175
170
  # arithmetic operators
@@ -63,7 +63,7 @@ module OpenHAB
63
63
  #
64
64
  def coerce(other)
65
65
  logger.trace("Coercing #{self} as a request from #{other.class}")
66
- return [String.new(other.to_str), self] if other.respond_to?(:to_str)
66
+ return [other.to_str, self] if other.respond_to?(:to_str)
67
67
  end
68
68
 
69
69
  # any method that exists on String gets forwarded to to_s
@@ -25,21 +25,6 @@ module OpenHAB
25
25
  to_s
26
26
  end
27
27
 
28
- #
29
- # Type Coercion
30
- #
31
- # Coerce object to the same Type
32
- #
33
- # @param [Type] other object to coerce to the same
34
- # Type as this one
35
- #
36
- # @return [[Type, Type], nil]
37
- #
38
- def coerce(other)
39
- logger.trace("Coercing #{self} (#{self.class}) as a request from #{other.class}")
40
- return [other.as(self.class), self] if other.is_a?(Type) && other.respond_to?(:as)
41
- end
42
-
43
28
  #
44
29
  # Check equality without type conversion
45
30
  #
@@ -51,19 +36,6 @@ module OpenHAB
51
36
  equals(other)
52
37
  end
53
38
 
54
- #
55
- # Case equality
56
- #
57
- # @return [true,false] if the values are of the same Type
58
- # or item state of the same type
59
- #
60
- def ===(other)
61
- logger.trace { "Type (#{self.class}) #{self} === #{other} (#{other.class})" }
62
- return false unless instance_of?(other.class)
63
-
64
- eql?(other)
65
- end
66
-
67
39
  #
68
40
  # Check equality, including type conversion
69
41
  #
@@ -95,6 +67,14 @@ module OpenHAB
95
67
 
96
68
  super
97
69
  end
70
+
71
+ # @!visibility private
72
+ #
73
+ # some openHAB Types don't implement as; do it for them
74
+ #
75
+ def as(klass)
76
+ self if klass == self.class
77
+ end
98
78
  end
99
79
 
100
80
  #
@@ -57,11 +57,24 @@ module OpenHAB
57
57
  ([command] | states).each do |method|
58
58
  logger.trace("Defining #{klass}##{method} for #{value}")
59
59
  klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
60
- def #{method} # def on?
61
- self == #{value} # self == ON
62
- end # end
60
+ def #{method} # def on?
61
+ as(#{value.class.java_class.simple_name}).equal?(#{value}) # as(OnOffType).equal?(ON)
62
+ end # end
63
63
  RUBY
64
64
  end
65
+
66
+ method = states.last
67
+ Events::ItemState.class_eval <<~RUBY, __FILE__, __LINE__ + 1
68
+ def #{method} # def on?
69
+ item_state.as(#{value.class.java_class.simple_name}).equal?(#{value}) # item_state.as(OnOffType).equal?(ON)
70
+ end # end
71
+ RUBY
72
+
73
+ Events::ItemStateChangedEvent.class_eval <<~RUBY, __FILE__, __LINE__ + 1
74
+ def was_#{method} # def was_on?
75
+ old_item_state.as(#{value.class.java_class.simple_name}).equal?(#{value}) # old_item_state.as(OnOffType).equal?(ON)
76
+ end # end
77
+ RUBY
65
78
  end
66
79
  end
67
80
  end
data/lib/openhab/core.rb CHANGED
@@ -3,12 +3,12 @@
3
3
  module OpenHAB
4
4
  # Contains classes and modules that wrap actual openHAB objects
5
5
  module Core
6
- # The openHAB Version. >= 3.3.0 is required.
6
+ # The openHAB Version. >= 3.4.0 is required.
7
7
  # @return [String]
8
8
  VERSION = org.openhab.core.OpenHAB.version.freeze
9
9
 
10
- unless Gem::Version.new(VERSION) >= Gem::Version.new("3.3.0")
11
- raise "`openhab-jrubyscripting` requires openHAB >= 3.3.0"
10
+ unless Gem::Version.new(VERSION) >= Gem::Version.new("3.4.0")
11
+ raise "`openhab-jrubyscripting` requires openHAB >= 3.4.0"
12
12
  end
13
13
 
14
14
  # @return [Integer] Number of seconds to wait between checks for automation manager
@@ -52,6 +52,8 @@ module OpenHAB
52
52
  return to_f <=> other if other.is_a?(Numeric)
53
53
 
54
54
  super
55
+ rescue TypeError
56
+ nil
55
57
  end
56
58
 
57
59
  #
@@ -7,16 +7,24 @@ module OpenHAB
7
7
  module Java
8
8
  java_import java.time.LocalDate
9
9
 
10
- # Extensions to LocalDate
10
+ # Extensions to {java.time.LocalDate}
11
11
  class LocalDate
12
12
  include Time
13
13
  include Between
14
14
  include Ephemeris
15
15
 
16
- class << self # rubocop:disable Lint/EmptyClass
17
- # @!attribute [r] now
18
- # @return [ZonedDateTime]
19
- end
16
+ # @!scope class
17
+
18
+ # @!attribute [r] now
19
+ # @return [LocalDate]
20
+
21
+ # @!method parse(text, formatter=nil)
22
+ # Converts the given text into a LocalDate.
23
+ # @param [String] text The text to parse
24
+ # @param [java.time.format.DateTimeFormatter] formatter The formatter to use
25
+ # @return [LocalDate]
26
+
27
+ # @!scope instance
20
28
 
21
29
  # @param [TemporalAmount, LocalDate, Numeric] other
22
30
  # If other is a Numeric, it's interpreted as days.
@@ -25,11 +33,11 @@ module OpenHAB
25
33
  def -(other)
26
34
  case other
27
35
  when Date
28
- Period.of_days(day_of_year - other.yday)
36
+ self - other.to_local_date
29
37
  when MonthDay
30
38
  self - other.at_year(year)
31
39
  when LocalDate
32
- Period.of_days(day_of_year - other.day_of_year)
40
+ Period.between(other, self)
33
41
  when Duration
34
42
  minus_days(other.to_days)
35
43
  when Numeric
@@ -7,6 +7,8 @@ module OpenHAB
7
7
  module Java
8
8
  java_import java.time.LocalTime
9
9
 
10
+ #
11
+ # Extensions to {java.time.LocalTime}
10
12
  #
11
13
  # @example
12
14
  # break_time = LocalTime::NOON
@@ -36,15 +38,23 @@ module OpenHAB
36
38
  include Between
37
39
  # @!parse include Time
38
40
 
39
- # @!visibility private
40
41
  class << self
42
+ # @!attribute [r] now
43
+ # @return [LocalTime]
44
+
45
+ # @!visibility private
46
+ alias_method :raw_parse, :parse
47
+
41
48
  #
42
- # Parses strings in the form "h[:mm[:ss]] [am/pm]"
49
+ # Parses strings in the form "h[:mm[:ss]] [am/pm]" when no formatter is given.
43
50
  #
44
51
  # @param [String] string
52
+ # @param [java.time.format.DateTimeFormatter] formatter The formatter to use
45
53
  # @return [LocalTime]
46
54
  #
47
- def parse(string)
55
+ def parse(string, formatter = nil)
56
+ return raw_parse(string, formatter) if formatter
57
+
48
58
  format = /(am|pm)$/i.match?(string) ? "h[:mm[:ss][.S]][ ]a" : "H[:mm[:ss][.S]]"
49
59
  java_send(:parse, [java.lang.CharSequence, java.time.format.DateTimeFormatter],
50
60
  string, java.time.format.DateTimeFormatterBuilder.new
@@ -7,7 +7,7 @@ module OpenHAB
7
7
  module Java
8
8
  Month = java.time.Month
9
9
 
10
- # Extensions to Month
10
+ # Extensions to {java.time.Month}
11
11
  class Month
12
12
  include Between
13
13
  # @!parse include Time
@@ -7,7 +7,7 @@ module OpenHAB
7
7
  module Java
8
8
  java_import java.time.MonthDay
9
9
 
10
- # Extensions to MonthDay
10
+ # Extensions to {java.time.MonthDay}
11
11
  class MonthDay
12
12
  include Between
13
13
  include Ephemeris
@@ -39,12 +39,22 @@ module OpenHAB
39
39
 
40
40
  # @return [MonthDay]
41
41
  def +(other)
42
- (LocalDate.of(1900, month, day_of_month) + other).to_month_day
42
+ case other
43
+ when java.time.temporal.TemporalAmount, Numeric
44
+ (LocalDate.of(1900, month, day_of_month) + other).to_month_day
45
+ else
46
+ (to_local_date(other.to_local_date) + other).to_month_day
47
+ end
43
48
  end
44
49
 
45
50
  # @return [MonthDay, Period]
46
51
  def -(other)
47
- d = (LocalDate.of(1900, month, day_of_month) - other)
52
+ d = case other
53
+ when java.time.temporal.TemporalAmount, Numeric
54
+ LocalDate.of(1900, month, day_of_month) - other
55
+ else
56
+ to_local_date(other.to_local_date) - other
57
+ end
48
58
  return d if d.is_a?(java.time.Period)
49
59
 
50
60
  d.to_month_day
@@ -5,7 +5,7 @@ module OpenHAB
5
5
  module Java
6
6
  java_import java.time.Period
7
7
 
8
- # Extensions to Period
8
+ # Extensions to {java.time.Period}
9
9
  class Period
10
10
  # @!parse include TemporalAmount
11
11
 
@@ -5,7 +5,7 @@ module OpenHAB
5
5
  module Java
6
6
  java_import java.time.temporal.TemporalAmount
7
7
 
8
- # Extensions to TemporalAmount
8
+ # Extensions to {java.time.temporal.TemporalAmount}
9
9
  module TemporalAmount
10
10
  # Subtract `self` to {ZonedDateTime.now}
11
11
  # @return [ZonedDateTime]
@@ -50,7 +50,11 @@ module OpenHAB
50
50
  # Convert `other` to this class, if possible
51
51
  # @return [Array, nil]
52
52
  def coerce(other)
53
- [other.send(self.class.coercion_method), self] if other.respond_to?(self.class.coercion_method)
53
+ coercion_method = self.class.coercion_method
54
+ return unless other.respond_to?(coercion_method)
55
+ return [other.send(coercion_method), self] if other.method(coercion_method).arity.zero?
56
+
57
+ [other.send(coercion_method, self), self]
54
58
  end
55
59
  end
56
60
  end
@@ -7,18 +7,31 @@ module OpenHAB
7
7
  module Java
8
8
  ZonedDateTime = java.time.ZonedDateTime
9
9
 
10
- # Extensions to ZonedDateTime
10
+ # Extensions to {java.time.ZonedDateTime}
11
11
  class ZonedDateTime
12
12
  include Time
13
13
  include Between
14
14
 
15
15
  class << self # rubocop:disable Lint/EmptyClass
16
+ # @!scope class
17
+
16
18
  # @!attribute [r] now
17
19
  # @return [ZonedDateTime]
20
+
21
+ # @!method parse(text, formatter = nil)
22
+ # Parses a string into a ZonedDateTime object.
23
+ #
24
+ # @param [String] text The text to parse.
25
+ # @param [java.time.format.DateTimeFormatter] formatter The formatter to use.
26
+ # @return [ZonedDateTime]
18
27
  end
19
28
 
29
+ # @!scope instance
30
+
20
31
  # @return [LocalTime]
21
- alias_method :to_local_time, :toLocalTime
32
+ def to_local_time(_context = nil)
33
+ toLocalTime
34
+ end
22
35
 
23
36
  # @return [Month]
24
37
  alias_method :to_month, :month
@@ -85,9 +85,9 @@ class Date
85
85
  #
86
86
  def coerce(other)
87
87
  return nil unless other.respond_to?(:to_date)
88
- return [other.to_date(self), self] if other.method(:to_date).arity == 1
88
+ return [other.to_date, self] if other.method(:to_date).arity.zero?
89
89
 
90
- [other.to_date, self]
90
+ [other.to_date(self), self]
91
91
  end
92
92
 
93
93
  remove_method :inspect
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Extensions to Class
4
- class Class
3
+ # Extensions to Module
4
+ class Module
5
5
  #
6
- # Returns the name of the class, without any containing module or package.
6
+ # Returns the name of the class or module, without any containing module or package.
7
7
  #
8
8
  # @return [String, nil]
9
9
  #
@@ -151,7 +151,12 @@ module OpenHAB
151
151
  # @return [QuantityType] `self` as a {QuantityType} of the supplied Unit
152
152
  #
153
153
  def |(unit) # rubocop:disable Naming/BinaryOperatorParameterName
154
- unit = org.openhab.core.types.util.UnitUtils.parse_unit(unit.to_str) if unit.respond_to?(:to_str)
154
+ if unit.respond_to?(:to_str)
155
+ parsed_unit = org.openhab.core.types.util.UnitUtils.parse_unit(unit.to_str)
156
+ raise ArgumentError, "Unknown unit #{unit}" unless parsed_unit
157
+
158
+ unit = parsed_unit
159
+ end
155
160
 
156
161
  return super unless unit.is_a?(javax.measure.Unit)
157
162
 
@@ -187,6 +187,30 @@ module OpenHAB
187
187
  def item_factory
188
188
  @item_factory ||= org.openhab.core.library.CoreItemFactory.new
189
189
  end
190
+
191
+ #
192
+ # Convert the given array to an array of strings.
193
+ # Convert Semantics classes to their simple name.
194
+ #
195
+ # @param [String,Symbol,Semantics::Tag] tags A list of strings, symbols, or Semantics classes
196
+ # @return [Array] An array of strings
197
+ #
198
+ # @example
199
+ # tags = normalize_tags("tag1", Semantics::LivingRoom)
200
+ #
201
+ # @!visibility private
202
+ def normalize_tags(*tags)
203
+ semantics = proc { |tag| tag.respond_to?(:java_class) && tag < Semantics::Tag }
204
+
205
+ tags.compact.map do |tag|
206
+ case tag
207
+ when String then tag
208
+ when Symbol then tag.to_s
209
+ when semantics then tag.java_class.simple_name
210
+ else raise ArgumentError, "`#{tag}` must be a subclass of Semantics::Tag, a `Symbol`, or a `String`."
211
+ end
212
+ end
213
+ end
190
214
  end
191
215
 
192
216
  # @param dimension [Symbol, nil] The unit dimension for a {NumberItem} (see {ItemBuilder#dimension})
@@ -294,18 +318,7 @@ module OpenHAB
294
318
  # @return [void]
295
319
  #
296
320
  def tag(*tags)
297
- unless tags.all? do |tag|
298
- tag.is_a?(String) ||
299
- tag.is_a?(Symbol) ||
300
- (tag.is_a?(Module) && tag < Semantics::Tag)
301
- end
302
- raise ArgumentError, "`tag` must be a subclass of Semantics::Tag, or a `String``."
303
- end
304
-
305
- tags.each do |tag|
306
- tag = tag.name.split("::").last if tag.is_a?(Module) && tag < Semantics::Tag
307
- @tags << tag.to_s
308
- end
321
+ @tags += self.class.normalize_tags(*tags)
309
322
  end
310
323
 
311
324
  #
@@ -38,23 +38,25 @@ module OpenHAB
38
38
  # def command(state)
39
39
  # return super(state) unless Thread.current[:openhab_ensure_states]
40
40
  #
41
+ # formatted_state = format_command(state)
41
42
  # logger.trace do
42
- # "#{name} ensure #{state}, format_command: #{format_command(state)}, current state: #{self.state}"
43
+ # "#{name} ensure #{state}, format_command: #{formatted_state}, current state: #{raw_state}"
43
44
  # end
44
- # return if self.state == format_command(state)
45
+ # return if raw_state == formatted_state
45
46
  #
46
- # super(state)
47
+ # super(formatted_state)
47
48
  # end
48
49
  class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
49
50
  def #{ensured_method}(state)
50
51
  return super(state) unless Thread.current[:openhab_ensure_states]
51
52
 
53
+ formatted_state = format_#{ensured_method}(state)
52
54
  logger.trace do
53
- "\#{name} ensure \#{state}, format_#{ensured_method}: \#{format_#{ensured_method}(state)}, current state: \#{self.state}"
55
+ "\#{name} ensure \#{state}, format_#{ensured_method}: \#{formatted_state}, current state: \#{raw_state}"
54
56
  end
55
- return if self.state == format_#{ensured_method}(state)
57
+ return if raw_state.as(formatted_state.class) == formatted_state
56
58
 
57
- super(state)
59
+ super(formatted_state)
58
60
  end
59
61
  RUBY
60
62
  end
@@ -35,7 +35,8 @@ module OpenHAB
35
35
  super()
36
36
  set_name(config.name)
37
37
  set_description(config.description)
38
- set_tags(to_string_set(config.tags))
38
+ tags = DSL::Items::ItemBuilder.normalize_tags(*config.tags)
39
+ set_tags(tags.to_set)
39
40
  set_triggers(config.triggers)
40
41
  self.uid = config.uid
41
42
  @run_context = config.caller
@@ -297,28 +298,6 @@ module OpenHAB
297
298
  @run_context.instance_exec(&task.block)
298
299
  end
299
300
 
300
- #
301
- # Convert the given array to a set of strings.
302
- # Convert Semantics classes to their simple name
303
- #
304
- # @example
305
- # to_string_set("tag1", Semantics::LivingRoom)
306
- #
307
- # @param tags [Array] An array of strings or Semantics classes
308
- #
309
- # @return [Set] A set of strings
310
- #
311
- def to_string_set(*tags)
312
- tags = tags.flatten.map do |tag|
313
- if tag.respond_to?(:java_class) && tag < org.openhab.core.semantics.Tag
314
- tag.java_class.simple_name
315
- else
316
- tag.to_s
317
- end
318
- end
319
- Set.new(tags)
320
- end
321
-
322
301
  #
323
302
  # Create a new hash in which all elements are converted to strings
324
303
  #
@@ -358,7 +358,7 @@ module OpenHAB
358
358
  #
359
359
  # Set the rule's tags.
360
360
  #
361
- # @param [String, Class, Array<String, Class>] tags
361
+ # @param [String, Symbol, Semantics::Tag] tags A list of tags to assign to the rule.
362
362
  # @return [void]
363
363
  #
364
364
  # @example
@@ -1101,7 +1101,10 @@ module OpenHAB
1101
1101
  # :saturday,
1102
1102
  # :sunday] value
1103
1103
  # When to execute rule.
1104
- # @param [LocalTime, String, nil] at What time of day to execute rule
1104
+ # @param [LocalTime, String, Core::Items::DateTimeItem, nil] at What time of day to execute rule
1105
+ # If `value` is `:day`, `at` can be a {Core::Items::DateTimeItem DateTimeItem}, and
1106
+ # the trigger will run every day at the (time only portion of) current state of the
1107
+ # item. If the item is {NULL} or {UNDEF}, the trigger will not run.
1105
1108
  # @param [Object] attach Object to be attached to the trigger
1106
1109
  # @return [void]
1107
1110
  #
@@ -1157,11 +1160,23 @@ module OpenHAB
1157
1160
  # run { logger.info "Happy Valentine's Day!" }
1158
1161
  # end
1159
1162
  #
1163
+ # @example
1164
+ # rule "Every day at sunset" do
1165
+ # every :day, at: Sunset_Time
1166
+ # run { logger.info "It's getting dark" }
1167
+ # end
1168
+ #
1160
1169
  def every(value, at: nil, attach: nil)
1161
1170
  return every(java.time.MonthDay.parse(value), at: at, attach: attach) if value.is_a?(String)
1162
1171
 
1163
1172
  @ruby_triggers << [:every, value, { at: at }]
1164
1173
 
1174
+ if value == :day && at.is_a?(Item)
1175
+ raise ArgumentError, "Attachments are not supported with dynamic datetime triggers" unless attach.nil?
1176
+
1177
+ return trigger("timer.DateTimeTrigger", itemName: at.name, timeOnly: true)
1178
+ end
1179
+
1165
1180
  cron_expression = case value
1166
1181
  when Symbol then Cron.from_symbol(value, at)
1167
1182
  when Duration then Cron.from_duration(value, at)
@@ -1502,6 +1517,36 @@ module OpenHAB
1502
1517
  trigger("core.GenericEventTrigger", eventTopic: topic, eventSource: source, eventTypes: types, attach: attach)
1503
1518
  end
1504
1519
 
1520
+ #
1521
+ # Creates a trigger based on the time stored in a {DateTimeItem}
1522
+ #
1523
+ # The trigger will dynamically update any time the state of the item
1524
+ # changes. If the item is {NULL} or {UNDEF}, the trigger will not run.
1525
+ #
1526
+ # @param [Item, String, Symbol] item The item (or it's name)
1527
+ # @return [void]
1528
+ #
1529
+ # @example
1530
+ # rule "say hello when the kids get home from school" do
1531
+ # at HomeFromSchool_Time
1532
+ # run do
1533
+ # KitchenEcho_TTS << "hi kids! how was school?"
1534
+ # end
1535
+ # end
1536
+ #
1537
+ # rule "set home from school time" do
1538
+ # on_load
1539
+ # every :day, at: "5:00am" do
1540
+ # run do
1541
+ # HomeFromSchool_Time.ensure.update(school_day? ? LocalTime.parse("3:30pm") : NULL)
1542
+ # end
1543
+ # end
1544
+ #
1545
+ def at(item)
1546
+ item = item.name if item.is_a?(Item)
1547
+ trigger("timer.DateTimeTrigger", itemName: item.to_s)
1548
+ end
1549
+
1505
1550
  #
1506
1551
  # Create a generic trigger given the trigger type uid and a configuration hash
1507
1552
  #