openhab-scripting 2.16.1 → 2.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab.rb +12 -16
  3. data/lib/openhab/core/entity_lookup.rb +168 -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 +49 -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/datetime_item.rb +97 -0
  11. data/lib/openhab/dsl/items/items.rb +46 -0
  12. data/lib/openhab/dsl/items/number_item.rb +352 -0
  13. data/lib/openhab/dsl/items/string_item.rb +120 -0
  14. data/lib/openhab/dsl/monkey_patch/actions/actions.rb +4 -0
  15. data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +32 -0
  16. data/lib/openhab/dsl/monkey_patch/events/events.rb +5 -0
  17. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +23 -0
  18. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +35 -0
  19. data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +33 -0
  20. data/lib/openhab/dsl/monkey_patch/items/contact_item.rb +61 -0
  21. data/lib/openhab/dsl/monkey_patch/items/dimmer_item.rb +193 -0
  22. data/lib/openhab/dsl/monkey_patch/items/group_item.rb +37 -0
  23. data/lib/openhab/dsl/monkey_patch/items/items.rb +133 -0
  24. data/lib/openhab/dsl/monkey_patch/items/metadata.rb +281 -0
  25. data/lib/openhab/dsl/monkey_patch/items/persistence.rb +70 -0
  26. data/lib/openhab/dsl/monkey_patch/items/switch_item.rb +95 -0
  27. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +39 -0
  28. data/lib/openhab/dsl/monkey_patch/ruby/range.rb +47 -0
  29. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +8 -0
  30. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +41 -0
  31. data/lib/openhab/dsl/monkey_patch/ruby/time.rb +32 -0
  32. data/lib/openhab/dsl/monkey_patch/types/decimal_type.rb +70 -0
  33. data/lib/openhab/dsl/monkey_patch/types/on_off_type.rb +51 -0
  34. data/lib/openhab/dsl/monkey_patch/types/open_closed_type.rb +36 -0
  35. data/lib/openhab/dsl/monkey_patch/types/percent_type.rb +32 -0
  36. data/lib/openhab/dsl/monkey_patch/types/quantity_type.rb +69 -0
  37. data/lib/openhab/dsl/monkey_patch/types/types.rb +8 -0
  38. data/lib/openhab/dsl/persistence.rb +25 -0
  39. data/lib/openhab/dsl/rules/automation_rule.rb +342 -0
  40. data/lib/openhab/dsl/rules/guard.rb +134 -0
  41. data/lib/openhab/dsl/rules/property.rb +102 -0
  42. data/lib/openhab/dsl/rules/rule.rb +116 -0
  43. data/lib/openhab/dsl/rules/rule_config.rb +151 -0
  44. data/lib/openhab/dsl/rules/triggers/changed.rb +143 -0
  45. data/lib/openhab/dsl/rules/triggers/channel.rb +53 -0
  46. data/lib/openhab/dsl/rules/triggers/command.rb +104 -0
  47. data/lib/openhab/dsl/rules/triggers/cron.rb +177 -0
  48. data/lib/openhab/dsl/rules/triggers/trigger.rb +124 -0
  49. data/lib/openhab/dsl/rules/triggers/updated.rb +98 -0
  50. data/lib/openhab/dsl/states.rb +61 -0
  51. data/lib/openhab/dsl/things.rb +91 -0
  52. data/lib/openhab/dsl/time_of_day.rb +232 -0
  53. data/lib/openhab/dsl/timers.rb +77 -0
  54. data/lib/openhab/dsl/types/datetime.rb +326 -0
  55. data/lib/openhab/dsl/types/quantity.rb +290 -0
  56. data/lib/openhab/dsl/units.rb +39 -0
  57. data/lib/openhab/log/configuration.rb +21 -0
  58. data/lib/openhab/log/logger.rb +172 -0
  59. data/lib/openhab/version.rb +1 -1
  60. metadata +58 -58
  61. data/lib/openhab/configuration.rb +0 -16
  62. data/lib/openhab/core/cron.rb +0 -27
  63. data/lib/openhab/core/debug.rb +0 -34
  64. data/lib/openhab/core/dsl.rb +0 -51
  65. data/lib/openhab/core/dsl/actions.rb +0 -107
  66. data/lib/openhab/core/dsl/entities.rb +0 -140
  67. data/lib/openhab/core/dsl/group.rb +0 -93
  68. data/lib/openhab/core/dsl/items/items.rb +0 -51
  69. data/lib/openhab/core/dsl/items/number_item.rb +0 -323
  70. data/lib/openhab/core/dsl/items/string_item.rb +0 -122
  71. data/lib/openhab/core/dsl/monkey_patch/actions/actions.rb +0 -4
  72. data/lib/openhab/core/dsl/monkey_patch/actions/script_thing_actions.rb +0 -22
  73. data/lib/openhab/core/dsl/monkey_patch/events.rb +0 -5
  74. data/lib/openhab/core/dsl/monkey_patch/events/item_command.rb +0 -13
  75. data/lib/openhab/core/dsl/monkey_patch/events/item_state_changed.rb +0 -25
  76. data/lib/openhab/core/dsl/monkey_patch/events/thing_status_info.rb +0 -26
  77. data/lib/openhab/core/dsl/monkey_patch/items/contact_item.rb +0 -54
  78. data/lib/openhab/core/dsl/monkey_patch/items/dimmer_item.rb +0 -182
  79. data/lib/openhab/core/dsl/monkey_patch/items/group_item.rb +0 -27
  80. data/lib/openhab/core/dsl/monkey_patch/items/items.rb +0 -132
  81. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +0 -283
  82. data/lib/openhab/core/dsl/monkey_patch/items/persistence.rb +0 -72
  83. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +0 -87
  84. data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +0 -41
  85. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +0 -47
  86. data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +0 -7
  87. data/lib/openhab/core/dsl/monkey_patch/ruby/string.rb +0 -43
  88. data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +0 -60
  89. data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +0 -41
  90. data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +0 -25
  91. data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +0 -23
  92. data/lib/openhab/core/dsl/monkey_patch/types/quantity_type.rb +0 -58
  93. data/lib/openhab/core/dsl/monkey_patch/types/types.rb +0 -8
  94. data/lib/openhab/core/dsl/persistence.rb +0 -27
  95. data/lib/openhab/core/dsl/property.rb +0 -96
  96. data/lib/openhab/core/dsl/rule/automation_rule.rb +0 -348
  97. data/lib/openhab/core/dsl/rule/guard.rb +0 -136
  98. data/lib/openhab/core/dsl/rule/rule.rb +0 -117
  99. data/lib/openhab/core/dsl/rule/rule_config.rb +0 -153
  100. data/lib/openhab/core/dsl/rule/triggers/changed.rb +0 -145
  101. data/lib/openhab/core/dsl/rule/triggers/channel.rb +0 -55
  102. data/lib/openhab/core/dsl/rule/triggers/command.rb +0 -106
  103. data/lib/openhab/core/dsl/rule/triggers/cron.rb +0 -160
  104. data/lib/openhab/core/dsl/rule/triggers/trigger.rb +0 -126
  105. data/lib/openhab/core/dsl/rule/triggers/updated.rb +0 -100
  106. data/lib/openhab/core/dsl/states.rb +0 -63
  107. data/lib/openhab/core/dsl/things.rb +0 -93
  108. data/lib/openhab/core/dsl/time_of_day.rb +0 -231
  109. data/lib/openhab/core/dsl/timers.rb +0 -79
  110. data/lib/openhab/core/dsl/types/quantity.rb +0 -292
  111. data/lib/openhab/core/dsl/units.rb +0 -41
  112. data/lib/openhab/core/log.rb +0 -170
  113. data/lib/openhab/core/patch_load_path.rb +0 -7
  114. data/lib/openhab/core/startup_delay.rb +0 -23
  115. 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,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+ require 'openhab/log/logger'
