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
data/lib/openhab/dsl.rb
ADDED
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "java"
|
|
4
|
+
require "method_source"
|
|
5
|
+
|
|
6
|
+
require "bundler/inline"
|
|
7
|
+
|
|
8
|
+
require_relative "log"
|
|
9
|
+
require_relative "osgi"
|
|
10
|
+
require_relative "core"
|
|
11
|
+
|
|
12
|
+
Dir[File.expand_path("dsl/**/*.rb", __dir__)].sort.each do |f|
|
|
13
|
+
require f
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
require_relative "core_ext"
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Main OpenHAB Module
|
|
20
|
+
#
|
|
21
|
+
module OpenHAB
|
|
22
|
+
#
|
|
23
|
+
# The main DSL available to rules.
|
|
24
|
+
#
|
|
25
|
+
# Methods on this module are extended onto `main`, the top level `self` in
|
|
26
|
+
# any file. You can also access them as class methods on the module for use
|
|
27
|
+
# inside of other classes, or include the module.
|
|
28
|
+
#
|
|
29
|
+
module DSL
|
|
30
|
+
# include this before Core::Actions so that Core::Action's method_missing
|
|
31
|
+
# takes priority
|
|
32
|
+
include Core::EntityLookup
|
|
33
|
+
[Core::Actions, Rules::Terse, ScriptHandling].each do |mod|
|
|
34
|
+
# make these available both as regular and class methods
|
|
35
|
+
include mod
|
|
36
|
+
singleton_class.include mod
|
|
37
|
+
public_class_method(*mod.private_instance_methods)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module_function
|
|
41
|
+
|
|
42
|
+
# @!group Rule Creation
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Create a new rule
|
|
46
|
+
#
|
|
47
|
+
# @param [String] name The rule name
|
|
48
|
+
# @yield Block executed in the context of a {Rules::Builder}
|
|
49
|
+
# @yieldparam [Rules::Builder] rule
|
|
50
|
+
# Optional parameter to access the rule configuration from within execution blocks and guards.
|
|
51
|
+
# @return [org.openhab.core.automation.Rule] The OpenHAB Rule object
|
|
52
|
+
#
|
|
53
|
+
# @see OpenHAB::DSL::Rules::Builder Rule builder for details on rule triggers, guards and execution blocks
|
|
54
|
+
# @see Rules::Terse Terse Rules
|
|
55
|
+
#
|
|
56
|
+
# @example
|
|
57
|
+
# require "openhab/dsl"
|
|
58
|
+
#
|
|
59
|
+
# rule "name" do
|
|
60
|
+
# <zero or more triggers>
|
|
61
|
+
# <zero or more execution blocks>
|
|
62
|
+
# <zero or more guards>
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
def rule(name = nil, id: nil, script: nil, binding: nil, &block)
|
|
66
|
+
raise ArgumentError, "Block is required" unless block
|
|
67
|
+
|
|
68
|
+
id ||= Rules::NameInference.infer_rule_id_from_name(name) if name
|
|
69
|
+
id ||= Rules::NameInference.infer_rule_id_from_block(block)
|
|
70
|
+
script ||= block.source rescue nil # rubocop:disable Style/RescueModifier
|
|
71
|
+
|
|
72
|
+
builder = nil
|
|
73
|
+
|
|
74
|
+
ThreadLocal.thread_local(openhab_rule_type: "rule", openhab_rule_uid: id) do
|
|
75
|
+
builder = Rules::Builder.new(binding || block.binding)
|
|
76
|
+
builder.uid(id)
|
|
77
|
+
builder.instance_exec(builder, &block)
|
|
78
|
+
builder.guard = Rules::Guard.new(run_context: builder.caller, only_if: builder.only_if,
|
|
79
|
+
not_if: builder.not_if)
|
|
80
|
+
|
|
81
|
+
name ||= Rules::NameInference.infer_rule_name(builder)
|
|
82
|
+
name ||= id
|
|
83
|
+
|
|
84
|
+
builder.name(name)
|
|
85
|
+
logger.trace { builder.inspect }
|
|
86
|
+
builder.build(script)
|
|
87
|
+
rescue Exception => e
|
|
88
|
+
if (defined?(::RSpec::Expectations::ExpectationNotMetError) &&
|
|
89
|
+
e.is_a?(::RSpec::Expectations::ExpectationNotMetError)) ||
|
|
90
|
+
(defined?(::RSpec::Mocks::MockExpectationError) && e.is_a?(::RSpec::Mocks::MockExpectationError))
|
|
91
|
+
raise e
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
builder.send(:logger).log_exception(e)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#
|
|
99
|
+
# Create a new script
|
|
100
|
+
#
|
|
101
|
+
# A script is a rule with no triggers. It can be called by various other actions,
|
|
102
|
+
# such as the Run Rules action.
|
|
103
|
+
#
|
|
104
|
+
# @param [String] id The script's ID
|
|
105
|
+
# @param [String] name A descriptive name
|
|
106
|
+
# @yield [] Block executed when the script is executed.
|
|
107
|
+
#
|
|
108
|
+
def script(name = nil, id: nil, script: nil, &block)
|
|
109
|
+
raise ArgumentError, "Block is required" unless block
|
|
110
|
+
|
|
111
|
+
id ||= Rules::NameInference.infer_rule_id_from_name(name) if name
|
|
112
|
+
id ||= Rules::NameInference.infer_rule_id_from_block(block)
|
|
113
|
+
name ||= id
|
|
114
|
+
script ||= block.source rescue nil # rubocop:disable Style/RescueModifier
|
|
115
|
+
|
|
116
|
+
builder = nil
|
|
117
|
+
ThreadLocal.thread_local(openhab_rule_type: "script", openhab_rule_uid: id) do
|
|
118
|
+
begin
|
|
119
|
+
builder = Rules::Builder.new(block.binding)
|
|
120
|
+
builder.uid(id)
|
|
121
|
+
builder.tags(["Script"])
|
|
122
|
+
builder.name(name)
|
|
123
|
+
builder.script(&block)
|
|
124
|
+
logger.trace { builder.inspect }
|
|
125
|
+
builder.build(script)
|
|
126
|
+
end
|
|
127
|
+
rescue Exception => e
|
|
128
|
+
builder.send(:logger).log_exception(e)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @!group Rule Support
|
|
133
|
+
|
|
134
|
+
#
|
|
135
|
+
# Defines a new profile that can be applied to item channel links.
|
|
136
|
+
#
|
|
137
|
+
# @param [String, Symbol] id The id for the profile.
|
|
138
|
+
# @yield [event, command: nil, state: nil, link:, item:, channel_uid:, configuration:, context:]
|
|
139
|
+
# All keyword params are optional. Any that aren't defined won't be passed.
|
|
140
|
+
# @yieldparam [Core::Things::ProfileCallback] callback
|
|
141
|
+
# The callback to be used to customize the action taken.
|
|
142
|
+
# @yieldparam [:command_from_item, :state_from_item, :command_from_handler, :state_from_handler] event
|
|
143
|
+
# The event that needs to be processed.
|
|
144
|
+
# @yieldparam [Command, nil] command
|
|
145
|
+
# The command being sent for `:command_from_item` and `:command_from_handler` events.
|
|
146
|
+
# @yieldparam [State, nil] state
|
|
147
|
+
# The state being sent for `:state_from_item` and `:state_from_handler` events.
|
|
148
|
+
# @yieldparam [Core::Things::ItemChannelLink] link
|
|
149
|
+
# The link between the item and the channel, including its configuration.
|
|
150
|
+
# @yieldparam [GenericItem] item The linked item.
|
|
151
|
+
# @yieldparam [Core::Things::ChannelUID] channel_uid The linked channel.
|
|
152
|
+
# @yieldparam [Hash] configuration The profile configuration.
|
|
153
|
+
# @yieldparam [org.openhab.core.thing.profiles.ProfileContext] context The profile context.
|
|
154
|
+
# @yieldreturn [Boolean] Return true from the block in order to have default processing.
|
|
155
|
+
# @return [void]
|
|
156
|
+
#
|
|
157
|
+
# @see org.openhab.thing.Profile
|
|
158
|
+
# @see org.openhab.thing.StateProfile
|
|
159
|
+
#
|
|
160
|
+
# @example
|
|
161
|
+
# profile(:veto_closing_shades) do |event, item:, command: nil|
|
|
162
|
+
# next false if command&.down?
|
|
163
|
+
#
|
|
164
|
+
# true
|
|
165
|
+
# end
|
|
166
|
+
#
|
|
167
|
+
# items.build do
|
|
168
|
+
# rollershutter_item "MyShade" do
|
|
169
|
+
# channel "thing:rollershutter", profile: "ruby:veto_closing_shades"
|
|
170
|
+
# end
|
|
171
|
+
# end
|
|
172
|
+
# # can also be referenced from an `.items` file:
|
|
173
|
+
# # Rollershutter MyShade { channel="thing:rollershutter"[profile="ruby:veto_closing_shades"] }
|
|
174
|
+
#
|
|
175
|
+
def profile(id, &block)
|
|
176
|
+
raise ArgumentError, "Block is required" unless block
|
|
177
|
+
|
|
178
|
+
uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
|
|
179
|
+
|
|
180
|
+
ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
|
|
181
|
+
Core::ProfileFactory.instance.register(uid, block)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# Remove a rule
|
|
187
|
+
#
|
|
188
|
+
# @param [String, org.openhab.core.automation.Rule] uid The rule UID or the Rule object to remove.
|
|
189
|
+
# @return [void]
|
|
190
|
+
#
|
|
191
|
+
# @example
|
|
192
|
+
# my_rule = rule do
|
|
193
|
+
# every :day
|
|
194
|
+
# run { nil }
|
|
195
|
+
# end
|
|
196
|
+
#
|
|
197
|
+
# remove_rule(my_rule)
|
|
198
|
+
#
|
|
199
|
+
def remove_rule(uid)
|
|
200
|
+
uid = uid.uid if uid.respond_to?(:uid)
|
|
201
|
+
automation_rule = Rules.script_rules.delete(uid)
|
|
202
|
+
raise "Rule #{uid} doesn't exist to remove" unless automation_rule
|
|
203
|
+
|
|
204
|
+
automation_rule.cleanup
|
|
205
|
+
# automation_manager doesn't have a remove method, so just have to
|
|
206
|
+
# remove it directly from the provider
|
|
207
|
+
Rules.scripted_rule_provider.remove_rule(uid)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
#
|
|
211
|
+
# Manually trigger a rule by ID
|
|
212
|
+
#
|
|
213
|
+
# @param [String] uid The rule ID
|
|
214
|
+
# @param [Object, nil] event The event to pass to the rule's execution blocks.
|
|
215
|
+
# @return [void]
|
|
216
|
+
#
|
|
217
|
+
def trigger_rule(uid, event = nil)
|
|
218
|
+
Rules.script_rules.fetch(uid).execute(nil, { "event" => event })
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# @!group Object Access
|
|
222
|
+
|
|
223
|
+
#
|
|
224
|
+
# Fetches all items from the item registry
|
|
225
|
+
#
|
|
226
|
+
# @return [Core::Items::Registry]
|
|
227
|
+
#
|
|
228
|
+
# The examples all assume the following items exist.
|
|
229
|
+
#
|
|
230
|
+
# ```xtend
|
|
231
|
+
# Dimmer DimmerTest "Test Dimmer"
|
|
232
|
+
# Switch SwitchTest "Test Switch"
|
|
233
|
+
# ```
|
|
234
|
+
#
|
|
235
|
+
# @example
|
|
236
|
+
# logger.info("Item Count: #{items.count}") # Item Count: 2
|
|
237
|
+
# logger.info("Items: #{items.map(&:label).sort.join(', ')}") # Items: Test Dimmer, Test Switch'
|
|
238
|
+
# logger.info("DimmerTest exists? #{items.key?('DimmerTest')}") # DimmerTest exists? true
|
|
239
|
+
# logger.info("StringTest exists? #{items.key?('StringTest')}") # StringTest exists? false
|
|
240
|
+
#
|
|
241
|
+
# @example
|
|
242
|
+
# rule 'Use dynamic item lookup to increase related dimmer brightness when switch is turned on' do
|
|
243
|
+
# changed SwitchTest, to: ON
|
|
244
|
+
# triggered { |item| items[item.name.gsub('Switch','Dimmer')].brighten(10) }
|
|
245
|
+
# end
|
|
246
|
+
#
|
|
247
|
+
# @example
|
|
248
|
+
# rule 'search for a suitable item' do
|
|
249
|
+
# on_start
|
|
250
|
+
# triggered do
|
|
251
|
+
# # Send ON to DimmerTest if it exists, otherwise send it to SwitchTest
|
|
252
|
+
# (items['DimmerTest'] || items['SwitchTest'])&.on
|
|
253
|
+
# end
|
|
254
|
+
# end
|
|
255
|
+
#
|
|
256
|
+
def items
|
|
257
|
+
Core::Items::Registry.instance
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
#
|
|
261
|
+
# Get all things known to OpenHAB
|
|
262
|
+
#
|
|
263
|
+
# @return [Core::Things::Registry] all Thing objects known to OpenHAB
|
|
264
|
+
#
|
|
265
|
+
# @example
|
|
266
|
+
# things.each { |thing| logger.info("Thing: #{thing.uid}")}
|
|
267
|
+
# logger.info("Thing: #{things['astro:sun:home'].uid}")
|
|
268
|
+
# homie_things = things.select { |t| t.thing_type_uid == "mqtt:homie300" }
|
|
269
|
+
# zwave_things = things.select { |t| t.binding_id == "zwave" }
|
|
270
|
+
# homeseer_dimmers = zwave_things.select { |t| t.thing_type_uid.id == "homeseer_hswd200_00_000" }
|
|
271
|
+
# things['zwave:device:512:node90'].uid.bridge_ids # => ["512"]
|
|
272
|
+
# things['mqtt:topic:4'].uid.bridge_ids # => []
|
|
273
|
+
#
|
|
274
|
+
def things
|
|
275
|
+
Core::Things::Registry.instance
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
#
|
|
279
|
+
# Provides access to timers created by {after}
|
|
280
|
+
#
|
|
281
|
+
# @return [TimerManager]
|
|
282
|
+
def timers
|
|
283
|
+
TimerManager.instance
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# @!group Utilities
|
|
287
|
+
|
|
288
|
+
#
|
|
289
|
+
# Create a timer and execute the supplied block after the specified duration
|
|
290
|
+
#
|
|
291
|
+
# ### Reentrant Timers
|
|
292
|
+
#
|
|
293
|
+
# Timers with an id are reentrant by id. Reentrant means that when the same id is encountered,
|
|
294
|
+
# the timer is rescheduled rather than creating a second new timer.
|
|
295
|
+
#
|
|
296
|
+
# This removes the need for the usual boilerplate code to manually keep track of timer objects.
|
|
297
|
+
#
|
|
298
|
+
# Timers with `id` can be managed with the built-in {timers} object.
|
|
299
|
+
#
|
|
300
|
+
# When a timer is cancelled, it will be removed from the object.
|
|
301
|
+
#
|
|
302
|
+
# Be sure that your ids are unique. For example, if you're using {GenericItem items} as your
|
|
303
|
+
# ids, you either need to be sure you don't use the same item for multiple logical contexts,
|
|
304
|
+
# or you need to make your id more specific, by doing something like embedding the item in
|
|
305
|
+
# array with a symbol of the timer's purpose, like `[:vacancy, item]`. But also note that
|
|
306
|
+
# assuming default settings, every Ruby file (for file-based rules) or UI rule gets its
|
|
307
|
+
# own instance of the timers object, so you don't need to worry about collisions among
|
|
308
|
+
# different files.
|
|
309
|
+
#
|
|
310
|
+
# @see timers
|
|
311
|
+
# @see Rules::Builder#changed
|
|
312
|
+
# @see Items::TimedCommand
|
|
313
|
+
#
|
|
314
|
+
# @param [java.time.temporal.TemporalAmount, #to_zoned_date_time, Proc] duration
|
|
315
|
+
# Duration after which to execute the block
|
|
316
|
+
# @param [Object] id ID to associate with timer. The timer can be managed via {timers}.
|
|
317
|
+
# @param [true,false] reschedule Reschedule the timer if it already exists.
|
|
318
|
+
# @yield Block to execute when the timer is elapsed.
|
|
319
|
+
# @yieldparam [Core::Timer] timer
|
|
320
|
+
#
|
|
321
|
+
# @return [Core::Timer] if `reschedule` is false, the existing timer.
|
|
322
|
+
# Otherwise the new timer.
|
|
323
|
+
#
|
|
324
|
+
# @example Create a simple timer
|
|
325
|
+
# after 5.seconds do
|
|
326
|
+
# logger.info("Timer Fired")
|
|
327
|
+
# end
|
|
328
|
+
#
|
|
329
|
+
# @example Timers delegate methods to OpenHAB timer objects
|
|
330
|
+
# after 1.second do |timer|
|
|
331
|
+
# logger.info("Timer is active? #{timer.active?}")
|
|
332
|
+
# end
|
|
333
|
+
#
|
|
334
|
+
# @example Timers can be rescheduled to run again, waiting the original duration
|
|
335
|
+
# after 3.seconds do |timer|
|
|
336
|
+
# logger.info("Timer Fired")
|
|
337
|
+
# timer.reschedule
|
|
338
|
+
# end
|
|
339
|
+
#
|
|
340
|
+
# @example Timers can be rescheduled for different durations
|
|
341
|
+
# after 3.seconds do |timer|
|
|
342
|
+
# logger.info("Timer Fired")
|
|
343
|
+
# timer.reschedule 5.seconds
|
|
344
|
+
# end
|
|
345
|
+
#
|
|
346
|
+
# @example Timers can be manipulated through the returned object
|
|
347
|
+
# mytimer = after 1.minute do
|
|
348
|
+
# logger.info("It has been 1 minute")
|
|
349
|
+
# end
|
|
350
|
+
#
|
|
351
|
+
# mytimer.cancel
|
|
352
|
+
#
|
|
353
|
+
# @example Reentrant timers will automatically reschedule if the same id is encountered again
|
|
354
|
+
# rule "Turn off closet light after 10 minutes" do
|
|
355
|
+
# changed ClosetLights.members, to: ON
|
|
356
|
+
# triggered do |item|
|
|
357
|
+
# after 10.minutes, id: item do
|
|
358
|
+
# item.ensure.off
|
|
359
|
+
# end
|
|
360
|
+
# end
|
|
361
|
+
# end
|
|
362
|
+
#
|
|
363
|
+
# @example Timers with id can be managed through the built-in `timers` object
|
|
364
|
+
# after 1.minute, id: :foo do
|
|
365
|
+
# logger.info("managed timer has fired")
|
|
366
|
+
# end
|
|
367
|
+
#
|
|
368
|
+
# timers.cancel(:foo)
|
|
369
|
+
#
|
|
370
|
+
# if timers.include?(:foo)
|
|
371
|
+
# logger.info("The timer :foo is not active")
|
|
372
|
+
# end
|
|
373
|
+
#
|
|
374
|
+
# @example Only create a new timer if it isn't already scheduled
|
|
375
|
+
# after(1.minute, id: :foo, reschedule: false) do
|
|
376
|
+
# logger.info("Timer fired")
|
|
377
|
+
# end
|
|
378
|
+
#
|
|
379
|
+
def after(duration, id: nil, reschedule: true, &block)
|
|
380
|
+
raise ArgumentError, "Block is required" unless block
|
|
381
|
+
|
|
382
|
+
# Carry rule name to timer
|
|
383
|
+
thread_locals = ThreadLocal.persist
|
|
384
|
+
timers.create(duration, id: id, reschedule: reschedule, thread_locals: thread_locals, block: block)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
#
|
|
388
|
+
# Convert a string based range into a range of LocalTime, LocalDate, MonthDay, or ZonedDateTime
|
|
389
|
+
# depending on the format of the string.
|
|
390
|
+
#
|
|
391
|
+
# @return [Range] converted range object
|
|
392
|
+
#
|
|
393
|
+
# @example Range#cover?
|
|
394
|
+
# logger.info("Within month-day range") if between('02-20'..'06-01').cover?(MonthDay.now)
|
|
395
|
+
#
|
|
396
|
+
# @example Use in a Case
|
|
397
|
+
# case MonthDay.now
|
|
398
|
+
# when between('01-01'..'03-31')
|
|
399
|
+
# logger.info("First quarter")
|
|
400
|
+
# when between('04-01'..'06-30')
|
|
401
|
+
# logger.info("Second quarter")
|
|
402
|
+
# end
|
|
403
|
+
#
|
|
404
|
+
# @example Create a time range
|
|
405
|
+
# between('7am'..'12pm').cover?(LocalTime.now)
|
|
406
|
+
#
|
|
407
|
+
def between(range)
|
|
408
|
+
raise ArgumentError, "Supplied object must be a range" unless range.is_a?(Range)
|
|
409
|
+
|
|
410
|
+
start = try_parse_time_like(range.begin)
|
|
411
|
+
finish = try_parse_time_like(range.end)
|
|
412
|
+
Range.new(start, finish, range.exclude_end?)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
#
|
|
416
|
+
# Store states of supplied items
|
|
417
|
+
#
|
|
418
|
+
# Takes one or more items and returns a map `{Item => State}` with the
|
|
419
|
+
# current state of each item. It is implemented by calling OpenHAB's
|
|
420
|
+
# [events.storeStates()](https://www.openhab.org/docs/configuration/actions.html#event-bus-actions).
|
|
421
|
+
#
|
|
422
|
+
# @param [GenericItem] items Items to store states of.
|
|
423
|
+
#
|
|
424
|
+
# @return [Core::Items::StateStorage] item states
|
|
425
|
+
#
|
|
426
|
+
# @example
|
|
427
|
+
# states = store_states Item1, Item2
|
|
428
|
+
# ...
|
|
429
|
+
# states.restore
|
|
430
|
+
#
|
|
431
|
+
# @example With a block
|
|
432
|
+
# store_states Item1, Item2 do
|
|
433
|
+
# ...
|
|
434
|
+
# end # the states will be restored here
|
|
435
|
+
#
|
|
436
|
+
def store_states(*items)
|
|
437
|
+
items = items.flatten.map do |item|
|
|
438
|
+
item.respond_to?(:__getobj__) ? item.__getobj__ : item
|
|
439
|
+
end
|
|
440
|
+
states = Core::Items::StateStorage.from_items(*items)
|
|
441
|
+
if block_given?
|
|
442
|
+
yield
|
|
443
|
+
states.restore
|
|
444
|
+
end
|
|
445
|
+
states
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
#
|
|
449
|
+
# @!group Block Modifiers
|
|
450
|
+
# These methods allow certain operations to be grouped inside the given block
|
|
451
|
+
# to reduce repetitions
|
|
452
|
+
#
|
|
453
|
+
|
|
454
|
+
#
|
|
455
|
+
# Global method that takes a block and for the duration of the block
|
|
456
|
+
# all commands sent will check if the item is in the command's state
|
|
457
|
+
# before sending the command.
|
|
458
|
+
#
|
|
459
|
+
# @yield
|
|
460
|
+
# @return [Object] The result of the block.
|
|
461
|
+
#
|
|
462
|
+
# @example Turn on several switches only if they're not already on
|
|
463
|
+
# ensure_states do
|
|
464
|
+
# Switch1.on
|
|
465
|
+
# Switch2.on
|
|
466
|
+
# end
|
|
467
|
+
#
|
|
468
|
+
# @example
|
|
469
|
+
# # VirtualSwitch is in state `ON`
|
|
470
|
+
# ensure_states do
|
|
471
|
+
# VirtualSwitch << ON # No command will be sent
|
|
472
|
+
# VirtualSwitch.update(ON) # No update will be posted
|
|
473
|
+
# VirtualSwitch << OFF # Off command will be sent
|
|
474
|
+
# VirtualSwitch.update(OFF) # No update will be posted
|
|
475
|
+
# end
|
|
476
|
+
#
|
|
477
|
+
# @example
|
|
478
|
+
# ensure_states do
|
|
479
|
+
# rule 'Items in an execution block will have ensure_states applied to them' do
|
|
480
|
+
# changed VirtualSwitch
|
|
481
|
+
# run do
|
|
482
|
+
# VirtualSwitch.on
|
|
483
|
+
# VirtualSwitch2.on
|
|
484
|
+
# end
|
|
485
|
+
# end
|
|
486
|
+
# end
|
|
487
|
+
#
|
|
488
|
+
# @example
|
|
489
|
+
# rule 'ensure_states must be in an execution block' do
|
|
490
|
+
# changed VirtualSwitch
|
|
491
|
+
# run do
|
|
492
|
+
# ensure_states do
|
|
493
|
+
# VirtualSwitch.on
|
|
494
|
+
# VirtualSwitch2.on
|
|
495
|
+
# end
|
|
496
|
+
# end
|
|
497
|
+
# end
|
|
498
|
+
#
|
|
499
|
+
def ensure_states
|
|
500
|
+
old = Thread.current[:openhab_ensure_states]
|
|
501
|
+
Thread.current[:openhab_ensure_states] = true
|
|
502
|
+
yield
|
|
503
|
+
ensure
|
|
504
|
+
Thread.current[:openhab_ensure_states] = old
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
#
|
|
508
|
+
# Sets a thread local variable to set the default persistence service
|
|
509
|
+
# for method calls inside the block
|
|
510
|
+
#
|
|
511
|
+
# @example
|
|
512
|
+
# persistence(:influxdb) do
|
|
513
|
+
# Item1.persist
|
|
514
|
+
# Item1.changed_since(1.hour)
|
|
515
|
+
# Item1.average_since(12.hours)
|
|
516
|
+
# end
|
|
517
|
+
#
|
|
518
|
+
# @see OpenHAB::Core::Items::Persistence
|
|
519
|
+
#
|
|
520
|
+
# @param [Object] service service either as a String or a Symbol
|
|
521
|
+
# @yield [] Block executed in context of the supplied persistence service
|
|
522
|
+
# @return [Object] The return value from the block.
|
|
523
|
+
#
|
|
524
|
+
def persistence(service)
|
|
525
|
+
old = Thread.current[:openhab_persistence_service]
|
|
526
|
+
Thread.current[:openhab_persistence_service] = service
|
|
527
|
+
yield
|
|
528
|
+
ensure
|
|
529
|
+
Thread.current[:openhab_persistence_service] = old
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
#
|
|
533
|
+
# Sets the implicit unit(s) for operations inside the block.
|
|
534
|
+
#
|
|
535
|
+
# @yield
|
|
536
|
+
#
|
|
537
|
+
# @overload unit(*units)
|
|
538
|
+
# Sets the implicit unit(s) for this thread such that classes
|
|
539
|
+
# operating inside the block can perform automatic conversions to the
|
|
540
|
+
# supplied unit for {QuantityType}.
|
|
541
|
+
#
|
|
542
|
+
# To facilitate conversion of multiple dimensioned and dimensionless
|
|
543
|
+
# numbers the unit block may be used. The unit block attempts to do the
|
|
544
|
+
# _right thing_ based on the mix of dimensioned and dimensionless items
|
|
545
|
+
# within the block. Specifically all dimensionless items are converted to
|
|
546
|
+
# the supplied unit, except when they are used for multiplication or
|
|
547
|
+
# division.
|
|
548
|
+
#
|
|
549
|
+
# @param [String, javax.measure.Unit] units
|
|
550
|
+
# Unit or String representing unit
|
|
551
|
+
# @yield [] The block will be executed in the context of the specified unit(s).
|
|
552
|
+
# @return [Object] the result of the block
|
|
553
|
+
#
|
|
554
|
+
# @example Arithmetic Operations Between QuantityType and Numeric
|
|
555
|
+
# # Number:Temperature NumberC = 23 °C
|
|
556
|
+
# # Number:Temperature NumberF = 70 °F
|
|
557
|
+
# # Number Dimensionless = 2
|
|
558
|
+
# unit('°F') { NumberC.state - NumberF.state < 4 } # => true
|
|
559
|
+
# unit('°F') { NumberC.state - 24 | '°C' < 4 } # => true
|
|
560
|
+
# unit('°F') { (24 | '°C') - NumberC.state < 4 } # => true
|
|
561
|
+
# unit('°C') { NumberF.state - 20 < 2 } # => true
|
|
562
|
+
# unit('°C') { NumberF.state - Dimensionless.state } # => 19.11 °C
|
|
563
|
+
# unit('°C') { NumberF.state - Dimensionless.state < 20 } # => true
|
|
564
|
+
# unit('°C') { Dimensionless.state + NumberC.state == 25 } # => true
|
|
565
|
+
# unit('°C') { 2 + NumberC.state == 25 } # => true
|
|
566
|
+
# unit('°C') { Dimensionless.state * NumberC.state == 46 } # => true
|
|
567
|
+
# unit('°C') { 2 * NumberC.state == 46 } # => true
|
|
568
|
+
# unit('°C') { ( (2 * (NumberF.state + NumberC.state) ) / Dimensionless.state ) < 45 } # => true
|
|
569
|
+
# unit('°C') { [NumberC.state, NumberF.state, Dimensionless.state].min } # => 2
|
|
570
|
+
#
|
|
571
|
+
# @example Commands and Updates inside a unit block
|
|
572
|
+
# unit('°F') { NumberC << 32 }; NumberC.state # => 0 °C
|
|
573
|
+
# # Equivalent to
|
|
574
|
+
# NumberC << "32 °F"
|
|
575
|
+
# # or
|
|
576
|
+
# NumberC << 32 | "°F"
|
|
577
|
+
#
|
|
578
|
+
# @example Specifying Multiple Units
|
|
579
|
+
# unit("°C", "kW") do
|
|
580
|
+
# TemperatureItem.update("50 °F")
|
|
581
|
+
# TemperatureItem.state < 20 # => true. TemperatureItem.state < 20 °C
|
|
582
|
+
# PowerUsage.update("3000 W")
|
|
583
|
+
# PowerUsage.state < 10 # => true. PowerUsage.state < 10 kW
|
|
584
|
+
# end
|
|
585
|
+
#
|
|
586
|
+
# @overload unit(dimension)
|
|
587
|
+
# @param [javax.measure.Dimension] dimension The dimension to fetch the unit for.
|
|
588
|
+
# @return [javax.measure.Unit] The current unit for the thread of the specified dimensions
|
|
589
|
+
#
|
|
590
|
+
# @example
|
|
591
|
+
# unit(SIUnits::METRE.dimension) # => ImperialUnits::FOOT
|
|
592
|
+
#
|
|
593
|
+
def unit(*units)
|
|
594
|
+
if units.length == 1 && units.first.is_a?(javax.measure.Dimension)
|
|
595
|
+
return Thread.current[:openhab_units]&.[](units.first)
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
raise ArgumentError, "You must give a block to set the unit for the duration of" unless block_given?
|
|
599
|
+
|
|
600
|
+
begin
|
|
601
|
+
old_units = unit!(*units)
|
|
602
|
+
yield
|
|
603
|
+
ensure
|
|
604
|
+
Thread.current[:openhab_units] = old_units
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
#
|
|
609
|
+
# Permanently sets the implicit unit(s) for this thread
|
|
610
|
+
#
|
|
611
|
+
# @note This method is only intended for use at the top level of rule
|
|
612
|
+
# scripts. If it's used within library methods, or hap-hazardly within
|
|
613
|
+
# rules, things can get very confusing because the prior state won't be
|
|
614
|
+
# properly restored.
|
|
615
|
+
#
|
|
616
|
+
# {unit!} calls are cumulative - additional calls will not erase the effects
|
|
617
|
+
# previous calls unless they are for the same dimension.
|
|
618
|
+
#
|
|
619
|
+
# @return [Hash<javax.measure.Dimension=>javax.measure.Unit>]
|
|
620
|
+
# the prior unit configuration
|
|
621
|
+
#
|
|
622
|
+
# @overload unit!(*units)
|
|
623
|
+
# @param [String, javax.measure.Unit] units
|
|
624
|
+
# Unit or String representing unit.
|
|
625
|
+
#
|
|
626
|
+
# @example Set several defaults at once
|
|
627
|
+
# unit!("°F", "ft", "lbs")
|
|
628
|
+
# (50 | "°F") == 50 # => true
|
|
629
|
+
#
|
|
630
|
+
# @example Calls are cumulative
|
|
631
|
+
# unit!("°F")
|
|
632
|
+
# unit!("ft")
|
|
633
|
+
# (50 | "°F") == 50 # => true
|
|
634
|
+
# (2 | "yd") == 6 # => true
|
|
635
|
+
#
|
|
636
|
+
# @example Subsequent calls override the same dimension from previous calls
|
|
637
|
+
# unit!("yd")
|
|
638
|
+
# unit!("ft")
|
|
639
|
+
# (2 | "yd") == 6 # => true
|
|
640
|
+
#
|
|
641
|
+
# @overload unit!
|
|
642
|
+
# Clear all unit settings
|
|
643
|
+
#
|
|
644
|
+
# @example Clear all unit settings
|
|
645
|
+
# unit!("ft")
|
|
646
|
+
# unit!
|
|
647
|
+
# (2 | "yd") == 6 # => false
|
|
648
|
+
#
|
|
649
|
+
def unit!(*units)
|
|
650
|
+
units = units.each_with_object({}) do |unit, r|
|
|
651
|
+
unit = org.openhab.core.types.util.UnitUtils.parse_unit(unit) if unit.is_a?(String)
|
|
652
|
+
r[unit.dimension] = unit
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
old_units = Thread.current[:openhab_units] || {}
|
|
656
|
+
Thread.current[:openhab_units] = units.empty? ? {} : old_units.merge(units)
|
|
657
|
+
old_units
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# @!visibility private
|
|
661
|
+
def try_parse_time_like(string)
|
|
662
|
+
return string unless string.is_a?(String)
|
|
663
|
+
|
|
664
|
+
exception = nil
|
|
665
|
+
[java.time.LocalTime, java.time.LocalDate, java.time.MonthDay, java.time.ZonedDateTime].each do |klass|
|
|
666
|
+
return klass.parse(string)
|
|
667
|
+
rescue ArgumentError => e
|
|
668
|
+
exception ||= e
|
|
669
|
+
next
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
raise exception
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
OpenHAB::Core.wait_till_openhab_ready
|
|
678
|
+
OpenHAB::Core.add_rubylib_to_load_path
|
|
679
|
+
|
|
680
|
+
# import Items classes into global namespace
|
|
681
|
+
OpenHAB::Core::Items.import_into_global_namespace
|
|
682
|
+
|
|
683
|
+
# Extend `main` with DSL methods
|
|
684
|
+
singleton_class.include(OpenHAB::DSL)
|
|
685
|
+
|
|
686
|
+
logger.debug "OpenHAB JRuby Scripting Library Version #{OpenHAB::DSL::VERSION} Loaded"
|