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,1373 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
require_relative "property"
|
|
6
|
+
require_relative "guard"
|
|
7
|
+
require_relative "rule_triggers"
|
|
8
|
+
|
|
9
|
+
Dir[File.expand_path("triggers/*.rb", __dir__)].sort.each do |f|
|
|
10
|
+
require f
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module OpenHAB
|
|
14
|
+
module DSL
|
|
15
|
+
#
|
|
16
|
+
# Creates and manages OpenHAB Rules
|
|
17
|
+
#
|
|
18
|
+
module Rules
|
|
19
|
+
#
|
|
20
|
+
# Rule configuration for OpenHAB Rules engine
|
|
21
|
+
#
|
|
22
|
+
class Builder
|
|
23
|
+
include Core::EntityLookup
|
|
24
|
+
include DSL
|
|
25
|
+
prepend Triggers
|
|
26
|
+
extend Property
|
|
27
|
+
extend Forwardable
|
|
28
|
+
|
|
29
|
+
delegate %i[triggers trigger_conditions attachments] => :@rule_triggers
|
|
30
|
+
|
|
31
|
+
# @!visibility private
|
|
32
|
+
# @return [Array] Of trigger guards
|
|
33
|
+
attr_accessor :guard
|
|
34
|
+
|
|
35
|
+
# @!visibility private
|
|
36
|
+
# @return [Object] object that invoked rule method
|
|
37
|
+
attr_accessor :caller
|
|
38
|
+
|
|
39
|
+
# @!visibility private
|
|
40
|
+
# @return [Array] Of trigger definitions as passed in Ruby
|
|
41
|
+
attr_reader :ruby_triggers
|
|
42
|
+
|
|
43
|
+
# @!visibility private
|
|
44
|
+
Run = Struct.new(:block)
|
|
45
|
+
|
|
46
|
+
# @!visibility private
|
|
47
|
+
Script = Struct.new(:block)
|
|
48
|
+
|
|
49
|
+
# @!visibility private
|
|
50
|
+
Trigger = Struct.new(:block)
|
|
51
|
+
|
|
52
|
+
# @!visibility private
|
|
53
|
+
Otherwise = Struct.new(:block)
|
|
54
|
+
|
|
55
|
+
# @!visibility private
|
|
56
|
+
Delay = Struct.new(:duration)
|
|
57
|
+
|
|
58
|
+
# @!group Execution Blocks
|
|
59
|
+
|
|
60
|
+
#
|
|
61
|
+
# @!method run
|
|
62
|
+
#
|
|
63
|
+
# Add a block that will be passed event data.
|
|
64
|
+
#
|
|
65
|
+
# The run property is the automation code that is executed when a rule
|
|
66
|
+
# is triggered. This property accepts a block of code and executes it.
|
|
67
|
+
# The block is automatically passed an event object which can be used
|
|
68
|
+
# to access multiple properties about the triggering event. The code
|
|
69
|
+
# for the automation can be entirely within the run block and can call
|
|
70
|
+
# methods defined in the Ruby script.
|
|
71
|
+
#
|
|
72
|
+
# @yieldparam [Core::Events::AbstractEvent] event
|
|
73
|
+
# @return [void]
|
|
74
|
+
#
|
|
75
|
+
# @example `{}` style used for single line blocks.
|
|
76
|
+
# rule 'Access Event Properties' do
|
|
77
|
+
# changed TestSwitch
|
|
78
|
+
# run { |event| logger.info("#{event.item.name} triggered from #{event.was} to #{event.state}") }
|
|
79
|
+
# end
|
|
80
|
+
#
|
|
81
|
+
# @example `do/end` style used for multi-line blocks.
|
|
82
|
+
# rule 'Multi Line Run Block' do
|
|
83
|
+
# changed TestSwitch
|
|
84
|
+
# run do |event|
|
|
85
|
+
# logger.info("#{event.item.name} triggered")
|
|
86
|
+
# logger.info("from #{event.was}") if event.was?
|
|
87
|
+
# logger.info("to #{event.state}") if event.state?
|
|
88
|
+
# end
|
|
89
|
+
# end
|
|
90
|
+
#
|
|
91
|
+
# @example Rules can have multiple run blocks and they are executed in order. Useful when used in combination with {#delay}.
|
|
92
|
+
# rule 'Multiple Run Blocks' do
|
|
93
|
+
# changed TestSwitch
|
|
94
|
+
# run { |event| logger.info("#{event.item.name} triggered") }
|
|
95
|
+
# run { |event| logger.info("from #{event.was}") if event.was? }
|
|
96
|
+
# run { |event| logger.info("to #{event.state}") if event.state? }
|
|
97
|
+
# end
|
|
98
|
+
#
|
|
99
|
+
prop_array :run, array_name: :run_queue, wrapper: Run
|
|
100
|
+
|
|
101
|
+
prop_array :script, array_name: :run_queue, wrapper: Script
|
|
102
|
+
|
|
103
|
+
#
|
|
104
|
+
# @!method triggered
|
|
105
|
+
#
|
|
106
|
+
# Add a block that will be passed the triggering item.
|
|
107
|
+
#
|
|
108
|
+
# This property is the same as the {#run} property except rather than
|
|
109
|
+
# passing an event object to the automation block the triggered item is
|
|
110
|
+
# passed. This enables optimizations for simple cases and supports
|
|
111
|
+
# Ruby's [pretzel colon `&:` operator.](https://medium.com/@dcjones/the-pretzel-colon-75df46dde0c7).
|
|
112
|
+
#
|
|
113
|
+
# @yieldparam [Item] item
|
|
114
|
+
# @return [void]
|
|
115
|
+
#
|
|
116
|
+
# @example
|
|
117
|
+
# rule "motion sensor triggered" do
|
|
118
|
+
# changed MotionSensor.members, to: :OPEN
|
|
119
|
+
# triggered do |item|
|
|
120
|
+
# logger.info("#{item.name} detected motion")
|
|
121
|
+
# end
|
|
122
|
+
# end
|
|
123
|
+
#
|
|
124
|
+
# @example
|
|
125
|
+
# rule 'Triggered has access directly to item triggered' do
|
|
126
|
+
# changed TestSwitch
|
|
127
|
+
# triggered { |item| logger.info("#{item.name} triggered") }
|
|
128
|
+
# end
|
|
129
|
+
#
|
|
130
|
+
# @example Triggered items are highly useful when working with groups
|
|
131
|
+
# # Switches is a group of Switch items
|
|
132
|
+
# rule 'Triggered item is item changed when a group item is changed.' do
|
|
133
|
+
# changed Switches.members
|
|
134
|
+
# triggered { |item| logger.info("Switch #{item.name} changed to #{item.state}")}
|
|
135
|
+
# end
|
|
136
|
+
#
|
|
137
|
+
# rule 'Turn off any switch that changes' do
|
|
138
|
+
# changed Switches.members
|
|
139
|
+
# triggered(&:off)
|
|
140
|
+
# end
|
|
141
|
+
#
|
|
142
|
+
# @example Like other execution blocks, multiple triggered blocks are supported in a single rule
|
|
143
|
+
# rule 'Turn a switch off and log it, 5 seconds after turning it on' do
|
|
144
|
+
# changed Switches.members, to: ON
|
|
145
|
+
# delay 5.seconds
|
|
146
|
+
# triggered(&:off)
|
|
147
|
+
# triggered {|item| logger.info("#{item.label} turned off") }
|
|
148
|
+
# end
|
|
149
|
+
prop_array :triggered, array_name: :run_queue, wrapper: Trigger
|
|
150
|
+
|
|
151
|
+
#
|
|
152
|
+
# @!method delay(duration)
|
|
153
|
+
#
|
|
154
|
+
# Add a wait between or after run blocks.
|
|
155
|
+
#
|
|
156
|
+
# The delay property is a non thread-blocking element that is executed
|
|
157
|
+
# after, before, or between run blocks.
|
|
158
|
+
#
|
|
159
|
+
# @param [java.time.temporal.TemporalAmount] duration How long to delay for.
|
|
160
|
+
# @return [void]
|
|
161
|
+
#
|
|
162
|
+
# @example
|
|
163
|
+
# rule "delay execution" do
|
|
164
|
+
# changed MotionSensor, to: CLOSED
|
|
165
|
+
# delay 5.seconds
|
|
166
|
+
# run { Light.off }
|
|
167
|
+
# end
|
|
168
|
+
#
|
|
169
|
+
# @example
|
|
170
|
+
# rule 'Delay sleeps between execution elements' do
|
|
171
|
+
# on_start
|
|
172
|
+
# run { logger.info("Sleeping") }
|
|
173
|
+
# delay 5.seconds
|
|
174
|
+
# run { logger.info("Awake") }
|
|
175
|
+
# end
|
|
176
|
+
#
|
|
177
|
+
# @example Like other execution blocks, multiple can exist in a single rule.
|
|
178
|
+
# rule 'Multiple delays can exist in a rule' do
|
|
179
|
+
# on_start
|
|
180
|
+
# run { logger.info("Sleeping") }
|
|
181
|
+
# delay 5.seconds
|
|
182
|
+
# run { logger.info("Sleeping Again") }
|
|
183
|
+
# delay 5.seconds
|
|
184
|
+
# run { logger.info("Awake") }
|
|
185
|
+
# end
|
|
186
|
+
#
|
|
187
|
+
# @example You can use Ruby code in your rule across multiple execution blocks like a run and a delay.
|
|
188
|
+
# rule 'Dim a switch on system startup over 100 seconds' do
|
|
189
|
+
# on_start
|
|
190
|
+
# 100.times do
|
|
191
|
+
# run { DimmerSwitch.dim }
|
|
192
|
+
# delay 1.second
|
|
193
|
+
# end
|
|
194
|
+
# end
|
|
195
|
+
#
|
|
196
|
+
prop_array :delay, array_name: :run_queue, wrapper: Delay
|
|
197
|
+
|
|
198
|
+
#
|
|
199
|
+
# @!method otherwise
|
|
200
|
+
#
|
|
201
|
+
# Add a block that will be passed event data, to be run if guards are
|
|
202
|
+
# not satisfied.
|
|
203
|
+
#
|
|
204
|
+
# The {otherwise} property is the automation code that is executed when
|
|
205
|
+
# a rule is triggered and guards are not satisfied. This property
|
|
206
|
+
# accepts a block of code and executes it. The block is automatically
|
|
207
|
+
# passed an event object which can be used to access multiple
|
|
208
|
+
# properties about the triggering event.
|
|
209
|
+
#
|
|
210
|
+
# @yieldparam [Core::Events::AbstractEvent] event
|
|
211
|
+
#
|
|
212
|
+
# @example
|
|
213
|
+
# rule 'Turn switch ON or OFF based on value of another switch' do
|
|
214
|
+
# on_start
|
|
215
|
+
# run { TestSwitch << ON }
|
|
216
|
+
# otherwise { TestSwitch << OFF }
|
|
217
|
+
# only_if { OtherSwitch.on? }
|
|
218
|
+
# end
|
|
219
|
+
#
|
|
220
|
+
prop_array :otherwise, array_name: :run_queue, wrapper: Otherwise
|
|
221
|
+
|
|
222
|
+
# @!group Configuration
|
|
223
|
+
|
|
224
|
+
#
|
|
225
|
+
# @!method uid(id)
|
|
226
|
+
#
|
|
227
|
+
# Set the rule's UID.
|
|
228
|
+
#
|
|
229
|
+
# @param [String] id
|
|
230
|
+
# @return [void]
|
|
231
|
+
#
|
|
232
|
+
prop :uid
|
|
233
|
+
|
|
234
|
+
#
|
|
235
|
+
# @!method name(value)
|
|
236
|
+
#
|
|
237
|
+
# Set the rule's name.
|
|
238
|
+
#
|
|
239
|
+
# @param [String] value
|
|
240
|
+
# @return [void]
|
|
241
|
+
#
|
|
242
|
+
prop :name
|
|
243
|
+
|
|
244
|
+
#
|
|
245
|
+
# @!method description(value)
|
|
246
|
+
#
|
|
247
|
+
# Set the rule's description.
|
|
248
|
+
#
|
|
249
|
+
# @param [String] value
|
|
250
|
+
# @return [void]
|
|
251
|
+
#
|
|
252
|
+
prop :description
|
|
253
|
+
|
|
254
|
+
#
|
|
255
|
+
# @!method tags(tags)
|
|
256
|
+
#
|
|
257
|
+
# Set the rule's tags.
|
|
258
|
+
#
|
|
259
|
+
# @param [String, Class, Array<String, Class>] tags
|
|
260
|
+
# @return [void]
|
|
261
|
+
#
|
|
262
|
+
# @example
|
|
263
|
+
# rule "tagged rule" do
|
|
264
|
+
# tags "lighting", "security"
|
|
265
|
+
# end
|
|
266
|
+
#
|
|
267
|
+
prop :tags
|
|
268
|
+
|
|
269
|
+
#
|
|
270
|
+
# @!method enabled(value)
|
|
271
|
+
#
|
|
272
|
+
# Enable or disable the rule from executing
|
|
273
|
+
#
|
|
274
|
+
# @param [true,false] value
|
|
275
|
+
# @return [void]
|
|
276
|
+
#
|
|
277
|
+
# @example
|
|
278
|
+
# rule "disabled rule" do
|
|
279
|
+
# enabled(false)
|
|
280
|
+
# end
|
|
281
|
+
#
|
|
282
|
+
prop :enabled
|
|
283
|
+
|
|
284
|
+
# @!group Guards
|
|
285
|
+
# Guards exist to only permit rules to run if certain conditions are
|
|
286
|
+
# satisfied. Think of these as declarative `if` statements that keep
|
|
287
|
+
# the run block free of conditional logic, although you can of course
|
|
288
|
+
# still use conditional logic in run blocks if you prefer.
|
|
289
|
+
#
|
|
290
|
+
# ### Guard Combination
|
|
291
|
+
#
|
|
292
|
+
# {#only_if} and {#not_if} can be used on the same rule. Both must be
|
|
293
|
+
# satisfied for a rule to execute.
|
|
294
|
+
#
|
|
295
|
+
# @example
|
|
296
|
+
# rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is OFF and Door is CLOSED" do
|
|
297
|
+
# changed LightSwitch, to: ON
|
|
298
|
+
# run { OutsideDimmer << 50 }
|
|
299
|
+
# only_if { Door.closed? }
|
|
300
|
+
# not_if { OtherSwitch.on? }
|
|
301
|
+
# end
|
|
302
|
+
#
|
|
303
|
+
|
|
304
|
+
#
|
|
305
|
+
# @!method between(range)
|
|
306
|
+
#
|
|
307
|
+
# Only execute rule if current time is between supplied time ranges.
|
|
308
|
+
#
|
|
309
|
+
# If the range is of strings, it will be parsed to an appropriate time class.
|
|
310
|
+
#
|
|
311
|
+
# @param [Range] range
|
|
312
|
+
# @return [void]
|
|
313
|
+
#
|
|
314
|
+
# @example
|
|
315
|
+
# rule "Between guard" do
|
|
316
|
+
# changed MotionSensor, to: OPEN
|
|
317
|
+
# between "6:05".."14:05:05" # Include end
|
|
318
|
+
# run { Light.on }
|
|
319
|
+
# end
|
|
320
|
+
#
|
|
321
|
+
# @example
|
|
322
|
+
# rule "Between guard" do
|
|
323
|
+
# changed MotionSensor, to: OPEN
|
|
324
|
+
# between "6:05".."14:05:05" # Excludes end second
|
|
325
|
+
# run { Light.on }
|
|
326
|
+
# end
|
|
327
|
+
#
|
|
328
|
+
# @example
|
|
329
|
+
# rule "Between guard" do
|
|
330
|
+
# changed MotionSensor, to: OPEN
|
|
331
|
+
# between LocalTime.of(6, 5)..LocalTime.of(14, 15, 5)
|
|
332
|
+
# run { Light.on }
|
|
333
|
+
# end
|
|
334
|
+
#
|
|
335
|
+
# @example String of {LocalTime}
|
|
336
|
+
# rule 'Log an entry if started between 3:30:04 and midnight using strings' do
|
|
337
|
+
# on_start
|
|
338
|
+
# run { logger.info ("Started at #{LocalTime.now}")}
|
|
339
|
+
# between '3:30:04'..LocalTime::MIDNIGHT
|
|
340
|
+
# end
|
|
341
|
+
#
|
|
342
|
+
# @example {LocalTime}
|
|
343
|
+
# rule 'Log an entry if started between 3:30:04 and midnight using LocalTime objects' do
|
|
344
|
+
# on_start
|
|
345
|
+
# run { logger.info ("Started at #{LocalTime.now}")}
|
|
346
|
+
# between LocalTime.of(3, 30, 4)..LocalTime::MIDNIGHT
|
|
347
|
+
# end
|
|
348
|
+
#
|
|
349
|
+
# @example String of {MonthDay}
|
|
350
|
+
# rule 'Log an entry if started between March 9th and April 10ths' do
|
|
351
|
+
# on_start
|
|
352
|
+
# run { logger.info ("Started at #{Time.now}")}
|
|
353
|
+
# between '03-09'..'04-10'
|
|
354
|
+
# end
|
|
355
|
+
#
|
|
356
|
+
# @example {MonthDay}
|
|
357
|
+
# rule 'Log an entry if started between March 9th and April 10ths' do
|
|
358
|
+
# on_start
|
|
359
|
+
# run { logger.info ("Started at #{Time.now}")}
|
|
360
|
+
# between MonthDay.of(03,09)..'04-06'
|
|
361
|
+
# end
|
|
362
|
+
#
|
|
363
|
+
prop :between
|
|
364
|
+
|
|
365
|
+
#
|
|
366
|
+
# @!method only_if
|
|
367
|
+
#
|
|
368
|
+
# {only_if} allows rule execution when the block's is true and prevents it when it's false.
|
|
369
|
+
#
|
|
370
|
+
# @yieldparam [Core::Events::AbstractEvent] event The event data that is about to trigger the rule.
|
|
371
|
+
# @yieldreturn [Boolean] A value indicating if the rule should run.
|
|
372
|
+
# @return [void]
|
|
373
|
+
#
|
|
374
|
+
# @example
|
|
375
|
+
# rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is also ON" do
|
|
376
|
+
# changed LightSwitch, to: ON
|
|
377
|
+
# run { OutsideDimmer << 50 }
|
|
378
|
+
# only_if { OtherSwitch.on? }
|
|
379
|
+
# end
|
|
380
|
+
#
|
|
381
|
+
# @example Multiple {only_if} statements can be used and *all* must be true for the rule to run.
|
|
382
|
+
# rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is also ON and Door is closed" do
|
|
383
|
+
# changed LightSwitch, to: ON
|
|
384
|
+
# run { OutsideDimmer << 50 }
|
|
385
|
+
# only_if { OtherSwitch.on? }
|
|
386
|
+
# only_if { Door.closed? }
|
|
387
|
+
# end
|
|
388
|
+
#
|
|
389
|
+
# @example Guards have access to event information.
|
|
390
|
+
# rule "Set OutsideDimmer to 50% if any switch in group Switches starting with Outside is switched On" do
|
|
391
|
+
# changed Switches.items, to: ON
|
|
392
|
+
# run { OutsideDimmer << 50 }
|
|
393
|
+
# only_if { |event| event.item.name.start_with?("Outside") }
|
|
394
|
+
# end
|
|
395
|
+
#
|
|
396
|
+
prop_array(:only_if) do |item|
|
|
397
|
+
unless item.is_a?(Proc) || [item].flatten.all? { |it| it.respond_to?(:truthy?) }
|
|
398
|
+
raise ArgumentError, "Object passed to only_if must be a proc"
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
#
|
|
403
|
+
# @!method not_if
|
|
404
|
+
#
|
|
405
|
+
# {not_if} prevents execution of rules when the block's result is true and allows it when it's true.
|
|
406
|
+
#
|
|
407
|
+
# @yieldparam [Core::Events::AbstractEvent] event The event data that is about to trigger the rule.
|
|
408
|
+
# @yieldreturn [Boolean] A value indicating if the rule should _not_ run.
|
|
409
|
+
# @return [void]
|
|
410
|
+
#
|
|
411
|
+
# @example
|
|
412
|
+
# rule "Set OutsideDimmer to 50% if LightSwtich turned on and OtherSwitch is OFF" do
|
|
413
|
+
# changed LightSwitch, to: ON
|
|
414
|
+
# run { OutsideDimmer << 50 }
|
|
415
|
+
# not_if { OtherSwitch.on? }
|
|
416
|
+
# end
|
|
417
|
+
#
|
|
418
|
+
# @example Multiple {not_if} statements can be used and if **any** of them are not satisfied the rule will not run.
|
|
419
|
+
# rule "Set OutsideDimmer to 50% if LightSwitch turned on and OtherSwitch is OFF and Door is not CLOSED" do
|
|
420
|
+
# changed LightSwitch, to: ON
|
|
421
|
+
# run { OutsideDimmer << 50 }
|
|
422
|
+
# not_if { OtherSwitch.on? }
|
|
423
|
+
# not_if { Door.closed? }
|
|
424
|
+
# end
|
|
425
|
+
#
|
|
426
|
+
prop_array(:not_if) do |item|
|
|
427
|
+
unless item.is_a?(Proc) || [item].flatten.all? { |it| it.respond_to?(:truthy?) }
|
|
428
|
+
raise ArgumentError, "Object passed to not_if must be a proc"
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# @!endgroup
|
|
433
|
+
|
|
434
|
+
# @!visibility private
|
|
435
|
+
#
|
|
436
|
+
# Create a new DSL
|
|
437
|
+
#
|
|
438
|
+
# @param [Object] caller_binding The object initializing this configuration.
|
|
439
|
+
# Used to execute within the object's context
|
|
440
|
+
#
|
|
441
|
+
def initialize(caller_binding)
|
|
442
|
+
@rule_triggers = RuleTriggers.new
|
|
443
|
+
@caller = caller_binding.eval "self"
|
|
444
|
+
@ruby_triggers = []
|
|
445
|
+
enabled(true)
|
|
446
|
+
on_start(false)
|
|
447
|
+
tags([])
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# @!group Triggers
|
|
451
|
+
# Triggers specify what will cause the execution blocks to run.
|
|
452
|
+
# Multiple triggers can be defined within the same rule.
|
|
453
|
+
#
|
|
454
|
+
# ### Trigger Attachments
|
|
455
|
+
#
|
|
456
|
+
# All triggers support event attachments that enable the association
|
|
457
|
+
# of an object to a trigger. This enables one to use the same rule
|
|
458
|
+
# and take different actions if the trigger is different. The
|
|
459
|
+
# attached object is passed to the execution block through the
|
|
460
|
+
# {Core::Events::AbstractEvent#attachment} accessor.
|
|
461
|
+
#
|
|
462
|
+
# @note The trigger attachment feature is not available for UI rules.
|
|
463
|
+
#
|
|
464
|
+
# @example
|
|
465
|
+
# rule 'Set Dark switch at sunrise and sunset' do
|
|
466
|
+
# channel 'astro:sun:home:rise#event', attach: OFF
|
|
467
|
+
# channel 'astro:sun:home:set#event', attach: ON
|
|
468
|
+
# run { |event| Dark << event.attachment }
|
|
469
|
+
# end
|
|
470
|
+
|
|
471
|
+
#
|
|
472
|
+
# Creates a channel trigger
|
|
473
|
+
#
|
|
474
|
+
# The channel trigger executes rule when a specific channel is triggered. The syntax
|
|
475
|
+
# supports one or more channels with one or more triggers. `thing` is an optional
|
|
476
|
+
# parameter that makes it easier to set triggers on multiple channels on the same thing.
|
|
477
|
+
#
|
|
478
|
+
# @param [String, Core::Things::Channel, Core::Things::ChannelUID] channels
|
|
479
|
+
# channels to create triggers for in form of 'binding_id:type_id:thing_id#channel_id'
|
|
480
|
+
# or 'channel_id' if thing is provided.
|
|
481
|
+
# @param [String, Core::Things::Thing, Core::Things::ThingUID] thing
|
|
482
|
+
# Thing(s) to create trigger for if not specified with the channel.
|
|
483
|
+
# @param [String, Array<String>] triggered
|
|
484
|
+
# Only execute rule if the event on the channel matches this/these event/events.
|
|
485
|
+
# @param [Object] attach object to be attached to the trigger
|
|
486
|
+
# @return [void]
|
|
487
|
+
#
|
|
488
|
+
# @example
|
|
489
|
+
# rule "Execute rule when channel is triggered" do
|
|
490
|
+
# channel "astro:sun:home:rise#event"
|
|
491
|
+
# run { logger.info("Channel triggered") }
|
|
492
|
+
# end
|
|
493
|
+
# # The above is the same as each of the below
|
|
494
|
+
#
|
|
495
|
+
# rule "Execute rule when channel is triggered" do
|
|
496
|
+
# channel "rise#event", thing: "astro:sun:home"
|
|
497
|
+
# run { logger.info("Channel triggered") }
|
|
498
|
+
# end
|
|
499
|
+
#
|
|
500
|
+
# rule "Execute rule when channel is triggered" do
|
|
501
|
+
# channel "rise#event", thing: things["astro:sun:home"]
|
|
502
|
+
# run { logger.info("Channel triggered") }
|
|
503
|
+
# end
|
|
504
|
+
#
|
|
505
|
+
# rule "Execute rule when channel is triggered" do
|
|
506
|
+
# channel "rise#event", thing: things["astro:sun:home"].uid
|
|
507
|
+
# run { logger.info("Channel triggered") }
|
|
508
|
+
# end
|
|
509
|
+
#
|
|
510
|
+
# rule "Execute rule when channel is triggered" do
|
|
511
|
+
# channel "rise#event", thing: ["astro:sun:home"]
|
|
512
|
+
# run { logger.info("Channel triggered") }
|
|
513
|
+
# end
|
|
514
|
+
#
|
|
515
|
+
# rule "Execute rule when channel is triggered" do
|
|
516
|
+
# channel things["astro:sun:home"].channels["rise#event"]
|
|
517
|
+
# run { logger.info("Channel triggered") }
|
|
518
|
+
# end
|
|
519
|
+
#
|
|
520
|
+
# rule "Execute rule when channel is triggered" do
|
|
521
|
+
# channel things["astro:sun:home"].channels["rise#event"].uid
|
|
522
|
+
# run { logger.info("Channel triggered") }
|
|
523
|
+
# end
|
|
524
|
+
#
|
|
525
|
+
# @example
|
|
526
|
+
# rule "Rule provides access to channel trigger events in run block" do
|
|
527
|
+
# channel "astro:sun:home:rise#event", triggered: 'START'
|
|
528
|
+
# run { |trigger| logger.info("Channel(#{trigger.channel}) triggered event: #{trigger.event}") }
|
|
529
|
+
# end
|
|
530
|
+
#
|
|
531
|
+
# @example
|
|
532
|
+
# rule "Keypad Code Received test" do
|
|
533
|
+
# channel "mqtt:homie300:mosquitto:backgate:keypad#code"
|
|
534
|
+
# run do |event|
|
|
535
|
+
# logger.info("Received keycode from #{event.channel.thing.uid.id}")
|
|
536
|
+
# end
|
|
537
|
+
# end
|
|
538
|
+
#
|
|
539
|
+
# @example
|
|
540
|
+
# rule "Rules support multiple channels" do
|
|
541
|
+
# channel "rise#event", "set#event", thing: "astro:sun:home"
|
|
542
|
+
# run { logger.info("Channel triggered") }
|
|
543
|
+
# end
|
|
544
|
+
#
|
|
545
|
+
# @example
|
|
546
|
+
# rule "Rules support multiple channels and triggers" do
|
|
547
|
+
# channel "rise#event", "set#event", thing: "astro:sun:home", triggered: ["START", "STOP"]
|
|
548
|
+
# run { logger.info("Channel triggered") }
|
|
549
|
+
# end
|
|
550
|
+
#
|
|
551
|
+
# @example
|
|
552
|
+
# rule "Rules support multiple things" do
|
|
553
|
+
# channel "keypad#code", thing: ["mqtt:homie300:keypad1", "mqtt:homie300:keypad2"]
|
|
554
|
+
# run { logger.info("Channel triggered") }
|
|
555
|
+
# end
|
|
556
|
+
#
|
|
557
|
+
def channel(*channels, thing: nil, triggered: nil, attach: nil)
|
|
558
|
+
channel_trigger = Channel.new(rule_triggers: @rule_triggers)
|
|
559
|
+
flattened_channels = Channel.channels(channels: channels, thing: thing)
|
|
560
|
+
triggers = [triggered].flatten
|
|
561
|
+
@ruby_triggers << [:channel, flattened_channels, { triggers: triggers }]
|
|
562
|
+
flattened_channels.each do |channel|
|
|
563
|
+
triggers.each do |trigger|
|
|
564
|
+
channel_trigger.trigger(channel: channel, trigger: trigger, attach: attach)
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
#
|
|
570
|
+
# Creates a channel linked trigger
|
|
571
|
+
#
|
|
572
|
+
# @param [Object] attach object to be attached to the trigger
|
|
573
|
+
# @return [void]
|
|
574
|
+
#
|
|
575
|
+
# @example
|
|
576
|
+
# rule "channel linked" do
|
|
577
|
+
# channel_linked
|
|
578
|
+
# run do |event|
|
|
579
|
+
# logger.info("#{event.link.item.name} linked to #{event.link.channel_uid}.")
|
|
580
|
+
# end
|
|
581
|
+
# end
|
|
582
|
+
def channel_linked(attach: nil)
|
|
583
|
+
@ruby_triggers << [:channel_linked]
|
|
584
|
+
trigger("core.GenericEventTrigger", eventTopic: "openhab/links/*/added",
|
|
585
|
+
eventTypes: "ItemChannelLinkAddedEvent", attach: attach)
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
#
|
|
589
|
+
# Creates a channel unlinked trigger
|
|
590
|
+
#
|
|
591
|
+
# Note that the item or the thing it's linked to may no longer exist,
|
|
592
|
+
# so if you try to access those objects they'll be nil.
|
|
593
|
+
#
|
|
594
|
+
# @param [Object] attach object to be attached to the trigger
|
|
595
|
+
# @return [void]
|
|
596
|
+
#
|
|
597
|
+
# @example
|
|
598
|
+
# rule "channel unlinked" do
|
|
599
|
+
# channel_unlinked
|
|
600
|
+
# run do |event|
|
|
601
|
+
# logger.info("#{event.link.item_name} unlinked from #{event.link.channel_uid}.")
|
|
602
|
+
# end
|
|
603
|
+
# end
|
|
604
|
+
def channel_unlinked(attach: nil)
|
|
605
|
+
@ruby_triggers << [:channel_linked]
|
|
606
|
+
trigger("core.GenericEventTrigger", eventTopic: "openhab/links/*/removed",
|
|
607
|
+
eventTypes: "ItemChannelLinkRemovedEvent", attach: attach)
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
#
|
|
611
|
+
# Creates a trigger when an item, member of a group, or a thing changed
|
|
612
|
+
# states.
|
|
613
|
+
#
|
|
614
|
+
# When the changed element is a {Core::Things::Thing Thing}, the `from`
|
|
615
|
+
# and `to` values will accept symbols and strings, where the symbol'
|
|
616
|
+
# matches the
|
|
617
|
+
# [supported status](https://www.openhab.org/docs/concepts/things.html#thing-status).
|
|
618
|
+
#
|
|
619
|
+
# The `event` passed to run blocks will be an
|
|
620
|
+
# {Core::Events::ItemStateChangedEvent} or a
|
|
621
|
+
# {Core::Events::ThingStatusInfoChangedEvent} depending on if the
|
|
622
|
+
# triggering element was an item or a thing.
|
|
623
|
+
#
|
|
624
|
+
# @param [Item, GroupItem::Members, Thing] items Objects to create trigger for.
|
|
625
|
+
# @param [State, Array<State>, Range, Proc] from
|
|
626
|
+
# Only execute rule if previous state matches `from` state(s).
|
|
627
|
+
# @param [State, Array<State>, Range, Proc] to State(s) for
|
|
628
|
+
# Only execute rule if new state matches `to` state(s).
|
|
629
|
+
# @param [java.time.temporal.TemporalAmount] for
|
|
630
|
+
# Duration item must remain in the same state before executing the execution blocks.
|
|
631
|
+
# @param [Object] attach object to be attached to the trigger
|
|
632
|
+
# @return [void]
|
|
633
|
+
#
|
|
634
|
+
# @example Multiple items can be separated with a comma:
|
|
635
|
+
# rule "Execute rule when either sensor changed" do
|
|
636
|
+
# changed FrontMotion_Sensor, RearMotion_Sensor
|
|
637
|
+
# run { |event| logger.info("Motion detected by #{event.item.name}") }
|
|
638
|
+
# end
|
|
639
|
+
#
|
|
640
|
+
# @example Group member trigger
|
|
641
|
+
# rule "Execute rule when member changed" do
|
|
642
|
+
# changed Sensors.members
|
|
643
|
+
# run { |event| logger.info("Motion detected by #{event.item.name}") }
|
|
644
|
+
# end
|
|
645
|
+
#
|
|
646
|
+
# @example `for` parameter can be a proc too:
|
|
647
|
+
# Alarm_Delay << 20
|
|
648
|
+
#
|
|
649
|
+
# rule "Execute rule when item is changed for specified duration" do
|
|
650
|
+
# changed Alarm_Mode, for: -> { Alarm_Delay.state }
|
|
651
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
652
|
+
# end
|
|
653
|
+
#
|
|
654
|
+
# @example You can optionally provide `from` and `to` states to restrict the cases in which the rule executes:
|
|
655
|
+
# rule "Execute rule when item is changed to specific number, from specific number, for specified duration" do
|
|
656
|
+
# changed Alarm_Mode, from: 8, to: [14,12], for: 12.seconds
|
|
657
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
658
|
+
# end
|
|
659
|
+
#
|
|
660
|
+
# @example Works with ranges:
|
|
661
|
+
# rule "Execute when item changed to a range of numbers, from a range of numbers, for specified duration" do
|
|
662
|
+
# changed Alarm_Mode, from: 8..10, to: 12..14, for: 12.seconds
|
|
663
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
664
|
+
# end
|
|
665
|
+
#
|
|
666
|
+
# @example Works with endless ranges:
|
|
667
|
+
# rule "Execute rule when item is changed to any number greater than 12"
|
|
668
|
+
# changed Alarm_Mode, to: (12..) # Parenthesis required for endless ranges
|
|
669
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
670
|
+
# end
|
|
671
|
+
#
|
|
672
|
+
# @example Works with procs:
|
|
673
|
+
# rule "Execute when item state is changed from an odd number, to an even number, for specified duration" do
|
|
674
|
+
# changed Alarm_Mode, from: proc { |from| from.odd? }, to: proc {|to| to.even? }, for: 12.seconds
|
|
675
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
676
|
+
# end
|
|
677
|
+
#
|
|
678
|
+
# @example Works with lambdas:
|
|
679
|
+
# rule "Execute when item state is changed from an odd number, to an even number, for specified duration" do
|
|
680
|
+
# changed Alarm_Mode, from: -> from { from.odd? }, to: -> to { to.even? }, for: 12.seconds
|
|
681
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
682
|
+
# end
|
|
683
|
+
#
|
|
684
|
+
# @example Works with Things:
|
|
685
|
+
# rule "Execute rule when thing is changed" do
|
|
686
|
+
# changed things["astro:sun:home"], :from => :online, :to => :uninitialized
|
|
687
|
+
# run { |event| logger.info("Thing #{event.uid} status <trigger> to #{event.status}") }
|
|
688
|
+
# end
|
|
689
|
+
#
|
|
690
|
+
# @example Real World Example
|
|
691
|
+
# rule "Log (or notify) when an exterior door is left open for more than 5 minutes" do
|
|
692
|
+
# changed ExteriorDoors.members, to: OPEN, for: 5.minutes
|
|
693
|
+
# triggered {|door| logger.info("#{door.name} has been left open!") }
|
|
694
|
+
# end
|
|
695
|
+
#
|
|
696
|
+
def changed(*items, to: nil, from: nil, for: nil, attach: nil)
|
|
697
|
+
changed = Changed.new(rule_triggers: @rule_triggers)
|
|
698
|
+
# for is a reserved word in ruby, so use local_variable_get :for
|
|
699
|
+
duration = binding.local_variable_get(:for)
|
|
700
|
+
|
|
701
|
+
from = [nil] if from.nil?
|
|
702
|
+
to = [nil] if to.nil?
|
|
703
|
+
|
|
704
|
+
@ruby_triggers << [:changed, items, { to: to, from: from, duration: duration }]
|
|
705
|
+
items.each do |item|
|
|
706
|
+
logger.trace("Creating changed trigger for entity(#{item}), to(#{to.inspect}), from(#{from.inspect})")
|
|
707
|
+
|
|
708
|
+
Array.wrap(from).each do |from_state|
|
|
709
|
+
Array.wrap(to).each do |to_state|
|
|
710
|
+
changed.trigger(item: item, from: from_state, to: to_state, duration: duration, attach: attach)
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
#
|
|
717
|
+
# Create a cron trigger
|
|
718
|
+
#
|
|
719
|
+
# @overload cron(expression, attach: nil)
|
|
720
|
+
# @param [String, nil] expression [OpenHAB style cron expression](https://www.openhab.org/docs/configuration/rules-dsl.html#time-based-triggers)
|
|
721
|
+
# @param [Object] attach object to be attached to the trigger
|
|
722
|
+
#
|
|
723
|
+
# @example Using a cron expression
|
|
724
|
+
# rule "cron expression" do
|
|
725
|
+
# cron "43 46 13 ? * ?"
|
|
726
|
+
# run { Light.on }
|
|
727
|
+
# end
|
|
728
|
+
#
|
|
729
|
+
# @overload cron(second: nil, minute: nil, hour: nil, dom: nil, month: nil, dow: nil, year: nil, attach: nil)
|
|
730
|
+
# The trigger can be created by specifying each field as keyword arguments.
|
|
731
|
+
# Omitted fields will default to `*` or `?` as appropriate.
|
|
732
|
+
#
|
|
733
|
+
# Each field is optional, but at least one must be specified.
|
|
734
|
+
#
|
|
735
|
+
# The same rules for the standard
|
|
736
|
+
# [cron expression](https://www.quartz-scheduler.org/documentation/quartz-2.2.2/tutorials/tutorial-lesson-06.html)
|
|
737
|
+
# apply for each field. For example, multiple values can be separated
|
|
738
|
+
# with a comma within a string.
|
|
739
|
+
#
|
|
740
|
+
# @param [Integer, String, nil] second
|
|
741
|
+
# @param [Integer, String, nil] minute
|
|
742
|
+
# @param [Integer, String, nil] hour
|
|
743
|
+
# @param [Integer, String, nil] dom
|
|
744
|
+
# @param [Integer, String, nil] month
|
|
745
|
+
# @param [Integer, String, nil] dow
|
|
746
|
+
# @param [Integer, String, nil] year
|
|
747
|
+
# @param [Object] attach object to be attached to the trigger
|
|
748
|
+
# @example
|
|
749
|
+
# # Run every 3 minutes on Monday to Friday
|
|
750
|
+
# # equivalent to the cron expression "0 */3 * ? * MON-FRI *"
|
|
751
|
+
# rule "Using cron fields" do
|
|
752
|
+
# cron second: 0, minute: "*/3", dow: "MON-FRI"
|
|
753
|
+
# run { logger.info "Cron rule executed" }
|
|
754
|
+
# end
|
|
755
|
+
#
|
|
756
|
+
# @return [void]
|
|
757
|
+
#
|
|
758
|
+
def cron(expression = nil, attach: nil, **fields)
|
|
759
|
+
if fields.any?
|
|
760
|
+
raise ArgumentError, "Cron elements cannot be used with a cron expression" if expression
|
|
761
|
+
|
|
762
|
+
cron_expression = Cron.from_fields(fields)
|
|
763
|
+
return cron(cron_expression, attach: attach)
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
raise ArgumentError, "Missing cron expression or elements" unless expression
|
|
767
|
+
|
|
768
|
+
cron = Cron.new(rule_triggers: @rule_triggers)
|
|
769
|
+
cron.trigger(config: { "cronExpression" => expression }, attach: attach)
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
#
|
|
773
|
+
# Create a rule that executes at the specified interval.
|
|
774
|
+
#
|
|
775
|
+
# @param [String,
|
|
776
|
+
# Duration,
|
|
777
|
+
# java.time.MonthDay,
|
|
778
|
+
# :second,
|
|
779
|
+
# :minute,
|
|
780
|
+
# :hour,
|
|
781
|
+
# :day,
|
|
782
|
+
# :week,
|
|
783
|
+
# :month,
|
|
784
|
+
# :year,
|
|
785
|
+
# :monday,
|
|
786
|
+
# :tuesday,
|
|
787
|
+
# :wednesday,
|
|
788
|
+
# :thursday,
|
|
789
|
+
# :friday,
|
|
790
|
+
# :saturday,
|
|
791
|
+
# :sunday] value
|
|
792
|
+
# When to execute rule.
|
|
793
|
+
# @param [LocalTime, String, nil] at What time of day to execute rule
|
|
794
|
+
# @param [Object] attach Object to be attached to the trigger
|
|
795
|
+
# @return [void]
|
|
796
|
+
#
|
|
797
|
+
# @example
|
|
798
|
+
# rule "Daily" do
|
|
799
|
+
# every :day, at: '5:15'
|
|
800
|
+
# run do
|
|
801
|
+
# Light.on
|
|
802
|
+
# end
|
|
803
|
+
# end
|
|
804
|
+
#
|
|
805
|
+
# @example The above rule could also be expressed using LocalTime class as below
|
|
806
|
+
# rule "Daily" do
|
|
807
|
+
# every :day, at: LocalTime.of(5, 15)
|
|
808
|
+
# run { Light.on }
|
|
809
|
+
# end
|
|
810
|
+
#
|
|
811
|
+
# @example
|
|
812
|
+
# rule "Weekly" do
|
|
813
|
+
# every :monday, at: '5:15'
|
|
814
|
+
# run do
|
|
815
|
+
# Light.on
|
|
816
|
+
# end
|
|
817
|
+
# end
|
|
818
|
+
#
|
|
819
|
+
# @example
|
|
820
|
+
# rule "Often" do
|
|
821
|
+
# every :minute
|
|
822
|
+
# run do
|
|
823
|
+
# Light.on
|
|
824
|
+
# end
|
|
825
|
+
# end
|
|
826
|
+
#
|
|
827
|
+
# @example
|
|
828
|
+
# rule "Hourly" do
|
|
829
|
+
# every :hour
|
|
830
|
+
# run do
|
|
831
|
+
# Light.on
|
|
832
|
+
# end
|
|
833
|
+
# end
|
|
834
|
+
#
|
|
835
|
+
# @example
|
|
836
|
+
# rule "Often" do
|
|
837
|
+
# every 5.minutes
|
|
838
|
+
# run do
|
|
839
|
+
# Light.on
|
|
840
|
+
# end
|
|
841
|
+
# end
|
|
842
|
+
#
|
|
843
|
+
# @example
|
|
844
|
+
# rule 'Every 14th of Feb at 2pm' do
|
|
845
|
+
# every '02-14', at: '2pm'
|
|
846
|
+
# run { logger.info "Happy Valentine's Day!" }
|
|
847
|
+
# end
|
|
848
|
+
#
|
|
849
|
+
def every(value, at: nil, attach: nil)
|
|
850
|
+
return every(java.time.MonthDay.parse(value), at: at, attach: attach) if value.is_a?(String)
|
|
851
|
+
|
|
852
|
+
@ruby_triggers << [:every, value, { at: at }]
|
|
853
|
+
|
|
854
|
+
cron_expression = case value
|
|
855
|
+
when Symbol then Cron.from_symbol(value, at)
|
|
856
|
+
when Duration then Cron.from_duration(value, at)
|
|
857
|
+
when java.time.MonthDay then Cron.from_monthday(value, at)
|
|
858
|
+
else raise ArgumentError, "Unknown interval"
|
|
859
|
+
end
|
|
860
|
+
cron(cron_expression, attach: attach)
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
#
|
|
864
|
+
# Run this rule when the script is loaded.
|
|
865
|
+
#
|
|
866
|
+
# Execute the rule on OpenHAB start up and whenever the script is
|
|
867
|
+
# reloaded. This is useful to perform initialization routines,
|
|
868
|
+
# especially when combined with other triggers.
|
|
869
|
+
#
|
|
870
|
+
# @param [true, false] run_on_start Run this rule on start, defaults to True
|
|
871
|
+
# @param [Object] attach object to be attached to the trigger
|
|
872
|
+
# @return [void]
|
|
873
|
+
#
|
|
874
|
+
# @example
|
|
875
|
+
# rule "startup rule" do
|
|
876
|
+
# on_start
|
|
877
|
+
# run do
|
|
878
|
+
# <calculate some item state>
|
|
879
|
+
# end
|
|
880
|
+
# end
|
|
881
|
+
#
|
|
882
|
+
# @example
|
|
883
|
+
# rule 'Ensure all security lights are on' do
|
|
884
|
+
# on_start
|
|
885
|
+
# run { Security_Lights.on }
|
|
886
|
+
# end
|
|
887
|
+
#
|
|
888
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
|
889
|
+
def on_start(run_on_start = true, attach: nil)
|
|
890
|
+
@on_start = Struct.new(:enabled, :attach).new(run_on_start, attach)
|
|
891
|
+
end
|
|
892
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
|
893
|
+
|
|
894
|
+
#
|
|
895
|
+
# Create a trigger for when an item or group receives a command
|
|
896
|
+
#
|
|
897
|
+
# The command/commands parameters are replicated for DSL fluency.
|
|
898
|
+
#
|
|
899
|
+
# The `event` passed to run blocks will be an
|
|
900
|
+
# {Core::Events::ItemCommandEvent}.
|
|
901
|
+
#
|
|
902
|
+
# @param [GenericItem, GroupItem::Members] items Items to create trigger for
|
|
903
|
+
# @param [Core::TypesCommand, Array<Command>, Range, Proc] command commands to match for trigger
|
|
904
|
+
# @param [Array<Command>, Range, Proc] commands Fluent alias for `command`
|
|
905
|
+
# @param [Object] attach object to be attached to the trigger
|
|
906
|
+
# @return [void]
|
|
907
|
+
#
|
|
908
|
+
# @example
|
|
909
|
+
# rule 'Execute rule when item received command' do
|
|
910
|
+
# received_command Alarm_Mode
|
|
911
|
+
# run { |event| logger.info("Item received command: #{event.command}" ) }
|
|
912
|
+
# end
|
|
913
|
+
#
|
|
914
|
+
# @example
|
|
915
|
+
# rule 'Execute rule when item receives specific command' do
|
|
916
|
+
# received_command Alarm_Mode, command: 7
|
|
917
|
+
# run { |event| logger.info("Item received command: #{event.command}" ) }
|
|
918
|
+
# end
|
|
919
|
+
#
|
|
920
|
+
# @example
|
|
921
|
+
# rule 'Execute rule when item receives one of many specific commands' do
|
|
922
|
+
# received_command Alarm_Mode, commands: [7,14]
|
|
923
|
+
# run { |event| logger.info("Item received command: #{event.command}" ) }
|
|
924
|
+
# end
|
|
925
|
+
#
|
|
926
|
+
# @example
|
|
927
|
+
# rule 'Execute rule when group receives a specific command' do
|
|
928
|
+
# received_command AlarmModes
|
|
929
|
+
# triggered { |item| logger.info("Group #{item.name} received command")}
|
|
930
|
+
# end
|
|
931
|
+
#
|
|
932
|
+
# @example
|
|
933
|
+
# rule 'Execute rule when member of group receives any command' do
|
|
934
|
+
# received_command AlarmModes.members
|
|
935
|
+
# triggered { |item| logger.info("Group item #{item.name} received command")}
|
|
936
|
+
# end
|
|
937
|
+
#
|
|
938
|
+
# @example
|
|
939
|
+
# rule 'Execute rule when member of group is changed to one of many states' do
|
|
940
|
+
# received_command AlarmModes.members, commands: [7, 14]
|
|
941
|
+
# triggered { |item| logger.info("Group item #{item.name} received command")}
|
|
942
|
+
# end
|
|
943
|
+
#
|
|
944
|
+
# @example
|
|
945
|
+
# rule 'Execute rule when item receives a range of commands' do
|
|
946
|
+
# received_command Alarm_Mode, commands: 7..14
|
|
947
|
+
# run { |event| logger.info("Item received command: #{event.command}" ) }
|
|
948
|
+
# end
|
|
949
|
+
#
|
|
950
|
+
# @example Works with procs
|
|
951
|
+
# rule 'Execute rule when Alarm Mode command is odd' do
|
|
952
|
+
# received_command Alarm_Mode, command: proc { |c| c.odd? }
|
|
953
|
+
# run { |event| logger.info("Item received command: #{event.command}" ) }
|
|
954
|
+
# end
|
|
955
|
+
#
|
|
956
|
+
# @example Works with lambdas
|
|
957
|
+
# rule 'Execute rule when Alarm Mode command is odd' do
|
|
958
|
+
# received_command Alarm_Mode, command: -> c { c.odd? }
|
|
959
|
+
# run { |event| logger.info("Item received command: #{event.command}" ) }
|
|
960
|
+
# end
|
|
961
|
+
#
|
|
962
|
+
def received_command(*items, command: nil, commands: nil, attach: nil)
|
|
963
|
+
command_trigger = Command.new(rule_triggers: @rule_triggers)
|
|
964
|
+
|
|
965
|
+
# if neither command nor commands is specified, ensure that we create
|
|
966
|
+
# a trigger that isn't looking for a specific command.
|
|
967
|
+
commands = [nil] if command.nil? && commands.nil?
|
|
968
|
+
commands = Array.wrap(command) | Array.wrap(commands)
|
|
969
|
+
|
|
970
|
+
@ruby_triggers << [:received_command, items, { command: commands }]
|
|
971
|
+
|
|
972
|
+
items.each do |item|
|
|
973
|
+
commands.each do |cmd|
|
|
974
|
+
logger.trace "Creating received command trigger for items #{item.inspect} and commands #{cmd.inspect}"
|
|
975
|
+
|
|
976
|
+
command_trigger.trigger(item: item, command: cmd, attach: attach)
|
|
977
|
+
end
|
|
978
|
+
end
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
#
|
|
982
|
+
# Creates a thing added trigger
|
|
983
|
+
#
|
|
984
|
+
# @param [Object] attach object to be attached to the trigger
|
|
985
|
+
# @return [void]
|
|
986
|
+
#
|
|
987
|
+
# @example
|
|
988
|
+
# rule "thing added" do
|
|
989
|
+
# thing_added
|
|
990
|
+
# run do |event|
|
|
991
|
+
# logger.info("#{event.thing.uid} added.")
|
|
992
|
+
# end
|
|
993
|
+
# end
|
|
994
|
+
def thing_added(attach: nil)
|
|
995
|
+
@ruby_triggers << [:thing_added]
|
|
996
|
+
trigger("core.GenericEventTrigger", eventTopic: "openhab/things/*/added",
|
|
997
|
+
eventTypes: "ThingAddedEvent", attach: attach)
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
#
|
|
1001
|
+
# Creates a thing removed trigger
|
|
1002
|
+
#
|
|
1003
|
+
# @param [Object] attach object to be attached to the trigger
|
|
1004
|
+
# @return [void]
|
|
1005
|
+
#
|
|
1006
|
+
# @example
|
|
1007
|
+
# rule "thing removed" do
|
|
1008
|
+
# thing_removed
|
|
1009
|
+
# run do |event|
|
|
1010
|
+
# logger.info("#{event.thing.uid} removed.")
|
|
1011
|
+
# end
|
|
1012
|
+
# end
|
|
1013
|
+
def thing_removed(attach: nil)
|
|
1014
|
+
@ruby_triggers << [:thing_removed]
|
|
1015
|
+
trigger("core.GenericEventTrigger", eventTopic: "openhab/things/*/removed",
|
|
1016
|
+
eventTypes: "ThingRemovedEvent", attach: attach)
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
#
|
|
1020
|
+
# Creates a thing updated trigger
|
|
1021
|
+
#
|
|
1022
|
+
# @param [Object] attach object to be attached to the trigger
|
|
1023
|
+
# @return [void]
|
|
1024
|
+
#
|
|
1025
|
+
# @example
|
|
1026
|
+
# rule "thing updated" do
|
|
1027
|
+
# thing_updated
|
|
1028
|
+
# run do |event|
|
|
1029
|
+
# logger.info("#{event.thing.uid} updated.")
|
|
1030
|
+
# end
|
|
1031
|
+
# end
|
|
1032
|
+
#
|
|
1033
|
+
def thing_updated(attach: nil)
|
|
1034
|
+
@ruby_triggers << [:thing_removed]
|
|
1035
|
+
trigger("core.GenericEventTrigger", eventTopic: "openhab/things/*/updated",
|
|
1036
|
+
eventTypes: "ThingUpdatedEvent", attach: attach)
|
|
1037
|
+
end
|
|
1038
|
+
|
|
1039
|
+
#
|
|
1040
|
+
# Create a generic trigger given the trigger type uid and a configuration hash
|
|
1041
|
+
#
|
|
1042
|
+
# This provides the ability to create a trigger type not already covered by the other methods.
|
|
1043
|
+
#
|
|
1044
|
+
# @param [String] type Trigger type UID
|
|
1045
|
+
# @param [Object] attach object to be attached to the trigger
|
|
1046
|
+
# @param [Hash] configuration A hash containing the trigger configuration entries
|
|
1047
|
+
# @return [void]
|
|
1048
|
+
#
|
|
1049
|
+
# @example Create a trigger for the [PID Controller Automation](https://www.openhab.org/addons/automation/pidcontroller/) add-on.
|
|
1050
|
+
# rule 'PID Control' do
|
|
1051
|
+
# trigger 'pidcontroller.trigger',
|
|
1052
|
+
# input: InputItem.name,
|
|
1053
|
+
# setpoint: SetPointItem.name,
|
|
1054
|
+
# kp: 10,
|
|
1055
|
+
# ki: 10,
|
|
1056
|
+
# kd: 10,
|
|
1057
|
+
# kdTimeConstant: 1,
|
|
1058
|
+
# loopTime: 1000
|
|
1059
|
+
#
|
|
1060
|
+
# run do |event|
|
|
1061
|
+
# logger.info("PID controller command: #{event.command}")
|
|
1062
|
+
# ControlItem << event.command
|
|
1063
|
+
# end
|
|
1064
|
+
# end
|
|
1065
|
+
#
|
|
1066
|
+
# @example DateTime Trigger
|
|
1067
|
+
# rule 'DateTime Trigger' do
|
|
1068
|
+
# description 'Triggers at a time specified in MyDateTimeItem'
|
|
1069
|
+
# trigger 'timer.DateTimeTrigger', itemName: MyDateTimeItem.name
|
|
1070
|
+
# run do
|
|
1071
|
+
# logger.info("DateTimeTrigger has been triggered")
|
|
1072
|
+
# end
|
|
1073
|
+
# end
|
|
1074
|
+
#
|
|
1075
|
+
def trigger(type, attach: nil, **configuration)
|
|
1076
|
+
logger.trace("Creating a generic trigger for type(#{type}) with configuration(#{configuration})")
|
|
1077
|
+
Triggers::Trigger.new(rule_triggers: @rule_triggers)
|
|
1078
|
+
.append_trigger(type: type, config: configuration, attach: attach)
|
|
1079
|
+
end
|
|
1080
|
+
|
|
1081
|
+
#
|
|
1082
|
+
# Create a trigger when item, group or thing is updated
|
|
1083
|
+
#
|
|
1084
|
+
# The `event` passed to run blocks will be an
|
|
1085
|
+
# {Core::Events::ItemStateEvent} or a
|
|
1086
|
+
# {Core::Events::ThingStatusInfoEvent} depending on if the triggering
|
|
1087
|
+
# element was an item or a thing.
|
|
1088
|
+
#
|
|
1089
|
+
# @param [GenericItem, GroupItem::Members, Thing] items
|
|
1090
|
+
# Objects to create trigger for.
|
|
1091
|
+
# @param [State, Array<State>, Range, Proc, Symbol, String] to
|
|
1092
|
+
# Only execute rule if the state matches `to` state(s). If the
|
|
1093
|
+
# updated element is a {Core::Things::Thing}, the `to` accepts
|
|
1094
|
+
# symbols and strings that match
|
|
1095
|
+
# [supported thing statuses](https://www.openhab.org/docs/concepts/things.html#thing-status).
|
|
1096
|
+
# @param [Object] attach object to be attached to the trigger
|
|
1097
|
+
# @return [void]
|
|
1098
|
+
#
|
|
1099
|
+
# @example
|
|
1100
|
+
# rule 'Execute rule when item is updated to any value' do
|
|
1101
|
+
# updated Alarm_Mode
|
|
1102
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
1103
|
+
# end
|
|
1104
|
+
#
|
|
1105
|
+
# @example
|
|
1106
|
+
# rule 'Execute rule when item is updated to specific number' do
|
|
1107
|
+
# updated Alarm_Mode, to: 7
|
|
1108
|
+
# run { logger.info("Alarm Mode Updated") }
|
|
1109
|
+
# end
|
|
1110
|
+
#
|
|
1111
|
+
# @example
|
|
1112
|
+
# rule 'Execute rule when item is updated to one of many specific states' do
|
|
1113
|
+
# updated Alarm_Mode, to: [7, 14]
|
|
1114
|
+
# run { logger.info("Alarm Mode Updated")}
|
|
1115
|
+
# end
|
|
1116
|
+
#
|
|
1117
|
+
# @example
|
|
1118
|
+
# rule 'Execute rule when item is within a range' do
|
|
1119
|
+
# updated Alarm_Mode, to: 7..14
|
|
1120
|
+
# run { logger.info("Alarm Mode Updated to a value between 7 and 14")}
|
|
1121
|
+
# end
|
|
1122
|
+
#
|
|
1123
|
+
# @example
|
|
1124
|
+
# rule 'Execute rule when group is updated to any state' do
|
|
1125
|
+
# updated AlarmModes
|
|
1126
|
+
# triggered { |item| logger.info("Group #{item.name} updated")}
|
|
1127
|
+
# end
|
|
1128
|
+
#
|
|
1129
|
+
# @example
|
|
1130
|
+
# rule 'Execute rule when member of group is changed to any state' do
|
|
1131
|
+
# updated AlarmModes.members
|
|
1132
|
+
# triggered { |item| logger.info("Group item #{item.name} updated")}
|
|
1133
|
+
# end
|
|
1134
|
+
#
|
|
1135
|
+
# @example
|
|
1136
|
+
# rule 'Execute rule when member of group is changed to one of many states' do
|
|
1137
|
+
# updated AlarmModes.members, to: [7, 14]
|
|
1138
|
+
# triggered { |item| logger.info("Group item #{item.name} updated")}
|
|
1139
|
+
# end
|
|
1140
|
+
#
|
|
1141
|
+
# @example Works with procs
|
|
1142
|
+
# rule 'Execute rule when member of group is changed to an odd state' do
|
|
1143
|
+
# updated AlarmModes.members, to: proc { |t| t.odd? }
|
|
1144
|
+
# triggered { |item| logger.info("Group item #{item.name} updated")}
|
|
1145
|
+
# end
|
|
1146
|
+
#
|
|
1147
|
+
# @example Works with lambdas:
|
|
1148
|
+
# rule 'Execute rule when member of group is changed to an odd state' do
|
|
1149
|
+
# updated AlarmModes.members, to: -> t { t.odd? }
|
|
1150
|
+
# triggered { |item| logger.info("Group item #{item.name} updated")}
|
|
1151
|
+
# end
|
|
1152
|
+
#
|
|
1153
|
+
# @example Works with things as well
|
|
1154
|
+
# rule 'Execute rule when thing is updated' do
|
|
1155
|
+
# updated things['astro:sun:home'], :to => :uninitialized
|
|
1156
|
+
# run { |event| logger.info("Thing #{event.uid} status <trigger> to #{event.status}") }
|
|
1157
|
+
# end
|
|
1158
|
+
#
|
|
1159
|
+
def updated(*items, to: nil, attach: nil)
|
|
1160
|
+
updated = Updated.new(rule_triggers: @rule_triggers)
|
|
1161
|
+
@ruby_triggers << [:updated, items, { to: to }]
|
|
1162
|
+
items.map do |item|
|
|
1163
|
+
logger.trace("Creating updated trigger for item(#{item}) to(#{to})")
|
|
1164
|
+
[to].flatten.map do |to_state|
|
|
1165
|
+
updated.trigger(item: item, to: to_state, attach: attach)
|
|
1166
|
+
end
|
|
1167
|
+
end.flatten
|
|
1168
|
+
end
|
|
1169
|
+
|
|
1170
|
+
#
|
|
1171
|
+
# Create a trigger to watch a path
|
|
1172
|
+
#
|
|
1173
|
+
# It provides the ability to create a trigger on file and directory
|
|
1174
|
+
# changes.
|
|
1175
|
+
#
|
|
1176
|
+
# If a file or a path that does not exist is supplied as the argument
|
|
1177
|
+
# to watch, the parent directory will be watched and the file or
|
|
1178
|
+
# non-existent part of the supplied path will become the glob. For
|
|
1179
|
+
# example, if the directory given is `/tmp/foo/bar` and `/tmp/foo`
|
|
1180
|
+
# exists but `bar` does not exist inside of of `/tmp/foo` then the
|
|
1181
|
+
# directory `/tmp/foo` will be watched for any files that match
|
|
1182
|
+
# `*/bar`.
|
|
1183
|
+
#
|
|
1184
|
+
# If the last part of the path contains any glob characters e.g.
|
|
1185
|
+
# `/tmp/foo/*bar`, the parent directory will be watched and the last
|
|
1186
|
+
# part of the path will be treated as if it was passed as the `glob`
|
|
1187
|
+
# argument. In other words, `watch '/tmp/foo/*bar'` is equivalent to
|
|
1188
|
+
# `watch '/tmp/foo', glob: '*bar'`
|
|
1189
|
+
#
|
|
1190
|
+
# The `event` passed to run blocks will be a {Events::WatchEvent}.
|
|
1191
|
+
#
|
|
1192
|
+
# @param [String] path Path to watch. Can be a directory of a file.
|
|
1193
|
+
# @param [String] glob
|
|
1194
|
+
# Limit events to paths matching this glob. Globs are matched using
|
|
1195
|
+
# [File.fnmatch?](https://ruby-doc.org/core-2.6/File.html#method-c-fnmatch-3F)
|
|
1196
|
+
# rules.
|
|
1197
|
+
# @param [Array<:created, :deleted, :modified>, :created, :deleted, :modified] for
|
|
1198
|
+
# Types of changes to watch for.
|
|
1199
|
+
# @param [Object] attach object to be attached to the trigger
|
|
1200
|
+
# @return [void]
|
|
1201
|
+
#
|
|
1202
|
+
# @example Watch `items` directory inside of the OpenHAB configuration path and log any changes.
|
|
1203
|
+
# rule 'watch directory' do
|
|
1204
|
+
# watch OpenHAB::Core.config_folder / 'items'
|
|
1205
|
+
# run { |event| logger.info("#{event.path.basename} - #{event.type}") }
|
|
1206
|
+
# end
|
|
1207
|
+
#
|
|
1208
|
+
# @example Watch `items` directory for files that end in `*.erb` and log any changes
|
|
1209
|
+
# rule 'watch directory' do
|
|
1210
|
+
# watch OpenHAB::Core.config_folder / 'items', glob: '*.erb'
|
|
1211
|
+
# run { |event| logger.info("#{event.path.basename} - #{event.type}") }
|
|
1212
|
+
# end
|
|
1213
|
+
#
|
|
1214
|
+
# @example Watch `items/foo.items` log any changes
|
|
1215
|
+
# rule 'watch directory' do
|
|
1216
|
+
# watch OpenHAB::Core.config_folder / 'items/foo.items'
|
|
1217
|
+
# run { |event| logger.info("#{event.path.basename} - #{event.type}") }
|
|
1218
|
+
# end
|
|
1219
|
+
#
|
|
1220
|
+
# @example Watch `items/*.items` log any changes
|
|
1221
|
+
# rule 'watch directory' do
|
|
1222
|
+
# watch OpenHAB::Core.config_folder / 'items/*.items'
|
|
1223
|
+
# run { |event| logger.info("#{event.path.basename} - #{event.type}") }
|
|
1224
|
+
# end
|
|
1225
|
+
#
|
|
1226
|
+
# @example Watch `items/*.items` for when items files are deleted or created (ignore changes)
|
|
1227
|
+
# rule 'watch directory' do
|
|
1228
|
+
# watch OpenHAB::Core.config_folder / 'items/*.items', for: [:deleted, :created]
|
|
1229
|
+
# run { |event| logger.info("#{event.path.basename} - #{event.type}") }
|
|
1230
|
+
# end
|
|
1231
|
+
#
|
|
1232
|
+
def watch(path, glob: "*", for: %i[created deleted modified], attach: nil)
|
|
1233
|
+
glob, path = Watch.glob_for_path(Pathname.new(path), glob)
|
|
1234
|
+
types = [binding.local_variable_get(:for)].flatten
|
|
1235
|
+
config = { path: path.to_s, types: types.map(&:to_s), glob: glob.to_s }
|
|
1236
|
+
|
|
1237
|
+
logger.trace "Creating a watch trigger for #{path} with glob #{glob} on types #{types.inspect}"
|
|
1238
|
+
Watch.new(rule_triggers: @rule_triggers).trigger(config: config, attach: attach)
|
|
1239
|
+
end
|
|
1240
|
+
|
|
1241
|
+
# @!endgroup
|
|
1242
|
+
|
|
1243
|
+
#
|
|
1244
|
+
# Checks if this rule should run on start
|
|
1245
|
+
#
|
|
1246
|
+
# @return [true, false] True if rule should run on start, false otherwise.
|
|
1247
|
+
#
|
|
1248
|
+
def on_start?
|
|
1249
|
+
@on_start.enabled
|
|
1250
|
+
end
|
|
1251
|
+
|
|
1252
|
+
#
|
|
1253
|
+
# Get the optional start attachment
|
|
1254
|
+
#
|
|
1255
|
+
# @return [Object] optional user provided attachment to the on_start method
|
|
1256
|
+
#
|
|
1257
|
+
# @!visibility private
|
|
1258
|
+
def start_attachment
|
|
1259
|
+
@on_start.attach
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
# @!visibility private
|
|
1263
|
+
#
|
|
1264
|
+
# Run the supplied block inside the object instance of the object that created the rule
|
|
1265
|
+
#
|
|
1266
|
+
# @yield [] Block executed in context of the object creating the rule
|
|
1267
|
+
#
|
|
1268
|
+
#
|
|
1269
|
+
def my(&block)
|
|
1270
|
+
@caller.instance_eval(&block)
|
|
1271
|
+
end
|
|
1272
|
+
|
|
1273
|
+
#
|
|
1274
|
+
# @return [String]
|
|
1275
|
+
#
|
|
1276
|
+
def inspect
|
|
1277
|
+
<<~TEXT.tr("\n", " ")
|
|
1278
|
+
#<OpenHAB::DSL::Rules::Builder: #{uid}
|
|
1279
|
+
triggers=#{triggers.inspect},
|
|
1280
|
+
run blocks=#{run.inspect},
|
|
1281
|
+
on_start=#{on_start?},
|
|
1282
|
+
Trigger Conditions=#{trigger_conditions.inspect},
|
|
1283
|
+
Trigger UIDs=#{triggers.map(&:id).inspect},
|
|
1284
|
+
Attachments=#{attachments.inspect}
|
|
1285
|
+
>
|
|
1286
|
+
TEXT
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1289
|
+
#
|
|
1290
|
+
# Process a rule based on the supplied configuration
|
|
1291
|
+
#
|
|
1292
|
+
# @param [String] script The source code of the rule
|
|
1293
|
+
#
|
|
1294
|
+
# @!visibility private
|
|
1295
|
+
def build(script)
|
|
1296
|
+
return unless create_rule?
|
|
1297
|
+
|
|
1298
|
+
rule = AutomationRule.new(config: self)
|
|
1299
|
+
added_rule = add_rule(rule)
|
|
1300
|
+
Rules.script_rules[rule.uid] = rule
|
|
1301
|
+
# add config so that MainUI can show the script
|
|
1302
|
+
added_rule.actions.first.configuration.put("type", "application/x-ruby")
|
|
1303
|
+
added_rule.actions.first.configuration.put("script", script)
|
|
1304
|
+
|
|
1305
|
+
rule.execute(nil, { "event" => Struct.new(:attachment).new(start_attachment) }) if on_start?
|
|
1306
|
+
added_rule
|
|
1307
|
+
end
|
|
1308
|
+
|
|
1309
|
+
private
|
|
1310
|
+
|
|
1311
|
+
# delegate to the caller's logger
|
|
1312
|
+
def logger
|
|
1313
|
+
@caller.send(:logger)
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
#
|
|
1317
|
+
# Should a rule be created based on rule configuration
|
|
1318
|
+
#
|
|
1319
|
+
# @return [true,false] true if it should be created, false otherwise
|
|
1320
|
+
#
|
|
1321
|
+
def create_rule?
|
|
1322
|
+
return true if tags.include?("Script")
|
|
1323
|
+
|
|
1324
|
+
if !triggers?
|
|
1325
|
+
logger.warn "Rule '#{uid}' has no triggers, not creating rule"
|
|
1326
|
+
elsif !execution_blocks?
|
|
1327
|
+
logger.warn "Rule '#{uid}' has no execution blocks, not creating rule"
|
|
1328
|
+
elsif !enabled
|
|
1329
|
+
logger.trace "Rule '#{uid}' marked as disabled, not creating rule."
|
|
1330
|
+
else
|
|
1331
|
+
return true
|
|
1332
|
+
end
|
|
1333
|
+
false
|
|
1334
|
+
end
|
|
1335
|
+
|
|
1336
|
+
#
|
|
1337
|
+
# Check if the rule has any triggers
|
|
1338
|
+
#
|
|
1339
|
+
# @return [true,false] True if rule has triggers, false otherwise
|
|
1340
|
+
#
|
|
1341
|
+
def triggers?
|
|
1342
|
+
on_start? || !triggers.empty?
|
|
1343
|
+
end
|
|
1344
|
+
|
|
1345
|
+
#
|
|
1346
|
+
# Check if the rule has any execution blocks
|
|
1347
|
+
#
|
|
1348
|
+
# @return [true,false] True if rule has execution blocks, false otherwise
|
|
1349
|
+
#
|
|
1350
|
+
def execution_blocks?
|
|
1351
|
+
!(run || []).empty?
|
|
1352
|
+
end
|
|
1353
|
+
|
|
1354
|
+
#
|
|
1355
|
+
# Add a rule to the automation manager
|
|
1356
|
+
#
|
|
1357
|
+
# @param [org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule] rule to add
|
|
1358
|
+
#
|
|
1359
|
+
#
|
|
1360
|
+
def add_rule(rule)
|
|
1361
|
+
base_uid = rule.uid
|
|
1362
|
+
duplicate_index = 1
|
|
1363
|
+
while $rules.get(rule.uid)
|
|
1364
|
+
duplicate_index += 1
|
|
1365
|
+
rule.uid = "#{base_uid} (#{duplicate_index})"
|
|
1366
|
+
end
|
|
1367
|
+
logger.trace("Adding rule: #{rule}")
|
|
1368
|
+
Core.automation_manager.add_rule(rule)
|
|
1369
|
+
end
|
|
1370
|
+
end
|
|
1371
|
+
end
|
|
1372
|
+
end
|
|
1373
|
+
end
|