openhab-scripting 4.30.3 → 4.30.4

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.
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
4
- require 'java'
5
- require_relative 'cron'
3
+ require 'forwardable'
6
4
 
7
5
  module OpenHAB
8
6
  module DSL
@@ -11,132 +9,66 @@ module OpenHAB
11
9
  # Module holds rule triggers
12
10
  #
13
11
  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, attach: nil)
51
- logger.trace("Creating trigger of type #{type} for #{config}")
52
- config.transform_keys!(&:to_s)
53
- trigger = Trigger.trigger(type: type, config: config)
54
- @attachments[trigger.id] = attach if attach
55
- @triggers << trigger
56
- trigger
57
- end
58
-
59
- #
60
- # Separates groups from items, and flattens any nested arrays of items
61
- #
62
- # @param [Array] item_array Array of items passed to a trigger
63
- #
64
- # @return [Array] A new flat array with any GroupMembers object left intact
65
- #
66
- def separate_groups(item_array)
67
- # we want to support anything that can be flattened... i.e. responds to to_ary
68
- # we want to be more lenient than only things that are currently Array,
69
- # but Enumerable is too lenient because Array#flatten won't traverse interior
70
- # Enumerables
71
- return item_array unless item_array.find { |item| item.respond_to?(:to_ary) }
72
-
73
- groups, items = item_array.partition { |item| item.is_a?(OpenHAB::DSL::Items::GroupItem::GroupMembers) }
74
- groups + separate_groups(items.flatten(1))
75
- end
76
-
77
12
  #
78
13
  # Class for creating and managing triggers
79
14
  #
80
15
  class Trigger
81
- java_import org.openhab.core.automation.util.TriggerBuilder
82
- java_import org.openhab.core.config.core.Configuration
83
-
84
- # @return [String] A channel event trigger
85
- CHANNEL_EVENT = 'core.ChannelEventTrigger'
86
-
87
- # @return [String] A thing status Change trigger
88
- THING_CHANGE = 'core.ThingStatusChangeTrigger'
89
-
90
- # @return [String] A thing status update trigger
91
- THING_UPDATE = 'core.ThingStatusUpdateTrigger'
92
-
93
- # @return [String] An item command trigger
94
- ITEM_COMMAND = 'core.ItemCommandTrigger'
95
-
96
- # @return [String] An item state update trigger
97
- ITEM_STATE_UPDATE = 'core.ItemStateUpdateTrigger'
98
-
99
- # @return [String] An item state change trigger
100
- ITEM_STATE_CHANGE = 'core.ItemStateChangeTrigger'
16
+ extend Forwardable
101
17
 
102
- # @return [String] A group state change trigger for items in the group
103
- GROUP_STATE_CHANGE = 'core.GroupStateChangeTrigger'
18
+ # Provide backwards compatibility for these fields
19
+ delegate :append_trigger => :@rule_triggers
104
20
 
105
- # @return [String] A group state update trigger for items in the group
106
- GROUP_STATE_UPDATE = 'core.GroupStateUpdateTrigger'
107
-
108
- # @return [String] A group command trigger for items in the group
109
- GROUP_COMMAND = 'core.GroupCommandTrigger'
110
-
111
- # @return [String] A time of day trigger
112
- TIME_OF_DAY = 'timer.TimeOfDayTrigger'
21
+ #
22
+ # Separates groups from items, and flattens any nested arrays of items
23
+ #
24
+ # @param [Array] item_array Array of items passed to a trigger
25
+ #
26
+ # @return [Array] A new flat array with any GroupMembers object left intact
27
+ #
28
+ def self.flatten_items(item_array)
29
+ # we want to support anything that can be flattened... i.e. responds to to_ary
30
+ # we want to be more lenient than only things that are currently Array,
31
+ # but Enumerable is too lenient because Array#flatten won't traverse interior
32
+ # Enumerables
33
+ return item_array unless item_array.find { |item| item.respond_to?(:to_ary) }
34
+
35
+ groups, items = item_array.partition { |item| item.is_a?(OpenHAB::DSL::Items::GroupItem::GroupMembers) }
36
+ groups + flatten_items(items.flatten(1))
37
+ end
113
38
 
