openhab-scripting 2.16.2 → 2.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab.rb +12 -16
  3. data/lib/openhab/core/entity_lookup.rb +162 -0
  4. data/lib/openhab/core/openhab_setup.rb +31 -0
  5. data/lib/openhab/core/osgi.rb +61 -0
  6. data/lib/openhab/dsl/actions.rb +105 -0
  7. data/lib/openhab/dsl/dsl.rb +47 -0
  8. data/lib/openhab/{core/dsl → dsl}/gems.rb +0 -1
  9. data/lib/openhab/dsl/group.rb +100 -0
  10. data/lib/openhab/dsl/items/items.rb +46 -0
  11. data/lib/openhab/dsl/items/number_item.rb +352 -0
  12. data/lib/openhab/dsl/items/string_item.rb +120 -0
  13. data/lib/openhab/dsl/monkey_patch/actions/actions.rb +4 -0
  14. data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +32 -0
  15. data/lib/openhab/dsl/monkey_patch/events/events.rb +5 -0
  16. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +23 -0
  17. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +35 -0
  18. data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +33 -0
  19. data/lib/openhab/dsl/monkey_patch/items/contact_item.rb +61 -0
  20. data/lib/openhab/dsl/monkey_patch/items/dimmer_item.rb +193 -0
  21. data/lib/openhab/dsl/monkey_patch/items/group_item.rb +37 -0
  22. data/lib/openhab/dsl/monkey_patch/items/items.rb +133 -0
  23. data/lib/openhab/dsl/monkey_patch/items/metadata.rb +281 -0
  24. data/lib/openhab/dsl/monkey_patch/items/persistence.rb +70 -0
  25. data/lib/openhab/dsl/monkey_patch/items/switch_item.rb +95 -0
  26. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +39 -0
  27. data/lib/openhab/dsl/monkey_patch/ruby/range.rb +47 -0
  28. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +7 -0
  29. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +41 -0
  30. data/lib/openhab/dsl/monkey_patch/types/decimal_type.rb +70 -0
  31. data/lib/openhab/dsl/monkey_patch/types/on_off_type.rb +51 -0
  32. data/lib/openhab/dsl/monkey_patch/types/open_closed_type.rb +36 -0
  33. data/lib/openhab/dsl/monkey_patch/types/percent_type.rb +32 -0
  34. data/lib/openhab/dsl/monkey_patch/types/quantity_type.rb +69 -0
  35. data/lib/openhab/dsl/monkey_patch/types/types.rb +8 -0
  36. data/lib/openhab/dsl/persistence.rb +25 -0
  37. data/lib/openhab/dsl/rules/automation_rule.rb +342 -0
  38. data/lib/openhab/dsl/rules/guard.rb +134 -0
  39. data/lib/openhab/dsl/rules/property.rb +102 -0
  40. data/lib/openhab/dsl/rules/rule.rb +116 -0
  41. data/lib/openhab/dsl/rules/rule_config.rb +151 -0
  42. data/lib/openhab/dsl/rules/triggers/changed.rb +143 -0
  43. data/lib/openhab/dsl/rules/triggers/channel.rb +53 -0
  44. data/lib/openhab/dsl/rules/triggers/command.rb +104 -0
  45. data/lib/openhab/dsl/rules/triggers/cron.rb +177 -0
  46. data/lib/openhab/dsl/rules/triggers/trigger.rb +124 -0
  47. data/lib/openhab/dsl/rules/triggers/updated.rb +98 -0
  48. data/lib/openhab/dsl/states.rb +61 -0
  49. data/lib/openhab/dsl/things.rb +91 -0
  50. data/lib/openhab/dsl/time_of_day.rb +228 -0
  51. data/lib/openhab/dsl/timers.rb +77 -0
  52. data/lib/openhab/dsl/types/quantity.rb +290 -0
  53. data/lib/openhab/dsl/units.rb +39 -0
  54. data/lib/openhab/log/configuration.rb +21 -0
  55. data/lib/openhab/log/logger.rb +172 -0
  56. data/lib/openhab/version.rb +1 -1
  57. metadata +55 -58
  58. data/lib/openhab/configuration.rb +0 -16
  59. data/lib/openhab/core/cron.rb +0 -27
  60. data/lib/openhab/core/debug.rb +0 -34
  61. data/lib/openhab/core/dsl.rb +0 -51
  62. data/lib/openhab/core/dsl/actions.rb +0 -107
  63. data/lib/openhab/core/dsl/entities.rb +0 -147
  64. data/lib/openhab/core/dsl/group.rb +0 -102
  65. data/lib/openhab/core/dsl/items/items.rb +0 -51
  66. data/lib/openhab/core/dsl/items/number_item.rb +0 -323
  67. data/lib/openhab/core/dsl/items/string_item.rb +0 -122
  68. data/lib/openhab/core/dsl/monkey_patch/actions/actions.rb +0 -4
  69. data/lib/openhab/core/dsl/monkey_patch/actions/script_thing_actions.rb +0 -22
  70. data/lib/openhab/core/dsl/monkey_patch/events.rb +0 -5
  71. data/lib/openhab/core/dsl/monkey_patch/events/item_command.rb +0 -13
  72. data/lib/openhab/core/dsl/monkey_patch/events/item_state_changed.rb +0 -25
  73. data/lib/openhab/core/dsl/monkey_patch/events/thing_status_info.rb +0 -26
  74. data/lib/openhab/core/dsl/monkey_patch/items/contact_item.rb +0 -54
  75. data/lib/openhab/core/dsl/monkey_patch/items/dimmer_item.rb +0 -182
  76. data/lib/openhab/core/dsl/monkey_patch/items/group_item.rb +0 -27
  77. data/lib/openhab/core/dsl/monkey_patch/items/items.rb +0 -132
  78. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +0 -283
  79. data/lib/openhab/core/dsl/monkey_patch/items/persistence.rb +0 -72
  80. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +0 -87
  81. data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +0 -41
  82. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +0 -47
  83. data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +0 -7
  84. data/lib/openhab/core/dsl/monkey_patch/ruby/string.rb +0 -43
  85. data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +0 -60
  86. data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +0 -41
  87. data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +0 -25
  88. data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +0 -23
  89. data/lib/openhab/core/dsl/monkey_patch/types/quantity_type.rb +0 -58
  90. data/lib/openhab/core/dsl/monkey_patch/types/types.rb +0 -8
  91. data/lib/openhab/core/dsl/persistence.rb +0 -27
  92. data/lib/openhab/core/dsl/property.rb +0 -96
  93. data/lib/openhab/core/dsl/rule/automation_rule.rb +0 -345
  94. data/lib/openhab/core/dsl/rule/guard.rb +0 -136
  95. data/lib/openhab/core/dsl/rule/rule.rb +0 -117
  96. data/lib/openhab/core/dsl/rule/rule_config.rb +0 -153
  97. data/lib/openhab/core/dsl/rule/triggers/changed.rb +0 -145
  98. data/lib/openhab/core/dsl/rule/triggers/channel.rb +0 -55
  99. data/lib/openhab/core/dsl/rule/triggers/command.rb +0 -106
  100. data/lib/openhab/core/dsl/rule/triggers/cron.rb +0 -160
  101. data/lib/openhab/core/dsl/rule/triggers/trigger.rb +0 -126
  102. data/lib/openhab/core/dsl/rule/triggers/updated.rb +0 -100
  103. data/lib/openhab/core/dsl/states.rb +0 -63
  104. data/lib/openhab/core/dsl/things.rb +0 -93
  105. data/lib/openhab/core/dsl/time_of_day.rb +0 -231
  106. data/lib/openhab/core/dsl/timers.rb +0 -79
  107. data/lib/openhab/core/dsl/types/quantity.rb +0 -292
  108. data/lib/openhab/core/dsl/units.rb +0 -41
  109. data/lib/openhab/core/log.rb +0 -170
  110. data/lib/openhab/core/patch_load_path.rb +0 -7
  111. data/lib/openhab/core/startup_delay.rb +0 -23
  112. data/lib/openhab/osgi.rb +0 -59
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'java'
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ module Rules
9
+ #
10
+ # Module holds rule triggers
11
+ #
12
+ module Triggers
13
+ #
14
+ # Create a trigger for a thing
15
+ #
16
+ # @param [Thing] thing to create trigger for
17
+ # @param [Trigger] trigger to map with thing
18
+ # @param [State] to for thing
19
+ # @param [State] from state of thing
20
+ #
21
+ # @return [Array] Trigger and config for thing
22
+ #
23
+ def trigger_for_thing(thing, trigger, to = nil, from = nil)
24
+ config = { 'thingUID' => thing.uid.to_s }
25
+ config['status'] = trigger_state_from_symbol(to).to_s if to
26
+ config['previousStatus'] = trigger_state_from_symbol(from).to_s if from
27
+ [trigger, config]
28
+ end
29
+
30
+ #
31
+ # converts object to upcase string if its a symbol
32
+ #
33
+ # @param [sym] sym potential symbol to convert
34
+ #
35
+ # @return [String] Upcased symbol as string
36
+ #
37
+ def trigger_state_from_symbol(sym)
38
+ sym.to_s.upcase if (sym.is_a? Symbol) || sym
39
+ end
40
+
41
+ #
42
+ # Append a trigger to the list of triggeres
43
+ #
44
+ # @param [String] type of trigger to create
45
+ # @param [Map] config map describing trigger configuration
46
+ #
47
+ # @return [Trigger] OpenHAB trigger
48
+ #
49
+ def append_trigger(type, config)
50
+ logger.trace("Creating trigger of type #{type} for #{config}")
51
+ trigger = Trigger.trigger(type: type, config: config)
52
+ @triggers << trigger
53
+ trigger
54
+ end
55
+
56
+ #
57
+ # Class for creating and managing triggers
58
+ #
59
+ class Trigger
60
+ java_import org.openhab.core.automation.util.TriggerBuilder
61
+ java_import org.openhab.core.config.core.Configuration
62
+
63
+ # @return [String] A channel event trigger
64
+ CHANNEL_EVENT = 'core.ChannelEventTrigger'
65
+
66
+ # @return [String] A thing status Change trigger
67
+ THING_CHANGE = 'core.ThingStatusChangeTrigger'
68
+
69
+ # @return [String] A thing status update trigger
70
+ THING_UPDATE = 'core.ThingStatusUpdateTrigger'
71
+
72
+ # @return [String] An item command trigger
73
+ ITEM_COMMAND = 'core.ItemCommandTrigger'
74
+
75
+ # @return [String] An item state update trigger
76
+ ITEM_STATE_UPDATE = 'core.ItemStateUpdateTrigger'
77
+
78
+ # @return [String] An item state change trigger
79
+ ITEM_STATE_CHANGE = 'core.ItemStateChangeTrigger'
80
+
81
+ # @return [String] A group state change trigger for items in the group
82
+ GROUP_STATE_CHANGE = 'core.GroupStateChangeTrigger'
83
+
84
+ # @return [String] A group state update trigger for items in the group
85
+ GROUP_STATE_UPDATE = 'core.GroupStateUpdateTrigger'
86
+
87
+ # @return [String] A group command trigger for items in the group
88
+ GROUP_COMMAND = 'core.GroupCommandTrigger'
89
+
90
+ # @return [String] A time of day trigger
91
+ TIME_OF_DAY = 'timer.TimeOfDayTrigger'
92
+
93
+ # @return [String] A cron trigger
94
+ CRON = 'timer.GenericCronTrigger'
95
+
96
+ #
97
+ # Create a trigger
98
+ #
99
+ # @param [String] type of trigger
100
+ # @param [Map] config map
101
+ #
102
+ # @return [OpenHAB Trigger] configured by type and supplied config
103
+ #
104
+ def self.trigger(type:, config:)
105
+ TriggerBuilder.create
106
+ .with_id(uuid)
107
+ .with_type_uid(type)
108
+ .with_configuration(Configuration.new(config))
109
+ .build
110
+ end
111
+
112
+ #
113
+ # Generate a UUID for triggers
114
+ #
115
+ # @return [String] UUID
116
+ #
117
+ def self.uuid
118
+ SecureRandom.uuid
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openhab/log/logger'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ module Rules
8
+ #
9
+ # Module holds rule triggers
10
+ #
11
+ module Triggers
12
+ include OpenHAB::Log
13
+
14
+ #
15
+ # Create a trigger when item, group or thing is updated
16
+ #
17
+ # @param [Array] items array to trigger on updated
18
+ # @param [State] to to match for tigger
19
+ #
20
+ # @return [Trigger] Trigger for updated entity
21
+ #
22
+ def updated(*items, to: nil)
23
+ items.flatten.each do |item|
24
+ logger.trace("Creating updated trigger for item(#{item}) to(#{to})")
25
+ [to].flatten.each do |to_state|
26
+ trigger, config = create_update_trigger(item, to_state)
27
+ append_trigger(trigger, config)
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ #
35
+ # Create a trigger for updates
36
+ #
37
+ # @param [Object] item Type of item [Group,Thing,Item] to create update trigger for
38
+ # @param [State] to_state state restriction on trigger
39
+ #
40
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
41
+ # second element is a Hash configuring trigger
42
+ #
43
+ def create_update_trigger(item, to_state)
44
+ case item
45
+ when GroupItems then group_update(item, to_state)
46
+ when Thing then thing_update(item, to_state)
47
+ else item_update(item, to_state)
48
+ end
49
+ end
50
+
51
+ #
52
+ # Create an update trigger for an item
53
+ #
54
+ # @param [Item] item to create trigger for
55
+ # @param [State] to_state optional state restriction for target
56
+ #
57
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
58
+ # second element is a Hash configuring trigger
59
+ #
60
+ def item_update(item, to_state)
61
+ config = { 'itemName' => item.name }
62
+ config['state'] = to_state.to_s unless to_state.nil?
63
+ trigger = Trigger::ITEM_STATE_UPDATE
64
+ [trigger, config]
65
+ end
66
+
67
+ #
68
+ # Create an update trigger for a group
69
+ #
70
+ # @param [Item] item to create trigger for
71
+ # @param [State] to_state optional state restriction for target
72
+ #
73
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
74
+ # second element is a Hash configuring trigger
75
+ #
76
+ def group_update(item, to_state)
77
+ config = { 'groupName' => item.group.name }
78
+ config['state'] = to_state.to_s unless to_state.nil?
79
+ trigger = Trigger::GROUP_STATE_UPDATE
80
+ [trigger, config]
81
+ end
82
+
83
+ #
84
+ # Create an update trigger for a thing
85
+ #
86
+ # @param [Thing] thing to create trigger for
87
+ # @param [State] to_state optional state restriction for target
88
+ #
89
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
90
+ # second element is a Hash configuring trigger
91
+ #
92
+ def thing_update(thing, to_state)
93
+ trigger_for_thing(thing, Trigger::THING_UPDATE, to_state)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ #
8
+ # Manages storing and restoring item state
9
+ #
10
+ module States
11
+ java_import org.openhab.core.model.script.actions.BusEvent
12
+
13
+ #
14
+ # Delegates state storage to a Hash providing methods to operate with states
15
+ #
16
+ class StateStorage < SimpleDelegator
17
+ #
18
+ # Restore the stored states of all items
19
+ #
20
+ #
21
+ def restore
22
+ BusEvent.restoreStates(to_h)
23
+ end
24
+
25
+ #
26
+ # Restore states for items that have changed
27
+ #
28
+ #
29
+ def restore_changes
30
+ BusEvent.restoreStates(select { |item, value| item != value })
31
+ end
32
+
33
+ #
34
+ # Detect if any item have changed states since being stored
35
+ #
36
+ # @return [Boolean] True if any items have changed states, false otherwise
37
+ #
38
+ def changed?
39
+ any? { |item, value| item != value }
40
+ end
41
+ end
42
+
43
+ #
44
+ # Store states of supplied items
45
+ #
46
+ # @param [Array] items to store states of
47
+ #
48
+ # @return [StateStorage] item states
49
+ #
50
+ def store_states(*items)
51
+ items = items.flatten.map { |item| item.is_a?(Group) ? item.group : item }
52
+ states = StateStorage.new(BusEvent.storeStates(*items).to_h)
53
+ if block_given?
54
+ yield
55
+ states.restore
56
+ end
57
+ states
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+ require 'openhab/log/logger'
5
+ require 'openhab/dsl/actions'
6
+ require 'delegate'
7
+
8
+ module OpenHAB
9
+ module DSL
10
+ #
11
+ # Support for OpenHAB Things
12
+ #
13
+ module Things
14
+ include OpenHAB::Log
15
+
16
+ #
17
+ # Ruby Delegator for Thing
18
+ #
19
+ class Thing < SimpleDelegator
20
+ include OpenHAB::DSL::Actions
21
+ include OpenHAB::Log
22
+
23
+ def initialize(thing)
24
+ super
25
+ define_action_methods
26
+ end
27
+
28
+ private
29
+
30
+ java_import 'org.openhab.core.automation.annotation.RuleAction'
31
+
32
+ #
33
+ # Define methods from actions mapped to this thing
34
+ #
35
+ #
36
+ def define_action_methods
37
+ actions_for_thing(uid).each do |action|
38
+ methods = action.java_class.declared_instance_methods
39
+ methods.select { |method| method.annotation_present?(RuleAction.java_class) }
40
+ .each { |method| define_action_method(action: action, method: method.name) }
41
+ end
42
+ end
43
+
44
+ #
45
+ # Define a method, delegating to supplied action class
46
+ #
47
+ # @param [Object] action object to delegate method to
48
+ # @param [String] method Name of method to delegate
49
+ #
50
+ #
51
+ def define_action_method(action:, method:)
52
+ logger.trace("Adding action method '#{method}' to thing '#{uid}'")
53
+ define_singleton_method(method) do |*args|
54
+ action.public_send(method, *args)
55
+ end
56
+ end
57
+ end
58
+
59
+ #
60
+ # Wraps all Things in a delegator to underlying set and provides lookup method
61
+ #
62
+ class Things < SimpleDelegator
63
+ java_import org.openhab.core.thing.ThingUID
64
+
65
+ # Gets a specific thing by name in the format binding_id:type_id:thing_id
66
+ # @return Thing specified by name or nil if name does not exist in thing registry
67
+ def[](uid)
68
+ thing_uid = ThingUID.new(*uid.split(':'))
69
+ # rubocop: disable Style/GlobalVars
70
+ thing = $things.get(thing_uid)
71
+ # rubocop: enable Style/GlobalVars
72
+ return unless thing
73
+
74
+ logger.trace("Retrieved Thing(#{thing}) from registry for uid: #{uid}")
75
+ Thing.new(thing)
76
+ end
77
+ end
78
+
79
+ #
80
+ # Get all things known to OpenHAB
81
+ #
82
+ # @return [Set] of all Thing objects known to openhab
83
+ #
84
+ def things
85
+ # rubocop: disable Style/GlobalVars
86
+ Things.new($things.getAll.map { |thing| Thing.new(thing) }.to_set)
87
+ # rubocop: enable Style/GlobalVars
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+ require 'openhab/log/logger'
5
+ require 'time'
6
+ require 'date'
7
+
8
+ module OpenHAB
9
+ module DSL
10
+ # Times without specific dates e.g. 6:00:00
11
+ # @author Brian O'Connell
12
+ # @since 0.0.1
13
+ module TimeOfDay
14
+ java_import java.time.LocalTime
15
+ java_import java.time.format.DateTimeFormatterBuilder
16
+ java_import java.util.Locale
17
+
18
+ # Class that encapsulates a Time of Day, often viewed as hour-minute-second
19
+ # @author Brian O'Connell
20
+ # @since 0.0.1
21
+ class TimeOfDay
22
+ include Comparable
23
+
24
+ # Immutable Java object containing Time Of Day
25
+ # @return [Java.Time.LocalTime] reprsenting the Time Of Day
26
+ attr_reader :local_time
27
+
28
+ # Constructs a TimeOfDay representing the time when called
29
+ # @since 0.0.1
30
+ # @return [TimeOfDay] representing time when method was invoked
31
+ def self.now
32
+ now = LocalTime.now()
33
+ TimeOfDay.new(h: now.hour, m: now.minute, s: now.second)
34
+ end
35
+
36
+ # Constructs a TimeOfDay representing midnight
37
+ # @since 0.0.1
38
+ # @return [TimeOfDay] representing midnight
39
+ def self.midnight
40
+ TimeOfDay.new(h: 0, m: 0, s: 0)
41
+ end
42
+
43
+ # Constructs a TimeOfDay representing noon
44
+ # @since 0.0.1
45
+ # @return [TimeOfDay] representing noon
46
+ def self.noon
47
+ TimeOfDay.new(h: 12, m: 0, s: 0)
48
+ end
49
+
50
+ # Constructs a TimeOfDay representing the time when called
51
+ # @since 0.0.1
52
+ # @param [String] string representation of TimeOfDay. Valid formats include "HH:MM:SS", "HH:MM",
53
+ # "H:MM", "HH", "H", "H:MM am"
54
+ # @return [TimeOfDay] object created by parsing supplied string
55
+ def self.parse(string)
56
+ format = /(am|pm)$/i.match?(string) ? 'h[:mm[:ss]][ ]a' : 'H[:mm[:ss]]'
57
+ local_time = LocalTime.parse(string, DateTimeFormatterBuilder.new
58
+ .parseCaseInsensitive.appendPattern(format).toFormatter(Locale::ENGLISH))
59
+ TimeOfDay.new(h: local_time.hour, m: local_time.minute, s: local_time.second)
60
+ rescue java.time.format.DateTimeParseException => e
61
+ raise ArgumentError, e.message
62
+ end
63
+
64
+ # Constructs a TimeOfDay representing the time when called
65
+ # @since 0.0.1
66
+ # @option opts [Number] :h Hour of the day, defaults to 0
67
+ # @option opts [Number] :m Minute of the day, defaults to 0
68
+ # @option opts [Number] :s Second of the day, defaults to 0
69
+ # @return [TimeOfDay] representing time when method was invoked
70
+ # rubocop: disable Naming/MethodParameterName
71
+ # This method has a better feel with short parameter names
72
+ def initialize(h: 0, m: 0, s: 0)
73
+ @local_time = LocalTime.of(h, m, s)
74
+ freeze
75
+ end
76
+ # rubocop: enable Naming/MethodParameterName
77
+
78
+ # Returns true if the time falls within a range
79
+ def between?(range)
80
+ between(range).cover? self
81
+ end
82
+
83
+ # Returns the hour of the TimeOfDay
84
+ # @since 0.0.1
85
+ # @return [Number] Hour of the day, from 0 to 23
86
+ def hour
87
+ @local_time.hour
88
+ end
89
+
90
+ # Returns the minute of the TimeOfDay
91
+ # @since 0.0.1
92
+ # @return [Number] minute of the day, from 0 to 59
93
+ def minute
94
+ @local_time.minute
95
+ end
96
+
97
+ # Returns the second of the TimeOfDay
98
+ # @since 0.0.1
99
+ # @return [Number] second of the day, from 0 to 59
100
+ def second
101
+ @local_time.second
102
+ end
103
+
104
+ # Returns the string representation of the TimeOfDay
105
+ # @since 0.0.1
106
+ # @return [String] in any of the following formats depending on time representation HH:mm, HH:mm:ss,
107
+ # HH:mm:ss.SSS, HH:mm:ss.SSSSSS, HH:mm:ss.SSSSSSSSS
108
+ def to_s
109
+ @local_time.to_s
110
+ end
111
+
112
+ # Compares one TimeOfDay to another
113
+ # @since 0.0.1
114
+ # @return [Number, nil] -1,0,1 if other TimeOfDay is less than, equal to, or greater than this TimeOfDay
115
+ # or nil if an object other than TimeOfDay is provided
116
+ def <=>(other)
117
+ case other
118
+ when TimeOfDay
119
+ @local_time.compare_to(other.local_time)
120
+ when String
121
+ @local_time.compare_to(TimeOfDay.parse(other).local_time)
122
+ else
123
+ -(other <=> self)
124
+ end
125
+ end
126
+ end
127
+
128
+ # Modules that refines the Ruby Range object cover? and include? methods to support TimeOfDay ranges
129
+ class TimeOfDayRangeElement < Numeric
130
+ include Comparable
131
+ include OpenHAB::Log
132
+
133
+ NUM_SECONDS_IN_DAY = (60 * 60 * 24)
134
+
135
+ attr_reader :sod
136
+
137
+ def initialize(sod:, range_begin:)
138
+ @sod = sod
139
+ @range_begin = range_begin
140
+ super()
141
+ end
142
+
143
+ # Returns the current second of day advanced by 1 second
144
+ def succ
145
+ TimeOfDayRangeElement.new(sod: @sod + 1, range_begin: @range_begin)
146
+ end
147
+
148
+ # Compares one TimeOfDayRangeElement to another
149
+ # @since 2.4.0
150
+ # @return [Number, nil] -1,0,1 if other is less than, equal to, or greater than this TimeOfDay
151
+ def <=>(other)
152
+ other_second_of_day = to_second_of_day(other)
153
+ logger.trace do
154
+ "SOD(#{sod}) "\
155
+ "other SOD(#{other_second_of_day}) "\
156
+ "Other Class (#{other.class}) "\
157
+ "Result (#{sod <=> other_second_of_day})"
158
+ end
159
+ sod <=> other_second_of_day
160
+ end
161
+
162
+ private
163
+
164
+ #
165
+ # Convert object to the seconds of a day they reprsent
166
+ #
167
+ # @param [Object] object TimeofDay,String,Time, or TimeOfDayRangeElement to convert
168
+ #
169
+ # @return [Integer] seconds of day represented by supplied object
170
+ #
171
+ def to_second_of_day(object)
172
+ case object
173
+ when TimeOfDay then adjust_second_of_day(object.local_time.to_second_of_day)
174
+ when String then adjust_second_of_day(TimeOfDay.parse(object).local_time.to_second_of_day)
175
+ when Time then adjust_second_of_day(TimeOfDay.new(h: object.hour, m: object.min,
176
+ s: object.sec).local_time.to_second_of_day)
177
+ when TimeOfDayRangeElement then object.sod
178
+ else raise ArgumentError, 'Supplied argument cannot be converted into Time Of Day Object'
179
+ end
180
+ end
181
+
182
+ def adjust_second_of_day(second_of_day)
183
+ second_of_day += NUM_SECONDS_IN_DAY if second_of_day < @range_begin
184
+ second_of_day
185
+ end
186
+ end
187
+
188
+ # Creates a range that can be compared against time of day objects or strings
189
+ # to see if they are within the range
190
+ # @since 2.4.0
191
+ # @return Range object representing a TimeOfDay Range
192
+ module_function
193
+
194
+ def between(range)
195
+ raise ArgumentError, 'Supplied object must be a range' unless range.is_a? Range
196
+
197
+ start = to_time_of_day(range.begin)
198
+ ending = to_time_of_day(range.end)
199
+
200
+ start_sod = start.local_time.to_second_of_day
201
+ ending_sod = ending.local_time.to_second_of_day
202
+ ending_sod += TimeOfDayRangeElement::NUM_SECONDS_IN_DAY if ending_sod < start_sod
203
+
204
+ start_range = TimeOfDayRangeElement.new(sod: start_sod, range_begin: start_sod)
205
+ ending_range = TimeOfDayRangeElement.new(sod: ending_sod, range_begin: start_sod)
206
+ range.exclude_end? ? (start_range...ending_range) : (start_range..ending_range)
207
+ end
208
+
209
+ #
210
+ # Convert object to TimeOfDay object
211
+ #
212
+ # @param [Object] object TimeOfDay or String to be converted
213
+ #
214
+ # @return [TimeOfDay] TimeOfDay created from supplied object
215
+ #
216
+ private_class_method def to_time_of_day(object)
217
+ case object
218
+ when String then TimeOfDay.parse(object)
219
+ else object
220
+ end
221
+ end
222
+
223
+ MIDNIGHT = TimeOfDay.midnight
224
+ NOON = TimeOfDay.noon
225
+ ALL_DAY = between(TimeOfDay.new(h: 0, m: 0, s: 0)..TimeOfDay.new(h: 23, m: 59, s: 59))
226
+ end
227
+ end
228
+ end