aws_iot_device 0.1.5 → 1.0.0

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/README.md +155 -148
  4. data/aws_iot_device.gemspec +11 -11
  5. data/codeclimate.yml +6 -0
  6. data/lib/aws_iot_device.rb +2 -1
  7. data/lib/aws_iot_device/mqtt_adapter.rb +1 -2
  8. data/lib/aws_iot_device/mqtt_adapter/client.rb +74 -6
  9. data/lib/aws_iot_device/mqtt_adapter/paho_mqtt_adapter.rb +207 -0
  10. data/lib/aws_iot_device/mqtt_adapter/ruby_mqtt_adapter.rb +10 -4
  11. data/lib/aws_iot_device/mqtt_shadow_client.rb +1 -0
  12. data/lib/aws_iot_device/mqtt_shadow_client/mqtt_manager.rb +133 -67
  13. data/lib/aws_iot_device/mqtt_shadow_client/shadow_action_manager.rb +229 -146
  14. data/lib/aws_iot_device/mqtt_shadow_client/shadow_client.rb +78 -20
  15. data/lib/aws_iot_device/mqtt_shadow_client/shadow_topic_manager.rb +51 -26
  16. data/lib/aws_iot_device/mqtt_shadow_client/topic_builder.rb +15 -30
  17. data/lib/aws_iot_device/version.rb +1 -1
  18. data/samples/config_shadow.rb +72 -0
  19. data/samples/mqtt_client_samples/mqtt_client_samples.rb +7 -59
  20. data/samples/shadow_action_samples/sample_shadow_action_update.rb +7 -61
  21. data/samples/shadow_client_samples/samples_shadow_client_block.rb +25 -0
  22. data/samples/shadow_client_samples/samples_shadow_client_delete.rb +9 -58
  23. data/samples/shadow_client_samples/samples_shadow_client_description.rb +64 -0
  24. data/samples/shadow_client_samples/samples_shadow_client_get.rb +11 -62
  25. data/samples/shadow_client_samples/samples_shadow_client_getting_started.rb +22 -0
  26. data/samples/shadow_client_samples/samples_shadow_client_update.rb +7 -58
  27. data/samples/shadow_topic_samples/sample_topic_manager.rb +10 -61
  28. metadata +71 -16
  29. data/lib/aws_iot_device/mqtt_adapter/mqtt_adapter.rb +0 -139
@@ -9,19 +9,21 @@ module AwsIotDevice
9
9
  module MqttShadowClient
10
10
  class ShadowActionManager
11
11
  ### This the main AWS action manager
12
- ### It allows to execute the AWS actions (get, update, delete)
13
- ### It allows to manage the time out after an action have been start
14
- ### Actions request are send on the general actions topic and answer is retreived from accepted/refused/delta topics
12
+ ### It enables the AWS IoT actions (get, update, delete)
13
+ ### It enables the time control the time out after an action have been start
14
+ ### Actions requests are send on the general actions topic and answer is retreived from accepted/refused/delta topics
15
15
 
16
- def initialize(shadow_name, shadow_topic_manager, persistent_subscribe=false)
16
+ attr_accessor :logger
17
+
18
+ def initialize(shadow_name, mqtt_client, persistent_subscribe=false)
17
19
  @shadow_name = shadow_name
18
- @topic_manager = shadow_topic_manager
20
+ @topic_manager = ShadowTopicManager.new(mqtt_client, shadow_name)
19
21
  @payload_parser = JSONPayloadParser.new
20
22
  @is_subscribed = {}
21
23
  @is_subscribed[:get] = false
22
24
  @is_subscribed[:update] = false
23
25
  @is_subscribed[:delete] = false
24
- @token_handler = TokenCreator.new(shadow_name, shadow_topic_manager.client_id)
26
+ @token_handler = TokenCreator.new(shadow_name, mqtt_client.client_id)
25
27
  @persistent_subscribe = persistent_subscribe
26
28
  @last_stable_version = -1 #Mean no currentely stable
27
29
  @topic_subscribed_callback = {}
@@ -33,78 +35,11 @@ module AwsIotDevice
33
35
  @topic_subscribed_task_count[:update] = 0
34
36
  @topic_subscribed_task_count[:delete] = 0
35
37
  @token_pool = {}
