openhab-jrubyscripting 5.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/openhab/core/actions.rb +163 -0
- data/lib/openhab/core/entity_lookup.rb +144 -0
- data/lib/openhab/core/events/abstract_event.rb +17 -0
- data/lib/openhab/core/events/item_channel_link.rb +36 -0
- data/lib/openhab/core/events/item_command_event.rb +78 -0
- data/lib/openhab/core/events/item_event.rb +22 -0
- data/lib/openhab/core/events/item_state_changed_event.rb +52 -0
- data/lib/openhab/core/events/item_state_event.rb +51 -0
- data/lib/openhab/core/events/thing.rb +29 -0
- data/lib/openhab/core/events/thing_status_info_event.rb +53 -0
- data/lib/openhab/core/events.rb +10 -0
- data/lib/openhab/core/items/accepted_data_types.rb +29 -0
- data/lib/openhab/core/items/color_item.rb +52 -0
- data/lib/openhab/core/items/contact_item.rb +52 -0
- data/lib/openhab/core/items/date_time_item.rb +58 -0
- data/lib/openhab/core/items/dimmer_item.rb +148 -0
- data/lib/openhab/core/items/generic_item.rb +344 -0
- data/lib/openhab/core/items/group_item.rb +174 -0
- data/lib/openhab/core/items/image_item.rb +109 -0
- data/lib/openhab/core/items/location_item.rb +34 -0
- data/lib/openhab/core/items/metadata/hash.rb +390 -0
- data/lib/openhab/core/items/metadata/namespace_hash.rb +469 -0
- data/lib/openhab/core/items/metadata.rb +11 -0
- data/lib/openhab/core/items/number_item.rb +62 -0
- data/lib/openhab/core/items/numeric_item.rb +22 -0
- data/lib/openhab/core/items/persistence.rb +327 -0
- data/lib/openhab/core/items/player_item.rb +66 -0
- data/lib/openhab/core/items/proxy.rb +59 -0
- data/lib/openhab/core/items/registry.rb +66 -0
- data/lib/openhab/core/items/rollershutter_item.rb +68 -0
- data/lib/openhab/core/items/semantics/enumerable.rb +152 -0
- data/lib/openhab/core/items/semantics.rb +476 -0
- data/lib/openhab/core/items/state_storage.rb +53 -0
- data/lib/openhab/core/items/string_item.rb +28 -0
- data/lib/openhab/core/items/switch_item.rb +78 -0
- data/lib/openhab/core/items.rb +114 -0
- data/lib/openhab/core/lazy_array.rb +52 -0
- data/lib/openhab/core/profile_factory.rb +118 -0
- data/lib/openhab/core/script_handling.rb +55 -0
- data/lib/openhab/core/things/channel.rb +48 -0
- data/lib/openhab/core/things/channel_uid.rb +51 -0
- data/lib/openhab/core/things/item_channel_link.rb +33 -0
- data/lib/openhab/core/things/profile_callback.rb +52 -0
- data/lib/openhab/core/things/proxy.rb +69 -0
- data/lib/openhab/core/things/registry.rb +46 -0
- data/lib/openhab/core/things/thing.rb +194 -0
- data/lib/openhab/core/things.rb +22 -0
- data/lib/openhab/core/timer.rb +128 -0
- data/lib/openhab/core/types/comparable_type.rb +23 -0
- data/lib/openhab/core/types/date_time_type.rb +259 -0
- data/lib/openhab/core/types/decimal_type.rb +192 -0
- data/lib/openhab/core/types/hsb_type.rb +183 -0
- data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
- data/lib/openhab/core/types/next_previous_type.rb +34 -0
- data/lib/openhab/core/types/numeric_type.rb +52 -0
- data/lib/openhab/core/types/on_off_type.rb +46 -0
- data/lib/openhab/core/types/open_closed_type.rb +41 -0
- data/lib/openhab/core/types/percent_type.rb +95 -0
- data/lib/openhab/core/types/play_pause_type.rb +38 -0
- data/lib/openhab/core/types/point_type.rb +117 -0
- data/lib/openhab/core/types/quantity_type.rb +327 -0
- data/lib/openhab/core/types/raw_type.rb +26 -0
- data/lib/openhab/core/types/refresh_type.rb +27 -0
- data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
- data/lib/openhab/core/types/stop_move_type.rb +34 -0
- data/lib/openhab/core/types/string_type.rb +76 -0
- data/lib/openhab/core/types/type.rb +117 -0
- data/lib/openhab/core/types/un_def_type.rb +38 -0
- data/lib/openhab/core/types/up_down_type.rb +50 -0
- data/lib/openhab/core/types.rb +69 -0
- data/lib/openhab/core/uid.rb +36 -0
- data/lib/openhab/core.rb +85 -0
- data/lib/openhab/core_ext/java/duration.rb +115 -0
- data/lib/openhab/core_ext/java/local_date.rb +93 -0
- data/lib/openhab/core_ext/java/local_time.rb +106 -0
- data/lib/openhab/core_ext/java/month.rb +59 -0
- data/lib/openhab/core_ext/java/month_day.rb +105 -0
- data/lib/openhab/core_ext/java/period.rb +103 -0
- data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
- data/lib/openhab/core_ext/java/time.rb +58 -0
- data/lib/openhab/core_ext/java/unit.rb +15 -0
- data/lib/openhab/core_ext/java/zoned_date_time.rb +116 -0
- data/lib/openhab/core_ext/ruby/array.rb +21 -0
- data/lib/openhab/core_ext/ruby/class.rb +15 -0
- data/lib/openhab/core_ext/ruby/date.rb +89 -0
- data/lib/openhab/core_ext/ruby/numeric.rb +190 -0
- data/lib/openhab/core_ext/ruby/range.rb +70 -0
- data/lib/openhab/core_ext/ruby/time.rb +104 -0
- data/lib/openhab/core_ext.rb +18 -0
- data/lib/openhab/dsl/events/watch_event.rb +18 -0
- data/lib/openhab/dsl/events.rb +9 -0
- data/lib/openhab/dsl/gems.rb +3 -0
- data/lib/openhab/dsl/items/builder.rb +618 -0
- data/lib/openhab/dsl/items/ensure.rb +93 -0
- data/lib/openhab/dsl/items/timed_command.rb +236 -0
- data/lib/openhab/dsl/rules/automation_rule.rb +308 -0
- data/lib/openhab/dsl/rules/builder.rb +1373 -0
- data/lib/openhab/dsl/rules/guard.rb +115 -0
- data/lib/openhab/dsl/rules/name_inference.rb +160 -0
- data/lib/openhab/dsl/rules/property.rb +76 -0
- data/lib/openhab/dsl/rules/rule_triggers.rb +96 -0
- data/lib/openhab/dsl/rules/terse.rb +63 -0
- data/lib/openhab/dsl/rules/triggers/changed.rb +169 -0
- data/lib/openhab/dsl/rules/triggers/channel.rb +57 -0
- data/lib/openhab/dsl/rules/triggers/command.rb +107 -0
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +161 -0
- data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +164 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +195 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +127 -0
- data/lib/openhab/dsl/rules/triggers/trigger.rb +56 -0
- data/lib/openhab/dsl/rules/triggers/updated.rb +130 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch.rb +55 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +155 -0
- data/lib/openhab/dsl/rules/triggers.rb +12 -0
- data/lib/openhab/dsl/rules.rb +29 -0
- data/lib/openhab/dsl/script_handling.rb +55 -0
- data/lib/openhab/dsl/things/builder.rb +263 -0
- data/lib/openhab/dsl/thread_local.rb +48 -0
- data/lib/openhab/dsl/timer_manager.rb +191 -0
- data/lib/openhab/dsl/version.rb +9 -0
- data/lib/openhab/dsl.rb +686 -0
- data/lib/openhab/log.rb +348 -0
- data/lib/openhab/osgi.rb +70 -0
- data/lib/openhab/rspec/configuration.rb +56 -0
- data/lib/openhab/rspec/example_group.rb +90 -0
- data/lib/openhab/rspec/helpers.rb +439 -0
- data/lib/openhab/rspec/hooks.rb +93 -0
- data/lib/openhab/rspec/jruby.rb +46 -0
- data/lib/openhab/rspec/karaf.rb +811 -0
- data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
- data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
- data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
- data/lib/openhab/rspec/mocks/metadata_provider.rb +75 -0
- data/lib/openhab/rspec/mocks/persistence_service.rb +140 -0
- data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
- data/lib/openhab/rspec/mocks/synchronous_executor.rb +56 -0
- data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
- data/lib/openhab/rspec/mocks/timer.rb +95 -0
- data/lib/openhab/rspec/openhab/core/actions.rb +26 -0
- data/lib/openhab/rspec/openhab/core/items/proxy.rb +27 -0
- data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
- data/lib/openhab/rspec/shell.rb +31 -0
- data/lib/openhab/rspec/suspend_rules.rb +60 -0
- data/lib/openhab/rspec.rb +17 -0
- data/lib/openhab/yard/cli/stats.rb +23 -0
- data/lib/openhab/yard/code_objects/group_object.rb +17 -0
- data/lib/openhab/yard/code_objects/java/base.rb +31 -0
- data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
- data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
- data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
- data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
- data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
- data/lib/openhab/yard/handlers/jruby/base.rb +49 -0
- data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
- data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
- data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +27 -0
- data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
- data/lib/openhab/yard/html_helper.rb +44 -0
- data/lib/openhab/yard/tags/constant_directive.rb +20 -0
- data/lib/openhab/yard/tags/group_directive.rb +24 -0
- data/lib/openhab/yard/tags/library.rb +3 -0
- data/lib/openhab/yard.rb +32 -0
- metadata +504 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "property"
|
|
4
|
+
|
|
5
|
+
module OpenHAB
|
|
6
|
+
module DSL
|
|
7
|
+
module Rules
|
|
8
|
+
#
|
|
9
|
+
# Guard that can prevent execution of a rule if not satisfied
|
|
10
|
+
#
|
|
11
|
+
# @!visibility private
|
|
12
|
+
class Guard
|
|
13
|
+
#
|
|
14
|
+
# Create a new Guard
|
|
15
|
+
#
|
|
16
|
+
# @param [Object] only_if Item or Proc to use as guard
|
|
17
|
+
# @param [Object] not_if Item or Proc to use as guard
|
|
18
|
+
#
|
|
19
|
+
def initialize(run_context:, only_if: nil, not_if: nil)
|
|
20
|
+
@run_context = run_context
|
|
21
|
+
@only_if = only_if
|
|
22
|
+
@not_if = not_if
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# Convert the guard into a string
|
|
27
|
+
#
|
|
28
|
+
# @return [String] describing the only_of and not_if guards
|
|
29
|
+
#
|
|
30
|
+
def to_s
|
|
31
|
+
"only_if: #{@only_if}, not_if: #{@not_if}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Checks if a guard should run
|
|
36
|
+
#
|
|
37
|
+
# @param [OpenHAB Trigger Event] event OpenHAB Trigger Event
|
|
38
|
+
#
|
|
39
|
+
# @return [true,false] True if guard is satisfied, false otherwise
|
|
40
|
+
#
|
|
41
|
+
def should_run?(event)
|
|
42
|
+
logger.trace("Checking guards #{self}")
|
|
43
|
+
check(@only_if, check_type: :only_if,
|
|
44
|
+
event: event) && check(@not_if, check_type: :not_if, event: event)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# Check if guard is satisfied
|
|
51
|
+
#
|
|
52
|
+
# @param [Array] conditions to check
|
|
53
|
+
# @param [Symbol] check_type type of check to perform (:only_if or :not_if)
|
|
54
|
+
# @param [Event] event OpenHAB event to see if it satisfies the guard
|
|
55
|
+
#
|
|
56
|
+
# @return [true,false] True if guard is satisfied, false otherwise
|
|
57
|
+
#
|
|
58
|
+
def check(conditions, check_type:, event:)
|
|
59
|
+
return true if conditions.nil? || conditions.empty?
|
|
60
|
+
|
|
61
|
+
procs, items = conditions.flatten.partition { |condition| condition.is_a?(Proc) }
|
|
62
|
+
logger.trace("Procs: #{procs} Items: #{items}")
|
|
63
|
+
|
|
64
|
+
items.each { |item| logger.trace { "#{item} truthy? #{item.truthy?}" } }
|
|
65
|
+
|
|
66
|
+
process_check(check_type: check_type, event: event, items: items, procs: procs)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# Execute the guard check
|
|
71
|
+
#
|
|
72
|
+
# @param [Symbol] check_type :only_if or :not_if to check
|
|
73
|
+
# @param [OpenHAB Event] event event to check if meets guard
|
|
74
|
+
# @param [Array<Item>] items to check if satisfy criteria
|
|
75
|
+
# @param [Array] procs to check if satisfy criteria
|
|
76
|
+
#
|
|
77
|
+
# @return [true,false] True if criteria are satisfied, false otherwise
|
|
78
|
+
#
|
|
79
|
+
def process_check(check_type:, event:, items:, procs:)
|
|
80
|
+
case check_type
|
|
81
|
+
when :only_if then process_only_if(event, items, procs)
|
|
82
|
+
when :not_if then process_not_if(event, items, procs)
|
|
83
|
+
else raise ArgumentError, "Unexpected check type: #{check_type}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
#
|
|
88
|
+
# Check not_if guard
|
|
89
|
+
#
|
|
90
|
+
# @param [OpenHAB Event] event to check if meets guard
|
|
91
|
+
# @param [Array<Item>] items to check if satisfy criteria
|
|
92
|
+
# @param [Array] procs to check if satisfy criteria
|
|
93
|
+
#
|
|
94
|
+
# @return [true,false] True if criteria are satisfied, false otherwise
|
|
95
|
+
#
|
|
96
|
+
def process_not_if(event, items, procs)
|
|
97
|
+
items.flatten.none?(&:truthy?) && procs.none? { |proc| @run_context.instance_exec(event, &proc) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#
|
|
101
|
+
# Check only_if guard
|
|
102
|
+
#
|
|
103
|
+
# @param [OpenHAB Event] event to check if meets guard
|
|
104
|
+
# @param [Array<Item>] items to check if satisfy criteria
|
|
105
|
+
# @param [Array] procs to check if satisfy criteria
|
|
106
|
+
#
|
|
107
|
+
# @return [true,false] True if criteria are satisfied, false otherwise
|
|
108
|
+
#
|
|
109
|
+
def process_only_if(event, items, procs)
|
|
110
|
+
items.flatten.all?(&:truthy?) && procs.all? { |proc| @run_context.instance_exec(event, &proc) }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "triggers/cron/cron"
|
|
4
|
+
|
|
5
|
+
module OpenHAB
|
|
6
|
+
module DSL
|
|
7
|
+
module Rules
|
|
8
|
+
# Contains helper methods for inferring a rule name from its triggers
|
|
9
|
+
# @!visibility private
|
|
10
|
+
module NameInference
|
|
11
|
+
# Trigger Type UIDs that we know how to generate a name for
|
|
12
|
+
KNOWN_TRIGGER_TYPES = [
|
|
13
|
+
"core.ChannelEventTrigger",
|
|
14
|
+
"core.GenericEventTrigger",
|
|
15
|
+
"core.GroupCommandTrigger",
|
|
16
|
+
"core.GroupStateChangeTrigger",
|
|
17
|
+
"core.GroupStateUpdateTrigger",
|
|
18
|
+
"core.ItemCommandTrigger",
|
|
19
|
+
"core.ItemStateChangeTrigger",
|
|
20
|
+
"core.ItemStateUpdateTrigger",
|
|
21
|
+
Triggers::Cron::CRON_TRIGGER_MODULE_ID
|
|
22
|
+
].freeze
|
|
23
|
+
private_constant :KNOWN_TRIGGER_TYPES
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# takes a freeform rule name and makes it more palatable as an id
|
|
27
|
+
def infer_rule_id_from_name(name)
|
|
28
|
+
name.downcase.delete("'\"").gsub(/\W+/, "_")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# get the block's source location, and simplify to a simple filename
|
|
32
|
+
def infer_rule_id_from_block(block)
|
|
33
|
+
file = File.basename(block.source_location.first)
|
|
34
|
+
"#{file}:#{block.source_location.last}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# formulate a readable rule name such as "TestSwitch received command ON" if possible
|
|
38
|
+
def infer_rule_name(config)
|
|
39
|
+
known_triggers, unknown_triggers = config.triggers.partition do |t|
|
|
40
|
+
KNOWN_TRIGGER_TYPES.include?(t.type_uid)
|
|
41
|
+
end
|
|
42
|
+
return nil unless unknown_triggers.empty?
|
|
43
|
+
|
|
44
|
+
cron_triggers = known_triggers.select { |t| t.type_uid == "jsr223.jruby.CronTrigger" }
|
|
45
|
+
ruby_every_triggers = config.ruby_triggers.select { |t| t.first == :every }
|
|
46
|
+
|
|
47
|
+
# makes sure there aren't any true cron triggers cause we can't format them
|
|
48
|
+
return nil unless cron_triggers.length == ruby_every_triggers.length
|
|
49
|
+
return nil unless config.ruby_triggers.length == 1
|
|
50
|
+
|
|
51
|
+
infer_rule_name_from_trigger(*config.ruby_triggers.first)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# formulate a readable rule name from a single trigger if possible
|
|
57
|
+
def infer_rule_name_from_trigger(trigger, items = nil, kwargs = {})
|
|
58
|
+
case trigger
|
|
59
|
+
when :every
|
|
60
|
+
infer_rule_name_from_every_trigger(items, **kwargs)
|
|
61
|
+
when :channel
|
|
62
|
+
infer_rule_name_from_channel_trigger(items, **kwargs)
|
|
63
|
+
when :changed, :updated, :received_command
|
|
64
|
+
infer_rule_name_from_item_trigger(trigger, items, kwargs)
|
|
65
|
+
when :channel_linked, :channel_unlinked
|
|
66
|
+
infer_rule_name_from_channel_link_trigger(trigger)
|
|
67
|
+
when :thing_added, :thing_removed, :thing_updated
|
|
68
|
+
infer_rule_name_from_thing_trigger(trigger)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# formulate a readable rule name from an item-type trigger
|
|
73
|
+
def infer_rule_name_from_item_trigger(trigger, items, kwargs)
|
|
74
|
+
kwargs.delete(:command) if kwargs[:command] == [nil]
|
|
75
|
+
return unless items.length <= 3 &&
|
|
76
|
+
(kwargs.keys - %i[from to command duration]).empty?
|
|
77
|
+
return if kwargs.values_at(:from, :to, :command).compact.any? do |v|
|
|
78
|
+
next false if v.is_a?(Array) && v.length <= 4 # arbitrary length
|
|
79
|
+
next false if v.is_a?(Range)
|
|
80
|
+
|
|
81
|
+
v.is_a?(Proc) || v.is_a?(Enumerable)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
trigger_name = trigger.to_s.tr("_", " ")
|
|
85
|
+
item_names = items.map do |item|
|
|
86
|
+
if item.is_a?(GroupItem::Members)
|
|
87
|
+
"#{item.group.name}.members"
|
|
88
|
+
else
|
|
89
|
+
item.name
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
name = "#{format_beginning_of_sentence_array(item_names)} #{trigger_name}"
|
|
93
|
+
|
|
94
|
+
name += " from #{format_inspected_array(kwargs[:from])}" if kwargs[:from]
|
|
95
|
+
name += " to #{format_inspected_array(kwargs[:to])}" if kwargs[:to]
|
|
96
|
+
name += " #{format_inspected_array(kwargs[:command])}" if kwargs[:command]
|
|
97
|
+
name += " for #{kwargs[:duration]}" if kwargs[:duration]
|
|
98
|
+
name.freeze
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# formulate a readable rule name from an every-style cron trigger
|
|
102
|
+
def infer_rule_name_from_every_trigger(value, at:)
|
|
103
|
+
name = "Every #{value}"
|
|
104
|
+
name += " at #{at}" if at
|
|
105
|
+
name
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# formulate a readable rule name from a channel trigger
|
|
109
|
+
def infer_rule_name_from_channel_trigger(channels, triggers:)
|
|
110
|
+
triggers = [] if triggers == [nil]
|
|
111
|
+
name = "#{format_beginning_of_sentence_array(channels)} triggered"
|
|
112
|
+
name += " #{format_inspected_array(triggers)}" unless triggers.empty?
|
|
113
|
+
name
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# formulate a readable rule name from a channel link trigger
|
|
117
|
+
def infer_rule_name_from_channel_link_trigger(trigger)
|
|
118
|
+
trigger == :channel_linked ? "Channel linked to item" : "Channel unlinked from item"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# formulate a readable rule name from a thing added/updated/remove trigger
|
|
122
|
+
def infer_rule_name_from_thing_trigger(trigger)
|
|
123
|
+
{
|
|
124
|
+
thing_added: "Thing Added",
|
|
125
|
+
thing_updated: "Thing updated",
|
|
126
|
+
thing_removed: "Thing removed"
|
|
127
|
+
}[trigger]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# format an array of words that will be the beginning of a sentence
|
|
131
|
+
def format_beginning_of_sentence_array(array)
|
|
132
|
+
result = format_array(array)
|
|
133
|
+
if array.length > 2
|
|
134
|
+
result = result.dup
|
|
135
|
+
result[0] = "A"
|
|
136
|
+
result.freeze
|
|
137
|
+
end
|
|
138
|
+
result
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# format an array of items that need to be inspected individually
|
|
142
|
+
def format_inspected_array(array)
|
|
143
|
+
return array.inspect if array.is_a?(Range)
|
|
144
|
+
|
|
145
|
+
array = [array] unless array.is_a?(Array)
|
|
146
|
+
format_array(array.map(&:inspect))
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# format an array of words in a friendly way
|
|
150
|
+
def format_array(array)
|
|
151
|
+
return array[0] if array.length == 1
|
|
152
|
+
return "#{array[0]} or #{array[1]}" if array.length == 2
|
|
153
|
+
|
|
154
|
+
"any of #{array.join(", ")}"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenHAB
|
|
4
|
+
module DSL
|
|
5
|
+
module Rules
|
|
6
|
+
#
|
|
7
|
+
# Provides methods to support DSL properties
|
|
8
|
+
#
|
|
9
|
+
# @visibility private
|
|
10
|
+
module Property
|
|
11
|
+
#
|
|
12
|
+
# Dynamically creates a property that acts and an accessor with no arguments
|
|
13
|
+
# and a setter with any number of arguments or a block.
|
|
14
|
+
#
|
|
15
|
+
# @param [String] name of the property
|
|
16
|
+
#
|
|
17
|
+
#
|
|
18
|
+
def prop(name)
|
|
19
|
+
# rubocop rules are disabled because this method is dynamically defined on the calling
|
|
20
|
+
# object making calls to other methods in this module impossible, or if done on methods
|
|
21
|
+
# in this module than instance variable belong to the module not the calling class
|
|
22
|
+
define_method(name) do |*args, &block|
|
|
23
|
+
if args.length.zero? && block.nil? == true
|
|
24
|
+
instance_variable_get("@#{name}")
|
|
25
|
+
else
|
|
26
|
+
logger.trace("Property '#{name}' called with args(#{args}) and block(#{block})")
|
|
27
|
+
if args.length == 1
|
|
28
|
+
instance_variable_set("@#{name}", args.first)
|
|
29
|
+
elsif args.length > 1
|
|
30
|
+
instance_variable_set("@#{name}", args)
|
|
31
|
+
elsif block
|
|
32
|
+
instance_variable_set("@#{name}", block)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Dynamically creates a property array acts and an accessor with no arguments
|
|
40
|
+
# and a pushes any number of arguments or a block onto they property array
|
|
41
|
+
# You can provide a block to this method which can be used to check if the provided value is acceptable.
|
|
42
|
+
#
|
|
43
|
+
# @param [String] name of the property
|
|
44
|
+
# @param [String] array_name name of the array to use, defaults to name of property
|
|
45
|
+
# @param [Class] wrapper object to put around elements added to the array
|
|
46
|
+
#
|
|
47
|
+
def prop_array(name, array_name: nil, wrapper: nil)
|
|
48
|
+
define_method(name) do |*args, &block|
|
|
49
|
+
array_name ||= name
|
|
50
|
+
if args.length.zero? && block.nil? == true
|
|
51
|
+
instance_variable_get("@#{array_name}")
|
|
52
|
+
else
|
|
53
|
+
logger.trace("Property '#{name}' called with args(#{args}) and block(#{block})")
|
|
54
|
+
if args.length == 1
|
|
55
|
+
insert = args.first
|
|
56
|
+
elsif args.length > 1
|
|
57
|
+
insert = args
|
|
58
|
+
elsif block
|
|
59
|
+
insert = block
|
|
60
|
+
end
|
|
61
|
+
yield insert if block_given?
|
|
62
|
+
insert = wrapper.new(insert) if wrapper
|
|
63
|
+
instance_variable_set("@#{array_name}", (instance_variable_get("@#{array_name}") || []) << insert)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
return unless array_name
|
|
68
|
+
|
|
69
|
+
define_method(array_name) do
|
|
70
|
+
instance_variable_get("@#{array_name}")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
require "securerandom"
|
|
6
|
+
|
|
7
|
+
require_relative "triggers/conditions/proc"
|
|
8
|
+
|
|
9
|
+
module OpenHAB
|
|
10
|
+
module DSL
|
|
11
|
+
module Rules
|
|
12
|
+
#
|
|
13
|
+
# Rule configuration for OpenHAB Rules engine
|
|
14
|
+
#
|
|
15
|
+
# @!visibility private
|
|
16
|
+
class RuleTriggers
|
|
17
|
+
# @return [Array] Of triggers
|
|
18
|
+
attr_accessor :triggers
|
|
19
|
+
|
|
20
|
+
# @return [Hash] Of trigger conditions
|
|
21
|
+
attr_reader :trigger_conditions
|
|
22
|
+
|
|
23
|
+
# @return [Hash] Hash of trigger UIDs to attachments
|
|
24
|
+
attr_reader :attachments
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# Create a new RuleTrigger
|
|
28
|
+
#
|
|
29
|
+
def initialize
|
|
30
|
+
@triggers = []
|
|
31
|
+
@trigger_conditions = Hash.new(Triggers::Conditions::Proc::ANY)
|
|
32
|
+
@attachments = {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# Append a trigger to the list of triggers
|
|
37
|
+
#
|
|
38
|
+
# @param [String] type of trigger to create
|
|
39
|
+
# @param [Map] config map describing trigger configuration
|
|
40
|
+
# @param [Object] attach object to be attached to the trigger
|
|
41
|
+
#
|
|
42
|
+
# @return [Trigger] OpenHAB trigger
|
|
43
|
+
#
|
|
44
|
+
def append_trigger(type:, config:, attach: nil, conditions: nil)
|
|
45
|
+
config.transform_keys!(&:to_s)
|
|
46
|
+
RuleTriggers.trigger(type: type, config: config).tap do |trigger|
|
|
47
|
+
logger.trace("Appending trigger (#{trigger}) attach (#{attach}) conditions(#{conditions})")
|
|
48
|
+
@triggers << trigger
|
|
49
|
+
@attachments[trigger.id] = attach if attach
|
|
50
|
+
@trigger_conditions[trigger.id] = conditions if conditions
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Create a trigger
|
|
56
|
+
#
|
|
57
|
+
# @param [String] type of trigger
|
|
58
|
+
# @param [Map] config map
|
|
59
|
+
#
|
|
60
|
+
# @return [OpenHAB Trigger] configured by type and supplied config
|
|
61
|
+
#
|
|
62
|
+
def self.trigger(type:, config:)
|
|
63
|
+
logger.trace("Creating trigger of type '#{type}' config: #{config}")
|
|
64
|
+
org.openhab.core.automation.util.TriggerBuilder.create
|
|
65
|
+
.with_id(uuid)
|
|
66
|
+
.with_type_uid(type)
|
|
67
|
+
.with_configuration(org.openhab.core.config.core.Configuration.new(config))
|
|
68
|
+
.build
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#
|
|
72
|
+
# Generate a UUID for triggers
|
|
73
|
+
#
|
|
74
|
+
# @return [String] UUID
|
|
75
|
+
#
|
|
76
|
+
def self.uuid
|
|
77
|
+
SecureRandom.uuid
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
# Inspect the config object
|
|
82
|
+
#
|
|
83
|
+
# @return [String] details of the config object
|
|
84
|
+
#
|
|
85
|
+
def inspect
|
|
86
|
+
<<~TEXT.tr("\n", " ")
|
|
87
|
+
#<RuleTriggers #{triggers.inspect}
|
|
88
|
+
Conditions: #{trigger_conditions.inspect}
|
|
89
|
+
UIDs: #{triggers.map(&:id).inspect}
|
|
90
|
+
Attachments: #{attachments.inspect}>
|
|
91
|
+
TEXT
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenHAB
|
|
4
|
+
module DSL
|
|
5
|
+
module Rules
|
|
6
|
+
# If you have a single trigger and execution block, you can use a terse rule:
|
|
7
|
+
# All parameters to the trigger are passed through, and an optional `name:` parameter is added.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# changed TestSwitch do |event|
|
|
11
|
+
# logger.info("TestSwitch changed to #{event.state}")
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# received_command TestSwitch, name: "My Test Switch Rule", command: ON do
|
|
16
|
+
# logger.info("TestSwitch received command ON")
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
module Terse
|
|
20
|
+
class << self
|
|
21
|
+
# @!visibility private
|
|
22
|
+
# @!macro def_terse_rule
|
|
23
|
+
# @!method $1(*args, name :nil, id: nil, **kwargs, &block)
|
|
24
|
+
# Create a new rule with a $1 trigger.
|
|
25
|
+
# @param name [String] The name for the rule.
|
|
26
|
+
# @param id [String] The ID for the rule.
|
|
27
|
+
# @yield The execution block for the rule.
|
|
28
|
+
# @return [void]
|
|
29
|
+
# @see Builder#$1
|
|
30
|
+
def def_terse_rule(trigger)
|
|
31
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
|
32
|
+
def #{trigger}(*args, name: nil, id: nil, **kwargs, &block) # def changed(*args, name: nil, id: nil, **kwargs, &block)
|
|
33
|
+
raise ArgumentError, "Block is required" unless block # raise ArgumentError, "Block is required" unless block
|
|
34
|
+
#
|
|
35
|
+
id ||= NameInference.infer_rule_id_from_name(name) if name # id ||= NameInference.infer_rule_id_from_name(name) if name
|
|
36
|
+
id ||= NameInference.infer_rule_id_from_block(block) # id ||= NameInference.infer_rule_id_from_block(block)
|
|
37
|
+
script = block.source rescue nil # script = block.source rescue nil
|
|
38
|
+
caller_binding = block.binding # caller_binding = block.binding
|
|
39
|
+
rule name, id: id, script: script, binding: caller_binding do # rule name, id: id, script: script, binding: caller_binding do
|
|
40
|
+
#{trigger}(*args, **kwargs) # changed(*args, **kwargs)
|
|
41
|
+
run(&block) # run(&block)
|
|
42
|
+
end # end
|
|
43
|
+
end # end
|
|
44
|
+
module_function #{trigger.inspect} # module_function :changed
|
|
45
|
+
RUBY
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def_terse_rule(:changed)
|
|
50
|
+
def_terse_rule(:channel)
|
|
51
|
+
def_terse_rule(:channel_linked)
|
|
52
|
+
def_terse_rule(:channel_unlinked)
|
|
53
|
+
def_terse_rule(:cron)
|
|
54
|
+
def_terse_rule(:every)
|
|
55
|
+
def_terse_rule(:received_command)
|
|
56
|
+
def_terse_rule(:thing_added)
|
|
57
|
+
def_terse_rule(:thing_updated)
|
|
58
|
+
def_terse_rule(:thing_removed)
|
|
59
|
+
def_terse_rule(:updated)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "conditions/duration"
|
|
4
|
+
require_relative "conditions/proc"
|
|
5
|
+
require_relative "trigger"
|
|
6
|
+
|
|
7
|
+
module OpenHAB
|
|
8
|
+
module DSL
|
|
9
|
+
module Rules
|
|
10
|
+
module Triggers
|
|
11
|
+
# @!visibility private
|
|
12
|
+
#
|
|
13
|
+
# Creates changed triggers
|
|
14
|
+
#
|
|
15
|
+
class Changed < Trigger
|
|
16
|
+
#
|
|
17
|
+
# Create the trigger
|
|
18
|
+
#
|
|
19
|
+
# @param [Object] item item to create trigger for
|
|
20
|
+
# @param [Item State] from state to restrict trigger to
|
|
21
|
+
# @param [Item State] to state to restrict trigger to
|
|
22
|
+
# @param [Duration, nil] duration ruration to delay trigger until to state is met
|
|
23
|
+
# @param [Object] attach object to be attached to the trigger
|
|
24
|
+
#
|
|
25
|
+
# @return [Trigger] OpenHAB triggers
|
|
26
|
+
#
|
|
27
|
+
def trigger(item:, from:, to:, duration:, attach:)
|
|
28
|
+
if duration
|
|
29
|
+
wait_trigger(item: item, from: from, to: to, duration: duration, attach: attach)
|
|
30
|
+
elsif [to, from].grep(Range).any?
|
|
31
|
+
range_trigger(item: item, from: from, to: to, attach: attach)
|
|
32
|
+
elsif [to, from].grep(Proc).any?
|
|
33
|
+
proc_trigger(item: item, from: from, to: to, attach: attach)
|
|
34
|
+
else
|
|
35
|
+
changed_trigger(item: item, from: from, to: to, attach: attach)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# @return [String] A thing status Change trigger
|
|
42
|
+
THING_CHANGE = "core.ThingStatusChangeTrigger"
|
|
43
|
+
|
|
44
|
+
# @return [String] An item state change trigger
|
|
45
|
+
ITEM_STATE_CHANGE = "core.ItemStateChangeTrigger"
|
|
46
|
+
|
|
47
|
+
# @return [String] A group state change trigger for items in the group
|
|
48
|
+
GROUP_STATE_CHANGE = "core.GroupStateChangeTrigger"
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# Create a TriggerDelay for for an item or group that is changed for a specific duration
|
|
52
|
+
#
|
|
53
|
+
# @param [Object] item to create trigger delay for
|
|
54
|
+
# @param [Duration] duration to delay trigger for until condition is met
|
|
55
|
+
# @param [Item State] to OpenHAB Item State item or group needs to change to
|
|
56
|
+
# @param [Item State] from OpenHAB Item State item or group needs to be coming from
|
|
57
|
+
# @param [Object] attach object to be attached to the trigger
|
|
58
|
+
#
|
|
59
|
+
# @return [Trigger] OpenHAB trigger
|
|
60
|
+
#
|
|
61
|
+
def wait_trigger(item:, duration:, to: nil, from: nil, attach: nil)
|
|
62
|
+
item_name = item.respond_to?(:name) ? item.name : item.to_s
|
|
63
|
+
logger.trace("Creating Changed Wait Change Trigger for Item(#{item_name}) Duration(#{duration}) "\
|
|
64
|
+
"To(#{to}) From(#{from}) Attach(#{attach})")
|
|
65
|
+
conditions = Conditions::Duration.new(to: to, from: from, duration: duration)
|
|
66
|
+
changed_trigger(item: item, to: nil, from: nil, attach: attach, conditions: conditions)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# Creates a trigger with a range condition on either 'from' or 'to' field
|
|
71
|
+
# @param [Object] item to create changed trigger on
|
|
72
|
+
# @param [Object] from state to restrict trigger to
|
|
73
|
+
# @param [Object] to state restrict trigger to
|
|
74
|
+
# @param [Object] attach object to be attached to the trigger
|
|
75
|
+
# @return [Trigger] OpenHAB trigger
|
|
76
|
+
#
|
|
77
|
+
def range_trigger(item:, from:, to:, attach:)
|
|
78
|
+
from, to = Conditions::Proc.range_procs(from, to)
|
|
79
|
+
proc_trigger(item: item, from: from, to: to, attach: attach)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# Creates a trigger with a proc condition on either 'from' or 'to' field
|
|
84
|
+
# @param [Object] item to create changed trigger on
|
|
85
|
+
# @param [Object] from state to restrict trigger to
|
|
86
|
+
# @param [Object] to state restrict trigger to
|
|
87
|
+
# @param [Object] attach object to be attached to the trigger
|
|
88
|
+
# @return [Trigger] OpenHAB trigger
|
|
89
|
+
#
|
|
90
|
+
def proc_trigger(item:, from:, to:, attach:)
|
|
91
|
+
# swap from/to w/ nil if from/to is a proc
|
|
92
|
+
# rubocop:disable Style/ParallelAssignment
|
|
93
|
+
from_proc, from = from, nil if from.is_a?(Proc)
|
|
94
|
+
to_proc, to = to, nil if to.is_a?(Proc)
|
|
95
|
+
# rubocop:enable Style/ParallelAssignment
|
|
96
|
+
conditions = Conditions::Proc.new(to: to_proc, from: from_proc)
|
|
97
|
+
changed_trigger(item: item, from: from, to: to, attach: attach, conditions: conditions)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#
|
|
101
|
+
# Create a changed trigger
|
|
102
|
+
#
|
|
103
|
+
# @param [Object] item to create changed trigger on
|
|
104
|
+
# @param [Object] from state to restrict trigger to
|
|
105
|
+
# @param [Object] to state restrict trigger to
|
|
106
|
+
# @param [Object] attach object to be attached to the trigger
|
|
107
|
+
#
|
|
108
|
+
def changed_trigger(item:, from:, to:, attach: nil, conditions: nil)
|
|
109
|
+
type, config = case item
|
|
110
|
+
when GroupItem::Members then group(group: item, from: from,
|
|
111
|
+
to: to)
|
|
112
|
+
when Core::Things::Thing then thing(thing: item, from: from, to: to)
|
|
113
|
+
else item(item: item, from: from, to: to)
|
|
114
|
+
end
|
|
115
|
+
append_trigger(type: type, config: config, attach: attach, conditions: conditions)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#
|
|
119
|
+
# Create a changed trigger for a thing
|
|
120
|
+
#
|
|
121
|
+
# @param [Thing] thing to detected changed states on
|
|
122
|
+
# @param [String] from state to restrict trigger to
|
|
123
|
+
# @param [String] to state to restrict trigger to
|
|
124
|
+
#
|
|
125
|
+
# @return [Array<Hash,String>] first element is a String specifying trigger type
|
|
126
|
+
# second element is a Hash configuring trigger
|
|
127
|
+
#
|
|
128
|
+
def thing(thing:, from:, to:)
|
|
129
|
+
trigger_for_thing(thing: thing, type: THING_CHANGE, to: to, from: from)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Create a changed trigger for an item
|
|
134
|
+
#
|
|
135
|
+
# @param [Item] item to detected changed states on
|
|
136
|
+
# @param [String] from state to restrict trigger to
|
|
137
|
+
# @param [String] to to restrict trigger to
|
|
138
|
+
#
|
|
139
|
+
# @return [Array<Hash,String>] first element is a String specifying trigger type
|
|
140
|
+
# second element is a Hash configuring trigger
|
|
141
|
+
#
|
|
142
|
+
def item(item:, from:, to:)
|
|
143
|
+
config = { "itemName" => item.name }
|
|
144
|
+
config["state"] = to.to_s if to
|
|
145
|
+
config["previousState"] = from.to_s if from
|
|
146
|
+
[ITEM_STATE_CHANGE, config]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#
|
|
150
|
+
# Create a changed trigger for group items
|
|
151
|
+
#
|
|
152
|
+
# @param [Group] group to detected changed states on
|
|
153
|
+
# @param [String] from state to restrict trigger to
|
|
154
|
+
# @param [String] to to restrict trigger to
|
|
155
|
+
#
|
|
156
|
+
# @return [Array<Hash,String>] first element is a String specifying trigger type
|
|
157
|
+
# second element is a Hash configuring trigger
|
|
158
|
+
#
|
|
159
|
+
def group(group:, from:, to:)
|
|
160
|
+
config = { "groupName" => group.group.name }
|
|
161
|
+
config["state"] = to.to_s if to
|
|
162
|
+
config["previousState"] = from.to_s if from
|
|
163
|
+
[GROUP_STATE_CHANGE, config]
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|