apiotics_aws_client 1.0.1

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 (36) 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.gemspec +40 -0
  9. data/bin/console +11 -0
  10. data/bin/setup +7 -0
  11. data/codeclimate.yml +6 -0
  12. data/lib/aws_iot_device.rb +8 -0
  13. data/lib/aws_iot_device/mqtt_adapter.rb +31 -0
  14. data/lib/aws_iot_device/mqtt_adapter/client.rb +207 -0
  15. data/lib/aws_iot_device/mqtt_adapter/paho_mqtt_adapter.rb +207 -0
  16. data/lib/aws_iot_device/mqtt_adapter/ruby_mqtt_adapter.rb +183 -0
  17. data/lib/aws_iot_device/mqtt_shadow_client.rb +7 -0
  18. data/lib/aws_iot_device/mqtt_shadow_client/json_payload_parser.rb +34 -0
  19. data/lib/aws_iot_device/mqtt_shadow_client/mqtt_manager.rb +201 -0
  20. data/lib/aws_iot_device/mqtt_shadow_client/shadow_action_manager.rb +318 -0
  21. data/lib/aws_iot_device/mqtt_shadow_client/shadow_client.rb +118 -0
  22. data/lib/aws_iot_device/mqtt_shadow_client/shadow_topic_manager.rb +75 -0
  23. data/lib/aws_iot_device/mqtt_shadow_client/token_creator.rb +36 -0
  24. data/lib/aws_iot_device/mqtt_shadow_client/topic_builder.rb +35 -0
  25. data/lib/aws_iot_device/version.rb +3 -0
  26. data/samples/config_shadow.rb +72 -0
  27. data/samples/mqtt_client_samples/mqtt_client_samples.rb +38 -0
  28. data/samples/shadow_action_samples/sample_shadow_action_update.rb +25 -0
  29. data/samples/shadow_client_samples/samples_shadow_client_block.rb +25 -0
  30. data/samples/shadow_client_samples/samples_shadow_client_delete.rb +24 -0
  31. data/samples/shadow_client_samples/samples_shadow_client_description.rb +64 -0
  32. data/samples/shadow_client_samples/samples_shadow_client_get.rb +23 -0
  33. data/samples/shadow_client_samples/samples_shadow_client_getting_started.rb +22 -0
  34. data/samples/shadow_client_samples/samples_shadow_client_update.rb +30 -0
  35. data/samples/shadow_topic_samples/sample_topic_manager.rb +26 -0
  36. metadata +216 -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