36
- @general_action_mutex = Mutex.new
37
- @default_callback = Proc.new do |message|
38
- do_default_callback(message)
39
- end
40
- end
41
-
42
- ### The default callback that is called by every actions
43
- ### It acknowledge the accepted status if action success
44
- ### Call a specific callback for each actions if it defined have been register previously
45
- def do_default_callback(message)
46
- @general_action_mutex.synchronize(){
47
- topic = message.topic
48
- action = parse_action(topic)
49
- type = parse_type(topic)
50
- payload = message.payload
51
- @payload_parser.set_message(payload)
52
- if %w(get update delete).include?(action)
53
- token = @payload_parser.get_attribute_value("clientToken")
54
- if @token_pool.has_key?(token)
55
- if type.eql?("accepted")
56
- new_version = @payload_parser.get_attribute_value("version")
57
- if new_version && new_version >= @last_stable_version
58
- type.eql?("delete") ? @last_stable_version = -1 : @last_stable_version = new_version
59
- Thread.new { @topic_subscribed_callback[action.to_sym].call(message) } unless @topic_subscribed_callback[action.to_sym].nil?
60
- else
61
- puts "CATCH AN UPDATE BUT OUTDATED/INVALID VERSION (= #{new_version}) FOR TOKEN #{token}\n"
62
- end
63
- end
64
- @token_pool[token].cancel
65
- @token_pool.delete(token)
66
- @topic_subscribed_task_count[action.to_sym] -= 1
67
- if @topic_subscribed_task_count[action.to_sym] <= 0
68
- @topic_subscribed_task_count[action.to_sym] = 0
69
- unless @persistent_subscribe
70
- @topic_manager.shadow_topic_unsubscribe(@shadow_name, action)
71
- @is_subscribed[action.to_sym] = false
72
- end
73
- end
74
- end
75
- elsif %w(delta).include?(action)
76
- new_version = @payload_parser.get_attribute_value("version")
77
- if new_version && new_version >= @last_stable_version
78
- @last_stable_version = new_version
79
- Thread.new { @topic_subscribed_callback[action.to_sym].call(message) } if @topic_subscribed_callback[action.to_sym]
80
- else
81
- puts "CATCH A DELTA BUT OUTDATED/INVALID VERSION (= #{new_version})\n"
82
- end
83
- end
84
- }
85
- end
86
-
87
- ### Should cancel the token after a preset time interval
88
- def timeout_manager(action_name, token)
89
- @general_action_mutex.synchronize(){
90
- if @token_pool.has_key?(token)
91
- action = action_name.to_sym
92
- @token_pool.delete(token)
93
- puts "The #{action_name} request with the token #{token} has timed out!\n"
94
- @topic_subscribed_task_count[action] -= 1
95
- unless @topic_subscribed_task_count[action] <= 0
96
- @topic_subscribed_task_count[action] = 0
97
- unless @persistent_subscribe
98
- @topic_manager.shadow_topic_unsubscribe(@shadow_name, action)
99
- @is_subscribed[action.to_sym] = false
100
- end
101
- end
102
- unless @topic_subscribed_callback[action].blank?
103
- puts "Shadow request with token: #{token} has timed out."
104
- @topic_subscribed_callback[action].call("REQUEST TIME OUT", "timeout", token)
105
- end
106
- end
107
- }
38
+ @token_callback = {}
39
+ @task_count_mutex = Mutex.new
40
+ @token_mutex = Mutex.new
41
+ @parser_mutex = Mutex.new
42
+ set_basic_callback
108
43
  end
109
44
 
110
45
  ### Send and publish packet with an empty payload contains in a valid JSON format.
@@ -128,92 +63,240 @@ module AwsIotDevice
128
63
  ### Returns :
129
64
  ### > the token associate to the current action (which also store in @token_pool)
130
65
 
