openhab-scripting 2.14.1 → 2.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab.rb +3 -0
  3. data/lib/openhab/core/dsl/actions.rb +1 -1
  4. data/lib/openhab/core/dsl/entities.rb +41 -4
  5. data/lib/openhab/core/dsl/gems.rb +1 -1
  6. data/lib/openhab/core/dsl/group.rb +3 -1
  7. data/lib/openhab/core/dsl/items/items.rb +3 -1
  8. data/lib/openhab/core/dsl/items/number_item.rb +151 -50
  9. data/lib/openhab/core/dsl/items/string_item.rb +21 -3
  10. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +66 -42
  11. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +2 -1
  12. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +2 -1
  13. data/lib/openhab/core/dsl/property.rb +15 -4
  14. data/lib/openhab/core/dsl/rule/automation_rule.rb +348 -0
  15. data/lib/openhab/core/dsl/rule/guard.rb +43 -6
  16. data/lib/openhab/core/dsl/rule/rule.rb +80 -367
  17. data/lib/openhab/core/dsl/rule/rule_config.rb +153 -0
  18. data/lib/openhab/core/dsl/rule/triggers/changed.rb +145 -0
  19. data/lib/openhab/core/dsl/rule/{channel.rb → triggers/channel.rb} +22 -8
  20. data/lib/openhab/core/dsl/rule/triggers/command.rb +106 -0
  21. data/lib/openhab/core/dsl/rule/{cron.rb → triggers/cron.rb} +36 -14
  22. data/lib/openhab/core/dsl/rule/triggers/trigger.rb +126 -0
  23. data/lib/openhab/core/dsl/rule/triggers/updated.rb +100 -0
  24. data/lib/openhab/core/dsl/time_of_day.rb +50 -24
  25. data/lib/openhab/core/dsl/timers.rb +2 -6
  26. data/lib/openhab/core/dsl/types/quantity.rb +106 -69
  27. data/lib/openhab/core/log.rb +3 -8
  28. data/lib/openhab/core/startup_delay.rb +1 -0
  29. data/lib/openhab/osgi.rb +7 -0
  30. data/lib/openhab/version.rb +1 -1
  31. metadata +10 -6
  32. data/lib/openhab/core/dsl/rule/item.rb +0 -203
  33. data/lib/openhab/core/dsl/rule/triggers.rb +0 -77
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+ require 'pp'
5
+ require 'core/dsl/property'
6
+ require 'core/dsl/rule/triggers/cron'
7
+ require 'core/dsl/rule/triggers/changed'
8
+ require 'core/dsl/rule/triggers/channel'
9
+ require 'core/dsl/rule/triggers/command'
10
+ require 'core/dsl/rule/triggers/updated'
11
+ require 'core/dsl/rule/guard'
12
+ require 'core/dsl/entities'
13
+ require 'core/dsl/time_of_day'
14
+ require 'core/dsl'
15
+ require 'core/dsl/timers'
16
+
17
+ module OpenHAB
18
+ module Core
19
+ module DSL
20
+ #
21
+ # Creates and manages OpenHAB Rules
22
+ #
23
+ module Rule
24
+ #
25
+ # Rule configuration for OpenHAB Rules engine
26
+ #
27
+ class RuleConfig
28
+ include EntityLookup
29
+ include OpenHAB::Core::DSL::Rule::Triggers
30
+ include Guard
31
+ include DSLProperty
32
+ include Logging
33
+ extend OpenHAB::Core::DSL
34
+
35
+ java_import org.openhab.core.library.items.SwitchItem
36
+
37
+ # @return [Array] Of triggers
38
+ attr_reader :triggers
39
+
40
+ # @return [Array] Of trigger delays
41
+ attr_reader :trigger_delays
42
+
43
+ # @return [Array] Of trigger guards
44
+ attr_accessor :guard
45
+
46
+ #
47
+ # Struct holding a run block
48
+ #
49
+ Run = Struct.new(:block)
50
+
51
+ #
52
+ # Struct holding a Triggered block
53
+ #
54
+ Trigger = Struct.new(:block)
55
+
56
+ #
57
+ # Struct holding an otherwise block
58
+ #
59
+ Otherwise = Struct.new(:block)
60
+
61
+ #
62
+ # Struct holding rule delays
63
+ #
64
+ Delay = Struct.new(:duration)
65
+
66
+ prop_array :run, array_name: :run_queue, wrapper: Run
67
+ prop_array :triggered, array_name: :run_queue, wrapper: Trigger
68
+ prop_array :delay, array_name: :run_queue, wrapper: Delay
69
+ prop_array :otherwise, array_name: :run_queue, wrapper: Otherwise
70
+
71
+ prop :name
72
+ prop :description
73
+ prop :enabled
74
+ prop :between
75
+
76
+ #
77
+ # Create a new RuleConfig
78
+ #
79
+ # @param [Object] caller_binding The object initializing this configuration.
80
+ # Used to execute within the object's context
81
+ #
82
+ def initialize(rule_name, caller_binding)
83
+ @triggers = []
84
+ @trigger_delays = {}
85
+ @caller = caller_binding.eval 'self'
86
+ enabled(true)
87
+ on_start(false)
88
+ name(rule_name)
89
+ end
90
+
91
+ #
92
+ # Start this rule on system startup
93
+ #
94
+ # @param [Boolean] run_on_start Run this rule on start, defaults to True
95
+ #
96
+ #
97
+ # rubocop: disable Style/OptionalBooleanParameter
98
+ # Disabled cop due to use in a DSL
99
+ def on_start(run_on_start = true)
100
+ @on_start = run_on_start
101
+ end
102
+ # rubocop: enable Style/OptionalBooleanParameter
103
+
104
+ #
105
+ # Checks if this rule should run on start
106
+ #
107
+ # @return [Boolean] True if rule should run on start, false otherwise.
108
+ #
109
+ def on_start?
110
+ @on_start
111
+ end
112
+
113
+ #
114
+ # Run the supplied block inside the object instance of the object that created the rule config
115
+ #
116
+ # @yield [] Block executed in context of the object creating the rule config
117
+ #
118
+ #
119
+ def my(&block)
120
+ @caller.instance_eval(&block)
121
+ end
122
+
123
+ #
124
+ # Create a logger where name includes rule name if name is set
125
+ #
126
+ # @return [Logging::Logger] Logger with name that appended with rule name if rule name is set
127
+ #
128
+ def logger
129
+ if name
130
+ Logging.logger(name.chomp.gsub(/\s+/, '_'))
131
+ else
132
+ super
133
+ end
134
+ end
135
+
136
+ #
137
+ # Inspect the config object
138
+ #
139
+ # @return [String] details of the config object
140
+ #
141
+ def inspect
142
+ "Name: (#{name}) " \
143
+ "Triggers: (#{triggers}) " \
144
+ "Run blocks: (#{run}) " \
145
+ "on_start: (#{on_start?}) " \
146
+ "Trigger Waits: #{trigger_delays} " \
147
+ "Trigger UIDs: #{triggers.map(&:id).join(', ')}"
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'core/log'
4
+
5
+ module OpenHAB
6
+ module Core
7
+ module DSL
8
+ module Rule
9
+ #
10
+ # Module holds rule triggers
11
+ #
12
+ module Triggers
13
+ include Logging
14
+
15
+ #
16
+ # Struct capturing data necessary for a conditional trigger
17
+ #
18
+ TriggerDelay = Struct.new(:to, :from, :duration, :timer, :tracking_to, keyword_init: true) do
19
+ def timer_active?
20
+ timer&.is_active
21
+ end
22
+ end
23
+
24
+ #
25
+ # Creates a trigger item, group and thing changed
26
+ #
27
+ # @param [Object] items array of objects to create trigger for
28
+ # @param [to] to state for object to change for
29
+ # @param [from] from <description>
30
+ # @param [OpenHAB::Core::Duration] for Duration to delay trigger until to state is met
31
+ #
32
+ # @return [Trigger] OpenHAB trigger
33
+ #
34
+ def changed(*items, to: nil, from: nil, for: nil)
35
+ items.flatten.each do |item|
36
+ logger.trace("Creating changed trigger for entity(#{item}), to(#{to}), from(#{from})")
37
+ # for is a reserved word in ruby, so use local_variable_get :for
38
+ if (wait_duration = binding.local_variable_get(:for))
39
+ changed_wait(item, to: to, from: from, duration: wait_duration)
40
+ else
41
+ # Place in array and flatten to support multiple to elements or single or nil
42
+ [to].flatten.each do |to_state|
43
+ create_changed_trigger(item, from, to_state)
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ #
52
+ # Create a TriggerDelay for for an item or group that is changed for a specific duration
53
+ #
54
+ # @param [Object] item to create trigger delay for
55
+ # @param [OpenHAB::Core::Duration] duration to delay trigger for until condition is met
56
+ # @param [Item State] to OpenHAB Item State item or group needs to change to
57
+ # @param [Item State] from OpenHAB Item State item or group needs to be coming from
58
+ #
59
+ # @return [Array] Array of current TriggerDelay objects
60
+ #
61
+ def changed_wait(item, duration:, to: nil, from: nil)
62
+ # If GroupItems specified, use the group state trigger instead
63
+ if item.is_a? GroupItems
64
+ config = { 'groupName' => item.group.name }
65
+ trigger = Trigger::GROUP_STATE_CHANGE
66
+ else
67
+ config = { 'itemName' => item.name }
68
+ trigger = Trigger::ITEM_STATE_CHANGE
69
+ end
70
+ logger.trace("Creating Changed Wait Change Trigger for #{config}")
71
+ trigger = append_trigger(trigger, config)
72
+ @trigger_delays = { trigger.id => TriggerDelay.new(to: to, from: from, duration: duration) }
73
+ end
74
+
75
+ #
76
+ # Create a changed trigger
77
+ #
78
+ # @param [Object] item to create changed trigger on
79
+ # @param [String] from state to restrict trigger to
80
+ # @param [String] to state restrict trigger to
81
+ #
82
+ #
83
+ def create_changed_trigger(item, from, to)
84
+ trigger, config = case item
85
+ when GroupItems then create_group_changed_trigger(item, from, to)
86
+ when Thing then create_thing_changed_trigger(item, from, to)
87
+ else create_item_changed_trigger(item, from, to)
88
+ end
89
+ append_trigger(trigger, config)
90
+ end
91
+
92
+ #
93
+ # Create a changed trigger for a thing
94
+ #
95
+ # @param [Thing] thing to detected changed states on
96
+ # @param [String] from state to restrict trigger to
97
+ # @param [String] to state to restrict trigger to
98
+ #
99
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
100
+ # second element is a Hash configuring trigger
101
+ #
102
+ def create_thing_changed_trigger(thing, from, to)
103
+ trigger_for_thing(thing, Trigger::THING_CHANGE, to, from)
104
+ end
105
+
106
+ #
107
+ # Create a changed trigger for an item
108
+ #
109
+ # @param [Item] item to detected changed states on
110
+ # @param [String] from state to restrict trigger to
111
+ # @param [String] to to restrict trigger to
112
+ #
113
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
114
+ # second element is a Hash configuring trigger
115
+ #
116
+ def create_item_changed_trigger(item, from, to)
117
+ config = { 'itemName' => item.name }
118
+ config['state'] = to.to_s if to
119
+ config['previousState'] = from.to_s if from
120
+ trigger = Trigger::ITEM_STATE_CHANGE
121
+ [trigger, config]
122
+ end
123
+
124
+ #
125
+ # Create a changed trigger for group items
126
+ #
127
+ # @param [Group] group to detected changed states on
128
+ # @param [String] from state to restrict trigger to
129
+ # @param [String] to to restrict trigger to
130
+ #
131
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
132
+ # second element is a Hash configuring trigger
133
+ #
134
+ def create_group_changed_trigger(group, from, to)
135
+ config = { 'groupName' => group.group.name }
136
+ config['state'] = to.to_s if to
137
+ config['previousState'] = from.to_s if from
138
+ trigger = Trigger::GROUP_STATE_CHANGE
139
+ [trigger, config]
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'core/log'
4
- require 'openhab/core/dsl/rule/triggers'
4
+ require 'openhab/core/dsl/rule/triggers/trigger'
5
5
 
