openhab-scripting 2.14.1 → 2.14.2

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.
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