openhab-scripting 4.3.0 → 4.6.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34dcff6b869bdb1bbcf1e61d84ffe7ff07d039204ee868d16f0f249c3074e898
4
- data.tar.gz: 39645a3f3486d9bba7db56a43d4c60883c346eea007242603a2dd2f7c8aa3b6e
3
+ metadata.gz: e7e6ee3df4ad1f531639fff95e296f8ed489f5b4a47a01c713d63855b98b5e6c
4
+ data.tar.gz: 8fe4c12aee553c1d8a4aba1509a0b90fb1fc40edc6f75c2b9c814b610e6b98a5
5
5
  SHA512:
6
- metadata.gz: 303f0751aac26ab7f28a4350ff03151a3a03bb1559f51ae0130c830ea5e20d6bb4165929a173ed4e749cf62ee0e4ab66f1f79845e8e851d44a288407e11be5a5
7
- data.tar.gz: dfee68ab34f41a303886bc8028ae7a30ff4baaee2fd87cc75602bfa7d82f57c2c3a7bf6190f6efecdb2a364c21a4514fe59984e9da8574cf19c5f3fd782abc7e
6
+ metadata.gz: f841778b56365cb9df2e8f3749d09c11626b2d528dcf6250d32b31e5733dfb9caead2a9987171808d20fdacd72fac62189f5391b538c48bc0e0e63c0b743a0ce
7
+ data.tar.gz: b64a487d9d4a3d6fcf4d7c6e8137ec4237ad75d6756fdc5b91546616db14c21833cf52156c0edfacbdaa0dc58c92bba2028cec98d58bf3df2e00f770f0a93fbf
@@ -4,6 +4,8 @@ require 'pp'
4
4
  require 'java'
5
5
  require 'set'
6
6
 
7
+ require 'openhab/log/logger'
8
+
7
9
  # Automation lookup and injection of OpenHab entities
8
10
 
9
11
  module OpenHAB
@@ -24,9 +26,6 @@ module OpenHAB
24
26
  # @return [Object] Item or Thing if found in registry
25
27
  #
26
28
  def method_missing(method, *args, &block)
27
- return if method.to_s == 'scriptLoaded'
28
- return if method.to_s == 'scriptUnloaded'
29
-
30
29
  logger.trace("method missing, performing OpenHab Lookup for: #{method}")
31
30
  EntityLookup.lookup_entity(method) || super
32
31
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openhab/log/logger'
4
+ require 'openhab/dsl/rules/rule'
5
+ require 'openhab/dsl/timers'
6
+
7
+ # OpenHAB main module
8
+ module OpenHAB
9
+ module Core
10
+ #
11
+ # Manages script loading and unloading
12
+ #
13
+ module ScriptHandling
14
+ include OpenHAB::Log
15
+
16
+ #
17
+ # Executed when OpenHAB unloads a script file
18
+ #
19
+ # rubocop:disable Naming/MethodName
20
+ # method name dictacted by OpenHAB
21
+ def scriptUnloaded
22
+ logger.trace('Script unloaded')
23
+ OpenHAB::DSL::Rules.cleanup_rules
24
+ OpenHAB::DSL::Timers.cancel_all
25
+ end
26
+ # rubocop:enable Naming/MethodName
27
+
28
+ #
29
+ # Executed when OpenHAB loads a script file
30
+ #
31
+ # rubocop:disable Naming/MethodName
32
+ # method name dictacted by OpenHAB
33
+ def scriptLoaded(filename)
34
+ logger.trace("Script loaded: #{filename}")
35
+ end
36
+ # rubocop:enable Naming/MethodName
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # OpenHAB main module
4
+ module OpenHAB
5
+ module Core
6
+ #
7
+ # Manages thread local varaibles for access inside of blocks
8
+ #
9
+ module ThreadLocal
10
+ #
11
+ # Execute the supplied block with the supplied values set for the currently running thread
12
+ # The previous values for each key are restored after the block is executed
13
+ #
14
+ # @param [Hash] Keys and values to set for running thread
15
+ #
16
+ def thread_local(**values)
17
+ old_values = values.map { |key, _value| [key, Thread.current[key]] }.to_h
18
+ values.each { |key, value| Thread.current[key] = value }
19
+ yield
20
+ ensure
21
+ old_values.each { |key, value| Thread.current[key] = value }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -9,7 +9,6 @@ module OpenHAB
9
9
  # Module to import and streamline access to OpenHAB actions
10
10
  #
11
11
  module Actions
12
- java_import org.openhab.core.library.types.PercentType
13
12
  include OpenHAB::Log
14
13
 
15
14
  OpenHAB::Core::OSGI.services('org.openhab.core.model.script.engine.action.ActionService')&.each do |service|
@@ -83,7 +82,7 @@ module OpenHAB
83
82
  # @return [void]
84
83
  #
85
84
  def say(text, voice: nil, sink: nil, volume: nil)
86
- volume = PercentType.new(volume&.to_i) unless volume.is_a?(PercentType) || volume.nil?
85
+ volume = Types::PercentType.new(volume) unless volume.is_a?(Types::PercentType) || volume.nil?
87
86
  Voice.say text, voice, sink, volume
88
87
  end
89
88
 
@@ -97,7 +96,7 @@ module OpenHAB
97
96
  # @return [void]
98
97
  #
99
98
  def play_sound(filename, sink: nil, volume: nil)
100
- volume = PercentType.new(volume&.to_i) unless volume.is_a?(PercentType) || volume.nil?
99
+ volume = Types::PercentType.new(volume) unless volume.is_a?(Types::PercentType) || volume.nil?
101
100
  Audio.playSound sink, filename, volume
