apiotics-paho-mqtt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/CODE_OF_CONDUCT.md +49 -0
  6. data/Gemfile +4 -0
  7. data/README.md +322 -0
  8. data/Rakefile +6 -0
  9. data/apiotics-paho-mqtt.gemspec +33 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/paho-mqtt.rb +165 -0
  13. data/lib/paho_mqtt/client.rb +417 -0
  14. data/lib/paho_mqtt/connection_helper.rb +169 -0
  15. data/lib/paho_mqtt/exception.rb +43 -0
  16. data/lib/paho_mqtt/handler.rb +273 -0
  17. data/lib/paho_mqtt/packet/base.rb +315 -0
  18. data/lib/paho_mqtt/packet/connack.rb +102 -0
  19. data/lib/paho_mqtt/packet/connect.rb +183 -0
  20. data/lib/paho_mqtt/packet/disconnect.rb +38 -0
  21. data/lib/paho_mqtt/packet/pingreq.rb +29 -0
  22. data/lib/paho_mqtt/packet/pingresp.rb +38 -0
  23. data/lib/paho_mqtt/packet/puback.rb +44 -0
  24. data/lib/paho_mqtt/packet/pubcomp.rb +44 -0
  25. data/lib/paho_mqtt/packet/publish.rb +148 -0
  26. data/lib/paho_mqtt/packet/pubrec.rb +44 -0
  27. data/lib/paho_mqtt/packet/pubrel.rb +62 -0
  28. data/lib/paho_mqtt/packet/suback.rb +75 -0
  29. data/lib/paho_mqtt/packet/subscribe.rb +124 -0
  30. data/lib/paho_mqtt/packet/unsuback.rb +49 -0
  31. data/lib/paho_mqtt/packet/unsubscribe.rb +84 -0
  32. data/lib/paho_mqtt/packet.rb +33 -0
  33. data/lib/paho_mqtt/publisher.rb +191 -0
  34. data/lib/paho_mqtt/sender.rb +86 -0
  35. data/lib/paho_mqtt/ssl_helper.rb +42 -0
  36. data/lib/paho_mqtt/subscriber.rb +163 -0
  37. data/lib/paho_mqtt/version.rb +3 -0
  38. data/samples/client_blocking(reading).rb +30 -0
  39. data/samples/client_blocking(writing).rb +18 -0
  40. data/samples/getting_started.rb +49 -0
  41. data/samples/test_client.rb +70 -0
  42. metadata +127 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 86a0102b3c815ba560bbd7641cea4e63b2fb8c4c