131
- def shadow_get(callback=nil, timeout=5)
132
- current_token = ""
133
- json_payload = ""
134
- timer = Timers::Group.new
135
- @general_action_mutex.synchronize(){
136
- @topic_subscribed_callback[:get] = callback
137
- @topic_subscribed_task_count[:get] += 1
138
- current_token = @token_handler.create_next_token
139
- timer.after(timeout){ timeout_manager(:get, current_token) }
140
- @payload_parser.set_attribute_value("clientToken",current_token)
141
- json_payload = @payload_parser.get_json
142
- unless @is_subscribed[:get]
143
- @topic_manager.shadow_topic_subscribe(@shadow_name, "get", @default_callback)
144
- @is_subscribed[:get] = true
145
- end
146
- @topic_manager.shadow_topic_publish(@shadow_name, "get", json_payload)
147
- @token_pool[current_token] = timer
148
- Thread.new{ timer.wait }
149
- current_token
150
- }
66
+ def shadow_get(timeout=5, callback=nil, &block)
67
+ shadow_action(:get, "", timeout, callback, &block)
68
+ end
69
+
70
+ def shadow_update(payload, timeout=5, callback=nil, &block)
71
+ shadow_action(:update, payload, timeout, callback, &block)
72
+ end
73
+
74
+ def shadow_delete(timeout=5, callback=nil, &block)
75
+ shadow_action(:delete, "", timeout, callback, &block)
76
+ end
77
+
78
+ def register_get_callback(callback, &block)
79
+ register_action_callback(:get, callback, &block)
80
+ end
81
+
82
+ def register_update_callback(callback, &block)
83
+ register_action_callback(:update, callback, &block)
84
+ end
85
+
86
+ def register_delete_callback(callback, &block)
87
+ register_action_callback(:delete, callback, &block)
88
+ end
89
+
90
+ def register_shadow_delta_callback(callback, &block)
91
+ if callback.is_a?(Proc)
92
+ @topic_subscribed_callback[:delta] = callback
93
+ elsif block_given?
94
+ @topic_subscribed_callback[:delta] = block
95
+ end
96
+ @topic_manager.shadow_topic_subscribe("delta", @default_callback)
97
+ end
98
+
99
+ def remove_get_callback
100
+ remove_action_callback(:get)
101
+ end
102
+
103
+ def remove_update_callback
104
+ remove_action_callback(:update)
105
+ end
106
+
107
+ def remove_delete_callback
108
+ remove_action_callback(:delete)
151
109
  end
152
110
 
153
- def shadow_update(payload, callback, timeout)
111
+ def remove_shadow_delta_callback
112
+ @topic_subscribe_callback.delete[:delta]
113
+ @topic_manager.shadow_topic_unsubscribe("delta")
114
+ end
115
+
116
+ def logger?
117
+ !@logger.nil? && @logger.is_a?(Logger)
118
+ end
119
+
120
+ private
121
+
122
+ def shadow_action(action, payload="", timeout=5, callback=nil, &block)
154
123
  current_token = Symbol
155
124
  timer = Timers::Group.new
156
125
  json_payload = ""
