openhab-scripting 2.13.1 → 2.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab.rb +3 -0
  3. data/lib/openhab/core/dsl.rb +4 -0
  4. data/lib/openhab/core/dsl/actions.rb +1 -1
  5. data/lib/openhab/core/dsl/entities.rb +41 -4
  6. data/lib/openhab/core/dsl/gems.rb +1 -1
  7. data/lib/openhab/core/dsl/group.rb +3 -1
  8. data/lib/openhab/core/dsl/items/items.rb +3 -1
  9. data/lib/openhab/core/dsl/items/number_item.rb +151 -50
  10. data/lib/openhab/core/dsl/items/string_item.rb +21 -3
  11. data/lib/openhab/core/dsl/monkey_patch/items/items.rb +2 -0
  12. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +66 -42
  13. data/lib/openhab/core/dsl/monkey_patch/items/persistence.rb +72 -0
  14. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +2 -1
  15. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +2 -1
  16. data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +1 -0
  17. data/lib/openhab/core/dsl/persistence.rb +27 -0
  18. data/lib/openhab/core/dsl/property.rb +15 -4
  19. data/lib/openhab/core/dsl/rule/automation_rule.rb +348 -0
  20. data/lib/openhab/core/dsl/rule/guard.rb +43 -6
  21. data/lib/openhab/core/dsl/rule/rule.rb +80 -354
  22. data/lib/openhab/core/dsl/rule/rule_config.rb +153 -0
  23. data/lib/openhab/core/dsl/rule/triggers/changed.rb +145 -0
  24. data/lib/openhab/core/dsl/rule/{channel.rb → triggers/channel.rb} +22 -8
  25. data/lib/openhab/core/dsl/rule/triggers/command.rb +106 -0
  26. data/lib/openhab/core/dsl/rule/{cron.rb → triggers/cron.rb} +36 -14
  27. data/lib/openhab/core/dsl/rule/triggers/trigger.rb +126 -0
  28. data/lib/openhab/core/dsl/rule/triggers/updated.rb +100 -0
  29. data/lib/openhab/core/dsl/time_of_day.rb +50 -24
  30. data/lib/openhab/core/dsl/timers.rb +2 -6
  31. data/lib/openhab/core/dsl/types/quantity.rb +106 -69
  32. data/lib/openhab/core/log.rb +3 -8
  33. data/lib/openhab/core/startup_delay.rb +1 -0
  34. data/lib/openhab/osgi.rb +7 -0
  35. data/lib/openhab/version.rb +1 -1
  36. metadata +12 -6
  37. data/lib/openhab/core/dsl/rule/item.rb +0 -208
  38. data/lib/openhab/core/dsl/rule/triggers.rb +0 -77
@@ -11,7 +11,7 @@ module OpenHAB
11
11
  #
12
12
  # Cron type rules
13
13
  #
14
- module Cron
14
+ module Triggers
15
15
  java_import org.openhab.core.automation.util.TriggerBuilder
16
16
  java_import org.openhab.core.config.core.Configuration
17
17
 
@@ -58,18 +58,12 @@ module OpenHAB
58
58
  #
59
59
  #
60
60
  def every(value, at: nil)
61
- case value
62
- when Symbol
63
- expression_map = EXPRESSION_MAP[value]
64
- expression_map = at_condition(expression_map, at) if at
65
- cron(map_to_cron(expression_map))
66
- when Java::JavaTime::Duration
67
- raise ArgumentError, '"at" cannot be used with duration' if at
68
-
69
- cron(map_to_cron(duration_to_map(value)))
70
- else
71
- raise ArgumentExpression, 'Unknown interval' unless expression_map
72
- end
61
+ cron_expression = case value
62
+ when Symbol then cron_from_symbol(value, at)
63
+ when Java::JavaTime::Duration then cron_from_duration(value, at)
64
+ else raise ArgumentExpression, 'Unknown interval'
65
+ end
66
+ cron(cron_expression)
73
67
  end
74
68
 
75
69
  #
@@ -83,6 +77,34 @@ module OpenHAB
83
77
 