4
+ data.tar.gz: e12811ac533098c5d70803bafaaa4a0dbbde6811
5
+ SHA512:
6
+ metadata.gz: eed9602dc4aa8560cfadccac8c1d93d29aa18f8ac87daaa4656b90a8c92189e4978a2569152d7d865683897a0f13bfceba2dc6eea7cca6b7f0e0a89d98dc8937
7
+ data.tar.gz: e842c88d1a4768b4427988130b53bd24a35525554ca5162542b5bd1348a79d68b8dd18f545bef42e45b47a45b6a4a976b3b9ab3397d95f0d2f047c73b32bd97f
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ *.gem
12
+ *.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at p-goudet@ruby-dev.jp. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in PahoMqttRuby.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,322 @@
1
+ # PahoMqtt
2
+
3
+ The following file describes the Paho Mqtt client API for the ruby programming language. It enables applications to connect to an MQTT message broker threw the [MQTT](http://mqtt.org/) protocol (versions 3.1.1). MQTT is a lightweight protocol designed for IoT/M2M. A Mqtt client can connect to a message broker in order to publish and received data contained in short messages. The messages are exchanged on topics where the client has to subscribe for receiving message.
4
+
5
+ ## Contents
6
+ * [Installation](#installation)
7
+ * [Usage](#usage)
8
+ * [Getting started](#getting-started)
9
+ * [Client](#client)
10
+ * [Initialization](#initialization)
11
+ * [Client's parameters](#clients-parameter)
12
+ * [Subscription](#subscription)
13
+ * [Publishing](#publishing)
14
+ * [Connection configuration](#connection-configuration)
15
+ * [Unencrypted mode](#unencrypted-mode)
16
+ * [Encrypted mode](#encrypted-mode)
17
+ * [Persistence](#persistence)
18
+ * [Foreground and Daemon](#foreground-and-daemon)
19
+ * [Control loops](#control-loops)
20
+ * [Reading loop](#reading-loop)
21
+ * [Writing loop](#writing-loop)
22
+ * [Miscellaneous loop](#miscellaneous-loop)
23
+ * [Handlers and Callbacks](#handlers-and-callbacks)
24
+ * [Handlers](#handlers)
25
+ * [Callbacks](#callbacks)
26
+ * [Mosquitto (message broker)](#mosquitto-message-broker)
27
+ * [Thanks](#thanks)
28
+
29
+ ## Installation
30
+
31
+ Add this line to your application's Gemfile:
32
+
33
+ ```ruby
34
+ gem 'paho-mqtt'
35
+ ```
36
+
37
+ And then execute:
38
+
39
+ $ bundle
40
+
41
+ Or install it yourself as:
42
+
43
+ $ gem install paho-mqtt
44
+
45
+ ## Usage
46
+
47
+ ### Getting started
48
+ The following samples files cover the main features of the client:
49
+ ```ruby
50
+ require 'paho-mqtt'
51
+
52
+ ### Create a simple client with default attributes
53
+ client = PahoMqtt::Client.new
54
+
55
+ ### Register a callback on message event to display messages
56
+ message_counter = 0
57
+ client.on_message do |message|
58
+ puts "Message recieved on topic: #{message.topic}\n>>> #{message.payload}"
59
+ message_counter += 1
60
+ end
61
+
62
+ ### Register a callback on suback to assert the subcription
63
+ waiting_suback = true
64
+ client.on_suback do
65
+ waiting_suback = false
66
+ puts "Subscribed"
67
+ end
68
+
69
+ ### Register a callback for puback event when receiving a puback
70
+ waiting_puback = true
71
+ client.on_puback do
72
+ waiting_puback = false
73
+ puts "Message Acknowledged"
74
+ end
75
+
76
+ ### Connect to the eclipse test server on port 1883 (Unencrypted mode)
77
+ client.connect('iot.eclipse.org', 1883)
78
+
79
+ ### Subscribe to a topic
80
+ client.subscribe(['/paho/ruby/test', 2])
81
+
82
+ ### Waiting for the suback answer and excute the previously set on_suback callback
83
+ while waiting_suback do
84
+ sleep 0.001
85
+ end
86
+
87
+ ### Publlish a message on the topic "/paho/ruby/test" with "retain == false" and "qos == 1"
88
+ client.publish("/paho/ruby/test", "Hello there!", false, 1)
89
+
90
+ while waiting_puback do
91
+ sleep 0.001
92
+ end
93
+
94
+ ### Waiting to assert that the message is displayed by on_message callback
95
+ sleep 1
96
+
97
+ ### Calling an explicit disconnect
98
+ client.disconnect
99
+ ```
100
+
101
+ ## Client
102
+ ### Initialization
103
+ The client may be initialized without paramaeters or with a hash of parameters. The list of client's accessor is details in the next parts. A client id would be generated if not provided, a default port would be also set (8883 if ssl set, else 1883).
104
+ ```ruby
105
+ client = PahoMqtt::Client.new
106
+ # Or
107
+ client = PahoMqtt::Client.new({host: "iot.eclispe.org", port: 1883, ssl: false})
108
+ ```
109
+
110
+ ### Client's parameters
111
+ The client has many accessors which help to configure the client depending on user's need. The different accessors could be splited in four roles, connection setup, last will setup, time-out setup and callback setup.
112
+ Connection setup:
113
+ ```
114
+ * host : The endpoint where the client would try to connect (defaut "")
115
+ * port : The port on the remote host where the socket would try to connect (default 1883)
116
+ * mqtt_version : The version of MQTT protocol used to communication (default 3.1.1)
117
+ * clean_session : If set to false, ask the message broker to try to restore the previous session (default true)
118
+ * persistent : Keep the client connected even after keep alive timer run out, automatically try to reconnect on failure (default false)
119
+ * reconnect_limit : If persistent mode is enabled, the maximum reconnect attempt (default 3)
120
+ * reconnect_delay : If persistent mode is enabled, the delay between to reconnection attempt in second (default 5)
121
+ * client_id : The identifier of the client (default nil)
122
+ * username : The username if the server require authentication (default nil)
123
+ * password : The password of the user if authentication required (default nil)
124
+ * ssl : Requiring the encryption for the communication (default false)
125
+ ```
126
+
127
+ Last Will:
128
+ ```
129
+ * will_topic : The topic where to publish the last will (default nil)
130
+ * will_payload : The message of the last will (default "")
131
+ * will_qos : The qos of the last will (default 0)
132
+ * will_retain : The retain status of the last will (default false)
133
+ ```
134
+
135
+ Timers:
136
+ ```
137
+ * keep_alive : The reference timer after which the client should decide to keep the connection alive or not
138
+ * ack_timeout : The timer after which a non-acknowledged packet is considered as a failure
139
+ ```
140
+
141
+ The description of the callback accessor is detailed in the section dedicated to the callbacks. The client also have three read only attributes which provide information on the client state.
142
+ ```
143
+ * registered_callback : The list of topics where callback have been registred which the associated callback
144
+ * subscribed_topics : The list of the topics where the client is currentely receiving publish.
145
+ * connection_state : The current state of the connection between the message broker and the client
146
+ ```
147
+
148
+ ### Subscription
149
+ In order to read a message sent on a topic, the client should subscribe to this topic. The client enables to subscribe to several topics in the same subscribe request. The subscription could also be done by using a wild-card, see more details on [MQTT protocol specifications](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html). Each topic is subscribed with a maximum qos level, only message with a qos level lower or equal to this value would be forwarded to the client. The subscribe command accepts one or several pair, each pair is composed by the topic (or wild-card) and the maximum qos level.
150
+ ```ruby
151
+ ### Subscribe to two topics with maximum qos associated
152
+ client.subscribe(["/foo/bar", 1], ["/foo/foo/", 2])
153
+ ```
154
+
155
+ The subscription is persistent, in case of an unexpected disconnecting, the current subscription state is saved and a new subscribe request is sent to the message broker.
156
+
157
+ ### Publishing
158
+ User data could be sent to the message broker with the publish operation. A publish operation requires a topic, and payload (user data), two other parameters may be configured, retain and qos. The retain flag tell to the message broker to keep the current publish packet, see the [MQTT protocol specifications](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html) for more details about retain. The qos enable different levels of control on the transmission of publish package. The PahoMqtt client supports the three levels of qos (0, 1 and 2), see the [MQTT protocol specifications](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html) for qos level details. The default retain value is False and the qos level is 0.
159
+ ```ruby
160
+ ### Publish to the topics "/foo/bar", with qos = 1 and no retain
161
+ client.publish("/foo/bar", "Hello Wourld!", false, 1)
162
+ ```
163
+
164
+ ## Connection configuration
165
+ ### Unencrypted mode
166
+ The most simple connection way is the unencrypted mode. All data would be sent clearly to the message broker, also it might not be safe for sensitive data. The connect method may set up or override some parameters of the client, the host, the port, the keep_alive timer, the persistence mode and blocking mode.
167
+ ```ruby
168
+ ### Simply connect to the message broker with default value or pre-set value
169
+ client.connect
170
+ # Or
171
+ ### Connect to the message broker with all parameter
172
+ client.connect("iot.eclipse.org", 1883, client.keep_alive, client.persistent, client.blocking)
173
+ ```
174
+
175
+ ### Encrypted mode
176
+ The client supports the encrypted connection threw tls-ssl socket. In order to use encrypted mode, the ssl flag of the client should be set to True.
177
+ ``` ruby
178
+ ### Set the encryption mode to True
179
+ client.ssl = true
180
+ ### Configure the user SSL key and the certificate
181
+ client.config_ssl_context(certificate_path, key_path)
182
+ client.connect("test.mosquitto.org", 8883)
183
+ ### Or if rootCA is needed
184
+ client.config_ssl_context(certificate_path, key_path, rootCA_path)
185
+ client.connect("test.mosquitto.org", 8884)
186
+ ```
187
+
188
+ ### Persistence
189
+ The client holds a keep_alive timer is the reference time that the connection should be held. The timer is reset every time a new valid packet is received from the message broker. The persistence flag, when set to True, enables the client to be more independent from the keep_alive timer. Just before the keep_alive run out, the client sends a ping request to tell to the message broker that the connection should be kept. The persistent mode also enables the client to automatically reconnect to the message broker after an unexpected failure.
190
+ When the client's persistence flag is set to False, it just simply disconnects when the keep_alive timer runs out.
191
+ ```ruby
192
+ ### This will connect to the message broker, keep connected and automatically reconnect on failure
193
+ client.connect('iot.eclipse.org', 1883, client.keep_alive, true, client.blocking)
194
+ #Or
195
+ ### This only connect to the message broker, disconnect after keep_alive or on failure
196
+ client.connect('iot.eclipse.org', 1883, client.keep_alive, false, client.blocking)
197
+ ```
198
+ The client has two attributes `@reconnect_limit` and `@reconnect_delay` which configure the reconnection process. `@reconnection_limit` is the maximum reconnection attempt that a client could try and `@reconnection_delay` is the delay that the client waits between two reconnection attempt. Setting the `@reconnect_limit` to -1 would run the reconnection process forever.
199
+
200
+ ### Foreground and Daemon
201
+ The client could be connected to the message broker using the main thread in foreground or as a daemon in a separate thread. The default mode is daemon mode, the daemon would run in the background the read/write operation as well as the control of the timers. If the client is connected using the main thread, all control operations are left to the user, using the different control loops. There are four different loop roles is detailed in the next part.
202
+
203
+ ```ruby
204
+ ### Connect to the message broker executing the mqtt_loop (socket reading/writing) in the background
205
+ client.connect('iot.eclipse.org', 1883, client.keep_alive, client.persistence, true)
206
+ #Or
207
+ ### This only connect to the message broker, nothing more
208
+ client.connect('iot.eclipse.org', 1883, client.keep_alive, client.persistence, false)
209
+ ```
210
+
211
+ ## Control loops
212
+ /!\ The control loops should not be used in a daemon mode.
213
+ They are automatically run in separate thread and execute the necessary operations for reading, writing and checking the connection state.
214
+
215
+ ### Reading loop
216
+ The reading loop provides access to the socket in a reading mode. Periodically, the socket would be inspected to try to find a mqtt packet. The read loop accepts a parameter, which is the number of loop's turn. The default value is five turns.
217
+ The default value is defined in the PahoMqtt module as the constant PahoMqtt::MAX_READ, another module constant could be modified to control the socket inspection period. The referring constant is SELECT_TIMEOUT (PahoMqtt::SELECT_TIMEOUT) and its default value is 0.
218
+ ```ruby
219
+ ### Trying to read 'max_packet' packets from the client socket
220
+ client.loop_read(max_packet)
221
+ ```
222
+
223
+ ### Writing loop
224
+ The writing loop send the packets which have previously been stacked by MQTT operations. This loop also accepts a parameter, which is the maximum packets number that could be written as the MAX_WRITING constant (PahoMqtt::MAX_WRITING). The writing loop exit if the maximum number of packet have been sent or if the waiting packet queue is empty.
225
+ ```ruby
226
+ ### Writing 'max_packet' packets to the client socket
227
+ client.loop_write(max_packet)
228
+ ```
229
+
230
+ ### Miscellaneous loop
231
+ The misc loop performs different control operations, modifying the packets states and the connection state. The misc loop parses the different queue of packet that are waiting for an acknowledgement. If the ack_timeout of a packet had run out, the packet is re-sent. The size of the different waiting queues is defined as module constants. This loop also asserts that the connection is still available by checking the keep_alive timer.
232
+ ```ruby
233
+ ### Perfom control operations on packets queues and connection
234
+ client.loop_misc
235
+ ```
236
+
237
+ ## Handlers and Callbacks
238
+ ### Handlers
239
+ When a packet is received and inspected, an appropriate handler is called. The handler performs different control operation such as update the connection state, update the subscribed topics, and send publish control packets. Each packet has a specific handler, except the pingreq/pingresp packet. Before returning the handler executes a callback, if the user has configured one for this type of packet. The publish handler may execute sequentially two callbacks. One callback for the reception of a generic publish packet and another one, if the user has configured a callback for the topic where the publish packet has been received.
240
+
241
+ ### Callbacks
242
+ The callbacks could be defined in a three different ways, as block, as Proc or as Lambda. The callback has access to the packet which triggered it.
243
+ ```ruby
244
+ ### Register a callback trigger on the reception of a CONNACK packet
245
+ client.on_connack = proc { puts "Successfully Connected" }
246
+
247
+ ### Register a callback trigger on the reception of PUBLISH packet
248
+ client.on_message do |packet|
249
+ puts "New message received on topic: #{packet.topic}\n>>>#{packet.payload}"
250
+ end
251
+ ```
252
+
253
+ A callback could be configured for every specific topics. The list of topics where a callbacks have been registered could be read at any time, threw the registered_callback variable. The following example details how to manage callbacks for specific topics.
254
+ ```ruby
255
+ ### Add a callback for every message received on /foo/bar
256
+ specific_callback = lambda { |packet| puts "Specific callback for #{packet.topic}" }
257
+ client.add_topic_callback("/foo/bar", specific_callback)
258
+ # Or
259
+ client.add_topic_callback("/foo/bar") do |packet|
260
+ puts "Specific callback for #{packet.topic}"
261
+ end
262
+
263
+ ### To remove a callback form a topic
264
+ client.remove_topic_callback("/foo/bar")
265
+ ```
266
+
267
+ ## Mosquitto (message broker)
268
+ Mosquitto is a message broker support by Eclipse, which is quite easy-going. In order to run spec or samples files, a message broker is needed. Mosquitto enable to run locally a message broker, it could be configured with the mosquitto.conf files.
269
+ ### Install mosquitto
270
+ #### OSX (homebrew)
271
+ ```
272
+ $ brew install mosquitto
273
+ ```
274
+ ### Run mosquitto
275
+ #### Default mode
276
+ The default mode of mosquitto is unencrypted, listening on the port 1883.
277
+ ```
278
+ $ mosquitto
279
+ ```
280
+
281
+ #### Encrypted mode
282
+ In order to successfully pass the spec, or for testing in encrypted mode, some configurations are needed on mosquitto. Private keys and certificates should be set on both client side and server side. The [mosquitto-tls](https://mosquitto.org/man/mosquitto-tls-7.html) page might help you create all the required credentials. Once the credentials are created, the mosquitto's config files should be updated as following.
283
+
284
+ ```
285
+ $ cp mosquitto.conf samples-mosquitto.conf
286
+ $ nano mosquitto.conf
287
+ ```
288
+
289
+ The following file enables the broker to support the unencrypted mode (default) on port 1883, and the encrypted mode on port 8883. Update the path variable with the file's location on your environment.
290
+ ```
291
+ ### mosquitto.conf
292
+ # =================================================================
293
+ # General configuration
294
+ # =================================================================
295
+ .
296
+ .
297
+ .
298
+ # =================================================================
299
+ # Extra listeners
300
+ # =================================================================
301
+ .
302
+ .
303
+ listener 8883
304
+ .
305
+ .
306
+ cafile "Path to the certificate authorithy certificate file"
307
+ certfile "Path to the server certificate file"
308
+ keyfile "Path to the server private keys file"
309
+ .
310
+ .
311
+ .
312
+ ```
313
+ Finally run the server with the updated configuration file.
314
+
315
+ ```
316
+ $ mosquitto -c mosquitto.conf
317
+ ```
318
+
319
+ See [Mosquitto message broker page](https://mosquitto.org/) for more details.
320
+
321
+ ## Thanks
322
+ Special thanks to [Nicholas Humfrey](https://github.com/njh) for providing a great help with the packet serializer/deserializer.
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,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'paho_mqtt/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "apiotics-paho-mqtt"
8
+ spec.version = PahoMqtt::VERSION
9
+ spec.authors = ["Mac Dougherty", "Pierre Goudet"]
10
+ spec.email = ["mac@apiotics.com"]
11
+
12
+ spec.summary = %q{A simple mqtt client gem}
13
+ spec.description = %q{A simple mqtt client gem}
14
+ spec.homepage = "https://portal.apiotics.com"
15
+ spec.license = "MIT"
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.11"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "paho-mqtt"
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
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/paho-mqtt.rb ADDED
@@ -0,0 +1,165 @@
1
+ # Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
2
+ #
3
+ # All rights reserved. This program and the accompanying materials
4
+ # are made available under the terms of the Eclipse Public License v1.0
5
+ # and Eclipse Distribution License v1.0 which accompany this distribution.
6
+ #
7
+ # The Eclipse Public License is available at
8
+ # https://eclipse.org/org/documents/epl-v10.php.
9
+ # and the Eclipse Distribution License is available at
10
+ # https://eclipse.org/org/documents/edl-v10.php.
11
+ #
12
+ # Contributors:
13
+ # Pierre Goudet - initial committer
14
+
15
+ require "paho_mqtt/version"
16
+ require "paho_mqtt/client"
17
+ require "paho_mqtt/exception"
18
+ require "paho_mqtt/packet"
19
+ require 'logger'
20
+
21
+ module PahoMqtt
22
+ extend self
23
+ attr_accessor :logger
24
+
25
+ # Default connection setup
26
+ DEFAULT_SSL_PORT = 8883
27
+ DEFAULT_PORT = 1883
28
+ SELECT_TIMEOUT = 0
29
+ LOOP_TEMPO = 0.005
30
+
31
+ # MAX size of queue
32
+ MAX_SUBACK = 10
33
+ MAX_UNSUBACK = 10
34
+ MAX_READ = 50
35
+ MAX_PUBACK = 100
36
+ MAX_PUBREC = 100
37
+ MAX_PUBREL = 100
38
+ MAX_PUBCOMP = 100
39
+ MAX_WRITING = MAX_PUBACK + MAX_PUBREC + MAX_PUBREL + MAX_PUBCOMP
40
+
41
+ # Connection states values
42
+ MQTT_CS_NEW = 0
43
+ MQTT_CS_CONNECTED = 1
44
+ MQTT_CS_DISCONNECT = 2
45
+
46
+ # Error values
47
+ MQTT_ERR_SUCCESS = 0
48
+ MQTT_ERR_FAIL = 1
49
+
50
+ PACKET_TYPES = [
51
+ nil,
52
+ PahoMqtt::Packet::Connect,
53
+ PahoMqtt::Packet::Connack,
54
+ PahoMqtt::Packet::Publish,
55
+ PahoMqtt::Packet::Puback,
56
+ PahoMqtt::Packet::Pubrec,
57
+ PahoMqtt::Packet::Pubrel,
58
+ PahoMqtt::Packet::Pubcomp,
59
+ PahoMqtt::Packet::Subscribe,
60
+ PahoMqtt::Packet::Suback,
61
+ PahoMqtt::Packet::Unsubscribe,
62
+ PahoMqtt::Packet::Unsuback,
63
+ PahoMqtt::Packet::Pingreq,
64
+ PahoMqtt::Packet::Pingresp,
65
+ PahoMqtt::Packet::Disconnect,
66
+ nil
67
+ ]
68
+
69
+ CONNACK_ERROR_MESSAGE = {
70
+ 0x02 => "Client Identifier is correct but not allowed by remote server.",
71
+ 0x03 => "Connection established but MQTT service unvailable on remote server.",
72
+ 0x04 => "User name or user password is malformed.",
73
+ 0x05 => "Client is not authorized to connect to the server."
74
+ }
75
+
76
+ CLIENT_ATTR_DEFAULTS = {
77
+ :host => "",
78
+ :port => nil,
79
+ :mqtt_version => '3.1.1',
80
+ :clean_session => true,
81
+ :persistent => false,
82
+ :blocking => false,
83
+ :client_id => nil,
84
+ :username => nil,
85
+ :password => nil,
86
+ :ssl => false,
87
+ :will_topic => nil,
88
+ :will_payload => nil,
89
+ :will_qos => 0,
90
+ :will_retain => false,
91
+ :keep_alive => 60,
92
+ :ack_timeout => 5,
93
+ :on_connack => nil,
94
+ :on_suback => nil,
95
+ :on_unsuback => nil,
96
+ :on_puback => nil,
97
+ :on_pubrel => nil,
98
+ :on_pubrec => nil,
99
+ :on_pubcomp => nil,
100
+ :on_message => nil,
101
+ }
102
+
103
+ Thread.abort_on_exception = true
104
+
105
+ def logger=(logger_path)
106
+ file = File.open(logger_path, "a+")
107
+ file.sync = true
108
+ log_file = Logger.new(file)
109
+ log_file.level = Logger::DEBUG
110
+ @logger = log_file
111
+ end
112
+
113
+ def logger
114
+ @logger
115
+ end
116
+
117
+ def logger?
118
+ @logger.is_a?(Logger)
119
+ end
120
+
121
+ def match_filter(topics, filters)
122
+ check_topics(topics, filters)
123
+ index = 0
124
+ rc = false
125
+ topic = topics.split('/')
126
+ filter = filters.split('/')
127
+ while index < [topic.length, filter.length].max do
128
+ if is_end?(topic[index], filter[index])
129
+ break
130
+ elsif is_wildcard?(filter[index])
131
+ rc = index == (filter.length - 1)
132
+ break
133
+ elsif keep_running?(filter[index], topic[index])
134
+ index = index + 1
135
+ else
136
+ break
137
+ end
138
+ end
139
+ is_matching?(rc, topic.length, filter.length, index)
140
+ end
141
+
142
+ def keep_running?(filter_part, topic_part)
143
+ filter_part == topic_part || filter_part == '+'
144
+ end
145
+
146
+ def is_wildcard?(filter_part)
147
+ filter_part == '#'
148
+ end
149
+
150
+ def is_end?(topic_part, filter_part)
151
+ topic_part.nil? || filter_part.nil?
152
+ end
153
+
154
+ def is_matching?(rc, topic_length, filter_length, index)
155
+ rc || index == [topic_length, filter_length].max
156
+ end
157
+
158
+ def check_topics(topics, filters)
159
+ if topics.is_a?(String) && filters.is_a?(String)
160
+ else
161
+ @logger.error("Topics or Wildcards are not found as String.") if logger?
162
+ raise ArgumentError
163
+ end
164
+ end
165
+ end