114
- # @return [String] A cron trigger
115
- CRON = OpenHAB::DSL::Rules::Triggers::Cron::CRON_TRIGGER_MODULE_ID
39
+ #
40
+ # Creates a new Trigger
41
+ # @param [RuleTrigger] rule trigger information
42
+ def initialize(rule_triggers:)
43
+ @rule_triggers = rule_triggers
44
+ end
116
45
 
117
46
  #
118
- # Create a trigger
47
+ # Create a trigger for a thing
119
48
  #
120
- # @param [String] type of trigger
121
- # @param [Map] config map
49
+ # @param [Thing] thing to create trigger for
50
+ # @param [Trigger] trigger to map with thing
51
+ # @param [State] to for thing
52
+ # @param [State] from state of thing
122
53
  #
123
- # @return [OpenHAB Trigger] configured by type and supplied config
54
+ # @return [Array] Trigger and config for thing
124
55
  #
125
- def self.trigger(type:, config:)
126
- TriggerBuilder.create
127
- .with_id(uuid)
128
- .with_type_uid(type)
129
- .with_configuration(Configuration.new(config))
130
- .build
56
+ def trigger_for_thing(thing:, type:, to: nil, from: nil)
57
+ config = { 'thingUID' => thing.uid.to_s }
58
+ config['status'] = trigger_state_from_symbol(to).to_s if to
59
+ config['previousStatus'] = trigger_state_from_symbol(from).to_s if from
60
+ [type, config]
131
61
  end
132
62
 
133
63
  #
134
- # Generate a UUID for triggers
64
+ # converts object to upcase string if its a symbol
65
+ #
66
+ # @param [sym] sym potential symbol to convert
135
67
  #
136
- # @return [String] UUID
68
+ # @return [String] Upcased symbol as string
137
69
  #
138
- def self.uuid
139
- SecureRandom.uuid
70
+ def trigger_state_from_symbol(sym)
71
+ sym.to_s.upcase if (sym.is_a? Symbol) || sym
140
72
  end
141
73
  end
142
74
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'cron/cron'
4
+ require_relative 'cron/cron_handler'
5
+ require_relative 'changed'
6
+ require_relative 'channel'
7
+ require_relative 'command'
8
+ require_relative 'updated'
9
+ require_relative 'generic'
10
+ require_relative 'watch/watch'
11
+ require_relative 'watch/watch_handler'
@@ -21,119 +21,133 @@ module OpenHAB
21
21
  # @return [Trigger] Trigger for updated entity
22
22
  #
23
23
  def updated(*items, to: nil, attach: nil)
24
- separate_groups(items).map do |item|
24
+ updated = Updated.new(rule_triggers: @rule_triggers)
25
+ Updated.flatten_items(items).map do |item|
25
26
  logger.trace("Creating updated trigger for item(#{item}) to(#{to})")
26
27
  [to].flatten.map do |to_state|
27
- update_trigger(item: item, to: to_state, attach: attach)
28
+ updated.trigger(item: item, to: to_state, attach: attach)
28
29
  end
29
30
  end.flatten
30
31
  end
31
32
 
32
- private
33
-
34
- #
35
- # Create the trigger
36
- #
37
- # @param [Object] item item to create trigger for
38
- # @param [Item State] from state to restrict trigger to
39
- # @param [Item State] to state to restrict trigger to
40
- # @param attach attachment
41
33
  #
42
- # @return [Trigger] OpenHAB triggers
34
+ # Creates updated triggers
43
35
  #
