openhab-scripting 4.3.0 → 4.6.1

Sign up to get free protection for your applications and to get access to all the features.
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