6
6
  module OpenHAB
7
7
  module Core
@@ -10,13 +10,14 @@ module OpenHAB
10
10
  #
11
11
  # Channel triggers
12
12
  #
13
- module Channel
13
+ module Triggers
14
14
  include Logging
15
15
 
16
16
  #
17
17
  # Creates a channel trigger
18
18
  #
19
- # @param [Array] channels array to create triggers for on form of 'binding_id:type_id:thing_id#channel_id' or 'channel_id' if thing is provided
19
+ # @param [Array] channels array to create triggers for on form of 'binding_id:type_id:thing_id#channel_id'
20
+ # or 'channel_id' if thing is provided
20
21
  # @param [thing] thing to create trigger for if not specified with the channel
21
22
  # @param [String] triggered specific triggering condition to match for trigger
22
23
  #
@@ -26,14 +27,27 @@ module OpenHAB
26
27
  channel = [thing, channel].join(':') if thing
27
28
  logger.trace("Creating channel trigger for channel(#{channel}), thing(#{thing}), trigger(#{triggered})")
28
29
  [triggered].flatten.each do |trigger|
29
- config = { 'channelUID' => channel }
30
- config['event'] = trigger.to_s unless trigger.nil?
31
- config['channelUID'] = channel
32
- logger.trace("Creating Change Trigger for #{config}")
33
- @triggers << Trigger.trigger(type: Trigger::CHANNEL_EVENT, config: config)
30
+ create_channel_trigger(channel, trigger)
34
31
  end