44
- def update_trigger(item:, to:, attach:)
45
- case to
46
- when Range then create_update_range_trigger(item: item, to: to, attach: attach)
47
- when Proc then create_update_proc_trigger(item: item, to: to, attach: attach)
48
- else create_update_trigger(item: item, to: to, attach: attach)
36
+ class Updated < Trigger
37
+ include OpenHAB::Log
38
+
39
+ #
40
+ # Create the trigger
41
+ #
42
+ # @param [Object] item item to create trigger for
43
+ # @param [Item State] from state to restrict trigger to
44
+ # @param [Item State] to state to restrict trigger to
45
+ # @param attach attachment
46
+ #
47
+ # @return [Trigger] OpenHAB triggers
48
+ #
49
+ def trigger(item:, to:, attach:)
50
+ case to
51
+ when Range then range_trigger(item: item, to: to, attach: attach)
52
+ when Proc then proc_trigger(item: item, to: to, attach: attach)
53
+ else update_trigger(item: item, to: to, attach: attach)
54
+ end
49
55
  end
50
- end
51
56
 
52
- #
53
- # Creates a trigger with a range condition on the 'to' field
54
- # @param [Object] item to create changed trigger on
55
- # @param [Object] to state restrict trigger to
56
- # @param [Object] attach to trigger
57
- # @return [Trigger] OpenHAB trigger
58
- #
59
- def create_update_range_trigger(item:, to:, attach:)
60
- to, * = Conditions::Proc.range_procs(to)
61
- create_update_proc_trigger(item: item, to: to, attach: attach)
62
- end
57
+ private
63
58
 
64
- #
65
- # Creates a trigger with a proc condition on the 'to' field
66
- # @param [Object] item to create changed trigger on
67
- # @param [Object] to state restrict trigger to
68
- # @param [Object] attach to trigger
69
- # @return [Trigger] OpenHAB trigger
70
- #
71
- def create_update_proc_trigger(item:, to:, attach:)
72
- create_update_trigger(item: item, to: nil, attach: attach).tap do |trigger|
73
- @trigger_conditions[trigger.id] = Conditions::Proc.new(to: to)
59
+ # @return [String] A thing status update trigger
60
+ THING_UPDATE = 'core.ThingStatusUpdateTrigger'
61
+
62
+ # @return [String] An item state update trigger
63
+ ITEM_STATE_UPDATE = 'core.ItemStateUpdateTrigger'
64
+
65
+ # @return [String] A group state update trigger for items in the group
66
+ GROUP_STATE_UPDATE = 'core.GroupStateUpdateTrigger'
67
+
68
+ #
69
+ # Creates a trigger with a range condition on the 'to' field
70
+ # @param [Object] item to create changed trigger on
71
+ # @param [Object] to state restrict trigger to
72
+ # @param [Object] attach to trigger
73
+ # @return [Trigger] OpenHAB trigger
74
+ #
75
+ def range_trigger(item:, to:, attach:)
76
+ to, * = Conditions::Proc.range_procs(to)
77
+ proc_trigger(item: item, to: to, attach: attach)
74
78
  end
75
- end
76
79
 
77
- #
78
- # Create a trigger for updates
79
- #
80
- # @param [Object] item Type of item [Group,Thing,Item] to create update trigger for
81
- # @param [State] to_state state restriction on trigger
82
- #
83
- # @return [Trigger] OpenHAB triggers
84
- #
85
- def create_update_trigger(item:, to:, attach:)
86
- trigger, config = case item
87
- when OpenHAB::DSL::Items::GroupItem::GroupMembers then group_update(item: item, to: to)
88
- when Thing then thing_update(thing: item, to: to)
89
- else item_update(item: item, to: to)
90
- end
91
- append_trigger(trigger, config, attach: attach)
92
- end
80
+ #
81
+ # Creates a trigger with a proc condition on the 'to' field
82
+ # @param [Object] item to create changed trigger on
83
+ # @param [Object] to state restrict trigger to
84
+ # @param [Object] attach to trigger
85
+ # @return [Trigger] OpenHAB trigger
86
+ #
87
+ def proc_trigger(item:, to:, attach:)
88
+ conditions = Conditions::Proc.new(to: to)
89
+ update_trigger(item: item, to: nil, attach: attach, conditions: conditions)
90
+ end
93
91
 
