apiotics_aws_iot_client 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +13 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +201 -0
  5. data/README.md +207 -0
  6. data/Rakefile +6 -0
  7. data/apiotics-aws-iot-client-1.0.0.gem +0 -0
  8. data/apiotics_aws_client-1.0.1.gem +0 -0
  9. data/apiotics_aws_iot_client.gemspec +40 -0
  10. data/bin/console +11 -0
  11. data/bin/setup +7 -0
  12. data/codeclimate.yml +6 -0
  13. data/lib/aws_iot_device.rb +8 -0
  14. data/lib/aws_iot_device/mqtt_adapter.rb +31 -0
  15. data/lib/aws_iot_device/mqtt_adapter/client.rb +207 -0
  16. data/lib/aws_iot_device/mqtt_adapter/paho_mqtt_adapter.rb +207 -0
  17. data/lib/aws_iot_device/mqtt_adapter/ruby_mqtt_adapter.rb +183 -0
  18. data/lib/aws_iot_device/mqtt_shadow_client.rb +7 -0
  19. data/lib/aws_iot_device/mqtt_shadow_client/json_payload_parser.rb +34 -0
  20. data/lib/aws_iot_device/mqtt_shadow_client/mqtt_manager.rb +201 -0
  21. data/lib/aws_iot_device/mqtt_shadow_client/shadow_action_manager.rb +318 -0
  22. data/lib/aws_iot_device/mqtt_shadow_client/shadow_client.rb +118 -0
  23. data/lib/aws_iot_device/mqtt_shadow_client/shadow_topic_manager.rb +75 -0
  24. data/lib/aws_iot_device/mqtt_shadow_client/token_creator.rb +36 -0
  25. data/lib/aws_iot_device/mqtt_shadow_client/topic_builder.rb +35 -0
  26. data/lib/aws_iot_device/version.rb +3 -0
  27. data/samples/config_shadow.rb +72 -0
  28. data/samples/mqtt_client_samples/mqtt_client_samples.rb +38 -0
  29. data/samples/shadow_action_samples/sample_shadow_action_update.rb +25 -0
  30. data/samples/shadow_client_samples/samples_shadow_client_block.rb +25 -0
  31. data/samples/shadow_client_samples/samples_shadow_client_delete.rb +24 -0
  32. data/samples/shadow_client_samples/samples_shadow_client_description.rb +64 -0
  33. data/samples/shadow_client_samples/samples_shadow_client_get.rb +23 -0
  34. data/samples/shadow_client_samples/samples_shadow_client_getting_started.rb +22 -0
  35. data/samples/shadow_client_samples/samples_shadow_client_update.rb +30 -0
  36. data/samples/shadow_topic_samples/sample_topic_manager.rb +26 -0
  37. metadata +217 -0