102
101
  end
103
102
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ require_relative 'comparable_item'
6
+ require 'openhab/dsl/types/hsb_type'
7
+
8
+ module OpenHAB
9
+ module DSL
10
+ module Items
11
+ java_import org.openhab.core.library.items.ColorItem
12
+
13
+ # Adds methods to core OpenHAB ColorItem type to make it more natural in
14
+ # Ruby
15
+ class ColorItem < DimmerItem
16
+ extend Forwardable
17
+ include ComparableItem
18
+
19
+ # !@visibility private
20
+ def ==(other)
21
+ # need to check if we're referring to the same item before
22
+ # forwarding to <=> (and thus checking equality with state)
23
+ return true if equal?(other) || eql?(other)
24
+
25
+ super
26
+ end
27
+
28
+ #
29
+ # Type Coercion
30
+ #
31
+ # Coerce object to a HSBType
32
+ #
33
+ # @param [Types::HSBType, String] other object to coerce to a
34
+ # HSBType
35
+ #
36
+ # @return [[Types::HSBType, Types::HSBType]]
37
+ #
38
+ def coerce(other)
39
+ logger.trace("Coercing #{self} as a request from #{other.class}")
40
+ return [other, nil] unless state?
41
+ return [other, state] if other.is_a?(Types::HSBType) || other.respond_to?(:to_str)
42
+
43
+ raise TypeError, "can't convert #{other.class} into #{self.class}"
44
+ end
45
+
46
+ # any method that exists on {Types::HSBType} gets forwarded to +state+
47
+ delegate (Types::HSBType.instance_methods - instance_methods) => :state
48
+
49
+ # string commands aren't allowed on ColorItems, so try to implicitly
50
+ # convert it to an HSBType
51
+ # @!visibility private
52
+ def format_type(command)
53
+ return Types::HSBType.new(command) if command.respond_to?(:to_str)
54
+
55
+ super
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'generic_item'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ module Items
8
+ # Functionality to implement +ensure+/+ensure_states+
9
+ module Ensure
10
+ # Contains the global +ensure_states+ method
11
+ module EnsureStates
12
+ # Global method that takes a block and for the duration of the block
13
+ # all commands sent will check if the item is in the command's state
14
+ # before sending the command.
15
+ #
16
+ # @example Turn on several switches only if they're not already on
17
+ # ensure_states do
18
+ # Switch1.on
19
+ # Switch2.on
20
+ # end
21
+ def ensure_states
22
+ old = Thread.current[:ensure_states]
23
+ Thread.current[:ensure_states] = true
24
+ yield
25
+ ensure
26
+ Thread.current[:ensure_states] = old
27
+ end
28
+ module_function :ensure_states
29
+ end
30
+
31
+ # Contains the +ensure+ method mixed into {GenericItem} and {GroupItem::GroupMembers}
32
+ module Ensurable
33
+ # Fluent method call that you can chain commands on to, that will
34
+ # then automatically ensure that the item is not in the command's
35
+ # state before sending the command.
36
+ #
37
+ # @example Turn switch on only if it's not on
38
+ # MySwitch.ensure.on
39
+ # @example Turn on all switches in a group that aren't already on
40
+ # MySwitchGroup.members.ensure.on
41
+ def ensure
42
+ GenericItemDelegate.new(self)
43
+ end
44
+ end
45
+
46
+ # Extensions for {Items::GenericItem} to implement {Ensure}'s
47
+ # functionality
48
+ module GenericItem
49
+ include Ensurable
50
+
51
+ # If +ensure_states+ is active (by block or chained method), then
52
+ # check if this item is in the command's state before actually
53
+ # sending the command
54
+ def command(command)
55
+ return super unless Thread.current[:ensure_states]
56
+ return if command == state
57
+
58
+ super
59
+ end
60
+ alias << command
61
+ end
62
+
63
+ # "anonymous" class that wraps any method call in +ensure_states+
64
+ # before forwarding to the wrapped object
65
+ # @!visibility private
66
+ class GenericItemDelegate
67
+ def initialize(item)
68
+ @item = item
69
+ end
70
+
71
+ # activate +ensure_states+ before forwarding to the wrapped object
72
+ def method_missing(method, *args, &block)
73
+ return super unless @item.respond_to?(method)
74
+
75
+ ensure_states do
76
+ @item.__send__(method, *args, &block)
77
+ end
78
+ end
79
+
80
+ # .
81
+ def respond_to_missing?(method, include_private = false)
82
+ @item.respond_to?(method, include_private) || super
83
+ end
84
+ end
85
+ end
86
+
87
+ GenericItem.prepend(Ensure::GenericItem)
88
+ GroupItem::GroupMembers.include(Ensure::Ensurable)
89
+ end
90
+ end
91
+ end
92
+
93
+ Object.include OpenHAB::DSL::Items::Ensure::EnsureStates
@@ -31,6 +31,74 @@ module OpenHAB
31
31
  def to_a
32
32
  group.get_members.to_a
33
33
  end
