openhab-scripting 2.16.2 → 2.16.3

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab.rb +12 -16
  3. data/lib/openhab/core/entity_lookup.rb +162 -0
  4. data/lib/openhab/core/openhab_setup.rb +31 -0
  5. data/lib/openhab/core/osgi.rb +61 -0
  6. data/lib/openhab/dsl/actions.rb +105 -0
  7. data/lib/openhab/dsl/dsl.rb +47 -0
  8. data/lib/openhab/{core/dsl → dsl}/gems.rb +0 -1
  9. data/lib/openhab/dsl/group.rb +100 -0
  10. data/lib/openhab/dsl/items/items.rb +46 -0
  11. data/lib/openhab/dsl/items/number_item.rb +352 -0
  12. data/lib/openhab/dsl/items/string_item.rb +120 -0
  13. data/lib/openhab/dsl/monkey_patch/actions/actions.rb +4 -0
  14. data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +32 -0
  15. data/lib/openhab/dsl/monkey_patch/events/events.rb +5 -0
  16. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +23 -0
  17. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +35 -0
  18. data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +33 -0
  19. data/lib/openhab/dsl/monkey_patch/items/contact_item.rb +61 -0
  20. data/lib/openhab/dsl/monkey_patch/items/dimmer_item.rb +193 -0
  21. data/lib/openhab/dsl/monkey_patch/items/group_item.rb +37 -0
  22. data/lib/openhab/dsl/monkey_patch/items/items.rb +133 -0
  23. data/lib/openhab/dsl/monkey_patch/items/metadata.rb +281 -0
  24. data/lib/openhab/dsl/monkey_patch/items/persistence.rb +70 -0
  25. data/lib/openhab/dsl/monkey_patch/items/switch_item.rb +95 -0
  26. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +39 -0
  27. data/lib/openhab/dsl/monkey_patch/ruby/range.rb +47 -0
  28. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +7 -0
  29. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +41 -0
  30. data/lib/openhab/dsl/monkey_patch/types/decimal_type.rb +70 -0
  31. data/lib/openhab/dsl/monkey_patch/types/on_off_type.rb +51 -0
  32. data/lib/openhab/dsl/monkey_patch/types/open_closed_type.rb +36 -0
  33. data/lib/openhab/dsl/monkey_patch/types/percent_type.rb +32 -0
  34. data/lib/openhab/dsl/monkey_patch/types/quantity_type.rb +69 -0
  35. data/lib/openhab/dsl/monkey_patch/types/types.rb +8 -0
  36. data/lib/openhab/dsl/persistence.rb +25 -0
  37. data/lib/openhab/dsl/rules/automation_rule.rb +342 -0
  38. data/lib/openhab/dsl/rules/guard.rb +134 -0
  39. data/lib/openhab/dsl/rules/property.rb +102 -0
  40. data/lib/openhab/dsl/rules/rule.rb +116 -0
  41. data/lib/openhab/dsl/rules/rule_config.rb +151 -0
  42. data/lib/openhab/dsl/rules/triggers/changed.rb +143 -0
  43. data/lib/openhab/dsl/rules/triggers/channel.rb +53 -0
  44. data/lib/openhab/dsl/rules/triggers/command.rb +104 -0
  45. data/lib/openhab/dsl/rules/triggers/cron.rb +177 -0
  46. data/lib/openhab/dsl/rules/triggers/trigger.rb +124 -0
  47. data/lib/openhab/dsl/rules/triggers/updated.rb +98 -0
  48. data/lib/openhab/dsl/states.rb +61 -0
  49. data/lib/openhab/dsl/things.rb +91 -0
  50. data/lib/openhab/dsl/time_of_day.rb +228 -0
  51. data/lib/openhab/dsl/timers.rb +77 -0
  52. data/lib/openhab/dsl/types/quantity.rb +290 -0
  53. data/lib/openhab/dsl/units.rb +39 -0
  54. data/lib/openhab/log/configuration.rb +21 -0
  55. data/lib/openhab/log/logger.rb +172 -0
  56. data/lib/openhab/version.rb +1 -1
  57. metadata +55 -58
  58. data/lib/openhab/configuration.rb +0 -16
  59. data/lib/openhab/core/cron.rb +0 -27
  60. data/lib/openhab/core/debug.rb +0 -34
  61. data/lib/openhab/core/dsl.rb +0 -51
  62. data/lib/openhab/core/dsl/actions.rb +0 -107
  63. data/lib/openhab/core/dsl/entities.rb +0 -147
  64. data/lib/openhab/core/dsl/group.rb +0 -102
  65. data/lib/openhab/core/dsl/items/items.rb +0 -51
  66. data/lib/openhab/core/dsl/items/number_item.rb +0 -323
  67. data/lib/openhab/core/dsl/items/string_item.rb +0 -122
  68. data/lib/openhab/core/dsl/monkey_patch/actions/actions.rb +0 -4
  69. data/lib/openhab/core/dsl/monkey_patch/actions/script_thing_actions.rb +0 -22
  70. data/lib/openhab/core/dsl/monkey_patch/events.rb +0 -5
  71. data/lib/openhab/core/dsl/monkey_patch/events/item_command.rb +0 -13
  72. data/lib/openhab/core/dsl/monkey_patch/events/item_state_changed.rb +0 -25
  73. data/lib/openhab/core/dsl/monkey_patch/events/thing_status_info.rb +0 -26
  74. data/lib/openhab/core/dsl/monkey_patch/items/contact_item.rb +0 -54
  75. data/lib/openhab/core/dsl/monkey_patch/items/dimmer_item.rb +0 -182
  76. data/lib/openhab/core/dsl/monkey_patch/items/group_item.rb +0 -27
  77. data/lib/openhab/core/dsl/monkey_patch/items/items.rb +0 -132
  78. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +0 -283
  79. data/lib/openhab/core/dsl/monkey_patch/items/persistence.rb +0 -72
  80. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +0 -87
  81. data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +0 -41
  82. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +0 -47
  83. data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +0 -7
  84. data/lib/openhab/core/dsl/monkey_patch/ruby/string.rb +0 -43
  85. data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +0 -60
  86. data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +0 -41
  87. data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +0 -25
  88. data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +0 -23
  89. data/lib/openhab/core/dsl/monkey_patch/types/quantity_type.rb +0 -58
  90. data/lib/openhab/core/dsl/monkey_patch/types/types.rb +0 -8
  91. data/lib/openhab/core/dsl/persistence.rb +0 -27
  92. data/lib/openhab/core/dsl/property.rb +0 -96
  93. data/lib/openhab/core/dsl/rule/automation_rule.rb +0 -345
  94. data/lib/openhab/core/dsl/rule/guard.rb +0 -136
  95. data/lib/openhab/core/dsl/rule/rule.rb +0 -117
  96. data/lib/openhab/core/dsl/rule/rule_config.rb +0 -153
  97. data/lib/openhab/core/dsl/rule/triggers/changed.rb +0 -145
  98. data/lib/openhab/core/dsl/rule/triggers/channel.rb +0 -55
  99. data/lib/openhab/core/dsl/rule/triggers/command.rb +0 -106
  100. data/lib/openhab/core/dsl/rule/triggers/cron.rb +0 -160
  101. data/lib/openhab/core/dsl/rule/triggers/trigger.rb +0 -126
  102. data/lib/openhab/core/dsl/rule/triggers/updated.rb +0 -100
  103. data/lib/openhab/core/dsl/states.rb +0 -63
  104. data/lib/openhab/core/dsl/things.rb +0 -93
  105. data/lib/openhab/core/dsl/time_of_day.rb +0 -231
  106. data/lib/openhab/core/dsl/timers.rb +0 -79
  107. data/lib/openhab/core/dsl/types/quantity.rb +0 -292
  108. data/lib/openhab/core/dsl/units.rb +0 -41
  109. data/lib/openhab/core/log.rb +0 -170
  110. data/lib/openhab/core/patch_load_path.rb +0 -7
  111. data/lib/openhab/core/startup_delay.rb +0 -23
  112. data/lib/openhab/osgi.rb +0 -59
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+ require 'delegate'
5
+ require 'forwardable'
6
+
7
+ module OpenHAB
8
+ module DSL
9
+ #
10
+ # Provides access to and ruby wrappers around OpenHAB timers
11
+ #
12
+ module Timers
13
+ java_import org.openhab.core.model.script.actions.ScriptExecution
14
+ java_import java.time.ZonedDateTime
15
+
16
+ # Ruby wrapper for OpenHAB Timer
17
+ # This class implements delegator to delegate methods to the OpenHAB timer
18
+ #
19
+ # @author Brian O'Connell
20
+ # @since 2.0.0
21
+ class Timer < SimpleDelegator
22
+ extend Forwardable
23
+
24
+ def_delegator :@timer, :is_active, :active?
25
+ def_delegator :@timer, :is_running, :running?
26
+ def_delegator :@timer, :has_terminated, :terminated?
27
+
28
+ #
29
+ # Create a new Timer Object
30
+ #
31
+ # @param [Duration] duration Duration until timer should fire
32
+ # @param [Block] block Block to execute when timer fires
33
+ #
34
+ def initialize(duration:, &block)
35
+ @duration = duration
36
+
37
+ # A semaphore is used to prevent a race condition in which calling the block from the timer thread
38
+ # occurs before the @timer variable can be set resulting in @timer being nil
39
+ semaphore = Mutex.new
40
+
41
+ timer_block = proc { semaphore.synchronize { block.call(self) } }
42
+
43
+ semaphore.synchronize do
44
+ @timer = ScriptExecution.createTimer(
45
+ ZonedDateTime.now.plus(@duration), timer_block
46
+ )
47
+ super(@timer)
48
+ end
49
+ end
50
+
51
+ #
52
+ # Reschedule timer
53
+ #
54
+ # @param [Duration] duration
55
+ #
56
+ # @return [<Type>] <description>
57
+ #
58
+ def reschedule(duration = nil)
59
+ duration ||= @duration
60
+ @timer.reschedule(ZonedDateTime.now.plus(duration))
61
+ end
62
+ end
63
+
64
+ #
65
+ # Execute the supplied block after the specified duration
66
+ #
67
+ # @param [Duration] duration after which to execute the block
68
+ # @param [Block] block to execute, block is passed a Timer object
69
+ #
70
+ # @return [Timer] Timer object
71
+ #
72
+ def after(duration, &block)
73
+ Timer.new(duration: duration, &block)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,290 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+ require 'forwardable'
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ #
9
+ # Ruby implementation of OpenHAB Types
10
+ #
11
+ module Types
12
+ #
13
+ # Ruby implementation for OpenHAB quantities
14
+ #
15
+ # rubocop: disable Metrics/ClassLength
16
+ # Disabled because this class has a single responsibility, there does not appear a logical
17
+ # way of breaking it up into multiple classes
18
+ class Quantity < Numeric
19
+ extend Forwardable
20
+ include OpenHAB::Log
21
+
22
+ def_delegator :@quantity, :to_s
23
+
24
+ java_import org.openhab.core.library.types.QuantityType
25
+ java_import 'tec.uom.se.format.SimpleUnitFormat'
26
+ java_import 'tec.uom.se.AbstractUnit'
27
+
28
+ # @return [Hash] Mapping of operation symbols to BigDecimal methods
29
+ OPERATIONS = {
30
+ '+' => 'add',
31
+ '-' => 'subtract',
32
+ '*' => 'multiply',
33
+ '/' => 'divide'
34
+ }.freeze
35
+
36
+ private_constant :OPERATIONS
37
+
38
+ attr_reader :quantity
39
+
40
+ #
41
+ # Create a new Quantity
42
+ #
43
+ # @param [object] quantity String,QuantityType or Numeric to be this quantity
44
+ #
45
+ # Cop disabled, case statement is compact and idiomatic
46
+ def initialize(quantity)
47
+ @quantity = case quantity
48
+ when String then QuantityType.new(quantity)
49
+ when QuantityType then quantity
50
+ when NumberItem, Numeric then QuantityType.new(quantity.to_d.to_java, AbstractUnit::ONE)
51
+ else raise ArgumentError, "Unexpected type #{quantity.class} provided to Quantity initializer"
52
+ end
53
+ super()
54
+ end
55
+
56
+ #
57
+ # Convert this quantity into a another unit
58
+ #
59
+ # @param [Object] other String or Unit to convert to
60
+ #
61
+ # @return [Quantity] This quantity converted to another unit
62
+ #
63
+ def |(other)
64
+ other = SimpleUnitFormat.instance.unitFor(other) if other.is_a? String
65
+
66
+ Quantity.new(quantity.to_unit(other))
67
+ end
68
+
69
+ #
70
+ # Compare this quantity
71
+ #
72
+ # @param [Object] other object to compare to
73
+ #
74
+ # @return [Integer] -1,0,1 if this object is less than, equal to, or greater than the supplied object,
75
+ # nil if it cannot be compared
76
+ #
77
+ def <=>(other)
78
+ logger.trace("Comparing #{self} to #{other}")
79
+ my_qt, other_qt = unitize(*to_qt(coerce(other).reverse))
80
+ my_qt.compare_to(other_qt)
81
+ end
82
+
83
+ #
84
+ # Coerce other object into a Quantity
85
+ #
86
+ # @param [Object] other object to convert to Quantity
87
+ #
88
+ # @return [Array] of self and other object as Quantity types, nil if object cannot be coerced
89
+ #
90
+ def coerce(other)
91
+ logger.trace("Coercing #{self} as a request from #{other.class}")
92
+ case other
93
+ when Quantity then [other.quantity, quantity]
94
+ when QuantityType then [other, quantity]
95
+ when NumberItem then [other.to_qt.quantity, quantity]
96
+ when Numeric, String then [Quantity.new(other), self]
97
+ end
98
+ end
99
+
100
+ #
101
+ # Forward missing methods to Openhab Quantity Item if they are defined
102
+ #
103
+ # @param [String] meth name of method invoked
104
+ # @param [Array] args arguments to invoked method
105
+ # @param [Proc] block block passed ot method
106
+ #
107
+ # @return [Object] result of delegation
108
+ #
109
+ def method_missing(meth, *args, &block)
110
+ logger.trace("Method missing, performing dynamic lookup for: #{meth}")
111
+ if quantity.respond_to?(meth)
112
+ quantity.__send__(meth, *args, &block)
113
+ elsif ::Kernel.method_defined?(meth) || ::Kernel.private_method_defined?(meth)
114
+ ::Kernel.instance_method(meth).bind_call(self, *args, &block)
115
+ else
116
+ super(meth, *args, &block)
117
+ end
118
+ end
119
+
120
+ #
121
+ # Checks if this method responds to the missing method
122
+ #
123
+ # @param [String] method_name Name of the method to check
124
+ # @param [Boolean] _include_private boolean if private methods should be checked
125
+ #
126
+ # @return [Boolean] true if this object will respond to the supplied method, false otherwise
127
+ #
128
+ def respond_to_missing?(method_name, _include_private = false)
129
+ quantity.respond_to?(method_name) ||
130
+ ::Kernel.method_defined?(method_name) ||
131
+ ::Kernel.private_method_defined?(method_name)
132
+ end
133
+
134
+ #
135
+ # Negate the quantity
136
+ #
137
+ # @return [Quantity] This quantity negated
138
+ #
139
+ def -@
140
+ Quantity.new(quantity.negate)
141
+ end
142
+
143
+ OPERATIONS.each do |operation, method|
144
+ define_method(operation) do |other|
145
+ logger.trace("Executing math operation '#{operation}' on quantity #{inspect} "\
146
+ "with other type #{other.class} and value #{other.inspect}")
147
+
148
+ a, b = to_qt(coerce(other).reverse)
149
+ logger.trace("Coerced a='#{a}' with b='#{b}'")
150
+ a, b = unitize(a, b, operation)
151
+ logger.trace("Unitized a='#{a}' b='#{b}'")
152
+ logger.trace("Performing operation '#{operation}' with method '#{method}' on a='#{a}' with b='#{b}'")
153
+ Quantity.new(a.public_send(method, b))
154
+ end
155
+ end
156
+
157
+ #
158
+ # Provide details about quantity object
159
+ #
160
+ # @return [String] Representing details about the quantity object
161
+ #
162
+ def inspect
163
+ if @quantity.unit == AbstractUnit::ONE
164
+ "unit=#{@quantity.unit}, value=#{@quantity.to_string}"
165
+ else
166
+ @quantity.to_string
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ # @return [Array] Array of strings for operations for which the operands will not be unitized
173
+ DIMENSIONLESS_NON_UNITIZED_OPERATIONS = %w[* /].freeze
174
+
175
+ # Dimensionless numbers should only be unitzed for addition and subtraction
176
+
177
+ #
178
+ # Convert one or more Quantity obects to the underlying quantitytypes
179
+ #
180
+ # @param [Array] quanities Array of either Quantity or QuantityType objects
181
+ #
182
+ # @return [Array] Array of QuantityType objects
183
+ #
184
+ def to_qt(*quanities)
185
+ [quanities].flatten.compact.map { |item| item.is_a?(Quantity) ? item.quantity : item }
186
+ end
187
+
188
+ #
189
+ # Checks if an item should be unitized
190
+ #
191
+ # @param [Quantity] quantity to check
192
+ # @param [String] operation quantity is being used with
193
+ #
194
+ # @return [Boolean] True if the quantity should be unitzed based on the unit and operation, false otherwise
195
+ #
196
+ def unitize?(quantity, operation)
197
+ !(quantity.unit == AbstractUnit::ONE && DIMENSIONLESS_NON_UNITIZED_OPERATIONS.include?(operation))
198
+ end
199
+
200
+ #
201
+ # Convert the unit for the quantity
202
+ #
203
+ # @param [Quantity] quantity being converted
204
+ #
205
+ # @return [Quantity] Quantity coverted to unit set by unit block
206
+ #
207
+ def convert_unit(quantity)
208
+ return quantity unless unit?
209
+
210
+ case quantity.unit
211
+ when unit
212
+ quantity
213
+ when AbstractUnit::ONE
214
+ convert_unit_from_dimensionless(quantity, unit)
215
+ else
216
+ convert_unit_from_dimensioned(quantity, unit)
217
+ end
218
+ end
219
+
220
+ #
221
+ # Converts a dimensioned quantity to a specific unit
222
+ #
223
+ # @param [Quantity] quantity to convert
224
+ # @param [Unit] unit to convert to
225
+ #
226
+ # @return [Java::org::openhab::core::library::types::QuantityType] converted quantity
227
+ #
228
+ def convert_unit_from_dimensioned(quantity, unit)
229
+ logger.trace("Converting dimensioned item #{inspect} to #{unit}")
230
+ quantity.to_unit(unit).tap do |converted|
231
+ raise "Conversion from #{quantity.unit} to #{unit} failed" unless converted
232
+ end
233
+ end
234
+
235
+ #
236
+ # Converts a dimensionless quantity to a unit
237
+ #
238
+ # @param [Quantity] quantity to convert
239
+ # @param [Unit] unit to convert to
240
+ #
241
+ # @return [Java::org::openhab::core::library::types::QuantityType] converted quantity
242
+ #
243
+ def convert_unit_from_dimensionless(quantity, unit)
244
+ logger.trace("Converting dimensionless #{quantity} to #{unit}")
245
+ QuantityType.new(quantity.to_big_decimal, unit)
246
+ end
247
+
248
+ #
249
+ # Convert quantities to appropriate units
250
+ #
251
+ # @param [Quantity] quantity_a Quantity on left side of operation
252
+ # @param [Quantity] quantity_b Quantity on right side of operation
253
+ # @param [String] operation Math operation
254
+ # @yield [quantity_a, quantity_b] yields unitized versions of supplied quantities
255
+ #
256
+ # @return [Array, Object] of quantites in correct units for the supplied operation and the unit
257
+ # or the result of the block if a block is given
258
+ #
259
+ def unitize(quantity_a, quantity_b, operation = nil)
260
+ logger.trace("Unitizing (#{quantity_a}) and (#{quantity_b})")
261
+ quantity_a, quantity_b = [quantity_a, quantity_b].map do |qt|
262
+ unitize?(qt, operation) ? convert_unit(qt) : qt
263
+ end
264
+ return yield quantity_a, quantity_b if block_given?
265
+
266
+ [quantity_a, quantity_b]
267
+ end
268
+
269
+ #
270
+ # Get the unit from the current thread local variable
271
+ #
272
+ # @return [Object] Unit or string representation of Unit, or nil if not set
273
+ #
274
+ def unit
275
+ Thread.current.thread_variable_get(:unit)
276
+ end
277
+
278
+ #
279
+ # Is a unit set for this thread
280
+ #
281
+ # @return [boolean] true if a unit is set by this thread, false otherwise
282
+ #
283
+ def unit?
284
+ unit != nil
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+ # rubocop: enable Metrics/ClassLength
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+
5
+ # Import Imperial and SI Units overriding provided values
6
+ %i[ImperialUnits SIUnits].each do |type|
7
+ Object.send(:remove_const, type)
8
+ java_import "org.openhab.core.library.unit.#{type}"
9
+ end
10
+
11
+ Object.send(:remove_const, :QuantityType)
12
+ java_import org.openhab.core.library.types.QuantityType
13
+
14
+ module OpenHAB
15
+ module DSL
16
+ #
17
+ # Provides support for interacting with OpenHAB Units of Measurement
18
+ #
19
+ module Units
20
+ java_import 'tec.uom.se.format.SimpleUnitFormat'
21
+
22
+ #
23
+ # Sets a thread local variable to the supplied unit such that classes operating inside the block
24
+ # can perform automatic conversions to the supplied unit for NumberItems
25
+ #
26
+ # @param [Object] unit OpenHAB Unit or String representing unit
27
+ # @yield [] Block executed in context of the supplied unit
28
+ #
29
+ #
30
+ def unit(unit)
31
+ unit = SimpleUnitFormat.instance.unitFor(unit) if unit.is_a? String
32
+ Thread.current.thread_variable_set(:unit, unit)
33
+ yield
34
+ ensure
35
+ Thread.current.thread_variable_set(:unit, nil)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ # Supports Logging
5
+ module Log
6
+ # This module holds global configuration values
7
+ module Configuration
8
+ # -*- coding: utf-8 -*-
9
+ LOG_PREFIX = 'jsr223.jruby'
10
+
11
+ #
12
+ # Gets the log prefix
13
+ #
14
+ # @return [String] Prefix for all log entries
15
+ #
16
+ def self.log_prefix
17
+ LOG_PREFIX
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openhab/log/configuration'
4
+ require 'java'
5
+ require 'pp'
6
+
7
+ module OpenHAB
8
+ #
9
+ # Provides access to the OpenHAB logging using a Ruby logging methods
10
+ #
11
+ module Log
12
+ #
13
+ # Ruby Logger that forwards messages at appropriate levels to OpenHAB Logger
14
+ #
15
+ class Logger
16
+ java_import org.slf4j.LoggerFactory
17
+
18
+ # @return [Array] Supported logging levels
19
+ LEVELS = %i[TRACE DEBUG WARN INFO ERROR].freeze
20
+
21
+ #
22
+ # Create a new logger
23
+ #
24
+ # @param [String] name of the logger
25
+ #
26
+ def initialize(name)
27
+ @sl4fj_logger = LoggerFactory.getLogger(name)
28
+ end
29
+
30
+ # Dynamically define the methods for each level as identified by the levels constant
31
+ # This creates a method for each level that looks like this
32
+ # def <level>(msg=nil, &block)
33
+ # log(severity: <level>, msg: msg, &block)
34
+ # end
35
+ LEVELS.each do |level|
36
+ method = level.to_s.downcase
37
+ define_method(method.to_s) do |msg = nil, &block|
38
+ log(severity: level, msg: msg, &block)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ #
45
+ # Log a message to the OpenHAB Logger
46
+ #
47
+ # @param [Symbol] severity Severity to log message at
48
+ # @param [Object] msg to log, if no msg supplied and a block is provided,
49
+ # the msg is taken from the result of the block
50
+ #
51
+ def log(severity:, msg: nil)
52
+ severity = severity.to_sym
53
+
54
+ raise ArgumentError, "Unknown Severity #{severity}" unless LEVELS.include? severity
55
+
56
+ # Dynamically check enablement of underlying logger, this expands to "is_<level>_enabled"
57
+ return unless @sl4fj_logger.send("is_#{severity.to_s.downcase}_enabled")
58
+
59
+ # Process block if no message provided
60
+ msg = yield if msg.nil? && block_given?
61
+
62
+ msg = message_to_string(msg: msg)
63
+
64
+ # Dynamically invoke underlying logger, this expands to "<level>(message)"
65
+ @sl4fj_logger.send(severity.to_s.downcase, msg)
66
+ end
67
+
68
+ #
69
+ # Conver the supplied message object to a String
70
+ #
71
+ # @param [object] msg object to convert
72
+ #
73
+ # @return [String] Msg object as a string
74
+ #
75
+ def message_to_string(msg:)
76
+ case msg
77
+ when ::String
78
+ msg
79
+ when ::Exception
80
+ "#{msg.message} (#{msg.class})\n#{msg.backtrace&.join("\n")}"
81
+ else
82
+ msg.inspect
83
+ end
84
+ end
85
+ end
86
+
87
+ @loggers = {}
88
+
89
+ # Return a logger with the configured log prefix plus the calling scripts name
90
+
91
+ #
92
+ # Create a logger for the current class
93
+ #
94
+ # @return [Logger] for the current class
95
+ #
96
+ def logger
97
+ Log.logger(self.class.name)
98
+ end
99
+
100
+ class << self
101
+ #
102
+ # Injects a logger into the base class
103
+ #
104
+ # @param [String] name of the logger
105
+ #
106
+ # @return [Logger] for the supplied name
107
+ #
108
+ def logger(name)
109
+ name ||= self.class.name
110
+ @loggers[name] ||= Log.logger_for(name)
111
+ end
112
+
113
+ #
114
+ # Configure a logger for the supplied class name
115
+ #
116
+ # @param [String] classname to configure logger for
117
+ #
118
+ # @return [Logger] for the supplied classname
119
+ #
120
+ def logger_for(classname)
121
+ configure_logger_for(classname)
122
+ end
123
+
124
+ private
125
+
126
+ #
127
+ # Configure a logger for the supplied classname
128
+ #
129
+ # @param [String] classname to create logger for
130
+ #
131
+ # @return [Logger] Logger for the supplied classname
132
+ #
133
+ def configure_logger_for(classname)
134
+ log_prefix = Configuration.log_prefix
135
+ log_prefix += if classname
136
+ ".#{classname}"
137
+ else
138
+ ".#{log_caller}"
139
+ end
140
+ Logger.new(log_prefix)
141
+ end
142
+
143
+ #
144
+ # Figure out the log prefix
145
+ #
146
+ # @return [String] Prefix for log messages
147
+ #
148
+ def log_caller
149
+ caller_locations.map(&:path)
150
+ .grep_v(%r{openhab/core/})
151
+ .grep_v(/rubygems/)
152
+ .grep_v(%r{lib/ruby})
153
+ .first
154
+ .yield_self { |caller| File.basename(caller, '.*') }
155
+ end
156
+ end
157
+
158
+ #
159
+ # Add logger method to the object that includes this module
160
+ #
161
+ # @param [Object] base Object to add method to
162
+ #
163
+ #
164
+ def self.included(base)
165
+ class << base
166
+ def logger
167
+ Log.logger(self.class.name)
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end