84
78
  private
85
79
 
80
+ #
81
+ # Create a cron map from a duration
82
+ #
83
+ # @param [java::time::Duration] duration
84
+ # @param [Object] at TimeOfDay or String representing time of day
85
+ #
86
+ # @return [Hash] map describing cron expression
87
+ #
88
+ def cron_from_duration(duration, at)
89
+ raise ArgumentError, '"at" cannot be used with duration' if at
90
+
91
+ map_to_cron(duration_to_map(duration))
92
+ end
93
+
94
+ #
95
+ # Create a cron map from a symbol
96
+ #
97
+ # @param [Symbol] symbol
98
+ # @param [Object] at TimeOfDay or String representing time of day
99
+ #
100
+ # @return [Hash] map describing cron expression created from symbol
101
+ #
102
+ def cron_from_symbol(symbol, at)
103
+ expression_map = EXPRESSION_MAP[symbol]
104
+ expression_map = at_condition(expression_map, at) if at
105
+ map_to_cron(expression_map)
106
+ end
107
+
86
108
  #
87
109
  # Map cron expression to to cron string
88
110
  #
@@ -126,7 +148,7 @@ module OpenHAB
126
148
  #
127
149
  def at_condition(expression_map, at_time)
128
150
  if at_time
129
- tod = (at_time.is_a? TimeOfDay) ? at_time : TimeOfDay.parse(at_time)
151
+ tod = at_time.is_a?(TimeOfDay) ? at_time : TimeOfDay.parse(at_time)
130
152
  expression_map = expression_map.merge(hour: tod.hour, minute: tod.minute, second: tod.second)
131
153
  end
