em-mqtt-sn 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 89cbe9cba1cf7a7b09bf5bef4ec54c47a2953f4d
4
+ data.tar.gz: 5e1138b972b3ac617062a9dadfb2e6afffac34b2
5
+ SHA512:
6
+ metadata.gz: ab1388f070cc1860fc8d73d30e3e1a5d33d9b1aafecaf369086b731ca710d7382cc1042500193e1e5e688fbfda41a186af8e0ce2a1d9578e67620a8a81de1372
7
+ data.tar.gz: bc7dabab0a5596af64424fedb78e785449518bddc5eeea86a1a4a4a5eb3298155a4537a2b79cebeada9b3f78633f66366710ed0d14d55b0ddc73602993d1257a
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+ =====================
3
+
4
+ Copyright (c) Nicholas J Humfrey
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
data/NEWS.md ADDED
@@ -0,0 +1,14 @@
1
+ EventMachine MQTT-SN NEWS
2
+ =========================
3
+
4
+ Version 0.0.2 (2015-03-03)
5
+ --------------------------
6
+
7
+ * Renamed gem from mqtts to mqtt-sn.
8
+ * Fixes for new version of MQTT gem
9
+
10
+
11
+ Version 0.0.1 (2013-04-20)
12
+ --------------------------
13
+
14
+ * Initial Release.
@@ -0,0 +1,54 @@
1
+ ruby-em-mqtt-sn
2
+ ===============
3
+
4
+ This gem adds MQTT-SN (MQTT For Sensor Networks) protocol support to EventMachine,
5
+ an event-processing library for Ruby.
6
+
7
+ It also includes a MQTT-SN gateway, to connect MQTT-SN clients to a standard [MQTT] server.
8
+
9
+ Usage: em-mqtt-sn-gateway [options]
10
+
11
+ Options:
12
+ -D, --debug turn on debug logging
13
+ -a, --address [HOST] bind to HOST address (default: 0.0.0.0)
14
+ -p, --port [PORT] UDP port number to run on (default: 1883)
15
+ -A, --server-address [HOST] MQTT server address to connect to (default: 127.0.0.1)
16
+ -P, --server-port [PORT] MQTT server port to connect to (default: 1883)
17
+ -h, --help show this message
18
+ --version show version
19
+
20
+
21
+ Example
22
+ -------
23
+
24
+ $ sudo gem install em-mqtt-sn
25
+ $ em-mqtt-sn-gateway -A test.mosquitto.org
26
+ I, [2013-04-20T12:08:56.850572 #29588] INFO -- : Starting MQTT-SN gateway on UDP 0.0.0.0:1883
27
+ I, [2013-04-20T12:08:56.850646 #29588] INFO -- : Server address test.mosquitto.org:1883
28
+ I, [2013-04-20T12:09:00.577446 #29588] INFO -- : mqtt-sn-tools-29710 is now connected
29
+ I, [2013-04-20T12:09:00.578032 #29588] INFO -- : mqtt-sn-tools-29710 subscribing to 'test'
30
+ I, [2013-04-20T12:09:00.601937 #29588] INFO -- : mqtt-sn-tools-29710 recieved publish to 'test'
31
+ I, [2013-04-20T12:09:07.770269 #29588] INFO -- : mqtt-sn-tools-29713 is now connected
32
+ I, [2013-04-20T12:09:07.770733 #29588] INFO -- : mqtt-sn-tools-29713 publishing to 'test'
33
+ I, [2013-04-20T12:09:07.783940 #29588] INFO -- : mqtt-sn-tools-29710 recieved publish to 'test'
34
+ I, [2013-04-20T12:09:22.815726 #29588] INFO -- : Disconnected: mqtt-sn-tools-29713
35
+
36
+
37
+ License
38
+ -------
39
+
40
+ The em-mqtt-sn gem is licensed under the terms of the MIT license.
41
+ See the file LICENSE for details.
42
+
43
+
44
+ Contact
45
+ -------
46
+
47
+ * Author: Nicholas J Humfrey
48
+ * Email: njh@aelius.com
49
+ * Twitter: [@njh]
50
+ * Home Page: http://www.aelius.com/njh/
51
+
52
+
53
+ [MQTT]: http://mqtt.org/
54
+ [@njh]: http://twitter.com/njh
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib')))
4
+ require 'em/mqtt-sn'
5
+
6
+ EventMachine::MQTTSN::Gateway.new(ARGV).run
@@ -0,0 +1,25 @@
1
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..')))
2
+
3
+ require 'eventmachine'
4
+ require 'logger'
5
+ require 'em/mqtt'
6
+
7
+ module EventMachine::MQTTSN
8
+
9
+ DEFAULT_HOST = 'localhost'
10
+ DEFAULT_PORT = 1883
11
+
12
+ class Exception < Exception
13
+ end
14
+
15
+ class ProtocolException < MQTT::Exception
16
+ end
17
+
18
+ require "em/mqtt-sn/version"
19
+
20
+ autoload :ServerConnection, 'em/mqtt-sn/server_connection'
21
+ autoload :Gateway, 'em/mqtt-sn/gateway'
22
+ autoload :GatewayHandler, 'em/mqtt-sn/gateway_handler'
23
+ autoload :Packet, 'em/mqtt-sn/packet'
24
+
25
+ end
@@ -0,0 +1,81 @@
1
+ require 'optparse'
2
+
3
+ class EventMachine::MQTTSN::Gateway
4
+ attr_accessor :local_address
5
+ attr_accessor :local_port
6
+ attr_accessor :server_address
7
+ attr_accessor :server_port
8
+ attr_accessor :logger
9
+
10
+ def initialize(args=[])
11
+ # Set defaults
12
+ self.local_address = "0.0.0.0"
13
+ self.local_port = EventMachine::MQTTSN::DEFAULT_PORT
14
+ self.server_address = "127.0.0.1"
15
+ self.server_port = MQTT::DEFAULT_PORT
16
+ self.logger = Logger.new(STDOUT)
17
+ self.logger.level = Logger::INFO
18
+ parse(args) unless args.empty?
19
+ end
20
+
21
+ def parse(args)
22
+ OptionParser.new("", 28, ' ') do |opts|
23
+ opts.banner = "Usage: #{File.basename $0} [options]"
24
+
25
+ opts.separator ""
26
+ opts.separator "Options:"
27
+
28
+ opts.on("-D", "--debug", "turn on debug logging") do
29
+ self.logger.level = Logger::DEBUG
30
+ end
31
+
32
+ opts.on("-a", "--address [HOST]", "bind to HOST address (default: #{local_address})") do |address|
33
+ self.local_address = address
34
+ end
35
+
36
+ opts.on("-p", "--port [PORT]", "UDP port number to run on (default: #{local_port})") do |port|
37
+ self.local_port = port
38
+ end
39
+
40
+ opts.on("-A", "--server-address [HOST]", "MQTT server address to connect to (default: #{server_address})") do |address|
41
+ self.server_address = address
42
+ end
43
+
44
+ opts.on("-P", "--server-port [PORT]", "MQTT server port to connect to (default: #{server_port})") do |port|
45
+ self.server_port = port
46
+ end
47
+
48
+ opts.on_tail("-h", "--help", "show this message") do
49
+ puts opts
50
+ exit
51
+ end
52
+
53
+ opts.on_tail("--version", "show version") do
54
+ puts EventMachine::MQTTSN::VERSION
55
+ exit
56
+ end
57
+
58
+ opts.parse!(args)
59
+ end
60
+ end
61
+
62
+ def run
63
+ EventMachine.run do
64
+ # hit Control + C to stop
65
+ Signal.trap("INT") { EventMachine.stop }
66
+ Signal.trap("TERM") { EventMachine.stop }
67
+
68
+ logger.info("Starting MQTT-SN gateway on UDP #{local_address}:#{local_port}")
69
+ logger.info("MQTT server address #{server_address}:#{server_port}")
70
+ EventMachine.open_datagram_socket(
71
+ local_address,
72
+ local_port,
73
+ EventMachine::MQTTSN::GatewayHandler,
74
+ :logger => logger,
75
+ :server_address => server_address,
76
+ :server_port => server_port
77
+ )
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,216 @@
1
+ #
2
+ # There is only a single instance of GatewayHandler which
3
+ # processes UDP packets from all MQTT-SN clients.
4
+ #
5
+
6
+ class EventMachine::MQTTSN::GatewayHandler < EventMachine::Connection
7
+ attr_reader :logger
8
+ attr_reader :connections
9
+ attr_reader :server_address
10
+ attr_reader :server_port
11
+
12
+ def initialize(attr)
13
+ @connections = {}
14
+ attr.each_pair do |k,v|
15
+ instance_variable_set("@#{k}", v)
16
+ end
17
+
18
+ # Run the cleanup task periodically
19
+ EventMachine.add_periodic_timer(10) { cleanup }
20
+ end
21
+
22
+ # UDP packet received by gateway
23
+ def receive_data(data)
24
+ packet = EventMachine::MQTTSN::Packet.parse(data)
25
+ unless packet.nil?
26
+ process_packet(get_peername, packet)
27
+ end
28
+ end
29
+
30
+ # Incoming packet received from client
31
+ def process_packet(peername, packet)
32
+ logger.debug("Received MQTT-SN: #{packet.class}")
33
+
34
+ if packet.class == EventMachine::MQTTSN::Packet::Connect
35
+ connect(peername, packet)
36
+ else
37
+ connection = @connections[peername]
38
+ unless connection.nil? or !connection.connected?
39
+ case packet
40
+ when EventMachine::MQTTSN::Packet::Register
41
+ register(connection, packet)
42
+ when EventMachine::MQTTSN::Packet::Publish
43
+ publish(connection, packet)
44
+ when EventMachine::MQTTSN::Packet::Subscribe
45
+ subscribe(connection, packet)
46
+ when EventMachine::MQTTSN::Packet::Pingreq
47
+ connection.send_packet MQTT::Packet::Pingreq.new
48
+ when EventMachine::MQTTSN::Packet::Pingresp
49
+ connection.send_packet MQTT::Packet::Pingresp.new
50
+ when EventMachine::MQTTSN::Packet::Disconnect
51
+ disconnect(connection)
52
+ else
53
+ logger.warn("Unable to handle MQTT-SN packet of type: #{packet.class}")
54
+ end
55
+ else
56
+ logger.warn("Received MQTT-SN packet of type: #{packet.class} while not connected")
57
+ end
58
+ end
59
+ end
60
+
61
+ # CONNECT received from client - establish connection to server
62
+ def connect(peername, packet)
63
+ # If connection already exists, disconnect first
64
+ if @connections.has_key?(peername)
65
+ logger.warn("Received CONNECT while already connected")
66
+ @connections[peername].disconnect
67
+ end
68
+
69
+ # Create a TCP connection to the server
70
+ client_port, client_address = Socket.unpack_sockaddr_in(peername)
71
+ connection = EventMachine::connect(
72
+ server_address, server_port,
73
+ EventMachine::MQTTSN::ServerConnection,
74
+ self, client_address, client_port
75
+ )
76
+
77
+ # Store the client ID
78
+ connection.client_id = packet.client_id
79
+
80
+ # Send a MQTT connect packet to the server
81
+ connection.send_packet MQTT::Packet::Connect.new(
82
+ :client_id => packet.client_id,
83
+ :keep_alive => packet.keep_alive,
84
+ :clean_session => packet.clean_session
85
+ )
86
+
87
+ # Add the connection to the table
88
+ @connections[peername] = connection
89
+ end
90
+
91
+ # Handle a MQTT packet coming back from the server
92
+ def relay_from_server(connection, packet)
93
+ logger.debug("Received MQTT: #{packet.inspect}")
94
+ case packet
95
+ when MQTT::Packet::Connack
96
+ # FIXME: re-map the return code
97
+ mqttsn_packet = EventMachine::MQTTSN::Packet::Connack.new(
98
+ :return_code => packet.return_code
99
+ )
100
+ if packet.return_code == 0
101
+ logger.info("#{connection.client_id} is now connected")
102
+ else
103
+ logger.info("#{connection.client_id} failed to connect: #{packet.return_msg}")
104
+ end
105
+ when MQTT::Packet::Suback
106
+ # Check that it is a response to a request we made
107
+ request = connection.remove_from_pending(packet.id)
108
+ if request
109
+ logger.debug("#{connection.client_id} now subscribed to '#{request.topic_name}'")
110
+ topic_id_type, topic_id = connection.get_topic_id(request.topic_name)
111
+ mqttsn_packet = EventMachine::MQTTSN::Packet::Suback.new(
112
+ :topic_id_type => topic_id_type,
113
+ :topic_id => topic_id,
114
+ :qos => packet.granted_qos.first,
115
+ :id => packet.id,
116
+ :return_code => 0x00
117
+ )
118
+ else
119
+ logger.warn("Received Suback from server for something we didn't request: #{packet.inspect}")
120
+ end
121
+ when MQTT::Packet::Publish
122
+ logger.info("#{connection.client_id} recieved publish to '#{packet.topic}'")
123
+ # FIXME: send register if this is a new topic
124
+ topic_id_type, topic_id = connection.get_topic_id(packet.topic)
125
+ mqttsn_packet = EventMachine::MQTTSN::Packet::Publish.new(
126
+ :duplicate => packet.duplicate,
127
+ :qos => packet.qos,
128
+ :retain => packet.retain,
129
+ :topic_id_type => topic_id_type,
130
+ :topic_id => topic_id,
131
+ :id => packet.id,
132
+ :data => packet.payload
133
+ )
134
+ when MQTT::Packet::Pingreq
135
+ mqttsn_packet = EventMachine::MQTTSN::Packet::Pingreq.new
136
+ when MQTT::Packet::Pingresp
137
+ mqttsn_packet = EventMachine::MQTTSN::Packet::Pingresp.new
138
+ else
139
+ logger.warn("Unable to handle MQTT packet of type: #{packet.class}")
140
+ end
141
+
142
+ unless mqttsn_packet.nil?
143
+ send_datagram(mqttsn_packet.to_s, connection.client_address, connection.client_port)
144
+ end
145
+ end
146
+
147
+ # REGISTER received from client
148
+ def register(connection, packet)
149
+ regack = EventMachine::MQTTSN::Packet::Regack.new(
150
+ :topic_id_type => :normal,
151
+ :id => packet.id
152
+ )
153
+
154
+ topic_id_type, topic_id = connection.get_topic_id(packet.topic_name)
155
+ unless topic_id.nil?
156
+ regack.return_code = 0x00 # Accepted
157
+ regack.topic_id = topic_id
158
+ else
159
+ regack.return_code = 0x02 # Rejected: invalid topic ID
160
+ end
161
+ send_data(regack.to_s)
162
+ end
163
+
164
+ # PUBLISH received from client - pass it on to the server
165
+ def publish(connection, packet)
166
+ if packet.topic_id_type == :short
167
+ topic_name = packet.topic_id
168
+ elsif packet.topic_id_type == :normal
169
+ topic_name = connection.get_topic_name(packet.topic_id)
170
+ end
171
+
172
+ if topic_name
173
+ logger.info("#{connection.client_id} publishing to '#{topic_name}'")
174
+ connection.send_packet MQTT::Packet::Publish.new(
175
+ :topic => topic_name,
176
+ :payload => packet.data,
177
+ :retain => packet.retain,
178
+ :qos => packet.qos
179
+ )
180
+ else
181
+ # FIXME: disconnect?
182
+ logger.warn("Invalid topic ID: #{packet.topic_id}")
183
+ end
184
+ end
185
+
186
+ # SUBSCRIBE received from client - pass it on to the server
187
+ def subscribe(connection, packet)
188
+ logger.info("#{connection.client_id} subscribing to '#{packet.topic_name}'")
189
+ mqtt_packet = MQTT::Packet::Subscribe.new(
190
+ :id => packet.id,
191
+ :topics => packet.topic_name
192
+ )
193
+ connection.add_to_pending(packet)
194
+ connection.send_packet(mqtt_packet)
195
+ end
196
+
197
+ # Disconnect client from server
198
+ def disconnect(connection)
199
+ if connection.connected?
200
+ logger.info("Disconnected: #{connection.client_id}")
201
+ mqttsn_packet = EventMachine::MQTTSN::Packet::Disconnect.new
202
+ send_datagram(mqttsn_packet.to_s, connection.client_address, connection.client_port)
203
+ connection.disconnect
204
+ end
205
+ end
206
+
207
+ # Periodic task to cleanup dead connections
208
+ def cleanup
209
+ connections.each_pair do |key,connection|
210
+ unless connection.connected?
211
+ logger.debug("Destroying connection: #{connection.client_id}")
212
+ @connections.delete(key)
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,348 @@
1
+ module EventMachine::MQTTSN
2
+
3
+ # Class representing a MQTTSN Packet
4
+ # Performs binary encoding and decoding of headers
5
+ class Packet
6
+ attr_accessor :duplicate # Duplicate delivery flag
7
+ attr_accessor :qos # Quality of Service level
8
+ attr_accessor :retain # Retain flag
9
+ attr_accessor :request_will # Request that gateway prompts for Will
10
+ attr_accessor :clean_session # When true, subscriptions are deleted after disconnect
11
+ attr_accessor :topic_id_type # One of :normal, :predefined or :short
12
+
13
+ DEFAULTS = {}
14
+
15
+ # Parse buffer into new packet object
16
+ def self.parse(buffer)
17
+ # Parse the fixed header (length and type)
18
+ length,type_id,body = buffer.unpack('CCa*')
19
+ if length == 1
20
+ length,type_id,body = buffer.unpack('xnCa*')
21
+ end
22
+
23
+ # Double-check the length
24
+ if buffer.length != length
25
+ raise ProtocolException.new("Length of packet is not the same as the length header")
26
+ end
27
+
28
+ packet_class = PACKET_TYPES[type_id]
29
+ if packet_class.nil?
30
+ raise ProtocolException.new("Invalid packet type identifier: #{type_id}")
31
+ end
32
+
33
+ # Create a new packet object
34
+ packet = packet_class.new
35
+ packet.parse_body(body)
36
+
37
+ return packet
38
+ end
39
+
40
+ # Create a new empty packet
41
+ def initialize(args={})
42
+ update_attributes(self.class::DEFAULTS.merge(args))
43
+ end
44
+
45
+ def update_attributes(attr={})
46
+ attr.each_pair do |k,v|
47
+ send("#{k}=", v)
48
+ end
49
+ end
50
+
51
+ # Get the identifer for this packet type
52
+ def type_id
53
+ PACKET_TYPES.each_pair do |key, value|
54
+ return key if self.class == value
55
+ end
56
+ raise "Invalid packet type: #{self.class}"
57
+ end
58
+
59
+ # Serialise the packet
60
+ def to_s
61
+ # Get the packet's variable header and payload
62
+ body = self.encode_body
63
+
64
+ # Build up the body length field bytes
65
+ body_length = body.length
66
+ if body_length > 65531
67
+ raise "Packet too big"
68
+ elsif body_length > 253
69
+ [0x01, body_length + 4, type_id].pack('CnC') + body
70
+ else
71
+ [body_length + 2, type_id].pack('CC') + body
72
+ end
73
+ end
74
+
75
+ def parse_body(buffer)
76
+ end
77
+
78
+ protected
79
+
80
+ def parse_flags(flags)
81
+ self.duplicate = ((flags & 0x80) >> 7) == 0x01
82
+ self.qos = (flags & 0x60) >> 5
83
+ self.qos = -1 if self.qos == 3
84
+ self.retain = ((flags & 0x10) >> 4) == 0x01
85
+ self.request_will = ((flags & 0x08) >> 3) == 0x01
86
+ self.clean_session = ((flags & 0x04) >> 2) == 0x01
87
+ case (flags & 0x03)
88
+ when 0x0
89
+ self.topic_id_type = :normal
90
+ when 0x1
91
+ self.topic_id_type = :predefined
92
+ when 0x2
93
+ self.topic_id_type = :short
94
+ else
95
+ self.topic_id_type = nil
96
+ end
97
+ end
98
+
99
+ # Get serialisation of packet's body (variable header and payload)
100
+ def encode_body
101
+ '' # No body by default
102
+ end
103
+
104
+ def encode_flags
105
+ flags = 0x00
106
+ flags += 0x80 if duplicate
107
+ flags += 0x10 if retain
108
+ flags += 0x08 if request_will
109
+ flags += 0x04 if clean_session
110
+ case topic_id_type
111
+ when :normal
112
+ flags += 0x0
113
+ when :predefined
114
+ flags += 0x1
115
+ when :short
116
+ flags += 0x2
117
+ end
118
+ return flags
119
+ end
120
+
121
+ def encode_topic_id
122
+ if topic_id_type == :short
123
+ (topic_id[0].ord << 8) + topic_id[1].ord
124
+ else
125
+ topic_id
126
+ end
127
+ end
128
+
129
+ def parse_topic_id(topic_id)
130
+ if topic_id_type == :short
131
+ int = topic_id.to_i
132
+ self.topic_id = [(int >> 8) & 0xFF, int & 0xFF].pack('CC')
133
+ else
134
+ self.topic_id = topic_id
135
+ end
136
+ end
137
+
138
+ class Connect < Packet
139
+ attr_accessor :keep_alive
140
+ attr_accessor :client_id
141
+
142
+ DEFAULTS = {
143
+ :request_will => false,
144
+ :clean_session => true,
145
+ :keep_alive => 15
146
+ }
147
+
148
+ # Get serialisation of packet's body
149
+ def encode_body
150
+ if @client_id.nil? or @client_id.length < 1 or @client_id.length > 23
151
+ raise "Invalid client identifier when serialising packet"
152
+ end
153
+
154
+ [encode_flags, 0x01, keep_alive, client_id].pack('CCna*')
155
+ end
156
+
157
+ def parse_body(buffer)
158
+ flags, protocol_id, self.keep_alive, self.client_id = buffer.unpack('CCna*')
159
+
160
+ if protocol_id != 0x01
161
+ raise ProtocolException.new("Unsupported protocol ID number: #{protocol_id}")
162
+ end
163
+
164
+ parse_flags(flags)
165
+ end
166
+ end
167
+
168
+ class Connack < Packet
169
+ attr_accessor :return_code
170
+
171
+ # Get a string message corresponding to a return code
172
+ def return_msg
173
+ case return_code
174
+ when 0x00
175
+ "Accepted"
176
+ when 0x01
177
+ "Rejected: congestion"
178
+ when 0x02
179
+ "Rejected: invalid topic ID"
180
+ when 0x03
181
+ "Rejected: not supported"
182
+ else
183
+ "Rejected: error code #{return_code}"
184
+ end
185
+ end
186
+
187
+ def encode_body
188
+ [return_code].pack('C')
189
+ end
190
+
191
+ def parse_body(buffer)
192
+ self.return_code = buffer.unpack('C')[0]
193
+ end
194
+ end
195
+
196
+ class Register < Packet
197
+ attr_accessor :id
198
+ attr_accessor :topic_id
199
+ attr_accessor :topic_name
200
+
201
+ DEFAULTS = {
202
+ :id => 0x00,
203
+ :topic_id_type => :normal
204
+ }
205
+
206
+ def encode_body
207
+ [encode_topic_id, id, topic_name].pack('nna*')
208
+ end
209
+
210
+ def parse_body(buffer)
211
+ topic_id, self.id, self.topic_name = buffer.unpack('nna*')
212
+ parse_topic_id(topic_id)
213
+ end
214
+ end
215
+
216
+ class Regack < Packet
217
+ attr_accessor :id
218
+ attr_accessor :topic_id
219
+ attr_accessor :return_code
220
+
221
+ DEFAULTS = {
222
+ :id => 0x00,
223
+ :topic_id_type => :normal
224
+ }
225
+
226
+ def encode_body
227
+ [encode_topic_id, id, return_code].pack('nnC')
228
+ end
229
+
230
+ def parse_body(buffer)
231
+ topic_id, self.id, self.return_code = buffer.unpack('nnC')
232
+ parse_topic_id(topic_id)
233
+ end
234
+ end
235
+
236
+ class Publish < Packet
237
+ attr_accessor :topic_id
238
+ attr_accessor :id
239
+ attr_accessor :data
240
+
241
+ DEFAULTS = {
242
+ :id => 0x00,
243
+ :duplicate => false,
244
+ :qos => 0,
245
+ :retain => false,
246
+ :topic_id_type => :normal
247
+ }
248
+
249
+ def encode_body
250
+ [encode_flags, encode_topic_id, id, data].pack('Cnna*')
251
+ end
252
+
253
+ def parse_body(buffer)
254
+ flags, topic_id, self.id, self.data = buffer.unpack('Cnna*')
255
+ parse_flags(flags)
256
+ parse_topic_id(topic_id)
257
+ end
258
+ end
259
+
260
+ class Subscribe < Packet
261
+ attr_accessor :id
262
+ attr_accessor :topic_id
263
+ attr_accessor :topic_name
264
+
265
+ DEFAULTS = {
266
+ :id => 0x00,
267
+ :topic_id_type => :normal
268
+ }
269
+
270
+ def encode_body
271
+ [encode_flags, id, topic_name].pack('Cna*')
272
+ end
273
+
274
+ def parse_body(buffer)
275
+ flags, self.id, self.topic_name = buffer.unpack('Cna*')
276
+ parse_flags(flags)
277
+ end
278
+ end
279
+
280
+ class Suback < Packet
281
+ attr_accessor :id
282
+ attr_accessor :topic_id
283
+ attr_accessor :return_code
284
+
285
+ DEFAULTS = {
286
+ :qos => 0,
287
+ :id => 0x00,
288
+ :topic_id_type => :normal
289
+ }
290
+
291
+ def encode_body
292
+ [encode_flags, encode_topic_id, id, return_code].pack('CnnC')
293
+ end
294
+
295
+ def parse_body(buffer)
296
+ flags, topic_id, self.id, self.return_code = buffer.unpack('CnnC')
297
+ parse_flags(flags)
298
+ parse_topic_id(topic_id)
299
+ end
300
+ end
301
+
302
+ class Pingreq < Packet
303
+ # No attributes
304
+ end
305
+
306
+ class Pingresp < Packet
307
+ # No attributes
308
+ end
309
+
310
+ class Disconnect < Packet
311
+ # No attributes
312
+ end
313
+
314
+ end
315
+
316
+
317
+ # An enumeration of the MQTT-SN packet types
318
+ PACKET_TYPES = {
319
+ # 0x00 => EventMachine::MQTTSN::Packet::Advertise,
320
+ # 0x01 => EventMachine::MQTTSN::Packet::Searchgw,
321
+ # 0x02 => EventMachine::MQTTSN::Packet::Gwinfo,
322
+ 0x04 => EventMachine::MQTTSN::Packet::Connect,
323
+ 0x05 => EventMachine::MQTTSN::Packet::Connack,
324
+ # 0x06 => EventMachine::MQTTSN::Packet::Willtopicreq,
325
+ # 0x07 => EventMachine::MQTTSN::Packet::Willtopic,
326
+ # 0x08 => EventMachine::MQTTSN::Packet::Willmsgreq,
327
+ # 0x09 => EventMachine::MQTTSN::Packet::Willmsg,
328
+ 0x0a => EventMachine::MQTTSN::Packet::Register,
329
+ 0x0b => EventMachine::MQTTSN::Packet::Regack,
330
+ 0x0c => EventMachine::MQTTSN::Packet::Publish,
331
+ # 0x0d => EventMachine::MQTTSN::Packet::Puback,
332
+ # 0x0e => EventMachine::MQTTSN::Packet::Pubcomp,
333
+ # 0x0f => EventMachine::MQTTSN::Packet::Pubrec,
334
+ # 0x10 => EventMachine::MQTTSN::Packet::Pubrel,
335
+ 0x12 => EventMachine::MQTTSN::Packet::Subscribe,
336
+ 0x13 => EventMachine::MQTTSN::Packet::Suback,
337
+ # 0x14 => EventMachine::MQTTSN::Packet::Unsubscribe,
338
+ # 0x15 => EventMachine::MQTTSN::Packet::Unsuback,
339
+ 0x16 => EventMachine::MQTTSN::Packet::Pingreq,
340
+ 0x17 => EventMachine::MQTTSN::Packet::Pingresp,
341
+ 0x18 => EventMachine::MQTTSN::Packet::Disconnect,
342
+ # 0x1a => EventMachine::MQTTSN::Packet::Willtopicupd,
343
+ # 0x1b => EventMachine::MQTTSN::Packet::Willtopicresp,
344
+ # 0x1c => EventMachine::MQTTSN::Packet::Willmsgupd,
345
+ # 0x1d => EventMachine::MQTTSN::Packet::Willmsgresp,
346
+ }
347
+
348
+ end