openhab-scripting 2.15.0 → 2.16.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/openhab.rb +12 -16
- data/lib/openhab/core/entity_lookup.rb +162 -0
- data/lib/openhab/core/openhab_setup.rb +31 -0
- data/lib/openhab/core/osgi.rb +61 -0
- data/lib/openhab/dsl/actions.rb +105 -0
- data/lib/openhab/dsl/dsl.rb +47 -0
- data/lib/openhab/{core/dsl → dsl}/gems.rb +0 -1
- data/lib/openhab/dsl/group.rb +100 -0
- data/lib/openhab/dsl/items/items.rb +46 -0
- data/lib/openhab/dsl/items/number_item.rb +352 -0
- data/lib/openhab/dsl/items/string_item.rb +120 -0
- data/lib/openhab/dsl/monkey_patch/actions/actions.rb +4 -0
- data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +32 -0
- data/lib/openhab/dsl/monkey_patch/events/events.rb +5 -0
- data/lib/openhab/dsl/monkey_patch/events/item_command.rb +23 -0
- data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +35 -0
- data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +33 -0
- data/lib/openhab/dsl/monkey_patch/items/contact_item.rb +61 -0
- data/lib/openhab/dsl/monkey_patch/items/dimmer_item.rb +193 -0
- data/lib/openhab/dsl/monkey_patch/items/group_item.rb +37 -0
- data/lib/openhab/dsl/monkey_patch/items/items.rb +133 -0
- data/lib/openhab/dsl/monkey_patch/items/metadata.rb +281 -0
- data/lib/openhab/dsl/monkey_patch/items/persistence.rb +70 -0
- data/lib/openhab/dsl/monkey_patch/items/switch_item.rb +95 -0
- data/lib/openhab/dsl/monkey_patch/ruby/number.rb +39 -0
- data/lib/openhab/dsl/monkey_patch/ruby/range.rb +47 -0
- data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +7 -0
- data/lib/openhab/dsl/monkey_patch/ruby/string.rb +41 -0
- data/lib/openhab/dsl/monkey_patch/types/decimal_type.rb +70 -0
- data/lib/openhab/dsl/monkey_patch/types/on_off_type.rb +51 -0
- data/lib/openhab/dsl/monkey_patch/types/open_closed_type.rb +36 -0
- data/lib/openhab/dsl/monkey_patch/types/percent_type.rb +32 -0
- data/lib/openhab/dsl/monkey_patch/types/quantity_type.rb +69 -0
- data/lib/openhab/dsl/monkey_patch/types/types.rb +8 -0
- data/lib/openhab/dsl/persistence.rb +25 -0
- data/lib/openhab/dsl/rules/automation_rule.rb +342 -0
- data/lib/openhab/dsl/rules/guard.rb +134 -0
- data/lib/openhab/dsl/rules/property.rb +102 -0
- data/lib/openhab/dsl/rules/rule.rb +116 -0
- data/lib/openhab/dsl/rules/rule_config.rb +151 -0
- data/lib/openhab/dsl/rules/triggers/changed.rb +143 -0
- data/lib/openhab/dsl/rules/triggers/channel.rb +53 -0
- data/lib/openhab/dsl/rules/triggers/command.rb +104 -0
- data/lib/openhab/dsl/rules/triggers/cron.rb +177 -0
- data/lib/openhab/dsl/rules/triggers/trigger.rb +124 -0
- data/lib/openhab/dsl/rules/triggers/updated.rb +98 -0
- data/lib/openhab/dsl/states.rb +61 -0
- data/lib/openhab/dsl/things.rb +91 -0
- data/lib/openhab/dsl/time_of_day.rb +228 -0
- data/lib/openhab/dsl/timers.rb +77 -0
- data/lib/openhab/dsl/types/quantity.rb +290 -0
- data/lib/openhab/dsl/units.rb +39 -0
- data/lib/openhab/log/configuration.rb +21 -0
- data/lib/openhab/log/logger.rb +172 -0
- data/lib/openhab/version.rb +1 -1
- metadata +55 -56
- data/lib/openhab/configuration.rb +0 -16
- data/lib/openhab/core/cron.rb +0 -27
- data/lib/openhab/core/debug.rb +0 -34
- data/lib/openhab/core/dsl.rb +0 -51
- data/lib/openhab/core/dsl/actions.rb +0 -107
- data/lib/openhab/core/dsl/entities.rb +0 -140
- data/lib/openhab/core/dsl/group.rb +0 -93
- data/lib/openhab/core/dsl/items/items.rb +0 -51
- data/lib/openhab/core/dsl/items/number_item.rb +0 -318
- data/lib/openhab/core/dsl/items/string_item.rb +0 -120
- data/lib/openhab/core/dsl/monkey_patch/actions/actions.rb +0 -4
- data/lib/openhab/core/dsl/monkey_patch/actions/script_thing_actions.rb +0 -22
- data/lib/openhab/core/dsl/monkey_patch/events.rb +0 -5
- data/lib/openhab/core/dsl/monkey_patch/events/item_command.rb +0 -13
- data/lib/openhab/core/dsl/monkey_patch/events/item_state_changed.rb +0 -25
- data/lib/openhab/core/dsl/monkey_patch/events/thing_status_info.rb +0 -26
- data/lib/openhab/core/dsl/monkey_patch/items/contact_item.rb +0 -54
- data/lib/openhab/core/dsl/monkey_patch/items/dimmer_item.rb +0 -167
- data/lib/openhab/core/dsl/monkey_patch/items/group_item.rb +0 -27
- data/lib/openhab/core/dsl/monkey_patch/items/items.rb +0 -132
- data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +0 -283
- data/lib/openhab/core/dsl/monkey_patch/items/persistence.rb +0 -72
- data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +0 -87
- data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +0 -41
- data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +0 -47
- data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +0 -6
- data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +0 -24
- data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +0 -41
- data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +0 -25
- data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +0 -23
- data/lib/openhab/core/dsl/monkey_patch/types/types.rb +0 -7
- data/lib/openhab/core/dsl/persistence.rb +0 -27
- data/lib/openhab/core/dsl/property.rb +0 -96
- data/lib/openhab/core/dsl/rule/automation_rule.rb +0 -348
- data/lib/openhab/core/dsl/rule/guard.rb +0 -136
- data/lib/openhab/core/dsl/rule/rule.rb +0 -117
- data/lib/openhab/core/dsl/rule/rule_config.rb +0 -153
- data/lib/openhab/core/dsl/rule/triggers/changed.rb +0 -145
- data/lib/openhab/core/dsl/rule/triggers/channel.rb +0 -55
- data/lib/openhab/core/dsl/rule/triggers/command.rb +0 -106
- data/lib/openhab/core/dsl/rule/triggers/cron.rb +0 -160
- data/lib/openhab/core/dsl/rule/triggers/trigger.rb +0 -126
- data/lib/openhab/core/dsl/rule/triggers/updated.rb +0 -100
- data/lib/openhab/core/dsl/states.rb +0 -63
- data/lib/openhab/core/dsl/things.rb +0 -93
- data/lib/openhab/core/dsl/time_of_day.rb +0 -229
- data/lib/openhab/core/dsl/timers.rb +0 -79
- data/lib/openhab/core/dsl/types/quantity.rb +0 -292
- data/lib/openhab/core/dsl/units.rb +0 -41
- data/lib/openhab/core/log.rb +0 -170
- data/lib/openhab/core/patch_load_path.rb +0 -7
- data/lib/openhab/core/startup_delay.rb +0 -23
- 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
|