132
154
  expression_map
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'java'
5
+
6
+ module OpenHAB
7
+ module Core
8
+ module DSL
9
+ module Rule
10
+ #
11
+ # Module holds rule triggers
12
+ #
13
+ module Triggers
14
+ #
15
+ # Create a trigger for a thing
16
+ #
17
+ # @param [Thing] thing to create trigger for
18
+ # @param [Trigger] trigger to map with thing
19
+ # @param [State] to for thing
20
+ # @param [State] from state of thing
21
+ #
22
+ # @return [Array] Trigger and config for thing
23
+ #
24
+ def trigger_for_thing(thing, trigger, to = nil, from = nil)
25
+ config = { 'thingUID' => thing.uid.to_s }
26
+ config['status'] = trigger_state_from_symbol(to).to_s if to
27
+ config['previousStatus'] = trigger_state_from_symbol(from).to_s if from
28
+ [trigger, config]
29
+ end
30
+
31
+ #
32
+ # converts object to upcase string if its a symbol
33
+ #
34
+ # @param [sym] sym potential symbol to convert
35
+ #
36
+ # @return [String] Upcased symbol as string
37
+ #
38
+ def trigger_state_from_symbol(sym)
39
+ sym.to_s.upcase if (sym.is_a? Symbol) || sym
40
+ end
41
+
42
+ #
43
+ # Append a trigger to the list of triggeres
44
+ #
45
+ # @param [String] type of trigger to create
46
+ # @param [Map] config map describing trigger configuration
47
+ #
48
+ # @return [Trigger] OpenHAB trigger
49
+ #
50
+ def append_trigger(type, config)
51
+ logger.trace("Creating trigger of type #{type} for #{config}")
52
+ trigger = Trigger.trigger(type: type, config: config)
53
+ @triggers << trigger
54
+ trigger
55
+ end
56
+
57
+ #
58
+ # Class for creating and managing triggers
59
+ #
60
+ class Trigger
61
+ java_import org.openhab.core.automation.util.TriggerBuilder
62
+ java_import org.openhab.core.config.core.Configuration
63
+
64
+ # @return [String] A channel event trigger
65
+ CHANNEL_EVENT = 'core.ChannelEventTrigger'
66
+
67
+ # @return [String] A thing status Change trigger
68
+ THING_CHANGE = 'core.ThingStatusChangeTrigger'
69
+
70
+ # @return [String] A thing status update trigger
71
+ THING_UPDATE = 'core.ThingStatusUpdateTrigger'
72
+
73
+ # @return [String] An item command trigger
74
+ ITEM_COMMAND = 'core.ItemCommandTrigger'
75
+
76
+ # @return [String] An item state update trigger
77
+ ITEM_STATE_UPDATE = 'core.ItemStateUpdateTrigger'
78
+
79
+ # @return [String] An item state change trigger
80
+ ITEM_STATE_CHANGE = 'core.ItemStateChangeTrigger'
81
+
82
+ # @return [String] A group state change trigger for items in the group
83
+ GROUP_STATE_CHANGE = 'core.GroupStateChangeTrigger'
84
+
85
+ # @return [String] A group state update trigger for items in the group
86
+ GROUP_STATE_UPDATE = 'core.GroupStateUpdateTrigger'
87
+
88
+ # @return [String] A group command trigger for items in the group
89
+ GROUP_COMMAND = 'core.GroupCommandTrigger'
90
+
91
+ # @return [String] A time of day trigger
92
+ TIME_OF_DAY = 'timer.TimeOfDayTrigger'
93
+
94
+ # @return [String] A cron trigger
95
+ CRON = 'timer.GenericCronTrigger'
96
+
97
+ #
98
+ # Create a trigger
99
+ #
100
+ # @param [String] type of trigger
101
+ # @param [Map] config map
102
+ #
103
+ # @return [OpenHAB Trigger] configured by type and supplied config
104
+ #
105
+ def self.trigger(type:, config:)
106
+ TriggerBuilder.create
107
+ .with_id(uuid)
108
+ .with_type_uid(type)
109
+ .with_configuration(Configuration.new(config))
110
+ .build
111
+ end
112
+
113
+ #
114
+ # Generate a UUID for triggers
115
+ #
116
+ # @return [String] UUID
117
+ #
118
+ def self.uuid
119
+ SecureRandom.uuid
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,100 @@
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 when item, group or thing is updated
17
+ #
18
+ # @param [Array] items array to trigger on updated
19
+ # @param [State] to to match for tigger
20
+ #
21
+ # @return [Trigger] Trigger for updated entity
22
+ #
23
+ def updated(*items, to: nil)
24
+ items.flatten.each do |item|
25
+ logger.trace("Creating updated trigger for item(#{item}) to(#{to})")
26
+ [to].flatten.each do |to_state|
27
+ trigger, config = create_update_trigger(item, to_state)
28
+ append_trigger(trigger, config)
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ #
36
+ # Create a trigger for updates
37
+ #
38
+ # @param [Object] item Type of item [Group,Thing,Item] to create update trigger for
39
+ # @param [State] to_state state restriction on trigger
40
+ #
41
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
42
+ # second element is a Hash configuring trigger
43
+ #
44
+ def create_update_trigger(item, to_state)
45
+ case item
46
+ when GroupItems then group_update(item, to_state)
47
+ when Thing then thing_update(item, to_state)
48
+ else item_update(item, to_state)
49
+ end
50
+ end
51
+
52
+ #
53
+ # Create an update trigger for an item
54
+ #
55
+ # @param [Item] item to create trigger for
56
+ # @param [State] to_state optional state restriction for target
57
+ #
58
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
59
+ # second element is a Hash configuring trigger
60
+ #
61
+ def item_update(item, to_state)
62
+ config = { 'itemName' => item.name }
63
+ config['state'] = to_state.to_s unless to_state.nil?
64
+ trigger = Trigger::ITEM_STATE_UPDATE
65
+ [trigger, config]
66
+ end
67
+
68
+ #
69
+ # Create an update trigger for a group
70
+ #
71
+ # @param [Item] item to create trigger for
72
+ # @param [State] to_state optional state restriction for target
73
+ #
74
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
75
+ # second element is a Hash configuring trigger
76
+ #
77
+ def group_update(item, to_state)
78
+ config = { 'groupName' => item.group.name }
79
+ config['state'] = to_state.to_s unless to_state.nil?
80
+ trigger = Trigger::GROUP_STATE_UPDATE
81
+ [trigger, config]
82
+ end
83
+
84
+ #
85
+ # Create an update trigger for a thing
86
+ #
87
+ # @param [Thing] thing to create trigger for
88
+ # @param [State] to_state optional state restriction for target
89
+ #
90
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
91
+ # second element is a Hash configuring trigger
92
+ #
93
+ def thing_update(thing, to_state)
94
+ trigger_for_thing(thing, Trigger::THING_UPDATE, to_state)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -50,7 +50,8 @@ module OpenHAB
50
50
 
