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.
- 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
|