mqtt 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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