mqtt 0.3.1 → 0.4.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.
@@ -368,7 +368,7 @@ module MQTT
368
368
  end
369
369
  body += encode_string(@topic)
370
370
  body += encode_short(@id) unless qos == 0
371
- body += payload.to_s.force_encoding('ASCII-8BIT')
371
+ body += payload.to_s.dup.force_encoding('ASCII-8BIT')
372
372
  return body
373
373
  end
374
374
 
@@ -780,9 +780,9 @@ module MQTT
780
780
 
781
781
  # Set one or more topic filters for the Subscribe packet
782
782
  # The topics parameter should be one of the following:
783
- # * String: subscribe to one topic with QOS 0
784
- # * Array: subscribe to multiple topics with QOS 0
785
- # * Hash: subscribe to multiple topics where the key is the topic and the value is the QOS level
783
+ # * String: subscribe to one topic with QoS 0
784
+ # * Array: subscribe to multiple topics with QoS 0
785
+ # * Hash: subscribe to multiple topics where the key is the topic and the value is the QoS level
786
786
  #
787
787
  # For example:
788
788
  # packet.topics = 'a/b'
@@ -877,7 +877,7 @@ module MQTT
877
877
  super(ATTR_DEFAULTS.merge(args))
878
878
  end
879
879
 
880
- # Set the granted QOS value for each of the topics that were subscribed to
880
+ # Set the granted QoS value for each of the topics that were subscribed to
881
881
  # Can either be an integer or an array or integers.
882
882
  def return_codes=(value)
883
883
  if value.is_a?(Array)
@@ -892,7 +892,7 @@ module MQTT
892
892
  # Get serialisation of packet's body
893
893
  def encode_body
894
894
  if @return_codes.empty?
895
- raise "no granted QOS given when serialising packet"
895
+ raise "no granted QoS given when serialising packet"
896
896
  end
897
897
  body = encode_short(@id)
898
898
  return_codes.each { |qos| body += encode_bytes(qos) }
@@ -24,6 +24,10 @@ class Encoding
24
24
  def to_s
25
25
  @name
26
26
  end
27
+
28
+ def name
29
+ @name
30
+ end
27
31
 
28
32
  UTF_8 = Encoding.new("UTF-8")
29
33
  ASCII_8BIT = Encoding.new("ASCII-8BIT")
