openhab-scripting 4.1.4 → 4.2.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/entity_lookup.rb +1 -57
  3. data/lib/openhab/dsl/dsl.rb +6 -12
  4. data/lib/openhab/dsl/group.rb +1 -5
  5. data/lib/openhab/dsl/items/comparable_item.rb +49 -0
  6. data/lib/openhab/dsl/items/contact_item.rb +41 -0
  7. data/lib/openhab/dsl/items/date_time_item.rb +64 -0
  8. data/lib/openhab/dsl/items/dimmer_item.rb +59 -0
  9. data/lib/openhab/dsl/items/generic_item.rb +197 -0
  10. data/lib/openhab/dsl/items/group_item.rb +56 -92
  11. data/lib/openhab/dsl/items/image_item.rb +5 -41
  12. data/lib/openhab/dsl/items/item_registry.rb +49 -0
  13. data/lib/openhab/dsl/items/items.rb +71 -35
  14. data/lib/openhab/dsl/items/metadata.rb +325 -0
  15. data/lib/openhab/dsl/items/number_item.rb +6 -312
  16. data/lib/openhab/dsl/items/numeric_item.rb +66 -0
  17. data/lib/openhab/dsl/items/persistence.rb +122 -0
  18. data/lib/openhab/dsl/items/player_item.rb +49 -40
  19. data/lib/openhab/dsl/items/rollershutter_item.rb +25 -77
  20. data/lib/openhab/dsl/items/string_item.rb +16 -58
  21. data/lib/openhab/dsl/items/switch_item.rb +62 -0
  22. data/lib/openhab/dsl/lazy_array.rb +8 -6
  23. data/lib/openhab/dsl/monkey_patch/events/events.rb +2 -2
  24. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +67 -24
  25. data/lib/openhab/dsl/monkey_patch/events/item_event.rb +5 -5
  26. data/lib/openhab/dsl/monkey_patch/events/item_state.rb +10 -11
  27. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +10 -11
  28. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +25 -2
  29. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +0 -3
  30. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +24 -24
  31. data/lib/openhab/dsl/states.rb +1 -1
  32. data/lib/openhab/dsl/time_of_day.rb +3 -5
  33. data/lib/openhab/dsl/types/comparable_type.rb +21 -0
  34. data/lib/openhab/dsl/types/date_time_type.rb +334 -0
  35. data/lib/openhab/dsl/types/decimal_type.rb +187 -0
  36. data/lib/openhab/dsl/types/increase_decrease_type.rb +23 -0
  37. data/lib/openhab/dsl/types/next_previous_type.rb +23 -0
  38. data/lib/openhab/dsl/types/numeric_type.rb +39 -0
  39. data/lib/openhab/dsl/types/on_off_type.rb +29 -0
  40. data/lib/openhab/dsl/types/open_closed_type.rb +29 -0
  41. data/lib/openhab/dsl/types/percent_type.rb +68 -0
  42. data/lib/openhab/dsl/types/play_pause_type.rb +27 -0
  43. data/lib/openhab/dsl/types/quantity_type.rb +275 -0
  44. data/lib/openhab/dsl/types/refresh_type.rb +18 -0
  45. data/lib/openhab/dsl/types/rewind_fastforward_type.rb +33 -0
  46. data/lib/openhab/dsl/types/stop_move_type.rb +23 -0
  47. data/lib/openhab/dsl/types/string_type.rb +88 -0
  48. data/lib/openhab/dsl/types/type.rb +72 -0
  49. data/lib/openhab/dsl/types/types.rb +77 -0
  50. data/lib/openhab/dsl/types/un_def_type.rb +22 -0
  51. data/lib/openhab/dsl/types/up_down_type.rb +32 -0
  52. data/lib/openhab/dsl/units.rb +11 -6
  53. data/lib/openhab/version.rb +1 -1
  54. data/lib/openhab.rb +0 -1
  55. metadata +31 -28
  56. data/lib/openhab/dsl/items/datetime_item.rb +0 -75
  57. data/lib/openhab/dsl/items/item_command.rb +0 -90
  58. data/lib/openhab/dsl/items/item_delegate.rb +0 -125
  59. data/lib/openhab/dsl/monkey_patch/items/contact_item.rb +0 -51
  60. data/lib/openhab/dsl/monkey_patch/items/dimmer_item.rb +0 -140
  61. data/lib/openhab/dsl/monkey_patch/items/items.rb +0 -142
  62. data/lib/openhab/dsl/monkey_patch/items/metadata.rb +0 -328
  63. data/lib/openhab/dsl/monkey_patch/items/persistence.rb +0 -123
  64. data/lib/openhab/dsl/monkey_patch/items/switch_item.rb +0 -71
  65. data/lib/openhab/dsl/monkey_patch/ruby/range.rb +0 -47
  66. data/lib/openhab/dsl/monkey_patch/ruby/time.rb +0 -32
  67. data/lib/openhab/dsl/monkey_patch/types/decimal_type.rb +0 -97
  68. data/lib/openhab/dsl/monkey_patch/types/increase_decrease_type.rb +0 -23
  69. data/lib/openhab/dsl/monkey_patch/types/next_previous_type.rb +0 -23
  70. data/lib/openhab/dsl/monkey_patch/types/on_off_type.rb +0 -79
  71. data/lib/openhab/dsl/monkey_patch/types/open_closed_type.rb +0 -71
  72. data/lib/openhab/dsl/monkey_patch/types/percent_type.rb +0 -77
  73. data/lib/openhab/dsl/monkey_patch/types/play_pause_type.rb +0 -23
  74. data/lib/openhab/dsl/monkey_patch/types/quantity_type.rb +0 -69
  75. data/lib/openhab/dsl/monkey_patch/types/refresh_type.rb +0 -23
  76. data/lib/openhab/dsl/monkey_patch/types/rewind_fastforward_type.rb +0 -23
  77. data/lib/openhab/dsl/monkey_patch/types/stop_move_type.rb +0 -23
  78. data/lib/openhab/dsl/monkey_patch/types/types.rb +0 -15
  79. data/lib/openhab/dsl/monkey_patch/types/up_down_type.rb +0 -72
  80. data/lib/openhab/dsl/types/datetime.rb +0 -338
  81. data/lib/openhab/dsl/types/quantity.rb +0 -300
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'time'
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ module Types
9
+ java_import org.openhab.core.library.types.DateTimeType
10
+
11
+ # global alias
12
+ ::DateTimeType = DateTimeType
13
+
14
+ # @deprecated
15
+ # Backwards-compatible alias
16
+ DateTime = DateTimeType
17
+
18
+ # rubocop: disable Metrics/ClassLength class has a single responsibility
19
+
20
+ #
21
+ # Add methods to core OpenHAB DateTimeType to make it behave as a Ruby
22
+ # Time object
23
+ #
24
+ # Any method not explicitly defined is forwarded to the +ZonedDateTime+
25
+ # or +Time+ representation of this object as appropriate.
26
+ #
27
+ class DateTimeType
28
+ # @!parse include Type
29
+
30
+ # remove the JRuby default == so that we can inherit the Ruby method
31
+ remove_method :==
32
+
33
+ extend Forwardable
34
+ include Comparable
35
+
36
+ #
37
+ # Regex expression to identify strings defining a time in hours, minutes and optionally seconds
38
+ #
39
+ TIME_ONLY_REGEX = /\A(?<hours>\d\d):(?<minutes>\d\d)(?<seconds>:\d\d)?\Z/.freeze
40
+
41
+ #
42
+ # Regex expression to identify strings defining a time in year, month, and day
43
+ #
44
+ DATE_ONLY_REGEX = /\A\d{4}-\d\d-\d\d\Z/.freeze
45
+ private_constant :TIME_ONLY_REGEX, :DATE_ONLY_REGEX
46
+
47
+ class << self
48
+ #
49
+ # Parses a String representing a time into an OpenHAB DateTimeType. First tries to parse it
50
+ # using java's DateTimeType's parser, then falls back to the Ruby Time.parse
51
+ #
52
+ # @param [String] time_string
53
+ #
54
+ # @return [DateTimeType]
55
+ #
56
+ def parse(time_string)
57
+ time_string = "#{time_string}Z" if TIME_ONLY_REGEX.match?(time_string)
58
+ DateTimeType.new(time_string)
59
+ rescue java.lang.StringIndexOutOfBoundsException, java.lang.IllegalArgumentException
60
+ # Try ruby's Time.parse if OpenHAB's DateTimeType parser fails
61
+ begin
62
+ DateTimeType.new(Time.parse(time_string))
63
+ rescue ArgumentError
64
+ raise ArgumentError, "Unable to parse #{time_string} into a DateTimeType"
65
+ end
66
+ end
67
+
68
+ # parses a String representing a duration
69
+ #
70
+ # for internal use
71
+ #
72
+ # @return [java.time.Duration]
73
+ #
74
+ # @!visibility private
75
+ def parse_duration(time_string)
76
+ # convert from common HH:MM to ISO8601 for parsing
77
+ if (match = time_string.match(TIME_ONLY_REGEX))
78
+ time_string = "PT#{match[:hours]}H#{match[:minutes]}M#{match[:seconds] || 0}S"
79
+ end
80
+ java.time.Duration.parse(time_string)
81
+ end
82
+ end
83
+
84
+ # act like a ruby Time
85
+ def_delegator :zoned_date_time, :month_value, :month
86
+ def_delegator :zoned_date_time, :day_of_month, :mday
87
+ def_delegator :zoned_date_time, :day_of_year, :yday
88
+ def_delegator :zoned_date_time, :minute, :min
89
+ def_delegator :zoned_date_time, :second, :sec
90
+ def_delegator :zoned_date_time, :nano, :nsec
91
+ def_delegator :zoned_date_time, :to_epoch_second, :to_i
92
+
93
+ alias day mday
94
+
95
+ #
96
+ # Create a new instance of DateTimeType
97
+ #
98
+ # @param value [ZonedDateTime, Time, String, Numeric]
99
+ #
100
+ def initialize(value = nil) # rubocop:disable Metrics
101
+ if value.respond_to?(:to_time)
102
+ time = value.to_time
103
+ instant = java.time.Instant.ofEpochSecond(time.to_i, time.nsec)
104
+ zone_id = java.time.ZoneId.of_offset('UTC', java.time.ZoneOffset.of_total_seconds(time.utc_offset))
105
+ super(ZonedDateTime.ofInstant(instant, zone_id))
106
+ return
107
+ elsif value.respond_to?(:to_str)
108
+ # strings respond_do?(:to_d), but we want to avoid that conversion
109
+ super(value.to_str)
110
+ return
111
+ elsif value.respond_to?(:to_d)
112
+ time = value.to_d
113
+ super(ZonedDateTime.ofInstant(
114
+ java.time.Instant.ofEpochSecond(time.to_i,
115
+ ((time % 1) * 1_000_000_000).to_i),
116
+ java.time.ZoneId.systemDefault
117
+ ))
118
+ return
119
+ end
120
+
121
+ super
122
+ end
123
+
124
+ #
125
+ # Check equality without type conversion
126
+ #
127
+ # @return [Boolean] if the same value is represented, without type
128
+ # conversion
129
+ def eql?(other)
130
+ return false unless other.instance_of?(self.class)
131
+
132
+ zoned_date_time.compare_to(other.zoned_date_time).zero?
133
+ end
134
+
135
+ #
136
+ # Comparison
137
+ #
138
+ # @param [DateTimeType, Items::DateTimeItem, Time,
139
+ # String] other object to compare to
140
+ #
141
+ # @return [Integer, nil] -1, 0, +1 depending on whether +other+ is
142
+ # less than, equal to, or greater than self
143
+ #
144
+ # nil is returned if the two values are incomparable
145
+ #
146
+ def <=>(other) # rubocop:disable Metrics
147
+ logger.trace("(#{self.class}) #{self} <=> #{other} (#{other.class})")
148
+ if other.is_a?(self.class)
149
+ zoned_date_time.to_instant.compare_to(other.zoned_date_time.to_instant)
150
+ elsif other.is_a?(Items::DateTimeItem) ||
151
+ (other.is_a?(Items::GroupItem) && other.base_item.is_a?(Items::DateTimeItem))
152
+ return nil unless other.state?
153
+
154
+ zoned_date_time.to_instant.compare_to(other.state.zoned_date_time.to_instant)
155
+ elsif other.is_a?(TimeOfDay::TimeOfDay) || other.is_a?(TimeOfDay::TimeOfDayRangeElement)
156
+ to_tod <=> other
157
+ elsif other.respond_to?(:to_time)
158
+ to_time <=> other.to_time
159
+ elsif other.respond_to?(:to_str)
160
+ time_string = other.to_str
161
+ time_string = "#{time_string}T00:00:00#{zone}" if DATE_ONLY_REGEX.match?(time_string)
162
+ self <=> DateTimeType.parse(time_string)
163
+ elsif other.respond_to?(:coerce)
164
+ lhs, rhs = other.coerce(self)
165
+ lhs <=> rhs
166
+ end
167
+ end
168
+
169
+ #
170
+ # Type Coercion
171
+ #
172
+ # Coerce object to a DateTimeType
173
+ #
174
+ # @param [Items::DateTimeItem, Time] other object to coerce to a
175
+ # DateTimeType
176
+ #
177
+ # @return [[DateTimeType, DateTimeType]]
178
+ #
179
+ def coerce(other)
180
+ logger.trace("Coercing #{self} as a request from #{other.class}")
181
+ if other.is_a?(Items::DateTimeItem)
182
+ raise TypeError, "can't convert #{UnDefType} into #{self.class}" unless other.state?
183
+
184
+ [other.state, self]
185
+ elsif other.respond_to?(:to_time)
186
+ [DateTimeType.new(other), self]
187
+ else
188
+ raise TypeError, "can't convert #{other.class} into #{self.class}"
189
+ end
190
+ end
191
+
192
+ #
193
+ # Convert this DateTimeType to a ruby Time object
194
+ #
195
+ # @return [Time] A Time object representing the same instant and timezone
196
+ #
197
+ def to_time
198
+ Time.at(to_i, nsec, :nsec).localtime(utc_offset)
199
+ end
200
+
201
+ #
202
+ # Convert the time part of this DateTimeType to a TimeOfDay object
203
+ #
204
+ # @return [TimeOfDay] A TimeOfDay object representing the time
205
+ #
206
+ def to_time_of_day
207
+ TimeOfDay::TimeOfDay.new(h: hour, m: minute, s: second)
208
+ end
209
+
210
+ alias to_tod to_time_of_day
211
+
212
+ #
213
+ # Returns the value of time as a floating point number of seconds since the Epoch
214
+ #
215
+ # @return [Float] Number of seconds since the Epoch, with nanosecond presicion
216
+ #
217
+ def to_f
218
+ zoned_date_time.to_epoch_second + (zoned_date_time.nano / 1_000_000_000)
219
+ end
220
+
221
+ #
222
+ # The offset in seconds from UTC
223
+ #
224
+ # @return [Integer] The offset from UTC, in seconds
225
+ #
226
+ def utc_offset
227
+ zoned_date_time.offset.total_seconds
228
+ end
229
+
230
+ #
231
+ # Returns true if time represents a time in UTC (GMT)
232
+ #
233
+ # @return [Boolean] true if utc_offset == 0, false otherwise
234
+ #
235
+ def utc?
236
+ utc_offset.zero?
237
+ end
238
+
239
+ #
240
+ # Returns an integer representing the day of the week, 0..6, with Sunday == 0.
241
+ #
242
+ # @return [Integer] The day of week
243
+ #
244
+ def wday
245
+ zoned_date_time.day_of_week.value % 7
246
+ end
247
+
248
+ #
249
+ # The timezone
250
+ #
251
+ # @return [String] The timezone in `[+-]hh:mm(:ss)` format (`Z` for UTC)
252
+ #
253
+ def zone
254
+ zoned_date_time.zone.id
255
+ end
256
+
257
+ # @!visibility private
258
+ def respond_to_missing?(method, _include_private = false)
259
+ return true if zoned_date_time.respond_to?(method)
260
+ return true if Time.instance_methods.include?(method.to_sym)
261
+
262
+ super
263
+ end
264
+
265
+ #
266
+ # Forward missing methods to the +ZonedDateTime+ object or a ruby +Time+
267
+ # object representing the same instant
268
+ #
269
+ def method_missing(method, *args, &block)
270
+ return zoned_date_time.send(method, *args, &block) if zoned_date_time.respond_to?(method)
271
+ return to_time.send(method, *args, &block) if Time.instance_methods.include?(method.to_sym)
272
+
273
+ super
274
+ end
275
+
276
+ # Add other to self
277
+ #
278
+ # @param other [java.time.Duration, String, Numeric]
279
+ #
280
+ # @return [DateTimeType]
281
+ def +(other) # rubocop:disable Metrics
282
+ if other.is_a?(java.time.Duration)
283
+ DateTimeType.new(zoned_date_time.plus(other))
284
+ elsif other.respond_to?(:to_str)
285
+ other = self.class.parse_duration(other.to_str)
286
+ self + other
287
+ elsif other.respond_to?(:to_d)
288
+ DateTimeType.new(zoned_date_time.plusNanos((other.to_d * 1_000_000_000).to_i))
289
+ elsif other.respond_to?(:coerce)
290
+ lhs, rhs = other.coerce(to_d)
291
+ lhs + rhs
292
+ else
293
+ raise TypeError, "\#{other.class} can't be coerced into \#{self.class}"
294
+ end
295
+ end
296
+
297
+ # Subtract other from self
298
+ #
299
+ # if other is a Duration-like object, the result is a new
300
+ # {DateTimeType} of duration seconds earlier in time.
301
+ #
302
+ # if other is a DateTime-like object, the result is a Duration
303
+ # representing how long between the two instants in time.
304
+ #
305
+ # @param other [java.time.Duration, Time, String, Numeric]
306
+ #
307
+ # @return [DateTimeType, java.Time.Duration]
308
+ def -(other) # rubocop:disable Metrics
309
+ if other.is_a?(java.time.Duration)
310
+ DateTimeType.new(zoned_date_time.minus(other))
311
+ elsif other.respond_to?(:to_time)
312
+ to_time - other.to_time
313
+ elsif other.respond_to?(:to_str)
314
+ time_string = other.to_str
315
+ other = if TIME_ONLY_REGEX.match?(time_string)
316
+ self.class.parse_duration(time_string)
317
+ else
318
+ DateTimeType.parse(time_string)
319
+ end
320
+ self - other
321
+ elsif other.respond_to?(:to_d)
322
+ DateTimeType.new(zoned_date_time.minusNanos((other.to_d * 1_000_000_000).to_i))
323
+ elsif other.respond_to?(:coerce)
324
+ lhs, rhs = other.coerce(to_d)
325
+ lhs - rhs
326
+ else
327
+ raise TypeError, "\#{other.class} can't be coerced into \#{self.class}"
328
+ end
329
+ end
330
+ end
331
+ # rubocop: enable Metrics/ClassLength
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'comparable_type'
4
+ require_relative 'numeric_type'
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ module Types
9
+ java_import org.openhab.core.library.types.DecimalType
10
+
11
+ #
12
+ # Add methods to core OpenHAB DecimalType to make it behave as a Ruby
13
+ # BigDecimal object
14
+ #
15
+ #
16
+ # Any method not explicitly defined is forwarded to the +BigDecimal+
17
+ # representation of this object.
18
+ #
19
+ class DecimalType
20
+ # @!parse include Type
21
+ include NumericType
22
+ include ComparableType
23
+
24
+ #
25
+ # Create a new instance of DateTimeType
26
+ #
27
+ # @param value [java.math.BigDecimal, Items::NumericItem, Numeric]
28
+ def initialize(*args) # rubocop:disable Metrics
29
+ unless args.length == 1
30
+ super
31
+ return
32
+ end
33
+
34
+ value = args.first
35
+ if value.is_a?(java.math.BigDecimal)
36
+ super
37
+ elsif value.is_a?(BigDecimal)
38
+ super(value.to_java)
39
+ elsif value.is_a?(DecimalType)
40
+ super(value.to_big_decimal)
41
+ elsif value.is_a?(Items::NumericItem) ||
42
+ (value.is_a?(Items::GroupItem) && value.base_item.is_a?(Items::NumericItem))
43
+ super(value.state)
44
+ elsif value.respond_to?(:to_d)
45
+ super(value.to_d.to_java)
46
+ else # rubocop:disable Lint/DuplicateBranch
47
+ # duplicates the Java BigDecimal branch, but that needs to go first
48
+ # in order to avoid unnecessary conversions
49
+ super
50
+ end
51
+ end
52
+
53
+ #
54
+ # Convert DecimalType to a QuantityType
55
+ #
56
+ # @param [Object] other String or Unit representing an OpenHAB Unit
57
+ #
58
+ # @return [QuantityType] +self+ as a {QuantityType} of the supplied Unit
59
+ #
60
+ def |(other)
61
+ other = org.openhab.core.types.util.UnitUtils.parse_unit(other.to_str) if other.respond_to?(:to_str)
62
+ QuantityType.new(to_big_decimal, other)
63
+ end
64
+
65
+ #
66
+ # Comparison
67
+ #
68
+ # @param [NumericType, Items::NumericItem, Numeric]
69
+ # other object to compare to
70
+ #
71
+ # @return [Integer, nil] -1, 0, +1 depending on whether +other+ is
72
+ # less than, equal to, or greater than self
73
+ #
74
+ # nil is returned if the two values are incomparable
75
+ #
76
+ def <=>(other) # rubocop:disable Metrics
77
+ logger.trace("(#{self.class}) #{self} <=> #{other} (#{other.class})")
78
+ if other.is_a?(QuantityType)
79
+ (other <=> self)&.-@
80
+ elsif other.is_a?(self.class)
81
+ compare_to(other)
82
+ elsif other.is_a?(Items::NumericItem) ||
83
+ (other.is_a?(Items::GroupItem) && other.base_item.is_a?(NumericItem))
84
+ return nil unless other.state?
85
+
86
+ self <=> other.state
87
+ elsif other.respond_to?(:to_d)
88
+ to_d <=> other.to_d
89
+ elsif other.respond_to?(:coerce)
90
+ lhs, rhs = other.coerce(self)
91
+ lhs <=> rhs
92
+ end
93
+ end
94
+
95
+ #
96
+ # Type Coercion
97
+ #
98
+ # Coerce object to a DecimalType
99
+ #
100
+ # @param [Items::NumericItem, Numeric, Type] other object to
101
+ # coerce to a {DecimalType}
102
+ #
103
+ # if +other+ is a {Type}, +self+ will instead be coerced
104
+ # to that type to accomodate comparison with things such as {OnOffType}
105
+ #
106
+ # @return [[DecimalType, DecimalType]]
107
+ #
108
+ def coerce(other) # rubocop:disable Metrics
109
+ logger.trace("Coercing #{self} as a request from #{other.class}")
110
+ if other.is_a?(Items::NumericItem) ||
111
+ (other.is_a?(Items::GroupItem) && other.base_item.is_a?(Items::NumericItem))
112
+ raise TypeError, "can't convert #{UnDefType} into #{self.class}" unless other.state?
113
+
114
+ [other.state, self]
115
+ elsif other.is_a?(Type)
116
+ [other, as(other.class)]
117
+ elsif other.respond_to?(:to_d)
118
+ [self.class.new(other.to_d), self]
119
+ else
120
+ raise TypeError, "can't convert #{other.class} into #{self.class}"
121
+ end
122
+ end
123
+
124
+ #
125
+ # Unary minus
126
+ #
127
+ # Negates self
128
+ #
129
+ # @return [DecimalType]
130
+ def -@
131
+ self.class.new(to_big_decimal.negate)
132
+ end
133
+
134
+ {
135
+ :add => :+,
136
+ :subtract => :-,
137
+ :multiply => :*,
138
+ :divide => :/,
139
+ :remainder => :%,
140
+ :pow => :**
141
+ }.each do |java_op, ruby_op|
142
+ class_eval( # rubocop:disable Style/DocumentDynamicEvalDefinition https://github.com/rubocop/rubocop/issues/10179
143
+ # def +(other)
144
+ # if other.is_a?(DecimalType)
145
+ # self.class.new(to_big_decimal.add(other.to_big_decimal))
146
+ # elsif other.is_a?(java.math.BigDecimal)
147
+ # self.class.new(to_big_decimal.add(other))
148
+ # elsif other.respond_to?(:to_d)
149
+ # result = to_d + other
150
+ # # result could already be a QuantityType
151
+ # result = self.class.new(result) unless result.is_a?(NumericType)
152
+ # result
153
+ # elsif other.respond_to?(:coerce)
154
+ # lhs, rhs = other.coerce(to_d)
155
+ # lhs + rhs
156
+ # else
157
+ # raise TypeError, "#{other.class} can't be coerced into #{self.class}"
158
+ # end
159
+ # end
160
+ <<~RUBY, __FILE__, __LINE__ + 1
161
+ def #{ruby_op}(other)
162
+ if other.is_a?(DecimalType)
163
+ self.class.new(to_big_decimal.#{java_op}(other.to_big_decimal))
164
+ elsif other.is_a?(java.math.BigDecimal)
165
+ self.class.new(to_big_decimal.#{java_op}(other))
166
+ elsif other.respond_to?(:to_d)
167
+ result = to_d #{ruby_op} other
168
+ # result could already be a QuantityType
169
+ result = self.class.new(result) unless result.is_a?(NumericType)
170
+ result
171
+ elsif other.respond_to?(:coerce)
172
+ lhs, rhs = other.coerce(to_d)
173
+ lhs #{ruby_op} rhs
174
+ else
175
+ raise TypeError, "\#{other.class} can't be coerced into \#{self.class}"
176
+ end
177
+ end
178
+ RUBY
179
+ )
180
+ end
181
+
182
+ # any method that exists on BigDecimal gets forwarded to to_d
183
+ delegate (BigDecimal.instance_methods - instance_methods) => :to_d
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Types
6
+ java_import org.openhab.core.library.types.IncreaseDecreaseType
7
+
8
+ # Adds methods to core OpenHAB IncreaseDecreaseType to make it more
9
+ # natural in Ruby
10
+ class IncreaseDecreaseType # rubocop:disable Lint/EmptyClass
11
+ # @!parse include Type
12
+
13
+ # @!method increase?
14
+ # Check if == +INCREASE+
15
+ # @return [Boolean]
16
+
17
+ # @!method decrease?
18
+ # Check if == +DECREASE+
19
+ # @return [Boolean]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Types
6
+ java_import org.openhab.core.library.types.NextPreviousType
7
+
8
+ # Adds methods to core OpenHAB NextPreviousType to make it more
9
+ # natural in Ruby
10
+ class NextPreviousType # rubocop:disable Lint/EmptyClass
11
+ # @!parse include Type
12
+
13
+ # @!method next?
14
+ # Check if == +NEXT+
15
+ # @return [Boolean]
16
+
17
+ # @!method previous?
18
+ # Check if == +PREVIOUS+
19
+ # @return [Boolean]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+ require 'forwardable'
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ module Types
9
+ # Mixin for methods common to DecimalType and QuantityType
10
+ module NumericType
11
+ # apply meta-programming to including class
12
+ def self.included(klass)
13
+ klass.extend Forwardable
14
+
15
+ klass.delegate %i[to_d zero?] => :to_big_decimal
16
+ klass.delegate %i[positive? negative? to_f to_i to_int hash] => :to_d
17
+ # remove the JRuby default == so that we can inherit the Ruby method
18
+ klass.remove_method :==
19
+ end
20
+
21
+ #
22
+ # Check equality without type conversion
23
+ #
24
+ # @return [Boolean] if the same value is represented, without type
25
+ # conversion
26
+ def eql?(other)
27
+ return false unless other.instance_of?(self.class)
28
+
29
+ compare_to(other).zero?
30
+ end
31
+
32
+ # Unary plus
33
+ def +@
34
+ self
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Types
6
+ java_import org.openhab.core.library.types.OnOffType
7
+
8
+ # Adds methods to core OpenHAB OnOffType to make it more natural in Ruby
9
+ class OnOffType
10
+ # @!parse include Type
11
+
12
+ # @!method on?
13
+ # Check if == +ON+
14
+ # @return [Boolean]
15
+
16
+ # @!method off?
17
+ # Check if == +OFF+
18
+ # @return [Boolean]
19
+
20
+ # Invert the type
21
+ # @return [OnOffType] +OFF+ if +ON+, +ON+ if +OFF+
22
+ def !
23
+ return OFF if on?
24
+ return ON if off?
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Types
6
+ java_import org.openhab.core.library.types.OpenClosedType
7
+
8
+ # Adds methods to core OpenHAB OpenClosedType to make it more natural in Ruby
9
+ class OpenClosedType
10
+ # @!parse include Type
11
+
12
+ # @!method open?
13
+ # Check if == +OPEN+
14
+ # @return [Boolean]
15
+
16
+ # @!method closed?
17
+ # Check if == +CLOSED+
18
+ # @return [Boolean]
19
+
20
+ # Invert the type
21
+ # @return [OpenClosedType] +OPEN+ if +CLOSED+, +CLOSED+ if +OPEN+
22
+ def !
23
+ return OPEN if open?
24
+ return CLOSED if closed?
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end