51
51
  # Constructs a TimeOfDay representing the time when called
52
52
  # @since 0.0.1
53
- # @param [String] string representation of TimeOfDay. Valid formats include "HH:MM:SS", "HH:MM", "H:MM", "HH", "H", "H:MM am"
53
+ # @param [String] string representation of TimeOfDay. Valid formats include "HH:MM:SS", "HH:MM",
54
+ # "H:MM", "HH", "H", "H:MM am"
54
55
  # @return [TimeOfDay] object created by parsing supplied string
55
56
  def self.parse(string)
56
57
  format = /(am|pm)$/i.match?(string) ? 'h[:mm[:ss]][ ]a' : 'H[:mm[:ss]]'
@@ -67,10 +68,13 @@ module OpenHAB
67
68
  # @option opts [Number] :m Minute of the day, defaults to 0
68
69
  # @option opts [Number] :s Second of the day, defaults to 0
69
70
  # @return [TimeOfDay] representing time when method was invoked
71
+ # rubocop: disable Naming/MethodParameterName
72
+ # This method has a better feel with short parameter names
70
73
  def initialize(h: 0, m: 0, s: 0)
71
74
  @local_time = LocalTime.of(h, m, s)
72
75
  freeze
73
76
  end
77
+ # rubocop: enable Naming/MethodParameterName
74
78
 
75
79
  # Returns true if the time falls within a range
76
80
  def between?(range)
@@ -100,14 +104,16 @@ module OpenHAB
100
104
 
101
105
  # Returns the string representation of the TimeOfDay
102
106
  # @since 0.0.1
103
- # @return [String] in any of the following formats depending on time representation HH:mm, HH:mm:ss, HH:mm:ss.SSS, HH:mm:ss.SSSSSS, HH:mm:ss.SSSSSSSSS
107
+ # @return [String] in any of the following formats depending on time representation HH:mm, HH:mm:ss,
108
+ # HH:mm:ss.SSS, HH:mm:ss.SSSSSS, HH:mm:ss.SSSSSSSSS
104
109
  def to_s
105
110
  @local_time.to_s
106
111
  end
107
112
 
108
113
  # Compares one TimeOfDay to another
109
114
  # @since 0.0.1
110
- # @return [Number, nil] -1,0,1 if other TimeOfDay is less than, equal to, or greater than this TimeOfDay or nil if an object other than TimeOfDay is provided
115
+ # @return [Number, nil] -1,0,1 if other TimeOfDay is less than, equal to, or greater than this TimeOfDay
116
+ # or nil if an object other than TimeOfDay is provided
111
117
  def <=>(other)
112
118
  case other
113
119
  when TimeOfDay
@@ -143,28 +149,36 @@ module OpenHAB
143
149
  # @since 2.4.0
144
150
  # @return [Number, nil] -1,0,1 if other is less than, equal to, or greater than this TimeOfDay
145
151
  def <=>(other)
146
- other_second_of_day = case other
147
- when TimeOfDay
148
- adjust_second_of_day(other.local_time.to_second_of_day)
149
- when String
150
- adjust_second_of_day(TimeOfDay.parse(other).local_time.to_second_of_day)
151
- when Time
152
- adjust_second_of_day(TimeOfDay.new(h: other.hour, m: other.min,
153
- s: other.sec).local_time.to_second_of_day)
154
- when TimeOfDayRangeElement
155
- other.sod
156
- else
157
- raise ArgumentError, 'Supplied argument cannot be converted into Time Of Day Object'
158
- end
159
-
152
+ other_second_of_day = to_second_of_day(other)
160
153
  logger.trace do
