em-mqtt-sn 0.0.2

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.
@@ -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