openhab-scripting 4.46.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/openhab/core/actions/audio.rb +47 -0
- data/lib/openhab/core/actions/ephemeris.rb +39 -0
- data/lib/openhab/core/actions/exec.rb +51 -0
- data/lib/openhab/core/actions/http.rb +80 -0
- data/lib/openhab/core/actions/ping.rb +30 -0
- data/lib/openhab/core/actions/transformation.rb +32 -0
- data/lib/openhab/core/actions/voice.rb +36 -0
- data/lib/openhab/core/actions.rb +82 -0
- data/lib/openhab/core/dependency_tracking.rb +34 -0
- data/lib/openhab/core/dto/item_channel_link.rb +33 -0
- data/lib/openhab/core/dto/thing.rb +27 -0
- data/lib/openhab/core/dto.rb +11 -0
- data/lib/openhab/core/entity_lookup.rb +152 -70
- data/lib/openhab/core/events/abstract_event.rb +18 -0
- data/lib/openhab/core/events/abstract_item_registry_event.rb +36 -0
- data/lib/openhab/core/events/abstract_thing_registry_event.rb +40 -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 +75 -0
- data/lib/openhab/core/events/item_state_event.rb +79 -0
- data/lib/openhab/core/events/thing_status_info_event.rb +55 -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 +59 -0
- data/lib/openhab/core/items/dimmer_item.rb +148 -0
- data/lib/openhab/core/items/generic_item.rb +292 -0
- data/lib/openhab/core/items/group_item.rb +176 -0
- data/lib/openhab/{dsl → core}/items/image_item.rb +35 -29
- data/lib/openhab/core/items/item.rb +273 -0
- data/lib/openhab/core/items/location_item.rb +34 -0
- data/lib/openhab/core/items/metadata/hash.rb +433 -0
- data/lib/openhab/core/items/metadata/namespace_hash.rb +475 -0
- data/lib/openhab/core/items/metadata/provider.rb +48 -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 +416 -0
- data/lib/openhab/core/items/player_item.rb +66 -0
- data/lib/openhab/core/items/provider.rb +44 -0
- data/lib/openhab/core/items/proxy.rb +136 -0
- data/lib/openhab/core/items/registry.rb +86 -0
- data/lib/openhab/core/items/rollershutter_item.rb +68 -0
- data/lib/openhab/core/items/semantics/enumerable.rb +177 -0
- data/lib/openhab/core/items/semantics.rb +473 -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 +108 -0
- data/lib/openhab/{dsl → core}/lazy_array.rb +9 -3
- data/lib/openhab/core/profile_factory.rb +132 -0
- data/lib/openhab/core/provider.rb +230 -0
- data/lib/openhab/core/proxy.rb +130 -0
- data/lib/openhab/core/registry.rb +40 -0
- data/lib/openhab/core/rules/module.rb +26 -0
- data/lib/openhab/core/rules/provider.rb +25 -0
- data/lib/openhab/core/rules/registry.rb +76 -0
- data/lib/openhab/core/rules/rule.rb +150 -0
- data/lib/openhab/core/rules.rb +25 -0
- data/lib/openhab/core/script_handling.rb +78 -20
- 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/links/provider.rb +78 -0
- data/lib/openhab/core/things/profile_callback.rb +52 -0
- data/lib/openhab/core/things/provider.rb +29 -0
- data/lib/openhab/core/things/proxy.rb +87 -0
- data/lib/openhab/core/things/registry.rb +73 -0
- data/lib/openhab/core/things/thing.rb +194 -0
- data/lib/openhab/core/things.rb +22 -0
- data/lib/openhab/core/timer.rb +148 -0
- data/lib/openhab/{dsl → core}/types/comparable_type.rb +5 -3
- data/lib/openhab/{dsl → core}/types/date_time_type.rb +55 -127
- data/lib/openhab/{dsl → core}/types/decimal_type.rb +50 -48
- data/lib/openhab/{dsl → core}/types/hsb_type.rb +35 -83
- 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/{dsl → core}/types/numeric_type.rb +20 -7
- 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/{dsl → core}/types/percent_type.rb +19 -20
- 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 +325 -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/{dsl → core}/types/string_type.rb +17 -28
- data/lib/openhab/{dsl → core}/types/type.rb +42 -40
- 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 +82 -0
- data/lib/openhab/{dsl → core}/uid.rb +4 -23
- data/lib/openhab/core/value_cache.rb +188 -0
- data/lib/openhab/core.rb +98 -0
- data/lib/openhab/core_ext/between.rb +32 -0
- data/lib/openhab/core_ext/ephemeris.rb +53 -0
- data/lib/openhab/core_ext/java/class.rb +34 -0
- data/lib/openhab/core_ext/java/duration.rb +142 -0
- data/lib/openhab/core_ext/java/list.rb +436 -0
- data/lib/openhab/core_ext/java/local_date.rb +104 -0
- data/lib/openhab/core_ext/java/local_time.rb +118 -0
- data/lib/openhab/core_ext/java/map.rb +66 -0
- data/lib/openhab/core_ext/java/month.rb +71 -0
- data/lib/openhab/core_ext/java/month_day.rb +119 -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 +62 -0
- data/lib/openhab/core_ext/java/unit.rb +15 -0
- data/lib/openhab/core_ext/java/zoned_date_time.rb +213 -0
- data/lib/openhab/core_ext/ruby/array.rb +21 -0
- data/lib/openhab/core_ext/ruby/date.rb +96 -0
- data/lib/openhab/core_ext/ruby/date_time.rb +55 -0
- data/lib/openhab/core_ext/ruby/module.rb +15 -0
- data/lib/openhab/core_ext/ruby/numeric.rb +195 -0
- data/lib/openhab/core_ext/ruby/range.rb +70 -0
- data/lib/openhab/core_ext/ruby/symbol.rb +7 -0
- data/lib/openhab/core_ext/ruby/time.rb +108 -0
- data/lib/openhab/core_ext.rb +18 -0
- data/lib/openhab/dsl/debouncer.rb +259 -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 +1 -1
- data/lib/openhab/dsl/items/builder.rb +578 -0
- data/lib/openhab/dsl/items/ensure.rb +73 -82
- data/lib/openhab/dsl/items/timed_command.rb +214 -159
- data/lib/openhab/dsl/rules/automation_rule.rb +126 -115
- data/lib/openhab/dsl/rules/builder.rb +1935 -0
- data/lib/openhab/dsl/rules/guard.rb +51 -114
- data/lib/openhab/dsl/rules/name_inference.rb +66 -25
- data/lib/openhab/dsl/rules/property.rb +48 -75
- data/lib/openhab/dsl/rules/rule_triggers.rb +22 -27
- data/lib/openhab/dsl/rules/terse.rb +58 -14
- data/lib/openhab/dsl/rules/triggers/changed.rb +48 -94
- data/lib/openhab/dsl/rules/triggers/channel.rb +9 -40
- data/lib/openhab/dsl/rules/triggers/command.rb +14 -63
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +34 -69
- data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +6 -14
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +48 -82
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +30 -47
- data/lib/openhab/dsl/rules/triggers/trigger.rb +7 -28
- data/lib/openhab/dsl/rules/triggers/updated.rb +21 -45
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +257 -102
- data/lib/openhab/dsl/rules/triggers.rb +12 -0
- data/lib/openhab/dsl/rules.rb +8 -0
- data/lib/openhab/dsl/things/builder.rb +299 -0
- data/lib/openhab/{core → dsl}/thread_local.rb +27 -17
- data/lib/openhab/dsl/timer_manager.rb +204 -0
- data/lib/openhab/dsl/version.rb +9 -0
- data/lib/openhab/dsl.rb +979 -0
- data/lib/openhab/log.rb +355 -0
- data/lib/openhab/osgi.rb +68 -0
- data/lib/openhab/rspec/configuration.rb +56 -0
- data/lib/openhab/rspec/example_group.rb +132 -0
- data/lib/openhab/rspec/helpers.rb +458 -0
- data/lib/openhab/rspec/hooks.rb +113 -0
- data/lib/openhab/rspec/jruby.rb +46 -0
- data/lib/openhab/rspec/karaf.rb +851 -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/instance_method_stasher.rb +22 -0
- data/lib/openhab/rspec/mocks/persistence_service.rb +155 -0
- data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
- data/lib/openhab/rspec/mocks/space.rb +23 -0
- data/lib/openhab/rspec/mocks/synchronous_executor.rb +63 -0
- data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
- data/lib/openhab/rspec/mocks/timer.rb +134 -0
- data/lib/openhab/rspec/openhab/core/actions.rb +38 -0
- data/lib/openhab/rspec/openhab/core/items/proxy.rb +15 -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 +50 -0
- data/lib/openhab/rspec.rb +26 -0
- data/lib/openhab/yard/base_helper.rb +19 -0
- data/lib/openhab/yard/cli/stats.rb +23 -0
- data/lib/openhab/yard/code_objects/group_object.rb +23 -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/coderay.rb +17 -0
- data/lib/openhab/yard/handlers/jruby/base.rb +58 -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 +30 -0
- data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
- data/lib/openhab/yard/html_helper.rb +78 -0
- data/lib/openhab/yard/markdown_helper.rb +148 -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 +38 -0
- metadata +475 -106
- data/lib/openhab/core/item_proxy.rb +0 -29
- data/lib/openhab/core/load_path.rb +0 -19
- data/lib/openhab/core/openhab_setup.rb +0 -29
- data/lib/openhab/core/osgi.rb +0 -58
- data/lib/openhab/core/services.rb +0 -24
- data/lib/openhab/dsl/actions.rb +0 -114
- data/lib/openhab/dsl/between.rb +0 -25
- data/lib/openhab/dsl/channel.rb +0 -43
- data/lib/openhab/dsl/dsl.rb +0 -59
- data/lib/openhab/dsl/group.rb +0 -54
- data/lib/openhab/dsl/imports.rb +0 -21
- data/lib/openhab/dsl/items/color_item.rb +0 -76
- data/lib/openhab/dsl/items/comparable_item.rb +0 -62
- data/lib/openhab/dsl/items/contact_item.rb +0 -41
- data/lib/openhab/dsl/items/date_time_item.rb +0 -65
- data/lib/openhab/dsl/items/dimmer_item.rb +0 -65
- data/lib/openhab/dsl/items/generic_item.rb +0 -229
- data/lib/openhab/dsl/items/group_item.rb +0 -127
- data/lib/openhab/dsl/items/item_equality.rb +0 -59
- data/lib/openhab/dsl/items/item_registry.rb +0 -54
- data/lib/openhab/dsl/items/items.rb +0 -109
- data/lib/openhab/dsl/items/location_item.rb +0 -59
- data/lib/openhab/dsl/items/metadata.rb +0 -326
- data/lib/openhab/dsl/items/number_item.rb +0 -17
- data/lib/openhab/dsl/items/numeric_item.rb +0 -87
- data/lib/openhab/dsl/items/persistence.rb +0 -307
- data/lib/openhab/dsl/items/player_item.rb +0 -58
- data/lib/openhab/dsl/items/rollershutter_item.rb +0 -51
- data/lib/openhab/dsl/items/semantics/enumerable.rb +0 -91
- data/lib/openhab/dsl/items/semantics.rb +0 -227
- data/lib/openhab/dsl/items/string_item.rb +0 -51
- data/lib/openhab/dsl/items/switch_item.rb +0 -70
- data/lib/openhab/dsl/monkey_patch/actions/actions.rb +0 -4
- data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +0 -39
- data/lib/openhab/dsl/monkey_patch/events/events.rb +0 -7
- data/lib/openhab/dsl/monkey_patch/events/item_command.rb +0 -85
- data/lib/openhab/dsl/monkey_patch/events/item_event.rb +0 -28
- data/lib/openhab/dsl/monkey_patch/events/item_state.rb +0 -61
- data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +0 -60
- data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +0 -33
- data/lib/openhab/dsl/monkey_patch/java/java.rb +0 -4
- data/lib/openhab/dsl/monkey_patch/java/local_time.rb +0 -44
- data/lib/openhab/dsl/monkey_patch/java/time_extensions.rb +0 -50
- data/lib/openhab/dsl/monkey_patch/java/zoned_date_time.rb +0 -45
- data/lib/openhab/dsl/monkey_patch/ruby/number.rb +0 -104
- data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +0 -6
- data/lib/openhab/dsl/monkey_patch/ruby/string.rb +0 -47
- data/lib/openhab/dsl/monkey_patch/ruby/time.rb +0 -61
- data/lib/openhab/dsl/openhab.rb +0 -30
- data/lib/openhab/dsl/persistence.rb +0 -27
- data/lib/openhab/dsl/rules/item_event.rb +0 -19
- data/lib/openhab/dsl/rules/rule.rb +0 -160
- data/lib/openhab/dsl/rules/rule_config.rb +0 -147
- data/lib/openhab/dsl/rules/triggers/generic.rb +0 -31
- data/lib/openhab/dsl/rules/triggers/triggers.rb +0 -11
- data/lib/openhab/dsl/rules/triggers/watch/watch.rb +0 -81
- data/lib/openhab/dsl/states.rb +0 -89
- data/lib/openhab/dsl/things.rb +0 -147
- data/lib/openhab/dsl/time/month_day.rb +0 -180
- data/lib/openhab/dsl/time/time_of_day.rb +0 -235
- data/lib/openhab/dsl/timers/manager.rb +0 -119
- data/lib/openhab/dsl/timers/reentrant_timer.rb +0 -38
- data/lib/openhab/dsl/timers/timer.rb +0 -132
- data/lib/openhab/dsl/timers.rb +0 -77
- data/lib/openhab/dsl/types/increase_decrease_type.rb +0 -23
- data/lib/openhab/dsl/types/next_previous_type.rb +0 -23
- data/lib/openhab/dsl/types/on_off_type.rb +0 -28
- data/lib/openhab/dsl/types/open_closed_type.rb +0 -29
- data/lib/openhab/dsl/types/play_pause_type.rb +0 -27
- data/lib/openhab/dsl/types/point_type.rb +0 -180
- data/lib/openhab/dsl/types/quantity_type.rb +0 -265
- data/lib/openhab/dsl/types/refresh_type.rb +0 -18
- data/lib/openhab/dsl/types/rewind_fastforward_type.rb +0 -33
- data/lib/openhab/dsl/types/stop_move_type.rb +0 -23
- data/lib/openhab/dsl/types/types.rb +0 -83
- data/lib/openhab/dsl/types/un_def_type.rb +0 -22
- data/lib/openhab/dsl/types/up_down_type.rb +0 -32
- data/lib/openhab/dsl/units.rb +0 -45
- data/lib/openhab/log/configuration.rb +0 -21
- data/lib/openhab/log/logger.rb +0 -282
- data/lib/openhab/version.rb +0 -9
- data/lib/openhab.rb +0 -36
data/lib/openhab/dsl.rb
ADDED
|
@@ -0,0 +1,979 @@
|
|
|
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
|
+
#
|
|
34
|
+
# @!parse
|
|
35
|
+
# include Core::Actions
|
|
36
|
+
# include Core::ScriptHandling
|
|
37
|
+
# include Rules::Terse
|
|
38
|
+
#
|
|
39
|
+
[Core::Actions, Core::ScriptHandling, Rules::Terse].each do |mod|
|
|
40
|
+
# make these available both as regular and class methods
|
|
41
|
+
include mod
|
|
42
|
+
singleton_class.include mod
|
|
43
|
+
public_class_method(*mod.private_instance_methods)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
# @!visibility private
|
|
48
|
+
attr_reader :debouncers
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@debouncers = java.util.concurrent.ConcurrentHashMap.new
|
|
52
|
+
|
|
53
|
+
module_function
|
|
54
|
+
|
|
55
|
+
# @!group Rule Creation
|
|
56
|
+
|
|
57
|
+
# (see Rules::Builder#rule)
|
|
58
|
+
def rule(name = nil, **kwargs, &block)
|
|
59
|
+
rules.build { rule(name, **kwargs, &block) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# (see Rules::Builder#script)
|
|
63
|
+
def script(name = nil, id: nil, **kwargs, &block)
|
|
64
|
+
rules.build { script(name, id: id, **kwargs, &block) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @!group Rule Support
|
|
68
|
+
|
|
69
|
+
# rubocop:disable Layout/LineLength
|
|
70
|
+
|
|
71
|
+
#
|
|
72
|
+
# Defines a new profile that can be applied to item channel links.
|
|
73
|
+
#
|
|
74
|
+
# @param [String, Symbol] id The id for the profile.
|
|
75
|
+
# @yield [event, command: nil, state: nil, link:, item:, channel_uid:, configuration:, context:]
|
|
76
|
+
# All keyword params are optional. Any that aren't defined won't be passed.
|
|
77
|
+
# @yieldparam [Core::Things::ProfileCallback] callback
|
|
78
|
+
# The callback to be used to customize the action taken.
|
|
79
|
+
# @yieldparam [:command_from_item, :state_from_item, :command_from_handler, :state_from_handler] event
|
|
80
|
+
# The event that needs to be processed.
|
|
81
|
+
# @yieldparam [Command, nil] command
|
|
82
|
+
# The command being sent for `:command_from_item` and `:command_from_handler` events.
|
|
83
|
+
# @yieldparam [State, nil] state
|
|
84
|
+
# The state being sent for `:state_from_item` and `:state_from_handler` events.
|
|
85
|
+
# @yieldparam [Core::Things::ItemChannelLink] link
|
|
86
|
+
# The link between the item and the channel, including its configuration.
|
|
87
|
+
# @yieldparam [Item] item The linked item.
|
|
88
|
+
# @yieldparam [Core::Things::ChannelUID] channel_uid The linked channel.
|
|
89
|
+
# @yieldparam [Hash] configuration The profile configuration.
|
|
90
|
+
# @yieldparam [org.openhab.core.thing.profiles.ProfileContext] context The profile context.
|
|
91
|
+
# @yieldreturn [Boolean] Return true from the block in order to have default processing.
|
|
92
|
+
# @return [void]
|
|
93
|
+
#
|
|
94
|
+
# @see org.openhab.thing.Profile
|
|
95
|
+
# @see org.openhab.thing.StateProfile
|
|
96
|
+
#
|
|
97
|
+
# @example Vetoing a command
|
|
98
|
+
# profile(:veto_closing_shades) do |event, item:, command:|
|
|
99
|
+
# next false if command&.down?
|
|
100
|
+
#
|
|
101
|
+
# true
|
|
102
|
+
# end
|
|
103
|
+
#
|
|
104
|
+
# items.build do
|
|
105
|
+
# rollershutter_item "MyShade" do
|
|
106
|
+
# channel "thing:rollershutter", profile: "ruby:veto_closing_shades"
|
|
107
|
+
# end
|
|
108
|
+
# end
|
|
109
|
+
# # can also be referenced from an `.items` file:
|
|
110
|
+
# # Rollershutter MyShade { channel="thing:rollershutter"[profile="ruby:veto_closing_shades"] }
|
|
111
|
+
#
|
|
112
|
+
# @example Overriding units from a binding
|
|
113
|
+
# profile(:set_uom) do |event, configuration:, state:, command:|
|
|
114
|
+
# unless configuration["unit"]
|
|
115
|
+
# logger.warn("Unit configuration not provided for set_uom profile")
|
|
116
|
+
# next true
|
|
117
|
+
# end
|
|
118
|
+
#
|
|
119
|
+
# case event
|
|
120
|
+
# when :state_from_handler
|
|
121
|
+
# next true unless state.is_a?(DecimalType) || state.is_a?(QuantityType) # what is it then?!
|
|
122
|
+
#
|
|
123
|
+
# state = state.to_d if state.is_a?(QuantityType) # ignore the units if QuantityType was given
|
|
124
|
+
# callback.send_update(state | configuration["unit"])
|
|
125
|
+
# false
|
|
126
|
+
# when :command_from_item
|
|
127
|
+
# # strip the unit from the command, as the binding likely can't handle it
|
|
128
|
+
# next true unless command.is_a?(QuantityType)
|
|
129
|
+
#
|
|
130
|
+
# callback.send_command(DecimalType.new(command.to_d))
|
|
131
|
+
# false
|
|
132
|
+
# else
|
|
133
|
+
# true # pass other events through as normal
|
|
134
|
+
# end
|
|
135
|
+
# end
|
|
136
|
+
# # can also be referenced from an `.items` file:
|
|
137
|
+
# # Number:Temperature MyTempWithNonUnitValueFromBinding "I prefer Celsius [%d °C]" { channel="something_that_returns_F"[profile="ruby:set_uom", unit="°F"] }
|
|
138
|
+
#
|
|
139
|
+
def profile(id, &block)
|
|
140
|
+
raise ArgumentError, "Block is required" unless block
|
|
141
|
+
|
|
142
|
+
id = id.to_s
|
|
143
|
+
uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
|
|
144
|
+
|
|
145
|
+
ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
|
|
146
|
+
Core::ProfileFactory.instance.register(uid, block)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# rubocop:enable Layout/LineLength
|
|
151
|
+
|
|
152
|
+
# @!group Object Access
|
|
153
|
+
|
|
154
|
+
#
|
|
155
|
+
# (see Core::ValueCache)
|
|
156
|
+
#
|
|
157
|
+
# @return [Core::ValueCache] the cache shared among all scripts and UI rules in all languages.
|
|
158
|
+
#
|
|
159
|
+
# @see Core::ValueCache ValueCache
|
|
160
|
+
#
|
|
161
|
+
def shared_cache
|
|
162
|
+
$sharedCache
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
#
|
|
166
|
+
# Fetches all rules from the rule registry.
|
|
167
|
+
#
|
|
168
|
+
# @return [Core::Rules::Registry]
|
|
169
|
+
#
|
|
170
|
+
def rules
|
|
171
|
+
Core::Rules::Registry.instance
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
#
|
|
175
|
+
# Fetches all items from the item registry
|
|
176
|
+
#
|
|
177
|
+
# @return [Core::Items::Registry]
|
|
178
|
+
#
|
|
179
|
+
# The examples all assume the following items exist.
|
|
180
|
+
#
|
|
181
|
+
# ```xtend
|
|
182
|
+
# Dimmer DimmerTest "Test Dimmer"
|
|
183
|
+
# Switch SwitchTest "Test Switch"
|
|
184
|
+
# ```
|
|
185
|
+
#
|
|
186
|
+
# @example
|
|
187
|
+
# logger.info("Item Count: #{items.count}") # Item Count: 2
|
|
188
|
+
# logger.info("Items: #{items.map(&:label).sort.join(', ')}") # Items: Test Dimmer, Test Switch'
|
|
189
|
+
# logger.info("DimmerTest exists? #{items.key?('DimmerTest')}") # DimmerTest exists? true
|
|
190
|
+
# logger.info("StringTest exists? #{items.key?('StringTest')}") # StringTest exists? false
|
|
191
|
+
#
|
|
192
|
+
# @example
|
|
193
|
+
# rule 'Use dynamic item lookup to increase related dimmer brightness when switch is turned on' do
|
|
194
|
+
# changed SwitchTest, to: ON
|
|
195
|
+
# triggered { |item| items[item.name.gsub('Switch','Dimmer')].brighten(10) }
|
|
196
|
+
# end
|
|
197
|
+
#
|
|
198
|
+
# @example
|
|
199
|
+
# rule 'search for a suitable item' do
|
|
200
|
+
# on_load
|
|
201
|
+
# triggered do
|
|
202
|
+
# # Send ON to DimmerTest if it exists, otherwise send it to SwitchTest
|
|
203
|
+
# (items['DimmerTest'] || items['SwitchTest'])&.on
|
|
204
|
+
# end
|
|
205
|
+
# end
|
|
206
|
+
#
|
|
207
|
+
def items
|
|
208
|
+
Core::Items::Registry.instance
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
#
|
|
212
|
+
# Get all things known to openHAB
|
|
213
|
+
#
|
|
214
|
+
# @return [Core::Things::Registry] all Thing objects known to openHAB
|
|
215
|
+
#
|
|
216
|
+
# @example
|
|
217
|
+
# things.each { |thing| logger.info("Thing: #{thing.uid}")}
|
|
218
|
+
# logger.info("Thing: #{things['astro:sun:home'].uid}")
|
|
219
|
+
# homie_things = things.select { |t| t.thing_type_uid == "mqtt:homie300" }
|
|
220
|
+
# zwave_things = things.select { |t| t.binding_id == "zwave" }
|
|
221
|
+
# homeseer_dimmers = zwave_things.select { |t| t.thing_type_uid.id == "homeseer_hswd200_00_000" }
|
|
222
|
+
# things['zwave:device:512:node90'].uid.bridge_ids # => ["512"]
|
|
223
|
+
# things['mqtt:topic:4'].uid.bridge_ids # => []
|
|
224
|
+
#
|
|
225
|
+
def things
|
|
226
|
+
Core::Things::Registry.instance
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
#
|
|
230
|
+
# Provides access to timers created by {after after}
|
|
231
|
+
#
|
|
232
|
+
# @return [TimerManager]
|
|
233
|
+
def timers
|
|
234
|
+
TimerManager.instance
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# @!group Utilities
|
|
238
|
+
|
|
239
|
+
#
|
|
240
|
+
# Create a timer and execute the supplied block after the specified duration
|
|
241
|
+
#
|
|
242
|
+
# ### Reentrant Timers
|
|
243
|
+
#
|
|
244
|
+
# Timers with an id are reentrant by id. Reentrant means that when the same id is encountered,
|
|
245
|
+
# the timer is rescheduled rather than creating a second new timer. Note that the timer will
|
|
246
|
+
# execute the block provided in the latest call.
|
|
247
|
+
#
|
|
248
|
+
# This removes the need for the usual boilerplate code to manually keep track of timer objects.
|
|
249
|
+
#
|
|
250
|
+
# Timers with `id` can be managed with the built-in {timers} object.
|
|
251
|
+
#
|
|
252
|
+
# When a timer is cancelled, it will be removed from the object.
|
|
253
|
+
#
|
|
254
|
+
# Be sure that your ids are unique. For example, if you're using {Item items} as your
|
|
255
|
+
# ids, you either need to be sure you don't use the same item for multiple logical contexts,
|
|
256
|
+
# or you need to make your id more specific, by doing something like embedding the item in
|
|
257
|
+
# array with a symbol of the timer's purpose, like `[:vacancy, item]`. But also note that
|
|
258
|
+
# assuming default settings, every Ruby file (for file-based rules) or UI rule gets its
|
|
259
|
+
# own instance of the timers object, so you don't need to worry about collisions among
|
|
260
|
+
# different files.
|
|
261
|
+
#
|
|
262
|
+
# @see timers
|
|
263
|
+
# @see Rules::BuilderDSL#changed
|
|
264
|
+
# @see Items::TimedCommand
|
|
265
|
+
#
|
|
266
|
+
# @param [java.time.temporal.TemporalAmount, #to_zoned_date_time, Proc] duration
|
|
267
|
+
# Duration after which to execute the block
|
|
268
|
+
# @param [Object] id ID to associate with timer. The timer can be managed via {timers}.
|
|
269
|
+
# @param [true,false] reschedule Reschedule the timer if it already exists.
|
|
270
|
+
# @yield Block to execute when the timer is elapsed.
|
|
271
|
+
# @yieldparam [Core::Timer] timer
|
|
272
|
+
#
|
|
273
|
+
# @return [Core::Timer] if `reschedule` is false, the existing timer.
|
|
274
|
+
# Otherwise the new timer.
|
|
275
|
+
#
|
|
276
|
+
# @example Create a simple timer
|
|
277
|
+
# after 5.seconds do
|
|
278
|
+
# logger.info("Timer Fired")
|
|
279
|
+
# end
|
|
280
|
+
#
|
|
281
|
+
# @example Timers delegate methods to openHAB timer objects
|
|
282
|
+
# after 1.second do |timer|
|
|
283
|
+
# logger.info("Timer is active? #{timer.active?}")
|
|
284
|
+
# end
|
|
285
|
+
#
|
|
286
|
+
# @example Timers can be rescheduled to run again, waiting the original duration
|
|
287
|
+
# after 3.seconds do |timer|
|
|
288
|
+
# logger.info("Timer Fired")
|
|
289
|
+
# timer.reschedule
|
|
290
|
+
# end
|
|
291
|
+
#
|
|
292
|
+
# @example Timers can be rescheduled for different durations
|
|
293
|
+
# after 3.seconds do |timer|
|
|
294
|
+
# logger.info("Timer Fired")
|
|
295
|
+
# timer.reschedule 5.seconds
|
|
296
|
+
# end
|
|
297
|
+
#
|
|
298
|
+
# @example Timers can be manipulated through the returned object
|
|
299
|
+
# mytimer = after 1.minute do
|
|
300
|
+
# logger.info("It has been 1 minute")
|
|
301
|
+
# end
|
|
302
|
+
#
|
|
303
|
+
# mytimer.cancel
|
|
304
|
+
#
|
|
305
|
+
# @example Reentrant timers will automatically reschedule if the same id is encountered again
|
|
306
|
+
# rule "Turn off closet light after 10 minutes" do
|
|
307
|
+
# changed ClosetLights.members, to: ON
|
|
308
|
+
# triggered do |item|
|
|
309
|
+
# after 10.minutes, id: item do
|
|
310
|
+
# item.ensure.off
|
|
311
|
+
# end
|
|
312
|
+
# end
|
|
313
|
+
# end
|
|
314
|
+
#
|
|
315
|
+
# @example Timers with id can be managed through the built-in `timers` object
|
|
316
|
+
# after 1.minute, id: :foo do
|
|
317
|
+
# logger.info("managed timer has fired")
|
|
318
|
+
# end
|
|
319
|
+
#
|
|
320
|
+
# timers.cancel(:foo)
|
|
321
|
+
#
|
|
322
|
+
# if timers.include?(:foo)
|
|
323
|
+
# logger.info("The timer :foo is not active")
|
|
324
|
+
# end
|
|
325
|
+
#
|
|
326
|
+
# @example Only create a new timer if it isn't already scheduled
|
|
327
|
+
# after(1.minute, id: :foo, reschedule: false) do
|
|
328
|
+
# logger.info("Timer fired")
|
|
329
|
+
# end
|
|
330
|
+
#
|
|
331
|
+
# @example Reentrant timers will execute the block from the most recent call
|
|
332
|
+
# # In the following example, if Item1 received a command, followed by Item2,
|
|
333
|
+
# # the timer will execute the block referring to Item2.
|
|
334
|
+
# rule "Execute The Most Recent Block" do
|
|
335
|
+
# received_command Item1, Item2
|
|
336
|
+
# run do |event|
|
|
337
|
+
# after(10.minutes, id: :common_timer) do
|
|
338
|
+
# logger.info "The latest command was received from #{event.item}"
|
|
339
|
+
# end
|
|
340
|
+
# end
|
|
341
|
+
# end
|
|
342
|
+
#
|
|
343
|
+
def after(duration, id: nil, reschedule: true, &block)
|
|
344
|
+
raise ArgumentError, "Block is required" unless block
|
|
345
|
+
|
|
346
|
+
# Carry rule name to timer
|
|
347
|
+
thread_locals = ThreadLocal.persist
|
|
348
|
+
timers.create(duration, id: id, reschedule: reschedule, thread_locals: thread_locals, block: block)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
#
|
|
352
|
+
# Convert a string based range into a range of LocalTime, LocalDate, MonthDay, or ZonedDateTime
|
|
353
|
+
# depending on the format of the string.
|
|
354
|
+
#
|
|
355
|
+
# @return [Range] converted range object
|
|
356
|
+
#
|
|
357
|
+
# @example Range#cover?
|
|
358
|
+
# logger.info("Within month-day range") if between('02-20'..'06-01').cover?(MonthDay.now)
|
|
359
|
+
#
|
|
360
|
+
# @example Use in a Case
|
|
361
|
+
# case MonthDay.now
|
|
362
|
+
# when between('01-01'..'03-31')
|
|
363
|
+
# logger.info("First quarter")
|
|
364
|
+
# when between('04-01'..'06-30')
|
|
365
|
+
# logger.info("Second quarter")
|
|
366
|
+
# end
|
|
367
|
+
#
|
|
368
|
+
# @example Create a time range
|
|
369
|
+
# between('7am'..'12pm').cover?(LocalTime.now)
|
|
370
|
+
#
|
|
371
|
+
# @see CoreExt::Between#between? #between?
|
|
372
|
+
#
|
|
373
|
+
def between(range)
|
|
374
|
+
raise ArgumentError, "Supplied object must be a range" unless range.is_a?(Range)
|
|
375
|
+
|
|
376
|
+
start = try_parse_time_like(range.begin)
|
|
377
|
+
finish = try_parse_time_like(range.end)
|
|
378
|
+
Range.new(start, finish, range.exclude_end?)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
#
|
|
382
|
+
# Limits the frequency of calls to the given block within a given amount of time.
|
|
383
|
+
#
|
|
384
|
+
# It can be useful to throttle certain actions even when a rule is triggered too often.
|
|
385
|
+
# This can be used to debounce triggers in a UI rule.
|
|
386
|
+
#
|
|
387
|
+
# @param (see Debouncer#initialize)
|
|
388
|
+
# @param [Object] id ID to associate with this debouncer.
|
|
389
|
+
# @param [Block] block The block to be debounced.
|
|
390
|
+
#
|
|
391
|
+
# @return [void]
|
|
392
|
+
#
|
|
393
|
+
# @example Run at most once per second even when being called more frequently
|
|
394
|
+
# (1..100).each do
|
|
395
|
+
# debounce for: 1.second do
|
|
396
|
+
# logger.info "This will not be logged more frequently than every 1 second"
|
|
397
|
+
# end
|
|
398
|
+
# sleep 0.1
|
|
399
|
+
# end
|
|
400
|
+
#
|
|
401
|
+
# @see Debouncer Debouncer class
|
|
402
|
+
# @see Rules::BuilderDSL#debounce debounce rule guard
|
|
403
|
+
#
|
|
404
|
+
# @!visibility private
|
|
405
|
+
def debounce(for:, leading: false, idle_time: nil, id: nil, &block)
|
|
406
|
+
interval = binding.local_variable_get(:for)
|
|
407
|
+
id ||= block.source_location
|
|
408
|
+
DSL.debouncers.compute(id) do |_key, debouncer|
|
|
409
|
+
debouncer ||= Debouncer.new(for: interval, leading: leading, idle_time: idle_time)
|
|
410
|
+
debouncer.call(&block)
|
|
411
|
+
debouncer
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
#
|
|
416
|
+
# Waits until calls to this method have stopped firing for a period of time
|
|
417
|
+
# before executing the block.
|
|
418
|
+
#
|
|
419
|
+
# This method acts as a guard for the given block to ensure that it doesn't get executed
|
|
420
|
+
# too frequently. The debounce_for method can be called as frequently as possible.
|
|
421
|
+
# The given block, however, will only be executed once the `debounce_time` has passed
|
|
422
|
+
# since the last call to debounce_for.
|
|
423
|
+
#
|
|
424
|
+
# This method can be used from within a UI rule as well as from a file-based rule.
|
|
425
|
+
#
|
|
426
|
+
# @param (see Rules::BuilderDSL#debounce_for)
|
|
427
|
+
# @param [Object] id ID to associate with this call.
|
|
428
|
+
# @param [Block] block The block to be debounced.
|
|
429
|
+
#
|
|
430
|
+
# @return [void]
|
|
431
|
+
#
|
|
432
|
+
# @example Run a block of code only after an item has stopped changing
|
|
433
|
+
# # This can be placed inside a UI rule with an Item Change trigger for
|
|
434
|
+
# # a Door contact sensor.
|
|
435
|
+
# # If the door state has stopped changing state for 10 minutes,
|
|
436
|
+
# # execute the block.
|
|
437
|
+
# debounce_for(10.minutes) do
|
|
438
|
+
# if DoorState.open?
|
|
439
|
+
# Voice.say("The door has been left open!")
|
|
440
|
+
# end
|
|
441
|
+
# end
|
|
442
|
+
#
|
|
443
|
+
# @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
|
|
444
|
+
#
|
|
445
|
+
def debounce_for(debounce_time, id: nil, &block)
|
|
446
|
+
idle_time = debounce_time.is_a?(Range) ? debounce_time.begin : debounce_time
|
|
447
|
+
debounce(for: debounce_time, idle_time: idle_time, id: id, &block)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
#
|
|
451
|
+
# Rate-limits block executions by delaying calls and only executing the last
|
|
452
|
+
# call within the given duration.
|
|
453
|
+
#
|
|
454
|
+
# When throttle_for is called, it will hold from executing the block and start
|
|
455
|
+
# a fixed timer for the given duration. Should more calls occur during
|
|
456
|
+
# this time, keep holding and once the wait time is over, execute the block.
|
|
457
|
+
#
|
|
458
|
+
# {throttle_for} will execute the block after it had waited for the given duration,
|
|
459
|
+
# regardless of how frequently `throttle_for` was called.
|
|
460
|
+
# In contrast, {debounce_for} will wait until there is a minimum interval
|
|
461
|
+
# between two triggers.
|
|
462
|
+
#
|
|
463
|
+
# {throttle_for} is ideal in situations where regular status updates need to be made
|
|
464
|
+
# for frequently changing values. It is also useful when a rule responds to triggers
|
|
465
|
+
# from multiple related items that are updated at around the same time. Instead of
|
|
466
|
+
# executing the rule multiple times, {throttle_for} will wait for a pre-set amount
|
|
467
|
+
# of time since the first group of triggers occurred before executing the rule.
|
|
468
|
+
#
|
|
469
|
+
# @param (see Rules::BuilderDSL#throttle_for)
|
|
470
|
+
# @param [Object] id ID to associate with this call.
|
|
471
|
+
# @param [Block] block The block to be throttled.
|
|
472
|
+
#
|
|
473
|
+
# @return [void]
|
|
474
|
+
#
|
|
475
|
+
# @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
|
|
476
|
+
# @see Rules::BuilderDSL#throttle_for
|
|
477
|
+
#
|
|
478
|
+
def throttle_for(duration, id: nil, &block)
|
|
479
|
+
debounce(for: duration, id: id, &block)
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# (see Core::Actions::Transformation.transform)
|
|
483
|
+
def transform(type, function, value)
|
|
484
|
+
Transformation.transform(type, function, value)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
#
|
|
488
|
+
# Limit how often the given block executes to the specified interval.
|
|
489
|
+
#
|
|
490
|
+
# {only_every} will execute the given block but prevents further executions
|
|
491
|
+
# until the given interval has passed. In contrast, {throttle_for} will not
|
|
492
|
+
# execute the block immediately, and will wait until the end of the interval.
|
|
493
|
+
#
|
|
494
|
+
# @param (see Rules::BuilderDSL#only_every)
|
|
495
|
+
# @param [Object] id ID to associate with this call.
|
|
496
|
+
# @param [Block] block The block to be throttled.
|
|
497
|
+
#
|
|
498
|
+
# @return [void]
|
|
499
|
+
#
|
|
500
|
+
# @example Prevent door bell from ringing repeatedly
|
|
501
|
+
# # This can be called from a UI rule.
|
|
502
|
+
# # For file based rule, use the `only_every` rule guard
|
|
503
|
+
# only_every(30.seconds) do { Audio.play_sound("doorbell.mp3") }
|
|
504
|
+
#
|
|
505
|
+
# @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
|
|
506
|
+
# @see Rules::BuilderDSL#only_every
|
|
507
|
+
#
|
|
508
|
+
def only_every(interval, id: nil, &block)
|
|
509
|
+
interval = 1.send(interval) if %i[second minute hour day].include?(interval)
|
|
510
|
+
debounce(for: interval, leading: true, id: id, &block)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
#
|
|
514
|
+
# Store states of supplied items
|
|
515
|
+
#
|
|
516
|
+
# Takes one or more items and returns a map `{Item => State}` with the
|
|
517
|
+
# current state of each item. It is implemented by calling openHAB's
|
|
518
|
+
# [events.storeStates()](https://www.openhab.org/docs/configuration/actions.html#event-bus-actions).
|
|
519
|
+
#
|
|
520
|
+
# @param [Item] items Items to store states of.
|
|
521
|
+
#
|
|
522
|
+
# @return [Core::Items::StateStorage] item states
|
|
523
|
+
#
|
|
524
|
+
# @example
|
|
525
|
+
# states = store_states Item1, Item2
|
|
526
|
+
# ...
|
|
527
|
+
# states.restore
|
|
528
|
+
#
|
|
529
|
+
# @example With a block
|
|
530
|
+
# store_states Item1, Item2 do
|
|
531
|
+
# ...
|
|
532
|
+
# end # the states will be restored here
|
|
533
|
+
#
|
|
534
|
+
def store_states(*items)
|
|
535
|
+
states = Core::Items::StateStorage.from_items(*items)
|
|
536
|
+
if block_given?
|
|
537
|
+
yield
|
|
538
|
+
states.restore
|
|
539
|
+
end
|
|
540
|
+
states
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
#
|
|
544
|
+
# @!group Block Modifiers
|
|
545
|
+
# These methods allow certain operations to be grouped inside the given block
|
|
546
|
+
# to reduce repetitions
|
|
547
|
+
#
|
|
548
|
+
|
|
549
|
+
#
|
|
550
|
+
# Global method that takes a block and for the duration of the block
|
|
551
|
+
# all commands sent will check if the item is in the command's state
|
|
552
|
+
# before sending the command.
|
|
553
|
+
#
|
|
554
|
+
# @yield
|
|
555
|
+
# @return [Object] The result of the block.
|
|
556
|
+
#
|
|
557
|
+
# @example Turn on several switches only if they're not already on
|
|
558
|
+
# ensure_states do
|
|
559
|
+
# Switch1.on
|
|
560
|
+
# Switch2.on
|
|
561
|
+
# end
|
|
562
|
+
#
|
|
563
|
+
# @example
|
|
564
|
+
# # VirtualSwitch is in state `ON`
|
|
565
|
+
# ensure_states do
|
|
566
|
+
# VirtualSwitch << ON # No command will be sent
|
|
567
|
+
# VirtualSwitch.update(ON) # No update will be posted
|
|
568
|
+
# VirtualSwitch << OFF # Off command will be sent
|
|
569
|
+
# VirtualSwitch.update(OFF) # No update will be posted
|
|
570
|
+
# end
|
|
571
|
+
#
|
|
572
|
+
# @example
|
|
573
|
+
# ensure_states do
|
|
574
|
+
# rule 'Items in an execution block will have ensure_states applied to them' do
|
|
575
|
+
# changed VirtualSwitch
|
|
576
|
+
# run do
|
|
577
|
+
# VirtualSwitch.on
|
|
578
|
+
# VirtualSwitch2.on
|
|
579
|
+
# end
|
|
580
|
+
# end
|
|
581
|
+
# end
|
|
582
|
+
#
|
|
583
|
+
# @example
|
|
584
|
+
# rule 'ensure_states must be in an execution block' do
|
|
585
|
+
# changed VirtualSwitch
|
|
586
|
+
# run do
|
|
587
|
+
# ensure_states do
|
|
588
|
+
# VirtualSwitch.on
|
|
589
|
+
# VirtualSwitch2.on
|
|
590
|
+
# end
|
|
591
|
+
# end
|
|
592
|
+
# end
|
|
593
|
+
#
|
|
594
|
+
def ensure_states
|
|
595
|
+
old = Thread.current[:openhab_ensure_states]
|
|
596
|
+
Thread.current[:openhab_ensure_states] = true
|
|
597
|
+
yield
|
|
598
|
+
ensure
|
|
599
|
+
Thread.current[:openhab_ensure_states] = old
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
#
|
|
603
|
+
# Sets a thread local variable to set the default persistence service
|
|
604
|
+
# for method calls inside the block
|
|
605
|
+
#
|
|
606
|
+
# @example
|
|
607
|
+
# persistence(:influxdb) do
|
|
608
|
+
# Item1.persist
|
|
609
|
+
# Item1.changed_since(1.hour)
|
|
610
|
+
# Item1.average_since(12.hours)
|
|
611
|
+
# end
|
|
612
|
+
#
|
|
613
|
+
# @param [Object] service Persistence service either as a String or a Symbol
|
|
614
|
+
# @yield [] Block executed in context of the supplied persistence service
|
|
615
|
+
# @return [Object] The return value from the block.
|
|
616
|
+
#
|
|
617
|
+
# @see persistence!
|
|
618
|
+
# @see OpenHAB::Core::Items::Persistence
|
|
619
|
+
#
|
|
620
|
+
def persistence(service)
|
|
621
|
+
old = persistence!(service)
|
|
622
|
+
yield
|
|
623
|
+
ensure
|
|
624
|
+
persistence!(old)
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
#
|
|
628
|
+
# Permanently sets the default persistence service for the current thread
|
|
629
|
+
#
|
|
630
|
+
# @note This method is only intended for use at the top level of rule
|
|
631
|
+
# scripts. If it's used within library methods, or hap-hazardly within
|
|
632
|
+
# rules, things can get very confusing because the prior state won't be
|
|
633
|
+
# properly restored.
|
|
634
|
+
#
|
|
635
|
+
# @param [Object] service Persistence service either as a String or a Symbol. When nil, use
|
|
636
|
+
# the system's default persistence service.
|
|
637
|
+
# @return [Object,nil] The previous persistence service settings, or nil when using the system's default.
|
|
638
|
+
#
|
|
639
|
+
# @see persistence
|
|
640
|
+
# @see OpenHAB::Core::Items::Persistence
|
|
641
|
+
#
|
|
642
|
+
def persistence!(service = nil)
|
|
643
|
+
old = Thread.current[:openhab_persistence_service]
|
|
644
|
+
Thread.current[:openhab_persistence_service] = service
|
|
645
|
+
old
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
#
|
|
649
|
+
# Sets the implicit unit(s) for operations inside the block.
|
|
650
|
+
#
|
|
651
|
+
# @yield
|
|
652
|
+
#
|
|
653
|
+
# @overload unit(*units)
|
|
654
|
+
# Sets the implicit unit(s) for this thread such that classes
|
|
655
|
+
# operating inside the block can perform automatic conversions to the
|
|
656
|
+
# supplied unit for {QuantityType}.
|
|
657
|
+
#
|
|
658
|
+
# To facilitate conversion of multiple dimensioned and dimensionless
|
|
659
|
+
# numbers the unit block may be used. The unit block attempts to do the
|
|
660
|
+
# _right thing_ based on the mix of dimensioned and dimensionless items
|
|
661
|
+
# within the block. Specifically all dimensionless items are converted to
|
|
662
|
+
# the supplied unit, except when they are used for multiplication or
|
|
663
|
+
# division.
|
|
664
|
+
#
|
|
665
|
+
# @param [String, javax.measure.Unit] units
|
|
666
|
+
# Unit or String representing unit
|
|
667
|
+
# @yield [] The block will be executed in the context of the specified unit(s).
|
|
668
|
+
# @return [Object] the result of the block
|
|
669
|
+
#
|
|
670
|
+
# @example Arithmetic Operations Between QuantityType and Numeric
|
|
671
|
+
# # Number:Temperature NumberC = 23 °C
|
|
672
|
+
# # Number:Temperature NumberF = 70 °F
|
|
673
|
+
# # Number Dimensionless = 2
|
|
674
|
+
# unit('°F') { NumberC.state - NumberF.state < 4 } # => true
|
|
675
|
+
# unit('°F') { NumberC.state - 24 | '°C' < 4 } # => true
|
|
676
|
+
# unit('°F') { (24 | '°C') - NumberC.state < 4 } # => true
|
|
677
|
+
# unit('°C') { NumberF.state - 20 < 2 } # => true
|
|
678
|
+
# unit('°C') { NumberF.state - Dimensionless.state } # => 19.11 °C
|
|
679
|
+
# unit('°C') { NumberF.state - Dimensionless.state < 20 } # => true
|
|
680
|
+
# unit('°C') { Dimensionless.state + NumberC.state == 25 } # => true
|
|
681
|
+
# unit('°C') { 2 + NumberC.state == 25 } # => true
|
|
682
|
+
# unit('°C') { Dimensionless.state * NumberC.state == 46 } # => true
|
|
683
|
+
# unit('°C') { 2 * NumberC.state == 46 } # => true
|
|
684
|
+
# unit('°C') { ( (2 * (NumberF.state + NumberC.state) ) / Dimensionless.state ) < 45 } # => true
|
|
685
|
+
# unit('°C') { [NumberC.state, NumberF.state, Dimensionless.state].min } # => 2
|
|
686
|
+
#
|
|
687
|
+
# @example Commands and Updates inside a unit block
|
|
688
|
+
# unit('°F') { NumberC << 32 }; NumberC.state # => 0 °C
|
|
689
|
+
# # Equivalent to
|
|
690
|
+
# NumberC << "32 °F"
|
|
691
|
+
# # or
|
|
692
|
+
# NumberC << 32 | "°F"
|
|
693
|
+
#
|
|
694
|
+
# @example Specifying Multiple Units
|
|
695
|
+
# unit("°C", "kW") do
|
|
696
|
+
# TemperatureItem.update("50 °F")
|
|
697
|
+
# TemperatureItem.state < 20 # => true. TemperatureItem.state < 20 °C
|
|
698
|
+
# PowerUsage.update("3000 W")
|
|
699
|
+
# PowerUsage.state < 10 # => true. PowerUsage.state < 10 kW
|
|
700
|
+
# end
|
|
701
|
+
#
|
|
702
|
+
# @overload unit(dimension)
|
|
703
|
+
# @param [javax.measure.Dimension] dimension The dimension to fetch the unit for.
|
|
704
|
+
# @return [javax.measure.Unit] The current unit for the thread of the specified dimensions
|
|
705
|
+
#
|
|
706
|
+
# @example
|
|
707
|
+
# unit(SIUnits::METRE.dimension) # => ImperialUnits::FOOT
|
|
708
|
+
#
|
|
709
|
+
def unit(*units)
|
|
710
|
+
if units.length == 1 && units.first.is_a?(javax.measure.Dimension)
|
|
711
|
+
return Thread.current[:openhab_units]&.[](units.first)
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
raise ArgumentError, "You must give a block to set the unit for the duration of" unless block_given?
|
|
715
|
+
|
|
716
|
+
begin
|
|
717
|
+
old_units = unit!(*units)
|
|
718
|
+
yield
|
|
719
|
+
ensure
|
|
720
|
+
Thread.current[:openhab_units] = old_units
|
|
721
|
+
end
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
#
|
|
725
|
+
# Permanently sets the implicit unit(s) for this thread
|
|
726
|
+
#
|
|
727
|
+
# @note This method is only intended for use at the top level of rule
|
|
728
|
+
# scripts. If it's used within library methods, or hap-hazardly within
|
|
729
|
+
# rules, things can get very confusing because the prior state won't be
|
|
730
|
+
# properly restored.
|
|
731
|
+
#
|
|
732
|
+
# {unit!} calls are cumulative - additional calls will not erase the effects
|
|
733
|
+
# of previous calls unless they are for the same dimension.
|
|
734
|
+
#
|
|
735
|
+
# @return [Hash<javax.measure.Dimension=>javax.measure.Unit>]
|
|
736
|
+
# the prior unit configuration
|
|
737
|
+
#
|
|
738
|
+
# @overload unit!(*units)
|
|
739
|
+
# @param [String, javax.measure.Unit] units
|
|
740
|
+
# Unit or String representing unit.
|
|
741
|
+
#
|
|
742
|
+
# @example Set several defaults at once
|
|
743
|
+
# unit!("°F", "ft", "lbs")
|
|
744
|
+
# (50 | "°F") == 50 # => true
|
|
745
|
+
#
|
|
746
|
+
# @example Calls are cumulative
|
|
747
|
+
# unit!("°F")
|
|
748
|
+
# unit!("ft")
|
|
749
|
+
# (50 | "°F") == 50 # => true
|
|
750
|
+
# (2 | "yd") == 6 # => true
|
|
751
|
+
#
|
|
752
|
+
# @example Subsequent calls override the same dimension from previous calls
|
|
753
|
+
# unit!("yd")
|
|
754
|
+
# unit!("ft")
|
|
755
|
+
# (2 | "yd") == 6 # => true
|
|
756
|
+
#
|
|
757
|
+
# @overload unit!
|
|
758
|
+
# Clear all unit settings
|
|
759
|
+
#
|
|
760
|
+
# @example Clear all unit settings
|
|
761
|
+
# unit!("ft")
|
|
762
|
+
# unit!
|
|
763
|
+
# (2 | "yd") == 6 # => false
|
|
764
|
+
#
|
|
765
|
+
def unit!(*units)
|
|
766
|
+
units = units.each_with_object({}) do |unit, r|
|
|
767
|
+
unit = org.openhab.core.types.util.UnitUtils.parse_unit(unit) if unit.is_a?(String)
|
|
768
|
+
r[unit.dimension] = unit
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
old_units = Thread.current[:openhab_units] || {}
|
|
772
|
+
Thread.current[:openhab_units] = units.empty? ? {} : old_units.merge(units)
|
|
773
|
+
old_units
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
#
|
|
777
|
+
# Sets the implicit provider(s) for operations inside the block.
|
|
778
|
+
#
|
|
779
|
+
# @param (see #provider!)
|
|
780
|
+
# @yield [] The block will be executed in the context of the specified unit(s).
|
|
781
|
+
# @return [Object] the result of the block
|
|
782
|
+
#
|
|
783
|
+
# @example
|
|
784
|
+
# provider(metadata: :persistent) do
|
|
785
|
+
# Switch1.metadata[:last_status_from_service] = status
|
|
786
|
+
# end
|
|
787
|
+
#
|
|
788
|
+
# provider!(metadata: { last_status_from_service: :persistent }, Switch2: :persistent)
|
|
789
|
+
# Switch1.metadata[:last_status_from_service] = status # this will persist in JSONDB
|
|
790
|
+
# Switch1.metadata[:homekit] = "Lightbulb" # this will be removed when the script is deleted
|
|
791
|
+
# Switch2.metadata[:homekit] = "Lightbulb" # this will persist in JSONDB
|
|
792
|
+
#
|
|
793
|
+
# @see provider!
|
|
794
|
+
# @see OpenHAB::Core::Provider.current Provider.current for how the current provider is calculated
|
|
795
|
+
#
|
|
796
|
+
def provider(*args, **kwargs)
|
|
797
|
+
raise ArgumentError, "You must give a block to set the provider for the duration of" unless block_given?
|
|
798
|
+
|
|
799
|
+
begin
|
|
800
|
+
old_providers = provider!(*args, **kwargs)
|
|
801
|
+
yield
|
|
802
|
+
ensure
|
|
803
|
+
Thread.current[:openhab_providers] = old_providers
|
|
804
|
+
end
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
#
|
|
808
|
+
# Permanently set the implicit provider(s) for this thread.
|
|
809
|
+
#
|
|
810
|
+
# @note This method is only intended for use at the top level of rule
|
|
811
|
+
# scripts. If it's used within library methods, or hap-hazardly within
|
|
812
|
+
# rules, things can get very confusing because the prior state won't be
|
|
813
|
+
# properly restored.
|
|
814
|
+
#
|
|
815
|
+
# {provider!} calls are cumulative - additional calls will not erase the effects
|
|
816
|
+
# of previous calls unless they are for the same provider type.
|
|
817
|
+
#
|
|
818
|
+
# @overload provider!(things: nil, items: nil, metadata: nil, links: nil, **metadata_items)
|
|
819
|
+
#
|
|
820
|
+
# @param [Core::Provider, org.openhab.core.common.registry.ManagedProvider, :persistent, :transient, Proc] providers
|
|
821
|
+
# An explicit provider to use. If it's a {Core::Provider}, the type will be inferred automatically.
|
|
822
|
+
# Otherwise it's applied to all types.
|
|
823
|
+
# @param [Hash] providers_by_type
|
|
824
|
+
# A list of providers by type. Type can be `:items`, `:metadata`, `:things`, `:links`,
|
|
825
|
+
# an {Item} applying the provider to all metadata on that item, or a String or Symbol
|
|
826
|
+
# applying the provider to all metadata of that namespace.
|
|
827
|
+
#
|
|
828
|
+
# The provider can be a {org.openhab.core.common.registry.Provider Provider}, `:persistent`,
|
|
829
|
+
# `:transient`, or a Proc returning one of those types. When the Proc is called for metadata
|
|
830
|
+
# elements, the {Core::Items::Metadata::Hash} will be passed as an argument. Therefore it's
|
|
831
|
+
# recommended that you use a Proc, not a Lambda, for permissive argument matching.
|
|
832
|
+
#
|
|
833
|
+
# @return [void]
|
|
834
|
+
#
|
|
835
|
+
# @see provider
|
|
836
|
+
# @see OpenHAB::Core::Provider.current Provider.current for how the current provider is calculated
|
|
837
|
+
#
|
|
838
|
+
def provider!(*providers, **providers_by_type)
|
|
839
|
+
thread_providers = Thread.current[:openhab_providers] ||= {}
|
|
840
|
+
old_providers = thread_providers.dup
|
|
841
|
+
|
|
842
|
+
providers.each do |provider|
|
|
843
|
+
case provider
|
|
844
|
+
when Core::Provider
|
|
845
|
+
thread_providers[provider.class.type] = provider
|
|
846
|
+
when org.openhab.core.common.registry.ManagedProvider
|
|
847
|
+
type = provider.type
|
|
848
|
+
unless type
|
|
849
|
+
raise ArgumentError, "#{provider.inspect} is for objects which are not supported by openhab-scripting"
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
thread_providers[type] = provider
|
|
853
|
+
when Proc,
|
|
854
|
+
:transient,
|
|
855
|
+
:persistent
|
|
856
|
+
Core::Provider::KNOWN_TYPES.each do |known_type|
|
|
857
|
+
thread_providers[known_type] = provider
|
|
858
|
+
end
|
|
859
|
+
when Hash
|
|
860
|
+
# non-symbols can't be used as kwargs, so Item keys show up as a separate hash here
|
|
861
|
+
# just merge it in, and allow it to be handled below
|
|
862
|
+
providers_by_type.merge!(provider)
|
|
863
|
+
else
|
|
864
|
+
raise ArgumentError, "#{provider.inspect} is not a valid provider"
|
|
865
|
+
end
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
providers_by_type.each do |type, provider|
|
|
869
|
+
case provider
|
|
870
|
+
when Proc,
|
|
871
|
+
org.openhab.core.common.registry.ManagedProvider,
|
|
872
|
+
:transient,
|
|
873
|
+
:persistent,
|
|
874
|
+
nil
|
|
875
|
+
nil
|
|
876
|
+
else
|
|
877
|
+
raise ArgumentError, "#{provider.inspect} is not a valid provider"
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
case type
|
|
881
|
+
when :items, :metadata, :things, :links
|
|
882
|
+
if provider.is_a?(org.openhab.core.common.registry.ManagedProvider) && provider.type != type
|
|
883
|
+
raise ArgumentError, "#{provider.inspect} is not a provider for #{type}"
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
thread_providers[type] = provider
|
|
887
|
+
when Symbol, String
|
|
888
|
+
(thread_providers[:metadata_namespaces] ||= {})[type.to_s] = provider
|
|
889
|
+
when Item
|
|
890
|
+
(thread_providers[:metadata_items] ||= {})[type.name] = provider
|
|
891
|
+
else
|
|
892
|
+
raise ArgumentError, "#{type.inspect} is not provider type"
|
|
893
|
+
end
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
old_providers
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
#
|
|
900
|
+
# @see CoreExt::Ephemeris
|
|
901
|
+
#
|
|
902
|
+
# @overload holiday_file(file)
|
|
903
|
+
#
|
|
904
|
+
# Sets a thread local variable to use a specific holiday file
|
|
905
|
+
# for {CoreExt::Ephemeris ephemeris calls} inside the block.
|
|
906
|
+
#
|
|
907
|
+
# @param [String, nil] file Path to a file defining holidays;
|
|
908
|
+
# `nil` to reset to default.
|
|
909
|
+
# @yield [] Block executed in context of the supplied holiday file
|
|
910
|
+
# @return [Object] The return value from the block.
|
|
911
|
+
#
|
|
912
|
+
# @example Set a specific holiday configuration file temporarily
|
|
913
|
+
# holiday_file("/home/cody/holidays.xml") do
|
|
914
|
+
# Time.now.next_holiday
|
|
915
|
+
# end
|
|
916
|
+
#
|
|
917
|
+
# @see holiday_file!
|
|
918
|
+
#
|
|
919
|
+
# @overload holiday_file
|
|
920
|
+
#
|
|
921
|
+
# Returns the current thread local value for the holiday file.
|
|
922
|
+
#
|
|
923
|
+
# @return [String, nil] the current holiday file
|
|
924
|
+
#
|
|
925
|
+
def holiday_file(*args)
|
|
926
|
+
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" if args.length > 1
|
|
927
|
+
|
|
928
|
+
old = Thread.current[:openhab_holiday_file]
|
|
929
|
+
return old if args.empty?
|
|
930
|
+
|
|
931
|
+
holiday_file!(args.first)
|
|
932
|
+
yield
|
|
933
|
+
ensure
|
|
934
|
+
holiday_file!(old)
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
#
|
|
938
|
+
# Sets a thread local variable to set the default holiday file.
|
|
939
|
+
#
|
|
940
|
+
# @see https://github.com/svendiedrichsen/jollyday/tree/master/src/main/resources/holidays Example data files from the source
|
|
941
|
+
#
|
|
942
|
+
# @example
|
|
943
|
+
# holiday_file!("/home/cody/holidays.xml")
|
|
944
|
+
# Time.now.next_holiday
|
|
945
|
+
#
|
|
946
|
+
# @param [String, nil] file Path to a file defining holidays;
|
|
947
|
+
# `nil` to reset to default.
|
|
948
|
+
# @return [Symbol, nil] the new holiday file
|
|
949
|
+
#
|
|
950
|
+
def holiday_file!(file = nil)
|
|
951
|
+
Thread.current[:openhab_holiday_file] = file
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
# @!visibility private
|
|
955
|
+
def try_parse_time_like(string)
|
|
956
|
+
return string unless string.is_a?(String)
|
|
957
|
+
|
|
958
|
+
exception = nil
|
|
959
|
+
[java.time.LocalTime, java.time.LocalDate, java.time.MonthDay, java.time.ZonedDateTime].each do |klass|
|
|
960
|
+
return klass.parse(string)
|
|
961
|
+
rescue ArgumentError => e
|
|
962
|
+
exception ||= e
|
|
963
|
+
next
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
raise exception
|
|
967
|
+
end
|
|
968
|
+
end
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
OpenHAB::Core.wait_till_openhab_ready
|
|
972
|
+
|
|
973
|
+
# import Items classes into global namespace
|
|
974
|
+
OpenHAB::Core::Items.import_into_global_namespace
|
|
975
|
+
|
|
976
|
+
# Extend `main` with DSL methods
|
|
977
|
+
singleton_class.include(OpenHAB::DSL)
|
|
978
|
+
|
|
979
|
+
logger.debug "openHAB JRuby Scripting Library Version #{OpenHAB::DSL::VERSION} Loaded"
|