34
+
35
+ # Send a command to each member of the group
36
+ #
37
+ # @return [GroupMembers] +self+
38
+ def command(command)
39
+ each { |item| item << command }
40
+ end
41
+ alias << command
42
+
43
+ # @!method refresh
44
+ # Send the +REFRESH+ command to each member of the group
45
+ # @return [GroupMembers] +self+
46
+
47
+ # @!method on
48
+ # Send the +ON+ command to each member of the group
49
+ # @return [GroupMembers] +self+
50
+
51
+ # @!method off
52
+ # Send the +OFF+ command to each member of the group
53
+ # @return [GroupMembers] +self+
54
+
55
+ # @!method up
56
+ # Send the +UP+ command to each member of the group
57
+ # @return [GroupMembers] +self+
58
+
59
+ # @!method down
60
+ # Send the +DOWN+ command to each member of the group
61
+ # @return [GroupMembers] +self+
62
+
63
+ # @!method stop
64
+ # Send the +STOP+ command to each member of the group
65
+ # @return [GroupMembers] +self+
66
+
67
+ # @!method move
68
+ # Send the +MOVE+ command to each member of the group
69
+ # @return [GroupMembers] +self+
70
+
71
+ # @!method increase
72
+ # Send the +INCREASE+ command to each member of the group
73
+ # @return [GroupMembers] +self+
74
+
75
+ # @!method desrease
76
+ # Send the +DECREASE+ command to each member of the group
77
+ # @return [GroupMembers] +self+
78
+
79
+ # @!method play
80
+ # Send the +PLAY+ command to each member of the group
81
+ # @return [GroupMembers] +self+
82
+
83
+ # @!method pause
84
+ # Send the +PAUSE+ command to each member of the group
85
+ # @return [GroupMembers] +self+
86
+
87
+ # @!method rewind
88
+ # Send the +REWIND+ command to each member of the group
89
+ # @return [GroupMembers] +self+
90
+
91
+ # @!method fast_forward
92
+ # Send the +FASTFORWARD+ command to each member of the group
93
+ # @return [GroupMembers] +self+
94
+
95
+ # @!method next
96
+ # Send the +NEXT+ command to each member of the group
97
+ # @return [GroupMembers] +self+
98
+
99
+ # @!method previous
100
+ # Send the +PREVIOUS+ command to each member of the group
101
+ # @return [GroupMembers] +self+
34
102
  end
35
103
 
36
104
  include Enumerable
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openhab/dsl/monkey_patch/events/item_command'
4
+ require 'openhab/dsl/types/types'
4
5
 
5
6
  require_relative 'item_registry'
6
7
 
@@ -10,19 +11,25 @@ require_relative 'switch_item'
10
11
  require_relative 'date_time_item'
11
12
  require_relative 'dimmer_item'
12
13
 
14
+ require_relative 'color_item'
13
15
  require_relative 'contact_item'
14
16
  require_relative 'group_item'
15
17
  require_relative 'image_item'
18
+ require_relative 'location_item'
16
19
  require_relative 'number_item'
17
20
  require_relative 'player_item'
18
21
  require_relative 'rollershutter_item'
19
22
  require_relative 'string_item'
20
23
 
24
+ require_relative 'ensure'
25
+
21
26
  module OpenHAB
22
27
  module DSL
23
28
  # Contains all OpenHAB *Item classes, as well as associated support
24
29
  # modules
25
30
  module Items
31
+ include OpenHAB::Log
32
+
26
33
  class << self
27
34
  private
28
35
 
@@ -40,7 +47,7 @@ module OpenHAB
40
47
  _command_predicate, state_predicate = Types::PREDICATE_ALIASES[state.to_s]
41
48
  next if klass.instance_methods.include?(state_predicate)
42
49
 
43
- OpenHAB::Core.logger.trace("Defining #{klass}##{state_predicate} for #{state}")
50
+ logger.trace("Defining #{klass}##{state_predicate} for #{state}")
44
51
  klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
45
52
  def #{state_predicate} # def on?
46
53
  raw_state == #{state} # raw_state == ON
@@ -51,19 +58,26 @@ module OpenHAB
51
58
 
52
59
  # defined methods for commanding an item to one of the Enum states
53
60
  # as well as predicates for if an ItemCommandEvent is one of those commands
54
- def def_command_methods(klass) # rubocop:disable Metrics/MethodLength method has single purpose
61
+ def def_command_methods(klass) # rubocop:disable Metrics method has single purpose
55
62
  values_for_enums(klass.ACCEPTED_COMMAND_TYPES).each do |value|
56
63
  command = Types::COMMAND_ALIASES[value.to_s]
57
64
  next if klass.instance_methods.include?(command)
58
65
 
59
- OpenHAB::Core.logger.trace("Defining #{klass}##{command} for #{value}")
66
+ logger.trace("Defining #{klass}##{command} for #{value}")
60
67
  klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
61
68
  def #{command} # def on
