mqtt 0.2.0 → 0.3.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.
data/NEWS.md CHANGED
@@ -1,15 +1,35 @@
1
1
  Ruby MQTT NEWS
2
2
  ==============
3
3
 
4
+ Ruby MQTT Version 0.3.0 (2014-08-26)
5
+ ------------------------------------
6
+
7
+ * Added support for MQTT protocol version 3.1.1
8
+ * Renamed a number of methods/attributes:
9
+ - Renamed ```:granted_qos``` to ```:return_codes```
10
+ - Renamed ```:remote_port``` to ```:port```
11
+ - Renamed ```:remote_host``` to ```:host```
12
+ - Renamed ```:message_id``` to ```:id```
13
+ - Renamed ```:protocol_version``` to ```:protocol_level```
14
+ - Renamed ```MQTT_SERVER``` environment variable to ```MQTT_SERVER```
15
+ * Added more checks to ensure that the 3.1.1 protocol specs are adhered to
16
+ * Added a Library Overview section to the README
17
+ * Added links to the protocol specification to README
18
+ * Improvements to the YARD API documentation
19
+ * Don't display payload in inspect if it contains non-visible ASCII characters
20
+ * Upgraded to rspec 3
21
+ * Various minor bug fixes and corrections
22
+
23
+
4
24
  Ruby MQTT Version 0.2.0 (2014-04-02)
5
25
  ------------------------------------
6
26
 
7
27
  * Added SSL/TLS support
8
28
  * Added support for passing connection details using a URI
9
- * Added support for using the MQTT_BROKER environment variable
29
+ * Added support for using the ```MQTT_BROKER``` environment variable
10
30
  * Allow passing array of topics to Client#unsubscribe
11
31
  * Allow more combinations of arguments to be passed to a new Client
12
- * No longer defaults to ‘localhost’ if there is no broker configured
32
+ * No longer defaults to ‘localhost’ if there is no server configured
13
33
  * Fixed more 'unused variable' warnings
14
34
  * Documentation improvements
15
35
  * Ruby 1.8 fixes