35
32
  end
36
33
  end
34
+
35
+ private
36
+
37
+ #
38
+ # Create a trigger for a channel
39
+ #
40
+ # @param [Channel] channel to look for triggers
41
+ # @param [Trigger] trigger specific channel trigger to match
42
+ #
43
+ #
44
+ def create_channel_trigger(channel, trigger)
45
+ config = { 'channelUID' => channel }
46
+ config['event'] = trigger.to_s unless trigger.nil?
47
+ config['channelUID'] = channel
48
+ logger.trace("Creating Change Trigger for #{config}")
49
+ @triggers << Trigger.trigger(type: Trigger::CHANNEL_EVENT, config: config)
50
+ end
37
51
  end
38
52
  end
39
53
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'core/log'
4
+
5
+ module OpenHAB
6
+ module Core
7
+ module DSL
8
+ module Rule
9
+ #
10
+ # Module holds rule triggers
11
+ #
12
+ module Triggers
13
+ include Logging
14
+
15
+ #
16
+ # Create a trigger for when an item or group receives a command
17
+ #
18
+ # The commands/commands parameters are replicated for DSL fluency
19
+ #
20
+ # @param [Array] items Array of items to create trigger for
21
+ # @param [Array] command commands to match for trigger
22
+ # @param [Array] commands commands to match for trigger
23
+ #
24
+ #
25
+ def received_command(*items, command: nil, commands: nil)
26
+ items.flatten.each do |item|
27
+ logger.trace("Creating received command trigger for item(#{item})"\
28
+ "command(#{command}) commands(#{commands})")
29
+
30
+ # Combine command and commands, doing union so only a single nil will be in the combined array.
31
+ combined_commands = combine_commands(command, commands)
32
+ create_received_trigger(combined_commands, item)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ #
39
+ # Create a received trigger based on item type
40
+ #
41
+ # @param [Array] commands to create trigger for
42
+ # @param [Object] item to create trigger for
43
+ #
44
+ #
45
+ def create_received_trigger(commands, item)
46
+ commands.each do |command|
47
+ if item.is_a? GroupItems
48
+ config, trigger = create_group_command_trigger(item)
49
+ else
50
+ config, trigger = create_item_command_trigger(item)
51
+ end
52
+ config['command'] = command.to_s unless command.nil?
53
+ append_trigger(trigger, config)
54
+ end
55
+ end
56
+
57
+ #
58
+ # Create trigger for item commands
59
+ #
60
+ # @param [Item] item to create trigger for
61
+ #
62
+ # @return [Array<Hash,Trigger>] first element is hash of trigger config properties
63
+ # second element is trigger type
64
+ #
65
+ def create_item_command_trigger(item)
66
+ config = { 'itemName' => item.name }
67
+ trigger = Trigger::ITEM_COMMAND
68
+ [config, trigger]
69
+ end
70
+
71
+ #
72
+ # Create trigger for group items
73
+ #
74
+ # @param [Group] group to create trigger for
75
+ #
76
+ # @return [Array<Hash,Trigger>] first element is hash of trigger config properties
77
+ # second element is trigger type
78
+ #
79
+ def create_group_command_trigger(group)
80
+ config = { 'groupName' => group.group.name }
81
+ trigger = Trigger::GROUP_COMMAND
82
+ [config, trigger]
83
+ end
84
+
85
+ #
86
+ # Combine command and commands into a single array
87
+ #
88
+ # @param [Array] command list of commands to trigger on
89
+ # @param [Array] commands list of commands to trigger on
90
+ #
91
+ # @return [Array] Combined flattened and compacted list of commands
92
+ #
93
+ def combine_commands(command, commands)
94
+ combined_commands = ([command] | [commands]).flatten
95
+
96
+ # If either command or commands has a value and one is nil, we need to remove nil from the array.
97
+ # If it is only now a single nil value, we leave the nil in place, so that we create a trigger
98
+ # That isn't looking for a specific command.
99
+ combined_commands = combined_commands.compact unless combined_commands.all?(&:nil?)
100
+ combined_commands
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end