62
69
  command(#{value}) # command(ON)
63
70
  end # end
64
71
  RUBY
65
72
 
66
- OpenHAB::Core.logger.trace("Defining ItemCommandEvent##{command}? for #{value}")
73
+ logger.trace("Defining GroupItem::GroupMembers##{command} for #{value}")
74
+ GroupItem::GroupMembers.class_eval <<~RUBY, __FILE__, __LINE__ + 1
75
+ def #{command} # def on
76
+ each(&:#{command}) # each(&:on)
77
+ end # end
78
+ RUBY
79
+
80
+ logger.trace("Defining ItemCommandEvent##{command}? for #{value}")
67
81
  MonkeyPatch::Events::ItemCommandEvent.class_eval <<~RUBY, __FILE__, __LINE__ + 1
68
82
  def #{command}? # def refresh?
69
83
  command == #{value} # command == REFRESH
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ require_relative 'comparable_item'
6
+ require 'openhab/dsl/types/point_type'
7
+
8
+ module OpenHAB
9
+ module DSL
10
+ module Items
11
+ java_import org.openhab.core.library.items.LocationItem
12
+
13
+ # Adds methods to core OpenHAB NumberItem type to make it more natural in
14
+ # Ruby
15
+ class LocationItem < GenericItem
16
+ extend Forwardable
17
+ include ComparableItem
18
+
19
+ # !@visibility private
20
+ def ==(other)
21
+ # need to check if we're referring to the same item before
22
+ # forwarding to <=> (and thus checking equality with state)
23
+ return true if equal?(other) || eql?(other)
24
+
25
+ super
26
+ end
27
+
28
+ #
29
+ # Type Coercion
30
+ #
31
+ # Coerce object to a PointType
32
+ #
33
+ # @param [Types::PointType, String] other object to coerce to a
34
+ # PointType
35
+ #
36
+ # @return [[Types::PointType, Types::PointType]]
37
+ #
38
+ def coerce(other)
39
+ logger.trace("Coercing #{self} as a request from #{other.class}")
40
+ return [other, nil] unless state?
41
+ return [other, state] if other.is_a?(Types::PointType) || other.respond_to?(:to_str)
42
+
43
+ raise TypeError, "can't convert #{other.class} into #{self.class}"
44
+ end
45
+
46
+ # OpenHAB has this method, but it _only_ accepts PointType, so remove it and delegate
47
+ remove_method :distance_from
48
+
49
+ # any method that exists on {Types::PointType} gets forwarded to +state+
50
+ delegate (Types::PointType.instance_methods - instance_methods) => :state
51
+ end
52
+ end
53
+ end
54
+ end
@@ -91,7 +91,7 @@ module OpenHAB
91
91
 
92
92
  return super unless other.is_a?(javax.measure.Unit)
93
93
 
94
- QuantityType.new(to_d.to_java, other)
94
+ Types::QuantityType.new(to_d.to_java, other)
95
95
  end
96
96
  end
97
97
  end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'java'
4
+ require 'set'
5
+ require 'openhab/core/thread_local'
6
+ require 'openhab/log/logger'
4
7
 
5
8
  module OpenHAB
6
9
  module DSL
@@ -16,6 +19,7 @@ module OpenHAB
16
19
  # way of breaking it up into multiple classes
17
20
  class AutomationRule < Java::OrgOpenhabCoreAutomationModuleScriptRulesupportSharedSimple::SimpleRule
18
21
  include OpenHAB::Log
22
+ include OpenHAB::Core::ThreadLocal
19
23
  include OpenHAB::DSL::TimeOfDay
20
24
  java_import java.time.ZonedDateTime
21
25
 
@@ -24,6 +28,7 @@ module OpenHAB
24
28
  #
25
29
  # @param [Config] config Rule configuration
26
30
  #
31
+ # Constructor sets a number of variables, no further decomposition necessary
27
32
  def initialize(config:)
28
33
  super()
29
34
  set_name(config.name)
@@ -59,6 +64,14 @@ module OpenHAB
59
64
  end
60
65
  end
61
66
 
67
+ #
68
+ # Cleanup any resources associated with automation rule
69
+ #
70
+ def cleanup
71
+ logger.trace "Cancelling #{@trigger_delays.length} Trigger Delays(s) for rule '#{name}'"
72
+ @trigger_delays.each_value { |trigger_delay| trigger_delay.timer&.cancel }
73
+ end
74
+
62
75
  private
63
76
 
64
77
  #
@@ -13,6 +13,12 @@ module OpenHAB
13
13
  # Creates and manages OpenHAB Rules
14
14
  #
15
15
  module Rules
16
+ @script_rules = []
17
+
18
+ class << self
19
+ attr_reader :script_rules
20
+ end
21
+
16
22
  #
17
23
  # Create a new rule
18
24
  #
@@ -45,6 +51,13 @@ module OpenHAB
45
51
  end
46
52
  end
47
53
 
54
+ #
55
+ # Cleanup rules in this script file
56
+ #
57
+ def self.cleanup_rules
58
+ @script_rules.each(&:cleanup)
59
+ end
60
+
48
61
  private
49
62
 
50
63
  #
@@ -67,6 +80,7 @@ module OpenHAB
67
80
  return unless create_rule?(config)
68
81
 
69
82
  rule = AutomationRule.new(config: config)
83
+ Rules.script_rules << rule
70
84
  add_rule(rule)
71
85
  rule.execute if config.on_start?
72
86
  end
@@ -26,7 +26,7 @@ module OpenHAB
26
26
  class RuleConfig
27
27
  include OpenHAB::Log
28
28
  include OpenHAB::Core::EntityLookup
29
- include OpenHAB::DSL::Rules::Triggers
29
+ prepend OpenHAB::DSL::Rules::Triggers
30
30
  include OpenHAB::DSL::Rules::Guard
31
31
  include OpenHAB::DSL::Rules::Property
32
32
  extend OpenHAB::DSL
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'openhab/log/logger'
4
4
  require 'openhab/dsl/types/date_time_type'
5
+ require 'openhab/dsl/items/items'
5
6
  require 'time'
6
7
 
7
8
  module OpenHAB
@@ -3,6 +3,7 @@
3
3
  require 'java'
4
4
  require 'delegate'
5
5
  require 'forwardable'
6
+ require 'openhab/log/logger'
6
7
 
7
8
  module OpenHAB
8
9
  module DSL
@@ -10,15 +11,23 @@ module OpenHAB
10
11
  # Provides access to and ruby wrappers around OpenHAB timers
11
12
  #
12
13
  module Timers
14
+ include OpenHAB::Log
13
15
  java_import org.openhab.core.model.script.actions.ScriptExecution
14
16
  java_import java.time.ZonedDateTime
15
17
 
18
+ # Tracks active timers
19
+ @timers = Set.new
20
+ class << self
21
+ attr_accessor :timers
22
+ end
23
+
16
24
  # Ruby wrapper for OpenHAB Timer
17
25
  # This class implements delegator to delegate methods to the OpenHAB timer
18
26
  #
19
27
  # @author Brian O'Connell
20
28
  # @since 2.0.0
21
29
  class Timer < SimpleDelegator
30
+ include OpenHAB::Log
22
31
  extend Forwardable
23
32
 
24
33
  def_delegator :@timer, :is_active, :active?
@@ -31,20 +40,19 @@ module OpenHAB
31
40
  # @param [Duration] duration Duration until timer should fire
32
41
  # @param [Block] block Block to execute when timer fires
33
42
  #
34
- def initialize(duration:)
43
+ def initialize(duration:, &block)
35
44
  @duration = duration
36
45
 
37
46
  # A semaphore is used to prevent a race condition in which calling the block from the timer thread
38
47
  # occurs before the @timer variable can be set resulting in @timer being nil
39
48
  semaphore = Mutex.new
40
49
 
41
- timer_block = proc { semaphore.synchronize { yield(self) } }
42
-
43
50
  semaphore.synchronize do
44
51
  @timer = ScriptExecution.createTimer(
45
- ZonedDateTime.now.plus(@duration), timer_block
52
+ ZonedDateTime.now.plus(@duration), timer_block(semaphore, &block)
46
53
  )
47
54
  super(@timer)
55
+ Timers.timers << self
48
56
  end
49
57
  end
50
58
 
@@ -53,12 +61,40 @@ module OpenHAB
53
61
  #
54
62
  # @param [Duration] duration
55
63
  #
56
- # @return [<Type>] <description>
64
+ # @return [Timer] Rescheduled timer instances
57
65
  #
58
66
  def reschedule(duration = nil)
59
67
  duration ||= @duration
68
+ Timers.timers << self
60
69
  @timer.reschedule(ZonedDateTime.now.plus(duration))
61
70
  end
71
+
72
+ # Cancel timer
73
+ #
74
+ # @return [Boolean] True if cancel was successful, false otherwise
75
+ #
76
+ def cancel
77
+ Timers.timers.delete(self)
78
+ @timer.cancel
79
+ end
80
+
81
+ private
82
+
83
+ #
84
+ # Constructs a block to execute timer within
85
+ #
86
+ # @param [Semaphore] Semaphore to obtain before executing
87
+ #
88
+ # @return [Proc] Block for timer to execute
89
+ #
90
+ def timer_block(semaphore)
91
+ proc {
92
+ semaphore.synchronize do
93
+ Timers.timers.delete(self)
94
+ yield(self)
95
+ end
96
+ }
97
+ end
62
98
  end
63
99
 
64
100
  #
@@ -72,6 +108,14 @@ module OpenHAB
72
108
  def after(duration, &block)
73
109
  Timer.new(duration: duration, &block)
74
110
  end
111
+
112
+ #
113
+ # Cancels all active timers
114
+ #
115
+ def self.cancel_all
116
+ logger.trace("Cancelling #{@timers.length} timers")
117
+ @timers.each(&:cancel)
118
+ end
75
119
  end
76
120
  end
77
121
  end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'percent_type'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ module Types
8
+ java_import org.openhab.core.library.types.HSBType
9
+
10
+ # global alias
11
+ ::HSBType = HSBType
12
+
13
+ # Adds methods to core OpenHAB HSBType to make it more natural in Ruby
14
+ class HSBType < PercentType
15
+ # @!parse BLACK = BLACK # @return [HSBType]
16
+ # @!parse WHITE = WHITE # @return [HSBType]
17
+ # @!parse RED = RED # @return [HSBType]
18
+ # @!parse GREEN = GREEN # @return [HSBType]
19
+ # @!parse BLUE = BLUE # @return [HSBType]
20
+
21
+ # conversion to QuantityType doesn't make sense on HSBType
22
+ undef_method :|
23
+
24
+ remove_method :==
25
+
26
+ # r, g, b as an array of symbols
27
+ RGB_KEYS = %i[r g b].freeze
28
+ private_constant :RGB_KEYS
29
+
30
+ class << self
31
+ # @!method from_rgb(r, g, b)
32
+ # Create HSBType from RGB values
33
+ # @param r [Integer] Red component (0-255)
34
+ # @param g [Integer] Green component (0-255)
35
+ # @param b [Integer] Blue component (0-255)
36
+ # @return [HSBType]
37
+
38
+ # @!method from_xy(x, y)
39
+ # Create HSBType representing the provided xy color values in CIE XY color model
40
+ # @param x [Float]
41
+ # @param y [Float]
42
+ # @return [HSBType]
43
+
44
+ # Create HSBType from hue, saturation, and brightness values
45
+ # @param hue [DecimalType, QuantityType, Numeric] Hue component (0-360º)
46
+ # @param saturation [PercentType, Numeric] Saturation component (0-100%)
47
+ # @param brightness [PercentType, Numeric] Brightness component (0-100%)
48
+ # @return [HSBType]
49
+ def from_hsv(hue, saturation, brightness)
50
+ new(hue, saturation, brightness)
51
+ end
52
+
53
+ # add additional "overloads" to the constructor
54
+ # @!visibility private
55
+ def new(*args) # rubocop:disable Metrics
56
+ if args.length == 1 && args.first.respond_to?(:to_str)
57
+ value = args.first.to_str
58
+
59
+ # parse some formats OpenHAB doesn't understand
60
+ # in this case, HTML hex format for rgb
61
+ if (match = value.match(/^#(\h{2})(\h{2})(\h{2})$/))
62
+ rgb = match.to_a[1..3].map { |v| v.to_i(16) }
63
+ logger.trace("creating from rgb #{rgb.inspect}")
64
+ return from_rgb(*rgb)
65
+ end
66
+ end
67
+
68
+ return super unless args.length == 3
69
+
70
+ # convert from several numeric-like types to the exact types
71
+ # OpenHAB needs
72
+ hue = args[0]
73
+ args[0] = if hue.is_a?(DecimalType)
74
+ hue
75
+ elsif hue.is_a?(QuantityType)
76
+ DecimalType.new(hue.to_unit(::Units::DEGREE_ANGLE).to_big_decimal)
77
+ elsif hue.respond_to?(:to_d)
78
+ DecimalType.new(hue)
79
+ end
80
+ args[1..2] = args[1..2].map do |v|
81
+ if v.is_a?(PercentType)
82
+ v
83
+ elsif v.respond_to?(:to_d)
84
+ PercentType.new(v)
85
+ end
86
+ end
87
+
88
+ super(*args)
89
+ end
90
+ end
91
+
92
+ #
93
+ # Comparison
94
+ #
95
+ # @param [NumericType, Items::NumericItem, Items::ColorItem, Numeric, String]
96
+ # other object to compare to
97
+ #
98
+ # @return [Integer, nil] -1, 0, +1 depending on whether +other+ is
99
+ # less than, equal to, or greater than self
100
+ #
101
+ # nil is returned if the two values are incomparable
102
+ #
103
+ def <=>(other)
104
+ logger.trace("(#{self.class}) #{self} <=> #{other} (#{other.class})")
105
+ if other.is_a?(Items::ColorItem) ||
106
+ (other.is_a?(Items::GroupItem) && other.base_item.is_a?(ColorItem))
107
+ return false unless other.state?
108
+
109
+ self <=> other.state
110
+ elsif other.respond_to?(:to_str)
111
+ self <=> HSBType.new(other)
112
+ else
113
+ super
114
+ end
115
+ end
116
+
117
+ #
118
+ # Type Coercion
119
+ #
120
+ # Coerce object to a HSBType
121
+ #
122
+ # @param [NumericType, Items::NumericItem, Items::ColorItem, Numeric, String]
123
+ # other object to coerce to a HSBType
124
+ #
125
+ # @return [[HSBType, HSBType]]
126
+ #
127
+ def coerce(other)
128
+ logger.trace("Coercing #{self} as a request from #{other.class}")
129
+ if other.is_a?(Items::NumericItem) ||
130
+ (other.is_a?(Items::GroupItem) && other.base_item.is_a?(Items::NumericItem))
131
+ raise TypeError, "can't convert #{UnDefType} into #{self.class}" unless other.state?
132
+
133
+ [other.state, self]
134
+ elsif other.respond_to?(:to_str)
135
+ [HSBType.new(other.to_str), self]
136
+ else
137
+ super
138
+ end
139
+ end
140
+
141
+ # rename raw methods so we can overwrite them
142
+ # @!visibility private
143
+ alias raw_hue hue
144
+
145
+ # @!attribute [r] hue
146
+ # @return [QuantityType]
147
+ def hue
148
+ QuantityType.new(raw_hue.to_big_decimal, ::Units::DEGREE_ANGLE)
149
+ end
150
+
151
+ # Convert to a packed 32-bit RGB value representing the color in the default sRGB color model.
152
+ #
153
+ # The alpha component is always 100%.
154
+ #
155
+ # @return [Integer]
156
+ alias argb rgb
157
+
158
+ # Convert to a packed 24-bit RGB value representing the color in the default sRGB color model.
159
+ # @return [Integer]
160
+ def rgb
161
+ argb & 0xffffff
162
+ end
163
+
164
+ # Convert to an HTML-style string of 6 hex characters in the default sRGB color model.
165
+ # @return [String] +'#xxxxxx'+
166
+ def to_hex
167
+ Kernel.format('#%06x', rgb)
168
+ end
169
+
170
+ # include units
171
+ # @!visibility private
172
+ def to_s
173
+ "#{hue},#{saturation},#{brightness}"
174
+ end
175
+
176
+ # @!attribute [r] saturation
177
+ # @return [PercentType]
178
+
179
+ # @!attribute [r] brightness
180
+ # @return [PercentType]
181
+
182
+ # @!attribute [r] red
183
+ # @return [PercentType]
184
+
185
+ # @!attribute [r] green
186
+ # @return [PercentType]
187
+
188
+ # @!attribute [r] blue
189
+ # @return [PercentType]
190
+
191
+ # @!method to_rgb
192
+ # Convert to RGB values representing the color in the default sRGB color model
193
+ # @return [[PercentType, PercentType, PercentType]]
194
+
195
+ # @!method to_xy
196
+ # Convert to the xyY values representing this object's color in CIE XY color model
197
+ # @return [[PercentType, PercentType]]
198
+ end
199
+ end
200
+ end
201
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'decimal_type'
4
+
3
5
  module OpenHAB
4
6
  module DSL
5
7
  module Types
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ module Types
6
+ java_import org.openhab.core.library.types.PointType
7
+
8
+ # global scope
9
+ # @!visibility private
10
+ ::PointType = PointType
11
+
12
+ # Adds methods to core OpenHAB PointType to make it more natural in Ruby
13
+ class PointType
14
+ # @!parse include PrimitiveType
15
+
16
+ # @param latitude [DecimalType, QuantityType, StringType, Numeric]
17
+ # @param longitude [DecimalType, QuantityType, StringType, Numeric]
18
+ # @param altitude [DecimalType, QuantityType, StringType, Numeric]
19
+ def initialize(*args) # rubocop:disable Metrics
20
+ if (2..3).cover?(args.length)
21
+ args = args.each_with_index.map do |value, index|
22
+ if value.is_a?(DecimalType) || value.is_a?(StringType)
23
+ value
24
+ elsif value.is_a?(QuantityType)
25
+ unit = index == 2 ? Units.unit || SIUnits::METRE : Units::DEGREE_ANGLE
26
+ DecimalType.new(value.to_unit(unit).to_big_decimal)
27
+ elsif value.respond_to?(:to_str)
28
+ StringType.new(value.to_str)
29
+ elsif value.respond_to?(:to_d)
30
+ DecimalType.new(value)
31
+ end
32
+ end
33
+ end
34
+
35
+ super(*args)
36
+ end
37
+
38
+ #
39
+ # Check equality without type conversion
40
+ #
41
+ # @return [Boolean] if the same value is represented, without type
42
+ # conversion
43
+ def eql?(other)
44
+ return false unless other.instance_of?(self.class)
45
+
46
+ equals(other.to_s).zero?
47
+ end
48
+
49
+ #
50
+ # Check equality with type conversion
51
+ #
52
+ # @param [PointType, Items::LocationItem, String]
53
+ # other object to compare to
54
+ #
55
+ # @return [Boolean]
56
+ #
57
+ def ==(other) # rubocop:disable Metrics
58
+ logger.trace("(#{self.class}) #{self} == #{other} (#{other.class})")
59
+ if other.is_a?(Items::LocationItem) ||
60
+ (other.is_a?(Items::GroupItem) && other.base_item.is_a?(LocationItem))
61
+ return false unless other.state?
62
+
63
+ self == other.state
64
+ elsif other.respond_to?(:to_str)
65
+ self == PointType.new(other)
66
+ elsif other.respond_to?(:coerce)
67
+ lhs, rhs = other.coerce(self)
68
+ lhs == rhs
69
+ end
70
+ end
71
+
72
+ #
73
+ # Type Coercion
74
+ #
75
+ # Coerce object to a PointType
76
+ #
77
+ # @param [Items::LocationItem, String] other object to coerce to a
78
+ # PointType
79
+ #
80
+ # @return [[PointType, PointType]]
81
+ #
82
+ def coerce(other)
83
+ [coerce_single(other), self]
84
+ end
85
+
86
+ # rename raw methods so we can overwrite them
87
+ # @!visibility private
88
+ alias raw_latitude latitude
89
+ # .
90
+ # @!visibility private
91
+ alias raw_longitude longitude
92
+ # .
93
+ # @!visibility private
94
+ alias raw_altitude altitude
95
+ # .
96
+ # @!visibility private
97
+ alias raw_distance_from distance_from
98
+
99
+ # @!attribute [r] latitude
100
+ # @return [QuantityType]
101
+ def latitude
102
+ QuantityType.new(raw_latitude.to_big_decimal, SIUnits::DEGREE_ANGLE)
103
+ end
104
+
105
+ # @!attribute [r] longitude
106
+ # @return [QuantityType]
107
+ def longitude
108
+ QuantityType.new(raw_longitude.to_big_decimal, SIUnits::DEGREE_ANGLE)
109
+ end
110
+
111
+ # @!attribute [r] altitude
112
+ # @return [QuantityType]
113
+ def altitude
114
+ QuantityType.new(raw_altitude.to_big_decimal, Units::METRE)
115
+ end
116
+
117
+ #
118
+ # Calculate the distance in meters from other, ignoring altitude.
119
+ #
120
+ # This algorithm also ignores the oblate spheroid shape of Earth and
121
+ # assumes a perfect sphere, so results are inexact.
122
+ #
123
+ # @return [QuantityType]
124
+ def distance_from(other)
125
+ logger.trace("(#{self}).distance_from(#{other} (#{other.class})")
126
+ QuantityType.new(raw_distance_from(coerce_single(other)), SIUnits::METRE)
127
+ end
128
+ alias - distance_from
129
+
130
+ private
131
+
132
+ # coerce an object to a PointType
133
+ # @return [PointType]
134
+ def coerce_single(other) # rubocop:disable Metrics/MethodLength
135
+ logger.trace("Coercing #{self} as a request from #{other.class}")
136
+ if other.is_a?(PointType)
137
+ other
138
+ elsif other.is_a?(Items::LocationItem)
139
+ raise TypeError, "can't convert #{other.raw_state} into #{self.class}" unless other.state?
140
+
141
+ other.state
142
+ elsif other.respond_to?(:to_str)
143
+ PointType.new(other.to_str)
144
+ else
145
+ raise TypeError, "can't convert #{other.class} into #{self.class}"
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -1,15 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'openhab/log/logger'
4
+
3
5
  require_relative 'type'
4
6
 
5
7
  require_relative 'date_time_type'
6
8
  require_relative 'decimal_type'
9
+ require_relative 'hsb_type'
7
10
  require_relative 'increase_decrease_type'
8
11
  require_relative 'next_previous_type'
9
12
  require_relative 'open_closed_type'
10
13
  require_relative 'on_off_type'
11
14
  require_relative 'percent_type'
12
15
  require_relative 'play_pause_type'
16
+ require_relative 'point_type'
13
17
  require_relative 'quantity_type'
14
18
  require_relative 'refresh_type'
15
19
  require_relative 'rewind_fastforward_type'
@@ -25,6 +29,8 @@ module OpenHAB
25
29
  # modules
26
30
  #
27
31
  module Types
32
+ include OpenHAB::Log
33
+
28
34
  # Hash taking a Enum value, and returning two symbols of
29
35
  # predicates to be defined for it. the first is the "command" form,
30
36
  # which should be defined on ItemCommandEvent, and on the Type itself.
@@ -63,7 +69,7 @@ module OpenHAB
63
69
  states = PREDICATE_ALIASES[value.to_s]
64
70
 
65
71
  ([command] | states).each do |method|
66
- OpenHAB::Core.logger.trace("Defining #{klass}##{method} for #{value}")
72
+ logger.trace("Defining #{klass}##{method} for #{value}")
67
73
  klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
68
74
  def #{method} # def on?
69
75
  self == #{value} # self == ON
@@ -5,5 +5,5 @@
5
5
  #
6
6
  module OpenHAB
7
7
  # @return [String] Version of OpenHAB helper libraries
8
- VERSION = '4.3.0'
8
+ VERSION = '4.6.1'
9
9
  end
data/lib/openhab.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openhab/core/load_path'
4
+ require 'openhab/core/entity_lookup'
5
+ require 'openhab/core/script_handling'
4
6
  require 'openhab/core/openhab_setup'
5
7
  require 'openhab/log/logger'
6
8
  require 'openhab/dsl/dsl'
@@ -17,9 +19,12 @@ module OpenHAB
17
19
  # @param [Object] base Object to decorate with DSL and helper methods
18
20
  #
19
21
  #
22
+ # rubocop:disable Metrics/MethodLength
23
+ # Number of extensions and includes requires more lines
20
24
  def self.extended(base)
21
25
  OpenHAB::Core.wait_till_openhab_ready
22
26
  base.extend Log
27
+ base.extend OpenHAB::Core::ScriptHandling
23
28
  base.extend OpenHAB::Core::EntityLookup
24
29
  base.extend OpenHAB::DSL
25
30
  base.extend OpenHAB::DSL::TimeOfDay
@@ -31,6 +36,7 @@ module OpenHAB
31
36
 
32
37
  OpenHAB::Core.add_rubylib_to_load_path
33
38
  end
39
+ # rubocop:enable Metrics/MethodLength
34
40
  end
35
41
 
36
42
  # Extend caller with OpenHAB methods
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.0
4
+ version: 4.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian O'Connell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-19 00:00:00.000000000 Z
11
+ date: 2021-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -50,20 +50,25 @@ files:
50
50
  - lib/openhab/core/load_path.rb
51
51
  - lib/openhab/core/openhab_setup.rb
52
52
  - lib/openhab/core/osgi.rb
53
+ - lib/openhab/core/script_handling.rb
54
+ - lib/openhab/core/thread_local.rb
53
55
  - lib/openhab/dsl/actions.rb
54
56
  - lib/openhab/dsl/dsl.rb
55
57
  - lib/openhab/dsl/gems.rb
56
58
  - lib/openhab/dsl/group.rb
59
+ - lib/openhab/dsl/items/color_item.rb
57
60
  - lib/openhab/dsl/items/comparable_item.rb
58
61
  - lib/openhab/dsl/items/contact_item.rb
59
62
  - lib/openhab/dsl/items/date_time_item.rb
60
63
  - lib/openhab/dsl/items/dimmer_item.rb
64
+ - lib/openhab/dsl/items/ensure.rb
61
65
  - lib/openhab/dsl/items/generic_item.rb
62
66
  - lib/openhab/dsl/items/group_item.rb
63
67
  - lib/openhab/dsl/items/image_item.rb
64
68
  - lib/openhab/dsl/items/item_equality.rb
65
69
  - lib/openhab/dsl/items/item_registry.rb
66
70
  - lib/openhab/dsl/items/items.rb
71
+ - lib/openhab/dsl/items/location_item.rb
67
72
  - lib/openhab/dsl/items/metadata.rb
68
73
  - lib/openhab/dsl/items/number_item.rb
69
74
  - lib/openhab/dsl/items/numeric_item.rb
@@ -104,6 +109,7 @@ files:
104
109
  - lib/openhab/dsl/types/comparable_type.rb
105
110
  - lib/openhab/dsl/types/date_time_type.rb
106
111
  - lib/openhab/dsl/types/decimal_type.rb
112
+ - lib/openhab/dsl/types/hsb_type.rb
107
113
  - lib/openhab/dsl/types/increase_decrease_type.rb
108
114
  - lib/openhab/dsl/types/next_previous_type.rb
109
115
  - lib/openhab/dsl/types/numeric_type.rb
@@ -111,6 +117,7 @@ files:
111
117
  - lib/openhab/dsl/types/open_closed_type.rb
112
118
  - lib/openhab/dsl/types/percent_type.rb
113
119
  - lib/openhab/dsl/types/play_pause_type.rb
120
+ - lib/openhab/dsl/types/point_type.rb
114
121
  - lib/openhab/dsl/types/quantity_type.rb
115
122
  - lib/openhab/dsl/types/refresh_type.rb
116
123
  - lib/openhab/dsl/types/rewind_fastforward_type.rb