openhab-scripting 2.16.2 → 2.19.0

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab.rb +12 -16
  3. data/lib/openhab/core/entity_lookup.rb +173 -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/rollershutter_item.rb +179 -0
  14. data/lib/openhab/dsl/items/string_item.rb +120 -0
  15. data/lib/openhab/dsl/monkey_patch/actions/actions.rb +4 -0
  16. data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +32 -0
  17. data/lib/openhab/dsl/monkey_patch/events/events.rb +5 -0
  18. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +23 -0
  19. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +35 -0
  20. data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +33 -0
  21. data/lib/openhab/dsl/monkey_patch/items/contact_item.rb +61 -0
  22. data/lib/openhab/dsl/monkey_patch/items/dimmer_item.rb +193 -0
  23. data/lib/openhab/dsl/monkey_patch/items/group_item.rb +37 -0
  24. data/lib/openhab/dsl/monkey_patch/items/items.rb +133 -0
  25. data/lib/openhab/dsl/monkey_patch/items/metadata.rb +281 -0
  26. data/lib/openhab/dsl/monkey_patch/items/persistence.rb +70 -0
  27. data/lib/openhab/dsl/monkey_patch/items/switch_item.rb +95 -0
  28. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +39 -0
  29. data/lib/openhab/dsl/monkey_patch/ruby/range.rb +47 -0
  30. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +8 -0
  31. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +41 -0
  32. data/lib/openhab/dsl/monkey_patch/ruby/time.rb +32 -0
  33. data/lib/openhab/dsl/monkey_patch/types/decimal_type.rb +70 -0
  34. data/lib/openhab/dsl/monkey_patch/types/on_off_type.rb +51 -0
  35. data/lib/openhab/dsl/monkey_patch/types/open_closed_type.rb +36 -0
  36. data/lib/openhab/dsl/monkey_patch/types/percent_type.rb +32 -0
  37. data/lib/openhab/dsl/monkey_patch/types/quantity_type.rb +69 -0
  38. data/lib/openhab/dsl/monkey_patch/types/types.rb +9 -0
  39. data/lib/openhab/dsl/monkey_patch/types/up_down_type.rb +33 -0
  40. data/lib/openhab/dsl/persistence.rb +25 -0
  41. data/lib/openhab/dsl/rules/automation_rule.rb +342 -0
  42. data/lib/openhab/dsl/rules/guard.rb +134 -0
  43. data/lib/openhab/dsl/rules/property.rb +102 -0
  44. data/lib/openhab/dsl/rules/rule.rb +116 -0
  45. data/lib/openhab/dsl/rules/rule_config.rb +151 -0
  46. data/lib/openhab/dsl/rules/triggers/changed.rb +143 -0
  47. data/lib/openhab/dsl/rules/triggers/channel.rb +53 -0
  48. data/lib/openhab/dsl/rules/triggers/command.rb +104 -0
  49. data/lib/openhab/dsl/rules/triggers/cron.rb +177 -0
  50. data/lib/openhab/dsl/rules/triggers/trigger.rb +124 -0
  51. data/lib/openhab/dsl/rules/triggers/updated.rb +98 -0
  52. data/lib/openhab/dsl/states.rb +61 -0
  53. data/lib/openhab/dsl/things.rb +91 -0
  54. data/lib/openhab/dsl/time_of_day.rb +232 -0
  55. data/lib/openhab/dsl/timers.rb +77 -0
  56. data/lib/openhab/dsl/types/datetime.rb +326 -0
  57. data/lib/openhab/dsl/types/quantity.rb +290 -0
  58. data/lib/openhab/dsl/units.rb +39 -0
  59. data/lib/openhab/log/configuration.rb +21 -0
  60. data/lib/openhab/log/logger.rb +172 -0
  61. data/lib/openhab/version.rb +1 -1
  62. metadata +60 -58
  63. data/lib/openhab/configuration.rb +0 -16
  64. data/lib/openhab/core/cron.rb +0 -27
  65. data/lib/openhab/core/debug.rb +0 -34
  66. data/lib/openhab/core/dsl.rb +0 -51
  67. data/lib/openhab/core/dsl/actions.rb +0 -107
  68. data/lib/openhab/core/dsl/entities.rb +0 -147
  69. data/lib/openhab/core/dsl/group.rb +0 -102
  70. data/lib/openhab/core/dsl/items/items.rb +0 -51
  71. data/lib/openhab/core/dsl/items/number_item.rb +0 -323
  72. data/lib/openhab/core/dsl/items/string_item.rb +0 -122
  73. data/lib/openhab/core/dsl/monkey_patch/actions/actions.rb +0 -4
  74. data/lib/openhab/core/dsl/monkey_patch/actions/script_thing_actions.rb +0 -22
  75. data/lib/openhab/core/dsl/monkey_patch/events.rb +0 -5
  76. data/lib/openhab/core/dsl/monkey_patch/events/item_command.rb +0 -13
  77. data/lib/openhab/core/dsl/monkey_patch/events/item_state_changed.rb +0 -25
  78. data/lib/openhab/core/dsl/monkey_patch/events/thing_status_info.rb +0 -26
  79. data/lib/openhab/core/dsl/monkey_patch/items/contact_item.rb +0 -54
  80. data/lib/openhab/core/dsl/monkey_patch/items/dimmer_item.rb +0 -182
  81. data/lib/openhab/core/dsl/monkey_patch/items/group_item.rb +0 -27
  82. data/lib/openhab/core/dsl/monkey_patch/items/items.rb +0 -132
  83. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +0 -283
  84. data/lib/openhab/core/dsl/monkey_patch/items/persistence.rb +0 -72
  85. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +0 -87
  86. data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +0 -41
  87. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +0 -47
  88. data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +0 -7
  89. data/lib/openhab/core/dsl/monkey_patch/ruby/string.rb +0 -43
  90. data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +0 -60
  91. data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +0 -41
  92. data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +0 -25
  93. data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +0 -23
  94. data/lib/openhab/core/dsl/monkey_patch/types/quantity_type.rb +0 -58
  95. data/lib/openhab/core/dsl/monkey_patch/types/types.rb +0 -8
  96. data/lib/openhab/core/dsl/persistence.rb +0 -27
  97. data/lib/openhab/core/dsl/property.rb +0 -96
  98. data/lib/openhab/core/dsl/rule/automation_rule.rb +0 -345
  99. data/lib/openhab/core/dsl/rule/guard.rb +0 -136
  100. data/lib/openhab/core/dsl/rule/rule.rb +0 -117
  101. data/lib/openhab/core/dsl/rule/rule_config.rb +0 -153
  102. data/lib/openhab/core/dsl/rule/triggers/changed.rb +0 -145
  103. data/lib/openhab/core/dsl/rule/triggers/channel.rb +0 -55
  104. data/lib/openhab/core/dsl/rule/triggers/command.rb +0 -106
  105. data/lib/openhab/core/dsl/rule/triggers/cron.rb +0 -160
  106. data/lib/openhab/core/dsl/rule/triggers/trigger.rb +0 -126
  107. data/lib/openhab/core/dsl/rule/triggers/updated.rb +0 -100
  108. data/lib/openhab/core/dsl/states.rb +0 -63
  109. data/lib/openhab/core/dsl/things.rb +0 -93
  110. data/lib/openhab/core/dsl/time_of_day.rb +0 -231
  111. data/lib/openhab/core/dsl/timers.rb +0 -79
  112. data/lib/openhab/core/dsl/types/quantity.rb +0 -292
  113. data/lib/openhab/core/dsl/units.rb +0 -41
  114. data/lib/openhab/core/log.rb +0 -170
  115. data/lib/openhab/core/patch_load_path.rb +0 -7
  116. data/lib/openhab/core/startup_delay.rb +0 -23
  117. 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