@@ -0,0 +1,763 @@
1
+ # encoding: BINARY
2
+
3
+ module MQTT::SN
4
+
5
+ # Class representing a MQTT::SN Packet
6
+ # Performs binary encoding and decoding of headers
7
+ class Packet
8
+ attr_accessor :duplicate # Duplicate delivery flag
9
+ attr_accessor :qos # Quality of Service level
10
+ attr_accessor :retain # Retain flag
11
+ attr_accessor :request_will # Request that gateway prompts for Will
12
+ attr_accessor :clean_session # When true, subscriptions are deleted after disconnect
13
+ attr_accessor :topic_id_type # One of :normal, :predefined or :short
14
+
15
+ DEFAULTS = {}
16
+
17
+ # Parse buffer into new packet object
18
+ def self.parse(buffer)
19
+ # Parse the fixed header (length and type)
20
+ length,type_id,body = buffer.unpack('CCa*')
21
+ if length == 1
22
+ length,type_id,body = buffer.unpack('xnCa*')
23
+ end
24
+
25
+ # Double-check the length
26
+ if buffer.length != length
27
+ raise ProtocolException.new("Length of packet is not the same as the length header")
28
+ end
29
+
30
+ packet_class = PACKET_TYPES[type_id]
31
+ if packet_class.nil?
32
+ raise ProtocolException.new("Invalid packet type identifier: #{type_id}")
33
+ end
34
+
35
+ # Create a new packet object
36
+ packet = packet_class.new
37
+ packet.parse_body(body)
38
+
39
+ return packet
40
+ end
41
+
42
+ # Create a new empty packet
43
+ def initialize(args={})
44
+ update_attributes(self.class::DEFAULTS.merge(args))
45
+ end
46
+
47
+ def update_attributes(attr={})
48
+ attr.each_pair do |k,v|
49
+ send("#{k}=", v)
50
+ end
51
+ end
52
+
53
+ # Get the identifer for this packet type
54
+ def type_id
55
+ PACKET_TYPES.each_pair do |key, value|
56
+ return key if self.class == value
57
+ end
58
+ raise "Invalid packet type: #{self.class}"
59
+ end
60
+
61
+ # Serialise the packet
62
+ def to_s
63
+ # Get the packet's variable header and payload
64
+ body = self.encode_body
65
+
66
+ # Build up the body length field bytes
67
+ body_length = body.length
68
+ if body_length > 65531
69
+ raise "MQTT-SN Packet is too big, maximum packet body size is 65531"
70
+ elsif body_length > 253
71
+ [0x01, body_length + 4, type_id].pack('CnC') + body
72
+ else
73
+ [body_length + 2, type_id].pack('CC') + body
74
+ end
75
+ end
76
+
77
+ def parse_body(buffer)
78
+ end
79
+
80
+ protected
81
+
82
+ def parse_flags(flags)
83
+ self.duplicate = ((flags & 0x80) >> 7) == 0x01
84
+ self.qos = (flags & 0x60) >> 5
85
+ self.qos = -1 if self.qos == 3
86
+ self.retain = ((flags & 0x10) >> 4) == 0x01
87
+ self.request_will = ((flags & 0x08) >> 3) == 0x01
88
+ self.clean_session = ((flags & 0x04) >> 2) == 0x01
89
+ case (flags & 0x03)
90
+ when 0x0
91
+ self.topic_id_type = :normal
92
+ when 0x1
93
+ self.topic_id_type = :predefined
94
+ when 0x2
95
+ self.topic_id_type = :short
96
+ else
97
+ self.topic_id_type = nil
98
+ end
99
+ end
100
+
101
+ # Get serialisation of packet's body (variable header and payload)
102
+ def encode_body
103
+ '' # No body by default
104
+ end
105
+
106
+ def encode_flags
107
+ flags = 0x00
108
+ flags += 0x80 if duplicate
109
+ case qos
110
+ when -1
111
+ flags += 0x60
112
+ when 1
113
+ flags += 0x20
114
+ when 2
115
+ flags += 0x40
116
+ end
117
+ flags += 0x10 if retain
118
+ flags += 0x08 if request_will
119
+ flags += 0x04 if clean_session
120
+ case topic_id_type
121
+ when :normal
122
+ flags += 0x0
123
+ when :predefined
124
+ flags += 0x1
125
+ when :short
126
+ flags += 0x2
127
+ end
128
+ return flags
129
+ end
130
+
131
+ def encode_topic_id
132
+ if topic_id_type == :short
133
+ unless topic_id.is_a?(String)
134
+ raise "topic_id must be an String for type #{topic_id_type}"
135
+ end
136
+ (topic_id[0].ord << 8) + topic_id[1].ord
137
+ else
138
+ unless topic_id.is_a?(Integer)
139
+ raise "topic_id must be an Integer for type #{topic_id_type}"
140
+ end
141
+ topic_id
142
+ end
143
+ end
144
+
145
+ def parse_topic_id(topic_id)
146
+ if topic_id_type == :short
147
+ int = topic_id.to_i
148
+ self.topic_id = [(int >> 8) & 0xFF, int & 0xFF].pack('CC')
149
+ else
150
+ self.topic_id = topic_id
151
+ end
152
+ end
153
+
154
+ # Used where a field can either be a Topic Id or a Topic Name
155
+ # (the Subscribe and Unsubscribe packet types)
156
+ def encode_topic
157
+ case topic_id_type
158
+ when :normal
159
+ topic_name
160
+ when :short
161
+ unless topic_name.nil?
162
+ topic_name
163
+ else
164
+ topic_id
165
+ end
166
+ when :predefined
167
+ [topic_id].pack('n')
168
+ end
169
+ end
170
+
171
+ # Used where a field can either be a Topic Id or a Topic Name
172
+ # (the Subscribe and Unsubscribe packet types)
173
+ def parse_topic(topic)
174
+ case topic_id_type
175
+ when :normal
176
+ self.topic_name = topic
177
+ when :short
178
+ self.topic_name = topic
179
+ self.topic_id = topic
180
+ when :predefined
181
+ self.topic_id = topic.unpack('n').first
182
+ end
183
+ end
184
+
185
+ class Advertise < Packet
186
+ attr_accessor :gateway_id
187
+ attr_accessor :duration
188
+
189
+ DEFAULTS = {
190
+ :gateway_id => 0x00,
191
+ :duration => 0
192
+ }
193
+
194
+ def encode_body
195
+ [gateway_id, duration].pack('Cn')
196
+ end
197
+
198
+ def parse_body(buffer)
199
+ self.gateway_id, self.duration = buffer.unpack('Cn')
200
+ end
201
+ end
202
+
203
+ class Searchgw < Packet
204
+ attr_accessor :radius
205
+ DEFAULTS = {
206
+ :radius => 1
207
+ }
208
+
209
+ def encode_body
210
+ [radius].pack('C')
211
+ end
212
+
213
+ def parse_body(buffer)
214
+ self.radius, _ignore = buffer.unpack('C')
215
+ end
216
+ end
217
+
218
+ class Gwinfo < Packet
219
+ attr_accessor :gateway_id
220
+ attr_accessor :gateway_address
221
+ DEFAULTS = {
222
+ :gateway_id => 0,
223
+ :gateway_address => nil
224
+ }
225
+
226
+ def encode_body
227
+ [gateway_id,gateway_address].pack('Ca*')
228
+ end
229
+
230
+ def parse_body(buffer)
231
+ if buffer.length > 1
232
+ self.gateway_id, self.gateway_address = buffer.unpack('Ca*')
233
+ else
234
+ self.gateway_id, _ignore = buffer.unpack('C')
235
+ self.gateway_address = nil
236
+ end
237
+ end
238
+ end
239
+
240
+ class Connect < Packet
241
+ attr_accessor :keep_alive
242
+ attr_accessor :client_id
243
+
244
+ DEFAULTS = {
245
+ :request_will => false,
246
+ :clean_session => true,
247
+ :keep_alive => 15
248
+ }
249
+
250
+ # Get serialisation of packet's body
251
+ def encode_body
252
+ if @client_id.nil? or @client_id.length < 1 or @client_id.length > 23
253
+ raise "Invalid client identifier when serialising packet"
254
+ end
255
+
256
+ [encode_flags, 0x01, keep_alive, client_id].pack('CCna*')
257
+ end
258
+
259
+ def parse_body(buffer)
260
+ flags, protocol_id, self.keep_alive, self.client_id = buffer.unpack('CCna*')
261
+
262
+ if protocol_id != 0x01
263
+ raise ProtocolException.new("Unsupported protocol ID number: #{protocol_id}")
264
+ end
265
+
266
+ parse_flags(flags)
267
+ end
268
+ end
269
+
270
+ class Connack < Packet
271
+ attr_accessor :return_code
272
+
273
+ # Get a string message corresponding to a return code
274
+ def return_msg
275
+ case return_code
276
+ when 0x00
277
+ "Accepted"
278
+ when 0x01
279
+ "Rejected: congestion"
280
+ when 0x02
281
+ "Rejected: invalid topic ID"
282
+ when 0x03
283
+ "Rejected: not supported"
284
+ else
285
+ "Rejected: error code #{return_code}"
286
+ end
287
+ end
288
+
289
+ def encode_body
290
+ unless return_code.is_a?(Integer)
291
+ raise "return_code must be an Integer"
292
+ end
293
+
294
+ [return_code].pack('C')
295
+ end
296
+
297
+ def parse_body(buffer)
298
+ self.return_code = buffer.unpack('C')[0]
299
+ end
300
+ end
301
+
302
+ class Willtopicreq < Packet
303
+ # No attributes
304
+ end
305
+
306
+ class Willtopic < Packet
307
+ attr_accessor :topic_name
308
+
309
+ DEFAULTS = {
310
+ :qos => 0,
311
+ :retain => false,
312
+ :topic_name => nil
313
+ }
314
+
315
+ def encode_body
316
+ if topic_name.nil? or topic_name.empty?
317
+ ''
318
+ else
319
+ [encode_flags, topic_name].pack('Ca*')
320
+ end
321
+ end
322
+
323
+ def parse_body(buffer)
324
+ if buffer.length > 1
325
+ flags, self.topic_name = buffer.unpack('Ca*')
326
+ else
327
+ flags, _ignore = buffer.unpack('C')
328
+ self.topic_name = nil
329
+ end
330
+ parse_flags(flags)
331
+ end
332
+ end
333
+
334
+ class Willmsgreq < Packet
335
+ # No attributes
336
+ end
337
+
338
+ class Willmsg < Packet
339
+ attr_accessor :data
340
+
341
+ def encode_body
342
+ data
343
+ end
344
+
345
+ def parse_body(buffer)
346
+ self.data = buffer
347
+ end
348
+ end
349
+
350
+ class Register < Packet
351
+ attr_accessor :id
352
+ attr_accessor :topic_id
353
+ attr_accessor :topic_name
354
+
355
+ DEFAULTS = {
356
+ :id => 0x00,
357
+ :topic_id_type => :normal
358
+ }
359
+
360
+ def encode_body
361
+ unless id.is_a?(Integer)
362
+ raise "id must be an Integer"
363
+ end
364
+
365
+ unless topic_id.is_a?(Integer)
366
+ raise "topic_id must be an Integer"
367
+ end
368
+
369
+ [topic_id, id, topic_name].pack('nna*')
370
+ end
371
+
372
+ def parse_body(buffer)
373
+ self.topic_id, self.id, self.topic_name = buffer.unpack('nna*')
374
+ end
375
+ end
376
+
377
+ class Regack < Packet
378
+ attr_accessor :id
379
+ attr_accessor :topic_id
380
+ attr_accessor :return_code
381
+
382
+ DEFAULTS = {
383
+ :id => 0x00,
384
+ :topic_id => 0x00,
385
+ :topic_id_type => :normal
386
+ }
387
+
388
+ def encode_body
389
+ unless id.is_a?(Integer)
390
+ raise "id must be an Integer"
391
+ end
392
+
393
+ unless topic_id.is_a?(Integer)
394
+ raise "topic_id must be an Integer"
395
+ end
396
+
397
+ [topic_id, id, return_code].pack('nnC')
398
+ end
399
+
400
+ def parse_body(buffer)
401
+ self.topic_id, self.id, self.return_code = buffer.unpack('nnC')
402
+ end
403
+ end
404
+
405
+ class Publish < Packet
406
+ attr_accessor :topic_id
407
+ attr_accessor :id
408
+ attr_accessor :data
409
+
410
+ DEFAULTS = {
411
+ :id => 0x00,
412
+ :duplicate => false,
413
+ :qos => 0,
414
+ :retain => false,
415
+ :topic_id_type => :normal
416
+ }
417
+
418
+ def encode_body
419
+ unless id.is_a?(Integer)
420
+ raise "id must be an Integer"
421
+ end
422
+
423
+ [encode_flags, encode_topic_id, id, data].pack('Cnna*')
424
+ end
425
+
426
+ def parse_body(buffer)
427
+ flags, topic_id, self.id, self.data = buffer.unpack('Cnna*')
428
+ parse_flags(flags)
429
+ parse_topic_id(topic_id)
430
+ end
431
+ end
432
+
433
+ class Puback < Packet
434
+ attr_accessor :topic_id
435
+ attr_accessor :id
436
+ attr_accessor :return_code
437
+
438
+ DEFAULTS = {
439
+ :id => 0x00,
440
+ :topic_id => nil,
441
+ :return_code => 0x00,
442
+ }
443
+
444
+ def encode_body
445
+ unless id.is_a?(Integer)
446
+ raise "id must be an Integer"
447
+ end
448
+
449
+ unless topic_id.is_a?(Integer)
450
+ raise "topic_id must be an Integer"
451
+ end
452
+
453
+ [topic_id, id, return_code].pack('nnC')
454
+ end
455
+
456
+ def parse_body(buffer)
457
+ self.topic_id, self.id, self.return_code = buffer.unpack('nnC')
458
+ end
459
+ end
460
+
461
+ class Pubcomp < Packet
462
+ attr_accessor :id
463
+
464
+ DEFAULTS = {
465
+ :id => 0x00
466
+ }
467
+
468
+ def encode_body
469
+ unless id.is_a?(Integer)
470
+ raise "id must be an Integer"
471
+ end
472
+
473
+ [id].pack('n')
474
+ end
475
+
476
+ def parse_body(buffer)
477
+ self.id, _ignore = buffer.unpack('n')
478
+ end
479
+ end
480
+
481
+ class Pubrec < Packet
482
+ attr_accessor :id
483
+
484
+ DEFAULTS = {
485
+ :id => 0x00
486
+ }
487
+
488
+ def encode_body
489
+ unless id.is_a?(Integer)
490
+ raise "id must be an Integer"
491
+ end
492
+
493
+ [id].pack('n')
494
+ end
495
+
496
+ def parse_body(buffer)
497
+ self.id, _ignore = buffer.unpack('n')
498
+ end
499
+ end
500
+
501
+ class Pubrel < Packet
502
+ attr_accessor :id
503
+
504
+ DEFAULTS = {
505
+ :id => 0x00
506
+ }
507
+
508
+ def encode_body
509
+ unless id.is_a?(Integer)
510
+ raise "id must be an Integer"
511
+ end
512
+
513
+ [id].pack('n')
514
+ end
515
+
516
+ def parse_body(buffer)
517
+ self.id, _ignore = buffer.unpack('n')
518
+ end
519
+ end
520
+
521
+ class Subscribe < Packet
522
+ attr_accessor :id
523
+ attr_accessor :topic_id
524
+ attr_accessor :topic_name
525
+
526
+ DEFAULTS = {
527
+ :id => 0x00,
528
+ :topic_id_type => :normal
529
+ }
530
+
531
+ def encode_body
532
+ unless id.is_a?(Integer)
533
+ raise "id must be an Integer"
534
+ end
535
+
536
+ [encode_flags, id, encode_topic].pack('Cna*')
537
+ end
538
+
539
+ def parse_body(buffer)
540
+ flags, self.id, topic = buffer.unpack('Cna*')
541
+ parse_flags(flags)
542
+ parse_topic(topic)
543
+ end
544
+ end
545
+
546
+ class Suback < Packet
547
+ attr_accessor :id
548
+ attr_accessor :topic_id
549
+ attr_accessor :return_code
550
+
551
+ DEFAULTS = {
552
+ :qos => 0,
553
+ :id => 0x00,
554
+ :topic_id => 0x00,
555
+ :topic_id_type => :normal
556
+ }
557
+
558
+ def encode_body
559
+ unless id.is_a?(Integer)
560
+ raise "id must be an Integer"
561
+ end
562
+
563
+ [encode_flags, encode_topic_id, id, return_code].pack('CnnC')
564
+ end
565
+
566
+ def parse_body(buffer)
567
+ flags, topic_id, self.id, self.return_code = buffer.unpack('CnnC')
568
+ parse_flags(flags)
569
+ parse_topic_id(topic_id)
570
+ end
571
+ end
572
+
573
+ class Unsubscribe < Packet
574
+ attr_accessor :id
575
+ attr_accessor :topic_id
576
+ attr_accessor :topic_name
577
+
578
+ DEFAULTS = {
579
+ :id => 0x00,
580
+ :topic_id_type => :normal
581
+ }
582
+
583
+ def encode_body
584
+ unless id.is_a?(Integer)
585
+ raise "id must be an Integer"
586
+ end
587
+
588
+ [encode_flags, id, encode_topic].pack('Cna*')
589
+ end
590
+
591
+ def parse_body(buffer)
592
+ flags, self.id, topic = buffer.unpack('Cna*')
593
+ parse_flags(flags)
594
+ parse_topic(topic)
595
+ end
596
+ end
597
+
598
+ class Unsuback < Packet
599
+ attr_accessor :id
600
+
601
+ DEFAULTS = {
602
+ :id => 0x00,
603
+ }
604
+
605
+ def encode_body
606
+ unless id.is_a?(Integer)
607
+ raise "id must be an Integer"
608
+ end
609
+
610
+ [id].pack('n')
611
+ end
612
+
613
+ def parse_body(buffer)
614
+ self.id = buffer.unpack('n').first
615
+ end
616
+ end
617
+
618
+ class Pingreq < Packet
619
+ # No attributes
620
+ end
621
+
622
+ class Pingresp < Packet
623
+ # No attributes
624
+ end
625
+
626
+ class Disconnect < Packet
627
+ attr_accessor :duration
628
+
629
+ DEFAULTS = {
630
+ :duration => nil
631
+ }
632
+
633
+ def encode_body
634
+ if duration.nil? or duration == 0
635
+ ''
636
+ else
637
+ [duration].pack('n')
638
+ end
639
+ end
640
+
641
+ def parse_body(buffer)
642
+ if buffer.length == 2
643
+ self.duration = buffer.unpack('n').first
644
+ else
645
+ self.duration = nil
646
+ end
647
+ end
648
+ end
649
+
650
+ class Willtopicupd < Packet
651
+ attr_accessor :topic_name
652
+
653
+ DEFAULTS = {
654
+ :qos => 0,
655
+ :retain => false,
656
+ :topic_name => nil
657
+ }
658
+
659
+ def encode_body
660
+ if topic_name.nil? or topic_name.empty?
661
+ ''
662
+ else
663
+ [encode_flags, topic_name].pack('Ca*')
664
+ end
665
+ end
666
+
667
+ def parse_body(buffer)
668
+ if buffer.length > 1
669
+ flags, self.topic_name = buffer.unpack('Ca*')
670
+ parse_flags(flags)
671
+ else
672
+ self.topic_name = nil
673
+ end
674
+ end
675
+ end
676
+
677
+ class Willtopicresp < Packet
678
+ attr_accessor :return_code
679
+
680
+ DEFAULTS = {
681
+ :return_code => 0x00
682
+ }
683
+
684
+ def encode_body
685
+ unless return_code.is_a?(Integer)
686
+ raise "return_code must be an Integer"
687
+ end
688
+
689
+ [return_code].pack('C')
690
+ end
691
+
692
+ def parse_body(buffer)
693
+ self.return_code, _ignore = buffer.unpack('C')
694
+ end
695
+ end
696
+
697
+ class Willmsgupd < Packet
698
+ attr_accessor :data
699
+
700
+ def encode_body
701
+ data
702
+ end
703
+
704
+ def parse_body(buffer)
705
+ self.data = buffer
706
+ end
707
+ end
708
+
709
+ class Willmsgresp < Packet
710
+ attr_accessor :return_code
711
+
712
+ DEFAULTS = {
713
+ :return_code => 0x00
714
+ }
715
+
716
+ def encode_body
717
+ unless return_code.is_a?(Integer)
718
+ raise "return_code must be an Integer"
719
+ end
720
+
721
+ [return_code].pack('C')
722
+ end
723
+
724
+ def parse_body(buffer)
725
+ self.return_code, _ignore = buffer.unpack('C')
726
+ end
727
+ end
728
+
729
+ end
730
+
731
+
732
+ # An enumeration of the MQTT-SN packet types
733
+ PACKET_TYPES = {
734
+ 0x00 => MQTT::SN::Packet::Advertise,
735
+ 0x01 => MQTT::SN::Packet::Searchgw,
736
+ 0x02 => MQTT::SN::Packet::Gwinfo,
737
+ 0x04 => MQTT::SN::Packet::Connect,
738
+ 0x05 => MQTT::SN::Packet::Connack,
739
+ 0x06 => MQTT::SN::Packet::Willtopicreq,
740
+ 0x07 => MQTT::SN::Packet::Willtopic,
741
+ 0x08 => MQTT::SN::Packet::Willmsgreq,
742
+ 0x09 => MQTT::SN::Packet::Willmsg,
743
+ 0x0a => MQTT::SN::Packet::Register,
744
+ 0x0b => MQTT::SN::Packet::Regack,
745
+ 0x0c => MQTT::SN::Packet::Publish,
746
+ 0x0d => MQTT::SN::Packet::Puback,
747
+ 0x0e => MQTT::SN::Packet::Pubcomp,
748
+ 0x0f => MQTT::SN::Packet::Pubrec,
749
+ 0x10 => MQTT::SN::Packet::Pubrel,
750
+ 0x12 => MQTT::SN::Packet::Subscribe,
751
+ 0x13 => MQTT::SN::Packet::Suback,
752
+ 0x14 => MQTT::SN::Packet::Unsubscribe,
753
+ 0x15 => MQTT::SN::Packet::Unsuback,
754
+ 0x16 => MQTT::SN::Packet::Pingreq,
755
+ 0x17 => MQTT::SN::Packet::Pingresp,
756
+ 0x18 => MQTT::SN::Packet::Disconnect,
757
+ 0x1a => MQTT::SN::Packet::Willtopicupd,
758
+ 0x1b => MQTT::SN::Packet::Willtopicresp,
759
+ 0x1c => MQTT::SN::Packet::Willmsgupd,
760
+ 0x1d => MQTT::SN::Packet::Willmsgresp,
761
+ }
762
+
763
+ end