94
- #
95
- # Create an update trigger for an item
96
- #
97
- # @param [Item] item to create trigger for
98
- # @param [State] to_state optional state restriction for target
99
- #
100
- # @return [Array<Hash,String>] first element is a String specifying trigger type
101
- # second element is a Hash configuring trigger
102
- #
103
- def item_update(item:, to:)
104
- config = { 'itemName' => item.name }
105
- config['state'] = to.to_s unless to.nil?
106
- trigger = Trigger::ITEM_STATE_UPDATE
107
- [trigger, config]
108
- end
92
+ #
93
+ # Create a trigger for updates
94
+ #
95
+ # @param [Object] item Type of item [Group,Thing,Item] to create update trigger for
96
+ # @param [State] to_state state restriction on trigger
97
+ #
98
+ # @return [Trigger] OpenHAB triggers
99
+ #
100
+ def update_trigger(item:, to:, attach: nil, conditions: nil)
101
+ type, config = case item
102
+ when OpenHAB::DSL::Items::GroupItem::GroupMembers then group_update(item: item, to: to)
103
+ when Thing then thing_update(thing: item, to: to)
104
+ else item_update(item: item, to: to)
105
+ end
106
+ append_trigger(type: type, config: config, attach: attach, conditions: conditions)
107
+ end
109
108
 
110
- #
111
- # Create an update trigger for a group
112
- #
113
- # @param [Item] item to create trigger for
114
- # @param [State] to_state optional state restriction for target
115
- #
116
- # @return [Array<Hash,String>] first element is a String specifying trigger type
117
- # second element is a Hash configuring trigger
118
- #
119
- def group_update(item:, to:)
120
- config = { 'groupName' => item.group.name }
121
- config['state'] = to.to_s unless to.nil?
122
- trigger = Trigger::GROUP_STATE_UPDATE
123
- [trigger, config]
124
- end
109
+ #
110
+ # Create an update trigger for an item
111
+ #
112
+ # @param [Item] item to create trigger for
113
+ # @param [State] to_state optional state restriction for target
114
+ #
115
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
116
+ # second element is a Hash configuring trigger
117
+ #
118
+ def item_update(item:, to:)
119
+ config = { 'itemName' => item.name }
120
+ config['state'] = to.to_s unless to.nil?
121
+ [ITEM_STATE_UPDATE, config]
122
+ end
125
123
 
126
- #
127
- # Create an update trigger for a thing
128
- #
129
- # @param [Thing] thing to create trigger for
130
- # @param [State] to_state optional state restriction for target
131
- #
132
- # @return [Array<Hash,String>] first element is a String specifying trigger type
133
- # second element is a Hash configuring trigger
134
- #
135
- def thing_update(thing:, to:)
136
- trigger_for_thing(thing, Trigger::THING_UPDATE, to)
124
+ #
125
+ # Create an update trigger for a group
126
+ #
127
+ # @param [Item] item to create trigger for
128
+ # @param [State] to_state optional state restriction for target
129
+ #
130
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
131
+ # second element is a Hash configuring trigger
132
+ #
133
+ def group_update(item:, to:)
134
+ config = { 'groupName' => item.group.name }
135
+ config['state'] = to.to_s unless to.nil?
136
+ [GROUP_STATE_UPDATE, config]
137
+ end
138
+
139
+ #
140
+ # Create an update trigger for a thing
141
+ #
142
+ # @param [Thing] thing to create trigger for
143
+ # @param [State] to_state optional state restriction for target
144
+ #
145
+ # @return [Array<Hash,String>] first element is a String specifying trigger type
146
+ # second element is a Hash configuring trigger
147
+ #
148
+ def thing_update(thing:, to:)
149
+ trigger_for_thing(thing: thing, type: THING_UPDATE, to: to)
150
+ end
137
151
  end
138
152
  end
