aws_iot_device 0.1.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 (31) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +51 -0
  3. data/CODE_OF_CONDUCT.md +13 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +201 -0
  6. data/README.md +175 -0
  7. data/Rakefile +6 -0
  8. data/aws_iot_device.gemspec +39 -0
  9. data/bin/console +11 -0
  10. data/bin/setup +7 -0
  11. data/lib/aws_iot_device.rb +7 -0
  12. data/lib/aws_iot_device/mqtt_adapter.rb +32 -0
  13. data/lib/aws_iot_device/mqtt_adapter/client.rb +139 -0
  14. data/lib/aws_iot_device/mqtt_adapter/mqtt_adapter.rb +139 -0
  15. data/lib/aws_iot_device/mqtt_adapter/ruby_mqtt_adapter.rb +176 -0
  16. data/lib/aws_iot_device/mqtt_shadow_client.rb +6 -0
  17. data/lib/aws_iot_device/mqtt_shadow_client/json_payload_parser.rb +34 -0
  18. data/lib/aws_iot_device/mqtt_shadow_client/mqtt_manager.rb +135 -0
  19. data/lib/aws_iot_device/mqtt_shadow_client/shadow_action_manager.rb +235 -0
  20. data/lib/aws_iot_device/mqtt_shadow_client/shadow_client.rb +60 -0
  21. data/lib/aws_iot_device/mqtt_shadow_client/shadow_topic_manager.rb +50 -0
  22. data/lib/aws_iot_device/mqtt_shadow_client/token_creator.rb +32 -0
  23. data/lib/aws_iot_device/mqtt_shadow_client/topic_builder.rb +50 -0
  24. data/lib/aws_iot_device/version.rb +3 -0
  25. data/samples/mqtt_client_samples/mqtt_client_samples.rb +90 -0
  26. data/samples/shadow_action_samples/sample_shadow_action_update.rb +79 -0
  27. data/samples/shadow_client_samples/samples_shadow_client_delete.rb +73 -0
  28. data/samples/shadow_client_samples/samples_shadow_client_get.rb +74 -0
  29. data/samples/shadow_client_samples/samples_shadow_client_update.rb +81 -0
  30. data/samples/shadow_topic_samples/sample_topic_manager.rb +77 -0
  31. metadata +186 -0
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'aws_iot_device/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "aws_iot_device"
8
+ spec.version = AwsIotDevice::VERSION
9
+ spec.authors = ["Pierre Goudet"]
10
+ spec.email = ["p-goudet@ruby-dev.jp"]
11
+
12
+ spec.summary = %q{A gem use to communicates with the Aws Iot platform through the MQTT protocol}
13
+ spec.description = %q{A gem use to communicates with the Aws Iot platform through the MQTT protocol}
14
+ spec.homepage = "https://github.com/RubyDevInc/aws-iot-device-sdk-ruby"
15
+ spec.license = "Apache 2.0"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.10"
31
+ spec.add_development_dependency "pry"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rspec"
34
+
35
+ spec.add_runtime_dependency "facets", "~> 3.1.0"
36
+ spec.add_runtime_dependency "json", "~> 1.8.3"
37
+ spec.add_runtime_dependency "mqtt", "~> 0.4.0"
38
+ spec.add_runtime_dependency "timers", "~> 4.1.1"
39
+ end
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "aws_iot"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ require "aws_iot_device/version"
2
+ require "aws_iot_device/mqtt_adapter"
3
+ require "aws_iot_device/mqtt_shadow_client"
4
+
5
+ module AwsIotDevice
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,32 @@
1
+ require 'facets'
2
+ require 'aws_iot_device/mqtt_adapter/client'
3
+
4
+ module AwsIotDevice
5
+ module MqttAdapter
6
+ extend self
7
+
8
+ ### Return the adapter selected for the module with either a pre-setted value or a default value
9
+ def adapter
10
+ return @adapter if @adapter
11
+ ### Calling the setter method with the default symbol 'RubyMqttAdapter' and return it.
12
+ self.adapter = :ruby_mqtt_adapter
13
+ @adapter
14
+ end
15
+
16
+ ### The setter of the module's adapter attributes
17
+ def adapter=(adapter_lib)
18
+ case adapter_lib
19
+ when Symbol, String
20
+ begin
21
+ require "aws_iot_device/mqtt_adapter/#{adapter_lib}"
22
+ rescue LoadError
23
+ raise "LoadError: Could find adapters for the lib #{adapter_lib}"
24
+ exit
25
+ end
26
+ @adapter = MqttAdapter.const_get("#{adapter_lib.to_s.camelcase(:upper)}")
27
+ else
28
+ raise "TypeError: Library name should be a String or Symbol"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,139 @@
1
+ module AwsIotDevice
2
+ module MqttAdapter
3
+ class Client
4
+
5
+ attr_reader :client_id
6
+
7
+ ### @adapter contains the name of the adapter that should be module as a third party librairy
8
+ ### The method call by the shared client are implemented by the third party or the adapter module itself.
9
+ ### @adapter default value is MqttShareLib::Adapters::RubyMqttAdapter
10
+ attr_accessor :adapter
11
+
12
+ ### @on_'event' contains the callback's [block, Proc, lambda] that should be called when 'event' is catched
13
+ ### Callbacks should be customized in the higher level class (ex. MqttManger or upper)
14
+ ### Callback should be called by some (private) handlers define in the third party librairy
15
+
16
+ ### On a MqttAdapter's create, the client adapter is set as the previously define module adapter
17
+ ### The client is then initialize with the client type of the third librairy of the adapter.
18
+ ### @client default type is MQTT::Client
19
+ def initialize(*args)
20
+ @adapter = MqttAdapter.adapter.new(*args)
21
+ end
22
+
23
+ def client_id
24
+ @adapter.client_id
25
+ end
26
+
27
+ ### The following method represent the basics common MQTT actions.
28
+ ### As possible, they should be implemented in the third party librairy
29
+ ### If not, the adpater should implement them or throw and excpetion
30
+ def connect(*args, &block)
31
+ @adapter.connect(*args, &block)
32
+ end
33
+
34
+ def publish(topic, payload='', retain=false, qos=0)
35
+ @adapter.publish(topic, payload, retain, qos)
36
+ end
37
+
38
+ def loop_start
39
+ @thread = @adapter.loop_start
40
+ end
41
+
42
+ def loop_stop
43
+ @adapter.loop_stop(@thread)
44
+ end
45
+
46
+ def loop_forever
47
+ @adapter.loop_forever
48
+ end
49
+
50
+ def mqtt_loop
51
+ @adapter.loop
52
+ end
53
+
54
+ def loop_read
55
+ @adapter.loop_read
56
+ end
57
+
58
+ def loop_write
59
+ @adapter.loop_write
60
+ end
61
+
62
+ def loop_misc
63
+ @adapter.loop_misc
64
+ end
65
+
66
+ def get(topic=nil, &block)
67
+ @adapter.get(topic, &block)
68
+ end
69
+
70
+ def get_packet(topic=nil, &block)
71
+ @adapter.get_packet(topic, &block)
72
+ end
73
+
74
+ def generate_client_id
75
+ @adapter.generate_client_id
76
+ end
77
+
78
+ def disconnect(send_msg=true)
79
+ @adapter.disconnect(send_msg)
80
+ end
81
+
82
+ def connected?
83
+ @adapter.connected?
84
+ end
85
+
86
+ def subscribe(topic)
87
+ @adapter.subscribe(topic)
88
+ end
89
+
90
+ def unsubscribe(topic)
91
+ @adapter.unsubscribe(topic)
92
+ end
93
+
94
+ def set_tls_ssl_context(ca_cert, cert=nil, key=nil)
95
+ @adapter.set_tls_ssl_context(ca_cert, cert, key)
96
+ end
97
+
98
+ def add_callback_filter_topic(topic, callback)
99
+ @adapter.add_callback_filter_topic(topic, callback)
100
+ end
101
+
102
+ def remove_callback_filter_topic(topic)
103
+ @adapter.remove_callback_filter_topic(topic)
104
+ end
105
+
106
+ def on_message=(callback)
107
+ @adapter.on_message = callback
108
+ end
109
+
110
+ ### The following attributes should exists in every MQTT third party librairy.
111
+ ### They are necessary (or really usefull and common) for the establishement of the connection and/or the basic Mqtt actions.
112
+ ### The setter directely change the third party client value when the getter remote the actual SharedClient instance's attribute value
113
+ def host
114
+ @adapter.host
115
+ end
116
+
117
+ def host=(host)
118
+ @adapter.host = host
119
+ end
120
+
121
+ def port
122
+ @adapter.port
123
+ end
124
+
125
+ def port=(port)
126
+ @adapter.port = port
127
+ end
128
+
129
+ # Boolean for the encrypted mode (true = ssl/tls | false = no encryption)
130
+ def ssl
131
+ @adapter.ssl
132
+ end
133
+
134
+ def ssl=(ssl)
135
+ @adapter.ssl = ssl
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,139 @@
1
+ module AwsIotDevice
2
+ module MqttAdapter
3
+ class MqttAdapter
4
+
5
+ attr_reader :client_id
6
+
7
+ ### @adapter contains the name of the adapter that should be module as a third party librairy
8
+ ### The method call by the shared client are implemented by the third party or the adapter module itself.
9
+ ### @adapter default value is MqttShareLib::Adapters::RubyMqttAdapter
10
+ attr_accessor :adapter
11
+
12
+ ### @on_'event' contains the callback's [block, Proc, lambda] that should be called when 'event' is catched
13
+ ### Callbacks should be customized in the higher level class (ex. MqttManger or upper)
14
+ ### Callback should be called by some (private) handlers define in the third party librairy
15
+
16
+ ### On a MqttAdapter's create, the client adapter is set as the previously define module adapter
17
+ ### The client is then initialize with the client type of the third librairy of the adapter.
18
+ ### @client default type is MQTT::Client
19
+ def initialize(*args)
20
+ @adapter = ::MqttAdapterLib.adapter.new(*args)
21
+ end
22
+
23
+ def client_id
24
+ @adapter.client_id
25
+ end
26
+
27
+ ### The following method represent the basics common MQTT actions.
28
+ ### As possible, they should be implemented in the third party librairy
29
+ ### If not, the adpater should implement them or throw and excpetion
30
+ def connect(*args, &block)
31
+ @adapter.connect(*args, &block)
32
+ end
33
+
34
+ def publish(topic, payload='', retain=false, qos=0)
35
+ @adapter.publish(topic, payload, retain, qos)
36
+ end
37
+
38
+ def loop_start
39
+ @thread = @adapter.loop_start
40
+ end
41
+
42
+ def loop_stop
43
+ @adapter.loop_stop(@thread)
44
+ end
45
+
46
+ def loop_forever
47
+ @adapter.loop_forever
48
+ end
49
+
50
+ def mqtt_loop
51
+ @adapter.loop
52
+ end
53
+
54
+ def loop_read
55
+ @adapter.loop_read
56
+ end
57
+
58
+ def loop_write
59
+ @adapter.loop_write
60
+ end
61
+
62
+ def loop_misc
63
+ @adapter.loop_misc
64
+ end
65
+
66
+ def get(topic=nil, &block)
67
+ @adapter.get(topic, &block)
68
+ end
69
+
70
+ def get_packet(topic=nil, &block)
71
+ @adapter.get_packet(topic, &block)
72
+ end
73
+
74
+ def generate_client_id
75
+ @adapter.generate_client_id
76
+ end
77
+
78
+ def disconnect(send_msg=true)
79
+ @adapter.disconnect(send_msg)
80
+ end
81
+
82
+ def connected?
83
+ @adapter.connected?
84
+ end
85
+
86
+ def subscribe(topic)
87
+ @adapter.subscribe(topic)
88
+ end
89
+
90
+ def unsubscribe(topic)
91
+ @adapter.unsubscribe(topic)
92
+ end
93
+
94
+ def set_tls_ssl_context(ca_cert, cert=nil, key=nil)
95
+ @adapter.set_tls_ssl_context(ca_cert, cert, key)
96
+ end
97
+
98
+ def add_callback_filter_topic(topic, callback)
99
+ @adapter.add_callback_filter_topic(topic, callback)
100
+ end
101
+
102
+ def remove_callback_filter_topic(topic)
103
+ @adapter.remove_callback_filter_topic(topic)
104
+ end
105
+
106
+ def on_message=(callback)
107
+ @adapter.on_message = callback
108
+ end
109
+
110
+ ### The following attributes should exists in every MQTT third party librairy.
111
+ ### They are necessary (or really usefull and common) for the establishement of the connection and/or the basic Mqtt actions.
112
+ ### The setter directely change the third party client value when the getter remote the actual SharedClient instance's attribute value
113
+ def host
114
+ @adapter.host
115
+ end
116
+
117
+ def host=(host)
118
+ @adapter.host = host
119
+ end
120
+
121
+ def port
122
+ @adapter.port
123
+ end
124
+
125
+ def port=(port)
126
+ @adapter.port = port
127
+ end
128
+
129
+ # Boolean for the encrypted mode (true = ssl/tls | false = no encryption)
130
+ def ssl
131
+ @adapter.ssl
132
+ end
133
+
134
+ def ssl=(ssl)
135
+ @adapter.ssl = ssl
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,176 @@
1
+ require 'mqtt'
2
+ require 'thread'
3
+
4
+ module AwsIotDevice
5
+ module MqttAdapter
6
+ class RubyMqttAdapter
7
+
8
+ attr_reader :client_id
9
+
10
+ attr_accessor :filtered_topics
11
+
12
+ def initialize(*args)
13
+ @client = MQTT::Client.new(*args)
14
+ @filtered_topics = {}
15
+ @client_id = ""
16
+ @client_id = generate_client_id
17
+ end
18
+
19
+ def client_id
20
+ @client_id
21
+ end
22
+
23
+ def publish(topic, payload='', retain=false, qos=0)
24
+ @client.publish(topic, payload, retain, qos)
25
+ end
26
+
27
+ def create_client(*args)
28
+ @client = MQTT::Client.new(*args)
29
+ end
30
+
31
+ def connect(*args, &block)
32
+ client = create_client(*args) if @client.nil?
33
+ @client.connect(&block)
34
+ loop_start
35
+ end
36
+
37
+ def generate_client_id(prefix='ruby', lenght=16)
38
+ charset = Array('A'..'Z') + Array('a'..'z') + Array('0'..'9')
39
+ @client_id << prefix << Array.new(lenght) { charset.sample }.join
40
+ end
41
+
42
+ def ssl_context
43
+ @client.ssl_context
44
+ end
45
+
46
+ def disconnect(send_msg=true)
47
+ @client.disconnect(send_msg)
48
+ end
49
+
50
+ def connected?
51
+ @client.connected?
52
+ end
53
+
54
+ def subscribe(topics, qos=0)
55
+ @client.subscribe(topics, qos)
56
+ end
57
+
58
+ def get(topic=nil, &block)
59
+ @client.get(topic, &block)
60
+ end
61
+
62
+ def get_packet(topic=nil, &block)
63
+ @client.get_packet(topic, &block)
64
+ end
65
+
66
+ def queue_empty?
67
+ @client.queue_empty?
68
+ end
69
+
70
+ def queue_length
71
+ @client.queue_length
72
+ end
73
+
74
+ def unsubscribe(*topics)
75
+ @client.unsubscribe(*topics)
76
+ end
77
+
78
+ def host
79
+ @client.host
80
+ end
81
+
82
+ def host=(host)
83
+ @client.host = host
84
+ end
85
+
86
+ def port
87
+ @client.port
88
+ end
89
+
90
+
91
+ def port=(port)
92
+ @client.port = port
93
+ end
94
+
95
+ def ssl=(ssl)
96
+ @client.ssl = ssl
97
+ end
98
+
99
+ def set_tls_ssl_context(ca_cert, cert=nil, key=nil)
100
+ @client.ssl ||= true
101
+ @client.ssl_context
102
+ @client.cert_file = cert
103
+ @client.key_file = key
104
+ @client.ca_file = ca_cert
105
+ end
106
+
107
+ ############### Custom Features #################
108
+
109
+ def loop_start
110
+ Thread.new{loop_forever}
111
+ end
112
+
113
+ def loop_stop(thread)
114
+ thread.join
115
+ end
116
+
117
+ def loop_forever
118
+ loop do
119
+ mqtt_loop
120
+ end
121
+ end
122
+
123
+ def mqtt_loop
124
+ loop_read
125
+ loop_write
126
+ loop_misc
127
+ end
128
+
129
+ def loop_read(max_message=10)
130
+ counter_message = 0
131
+ while !@client.queue_empty? && counter_message <= max_message
132
+ message = get_packet
133
+ ### Fitlering message if matching to filtered topic
134
+ topic = message.topic
135
+ if @filtered_topics.key?(topic)
136
+ callback = @filtered_topics[topic]
137
+ callback.call(message)
138
+ else
139
+ on_message_callback(message)
140
+ end
141
+ counter_message += 1
142
+ end
143
+ end
144
+
145
+ def loop_write
146
+ end
147
+
148
+ def loop_misc
149
+ end
150
+
151
+ def on_message=(callback)
152
+ @on_message = callback
153
+ end
154
+
155
+ def on_message_callback(message)
156
+ if @on_message.is_a? Proc
157
+ @on_message.call(message)
158
+ end
159
+ end
160
+
161
+ def add_callback_filter_topic(topic, callback)
162
+ unless callback.nil?
163
+ @filtered_topics[topic] = callback
164
+ else
165
+ @filtered_topic.delete(topic)
166
+ end
167
+ end
168
+
169
+ def remove_callback_filter_topic(topic)
170
+ if @filtered_topics.key(topic)
171
+ @filtered_topics.delete(topic)
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end