mqtt 0.0.5 → 0.0.7
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/README +12 -25
- data/lib/mqtt.rb +1 -1
- data/lib/mqtt/client.rb +76 -18
- data/lib/mqtt/version.rb +1 -1
- data/spec/mqtt_client_spec.rb +85 -32
- data/spec/mqtt_proxy_spec.rb +1 -1
- data/spec/mqtt_version_spec.rb +3 -3
- metadata +4 -4
data/README
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
ruby-mqtt
|
2
2
|
=========
|
3
3
|
|
4
|
-
Pure Ruby gem that implements the MQTT (Message Queue Telemetry Transport) protocol,
|
4
|
+
Pure Ruby gem that implements the MQTT (Message Queue Telemetry Transport) protocol,
|
5
|
+
a lightweight protocol for publish/subscribe messaging.
|
5
6
|
|
6
7
|
|
7
8
|
Installing
|
8
9
|
----------
|
9
10
|
|
10
|
-
You may get the latest stable version from
|
11
|
+
You may get the latest stable version from Rubygems:
|
11
12
|
|
12
13
|
$ gem install mqtt
|
13
14
|
|
15
|
+
|
14
16
|
Synopsis
|
15
17
|
--------
|
16
18
|
|
@@ -18,37 +20,22 @@ Synopsis
|
|
18
20
|
require 'mqtt'
|
19
21
|
|
20
22
|
# Publish example
|
21
|
-
|
22
|
-
|
23
|
-
c.publish('topic','message')
|
23
|
+
MQTT::Client.connect('test.mosquitto.org') do |c|
|
24
|
+
c.publish('topic', 'message')
|
24
25
|
end
|
25
26
|
|
26
27
|
# Subscribe example
|
27
|
-
|
28
|
-
|
29
|
-
client.subscribe('test')
|
30
|
-
loop do
|
31
|
-
topic,message = client.get
|
28
|
+
MQTT::Client.connect('test.mosquitto.org') do |c|
|
29
|
+
c.get('test') do |topic,message|
|
32
30
|
puts "#{topic}: #{message}"
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
36
34
|
|
37
|
-
|
38
|
-
|
35
|
+
Limitations
|
36
|
+
-----------
|
39
37
|
|
40
|
-
*
|
41
|
-
* Process acknowledgement packets / Implement QOS 1 in client
|
42
|
-
* More validations of data/parameters
|
43
|
-
* More error checking and exception throwing
|
44
|
-
- Check that packet data is valid - don't blindly read values
|
45
|
-
- Subscribe and Unsubscribe packets should always have QOS of 1
|
46
|
-
* More examples
|
47
|
-
* Integration tests
|
48
|
-
* Refactor to add callbacks that are called from seperate thread
|
49
|
-
* Implement QOS Level 2 in client
|
50
|
-
* Prevent proxy from connecting to itself
|
51
|
-
* Add support for binding socket to specific local address
|
38
|
+
* Only QOS 0 currently supported
|
52
39
|
|
53
40
|
|
54
41
|
Resources
|
@@ -56,7 +43,7 @@ Resources
|
|
56
43
|
|
57
44
|
* MQTT Homepage: http://www.mqtt.org/
|
58
45
|
* GitHub Project: http://github.com/njh/ruby-mqtt
|
59
|
-
* Documentation: http://
|
46
|
+
* API Documentation: http://rubydoc.info/gems/mqtt/frames
|
60
47
|
|
61
48
|
|
62
49
|
Contact
|
data/lib/mqtt.rb
CHANGED
data/lib/mqtt/client.rb
CHANGED
@@ -16,23 +16,67 @@ class MQTT::Client
|
|
16
16
|
# Timeout between select polls (in seconds)
|
17
17
|
SELECT_TIMEOUT = 0.5
|
18
18
|
|
19
|
+
# Default attribute values
|
20
|
+
ATTR_DEFAULTS = {
|
21
|
+
:remote_host => MQTT::DEFAULT_HOST,
|
22
|
+
:remote_port => MQTT::DEFAULT_PORT,
|
23
|
+
:keep_alive => 15,
|
24
|
+
:clean_session => true,
|
25
|
+
:client_id => nil,
|
26
|
+
:ack_timeout => 5,
|
27
|
+
:username => nil,
|
28
|
+
:password => nil
|
29
|
+
}
|
30
|
+
|
31
|
+
# Create and connect a new MQTT Client
|
32
|
+
# Accepts the same arguments as creating a new client.
|
33
|
+
# If a block is given, then it will be executed before disconnecting again.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
# MQTT::Client.connect('myserver.example.com') do |client|
|
37
|
+
# # do stuff here
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
def self.connect(*args, &block)
|
41
|
+
client = MQTT::Client.new(*args)
|
42
|
+
client.connect(&block)
|
43
|
+
return client
|
44
|
+
end
|
45
|
+
|
19
46
|
# Create a new MQTT Client instance
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
47
|
+
#
|
48
|
+
# Examples:
|
49
|
+
# client = MQTT::Client.new('myserver.example.com')
|
50
|
+
# client = MQTT::Client.new('myserver.example.com', 18830)
|
51
|
+
# client = MQTT::Client.new(:remote_host => 'myserver.example.com')
|
52
|
+
# client = MQTT::Client.new(:remote_host => 'myserver.example.com', :keep_alive => 30)
|
53
|
+
#
|
54
|
+
def initialize(*args)
|
55
|
+
if args.count == 0
|
56
|
+
args = {}
|
57
|
+
elsif args.count == 1 and args[0].is_a?(Hash)
|
58
|
+
args = args[0]
|
59
|
+
elsif args.count == 1
|
60
|
+
args = {:remote_host => args[0]}
|
61
|
+
elsif args.count == 2
|
62
|
+
args = {:remote_host => args[0], :remote_port => args[1]}
|
63
|
+
else
|
64
|
+
raise ArgumentError, "Unsupported number of arguments"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Merge arguments with default values for attributes
|
68
|
+
ATTR_DEFAULTS.merge(args).each_pair do |k,v|
|
69
|
+
instance_variable_set("@#{k}", v)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Initialise private instance variables
|
26
73
|
@message_id = 0
|
27
|
-
@ack_timeout = 5
|
28
74
|
@last_pingreq = Time.now
|
29
75
|
@last_pingresp = Time.now
|
30
76
|
@socket = nil
|
31
77
|
@read_queue = Queue.new
|
32
78
|
@read_thread = nil
|
33
79
|
@write_semaphore = Mutex.new
|
34
|
-
@username = nil
|
35
|
-
@password = nil
|
36
80
|
end
|
37
81
|
|
38
82
|
# Connect to the MQTT broker
|
@@ -40,7 +84,7 @@ class MQTT::Client
|
|
40
84
|
def connect(clientid=nil)
|
41
85
|
if !clientid.nil?
|
42
86
|
@client_id = clientid
|
43
|
-
elsif clientid.nil?
|
87
|
+
elsif @clientid.nil?
|
44
88
|
@client_id = random_letters(16)
|
45
89
|
@clean_session = true
|
46
90
|
end
|
@@ -140,17 +184,31 @@ class MQTT::Client
|
|
140
184
|
end
|
141
185
|
|
142
186
|
# Return the next message recieved from the MQTT broker.
|
143
|
-
#
|
187
|
+
# An optional topic can be given to subscribe to.
|
144
188
|
#
|
145
|
-
# The method returns the topic and message as an array:
|
189
|
+
# The method either returns the topic and message as an array:
|
146
190
|
# topic,message = client.get
|
147
191
|
#
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
192
|
+
# Or can be used with a block to keep processing messages:
|
193
|
+
# client.get('test') do |topic,payload|
|
194
|
+
# # Do stuff here
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
def get(topic=nil)
|
198
|
+
# Subscribe to a topic, if an argument is given
|
199
|
+
subscribe(topic) unless topic.nil?
|
200
|
+
|
201
|
+
if block_given?
|
202
|
+
# Loop forever!
|
203
|
+
loop do
|
204
|
+
packet = @read_queue.pop
|
205
|
+
yield(packet.topic, packet.payload)
|
206
|
+
end
|
207
|
+
else
|
208
|
+
# Wait for one packet to be available
|
209
|
+
packet = @read_queue.pop
|
210
|
+
return packet.topic, packet.payload
|
211
|
+
end
|
154
212
|
end
|
155
213
|
|
156
214
|
# Send a unsubscribe message for one or more topics on the MQTT broker
|
data/lib/mqtt/version.rb
CHANGED
data/spec/mqtt_client_spec.rb
CHANGED
@@ -9,45 +9,98 @@ describe MQTT::Client do
|
|
9
9
|
@client = MQTT::Client.new
|
10
10
|
@socket = StringIO.new
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
|
+
describe "initializing a client" do
|
14
|
+
it "with no arguments, it should use the defaults" do
|
15
|
+
@client = MQTT::Client.new
|
16
|
+
@client.remote_host.should == 'localhost'
|
17
|
+
@client.remote_port.should == 1883
|
18
|
+
@client.keep_alive.should == 15
|
19
|
+
end
|
20
|
+
|
21
|
+
it "with a single string argument, it should use it has the host" do
|
22
|
+
@client = MQTT::Client.new('otherhost.mqtt.org')
|
23
|
+
@client.remote_host.should == 'otherhost.mqtt.org'
|
24
|
+
@client.remote_port.should == 1883
|
25
|
+
@client.keep_alive.should == 15
|
26
|
+
end
|
27
|
+
|
28
|
+
it "with two arguments, it should use it as the host and port" do
|
29
|
+
@client = MQTT::Client.new('otherhost.mqtt.org', 1000)
|
30
|
+
@client.remote_host.should == 'otherhost.mqtt.org'
|
31
|
+
@client.remote_port.should == 1000
|
32
|
+
@client.keep_alive.should == 15
|
33
|
+
end
|
34
|
+
|
35
|
+
it "with names arguments, it should use those as arguments" do
|
36
|
+
@client = MQTT::Client.new(:remote_host => 'otherhost.mqtt.org', :remote_port => 1000)
|
37
|
+
@client.remote_host.should == 'otherhost.mqtt.org'
|
38
|
+
@client.remote_port.should == 1000
|
39
|
+
@client.keep_alive.should == 15
|
40
|
+
end
|
41
|
+
|
42
|
+
it "with a hash, it should use those as arguments" do
|
43
|
+
@client = MQTT::Client.new({:remote_host => 'otherhost.mqtt.org', :remote_port => 1000})
|
44
|
+
@client.remote_host.should == 'otherhost.mqtt.org'
|
45
|
+
@client.remote_port.should == 1000
|
46
|
+
@client.keep_alive.should == 15
|
47
|
+
end
|
48
|
+
|
49
|
+
it "with a hash containing just a keep alive setting" do
|
50
|
+
@client = MQTT::Client.new(:keep_alive => 60)
|
51
|
+
@client.remote_host.should == 'localhost'
|
52
|
+
@client.remote_port.should == 1883
|
53
|
+
@client.keep_alive.should == 60
|
54
|
+
end
|
55
|
+
|
56
|
+
it "with three arguments" do
|
57
|
+
lambda {
|
58
|
+
@client = MQTT::Client.new(1, 2, 3)
|
59
|
+
}.should raise_error(
|
60
|
+
'Unsupported number of arguments'
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
13
66
|
describe "when calling the 'connect' method" do
|
14
67
|
before(:each) do
|
15
68
|
TCPSocket.stubs(:new).returns(@socket)
|
16
69
|
Thread.stubs(:new)
|
17
70
|
@client.stubs(:receive_connack)
|
18
71
|
end
|
19
|
-
|
72
|
+
|
20
73
|
it "should create a TCP Socket if not connected" do
|
21
74
|
TCPSocket.expects(:new).once.returns(@socket)
|
22
75
|
@client.connect('myclient')
|
23
76
|
end
|
24
|
-
|
77
|
+
|
25
78
|
it "should not create a new TCP Socket if connected" do
|
26
79
|
@client.stubs(:connected?).returns(true)
|
27
80
|
TCPSocket.expects(:new).never
|
28
81
|
@client.connect('myclient')
|
29
82
|
end
|
30
|
-
|
83
|
+
|
31
84
|
it "should start the reader thread if not connected" do
|
32
85
|
Thread.expects(:new).once
|
33
86
|
@client.connect('myclient')
|
34
87
|
end
|
35
|
-
|
88
|
+
|
36
89
|
it "should write a valid CONNECT packet to the socket if not connected" do
|
37
90
|
@client.connect('myclient')
|
38
|
-
@socket.string.should == "\020\026\x00\x06MQIsdp\x03\x02\x00\
|
91
|
+
@socket.string.should == "\020\026\x00\x06MQIsdp\x03\x02\x00\x0f\x00\x08myclient"
|
39
92
|
end
|
40
|
-
|
93
|
+
|
41
94
|
it "should try and read an acknowledgement packet to the socket if not connected" do
|
42
95
|
@client.expects(:receive_connack).once
|
43
96
|
@client.connect('myclient')
|
44
97
|
end
|
45
|
-
|
98
|
+
|
46
99
|
it "should disconnect after connecting, if a block is given" do
|
47
100
|
@client.expects(:disconnect).once
|
48
101
|
@client.connect('myclient') { nil }
|
49
102
|
end
|
50
|
-
|
103
|
+
|
51
104
|
it "should not disconnect after connecting, if no block is given" do
|
52
105
|
@client.expects(:disconnect).never
|
53
106
|
@client.connect('myclient')
|
@@ -60,8 +113,8 @@ describe MQTT::Client do
|
|
60
113
|
@socket.string.should ==
|
61
114
|
"\x10\x2A"+
|
62
115
|
"\x00\x06MQIsdp"+
|
63
|
-
"\x03\xC2\x00\
|
64
|
-
"\x08myclient"+
|
116
|
+
"\x03\xC2\x00\x0f"+
|
117
|
+
"\x00\x08myclient"+
|
65
118
|
"\x00\x08username"+
|
66
119
|
"\x00\x08password"
|
67
120
|
end
|
@@ -79,74 +132,74 @@ describe MQTT::Client do
|
|
79
132
|
@client.instance_variable_set(:@socket, @socket)
|
80
133
|
IO.stubs(:select).returns([[@socket], [], []])
|
81
134
|
end
|
82
|
-
|
135
|
+
|
83
136
|
it "should not throw an exception for a successful CONNACK packet" do
|
84
137
|
@socket.write("\x20\x02\x00\x00")
|
85
138
|
@socket.rewind
|
86
139
|
lambda { @client.send(:receive_connack) }.should_not raise_error
|
87
140
|
end
|
88
|
-
|
141
|
+
|
89
142
|
it "should throw an exception if the packet type isn't CONNACK" do
|
90
143
|
@socket.write("\xD0\x00")
|
91
144
|
@socket.rewind
|
92
145
|
lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException)
|
93
146
|
end
|
94
|
-
|
147
|
+
|
95
148
|
it "should throw an exception if the CONNACK packet return code is 'unacceptable protocol version'" do
|
96
149
|
@socket.write("\x20\x02\x00\x01")
|
97
150
|
@socket.rewind
|
98
151
|
lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /unacceptable protocol version/i)
|
99
152
|
end
|
100
|
-
|
153
|
+
|
101
154
|
it "should throw an exception if the CONNACK packet return code is 'client identifier rejected'" do
|
102
155
|
@socket.write("\x20\x02\x00\x02")
|
103
156
|
@socket.rewind
|
104
157
|
lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /client identifier rejected/i)
|
105
158
|
end
|
106
|
-
|
159
|
+
|
107
160
|
it "should throw an exception if the CONNACK packet return code is 'broker unavailable'" do
|
108
161
|
@socket.write("\x20\x02\x00\x03")
|
109
162
|
@socket.rewind
|
110
163
|
lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /broker unavailable/i)
|
111
164
|
end
|
112
|
-
|
165
|
+
|
113
166
|
it "should throw an exception if the CONNACK packet return code is an unknown" do
|
114
167
|
@socket.write("\x20\x02\x00\xAA")
|
115
168
|
@socket.rewind
|
116
169
|
lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /connection refused/i)
|
117
170
|
end
|
118
171
|
end
|
119
|
-
|
172
|
+
|
120
173
|
describe "when calling the 'disconnect' method" do
|
121
174
|
before(:each) do
|
122
175
|
@client.instance_variable_set(:@socket, @socket)
|
123
176
|
@client.instance_variable_set(:@read_thread, stub_everything('Read Thread'))
|
124
177
|
end
|
125
|
-
|
178
|
+
|
126
179
|
it "should not do anything if the socket is already disconnected" do
|
127
180
|
@client.stubs(:connected?).returns(false)
|
128
181
|
@client.disconnect(true)
|
129
182
|
@socket.string.should == ""
|
130
183
|
end
|
131
|
-
|
184
|
+
|
132
185
|
it "should write a valid DISCONNECT packet to the socket if connected and the send_msg=true an" do
|
133
186
|
@client.stubs(:connected?).returns(true)
|
134
187
|
@client.disconnect(true)
|
135
188
|
@socket.string.should == "\xE0\x00"
|
136
189
|
end
|
137
|
-
|
190
|
+
|
138
191
|
it "should not write anything to the socket if the send_msg=false" do
|
139
192
|
@client.stubs(:connected?).returns(true)
|
140
193
|
@client.disconnect(false)
|
141
194
|
@socket.string.should be_empty
|
142
195
|
end
|
143
|
-
|
196
|
+
|
144
197
|
it "should call the close method on the socket" do
|
145
198
|
@socket.expects(:close)
|
146
199
|
@client.disconnect
|
147
200
|
end
|
148
201
|
end
|
149
|
-
|
202
|
+
|
150
203
|
describe "when calling the 'ping' method" do
|
151
204
|
before(:each) do
|
152
205
|
@client.instance_variable_set(:@socket, @socket)
|
@@ -163,7 +216,7 @@ describe MQTT::Client do
|
|
163
216
|
@client.instance_variable_get(:@last_pingreq).should_not == 0
|
164
217
|
end
|
165
218
|
end
|
166
|
-
|
219
|
+
|
167
220
|
describe "when calling the 'publish' method" do
|
168
221
|
before(:each) do
|
169
222
|
@client.instance_variable_set(:@socket, @socket)
|
@@ -173,17 +226,17 @@ describe MQTT::Client do
|
|
173
226
|
@client.publish('topic','payload', false, 0)
|
174
227
|
@socket.string.should == "\x30\x0e\x00\x05topicpayload"
|
175
228
|
end
|
176
|
-
|
229
|
+
|
177
230
|
it "should write a valid PUBLISH packet to the socket with the retain flag set" do
|
178
231
|
@client.publish('topic','payload', true, 0)
|
179
232
|
@socket.string.should == "\x31\x0e\x00\x05topicpayload"
|
180
233
|
end
|
181
|
-
|
234
|
+
|
182
235
|
it "should write a valid PUBLISH packet to the socket with the QOS set to 1" do
|
183
236
|
@client.publish('topic','payload', false, 1)
|
184
237
|
@socket.string.should == "\x32\x10\x00\x05topic\x00\x01payload"
|
185
238
|
end
|
186
|
-
|
239
|
+
|
187
240
|
it "should write a valid PUBLISH packet to the socket with the QOS set to 2" do
|
188
241
|
@client.publish('topic','payload', false, 2)
|
189
242
|
@socket.string.should == "\x34\x10\x00\x05topic\x00\x01payload"
|
@@ -250,13 +303,13 @@ describe MQTT::Client do
|
|
250
303
|
@client.unsubscribe('a/b')
|
251
304
|
@socket.string.should == "\xa2\x07\x00\x01\x00\x03a/b"
|
252
305
|
end
|
253
|
-
|
306
|
+
|
254
307
|
it "should write a valid UNSUBSCRIBE packet to the socket if given a two topic Strings" do
|
255
308
|
@client.unsubscribe('a/b','c/d')
|
256
309
|
@socket.string.should == "\xa2\x0c\x00\x01\x00\x03a/b\x00\x03c/d"
|
257
310
|
end
|
258
311
|
end
|
259
|
-
|
312
|
+
|
260
313
|
describe "when calling the 'receive_packet' method" do
|
261
314
|
before(:each) do
|
262
315
|
@client.instance_variable_set(:@socket, @socket)
|
@@ -278,7 +331,7 @@ describe MQTT::Client do
|
|
278
331
|
@client.send(:receive_packet)
|
279
332
|
@read_queue.size.should == 0
|
280
333
|
end
|
281
|
-
|
334
|
+
|
282
335
|
it "should send a ping packet if one is due" do
|
283
336
|
IO.expects(:select).returns(nil)
|
284
337
|
@client.instance_variable_set(:@last_pingreq, Time.at(0))
|
@@ -297,7 +350,7 @@ describe MQTT::Client do
|
|
297
350
|
MQTT::Packet.stubs(:read).raises(MQTT::Exception)
|
298
351
|
@client.send(:receive_packet)
|
299
352
|
end
|
300
|
-
|
353
|
+
|
301
354
|
end
|
302
|
-
|
355
|
+
|
303
356
|
end
|
data/spec/mqtt_proxy_spec.rb
CHANGED
data/spec/mqtt_version_spec.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mqtt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 7
|
10
|
+
version: 0.0.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nicholas J Humfrey
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-01-
|
18
|
+
date: 2012-01-19 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|