139
153
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openhab/log/logger'
4
+ require 'openhab/dsl/rules/triggers/trigger'
5
+
6
+ module OpenHAB
7
+ module DSL
8
+ module Rules
9
+ #
10
+ # Module holds rule triggers
11
+ #
12
+ module Triggers
13
+ #
14
+ # Module for watching directories/files
15
+ #
16
+
17
+ #
18
+ # Create a trigger to watch a path
19
+ #
20
+ # @param [String] path to watch
21
+ #
22
+ # @return [Trigger] Trigger object
23
+ #
24
+ def watch(path, glob: '*', for: %i[created deleted modified], attach: nil)
25
+ glob, path = Watch.glob_for_path(Pathname.new(path), glob)
26
+ types = [binding.local_variable_get(:for)].flatten
27
+ config = { path: path.to_s, types: types.map(&:to_s), glob: glob.to_s }
28
+
29
+ logger.state 'Creating a watch trigger', path: path, glob: glob, types: types
30
+ Watch.new(rule_triggers: @rule_triggers).trigger(config: config, attach: attach)
31
+ end
32
+
33
+ #
34
+ # Creates watch triggers
35
+ #
36
+ class Watch < Trigger
37
+ # Characters in an fnmatch compatible glob
38
+ GLOB_CHARS = ['**', '*', '?', '[', ']', '{', '}'].freeze
39
+ private_constant :GLOB_CHARS
40
+
41
+ #
42
+ # Automatically creates globs for supplied paths if necessary
43
+ # @param [Pathname] path to check
44
+ # @param [String] specified glob
45
+ #
46
+ # @return [Pathname,String] Pathname to watch and glob to match
47
+ def self.glob_for_path(path, glob)
48
+ # Checks if the supplied pathname last element contains a glob char
49
+ if GLOB_CHARS.any? { |char| path.basename.to_s.include? char }
50
+ # Splits the supplied pathname into a glob string and parent path
51
+ [path.basename.to_s, path.parent]
52
+ elsif path.file? || !path.exist?
53
+ # glob string matching end of Pathname and parent path
54
+ ["*/#{path.basename}", path.parent]
55
+ else
56
+ [glob, path]
57
+ end
58
+ end
59
+
60
+ #
61
+ # Create a watch trigger based on item type
62
+ #
63
+ # @param [Array] commands to create trigger for
64
+ # @param [Object] item to create trigger for
65
+ #
66
+ #
67
+ def trigger(config:, attach:)
68
+ append_trigger(type: WatchHandler::WATCH_TRIGGER_MODULE_ID,
69
+ config: config,
70
+ attach: attach)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -14,32 +14,15 @@ module OpenHAB
14
14
  #
15
15
  # Module for watching directories/files
16
16
  #
17
- module Watch
17
+ module WatchHandler
18
18
  include OpenHAB::Log
19
19
 
20
- # Characters in an fnmatch compatible glob
21
- GLOB_CHARS = ['**', '*', '?', '[', ']', '{', '}'].freeze
22
-
23
- #
24
- # Creates trigger types and trigger type factories for OpenHAB
25
- #
26
- def self.add_watch_handler
27
- java_import org.openhab.core.automation.type.TriggerType
28
- OpenHAB::Core.automation_manager.add_trigger_handler(
29
- OpenHAB::DSL::Rules::Triggers::Watch::WATCH_TRIGGER_MODULE_ID,
30
- OpenHAB::DSL::Rules::Triggers::Watch::WatchTriggerHandlerFactory.new
31
- )
32
-
33
- OpenHAB::Core.automation_manager.add_trigger_type(watch_trigger_type)
34
- OpenHAB::Log.logger(self).trace('Added watch trigger handler')
35
- end
36
-
37
20
  #
38
21
  # Creates trigger types and trigger type factories for OpenHAB
39
22
  #
40
23
  private_class_method def self.watch_trigger_type