5
+ require 'openhab/dsl/items/datetime_item'
6
+ require 'openhab/dsl/types/datetime'
7
+ require 'time'
8
+
9
+ module OpenHAB
10
+ module DSL
11
+ # Times without specific dates e.g. 6:00:00
12
+ # @author Brian O'Connell
13
+ # @since 0.0.1
14
+ module TimeOfDay
15
+ java_import java.time.LocalTime
16
+ java_import java.time.format.DateTimeFormatterBuilder
17
+ java_import java.util.Locale
18
+
19
+ # Class that encapsulates a Time of Day, often viewed as hour-minute-second
20
+ # @author Brian O'Connell
21
+ # @since 0.0.1
22
+ class TimeOfDay
23
+ include Comparable
24
+
25
+ # Immutable Java object containing Time Of Day
26
+ # @return [Java.Time.LocalTime] reprsenting the Time Of Day
27
+ attr_reader :local_time
28
+
29
+ # Constructs a TimeOfDay representing the time when called
30
+ # @since 0.0.1
31
+ # @return [TimeOfDay] representing time when method was invoked
32
+ def self.now
33
+ now = LocalTime.now()
34
+ TimeOfDay.new(h: now.hour, m: now.minute, s: now.second)
35
+ end
36
+
37
+ # Constructs a TimeOfDay representing midnight
38
+ # @since 0.0.1
39
+ # @return [TimeOfDay] representing midnight
40
+ def self.midnight
41
+ TimeOfDay.new(h: 0, m: 0, s: 0)
42
+ end
43
+
44
+ # Constructs a TimeOfDay representing noon
45
+ # @since 0.0.1
46
+ # @return [TimeOfDay] representing noon
47
+ def self.noon
48
+ TimeOfDay.new(h: 12, m: 0, s: 0)
49
+ end
50
+
51
+ # Constructs a TimeOfDay representing the time when called
52
+ # @since 0.0.1
53
+ # @param [String] string representation of TimeOfDay. Valid formats include "HH:MM:SS", "HH:MM",
54
+ # "H:MM", "HH", "H", "H:MM am"
55
+ # @return [TimeOfDay] object created by parsing supplied string
56
+ def self.parse(string)
57
+ format = /(am|pm)$/i.match?(string) ? 'h[:mm[:ss]][ ]a' : 'H[:mm[:ss]]'
58
+ local_time = LocalTime.parse(string, DateTimeFormatterBuilder.new
59
+ .parseCaseInsensitive.appendPattern(format).toFormatter(Locale::ENGLISH))
60
+ TimeOfDay.new(h: local_time.hour, m: local_time.minute, s: local_time.second)
61
+ rescue java.time.format.DateTimeParseException => e
62
+ raise ArgumentError, e.message
63
+ end
64
+
65
+ # Constructs a TimeOfDay representing the time when called
66
+ # @since 0.0.1
67
+ # @option opts [Number] :h Hour of the day, defaults to 0
68
+ # @option opts [Number] :m Minute of the day, defaults to 0
69
+ # @option opts [Number] :s Second of the day, defaults to 0
70
+ # @return [TimeOfDay] representing time when method was invoked
71
+ # rubocop: disable Naming/MethodParameterName
72
+ # This method has a better feel with short parameter names
73
+ def initialize(h: 0, m: 0, s: 0)
74
+ @local_time = LocalTime.of(h, m, s)
75
+ freeze
76
+ end
77
+ # rubocop: enable Naming/MethodParameterName
78
+
79
+ # Returns true if the time falls within a range
80
+ def between?(range)
81
+ between(range).cover? self
82
+ end
83
+
84
+ # Returns the hour of the TimeOfDay
85
+ # @since 0.0.1
86
+ # @return [Number] Hour of the day, from 0 to 23
87
+ def hour
88
+ @local_time.hour
89
+ end
90
+
91
+ # Returns the minute of the TimeOfDay
92
+ # @since 0.0.1
93
+ # @return [Number] minute of the day, from 0 to 59
94
+ def minute
95
+ @local_time.minute
96
+ end
97
+
98
+ # Returns the second of the TimeOfDay
99
+ # @since 0.0.1
100
+ # @return [Number] second of the day, from 0 to 59
101
+ def second
102
+ @local_time.second
103
+ end
104
+
105
+ # Returns the string representation of the TimeOfDay
106
+ # @since 0.0.1
107
+ # @return [String] in any of the following formats depending on time representation HH:mm, HH:mm:ss,
108
+ # HH:mm:ss.SSS, HH:mm:ss.SSSSSS, HH:mm:ss.SSSSSSSSS
109
+ def to_s
110
+ @local_time.to_s
111
+ end
112
+
113
+ # Compares one TimeOfDay to another
114
+ # @since 0.0.1
115
+ # @return [Number, nil] -1,0,1 if other TimeOfDay is less than, equal to, or greater than this TimeOfDay
116
+ # or nil if an object other than TimeOfDay is provided
117
+ def <=>(other)
118
+ case other
119
+ when TimeOfDay
120
+ @local_time.compare_to(other.local_time)
121
+ when String
122
+ @local_time.compare_to(TimeOfDay.parse(other).local_time)
123
+ else
124
+ -(other <=> self)
125
+ end
126
+ end
127
+ end
128
+
129
+ # Modules that refines the Ruby Range object cover? and include? methods to support TimeOfDay ranges
130
+ class TimeOfDayRangeElement < Numeric
131
+ include Comparable
132
+ include OpenHAB::Log
133
+
134
+ NUM_SECONDS_IN_DAY = (60 * 60 * 24)
135
+
136
+ attr_reader :sod
137
+
138
+ def initialize(sod:, range_begin:)
139
+ @sod = sod
140
+ @range_begin = range_begin
141
+ super()
142
+ end
143
+
144
+ # Returns the current second of day advanced by 1 second
145
+ def succ
146
+ TimeOfDayRangeElement.new(sod: @sod + 1, range_begin: @range_begin)
147
+ end
148
+
149
+ # Compares one TimeOfDayRangeElement to another
150
+ # @since 2.4.0
151
+ # @return [Number, nil] -1,0,1 if other is less than, equal to, or greater than this TimeOfDay
152
+ def <=>(other)
153
+ other_second_of_day = to_second_of_day(other)
154
+ logger.trace do
155
+ "SOD(#{sod}) "\
156
+ "other SOD(#{other_second_of_day}) "\
157
+ "Other Class (#{other.class}) "\
158
+ "Result (#{sod <=> other_second_of_day})"
159
+ end
160
+ sod <=> other_second_of_day
161
+ end
162
+
163
+ private
164
+
165
+ #
166
+ # Convert object to the seconds of a day they reprsent
167
+ #
168
+ # @param [Object] object TimeofDay,String,Time, or TimeOfDayRangeElement to convert
169
+ #
170
+ # @return [Integer] seconds of day represented by supplied object
171
+ #
172
+ def to_second_of_day(object)
173
+ case object
174
+ when TimeOfDay then adjust_second_of_day(object.local_time.to_second_of_day)
175
+ when String then adjust_second_of_day(TimeOfDay.parse(object).local_time.to_second_of_day)
176
+ when Time, OpenHAB::DSL::Types::DateTime, OpenHAB::DSL::Items::DateTimeItem
177
+ adjust_second_of_day(TimeOfDay.new(h: object.hour, m: object.min, s: object.sec)
178
+ .local_time.to_second_of_day)
179
+ when TimeOfDayRangeElement then object.sod
180
+ else raise ArgumentError, 'Supplied argument cannot be converted into Time Of Day Object'
181
+ end
182
+ end
183
+
184
+ def adjust_second_of_day(second_of_day)
185
+ second_of_day += NUM_SECONDS_IN_DAY if second_of_day < @range_begin
186
+ second_of_day
187
+ end
188
+ end
189
+
190
+ # Creates a range that can be compared against time of day objects or strings
191
+ # to see if they are within the range
192
+ # @since 2.4.0
193
+ # @return Range object representing a TimeOfDay Range
194
+ module_function
195
+
196
+ def between(range)
197
+ raise ArgumentError, 'Supplied object must be a range' unless range.is_a? Range
198
+
199
+ start = to_time_of_day(range.begin)
200
+ ending = to_time_of_day(range.end)
201
+
202
+ start_sod = start.local_time.to_second_of_day
203
+ ending_sod = ending.local_time.to_second_of_day
204
+ ending_sod += TimeOfDayRangeElement::NUM_SECONDS_IN_DAY if ending_sod < start_sod
205
+
206
+ start_range = TimeOfDayRangeElement.new(sod: start_sod, range_begin: start_sod)
207
+ ending_range = TimeOfDayRangeElement.new(sod: ending_sod, range_begin: start_sod)
208
+ range.exclude_end? ? (start_range...ending_range) : (start_range..ending_range)
209
+ end
210
+
211
+ #
212
+ # Convert object to TimeOfDay object
213
+ #
214
+ # @param [Object] object TimeOfDay or String to be converted
215
+ #
216
+ # @return [TimeOfDay] TimeOfDay created from supplied object
217
+ #
218
+ private_class_method def to_time_of_day(object)
219
+ case object
220
+ when String then TimeOfDay.parse(object)
221
+ when Time, OpenHAB::DSL::Types::DateTime, OpenHAB::DSL::Items::DateTimeItem
222
+ TimeOfDay.new(h: object.hour, m: object.min, s: object.sec)
223
+ else object
224
+ end
225
+ end
226
+
227
+ MIDNIGHT = TimeOfDay.midnight
228
+ NOON = TimeOfDay.noon
229
+ ALL_DAY = between(TimeOfDay.new(h: 0, m: 0, s: 0)..TimeOfDay.new(h: 23, m: 59, s: 59))
230
+ end
231
+ end
232
+ end