mqtt-sn-ruby 0.0.9 → 0.0.10
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 +4 -4
- data/bin/mqtt-sn-forwarder.rb +4 -0
- data/bin/mqtt-sn-pub.rb +10 -2
- data/lib/mqtt-sn-ruby.rb +140 -126
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bdd116c2da406a3163968c7f8111b7649a83cf8
|
4
|
+
data.tar.gz: 775b6358cb108c2475679dc35e5844b0c9f68428
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee3244e7ffe27bd3ebcb2894a731f1e3ecc9afa8bd7807c6755ce035d9621293b83344590619f5a87354c2de6de901dbb3b087324a2eb3b812dfde512dddee2b
|
7
|
+
data.tar.gz: 3a7b1be4136a6866bfed5d08dcd14f6a340f582affb25409d14f267e60a0ddf4837afa53c1b39b66ac029709117ddd31457bee156bac972b91fb4609b624b04e
|
data/bin/mqtt-sn-forwarder.rb
CHANGED
@@ -31,6 +31,10 @@ OptionParser.new do |opts|
|
|
31
31
|
opts.on("-l", "--localport port", "MQTT-SN local port to listen (1882)") do |v|
|
32
32
|
options[:local_port] = v.to_i
|
33
33
|
end
|
34
|
+
options[:gw_id] = 123
|
35
|
+
opts.on("-i", "--id GwId", "MQTT-SN if of this GateWay (123)") do |v|
|
36
|
+
options[:gw_id] = v.to_i
|
37
|
+
end
|
34
38
|
opts.on("-h", "--http port", "Http port for debug/status JSON server (false)") do |v|
|
35
39
|
options[:http_port] = v.to_i
|
36
40
|
end
|
data/bin/mqtt-sn-pub.rb
CHANGED
@@ -39,7 +39,8 @@ OptionParser.new do |opts|
|
|
39
39
|
options[:topic] = topic
|
40
40
|
end
|
41
41
|
end.parse!
|
42
|
-
|
42
|
+
#require 'ruby-prof'
|
43
|
+
#RubyProf.start
|
43
44
|
puts "MQTT-SN-PUB: #{options.to_json}"
|
44
45
|
begin
|
45
46
|
sn=MqttSN.new options
|
@@ -47,7 +48,9 @@ begin
|
|
47
48
|
sn.send :searchgw #replies may or may not come -- even multiple!
|
48
49
|
sn.publish options[:topic]||"test/message/123", options[:msg]||"test_value", qos: options[:qos]
|
49
50
|
puts "Sent ok."
|
50
|
-
|
51
|
+
while not sn.log_empty?
|
52
|
+
sleep 0.1
|
53
|
+
end
|
51
54
|
rescue SystemExit, Interrupt
|
52
55
|
puts "\nExiting after Disconnect\n"
|
53
56
|
rescue => e
|
@@ -58,3 +61,8 @@ sn.disconnect if sn
|
|
58
61
|
|
59
62
|
puts "MQTT-SN-PUB Done."
|
60
63
|
|
64
|
+
#result = RubyProf.stop
|
65
|
+
|
66
|
+
# Print a flat profile to text
|
67
|
+
#printer = RubyProf::FlatPrinter.new(result)
|
68
|
+
#printer.print(STDOUT)
|
data/lib/mqtt-sn-ruby.rb
CHANGED
@@ -51,24 +51,21 @@ class MqttSN
|
|
51
51
|
QOS1_FLAG =0x20
|
52
52
|
QOS0_FLAG =0x00
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
log_thread
|
54
|
+
|
55
|
+
def logger str,*args
|
56
|
+
if @verbose or @debug
|
57
|
+
@log_q << sprintf(str,*args)
|
58
|
+
end
|
60
59
|
end
|
61
60
|
|
62
|
-
def
|
63
|
-
|
61
|
+
def note str,*args
|
62
|
+
@log_q << sprintf(str,*args)
|
64
63
|
end
|
65
64
|
|
66
65
|
def self.open_port uri_s
|
67
66
|
begin
|
68
67
|
uri = URI.parse(uri_s)
|
69
|
-
|
70
|
-
if uri.scheme== 'udp'
|
71
|
-
puts "server: #{@server}:#{@port}"
|
68
|
+
if uri.scheme== 'udp'
|
72
69
|
return [UDPSocket.new,uri.host,uri.port]
|
73
70
|
else
|
74
71
|
raise "Error: Cannot open socket for '#{uri_s}', unsupported scheme: '#{uri.scheme}'"
|
@@ -102,10 +99,23 @@ class MqttSN
|
|
102
99
|
@verbose=hash[:verbose]
|
103
100
|
@state=:inited
|
104
101
|
@bcast_port=5000
|
102
|
+
@bcast_period=10
|
105
103
|
@forward=hash[:forward] #flag to indicate forward mode
|
106
104
|
@will_topic=nil
|
107
105
|
@will_msg=nil
|
108
106
|
@id="?"
|
107
|
+
|
108
|
+
|
109
|
+
@msg_id=1
|
110
|
+
@clients={}
|
111
|
+
@gateways={}
|
112
|
+
@log_q = Queue.new #log queue :)
|
113
|
+
|
114
|
+
@log_t=Thread.new do
|
115
|
+
log_thread
|
116
|
+
end
|
117
|
+
|
118
|
+
|
109
119
|
@sem=Mutex.new
|
110
120
|
@topics={} #hash of registered topics is stored here
|
111
121
|
@iq = Queue.new
|
@@ -126,96 +136,69 @@ class MqttSN
|
|
126
136
|
@s.bind("0.0.0.0",hash[:local_port]||1883)
|
127
137
|
@bcast_period=60
|
128
138
|
loop do
|
129
|
-
|
139
|
+
forwarder_thread @s
|
130
140
|
end
|
131
141
|
end
|
132
142
|
|
133
143
|
end
|
134
144
|
|
135
145
|
|
136
|
-
def
|
146
|
+
def forwarder_thread socket
|
137
147
|
begin
|
138
|
-
last_bcast=0
|
139
148
|
last_kill=0
|
140
149
|
stime=Time.now.to_i
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
#periodically broadcast :advertize
|
145
|
-
MqttSN::send_packet [ADVERTISE_TYPE,0xAB,0,30],@bcast_s,MULTICAST_ADDR,@bcast_port
|
146
|
-
last_bcast=now
|
147
|
-
end
|
148
|
-
#periodically kill disconnected clients -- and those timed out..
|
149
|
-
if now>last_kill+1
|
150
|
+
Thread.new do #maintenance
|
151
|
+
while true do
|
152
|
+
sleep 1
|
150
153
|
changes=false
|
151
|
-
|
154
|
+
@clients.dup.each do |key,data|
|
152
155
|
if data[:state]==:disconnected
|
153
156
|
dest="#{data[:ip]}:#{data[:port]}"
|
154
|
-
|
155
|
-
|
157
|
+
note "- %s",dest
|
158
|
+
@clients.delete key
|
156
159
|
changes=true
|
157
160
|
end
|
158
161
|
end
|
159
162
|
if changes
|
160
|
-
|
161
|
-
end
|
162
|
-
last_kill=now
|
163
|
-
end
|
164
|
-
|
165
|
-
if false # pac=MqttSN::poll_packet(@bcast)
|
166
|
-
r,client_ip,client_port=pac
|
167
|
-
key="#{client_ip}:#{client_port}"
|
168
|
-
dest="#{MULTICAST_ADDR};#{@bcast_port}"
|
169
|
-
m=MqttSN::parse_message r
|
170
|
-
MqttSN::logger "R %-18.18s -> %-18.18s | %s",key,dest,m.to_json
|
171
|
-
|
172
|
-
case m[:type]
|
173
|
-
when :searchgw
|
174
|
-
dest="#{client_ip}:#{client_port}"
|
175
|
-
MqttSN::logger "C %-18.18s -> %-18.18s | %s",key,dest,m.to_json
|
176
|
-
MqttSN::send_packet [GWINFO_TYPE,0xAB],@bcast_s,MULTICAST_ADDR,@bcast_port
|
177
|
-
done=true
|
178
|
-
end
|
179
|
-
|
180
|
-
end
|
181
|
-
if pac=MqttSN::poll_packet(@s)
|
182
|
-
r,client_ip,client_port=pac
|
183
|
-
key="#{client_ip}:#{client_port}"
|
184
|
-
if not @@clients[key]
|
185
|
-
@@clients[key]={ip:client_ip, port:client_port, socket: UDPSocket.new, state: :active }
|
186
|
-
dest="#{client_ip}:#{client_port}"
|
187
|
-
MqttSN::logger "+ %s\n",dest
|
188
|
-
MqttSN::logger "cli: #{@@clients.to_json}"
|
189
|
-
end
|
190
|
-
@@clients[key][:stamp]=Time.now.to_i
|
191
|
-
m=MqttSN::parse_message r
|
192
|
-
done=false
|
193
|
-
|
194
|
-
if not done # not done locally -> forward it
|
195
|
-
sbytes=@@clients[key][:socket].send(r, 0, @server, @port) # to rsmb -- ok as is
|
196
|
-
_,port,_,_ = @@clients[key][:socket].addr
|
197
|
-
dest="#{@server}:#{port}"
|
198
|
-
MqttSN::logger "C %-18.18s -> %-18.18s | %s",key,dest,m.to_json
|
163
|
+
note "cli:#{@clients.to_json}"
|
199
164
|
end
|
200
165
|
end
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
166
|
+
end
|
167
|
+
while true
|
168
|
+
pac=MqttSN::poll_packet_block(socket) #data from clients to our service sovket
|
169
|
+
r,client_ip,client_port=pac
|
170
|
+
key="#{client_ip}:#{client_port}"
|
171
|
+
if not @clients[key]
|
172
|
+
@clients[key]={ip:client_ip, port:client_port, socket: UDPSocket.new, state: :active }
|
173
|
+
c=@clients[key]
|
174
|
+
puts "thread start for #{key}"
|
175
|
+
@clients[key][:thread]=Thread.new(key) do |my_key|
|
176
|
+
while true
|
177
|
+
pacc=MqttSN::poll_packet_block(@clients[my_key][:socket]) #if we get data from server destined to our client
|
178
|
+
rr,client_ip,client_port=pacc
|
179
|
+
@s.send(rr, 0, @clients[my_key][:ip], @clients[my_key][:port]) # send_packet to client
|
180
|
+
mm=MqttSN::parse_message rr
|
181
|
+
_,port,_,_ = @clients[my_key][:socket].addr
|
182
|
+
dest="#{@server}:#{port}"
|
183
|
+
logger "S %-18.18s <- %-18.18s | %s",my_key,dest,mm.to_json
|
184
|
+
case mm[:type]
|
185
|
+
when :disconnect
|
186
|
+
@clients[my_key][:state]=:disconnected
|
187
|
+
end
|
214
188
|
end
|
215
189
|
end
|
190
|
+
dest="#{client_ip}:#{client_port}"
|
191
|
+
note "+ %s\n",dest
|
192
|
+
note "cli: #{@clients.to_json}"
|
216
193
|
end
|
217
|
-
|
218
|
-
|
194
|
+
@clients[key][:stamp]=Time.now.to_i
|
195
|
+
m=MqttSN::parse_message r
|
196
|
+
sbytes=@clients[key][:socket].send(r, 0, @server, @port) # to rsmb -- ok as is
|
197
|
+
_,port,_,_ = @clients[key][:socket].addr
|
198
|
+
dest="#{@server}:#{port}"
|
199
|
+
logger "C %-18.18s -> %-18.18s | %s",key,dest,m.to_json
|
200
|
+
|
201
|
+
end
|
219
202
|
end
|
220
203
|
end
|
221
204
|
|
@@ -265,11 +248,11 @@ class MqttSN
|
|
265
248
|
@id=hash[:id]
|
266
249
|
when :register
|
267
250
|
raise "Need :topic to Publish!" if not hash[:topic]
|
268
|
-
p=[REGISTER_TYPE,0,0
|
251
|
+
p=[REGISTER_TYPE,0,0,@msg_id >>8 ,@msg_id & 0xff]
|
269
252
|
hash[:topic].each_byte do |b|
|
270
253
|
p<<b
|
271
254
|
end
|
272
|
-
|
255
|
+
@msg_id+=1
|
273
256
|
when :register_ack
|
274
257
|
p=[REGACK_TYPE,hash[:topic_id]>>8 ,hash[:topic_id] & 0xff,hash[:msg_id]>>8 ,hash[:msg_id] & 0xff,hash[:return_code]]
|
275
258
|
when :publish_ack
|
@@ -312,19 +295,19 @@ class MqttSN
|
|
312
295
|
else
|
313
296
|
flags+=QOS1_FLAG*qos
|
314
297
|
end
|
315
|
-
p=[SUBSCRIBE_TYPE,flags
|
298
|
+
p=[SUBSCRIBE_TYPE,flags,@msg_id >>8 ,@msg_id & 0xff]
|
316
299
|
hash[:topic].each_byte do |b|
|
317
300
|
p<<b
|
318
301
|
end
|
319
|
-
|
302
|
+
@msg_id+=1
|
320
303
|
|
321
304
|
when :unsubscribe
|
322
305
|
raise "Need :topic to :unsubscribe" if not hash[:topic]
|
323
|
-
p=[UNSUBSCRIBE_TYPE,0
|
306
|
+
p=[UNSUBSCRIBE_TYPE,0,@msg_id >>8 ,@msg_id & 0xff]
|
324
307
|
hash[:topic].each_byte do |b|
|
325
308
|
p<<b
|
326
309
|
end
|
327
|
-
|
310
|
+
@msg_id+=1
|
328
311
|
|
329
312
|
when :publish
|
330
313
|
raise "Need :topic_id to Publish!" if not hash[:topic_id]
|
@@ -336,11 +319,11 @@ class MqttSN
|
|
336
319
|
else
|
337
320
|
flags+=QOS1_FLAG*qos
|
338
321
|
end
|
339
|
-
p=[PUBLISH_TYPE,flags,hash[:topic_id] >>8 ,hash[:topic_id] & 0xff
|
322
|
+
p=[PUBLISH_TYPE,flags,hash[:topic_id] >>8 ,hash[:topic_id] & 0xff,@msg_id >>8 ,@msg_id & 0xff]
|
340
323
|
hash[:msg].each_byte do |b|
|
341
324
|
p<<b
|
342
325
|
end
|
343
|
-
|
326
|
+
@msg_id+=1
|
344
327
|
when :pubrel
|
345
328
|
raise "Need the original :msg_id of the Publish for PubRel!" if not hash[:msg_id]
|
346
329
|
p=[PUBREL_TYPE,hash[:msg_id] >>8 ,hash[:msg_id] & 0xff]
|
@@ -360,6 +343,15 @@ class MqttSN
|
|
360
343
|
end
|
361
344
|
status=:timeout
|
362
345
|
m={}
|
346
|
+
if not hash[:expect]
|
347
|
+
if type==:searchgw
|
348
|
+
raw=send_packet p,@bcast,MULTICAST_ADDR,@bcast_port
|
349
|
+
else
|
350
|
+
raw=send_packet p,@s,@server,@port
|
351
|
+
end
|
352
|
+
hash[:debug]=raw if @debug
|
353
|
+
return
|
354
|
+
end
|
363
355
|
@sem.synchronize do #one command at a time --
|
364
356
|
if hash[:expect]
|
365
357
|
while not @iq.empty?
|
@@ -369,9 +361,9 @@ class MqttSN
|
|
369
361
|
@iq.clear
|
370
362
|
end
|
371
363
|
if type==:searchgw
|
372
|
-
raw=
|
364
|
+
raw=send_packet p,@bcast,MULTICAST_ADDR,@bcast_port
|
373
365
|
else
|
374
|
-
raw=
|
366
|
+
raw=send_packet p,@s,@server,@port
|
375
367
|
end
|
376
368
|
hash[:debug]=raw if @debug
|
377
369
|
#puts "send:#{@id} #{type},#{hash.to_json}" if @verbose
|
@@ -396,7 +388,7 @@ class MqttSN
|
|
396
388
|
break
|
397
389
|
else
|
398
390
|
retries+=1
|
399
|
-
send_packet p
|
391
|
+
send_packet p,@s,@server,@port
|
400
392
|
puts "fail to get ack, retry #{retries} :#{@id} #{type},#{hash.to_json}"
|
401
393
|
#need to set DUP flag !
|
402
394
|
end
|
@@ -418,18 +410,13 @@ class MqttSN
|
|
418
410
|
end
|
419
411
|
end
|
420
412
|
|
421
|
-
def
|
413
|
+
def send_packet m,socket,server,port
|
422
414
|
msg=MqttSN::build_packet m
|
423
415
|
MqttSN::send_raw_packet msg,socket,server,port
|
424
416
|
dest="#{server}:#{port}"
|
425
417
|
_,port,_,_ = socket.addr
|
426
418
|
src=":#{port}"
|
427
|
-
|
428
|
-
end
|
429
|
-
|
430
|
-
|
431
|
-
def send_packet m
|
432
|
-
MqttSN::send_packet m,@s,@server,@port
|
419
|
+
logger "< %-18.18s <- %-18.18s | %s",dest,src,MqttSN::parse_message(msg).to_json
|
433
420
|
end
|
434
421
|
|
435
422
|
def self.poll_packet socket
|
@@ -440,6 +427,7 @@ class MqttSN
|
|
440
427
|
client_port=stuff[1]
|
441
428
|
return [r,client_ip,client_port]
|
442
429
|
rescue IO::WaitReadable
|
430
|
+
sleep 0.1
|
443
431
|
rescue => e
|
444
432
|
puts "Error: receive thread died: #{e}"
|
445
433
|
pp e.backtrace
|
@@ -447,12 +435,20 @@ class MqttSN
|
|
447
435
|
return nil
|
448
436
|
end
|
449
437
|
|
438
|
+
def self.poll_packet_block socket
|
439
|
+
#decide how to get data -- UDP-socket or FM-radio
|
440
|
+
r,stuff=socket.recvfrom(200) #get_packet --high level func!
|
441
|
+
client_ip=stuff[2]
|
442
|
+
client_port=stuff[1]
|
443
|
+
return [r,client_ip,client_port]
|
444
|
+
end
|
445
|
+
|
450
446
|
def self.get_clients
|
451
|
-
|
447
|
+
@clients
|
452
448
|
end
|
453
449
|
|
454
450
|
def self.get_gateways
|
455
|
-
|
451
|
+
@gateways
|
456
452
|
end
|
457
453
|
|
458
454
|
|
@@ -723,7 +719,7 @@ class MqttSN
|
|
723
719
|
def client_thread socket
|
724
720
|
while true do
|
725
721
|
begin
|
726
|
-
if pac=MqttSN::
|
722
|
+
if pac=MqttSN::poll_packet_block(socket)
|
727
723
|
r,client_ip,client_port=pac
|
728
724
|
m=MqttSN::parse_message r
|
729
725
|
if @debug and m
|
@@ -732,8 +728,10 @@ class MqttSN
|
|
732
728
|
dest="#{client_ip}:#{client_port}"
|
733
729
|
_,port,_,_ = @s.addr
|
734
730
|
src=port
|
735
|
-
|
731
|
+
logger "> %-18.18s -> %-18.18s | %s",dest,":#{port}",m.to_json
|
736
732
|
process_message m
|
733
|
+
else
|
734
|
+
sleep 0.01
|
737
735
|
end
|
738
736
|
rescue => e
|
739
737
|
puts "Error: receive thread died: #{e}"
|
@@ -745,41 +743,51 @@ class MqttSN
|
|
745
743
|
def process_broadcast_message m,client_ip,client_port
|
746
744
|
case m[:type]
|
747
745
|
when :searchgw
|
748
|
-
|
746
|
+
note "hey, someone is looking for gateway..."
|
749
747
|
key="#{client_ip}:#{client_port}"
|
750
748
|
dest="#{MULTICAST_ADDR};#{@bcast_port}"
|
751
749
|
#actually -- send data on all gateways we know...
|
752
|
-
|
753
|
-
|
750
|
+
if @options[:forwarder]
|
751
|
+
logger "r %-18.18s -> %-18.18s | %s",key,dest,m.to_json
|
752
|
+
send_packet [GWINFO_TYPE,@options[:gw_id]],@bcast_s,MULTICAST_ADDR,@bcast_port
|
753
|
+
end
|
754
754
|
when :advertise,:gwinfo
|
755
|
-
|
756
|
-
if not
|
757
|
-
|
755
|
+
note "hey, we have gateway there!"
|
756
|
+
if not @gateways[client_ip]
|
757
|
+
@gateways[client_ip]={stamp: Time.now.to_i}
|
758
758
|
else
|
759
|
-
|
759
|
+
@gateways[client_ip][:stamp]=Time.now.to_i
|
760
760
|
end
|
761
|
-
|
762
|
-
|
763
|
-
|
761
|
+
@gateways[client_ip][:gw_id]=m[:gw_id]
|
762
|
+
@gateways[client_ip][:duration]=m[:duration]
|
763
|
+
note "gw: #{@gateways.to_json}"
|
764
764
|
end
|
765
765
|
end
|
766
766
|
|
767
767
|
def roam_thread socket
|
768
|
+
@last_bcast=0
|
769
|
+
if @options[:forwarder]
|
770
|
+
Thread.new do
|
771
|
+
while true do
|
772
|
+
send_packet [ADVERTISE_TYPE,@options[:gw_id],@bcast_period>>8,@bcast_period&0xff],@bcast_s,MULTICAST_ADDR,@bcast_port
|
773
|
+
sleep @bcast_period
|
774
|
+
end
|
775
|
+
end
|
776
|
+
end
|
768
777
|
while true do
|
769
778
|
begin
|
770
779
|
if @bcast
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
end
|
777
|
-
dest="#{client_ip}:#{client_port}"
|
778
|
-
_,port,_,_ = @bcast.addr
|
779
|
-
src=port
|
780
|
-
MqttSN::logger "R %-18.18s <- %-18.18s | %s",dest,":#{port}",m.to_json
|
781
|
-
process_broadcast_message m,client_ip,client_port
|
780
|
+
pac=MqttSN::poll_packet_block(socket)
|
781
|
+
r,client_ip,client_port=pac
|
782
|
+
m=MqttSN::parse_message r
|
783
|
+
if @debug and m
|
784
|
+
m[:debug]=MqttSN::hexdump r
|
782
785
|
end
|
786
|
+
dest="#{client_ip}:#{client_port}"
|
787
|
+
_,port,_,_ = @bcast.addr
|
788
|
+
src=port
|
789
|
+
logger "R %-18.18s <- %-18.18s | %s",dest,":#{port}",m.to_json
|
790
|
+
process_broadcast_message m,client_ip,client_port
|
783
791
|
end
|
784
792
|
rescue => e
|
785
793
|
puts "Error: receive thread died: #{e}"
|
@@ -788,12 +796,18 @@ class MqttSN
|
|
788
796
|
end
|
789
797
|
end
|
790
798
|
|
791
|
-
def
|
799
|
+
def log_empty?
|
800
|
+
@log_q.empty? or not @verbose
|
801
|
+
end
|
802
|
+
|
803
|
+
def log_thread
|
792
804
|
while true do
|
793
805
|
begin
|
794
|
-
if not
|
795
|
-
l
|
806
|
+
if not @log_q.empty?
|
807
|
+
l=@log_q.pop
|
796
808
|
puts l
|
809
|
+
else
|
810
|
+
sleep 0.01
|
797
811
|
end
|
798
812
|
rescue => e
|
799
813
|
puts "Error: receive thread died: #{e}"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mqtt-sn-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ari Siitonen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Ruby toolkit for MQTT-SN, compatible with RSMB, command line tools and
|
14
14
|
API
|