openhab-scripting 4.1.4 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
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