openhab-scripting 2.14.3 → 2.16.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) 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 -54
  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 -47
  62. data/lib/openhab/core/dsl/actions.rb +0 -107
  63. data/lib/openhab/core/dsl/entities.rb +0 -140
  64. data/lib/openhab/core/dsl/group.rb +0 -93
  65. data/lib/openhab/core/dsl/items/items.rb +0 -51
  66. data/lib/openhab/core/dsl/items/number_item.rb +0 -318
  67. data/lib/openhab/core/dsl/items/string_item.rb +0 -120
  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 -167
  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 -130
  78. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +0 -283
  79. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +0 -87
  80. data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +0 -41
  81. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +0 -47
  82. data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +0 -6
  83. data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +0 -24
  84. data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +0 -41
  85. data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +0 -25
  86. data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +0 -23
  87. data/lib/openhab/core/dsl/monkey_patch/types/types.rb +0 -7
  88. data/lib/openhab/core/dsl/property.rb +0 -96
  89. data/lib/openhab/core/dsl/rule/automation_rule.rb +0 -348
  90. data/lib/openhab/core/dsl/rule/guard.rb +0 -136
  91. data/lib/openhab/core/dsl/rule/rule.rb +0 -117
  92. data/lib/openhab/core/dsl/rule/rule_config.rb +0 -153
  93. data/lib/openhab/core/dsl/rule/triggers/changed.rb +0 -145
  94. data/lib/openhab/core/dsl/rule/triggers/channel.rb +0 -55
  95. data/lib/openhab/core/dsl/rule/triggers/command.rb +0 -106
  96. data/lib/openhab/core/dsl/rule/triggers/cron.rb +0 -160
  97. data/lib/openhab/core/dsl/rule/triggers/trigger.rb +0 -126
  98. data/lib/openhab/core/dsl/rule/triggers/updated.rb +0 -100
  99. data/lib/openhab/core/dsl/states.rb +0 -63
  100. data/lib/openhab/core/dsl/things.rb +0 -93
  101. data/lib/openhab/core/dsl/time_of_day.rb +0 -229
  102. data/lib/openhab/core/dsl/timers.rb +0 -79
  103. data/lib/openhab/core/dsl/types/quantity.rb +0 -292
  104. data/lib/openhab/core/dsl/units.rb +0 -41
  105. data/lib/openhab/core/log.rb +0 -170
  106. data/lib/openhab/core/patch_load_path.rb +0 -7
  107. data/lib/openhab/core/startup_delay.rb +0 -23
  108. 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