@@ -0,0 +1,118 @@
1
+ require 'aws_iot_device/mqtt_shadow_client/mqtt_manager'
2
+ require 'aws_iot_device/mqtt_shadow_client/shadow_topic_manager'
3
+ require 'aws_iot_device/mqtt_shadow_client/shadow_action_manager'
4
+
5
+ module AwsIotDevice
6
+ module MqttShadowClient
7
+ class ShadowClient
8
+
9
+ def initialize(*args)
10
+ unless args.last.nil?
11
+ config_attr(args.last)
12
+ else
13
+ @mqtt_client = MqttManager.new
14
+ end
15
+ end
16
+
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
27
+ end
28
+
29
+ def create_shadow_handler_with_name(shadow_name, persistent_subscribe=false)
30
+ @action_manager = ShadowActionManager.new(shadow_name, @mqtt_client, persistent_subscribe)
31
+ end
32
+
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
38
+ end
39
+
40
+ def logger
41
+ @action_manager.logger
42
+ end
43
+
44
+ def logger?
45
+ @action_manager.logger?
46
+ end
47
+
48
+ def get_shadow(timeout=5, callback=nil, &block)
49
+ @action_manager.shadow_get(timeout, callback, &block)
50
+ end
51
+
52
+ def update_shadow(payload, timeout=5, callback=nil, &block)
53
+ @action_manager.shadow_update(payload, timeout, callback, &block)
54
+ end
55
+
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
77
+ @action_manager.remove_shadow_delta_callback
78
+ end
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
+
92
+ def disconnect
93
+ @mqtt_client.disconnect
94
+ end
95
+
96
+ def configure_endpoint(host, port)
97
+ @mqtt_client.config_endpoint(host,port)
98
+ end
99
+
100
+ def configure_credentials(ca_file, key, cert)
101
+ @mqtt_client.config_ssl_context(ca_file, key, cert)
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
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,75 @@
1
+ require 'aws_iot_device/mqtt_shadow_client/topic_builder'
2
+
3
+ module AwsIotDevice
4
+ module MqttShadowClient
5
+ class ShadowTopicManager
6
+
7
+ def initialize(mqtt_manager, shadow_name)
8
+ raise ArgumentError, "topic manager should be initialized with a mqtt_manager but was undefined" if mqtt_manager.nil?
9
+ raise ArgumentError, "topic manager should be initialize with a mqtt mmanager but was undefined" if shadow_name.nil?
10
+
11
+ @mqtt_manager = mqtt_manager
12
+ @sub_unsub_mutex = Mutex.new()
13
+ @topic = TopicBuilder.new(shadow_name)
14
+ @timeout = mqtt_manager.mqtt_operation_timeout_s
15
+ end
16
+
17
+ def shadow_topic_publish(action, payload)
18
+ @mqtt_manager.publish(@topic.get_topic_general(action), payload, false, 0)
19
+ end
20
+
21
+ def shadow_topic_subscribe(action, callback=nil)
22
+ @sub_unsub_mutex.synchronize() {
23
+ @subacked = false
24
+ if @topic.is_delta?(action)
25
+ @mqtt_manager.subscribe(@topic.get_topic_delta, 0, callback)
26
+ else
27
+ @mqtt_manager.subscribe_bunch([@topic.get_topic_accepted(action), 1, callback], [@topic.get_topic_rejected(action), 1, callback])
28
+ end
29
+ }
30
+ end
31
+
32
+ def shadow_topic_unsubscribe(action)
33
+ @sub_unsub_mutex.synchronize(){
34
+ @unsubacked = false
35
+ if @topic.is_delta?(action)
36
+ @mqtt_manager.unsubscribe(@topic.get_topic_delta)
37
+ else
38
+ @mqtt_manager.unsubscribe_bunch(@topic.get_topic_accepted(action), @topic.get_topic_rejected(action))
39
+ end
40
+ }
41
+ end
42
+
43
+ def retrieve_action(topic)
44
+ res = nil
45
+ ACTION_NAME.each do |action|
46
+ if topic[0] == @topic.get_topic_accepted(action)
47
+ res = action.to_sym
48
+ break
49
+ end
50
+ end
51
+ res
52
+ end
53
+
54
+ def paho_client?
55
+ @mqtt_manager.paho_client?
56
+ end
57
+
58
+ def on_suback=(callback)
59
+ @mqtt_manager.on_suback = callback
60
+ end
61
+
62
+ def on_suback(&block)
63
+ @mqtt_manager.on_suback(&block)
64
+ end
65
+
66
+ def on_unsuback=(callback)
67
+ @mqtt_manager.on_unsuback = callback
68
+ end
69
+
70
+ def on_unsuback(&block)
71
+ @mqtt_manager.on_unsuback(&block)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,36 @@
1
+ require 'securerandom'
2
+ require 'timers'
3
+ require 'thread'
4
+ require 'json'
5
+
6
+ module AwsIotDevice
7
+ module MqttShadowClient
8
+ class TokenCreator
9
+ ### This class manage the clients token.
10
+ ### Every actions receive a token for a certian interval, meaning that action is waiting to be proceed.
11
+ ### When token time run out or the actions have been treated token should deleted.
12
+
13
+ def initialize(shadow_name, client_id)
14
+ if shadow_name.length > 16
15
+ @shadow_name = shadow_name[0..15]
16
+ else
17
+ @shadow_name = shadow_name
18
+ end
19
+ @client_id = client_id
20
+ @sequence_number = 0
21
+ end
22
+
23
+ def create_next_token
24
+ token = ""
25
+ token << "#{@client_id}" << "_" << "#{@shadow_name}" << "_" << "#{@sequence_number}" << "_" << "#{random_token_string(5)}"
26
+ end
27
+
28
+ private
29
+
30
+ def random_token_string(lenght)
31
+ charset = Array('A'..'Z') + Array('a'..'z') + Array('0'..'9')
32
+ Array.new(lenght) { charset.sample }.join
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ module AwsIotDevice
2
+ module MqttShadowClient
3
+ class TopicBuilder
4
+ def initialize(shadow_name)
5
+ raise ArgumentError, "topic_builder initialization, shadow_name is required but undefined" if shadow_name.nil?
6
+
7
+ @shadow_name = shadow_name
8
+
9
+ @topic_delta = "$aws/things/#{shadow_name}/shadow/update/delta"
10
+ @topic_general = "$aws/things/#{shadow_name}/shadow/"
11
+ end
12
+
13
+ def is_delta?(action)
14
+ action == ACTION_NAME[3]
15
+ end
16
+
17
+ def get_topic_general(action)
18
+ raise ArgumentError, "topic_builder, get topic, unreconized action_name \"#{action}\"" unless ACTION_NAME.include?(action)
19
+ @topic_general + action
20
+ end
21
+
22
+ def get_topic_accepted(action)
23
+ get_topic_general(action) + "/accepted"
24
+ end
25
+
26
+ def get_topic_rejected(action)
27
+ get_topic_general(action) + "/rejected"
28
+ end
29
+
30
+ def get_topic_delta
31
+ @topic_delta
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module AwsIotDevice
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,72 @@
1
+ require 'optparse'
2
+
3
+ options = {}
4
+
5
+ OptionParser.new do |opts|
6
+ opts.banner = "Basic usage basic_greeting.rb -c \"YOUR_CERTIFICATE_PATH\" -k \"YOUR_KEY_FILE_PATH\" -ca \"YOUR_ROOT_CA_PATH -H \"YOUR_ENDPOINT\" -p 8883 -t \"YOUR THING NAME\"\n"
7
+
8
+ opts.separator ""
9
+ opts.separator "Common options"
10
+ opts.on_tail("-h", "--help", "--usage", "Show this message") do
11
+ puts opts
12
+ exit
13
+ end
14
+
15
+ opts.on("-H", "--host [END_POINT]", "The endpoint where you want to connect") do |host|
16
+ options[:host] = host
17
+ end
18
+
19
+ opts.on("-p", "--port [MQTT_PORT]", "The port used for the connection Default is 8883") do |port|
20
+ options[:port] = port
21
+ end
22
+
23
+ opts.on("-c", "--cert [CERT_FILE_PATH]", "The path to the certificate file of the private key.") do |cert|
24
+ options[:cert] = cert
25
+ end
26
+
27
+ opts.on("-k", "--key [KEY_FILE_PATH]", "The path to private key file that would be used for encryption") do |key|
28
+ options[:key] = key
29
+ end
30
+
31
+ opts.on("-a", "--root_ca [ROOT_CA_PATH]", "The path to the authority certification file") do |root_ca|
32
+ options[:root_ca] = root_ca
33
+ end
34
+
35
+ opts.on("-t", "--thing [THING_NAME]", "The Thing name on which the action would be done") do |thing|
36
+ options[:things] = thing
37
+ end
38
+ end.parse!(ARGV)
39
+
40
+ @host = options[:host]
41
+ @port = options[:port] || 8883
42
+ @certificate_path = options[:cert]
43
+ @private_key_path = options[:key]
44
+ @root_ca_path = options[:root_ca]
45
+ @thing = options[:things]
46
+
47
+ def setting_mqtt_client
48
+ mqtt_client = AwsIotDevice::MqttShadowClient::MqttManager.new(host: @host,
49
+ port: @port,
50
+ ssl: true)
51
+ mqtt_client.config_ssl_context(@root_ca_path, @private_key_path, @certificate_path)
52
+ mqtt_client.connect
53
+ mqtt_client
54
+ end
55
+
56
+ def setting_topic_manager(mqtt_client)
57
+ topic_manager = AwsIotDevice::MqttShadowClient::ShadowTopicManager.new(mqtt_client, @thing)
58
+ end
59
+
60
+ def setting_action_manager(mqtt_client)
61
+ action_manager = AwsIotDevice::MqttShadowClient::ShadowActionManager.new(@thing, mqtt_client, false)
62
+ action_manager
63
+ end
64
+
65
+ def setting_shadow
66
+ shadow_client = AwsIotDevice::MqttShadowClient::ShadowClient.new
67
+ shadow_client.configure_endpoint(@host, @port)
68
+ shadow_client.configure_credentials(@root_ca_path, @private_key_path, @certificate_path)
69
+ shadow_client.create_shadow_handler_with_name(@thing, true)
70
+ shadow_client.connect
71
+ shadow_client
72
+ end
@@ -0,0 +1,38 @@
1
+ $:.unshift(File.expand_path("../../../samples", __FILE__))
2
+
3
+ require 'aws_iot_device'
4
+ require 'config_shadow'
5
+
6
+ client = setting_mqtt_client
7
+
8
+ client2 = setting_mqtt_client
9
+
10
+ callback = Proc.new do |message|
11
+ p " Client1 catch message event"
12
+ p "--- Topic: #{message.topic}"
13
+ p "--- Payload: #{message.payload}"
14
+ end
15
+
16
+ callback2 = Proc.new do |message|
17
+ p " Client2 catch message event"
18
+ p "--- Topic: #{message.topic}"
19
+ p "--- Payload: #{message.payload}"
20
+ end
21
+
22
+ client.subscribe("topic_2", 0, callback)
23
+ client2.subscribe("topic_1", 0,callback2)
24
+
25
+ puts "# STARTING EXAMPLE #"
26
+ client.publish("topic_1", "Hello Sir. My name is client 1. How do you do? ")
27
+ client2.publish("topic_2", "Hello Mister Client 1. My name is client 2. How do you do?")
28
+ sleep 1
29
+
30
+ 2.times do
31
+ client.publish("topic_1", "How do you do?")
32
+ sleep 1
33
+ client2.publish("topic_2", "How do you do?")
34
+ sleep 1
35
+ end
36
+ sleep 2
37
+ client.disconnect
38
+ client2.disconnect
@@ -0,0 +1,25 @@
1
+ $:.unshift(File.expand_path("../../../samples", __FILE__))
2
+
3
+ require 'aws_iot_device'
4
+ require 'config_shadow'
5
+
6
+ filter_callback = Proc.new do |message|
7
+ puts "Executing the specific callback for topic: #{message.topic}\n##########################################\n"
8
+ end
9
+
10
+ mqtt_client = setting_mqtt_client
11
+ client = setting_action_manager(mqtt_client)
12
+
13
+ client.register_shadow_delta_callback(filter_callback)
14
+ timeout = 5
15
+
16
+ n = 1
17
+ 5.times do
18
+ json_payload = "{\"state\":{\"desired\":{\"property\":\"RubySDK\",\"count\":#{n}}}}"
19
+ client.shadow_update(json_payload, timeout, filter_callback)
20
+ n += 1
21
+ end
22
+
23
+ sleep timeout
24
+
25
+ mqtt_client.disconnect
@@ -0,0 +1,25 @@
1
+ $:.unshift(File.expand_path("../../../samples", __FILE__))
2
+
3
+ require 'aws_iot_device'
4
+ require 'config_shadow'
5
+
6
+ filter_callback = Proc.new do |message|
7
+ puts "Executing the specific callback for topic: #{message.topic}\n##########################################\n"
8
+ end
9
+
10
+ my_shadow_client = setting_shadow
11
+
12
+ my_shadow_client.connect do |client|
13
+
14
+ puts "##### Starting test_shadow_client_get ######"
15
+ client.get_shadow(4, filter_callback)
16
+
17
+ puts "##### Starting test_shadow_client_get ######"
18
+ client.get_shadow(4) do
19
+ puts "CALLED FROM BLOCK"
20
+ end
21
+
22
+ puts "##### Starting test_shadow_client_get ######"
23
+ client.get_shadow(4, filter_callback)
24
+ sleep 5
25
+ end
@@ -0,0 +1,24 @@
1
+ $:.unshift(File.expand_path("../../../samples", __FILE__))
2
+
3
+ require 'aws_iot_device'
4
+ require 'config_shadow'
5
+
6
+ filter_callback = Proc.new do |message|
7
+ puts "Executing the specific callback for topic: #{message.topic}\n##########################################\n"
8
+ end
9
+
10
+ my_shadow_client = setting_shadow
11
+
12
+ n = 1
13
+ 3.times do
14
+ puts "Start shadow_delete\n"
15
+ my_shadow_client.delete_shadow(5, filter_callback)
16
+ sleep 0.01
17
+ json_payload = '{"state":{"desired":{"property":"RubySDK"}}}'
18
+ my_shadow_client.update_shadow(json_payload, 5)
19
+ n += 1
20
+ end
21
+
22
+ sleep 2
23
+
24
+ my_shadow_client.disconnect