161
- "SOD(#{sod}) other SOD(#{other_second_of_day}) Other Class (#{other.class}) Result (#{sod <=> other_second_of_day})"
154
+ "SOD(#{sod}) "\
155
+ "other SOD(#{other_second_of_day}) "\
156
+ "Other Class (#{other.class}) "\
157
+ "Result (#{sod <=> other_second_of_day})"
162
158
  end
163
159
  sod <=> other_second_of_day
164
160
  end
165
161
 
166
162
  private
167
163
 
164
+ #
165
+ # Convert object to the seconds of a day they reprsent
166
+ #
167
+ # @param [Object] object TimeofDay,String,Time, or TimeOfDayRangeElement to convert
168
+ #
169
+ # @return [Integer] seconds of day represented by supplied object
170
+ #
171
+ def to_second_of_day(object)
172
+ case object
173
+ when TimeOfDay then adjust_second_of_day(object.local_time.to_second_of_day)
174
+ when String then adjust_second_of_day(TimeOfDay.parse(object).local_time.to_second_of_day)
175
+ when Time then adjust_second_of_day(TimeOfDay.new(h: object.hour, m: object.min,
176
+ s: object.sec).local_time.to_second_of_day)
177
+ when TimeOfDayRangeElement then object.sod
178
+ else raise ArgumentError, 'Supplied argument cannot be converted into Time Of Day Object'
179
+ end
180
+ end
181
+
168
182
  def adjust_second_of_day(second_of_day)
169
183
  second_of_day += NUM_SECONDS_IN_DAY if second_of_day < @range_begin
170
184
  second_of_day
@@ -175,14 +189,13 @@ module OpenHAB
175
189
  # to see if they are within the range
176
190
  # @since 2.4.0
177
191
  # @return Range object representing a TimeOfDay Range
192
+ module_function
193
+
178
194
  def between(range)
179
195
  raise ArgumentError, 'Supplied object must be a range' unless range.is_a? Range
180
196
 
181
- start = range.begin
182
- ending = range.end
183
-
184
- start = TimeOfDay.parse(start) if start.is_a? String
185
- ending = TimeOfDay.parse(ending) if ending.is_a? String
197
+ start = to_time_of_day(range.begin)
198
+ ending = to_time_of_day(range.end)
186
199
 
187
200
  start_sod = start.local_time.to_second_of_day
188
201
  ending_sod = ending.local_time.to_second_of_day
@@ -192,7 +205,20 @@ module OpenHAB
192
205
  ending_range = TimeOfDayRangeElement.new(sod: ending_sod, range_begin: start_sod)
193
206
  range.exclude_end? ? (start_range...ending_range) : (start_range..ending_range)
194
207
  end
195
- module_function :between
208
+
209
+ #
210
+ # Convert object to TimeOfDay object
211
+ #
212
+ # @param [Object] object TimeOfDay or String to be converted
213
+ #
214
+ # @return [TimeOfDay] TimeOfDay created from supplied object
215
+ #
216
+ private_class_method def to_time_of_day(object)
217
+ case object
218
+ when String then TimeOfDay.parse(object)
219
+ else object
220
+ end
221
+ end
196
222
 
197
223
  MIDNIGHT = TimeOfDay.midnight
198
224
  NOON = TimeOfDay.noon
@@ -39,15 +39,11 @@ module OpenHAB
39
39
  # occurs before the @timer variable can be set resulting in @timer being nil
40
40
  semaphore = Mutex.new
41
41
 
42
- @block = proc do
43
- semaphore.synchronize do
44
- block.call(self)
45
- end
46
- end
42
+ timer_block = proc { semaphore.synchronize { block.call(self) } }
47
43
 
48
44
  semaphore.synchronize do
49
45
  @timer = ScriptExecution.createTimer(
50
- ZonedDateTime.now.plus(@duration), @block
46
+ ZonedDateTime.now.plus(@duration), timer_block
51
47
  )
52
48
  super(@timer)
53
49
  end