157
- @general_action_mutex.synchronize(){
158
- if callback.is_a?(Proc)
159
- @topic_subscribed_callback[:update] = callback
160
- end
161
- @topic_subscribed_task_count[:update] += 1
126
+ @token_mutex.synchronize(){
162
127
  current_token = @token_handler.create_next_token
163
- timer.after(timeout){ timeout_manager(:update, current_token) }
164
- @payload_parser.set_message(payload)
165
- @payload_parser.set_attribute_value("clientToken",current_token)
128
+ }
129
+ timer.after(timeout){ timeout_manager(action, current_token) }
130
+ @parser_mutex.synchronize {
131
+ @payload_parser.set_message(payload) unless payload == ""
132
+ @payload_parser.set_attribute_value("clientToken", current_token)
166
133
  json_payload = @payload_parser.get_json
167
- unless @is_subscribed[:update]
168
- @topic_manager.shadow_topic_subscribe(@shadow_name, "update", @default_callback)
169
- @is_subscribed[:update] = true
170
- end
171
- @topic_manager.shadow_topic_publish(@shadow_name, "update", json_payload)
172
- @token_pool[current_token] = timer
173
- Thread.new{ timer.wait }
174
- current_token
175
134
  }
135
+ handle_subscription(action, timeout) unless @is_subscribed[action]
136
+ @topic_manager.shadow_topic_publish(action.to_s, json_payload)
137
+ @task_count_mutex.synchronize {
138
+ @topic_subscribed_task_count[action] += 1
139
+ }
140
+ @token_pool[current_token] = timer
141
+ register_token_callback(current_token, callback, &block)
142
+ Thread.new{ timer.wait }
143
+ current_token
176
144
  end
177
145
 
178
- def shadow_delete(callback, timeout)
179
- current_token = Symbol
180
- timer = Timers::Group.new
181
- json_payload = ""
182
- @general_action_mutex.synchronize(){
183
- if callback.is_a?(Proc)
184
- @topic_subscribed_callback[:delete] = callback
185
- end
186
- @topic_subscribed_task_count[:delete] += 1
187
- current_token = @token_handler.create_next_token
188
- timer.after(timeout){ timeout_manager(:delete, current_token) }
189
- @payload_parser.set_attribute_value("clientToken",current_token)
190
- json_payload = @payload_parser.get_json
191
- unless @is_subscribed[:delete]
192
- @topic_manager.shadow_topic_subscribe(@shadow_name, "delete", @default_callback)
193
- @is_subscribed[:delete] = true
146
+ ### Should cancel the token after a preset time interval
147
+ def timeout_manager(action_name, token)
148
+ if @token_pool.has_key?(token)
149
+ action = action_name.to_sym
150
+ @token_pool.delete(token)
151
+ @token_callback.delete(token)
152
+ @logger.warn("The #{action_name} request with the token #{token} has timed out!\n") if logger?
153
+ @task_count_mutex.synchronize {
154
+ @topic_subscribed_task_count[action] -= 1
155
+ unless @topic_subscribed_task_count[action] <= 0
156
+ @topic_subscribed_task_count[action] = 0
157
+ unless @persistent_subscribe
158
+ @topic_manager.shadow_topic_unsubscribe(action)
159
+ @is_subscribed[action.to_sym] = false
160
+ end
161
+ end
162
+ }
163
+ end
164
+ end
165
+
166
+ def set_basic_callback
167
+ @default_callback = proc { |message| do_message_callback(message) }
168
+
169
+ @topic_manager.on_suback = lambda do |topics|
170
+ action = @topic_manager.retrieve_action(topics[0])
171
+ @is_subscribed[action] ||= true unless action.nil?
172
+ end
173
+
174
+ @topic_manager.on_unsuback = lambda do |topics|
175
+ action = @topic_manager.retrieve_action(topics[0])
176
+ @is_subscribed[action] = false if action.nil?
177
+ end
178
+ end
179
+
180
+ def register_token_callback(token, callback, &block)
181
+ if callback.is_a?(Proc)
182
+ @token_callback[token] = callback
183
+ elsif block_given?
184
+ @token_callback[token] = block
185
+ end
186
+ end
187
+
188
+ def remove_token_callback(token)
189
+ @token_callback.delete(token)
190
+ end
191
+
192
+ def register_action_callback(action, callback, &block)
193
+ if callback.is_a?(Proc)
194
+ @topic_subscribed_callback[action] = callback
195
+ elsif block_given?
196
+ @topic_subscribed_callback[action] = block
197
+ end
198
+ end
199
+
200
+ def remove_action_callback(action)
201
+ @topic_subscribed_callback[action] = nil
202
+ end
203
+
204
+ def decresase_task_count(action)
205
+ @topic_subscribed_task_count[action] -= 1
206
+ if @topic_subscribed_task_count[action] <= 0
207
+ @topic_subscribed_task_count[action] = 0
208
+ unless @persistent_subscribe
209
+ @topic_manager.shadow_topic_unsubscribe(action.to_s)
210
+ @is_subscribed[action] = false
194
211
  end
195
- @topic_manager.shadow_topic_publish(@shadow_name, "delete", json_payload)
196
- @token_pool[current_token] = timer
197
- Thread.new{ timer.wait }
198
- current_token
199
- }
212
+ end
200
213
  end
201
214
 
202
- def register_shadow_delta_callback(callback)
203
- @general_action_mutex.synchronize(){
204
- @topic_subscribed_callback[:delta] = callback
205
- @topic_manager.shadow_topic_subscribe(@shadow_name, "delta", @default_callback)
215
+ ### The default callback that is called by every actions
216
+ ### It acknowledge the accepted status if action success
217
+ ### Call a specific callback for each actions if it defined have been register previously
218
+ def do_message_callback(message)
219
+ topic = message.topic
220
+ action = parse_action(topic)
221
+ type = parse_type(topic)
222
+ payload = message.payload
223
+ token = nil
224
+ new_version = -1
225
+ @parser_mutex.synchronize() {
226
+ @payload_parser.set_message(payload)
227
+ new_version = @payload_parser.get_attribute_value("version")
228
+ token = @payload_parser.get_attribute_value("clientToken")
206
229
  }
230
+ if %w(get update delete).include?(action)
231
+ if @token_pool.has_key?(token)
232
+ @token_pool[token].cancel
233
+ @token_pool.delete(token)
234
+ if type.eql?("accepted")
235
+ do_accepted(message, action.to_sym, token, type, new_version)
236
+ else
237
+ do_rejected(token, action, new_version)
238
+ end
239
+ @task_count_mutex.synchronize {
240
+ decresase_task_count(action.to_sym)
241
+ }
242
+ end
243
+ elsif %w(delta).include?(action)
244
+ do_delta(message, new_version)
245
+ end
207
246
  end
208
247
 
209
- def remove_shadow_delta_callback
210
- @general_action_mutex.synchronize(){
211
- @topic_subscribe_callback.delete[:delta]
212
- @topic_manager.shadow_topic_unsubscribe(@shadow_name, "delta")
213
- }
248
+ def do_accepted(message, action, token, type, new_version)
249
+ if new_version && new_version >= @last_stable_version
250
+ @logger.info("The #{action} action with the token #{token} have been accepted.") if logger?
251
+ type.eql?("delete") ? @last_stable_version = -1 : @last_stable_version = new_version
252
+ Thread.new do
253
+ accepted_tasks(message, action, token)
254
+ end
255
+ else
256
+ @logger.warn("CATCH AN ACCEPTED #{action} BUT OUTDATED/INVALID VERSION (= #{new_version})\n") if logger?
257
+ end
214
258
  end
215
259
 
216
- private
260
+ def accepted_tasks(message, action, token)
261
+ @topic_subscribed_callback[action].call(message) unless @topic_subscribed_callback[action].nil?
262
+ @token_callback[token].call(message) if @token_callback.has_key?(token)
263
+ @token_callback.delete(token)
264
+ end
265
+
266
+ def do_rejected(token, action, new_version)
267
+ if new_version && new_version >= @last_stable_version
268
+ @logger.info("The #{action} action with the token #{token} have been rejected.") if logger?
269
+ @token_callback.delete(token)
270
+ else
271
+ @logger.warn("CATCH AN REJECTED #{action} BUT OUTDATED/INVALID VERSION (= #{new_version})\n") if logger?
272
+ end
273
+ end
274
+
275
+ def do_delta(message, new_version)
276
+ if new_version && new_version >= @last_stable_version
277
+ @logger.info("A delta action have been accepted.") if logger?
278
+ @last_stable_version = new_version
279
+ Thread.new { @topic_subscribed_callback[:delta].call(message) } unless @topic_subscribed_callback[:delta].nil?
280
+ else
281
+ @logger.warn("CATCH A DELTA BUT OUTDATED/INVALID VERSION (= #{new_version})\n") if logger?
282
+ end
283
+ end
284
+
285
+ def handle_subscription(action, timeout)
286
+ @topic_manager.shadow_topic_subscribe(action.to_s, @default_callback)
287
+ if @topic_manager.paho_client?
288
+ ref = Time.now + timeout
289
+ while !@is_subscribed[action] && handle_timeout(ref) do
290
+ sleep 0.0001
291
+ end
292
+ else
293
+ sleep 2
294
+ end
295
+ end
296
+
297
+ def handle_timeout(ref)
298
+ Time.now <= ref
299
+ end
217
300
 
218
301
  def parse_shadow_name(topic)
219
302
  topic.split('/')[2]
@@ -5,56 +5,114 @@ require 'aws_iot_device/mqtt_shadow_client/shadow_action_manager'
5
5
  module AwsIotDevice
6
6
  module MqttShadowClient
7
7
  class ShadowClient
8
- attr_accessor :action_manager
9
8
 
10
- def initialize
11
- @mqtt_client = MqttManager.new
9
+ def initialize(*args)
10
+ unless args.last.nil?
11
+ config_attr(args.last)
12
+ else
13
+ @mqtt_client = MqttManager.new
14
+ end
12
15
  end
13
16
 
14
- def connect
15
- @mqtt_client.connect
17
+ def connect(*args, &block)
18
+ @mqtt_client.connect(*args)
19
+ self.logger.info("Connected to the AWS IoT platform") if logger?
20
+ if block_given?
21
+ begin
22
+ yield(self)
23
+ ensure
24
+ @mqtt_client.disconnect
25
+ end
26
+ end
16
27
  end
17
28
 
18
- def topic_manager
19
- @topic_manager = ShadowTopicManager.new(@mqtt_client)
29
+ def create_shadow_handler_with_name(shadow_name, persistent_subscribe=false)
30
+ @action_manager = ShadowActionManager.new(shadow_name, @mqtt_client, persistent_subscribe)
20
31
  end
21
32
 
22
- def create_shadow_handler_with_name(shadow_name, is_persistent_subscribe=false)
23
- topic_manager
24
- @action_manager = ShadowActionManager.new(shadow_name, @topic_manager, is_persistent_subscribe)
33
+ def logger=(logger_path)
34
+ file = File.open(logger_path, "a+")
35
+ log_file = Logger.new(file)
36
+ log_file.level = Logger::DEBUG
37
+ @action_manager.logger = log_file
25
38
  end
26
39
 
27
- def get_shadow(callback=nil, timeout=5)
28
- @action_manager.shadow_get(callback, timeout)
40
+ def logger
41
+ @action_manager.logger
29
42
  end
30
43
 
31
- def update_shadow(payload, callback=nil, timeout=5)
32
- @action_manager.shadow_update(payload, callback, timeout)
44
+ def logger?
45
+ @action_manager.logger?
33
46
  end
34
47
 
35
- def delete_shadow(callback=nil, timeout=5)
36
- @action_manager.shadow_delete(callback, timeout)
48
+ def get_shadow(timeout=5, callback=nil, &block)
49
+ @action_manager.shadow_get(timeout, callback, &block)
37
50
  end
38
51
 
39
- def register_delta_callback(callback)
40
- @action_manager.register_shadow_delta_callback(callback)
52
+ def update_shadow(payload, timeout=5, callback=nil, &block)
53
+ @action_manager.shadow_update(payload, timeout, callback, &block)
41
54
  end
42
55
 
43
- def remove_shadow_delta_callback
56
+ def delete_shadow(timeout=5, callback=nil, &block)
57
+ @action_manager.shadow_delete(timeout, callback, &block)
58
+ end
59
+
60
+ def register_get_callback(callback=nil, &block)
61
+ @action_manager.register_get_callback(callback, &block)
62
+ end
63
+
64
+ def register_update_callback(callback=nil, &block)
65
+ @action_manager.register_update_callback(callback, &block)
66
+ end
67
+
68
+ def register_delete_callback(callback=nil, &block)
69
+ @action_manager.register_delete_callback(callback, &block)
70
+ end
71
+
72
+ def register_delta_callback(callback=nil, &block)
73
+ @action_manager.register_shadow_delta_callback(callback, &block)
74
+ end
75
+
76
+ def remove_delta_callback
44
77
  @action_manager.remove_shadow_delta_callback
45
78
  end
46
79
 
80
+ def remove_get_callback
81
+ @action_manager.remove_get_callback
82
+ end
83
+
84
+ def remove_update_callback
85
+ @action_manager.remove_update_callback
86
+ end
87
+
88
+ def remove_delete_callback
89
+ @action_manager.remove_delete_callback
90
+ end
91
+
47
92
  def disconnect
48
93
  @mqtt_client.disconnect
49
94
  end
50
95
 
51
- def configure_endpoint(host,port)
96
+ def configure_endpoint(host, port)
52
97
  @mqtt_client.config_endpoint(host,port)
53
98
  end
54
99
 
55
100
  def configure_credentials(ca_file, key, cert)
56
101
  @mqtt_client.config_ssl_context(ca_file, key, cert)
57
102
  end
103
+
104
+
105
+ private
106
+
107
+ def config_attr(args)
108
+ shadow_attr = args.dup
109
+ shadow_attr.keep_if {|key| key == :shadow_name || key == :persistent_subscribe || key == :logger }
110
+ mqtt_attr = args
111
+ mqtt_attr.delete_if {|key| key == :shadow_name || key == :persistent_subscribe || key == :logger }
112
+ @mqtt_client = MqttManager.new(mqtt_attr)
113
+ shadow_attr[:persistent_subscribe] ||= false
114
+ @action_manager = create_shadow_handler_with_name(shadow_attr[:shadow_name], shadow_attr[:persistent_subsrcibe]) if shadow_attr.has_key?(:shadow_name)
115
+ end
58
116
  end
59
117
  end
60
118
  end