openhab-jrubyscripting 5.0.0.rc1
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.
- checksums.yaml +7 -0
- data/lib/openhab/core/actions.rb +163 -0
- data/lib/openhab/core/entity_lookup.rb +144 -0
- data/lib/openhab/core/events/abstract_event.rb +17 -0
- data/lib/openhab/core/events/item_channel_link.rb +36 -0
- data/lib/openhab/core/events/item_command_event.rb +78 -0
- data/lib/openhab/core/events/item_event.rb +22 -0
- data/lib/openhab/core/events/item_state_changed_event.rb +52 -0
- data/lib/openhab/core/events/item_state_event.rb +51 -0
- data/lib/openhab/core/events/thing.rb +29 -0
- data/lib/openhab/core/events/thing_status_info_event.rb +53 -0
- data/lib/openhab/core/events.rb +10 -0
- data/lib/openhab/core/items/accepted_data_types.rb +29 -0
- data/lib/openhab/core/items/color_item.rb +52 -0
- data/lib/openhab/core/items/contact_item.rb +52 -0
- data/lib/openhab/core/items/date_time_item.rb +58 -0
- data/lib/openhab/core/items/dimmer_item.rb +148 -0
- data/lib/openhab/core/items/generic_item.rb +344 -0
- data/lib/openhab/core/items/group_item.rb +174 -0
- data/lib/openhab/core/items/image_item.rb +109 -0
- data/lib/openhab/core/items/location_item.rb +34 -0
- data/lib/openhab/core/items/metadata/hash.rb +390 -0
- data/lib/openhab/core/items/metadata/namespace_hash.rb +469 -0
- data/lib/openhab/core/items/metadata.rb +11 -0
- data/lib/openhab/core/items/number_item.rb +62 -0
- data/lib/openhab/core/items/numeric_item.rb +22 -0
- data/lib/openhab/core/items/persistence.rb +327 -0
- data/lib/openhab/core/items/player_item.rb +66 -0
- data/lib/openhab/core/items/proxy.rb +59 -0
- data/lib/openhab/core/items/registry.rb +66 -0
- data/lib/openhab/core/items/rollershutter_item.rb +68 -0
- data/lib/openhab/core/items/semantics/enumerable.rb +152 -0
- data/lib/openhab/core/items/semantics.rb +476 -0
- data/lib/openhab/core/items/state_storage.rb +53 -0
- data/lib/openhab/core/items/string_item.rb +28 -0
- data/lib/openhab/core/items/switch_item.rb +78 -0
- data/lib/openhab/core/items.rb +114 -0
- data/lib/openhab/core/lazy_array.rb +52 -0
- data/lib/openhab/core/profile_factory.rb +118 -0
- data/lib/openhab/core/script_handling.rb +55 -0
- data/lib/openhab/core/things/channel.rb +48 -0
- data/lib/openhab/core/things/channel_uid.rb +51 -0
- data/lib/openhab/core/things/item_channel_link.rb +33 -0
- data/lib/openhab/core/things/profile_callback.rb +52 -0
- data/lib/openhab/core/things/proxy.rb +69 -0
- data/lib/openhab/core/things/registry.rb +46 -0
- data/lib/openhab/core/things/thing.rb +194 -0
- data/lib/openhab/core/things.rb +22 -0
- data/lib/openhab/core/timer.rb +128 -0
- data/lib/openhab/core/types/comparable_type.rb +23 -0
- data/lib/openhab/core/types/date_time_type.rb +259 -0
- data/lib/openhab/core/types/decimal_type.rb +192 -0
- data/lib/openhab/core/types/hsb_type.rb +183 -0
- data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
- data/lib/openhab/core/types/next_previous_type.rb +34 -0
- data/lib/openhab/core/types/numeric_type.rb +52 -0
- data/lib/openhab/core/types/on_off_type.rb +46 -0
- data/lib/openhab/core/types/open_closed_type.rb +41 -0
- data/lib/openhab/core/types/percent_type.rb +95 -0
- data/lib/openhab/core/types/play_pause_type.rb +38 -0
- data/lib/openhab/core/types/point_type.rb +117 -0
- data/lib/openhab/core/types/quantity_type.rb +327 -0
- data/lib/openhab/core/types/raw_type.rb +26 -0
- data/lib/openhab/core/types/refresh_type.rb +27 -0
- data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
- data/lib/openhab/core/types/stop_move_type.rb +34 -0
- data/lib/openhab/core/types/string_type.rb +76 -0
- data/lib/openhab/core/types/type.rb +117 -0
- data/lib/openhab/core/types/un_def_type.rb +38 -0
- data/lib/openhab/core/types/up_down_type.rb +50 -0
- data/lib/openhab/core/types.rb +69 -0
- data/lib/openhab/core/uid.rb +36 -0
- data/lib/openhab/core.rb +85 -0
- data/lib/openhab/core_ext/java/duration.rb +115 -0
- data/lib/openhab/core_ext/java/local_date.rb +93 -0
- data/lib/openhab/core_ext/java/local_time.rb +106 -0
- data/lib/openhab/core_ext/java/month.rb +59 -0
- data/lib/openhab/core_ext/java/month_day.rb +105 -0
- data/lib/openhab/core_ext/java/period.rb +103 -0
- data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
- data/lib/openhab/core_ext/java/time.rb +58 -0
- data/lib/openhab/core_ext/java/unit.rb +15 -0
- data/lib/openhab/core_ext/java/zoned_date_time.rb +116 -0
- data/lib/openhab/core_ext/ruby/array.rb +21 -0
- data/lib/openhab/core_ext/ruby/class.rb +15 -0
- data/lib/openhab/core_ext/ruby/date.rb +89 -0
- data/lib/openhab/core_ext/ruby/numeric.rb +190 -0
- data/lib/openhab/core_ext/ruby/range.rb +70 -0
- data/lib/openhab/core_ext/ruby/time.rb +104 -0
- data/lib/openhab/core_ext.rb +18 -0
- data/lib/openhab/dsl/events/watch_event.rb +18 -0
- data/lib/openhab/dsl/events.rb +9 -0
- data/lib/openhab/dsl/gems.rb +3 -0
- data/lib/openhab/dsl/items/builder.rb +618 -0
- data/lib/openhab/dsl/items/ensure.rb +93 -0
- data/lib/openhab/dsl/items/timed_command.rb +236 -0
- data/lib/openhab/dsl/rules/automation_rule.rb +308 -0
- data/lib/openhab/dsl/rules/builder.rb +1373 -0
- data/lib/openhab/dsl/rules/guard.rb +115 -0
- data/lib/openhab/dsl/rules/name_inference.rb +160 -0
- data/lib/openhab/dsl/rules/property.rb +76 -0
- data/lib/openhab/dsl/rules/rule_triggers.rb +96 -0
- data/lib/openhab/dsl/rules/terse.rb +63 -0
- data/lib/openhab/dsl/rules/triggers/changed.rb +169 -0
- data/lib/openhab/dsl/rules/triggers/channel.rb +57 -0
- data/lib/openhab/dsl/rules/triggers/command.rb +107 -0
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +161 -0
- data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +164 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +195 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +127 -0
- data/lib/openhab/dsl/rules/triggers/trigger.rb +56 -0
- data/lib/openhab/dsl/rules/triggers/updated.rb +130 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch.rb +55 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +155 -0
- data/lib/openhab/dsl/rules/triggers.rb +12 -0
- data/lib/openhab/dsl/rules.rb +29 -0
- data/lib/openhab/dsl/script_handling.rb +55 -0
- data/lib/openhab/dsl/things/builder.rb +263 -0
- data/lib/openhab/dsl/thread_local.rb +48 -0
- data/lib/openhab/dsl/timer_manager.rb +191 -0
- data/lib/openhab/dsl/version.rb +9 -0
- data/lib/openhab/dsl.rb +686 -0
- data/lib/openhab/log.rb +348 -0
- data/lib/openhab/osgi.rb +70 -0
- data/lib/openhab/rspec/configuration.rb +56 -0
- data/lib/openhab/rspec/example_group.rb +90 -0
- data/lib/openhab/rspec/helpers.rb +439 -0
- data/lib/openhab/rspec/hooks.rb +93 -0
- data/lib/openhab/rspec/jruby.rb +46 -0
- data/lib/openhab/rspec/karaf.rb +811 -0
- data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
- data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
- data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
- data/lib/openhab/rspec/mocks/metadata_provider.rb +75 -0
- data/lib/openhab/rspec/mocks/persistence_service.rb +140 -0
- data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
- data/lib/openhab/rspec/mocks/synchronous_executor.rb +56 -0
- data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
- data/lib/openhab/rspec/mocks/timer.rb +95 -0
- data/lib/openhab/rspec/openhab/core/actions.rb +26 -0
- data/lib/openhab/rspec/openhab/core/items/proxy.rb +27 -0
- data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
- data/lib/openhab/rspec/shell.rb +31 -0
- data/lib/openhab/rspec/suspend_rules.rb +60 -0
- data/lib/openhab/rspec.rb +17 -0
- data/lib/openhab/yard/cli/stats.rb +23 -0
- data/lib/openhab/yard/code_objects/group_object.rb +17 -0
- data/lib/openhab/yard/code_objects/java/base.rb +31 -0
- data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
- data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
- data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
- data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
- data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
- data/lib/openhab/yard/handlers/jruby/base.rb +49 -0
- data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
- data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
- data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +27 -0
- data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
- data/lib/openhab/yard/html_helper.rb +44 -0
- data/lib/openhab/yard/tags/constant_directive.rb +20 -0
- data/lib/openhab/yard/tags/group_directive.rb +24 -0
- data/lib/openhab/yard/tags/library.rb +3 -0
- data/lib/openhab/yard.rb +32 -0
- metadata +504 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "trigger"
|
|
4
|
+
|
|
5
|
+
module OpenHAB
|
|
6
|
+
module DSL
|
|
7
|
+
module Rules
|
|
8
|
+
module Triggers
|
|
9
|
+
# @!visibility private
|
|
10
|
+
#
|
|
11
|
+
# Creates channel triggers
|
|
12
|
+
#
|
|
13
|
+
class Channel < Trigger
|
|
14
|
+
# @return [String] A channel event trigger
|
|
15
|
+
CHANNEL_EVENT = "core.ChannelEventTrigger"
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Get an enumerator over the product of the channels and things and map them to a channel id
|
|
19
|
+
# @param [Object] channels to iterate over
|
|
20
|
+
# @param [Object] thing to combine with channels and iterate over
|
|
21
|
+
# @return [Enumerable] enumerable channel ids to trigger on
|
|
22
|
+
def self.channels(channels:, thing:)
|
|
23
|
+
logger.trace "Creating Channel/Thing Pairs for channels #{channels.inspect} and things #{thing.inspect}"
|
|
24
|
+
channels.flatten.product([thing].flatten)
|
|
25
|
+
.map { |channel_thing| channel_id(*channel_thing) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Get a channel id from a channel and thing
|
|
30
|
+
# @param [Object] channel part of channel id, get UID if object is a Channel
|
|
31
|
+
# @param [Object] thing part of channel id, get UID if object is a Thing
|
|
32
|
+
#
|
|
33
|
+
def self.channel_id(channel, thing)
|
|
34
|
+
channel = channel.uid if channel.is_a?(org.openhab.core.thing.Channel)
|
|
35
|
+
thing = thing.uid if thing.is_a?(Core::Things::Thing)
|
|
36
|
+
[thing, channel].compact.join(":")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Create a trigger for a channel
|
|
41
|
+
#
|
|
42
|
+
# @param [String] channel to look for triggers
|
|
43
|
+
# @param [String] trigger specific channel trigger to match
|
|
44
|
+
# @param [Object] attach object to be attached to the trigger
|
|
45
|
+
#
|
|
46
|
+
#
|
|
47
|
+
def trigger(channel:, trigger:, attach:)
|
|
48
|
+
config = { "channelUID" => channel }
|
|
49
|
+
config["event"] = trigger.to_s unless trigger.nil?
|
|
50
|
+
logger.trace "Creating Channel Trigger for channels #{channel.inspect} and config #{config.inspect}"
|
|
51
|
+
append_trigger(type: CHANNEL_EVENT, config: config, attach: attach)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "trigger"
|
|
4
|
+
|
|
5
|
+
module OpenHAB
|
|
6
|
+
module DSL
|
|
7
|
+
module Rules
|
|
8
|
+
module Triggers
|
|
9
|
+
# @!visibility private
|
|
10
|
+
#
|
|
11
|
+
# Creates command triggers
|
|
12
|
+
#
|
|
13
|
+
class Command < Trigger
|
|
14
|
+
#
|
|
15
|
+
# Create a received command trigger
|
|
16
|
+
#
|
|
17
|
+
# @param [Object] item item to create trigger for
|
|
18
|
+
# @param [Object] command to check against
|
|
19
|
+
# @param [Object] attach object to be attached to the trigger
|
|
20
|
+
#
|
|
21
|
+
# @return [Trigger] OpenHAB triggers
|
|
22
|
+
#
|
|
23
|
+
def trigger(item:, command:, attach:)
|
|
24
|
+
case command
|
|
25
|
+
when Range then range_trigger(item: item, command: command, attach: attach)
|
|
26
|
+
when Proc then proc_trigger(item: item, command: command, attach: attach)
|
|
27
|
+
else command_trigger(item: item, command: command, attach: attach)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# Creates a trigger with a range condition on the 'command' field
|
|
33
|
+
# @param [Object] item to create changed trigger on
|
|
34
|
+
# @param [Object] command to restrict trigger to
|
|
35
|
+
# @param [Object] attach object to be attached to the trigger
|
|
36
|
+
# @return [Trigger] OpenHAB trigger
|
|
37
|
+
#
|
|
38
|
+
def range_trigger(item:, command:, attach:)
|
|
39
|
+
command_range, * = Conditions::Proc.range_procs(command)
|
|
40
|
+
proc_trigger(item: item, command: command_range, attach: attach)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
# Creates a trigger with a proc condition on the 'command' field
|
|
45
|
+
# @param [Object] item to create changed trigger on
|
|
46
|
+
# @param [Object] command to restrict trigger to
|
|
47
|
+
# @param [Object] attach object to be attached to the trigger
|
|
48
|
+
# @return [Trigger] OpenHAB trigger
|
|
49
|
+
#
|
|
50
|
+
def proc_trigger(item:, command:, attach:)
|
|
51
|
+
conditions = Conditions::Proc.new(command: command)
|
|
52
|
+
command_trigger(item: item, command: nil, attach: attach, conditions: conditions)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Create a received trigger based on item type
|
|
57
|
+
#
|
|
58
|
+
# @param [Object] item to create trigger for
|
|
59
|
+
# @param [String] command to create trigger for
|
|
60
|
+
# @param [Object] attach object to be attached to the trigger
|
|
61
|
+
#
|
|
62
|
+
def command_trigger(item:, command:, attach: nil, conditions: nil)
|
|
63
|
+
type, config = if item.is_a?(GroupItem::Members)
|
|
64
|
+
group(group: item)
|
|
65
|
+
else
|
|
66
|
+
item(item: item)
|
|
67
|
+
end
|
|
68
|
+
config["command"] = command.to_s unless command.nil?
|
|
69
|
+
append_trigger(type: type, config: config, attach: attach, conditions: conditions)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# @return [String] item command trigger
|
|
75
|
+
ITEM_COMMAND = "core.ItemCommandTrigger"
|
|
76
|
+
|
|
77
|
+
# @return [String] A group command trigger for items in the group
|
|
78
|
+
GROUP_COMMAND = "core.GroupCommandTrigger"
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
# Create trigger for item commands
|
|
82
|
+
#
|
|
83
|
+
# @param [Item] item to create trigger for
|
|
84
|
+
#
|
|
85
|
+
# @return [Array<Hash,Trigger>] first element is hash of trigger config properties
|
|
86
|
+
# second element is trigger type
|
|
87
|
+
#
|
|
88
|
+
def item(item:)
|
|
89
|
+
[ITEM_COMMAND, { "itemName" => item.name }]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# Create trigger for group items
|
|
94
|
+
#
|
|
95
|
+
# @param [Group] group to create trigger for
|
|
96
|
+
#
|
|
97
|
+
# @return [Array<Hash,Trigger>] first element is hash of trigger config properties
|
|
98
|
+
# second element is trigger type
|
|
99
|
+
#
|
|
100
|
+
def group(group:)
|
|
101
|
+
[GROUP_COMMAND, { "groupName" => group.group.name }]
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenHAB
|
|
4
|
+
module DSL
|
|
5
|
+
module Rules
|
|
6
|
+
module Triggers
|
|
7
|
+
# @!visibility private
|
|
8
|
+
module Conditions
|
|
9
|
+
#
|
|
10
|
+
# this is a no-op condition which simply executes the provided block
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# Struct capturing data necessary for a conditional trigger
|
|
15
|
+
#
|
|
16
|
+
# TriggerDelay = Struct.new(:to, :from, :duration, :timer, :tracking_to, keyword_init: true) do
|
|
17
|
+
# def timer_active?
|
|
18
|
+
# timer&.active?
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
class Duration
|
|
22
|
+
#
|
|
23
|
+
# Create a new duration condition
|
|
24
|
+
# @param [Object] to optional condition on to state
|
|
25
|
+
# @param [Object] from optional condition on from state
|
|
26
|
+
# @param [java.time.temporal.TemporalAmount] duration to state must stay at specific value before triggering
|
|
27
|
+
#
|
|
28
|
+
def initialize(to:, from:, duration:)
|
|
29
|
+
to = Conditions::Proc.from_value(to)
|
|
30
|
+
from = Conditions::Proc.from_value(from)
|
|
31
|
+
@conditions = Conditions::Proc.new(to: to, from: from)
|
|
32
|
+
@duration = duration
|
|
33
|
+
@timer = nil
|
|
34
|
+
logger.trace "Created Duration Condition To(#{to}) From(#{from}) "\
|
|
35
|
+
"Conditions(#{@conditions}) Duration(#{@duration})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Process rule
|
|
39
|
+
# @param [Hash] inputs inputs from trigger
|
|
40
|
+
#
|
|
41
|
+
def process(mod:, inputs:, &block)
|
|
42
|
+
process_trigger_delay(mod, inputs, &block)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Cleanup any resources from the condition
|
|
46
|
+
#
|
|
47
|
+
# Cancels the timer, if it's active
|
|
48
|
+
def cleanup
|
|
49
|
+
@timer&.cancel
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Checks if there is an active timer
|
|
56
|
+
# @return [true, false] true if the timer exists and is active, false otherwise
|
|
57
|
+
def timer_active?
|
|
58
|
+
@timer&.active?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Check if trigger guards prevent rule execution
|
|
63
|
+
#
|
|
64
|
+
# @param [Map] inputs OpenHAB map object describing rule trigger
|
|
65
|
+
#
|
|
66
|
+
# @return [true,false] True if the rule should execute, false if trigger guard prevents execution
|
|
67
|
+
#
|
|
68
|
+
def check_trigger_guards(inputs)
|
|
69
|
+
new_state, old_state = retrieve_states(inputs)
|
|
70
|
+
@conditions.check_from(state: old_state) && @conditions.check_to(state: new_state)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# Rerieve the newState and oldState, alternatively newStatus and oldStatus
|
|
75
|
+
# from the input map
|
|
76
|
+
#
|
|
77
|
+
# @param [Map] inputs OpenHAB map object describing rule trigger
|
|
78
|
+
#
|
|
79
|
+
# @return [Array] An array of the values for [newState, oldState] or [newStatus, oldStatus]
|
|
80
|
+
#
|
|
81
|
+
def retrieve_states(inputs)
|
|
82
|
+
new_state = inputs["newState"] || thing_status_to_sym(inputs["newStatus"])
|
|
83
|
+
old_state = inputs["oldState"] || thing_status_to_sym(inputs["oldStatus"])
|
|
84
|
+
|
|
85
|
+
[new_state, old_state]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# Converts a ThingStatus object to a ruby Symbol
|
|
90
|
+
#
|
|
91
|
+
# @param [org.openhab.core.thing.ThingStatus] status A ThingStatus instance
|
|
92
|
+
#
|
|
93
|
+
# @return [Symbol] A corresponding symbol, in lower case
|
|
94
|
+
#
|
|
95
|
+
def thing_status_to_sym(status)
|
|
96
|
+
status&.to_s&.downcase&.to_sym
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#
|
|
100
|
+
# Process any matching trigger delays
|
|
101
|
+
#
|
|
102
|
+
# @param [Map] mod OpenHAB map object describing rule trigger
|
|
103
|
+
# @param [Map] inputs OpenHAB map object describing rule trigger
|
|
104
|
+
#
|
|
105
|
+
#
|
|
106
|
+
def process_trigger_delay(mod, inputs, &block)
|
|
107
|
+
if timer_active?
|
|
108
|
+
process_active_timer(inputs, mod, &block)
|
|
109
|
+
elsif check_trigger_guards(inputs)
|
|
110
|
+
logger.trace("Trigger Guards Matched for #{self}, delaying rule execution")
|
|
111
|
+
# Add timer and attach timer to delay object, and also state being tracked to so
|
|
112
|
+
# timer can be cancelled if state changes
|
|
113
|
+
# Also another timer should not be created if changed to same value again but instead rescheduled
|
|
114
|
+
create_trigger_delay_timer(inputs, mod, &block)
|
|
115
|
+
else
|
|
116
|
+
logger.trace("Trigger Guards did not match for #{self}, ignoring trigger.")
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
#
|
|
121
|
+
# Creates a timer for trigger delays
|
|
122
|
+
#
|
|
123
|
+
# @param [Hash] inputs rule trigger inputs
|
|
124
|
+
# @param [Hash] _mod rule trigger mods
|
|
125
|
+
#
|
|
126
|
+
#
|
|
127
|
+
def create_trigger_delay_timer(inputs, _mod)
|
|
128
|
+
logger.trace("Creating timer for trigger delay #{self}")
|
|
129
|
+
@timer = DSL.after(@duration) do
|
|
130
|
+
logger.trace("Delay Complete for #{self}, executing rule")
|
|
131
|
+
@timer = nil
|
|
132
|
+
yield
|
|
133
|
+
end
|
|
134
|
+
@tracking_to, = retrieve_states(inputs)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
#
|
|
138
|
+
# Process an active trigger timer
|
|
139
|
+
#
|
|
140
|
+
# @param [Hash] inputs rule trigger inputs
|
|
141
|
+
# @param [Hash] mod rule trigger mods
|
|
142
|
+
#
|
|
143
|
+
#
|
|
144
|
+
def process_active_timer(inputs, mod, &block)
|
|
145
|
+
state, = retrieve_states(inputs)
|
|
146
|
+
if state == @tracking_to
|
|
147
|
+
logger.trace("Item changed to #{state} for #{self}, rescheduling timer.")
|
|
148
|
+
@timer.reschedule(ZonedDateTime.now.plus(@duration))
|
|
149
|
+
else
|
|
150
|
+
logger.trace("Item changed to #{state} for #{self}, canceling timer.")
|
|
151
|
+
@timer.cancel
|
|
152
|
+
# Reprocess trigger delay after canceling to track new state (if guards matched, etc)
|
|
153
|
+
process_trigger_delay(mod, inputs, &block)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenHAB
|
|
4
|
+
module DSL
|
|
5
|
+
module Rules
|
|
6
|
+
module Triggers
|
|
7
|
+
# @!visibility private
|
|
8
|
+
module Conditions
|
|
9
|
+
#
|
|
10
|
+
# This creates trigger conditions that work on procs
|
|
11
|
+
# @param [Proc] from Proc
|
|
12
|
+
# @param [Proc] to Proc
|
|
13
|
+
#
|
|
14
|
+
class Proc
|
|
15
|
+
#
|
|
16
|
+
# Converts supplied ranges to procs that check range
|
|
17
|
+
# @param [Array] ranges objects to convert to range proc if they are ranges
|
|
18
|
+
# @return [Array] of procs or supplied arguments if argument was not of type Range
|
|
19
|
+
#
|
|
20
|
+
def self.range_procs(*ranges)
|
|
21
|
+
ranges.map { |range| range.is_a?(Range) ? range_proc(range) : range }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Create a range proc for the supplied range object
|
|
26
|
+
# @param [Range] range to build proc for
|
|
27
|
+
#
|
|
28
|
+
def self.range_proc(range)
|
|
29
|
+
logger.trace("Creating range proc for #{range}")
|
|
30
|
+
lambda do |val|
|
|
31
|
+
logger.trace("Range proc checking if #{val} is in #{range}")
|
|
32
|
+
range.cover? val
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Create an equality proc for the supplied range object
|
|
38
|
+
# @param [State] value to build proc for
|
|
39
|
+
#
|
|
40
|
+
def self.equality_proc(value)
|
|
41
|
+
logger.trace("Creating equality proc for #{value}")
|
|
42
|
+
lambda do |state|
|
|
43
|
+
logger.trace("Equality proc comparing #{value} against #{state}")
|
|
44
|
+
value == state
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Constructs a proc for the specific value type
|
|
50
|
+
# if the value is a proc return the proc
|
|
51
|
+
# if the value is a range create a range proc
|
|
52
|
+
# if the value is nil, return nil
|
|
53
|
+
# otherwise create an equality proc
|
|
54
|
+
# @param [Object] value to construct proc from
|
|
55
|
+
def self.from_value(value)
|
|
56
|
+
logger.trace("Creating proc for Value(#{value})")
|
|
57
|
+
return value if value.nil?
|
|
58
|
+
return value if value.is_a?(::Proc)
|
|
59
|
+
return range_proc(value) if value.is_a?(Range)
|
|
60
|
+
|
|
61
|
+
equality_proc(value)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
# Create a new Proc Condition that executes only if procs return true
|
|
66
|
+
# @param [Proc] from Proc to check against from value
|
|
67
|
+
# @param [Proc] to Proc to check against to value
|
|
68
|
+
#
|
|
69
|
+
def initialize(from: nil, to: nil, command: nil)
|
|
70
|
+
@from = from
|
|
71
|
+
@to = to
|
|
72
|
+
@command = command
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Proc that doesn't check any fields
|
|
76
|
+
ANY = Proc.new.freeze # this needs to be defined _after_ initialize so its instance variables are set
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# Process rule
|
|
80
|
+
# @param [Hash] inputs inputs from trigger
|
|
81
|
+
#
|
|
82
|
+
def process(mod:, inputs:) # rubocop:disable Lint/UnusedMethodArgument - mod is unused here but required
|
|
83
|
+
logger.trace("Checking #{inputs} against condition trigger #{self}")
|
|
84
|
+
yield if check_procs(inputs: inputs)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Cleanup any resources from the condition
|
|
88
|
+
def cleanup; end
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
# Check if command condition match the proc
|
|
92
|
+
# @param [Hash] inputs from trigger must be supplied if state is not supplied
|
|
93
|
+
# @return [true/false] depending on if from is set and matches supplied conditions
|
|
94
|
+
#
|
|
95
|
+
def check_command(inputs: nil)
|
|
96
|
+
command = input_state(inputs, "command")
|
|
97
|
+
logger.trace "Checking command(#{@command}) against command(#{command})"
|
|
98
|
+
check_proc(proc: @command, value: command)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
#
|
|
102
|
+
# Check if from condition match the proc
|
|
103
|
+
# @param [Hash] inputs from trigger must be supplied if state is not supplied
|
|
104
|
+
# @param [String] state if supplied proc will be passed state value for comparision
|
|
105
|
+
# @return [true/false] depending on if from is set and matches supplied conditions
|
|
106
|
+
#
|
|
107
|
+
def check_from(inputs: nil, state: nil)
|
|
108
|
+
state ||= input_state(inputs, "oldState")
|
|
109
|
+
logger.trace "Checking from(#{@from}) against state(#{state})"
|
|
110
|
+
check_proc(proc: @from, value: state)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#
|
|
114
|
+
# Check if to conditions match the proc
|
|
115
|
+
# @param [Hash] inputs from trigger must be supplied if state is not supplied
|
|
116
|
+
# @param [String] state if supplied proc will be passed state value for comparision
|
|
117
|
+
# @return [true/false] depending on if from is set and matches supplied conditions
|
|
118
|
+
#
|
|
119
|
+
def check_to(inputs: nil, state: nil)
|
|
120
|
+
state ||= input_state(inputs, "newState", "state")
|
|
121
|
+
logger.trace "Checking to(#{@to}) against state(#{state})"
|
|
122
|
+
check_proc(proc: @to, value: state)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Describe the Proc Condition as a string
|
|
126
|
+
# @return [String] string representation of proc condition
|
|
127
|
+
#
|
|
128
|
+
def to_s
|
|
129
|
+
"From:(#{@from}) To:(#{@to}) Command:(#{@command})"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
#
|
|
135
|
+
# Check all procs
|
|
136
|
+
# @param [Hash] inputs from event
|
|
137
|
+
# @return [true/false] true if all procs return true, false otherwise
|
|
138
|
+
def check_procs(inputs:)
|
|
139
|
+
check_from(inputs: inputs) && check_to(inputs: inputs) && check_command(inputs: inputs)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Check if a field matches the proc condition
|
|
143
|
+
# @param [Proc] proc to call
|
|
144
|
+
# @param [Hash] value to check
|
|
145
|
+
# @return [true,false] true if proc is nil or proc.call returns true, false otherwise
|
|
146
|
+
def check_proc(proc:, value:)
|
|
147
|
+
return true if proc.nil? || proc.call(value)
|
|
148
|
+
|
|
149
|
+
logger.trace("Skipped execution of rule because value #{value} evaluated false for (#{proc})")
|
|
150
|
+
false
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Get the first field from supplied fields in inputs
|
|
154
|
+
# @param [Hash] inputs containing fields
|
|
155
|
+
# @param [Array] fields array of fields to extract from inputs, first one found is returned
|
|
156
|
+
def input_state(inputs, *fields)
|
|
157
|
+
fields.map { |f| inputs[f] }.compact.first
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openhab/dsl/rules/triggers/trigger"
|
|
4
|
+
|
|
5
|
+
module OpenHAB
|
|
6
|
+
module DSL
|
|
7
|
+
module Rules
|
|
8
|
+
module Triggers
|
|
9
|
+
# @!visibility private
|
|
10
|
+
#
|
|
11
|
+
# Creates cron triggers
|
|
12
|
+
#
|
|
13
|
+
class Cron < Trigger
|
|
14
|
+
# Trigger ID for Watch Triggers
|
|
15
|
+
CRON_TRIGGER_MODULE_ID = "jsr223.jruby.CronTrigger"
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Returns a default map for cron expressions that fires every second
|
|
19
|
+
# This map is usually updated via merge by other methods to refine cron type triggers.
|
|
20
|
+
#
|
|
21
|
+
# @return [Hash] Map with symbols for :seconds, :minute, :hour, :dom, :month, :dow
|
|
22
|
+
# configured to fire every second
|
|
23
|
+
#
|
|
24
|
+
CRON_EXPRESSION_MAP =
|
|
25
|
+
{
|
|
26
|
+
second: "*",
|
|
27
|
+
minute: "*",
|
|
28
|
+
hour: "*",
|
|
29
|
+
dom: "?",
|
|
30
|
+
month: "*",
|
|
31
|
+
dow: "?",
|
|
32
|
+
year: "*"
|
|
33
|
+
}.freeze
|
|
34
|
+
private_constant :CRON_EXPRESSION_MAP
|
|
35
|
+
|
|
36
|
+
# @return [Hash] Map of days of the week from symbols to to OpenHAB cron strings
|
|
37
|
+
DAY_OF_WEEK_MAP = {
|
|
38
|
+
monday: "MON",
|
|
39
|
+
tuesday: "TUE",
|
|
40
|
+
wednesday: "WED",
|
|
41
|
+
thursday: "THU",
|
|
42
|
+
friday: "FRI",
|
|
43
|
+
saturday: "SAT",
|
|
44
|
+
sunday: "SUN"
|
|
45
|
+
}.freeze
|
|
46
|
+
private_constant :DAY_OF_WEEK_MAP
|
|
47
|
+
|
|
48
|
+
# @return [Hash] Converts the DAY_OF_WEEK_MAP to map used by Cron Expression
|
|
49
|
+
DAY_OF_WEEK_EXPRESSION_MAP = DAY_OF_WEEK_MAP.transform_values { |v| CRON_EXPRESSION_MAP.merge(dow: v) }
|
|
50
|
+
|
|
51
|
+
private_constant :DAY_OF_WEEK_EXPRESSION_MAP
|
|
52
|
+
|
|
53
|
+
# @return [Hash] Create a set of cron expressions based on different time intervals
|
|
54
|
+
EXPRESSION_MAP = {
|
|
55
|
+
second: CRON_EXPRESSION_MAP,
|
|
56
|
+
minute: CRON_EXPRESSION_MAP.merge(second: "0"),
|
|
57
|
+
hour: CRON_EXPRESSION_MAP.merge(second: "0", minute: "0"),
|
|
58
|
+
day: CRON_EXPRESSION_MAP.merge(second: "0", minute: "0", hour: "0"),
|
|
59
|
+
week: CRON_EXPRESSION_MAP.merge(second: "0", minute: "0", hour: "0", dow: "MON"),
|
|
60
|
+
month: CRON_EXPRESSION_MAP.merge(second: "0", minute: "0", hour: "0", dom: "1"),
|
|
61
|
+
year: CRON_EXPRESSION_MAP.merge(second: "0", minute: "0", hour: "0", dom: "1", month: "1")
|
|
62
|
+
}.merge(DAY_OF_WEEK_EXPRESSION_MAP).freeze
|
|
63
|
+
|
|
64
|
+
private_constant :EXPRESSION_MAP
|
|
65
|
+
|
|
66
|
+
#
|
|
67
|
+
# Create a cron map from a duration
|
|
68
|
+
#
|
|
69
|
+
# @param [Duration] duration
|
|
70
|
+
# @param [Object] at LocalTime or String representing time of day
|
|
71
|
+
#
|
|
72
|
+
# @return [Hash] map describing cron expression
|
|
73
|
+
#
|
|
74
|
+
def self.from_duration(duration, at)
|
|
75
|
+
raise ArgumentError, '"at" cannot be used with duration' if at
|
|
76
|
+
|
|
77
|
+
map_to_cron(duration_to_map(duration))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
# Create a cron map from a MonthDay
|
|
82
|
+
#
|
|
83
|
+
# @param [MonthDay] monthday a {MonthDay} object
|
|
84
|
+
# @param [Object] at LocalTime or String representing time of day
|
|
85
|
+
#
|
|
86
|
+
# @return [Hash] map describing cron expression
|
|
87
|
+
#
|
|
88
|
+
def self.from_monthday(monthday, at)
|
|
89
|
+
expression_map = EXPRESSION_MAP[:day].merge(month: monthday.month_value, dom: monthday.day_of_month)
|
|
90
|
+
expression_map = at_condition(expression_map, at) if at
|
|
91
|
+
map_to_cron(expression_map)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
#
|
|
95
|
+
# Create a cron map from a symbol
|
|
96
|
+
#
|
|
97
|
+
# @param [Symbol] symbol
|
|
98
|
+
# @param [Object] at LocalTime or String representing time of day
|
|
99
|
+
#
|
|
100
|
+
# @return [Hash] map describing cron expression created from symbol
|
|
101
|
+
#
|
|
102
|
+
def self.from_symbol(symbol, at)
|
|
103
|
+
expression_map = EXPRESSION_MAP[symbol]
|
|
104
|
+
expression_map = at_condition(expression_map, at) if at
|
|
105
|
+
map_to_cron(expression_map)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#
|
|
109
|
+
# Create a cron map from cron elements
|
|
110
|
+
#
|
|
111
|
+
# @param [Hash] fields Cron fields (second, minute, hour, dom, month, dow, year)
|
|
112
|
+
#
|
|
113
|
+
# @return [Hash] map describing cron expression
|
|
114
|
+
#
|
|
115
|
+
def self.from_fields(fields)
|
|
116
|
+
extra_fields = fields.keys - CRON_EXPRESSION_MAP.keys
|
|
117
|
+
unless extra_fields.empty?
|
|
118
|
+
raise ArgumentError,
|
|
119
|
+
"unknown keyword#{"s" if extra_fields.size > 1}: #{extra_fields.map(&:inspect).join(", ")}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
fields = fields.transform_values { |value| value.to_s.delete(" ") }
|
|
123
|
+
# find the first expression map that has a field from fields.
|
|
124
|
+
# this ensure more-specific fields get set to 0, not *
|
|
125
|
+
base_key = EXPRESSION_MAP.keys.find { |field, _| fields.key?(field) }
|
|
126
|
+
base_expression = EXPRESSION_MAP[base_key]
|
|
127
|
+
expression_map = base_expression.merge(fields)
|
|
128
|
+
|
|
129
|
+
map_to_cron(expression_map)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Map cron expression to to cron string
|
|
134
|
+
#
|
|
135
|
+
# @param [Map] map of cron expression
|
|
136
|
+
#
|
|
137
|
+
# @return [String] OpenHAB cron string
|
|
138
|
+
#
|
|
139
|
+
def self.map_to_cron(map)
|
|
140
|
+
%i[second minute hour dom month dow year].map { |field| map.fetch(field) }.join(" ")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
#
|
|
144
|
+
# Convert a Java Duration to a map for the map_to_cron method
|
|
145
|
+
#
|
|
146
|
+
# @param duration [Duration] The duration object
|
|
147
|
+
#
|
|
148
|
+
# @return [Hash] a map suitable for map_to_cron
|
|
149
|
+
#
|
|
150
|
+
def self.duration_to_map(duration)
|
|
151
|
+
if duration.to_millis_part.zero? && duration.to_nanos_part.zero? && duration.to_days.zero?
|
|
152
|
+
%i[second minute hour].each do |unit|
|
|
153
|
+
to_unit_part = duration.public_send("to_#{unit}s_part")
|
|
154
|
+
next unless to_unit_part.positive?
|
|
155
|
+
|
|
156
|
+
to_unit = duration.public_send("to_#{unit}s")
|
|
157
|
+
break unless to_unit_part == to_unit
|
|
158
|
+
|
|
159
|
+
return EXPRESSION_MAP[unit].merge(unit => "*/#{to_unit}")
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
raise ArgumentError, "Cron Expression not supported for duration: #{duration}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
#
|
|
166
|
+
# If an at time is provided, parse that and merge the new fields into the expression.
|
|
167
|
+
#
|
|
168
|
+
# @param [<Type>] expression_map <description>
|
|
169
|
+
# @param [<Type>] at_time <description>
|
|
170
|
+
#
|
|
171
|
+
# @return [<Type>] <description>
|
|
172
|
+
#
|
|
173
|
+
def self.at_condition(expression_map, at_time)
|
|
174
|
+
if at_time
|
|
175
|
+
tod = at_time.is_a?(LocalTime) ? at_time : LocalTime.parse(at_time)
|
|
176
|
+
expression_map = expression_map.merge(hour: tod.hour, minute: tod.minute, second: tod.second)
|
|
177
|
+
end
|
|
178
|
+
expression_map
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
#
|
|
182
|
+
# Create a cron trigger based on item type
|
|
183
|
+
#
|
|
184
|
+
# @param [Config] config Rule configuration
|
|
185
|
+
# @param [Object] attach object to be attached to the trigger
|
|
186
|
+
#
|
|
187
|
+
#
|
|
188
|
+
def trigger(config:, attach:)
|
|
189
|
+
append_trigger(type: CRON_TRIGGER_MODULE_ID, config: config, attach: attach)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|