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