data/README.md CHANGED
@@ -1,19 +1,35 @@
1
+ [![Build Status](https://travis-ci.org/njh/ruby-mqtt.svg)](https://travis-ci.org/njh/ruby-mqtt)
2
+
1
3
  ruby-mqtt
2
4
  =========
3
5
 
4
- Pure Ruby gem that implements the MQTT protocol, a lightweight protocol for publish/subscribe messaging.
6
+ Pure Ruby gem that implements the [MQTT] protocol, a lightweight protocol for publish/subscribe messaging.
7
+
8
+
9
+ Table of Contents
10
+ -----------------
11
+ * [Installation](#installation)
12
+ * [Quick Start](#quick-start)
13
+ * [Library Overview](#library-overview)
14
+ * [Resources](#resources)
15
+ * [License](#license)
16
+ * [Contact](#contact)
5
17
 
6
18
 
7
- Installing
8
- ----------
19
+ Installation
20
+ ------------
9
21
 
10
- You may get the latest stable version from Rubygems:
22
+ You may get the latest stable version from [Rubygems]:
11
23
 
12
24
  $ gem install mqtt
13
25
 
26
+ Alternatively, to use a development snapshot from GitHub using [Bundler]:
14
27
 
15
- Synopsis
16
- --------
28
+ gem 'mqtt', :git => 'https://github.com/njh/ruby-mqtt.git'
29
+
30
+
31
+ Quick Start
32
+ -----------
17
33
 
18
34
  require 'rubygems'
19
35
  require 'mqtt'
@@ -32,18 +48,106 @@ Synopsis
32
48
  end
33
49
 
34
50
 
51
+
52
+ Library Overview
53
+ ----------------
54
+
55
+ ### Connecting ###
56
+
57
+ A new client connection can be created by passing either a [MQTT URI], a host and port or by passing a hash of attributes.
58
+
59
+ client = MQTT::Client.connect('mqtt://myserver.example.com')
60
+ client = MQTT::Client.connect('mqtts://user:pass@myserver.example.com')
61
+ client = MQTT::Client.connect('myserver.example.com')
62
+ client = MQTT::Client.connect('myserver.example.com', 18830)
63
+ client = MQTT::Client.connect(:host => 'myserver.example.com', :port => 1883 ... )
64
+
65
+ TLS/SSL is not enabled by default, to enabled it, pass ```:ssl => true```:
66
+
67
+ client = MQTT::Client.connect(
68
+ :host => 'test.mosquitto.org',
69
+ :port => 8883
70
+ :ssl => true
71
+ )
72
+
73
+ Alternatively you can create a new Client object and then configure it by setting attributes. This example shows setting up client certificate based authentication:
74
+
75
+ client = MQTT::Client.new
76
+ client.host = 'myserver.example.com'
77
+ client.ssl = true
78
+ client.cert_file = path_to('client.pem')
79
+ client.key_file = path_to('client.key')
80
+ client.ca_file = path_to('root-ca.pem')
81
+ client.connect()
82
+
83
+ The connection can either be made without the use of a block:
84
+
85
+ client = MQTT::Client.connect('test.mosquitto.org')
86
+ # perform operations
87
+ client.disconnect()
88
+
89
+ Or, if using a block, with an implicit disconnection at the end of the block.
90
+
91
+ MQTT::Client.connect('test.mosquitto.org') do |client|
92
+ # perform operations
93
+ end
94
+
95
+ For more information, see and list of attributes for the [MQTT::Client] class and the [MQTT::Client.connect] method.
96
+
97
+
98
+ ### Publishing ###
99
+
100
+ To send a message to a topic, use the ```publish``` method:
101
+
102
+ client.publish(topic, payload, retain=false)
103
+
104
+ The method will return once the message has been sent to the MQTT server.
105
+
106
+ For more information see the [MQTT::Client#publish] method.
107
+
108
+
109
+ ### Subscribing ###
110
+
111
+ You can send a subscription request to the MQTT server using the subscribe method. One or more [Topic Filters] may be passed in:
112
+
113
+ client.subscribe( 'topic1' )
114
+ client.subscribe( 'topic1', 'topic2' )
115
+ client.subscribe( 'foo/#' )
116
+
117
+ For more information see the [MQTT::Client#subscribe] method.
118
+
119
+
120
+ ### Receiving Messages ###
121
+
122
+ To receive a message, use the get method. This method will block until a message is available. The topic is the name of the topic the message was sent to. The message is a string:
123
+
124
+ topic,message = client.get
125
+
126
+ Alternatively, you can give the get method a block, which will be called for every message received and loop forever:
127
+
128
+ client.get do |topic,message|
129
+ # Block is executed for every message received
130
+ end
131
+
132
+ For more information see the [MQTT::Client#get] method.
133
+
134
+
135
+
35
136
  Limitations
36
137
  -----------
37
138
 
38
139
  * Only QOS 0 currently supported
140
+ * Automatic re-connects to the server are not supported
39
141
 
40
142
 
41
143
  Resources
42
144
  ---------
43
145
 
146
+ * API Documentation: http://rubydoc.info/gems/mqtt
147
+ * Protocol Specification v3.1.1: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html
148
+ * Protocol Specification v3.1: http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html
44
149
  * MQTT Homepage: http://www.mqtt.org/
45
150
  * GitHub Project: http://github.com/njh/ruby-mqtt
46
- * API Documentation: http://rubydoc.info/gems/mqtt/frames
47
151
 
48
152
 
49
153
  License
@@ -59,3 +163,18 @@ Contact
59
163
  * Author: Nicholas J Humfrey
60
164
  * Email: njh@aelius.com
61
165
  * Home Page: http://www.aelius.com/njh/
166
+
167
+
168
+
169
+ [MQTT]: http://www.mqtt.org/
170
+ [Rubygems]: http://rubygems.org/
171
+ [Bundler]: http://bundler.io/
172
+ [MQTT URI]: https://github.com/mqtt/mqtt.github.io/wiki/URI-Scheme
173
+ [Topic Filters]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html#_Toc388534397
174
+
175
+ [MQTT::Client]: http://rubydoc.info/gems/mqtt/MQTT/Client#instance_attr_details
176
+ [MQTT::Client.connect]: http://rubydoc.info/gems/mqtt/MQTT/Client.connect
177
+ [MQTT::Client#publish]: http://rubydoc.info/gems/mqtt/MQTT/Client:publish
178
+ [MQTT::Client#subscribe]: http://rubydoc.info/gems/mqtt/MQTT/Client:subscribe
179
+ [MQTT::Client#get]: http://rubydoc.info/gems/mqtt/MQTT/Client:get
180
+
@@ -14,22 +14,22 @@ end
14
14
 
15
15
  module MQTT
16
16
 
17
- # Default port number for unencrypted connections
17
+ # Default port number for unencrypted connections
18
18
  DEFAULT_PORT = 1883
19
-
20
- # Default port number for TLS/SSL encrypted connections
19
+
20
+ # Default port number for TLS/SSL encrypted connections
21
21
  DEFAULT_SSL_PORT = 8883
22
22
 
23
23
  # Super-class for other MQTT related exceptions
24
24
  class Exception < Exception
25
25
  end
26
26
 
27
- # A ProtocolException will be raised if there is a
27
+ # A ProtocolException will be raised if there is a
28
28
  # problem with data received from a remote host
29
29
  class ProtocolException < MQTT::Exception
30
30
  end
31
31
 
32
- # A NotConnectedException will be raised when trying to
32
+ # A NotConnectedException will be raised when trying to
33
33
  # perform a function but no connection has been
34
34
  # established
35
35
  class NotConnectedException < MQTT::Exception
@@ -2,52 +2,55 @@ autoload :OpenSSL, 'openssl'
2
2
  autoload :URI, 'uri'
3
3
 
4
4
 
5
- # Client class for talking to an MQTT broker
5
+ # Client class for talking to an MQTT server
6
6
  class MQTT::Client
7
- # Hostname of the remote broker
8
- attr_accessor :remote_host
7
+ # Hostname of the remote server
8
+ attr_accessor :host
9
9
 
10
- # Port number of the remote broker
11
- attr_accessor :remote_port
10
+ # Port number of the remote server
11
+ attr_accessor :port
12
+
13
+ # The version number of the MQTT protocol to use (default 3.1.0)
14
+ attr_accessor :version
12
15
 
13
16
  # Set to true to enable SSL/TLS encrypted communication
14
17
  #
15
18
  # Set to a symbol to use a specific variant of SSL/TLS.
16
- # Allowed values include:
17
- #
19
+ # Allowed values include:
20
+ #
18
21
  # @example Using TLS 1.0
19
22
  # client = Client.new('mqtt.example.com', :ssl => :TLSv1)
20
23
  # @see OpenSSL::SSL::SSLContext::METHODS
21
24
  attr_accessor :ssl
22
25
 
23
- # Time (in seconds) between pings to remote broker
26
+ # Time (in seconds) between pings to remote server (default is 15 seconds)
24
27
  attr_accessor :keep_alive
25
28
 
26
- # Set the 'Clean Session' flag when connecting?
29
+ # Set the 'Clean Session' flag when connecting? (default is true)
27
30
  attr_accessor :clean_session
28
31
 
29
32
  # Client Identifier
30
33
  attr_accessor :client_id
31
34
 
32
- # Number of seconds to wait for acknowledgement packets
35
+ # Number of seconds to wait for acknowledgement packets (default is 5 seconds)
33
36
  attr_accessor :ack_timeout
34
37
 
35
- # Username to authenticate to the broker with
38
+ # Username to authenticate to the server with
36
39
  attr_accessor :username
37
40
 
38
- # Password to authenticate to the broker with
41
+ # Password to authenticate to the server with
39
42
  attr_accessor :password
40
43
 
41
44
  # The topic that the Will message is published to
42
45
  attr_accessor :will_topic
43
46
 
44
- # Contents of message that is sent by broker when client disconnect
47
+ # Contents of message that is sent by server when client disconnect
45
48
  attr_accessor :will_payload
46
49
 
47
- # The QoS level of the will message sent by the broker
50
+ # The QoS level of the will message sent by the server
48
51
  attr_accessor :will_qos
49
52
 
50
- # If the Will message should be retain by the broker after it is sent
53
+ # If the Will message should be retain by the server after it is sent
51
54
  attr_accessor :will_retain
52
55
 
53
56
 
@@ -56,8 +59,9 @@ class MQTT::Client
56
59
 
57
60
  # Default attribute values
58
61
  ATTR_DEFAULTS = {
59
- :remote_host => nil,
60
- :remote_port => nil,
62
+ :host => nil,
63
+ :port => nil,
64
+ :version => '3.1.0',
61
65
  :keep_alive => 15,
62
66
  :clean_session => true,
63
67
  :client_id => nil,
@@ -89,7 +93,7 @@ class MQTT::Client
89
93
 
90
94
  # Generate a random client identifier
91
95
  # (using the characters 0-9 and a-z)
92
- def self.generate_client_id(prefix='ruby_', length=16)
96
+ def self.generate_client_id(prefix='ruby', length=16)
93
97
  str = prefix.dup
94
98
  length.times do
95
99
  num = rand(36)
@@ -113,7 +117,7 @@ class MQTT::Client
113
117
  # - a Hash containing attributes to be set on the new instance
114
118
  #
115
119
  # If no arguments are given then the method will look for a URI
116
- # in the MQTT_BROKER environment variable.
120
+ # in the MQTT_SERVER environment variable.
117
121
  #
118
122
  # Examples:
119
123
  # client = MQTT::Client.new
@@ -121,8 +125,8 @@ class MQTT::Client
121
125
  # client = MQTT::Client.new('mqtt://user:pass@myserver.example.com')
122
126
  # client = MQTT::Client.new('myserver.example.com')
123
127
  # client = MQTT::Client.new('myserver.example.com', 18830)
124
- # client = MQTT::Client.new(:remote_host => 'myserver.example.com')
125
- # client = MQTT::Client.new(:remote_host => 'myserver.example.com', :keep_alive => 30)
128
+ # client = MQTT::Client.new(:host => 'myserver.example.com')
129
+ # client = MQTT::Client.new(:host => 'myserver.example.com', :keep_alive => 30)
126
130
  #
127
131
  def initialize(*args)
128
132
  if args.last.is_a?(Hash)
@@ -132,8 +136,8 @@ class MQTT::Client
132
136
  end
133
137
 
134
138
  if args.length == 0
135
- if ENV['MQTT_BROKER']
136
- attr.merge!(parse_uri(ENV['MQTT_BROKER']))
139
+ if ENV['MQTT_SERVER']
140
+ attr.merge!(parse_uri(ENV['MQTT_SERVER']))
137
141
  end
138
142
  end
139
143
 
@@ -144,12 +148,12 @@ class MQTT::Client
144
148
  when %r|^mqtts?://|
145
149
  attr.merge!(parse_uri(args[0]))
146
150
  else
147
- attr.merge!(:remote_host => args[0])
151
+ attr.merge!(:host => args[0])
148
152
  end
149
153
  end
150
154
 
151
155
  if args.length >= 2
152
- attr.merge!(:remote_port => args[1])
156
+ attr.merge!(:port => args[1]) unless args[1].nil?
153
157
  end
154
158
 
155
159
  if args.length >= 3
@@ -162,12 +166,12 @@ class MQTT::Client
162
166
  end
163
167
 
164
168
  # Set a default port number
165
- if @remote_port.nil?
166
- @remote_port = @ssl ? MQTT::DEFAULT_SSL_PORT : MQTT::DEFAULT_PORT
169
+ if @port.nil?
170
+ @port = @ssl ? MQTT::DEFAULT_SSL_PORT : MQTT::DEFAULT_PORT
167
171
  end
168
172
 
169
173
  # Initialise private instance variables
170
- @message_id = 0
174
+ @packet_id = 0
171
175
  @last_pingreq = Time.now
172
176
  @last_pingresp = Time.now
173
177
  @socket = nil
@@ -201,8 +205,8 @@ class MQTT::Client
201
205
 
202
206
  # Set the Will for the client
203
207
  #
204
- # The will is a message that will be delivered by the broker when the client dies.
205
- # The Will must be set before establishing a connection to the broker
208
+ # The will is a message that will be delivered by the server when the client dies.
209
+ # The Will must be set before establishing a connection to the server
206
210
  def set_will(topic, payload, retain=false, qos=0)
207
211
  self.will_topic = topic
208
212
  self.will_payload = payload
@@ -210,7 +214,7 @@ class MQTT::Client
210
214
  self.will_qos = qos
211
215
  end
212
216
 
213
- # Connect to the MQTT broker
217
+ # Connect to the MQTT server
214
218
  # If a block is given, then yield to that block and then disconnect again.
215
219
  def connect(clientid=nil)
216
220
  unless clientid.nil?
@@ -219,19 +223,22 @@ class MQTT::Client
219
223
 
220
224
  if @client_id.nil? or @client_id.empty?
221
225
  if @clean_session
222
- @client_id = MQTT::Client.generate_client_id
226
+ if @version == '3.1.0'
227
+ # Empty client id is not allowed for version 3.1.0
228
+ @client_id = MQTT::Client.generate_client_id
229
+ end
223
230
  else
224
231
  raise 'Must provide a client_id if clean_session is set to false'
225
232
  end
226
233
  end
227
234
 
228
- if @remote_host.nil?
229
- raise 'No MQTT broker host set when attempting to connect'
235
+ if @host.nil?
236
+ raise 'No MQTT server host set when attempting to connect'
230
237
  end
231
238
 
232
239
  if not connected?
233
240
  # Create network socket
234
- tcp_socket = TCPSocket.new(@remote_host, @remote_port)
241
+ tcp_socket = TCPSocket.new(@host, @port)
235
242
 
236
243
  if @ssl
237
244
  # Set the protocol version
@@ -246,8 +253,9 @@ class MQTT::Client
246
253
  @socket = tcp_socket
247
254
  end
248
255
 
249
- # Protocol name and version
256
+ # Construct a connect packet
250
257
  packet = MQTT::Packet::Connect.new(
258
+ :version => @version,
251
259
  :clean_session => @clean_session,
252
260
  :keep_alive => @keep_alive,
253
261
  :client_id => @client_id,
@@ -281,8 +289,8 @@ class MQTT::Client
281
289
  end
282
290
  end
283
291
 
284
- # Disconnect from the MQTT broker.
285
- # If you don't want to say goodbye to the broker, set send_msg to false.
292
+ # Disconnect from the MQTT server.
293
+ # If you don't want to say goodbye to the server, set send_msg to false.
286
294
  def disconnect(send_msg=true)
287
295
  # Stop reading packets from the socket first
288
296
  @read_thread.kill if @read_thread and @read_thread.alive?
@@ -299,7 +307,7 @@ class MQTT::Client
299
307
  end
300
308
  end
301
309
 
302
- # Checks whether the client is connected to the broker.
310
+ # Checks whether the client is connected to the server.
303
311
  def connected?
304
312
  (not @socket.nil?) and (not @socket.closed?)
305
313
  end
@@ -314,21 +322,24 @@ class MQTT::Client
314
322
  @last_pingreq = Time.now
315
323
  end
316
324
 
317
- # Publish a message on a particular topic to the MQTT broker.
318
- def publish(topic, payload, retain=false, qos=0)
325
+ # Publish a message on a particular topic to the MQTT server.
326
+ def publish(topic, payload='', retain=false, qos=0)
327
+ raise ArgumentError.new("Topic name cannot be nil") if topic.nil?
328
+ raise ArgumentError.new("Topic name cannot be empty") if topic.empty?
329
+
319
330
  packet = MQTT::Packet::Publish.new(
331
+ :id => @packet_id.next,
320
332
  :qos => qos,
321
333
  :retain => retain,
322
334
  :topic => topic,
323
- :payload => payload,
324
- :message_id => @message_id.next
335
+ :payload => payload
325
336
  )
326
337
 
327
338
  # Send the packet
328
339
  send_packet(packet)
329
340
  end
330
341
 
331
- # Send a subscribe message for one or more topics on the MQTT broker.
342
+ # Send a subscribe message for one or more topics on the MQTT server.
332
343
  # The topics parameter should be one of the following:
333
344
  # * String: subscribe to one topic with QOS 0
334
345
  # * Array: subscribe to multiple topics with QOS 0
@@ -342,13 +353,13 @@ class MQTT::Client
342
353
  #
343
354
  def subscribe(*topics)
344
355
  packet = MQTT::Packet::Subscribe.new(
345
- :topics => topics,
346
- :message_id => @message_id.next
356
+ :id => @packet_id.next,
357
+ :topics => topics
347
358
  )
348
359
  send_packet(packet)
349
360
  end
350
361
 
351
- # Return the next message received from the MQTT broker.
362
+ # Return the next message received from the MQTT server.
352
363
  # An optional topic can be given to subscribe to.
353
364
  #
354
365
  # The method either returns the topic and message as an array:
@@ -376,7 +387,7 @@ class MQTT::Client
376
387
  end
377
388
  end
378
389
 
379
- # Return the next packet object received from the MQTT broker.
390
+ # Return the next packet object received from the MQTT server.
380
391
  # An optional topic can be given to subscribe to.
381
392
  #
382
393
  # The method either returns a single packet:
@@ -414,7 +425,7 @@ class MQTT::Client
414
425
  @read_queue.length
415
426
  end
416
427
 
417
- # Send a unsubscribe message for one or more topics on the MQTT broker
428
+ # Send a unsubscribe message for one or more topics on the MQTT server
418
429
  def unsubscribe(*topics)
419
430
  if topics.is_a?(Enumerable) and topics.count == 1
420
431
  topics = topics.first
@@ -422,14 +433,14 @@ class MQTT::Client
422
433
 
423
434
  packet = MQTT::Packet::Unsubscribe.new(
424
435
  :topics => topics,
425
- :message_id => @message_id.next
436
+ :id => @packet_id.next
426
437
  )
427
438
  send_packet(packet)
428
439
  end
429
440
 
430
441
  private
431
442
 
432
- # Try to read a packet from the broker
443
+ # Try to read a packet from the server
433
444
  # Also sends keep-alive ping packets.
434
445
  def receive_packet
435
446
  begin
@@ -470,7 +481,9 @@ private
470
481
  Timeout.timeout(@ack_timeout) do
471
482
  packet = MQTT::Packet.read(@socket)
472
483
  if packet.class != MQTT::Packet::Connack
473
- raise MQTT::ProtocolException.new("Response wan't a connection acknowledgement: #{packet.class}")
484
+ raise MQTT::ProtocolException.new(
485
+ "Response wasn't a connection acknowledgement: #{packet.class}"
486
+ )
474
487
  end
475
488
 
476
489
  # Check the return code
@@ -480,7 +493,7 @@ private
480
493
  end
481
494
  end
482
495
 
483
- # Send a packet to broker
496
+ # Send a packet to server
484
497
  def send_packet(data)
485
498
  # Throw exception if we aren't connected
486
499
  raise MQTT::NotConnectedException if not connected?
@@ -503,12 +516,36 @@ private
503
516
  end
504
517
 
505
518
  {
506
- :remote_host => uri.host,
507
- :remote_port => uri.port || nil,
519
+ :host => uri.host,
520
+ :port => uri.port || nil,
508
521
  :username => uri.user,
509
522
  :password => uri.password,
510
523
  :ssl => ssl
511
524
  }
512
525
  end
513
526
 
527
+
528
+ # ---- Deprecated attributes and methods ---- #
529
+ public
530
+
531
+ # @deprecated Please use {#host} instead
532
+ def remote_host
533
+ host
534
+ end
535
+
536
+ # @deprecated Please use {#host=} instead
537
+ def remote_host=(args)
538
+ self.host = args
539
+ end
540
+
541
+ # @deprecated Please use {#port} instead
542
+ def remote_port
543
+ port
544
+ end
545
+
546
+ # @deprecated Please use {#port=} instead
547
+ def remote_port=(args)
548
+ self.port = args
549
+ end
550
+
514
551
  end