41
24
  TriggerType.new(
42
- OpenHAB::DSL::Rules::Triggers::Watch::WATCH_TRIGGER_MODULE_ID,
25
+ WATCH_TRIGGER_MODULE_ID,
43
26
  nil,
44
27
  'A path change event is detected',
45
28
  'Triggers when a path change event is detected',
@@ -163,44 +146,19 @@ module OpenHAB
163
146
  WatchTriggerHandler.new(trigger)
164
147
  end
165
148
  end
166
- end
167
149
 
168
- #
169
- # Create a trigger to watch a path
170
- #
171
- # @param [String] path to watch
172
- #
173
- # @return [Trigger] Trigger object
174
- #
175
- def watch(path, glob: '*', for: %i[created deleted modified], attach: nil)
176
- glob, path = glob_for_path(Pathname.new(path), glob)
177
- types = [binding.local_variable_get(:for)].flatten
178
- conf = { path: path.to_s, types: types.map(&:to_s), glob: glob.to_s }
179
-
180
- logger.trace("Creating a watch trigger for path(#{path}) with glob(#{glob}) for types(#{types})")
181
- append_trigger(OpenHAB::DSL::Rules::Triggers::Watch::WATCH_TRIGGER_MODULE_ID,
182
- conf,
183
- attach: attach)
184
- end
185
-
186
- private
150
+ #
151
+ # Creates trigger types and trigger type factories for OpenHAB
152
+ #
153
+ def self.add_watch_handler
154
+ java_import org.openhab.core.automation.type.TriggerType
155
+ OpenHAB::Core.automation_manager.add_trigger_handler(
156
+ WATCH_TRIGGER_MODULE_ID,
157
+ WatchTriggerHandlerFactory.new
158
+ )
187
159
 
188
- #
189
- # Automatically creates globs for supplied paths if necessary
190
- # @param [Pathname] path to check
191
- # @param [String] specified glob
192
- #
193
- # @return [Pathname,String] Pathname to watch and glob to match
194
- def glob_for_path(path, glob)
195
- # Checks if the supplied pathname last element contains a glob char
196
- if OpenHAB::DSL::Rules::Triggers::Watch::GLOB_CHARS.any? { |char| path.basename.to_s.include? char }
197
- # Splits the supplied pathname into a glob string and parent path
198
- [path.basename.to_s, path.parent]
199
- elsif path.file? || !path.exist?
200
- # glob string matching end of Pathname and parent path
201
- ["*/#{path.basename}", path.parent]
202
- else
203
- [glob, path]
160
+ OpenHAB::Core.automation_manager.add_trigger_type(watch_trigger_type)
161
+ OpenHAB::Log.logger(self).trace('Added watch trigger handler')
204
162
  end
205
163
  end
206
164
  end
@@ -208,4 +166,4 @@ module OpenHAB
208
166
  end
209
167
  end
210
168
  # Add the watch handler to OpenHAB
211
- OpenHAB::DSL::Rules::Triggers::Watch.add_watch_handler
169
+ OpenHAB::DSL::Rules::Triggers::WatchHandler.add_watch_handler
@@ -61,6 +61,22 @@ module OpenHAB
61
61
  define_method("#{level}_enabled?") { @sl4fj_logger.send("is_#{level}_enabled") }
62
62
  end
63
63
 
64
+ #
65
+ # Logs a map of key(value) with an optional preamble at trace level
66
+ # @param [String] preamble to put at start of log message
67
+ # @param [Hash] key and values to log
68
+ def state(preamble = 'State:', **kwargs)
69
+ return unless trace_enabled?
70
+
71
+ states = kwargs.transform_keys(&:to_s)
72
+ .transform_keys(&:capitalize)
73
+ .transform_values { |v| v.nil? ? 'nil' : v }
74
+ .map { |k, v| "#{k}(#{v})" }
75
+ .join(' ')
76
+ trace "#{preamble} #{states}"
77
+ end
78
+ alias states state
79
+
64
80
  #
65
81
  # Cleans the backtrace of an error to remove internal calls. If logging is set
66
82
  # to debug or lower, the full backtrace is kept
@@ -5,5 +5,5 @@
5
5
  #
6
6
  module OpenHAB
7
7
  # @return [String] Version of OpenHAB helper libraries
8
- VERSION = '4.30.3'
8
+ VERSION = '4.30.4'
9
9
  end