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.
- checksums.yaml +7 -0
- data/.gitignore +51 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE +201 -0
- data/README.md +175 -0
- data/Rakefile +6 -0
- data/aws_iot_device.gemspec +39 -0
- data/bin/console +11 -0
- data/bin/setup +7 -0
- data/lib/aws_iot_device.rb +7 -0
- data/lib/aws_iot_device/mqtt_adapter.rb +32 -0
- data/lib/aws_iot_device/mqtt_adapter/client.rb +139 -0
- data/lib/aws_iot_device/mqtt_adapter/mqtt_adapter.rb +139 -0
- data/lib/aws_iot_device/mqtt_adapter/ruby_mqtt_adapter.rb +176 -0
- data/lib/aws_iot_device/mqtt_shadow_client.rb +6 -0
- data/lib/aws_iot_device/mqtt_shadow_client/json_payload_parser.rb +34 -0
- data/lib/aws_iot_device/mqtt_shadow_client/mqtt_manager.rb +135 -0
- data/lib/aws_iot_device/mqtt_shadow_client/shadow_action_manager.rb +235 -0
- data/lib/aws_iot_device/mqtt_shadow_client/shadow_client.rb +60 -0
- data/lib/aws_iot_device/mqtt_shadow_client/shadow_topic_manager.rb +50 -0
- data/lib/aws_iot_device/mqtt_shadow_client/token_creator.rb +32 -0
- data/lib/aws_iot_device/mqtt_shadow_client/topic_builder.rb +50 -0
- data/lib/aws_iot_device/version.rb +3 -0
- data/samples/mqtt_client_samples/mqtt_client_samples.rb +90 -0
- data/samples/shadow_action_samples/sample_shadow_action_update.rb +79 -0
- data/samples/shadow_client_samples/samples_shadow_client_delete.rb +73 -0
- data/samples/shadow_client_samples/samples_shadow_client_get.rb +74 -0
- data/samples/shadow_client_samples/samples_shadow_client_update.rb +81 -0
- data/samples/shadow_topic_samples/sample_topic_manager.rb +77 -0
- metadata +186 -0
data/Rakefile
ADDED
@@ -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,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
|