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.
- checksums.yaml +4 -4
- data/lib/openhab/core/entity_lookup.rb +1 -57
- data/lib/openhab/dsl/dsl.rb +6 -12
- data/lib/openhab/dsl/group.rb +1 -5
- data/lib/openhab/dsl/items/comparable_item.rb +49 -0
- data/lib/openhab/dsl/items/contact_item.rb +41 -0
- data/lib/openhab/dsl/items/date_time_item.rb +64 -0
- data/lib/openhab/dsl/items/dimmer_item.rb +59 -0
- data/lib/openhab/dsl/items/generic_item.rb +197 -0
- data/lib/openhab/dsl/items/group_item.rb +56 -92
- data/lib/openhab/dsl/items/image_item.rb +5 -41
- data/lib/openhab/dsl/items/item_registry.rb +49 -0
- data/lib/openhab/dsl/items/items.rb +71 -35
- data/lib/openhab/dsl/items/metadata.rb +325 -0
- data/lib/openhab/dsl/items/number_item.rb +6 -312
- data/lib/openhab/dsl/items/numeric_item.rb +66 -0
- data/lib/openhab/dsl/items/persistence.rb +122 -0
- data/lib/openhab/dsl/items/player_item.rb +49 -40
- data/lib/openhab/dsl/items/rollershutter_item.rb +25 -77
- data/lib/openhab/dsl/items/string_item.rb +16 -58
- data/lib/openhab/dsl/items/switch_item.rb +62 -0
- data/lib/openhab/dsl/lazy_array.rb +8 -6
- data/lib/openhab/dsl/monkey_patch/events/events.rb +2 -2
- data/lib/openhab/dsl/monkey_patch/events/item_command.rb +67 -24
- data/lib/openhab/dsl/monkey_patch/events/item_event.rb +5 -5
- data/lib/openhab/dsl/monkey_patch/events/item_state.rb +10 -11
- data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +10 -11
- data/lib/openhab/dsl/monkey_patch/ruby/number.rb +25 -2
- data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +0 -3
- data/lib/openhab/dsl/monkey_patch/ruby/string.rb +24 -24
- data/lib/openhab/dsl/states.rb +1 -1
- data/lib/openhab/dsl/time_of_day.rb +3 -5
- data/lib/openhab/dsl/types/comparable_type.rb +21 -0
- data/lib/openhab/dsl/types/date_time_type.rb +334 -0
- data/lib/openhab/dsl/types/decimal_type.rb +187 -0
- data/lib/openhab/dsl/types/increase_decrease_type.rb +23 -0
- data/lib/openhab/dsl/types/next_previous_type.rb +23 -0
- data/lib/openhab/dsl/types/numeric_type.rb +39 -0
- data/lib/openhab/dsl/types/on_off_type.rb +29 -0
- data/lib/openhab/dsl/types/open_closed_type.rb +29 -0
- data/lib/openhab/dsl/types/percent_type.rb +68 -0
- data/lib/openhab/dsl/types/play_pause_type.rb +27 -0
- data/lib/openhab/dsl/types/quantity_type.rb +275 -0
- data/lib/openhab/dsl/types/refresh_type.rb +18 -0
- data/lib/openhab/dsl/types/rewind_fastforward_type.rb +33 -0
- data/lib/openhab/dsl/types/stop_move_type.rb +23 -0
- data/lib/openhab/dsl/types/string_type.rb +88 -0
- data/lib/openhab/dsl/types/type.rb +72 -0
- data/lib/openhab/dsl/types/types.rb +77 -0
- data/lib/openhab/dsl/types/un_def_type.rb +22 -0
- data/lib/openhab/dsl/types/up_down_type.rb +32 -0
- data/lib/openhab/dsl/units.rb +11 -6
- data/lib/openhab/version.rb +1 -1
- data/lib/openhab.rb +0 -1
- metadata +31 -28
- data/lib/openhab/dsl/items/datetime_item.rb +0 -75
- data/lib/openhab/dsl/items/item_command.rb +0 -90
- data/lib/openhab/dsl/items/item_delegate.rb +0 -125
- data/lib/openhab/dsl/monkey_patch/items/contact_item.rb +0 -51
- data/lib/openhab/dsl/monkey_patch/items/dimmer_item.rb +0 -140
- data/lib/openhab/dsl/monkey_patch/items/items.rb +0 -142
- data/lib/openhab/dsl/monkey_patch/items/metadata.rb +0 -328
- data/lib/openhab/dsl/monkey_patch/items/persistence.rb +0 -123
- data/lib/openhab/dsl/monkey_patch/items/switch_item.rb +0 -71
- data/lib/openhab/dsl/monkey_patch/ruby/range.rb +0 -47
- data/lib/openhab/dsl/monkey_patch/ruby/time.rb +0 -32
- data/lib/openhab/dsl/monkey_patch/types/decimal_type.rb +0 -97
- data/lib/openhab/dsl/monkey_patch/types/increase_decrease_type.rb +0 -23
- data/lib/openhab/dsl/monkey_patch/types/next_previous_type.rb +0 -23
- data/lib/openhab/dsl/monkey_patch/types/on_off_type.rb +0 -79
- data/lib/openhab/dsl/monkey_patch/types/open_closed_type.rb +0 -71
- data/lib/openhab/dsl/monkey_patch/types/percent_type.rb +0 -77
- data/lib/openhab/dsl/monkey_patch/types/play_pause_type.rb +0 -23
- data/lib/openhab/dsl/monkey_patch/types/quantity_type.rb +0 -69
- data/lib/openhab/dsl/monkey_patch/types/refresh_type.rb +0 -23
- data/lib/openhab/dsl/monkey_patch/types/rewind_fastforward_type.rb +0 -23
- data/lib/openhab/dsl/monkey_patch/types/stop_move_type.rb +0 -23
- data/lib/openhab/dsl/monkey_patch/types/types.rb +0 -15
- data/lib/openhab/dsl/monkey_patch/types/up_down_type.rb +0 -72
- data/lib/openhab/dsl/types/datetime.rb +0 -338
- 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
|