meshtastic 0.0.41 → 0.0.43
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/Gemfile +2 -2
- data/lib/meshtastic/mqtt.rb +110 -92
- data/lib/meshtastic/version.rb +1 -1
- data/lib/meshtastic.rb +187 -0
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 105d223bd94e02d4473ac5fd66ed0b8dc7eef73d44578eb427fc3d55eb51ae21
|
4
|
+
data.tar.gz: 216a3957a439408579d1d7b48dd552d394885bbc09b8dc3410d776e4c1eb37d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1381a13cff943403ef269c5a349529ab9c2f12e3a3af5b3e88c589f7fd427a6e45b23543251449e1d520471fdf5f6c8a11c55c56bfc24553dc2b02db7ace28d9
|
7
|
+
data.tar.gz: 01d01863635a22c2d3c42ace2a34076c4094ece8ef9629f08ae0e8da7c0a98bbc5815db26e2e4063fa462619caf710938628a8cd086f62411d1cd885017665a4
|
data/Gemfile
CHANGED
@@ -13,7 +13,7 @@ gemspec
|
|
13
13
|
# to review these custom flags (e.g. pg, serialport, etc).
|
14
14
|
gem 'bundler', '>=2.5.3'
|
15
15
|
gem 'bundle-audit', '0.1.0'
|
16
|
-
gem 'geocoder', '1.8.
|
16
|
+
gem 'geocoder', '1.8.3'
|
17
17
|
gem 'google-protobuf', '3.21.12'
|
18
18
|
gem 'mqtt', '0.6.0'
|
19
19
|
gem 'rake', '13.2.1'
|
@@ -21,7 +21,7 @@ gem 'rdoc', '6.6.3.1'
|
|
21
21
|
gem 'rspec', '3.13.0'
|
22
22
|
gem 'rubocop', '1.63.4'
|
23
23
|
gem 'rubocop-rake', '0.6.0'
|
24
|
-
gem 'rubocop-rspec', '2.29.
|
24
|
+
gem 'rubocop-rspec', '2.29.2'
|
25
25
|
gem 'rvm', '1.11.3.9'
|
26
26
|
gem 'tty-prompt', '0.23.1'
|
27
27
|
gem 'yard', '0.9.36'
|
data/lib/meshtastic/mqtt.rb
CHANGED
@@ -63,6 +63,96 @@ module Meshtastic
|
|
63
63
|
raise e
|
64
64
|
end
|
65
65
|
|
66
|
+
# Supported Method Parameters::
|
67
|
+
# Meshtastic::MQQT.decode_payload(
|
68
|
+
# payload: 'required - payload to recursively decode',
|
69
|
+
# msg_type: 'required - message type (e.g. :TEXT_MESSAGE_APP)',
|
70
|
+
# gps_metadata: 'optional - include GPS metadata in output (default: false)',
|
71
|
+
# )
|
72
|
+
|
73
|
+
public_class_method def self.decode_payload(opts = {})
|
74
|
+
payload = opts[:payload]
|
75
|
+
msg_type = opts[:msg_type]
|
76
|
+
gps_metadata = opts[:gps_metadata]
|
77
|
+
|
78
|
+
case msg_type
|
79
|
+
when :ADMIN_APP
|
80
|
+
decoder = Meshtastic::AdminMessage
|
81
|
+
when :ATAK_FORWARDER, :ATAK_PLUGIN
|
82
|
+
decoder = Meshtastic::TAKPacket
|
83
|
+
# when :AUDIO_APP
|
84
|
+
# decoder = Meshtastic::Audio
|
85
|
+
when :DETECTION_SENSOR_APP
|
86
|
+
decoder = Meshtastic::DeviceState
|
87
|
+
# when :IP_TUNNEL_APP
|
88
|
+
# decoder = Meshtastic::IpTunnel
|
89
|
+
when :MAP_REPORT_APP
|
90
|
+
decoder = Meshtastic::MapReport
|
91
|
+
# when :MAX
|
92
|
+
# decoder = Meshtastic::Max
|
93
|
+
when :NEIGHBORINFO_APP
|
94
|
+
decoder = Meshtastic::NeighborInfo
|
95
|
+
when :NODEINFO_APP
|
96
|
+
decoder = Meshtastic::User
|
97
|
+
when :PAXCOUNTER_APP
|
98
|
+
decoder = Meshtastic::Paxcount
|
99
|
+
when :POSITION_APP
|
100
|
+
decoder = Meshtastic::Position
|
101
|
+
# when :PRIVATE_APP
|
102
|
+
# decoder = Meshtastic::Private
|
103
|
+
when :RANGE_TEST_APP
|
104
|
+
# Unsure if this is the correct protobuf object
|
105
|
+
decoder = Meshtastic::FromRadio
|
106
|
+
when :REMOTE_HARDWARE_APP
|
107
|
+
decoder = Meshtastic::HardwareMessage
|
108
|
+
# when :REPLY_APP
|
109
|
+
# decoder = Meshtastic::Reply
|
110
|
+
when :ROUTING_APP
|
111
|
+
decoder = Meshtastic::Routing
|
112
|
+
when :SERIAL_APP
|
113
|
+
decoder = Meshtastic::SerialConnectionStatus
|
114
|
+
when :SIMULATOR_APP
|
115
|
+
decoder = Meshtastic::Compressed
|
116
|
+
when :STORE_FORWARD_APP
|
117
|
+
decoder = Meshtastic::StoreAndForward
|
118
|
+
when :TEXT_MESSAGE_APP
|
119
|
+
# Unsure if this is the correct protobuf object
|
120
|
+
# decoder = Meshtastic::MqttClientProxyMessage
|
121
|
+
decoder = Meshtastic::Data
|
122
|
+
when :TELEMETRY_APP
|
123
|
+
decoder = Meshtastic::Telemetry
|
124
|
+
when :TRACEROUTE_APP
|
125
|
+
decoder = Meshtastic::RouteDiscovery
|
126
|
+
when :WAYPOINT_APP
|
127
|
+
decoder = Meshtastic::Waypoint
|
128
|
+
# when :ZPS_APP
|
129
|
+
# decoder = Meshtastic::Zps
|
130
|
+
end
|
131
|
+
payload = decoder.decode(payload).to_h
|
132
|
+
if payload.keys.include?(:latitude_i) && payload.keys.include?(:longitude_i)
|
133
|
+
lat = payload[:latitude_i] * 0.0000001
|
134
|
+
lon = payload[:longitude_i] * 0.0000001
|
135
|
+
payload[:latitude] = lat
|
136
|
+
payload[:longitude] = lon
|
137
|
+
if gps_metadata
|
138
|
+
gps_search_resp = Meshtastic.gps_search(lat: lat, lon: lon)
|
139
|
+
payload[:gps_metadata] = gps_search_resp
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
if payload.keys.include?(:macaddr)
|
144
|
+
mac_hex_arr = payload[:macaddr].bytes.map { |byte| byte.to_s(16).rjust(2, '0') }
|
145
|
+
mac_str = mac_hex_arr.join(':')
|
146
|
+
payload[:macaddr] = mac_str
|
147
|
+
end
|
148
|
+
|
149
|
+
payload
|
150
|
+
rescue Google::Protobuf::ParseError
|
151
|
+
payload
|
152
|
+
rescue StandardError => e
|
153
|
+
raise e
|
154
|
+
end
|
155
|
+
|
66
156
|
# Supported Method Parameters::
|
67
157
|
# Meshtastic::MQQT.subscribe(
|
68
158
|
# mqtt_obj: 'required - mqtt_obj returned from #connect method'
|
@@ -72,7 +162,8 @@ module Meshtastic
|
|
72
162
|
# psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: "AQ==" })',
|
73
163
|
# qos: 'optional - quality of service (default: 0)',
|
74
164
|
# filter: 'optional - comma-delimited string(s) to filter on in message (default: nil)',
|
75
|
-
# gps_metadata: 'optional - include GPS metadata in output (default: false)'
|
165
|
+
# gps_metadata: 'optional - include GPS metadata in output (default: false)',
|
166
|
+
# include_raw: 'optional - include raw packet data in output (default: false)'
|
76
167
|
# )
|
77
168
|
|
78
169
|
public_class_method def self.subscribe(opts = {})
|
@@ -93,6 +184,7 @@ module Meshtastic
|
|
93
184
|
json = opts[:json] ||= false
|
94
185
|
filter = opts[:filter]
|
95
186
|
gps_metadata = opts[:gps_metadata] ||= false
|
187
|
+
include_raw = opts[:include_raw] ||= false
|
96
188
|
|
97
189
|
# NOTE: Use MQTT Explorer for topic discovery
|
98
190
|
full_topic = "#{root_topic}/#{region}/#{channel}"
|
@@ -101,7 +193,7 @@ module Meshtastic
|
|
101
193
|
|
102
194
|
filter_arr = filter.to_s.split(',').map(&:strip)
|
103
195
|
mqtt_obj.get_packet do |packet_bytes|
|
104
|
-
|
196
|
+
raw_packet = packet_bytes.to_s.b if include_raw
|
105
197
|
raw_topic = packet_bytes.topic ||= ''
|
106
198
|
raw_payload = packet_bytes.payload ||= ''
|
107
199
|
|
@@ -123,10 +215,10 @@ module Meshtastic
|
|
123
215
|
message[:node_id_from] = "!#{message[:from].to_i.to_s(16)}"
|
124
216
|
message[:node_id_to] = "!#{message[:to].to_i.to_s(16)}"
|
125
217
|
|
218
|
+
# If encrypted_message is not nil, then decrypt
|
219
|
+
# the message prior to decoding.
|
126
220
|
encrypted_message = message[:encrypted]
|
127
|
-
# If encrypted_message is not nil, then decrypt the message
|
128
221
|
if encrypted_message.to_s.length.positive?
|
129
|
-
|
130
222
|
packet_id = message[:id]
|
131
223
|
packet_from = message[:from]
|
132
224
|
|
@@ -135,8 +227,8 @@ module Meshtastic
|
|
135
227
|
nonce = "#{nonce_packet_id}#{nonce_from_node}".b
|
136
228
|
|
137
229
|
psk = psks[:LongFast]
|
138
|
-
|
139
|
-
psk = psks[
|
230
|
+
target_channel = decoded_payload_hash[:channel_id].to_s.to_sym
|
231
|
+
psk = psks[target_channel] if psks.keys.include?(target_channel)
|
140
232
|
dec_psk = Base64.strict_decode64(psk)
|
141
233
|
|
142
234
|
cipher = OpenSSL::Cipher.new('AES-128-CTR')
|
@@ -146,99 +238,22 @@ module Meshtastic
|
|
146
238
|
cipher.iv = nonce
|
147
239
|
|
148
240
|
decrypted = cipher.update(encrypted_message) + cipher.final
|
149
|
-
message[:
|
241
|
+
message[:decoded] = Meshtastic::Data.decode(decrypted).to_h
|
242
|
+
message[:encrypted] = :decrypted
|
150
243
|
end
|
151
244
|
|
152
245
|
if message[:decoded]
|
246
|
+
# payload = Meshtastic::Data.decode(message[:decoded][:payload]).to_h
|
153
247
|
payload = message[:decoded][:payload]
|
154
|
-
|
155
248
|
msg_type = message[:decoded][:portnum]
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
# when :AUDIO_APP
|
162
|
-
# pb_obj = Meshtastic::Audio.decode(payload)
|
163
|
-
when :DETECTION_SENSOR_APP
|
164
|
-
pb_obj = Meshtastic::DeviceState.decode(payload)
|
165
|
-
# when :IP_TUNNEL_APP
|
166
|
-
# pb_obj = Meshtastic::IpTunnel.decode(payload)
|
167
|
-
when :MAP_REPORT_APP
|
168
|
-
pb_obj = Meshtastic::MapReport.decode(payload)
|
169
|
-
# when :MAX
|
170
|
-
# pb_obj = Meshtastic::Max.decode(payload)
|
171
|
-
when :NEIGHBORINFO_APP
|
172
|
-
pb_obj = Meshtastic::NeighborInfo.decode(payload)
|
173
|
-
when :NODEINFO_APP
|
174
|
-
pb_obj = Meshtastic::User.decode(payload)
|
175
|
-
when :PAXCOUNTER_APP
|
176
|
-
pb_obj = Meshtastic::Paxcount.decode(payload)
|
177
|
-
when :POSITION_APP
|
178
|
-
pb_obj = Meshtastic::Position.decode(payload)
|
179
|
-
# when :PRIVATE_APP
|
180
|
-
# pb_obj = Meshtastic::Private.decode(payload)
|
181
|
-
when :RANGE_TEST_APP
|
182
|
-
# Unsure if this is the correct protobuf object
|
183
|
-
pb_obj = Meshtastic::FromRadio.decode(payload)
|
184
|
-
when :REMOTE_HARDWARE_APP
|
185
|
-
pb_obj = Meshtastic::HardwareMessage.decode(payload)
|
186
|
-
# when :REPLY_APP
|
187
|
-
# pb_obj = Meshtastic::Reply.decode(payload)
|
188
|
-
when :ROUTING_APP
|
189
|
-
pb_obj = Meshtastic::Routing.decode(payload)
|
190
|
-
when :SERIAL_APP
|
191
|
-
pb_obj = Meshtastic::SerialConnectionStatus.decode(payload)
|
192
|
-
when :SIMULATOR_APP,
|
193
|
-
:TEXT_MESSAGE_COMPRESSED_APP
|
194
|
-
# Unsure if this is the correct protobuf object
|
195
|
-
# for TEXT_MESSAGE_COMPRESSED_APP
|
196
|
-
pb_obj = Meshtastic::Compressed.decode(payload)
|
197
|
-
when :STORE_FORWARD_APP
|
198
|
-
pb_obj = Meshtastic::StoreAndForward.decode(payload)
|
199
|
-
when :TEXT_MESSAGE_APP
|
200
|
-
# Unsure if this is the correct protobuf object
|
201
|
-
# pb_obj = Meshtastic::MqttClientProxyMessage.decode(payload)
|
202
|
-
pb_obj = Meshtastic::Data.decode(payload)
|
203
|
-
when :TELEMETRY_APP
|
204
|
-
pb_obj = Meshtastic::Telemetry.decode(payload)
|
205
|
-
when :TRACEROUTE_APP
|
206
|
-
pb_obj = Meshtastic::RouteDiscovery.decode(payload)
|
207
|
-
# when :UNKNOWN_APP
|
208
|
-
# pb_obj = Meshtastic.Unknown.decode(payload)
|
209
|
-
when :WAYPOINT_APP
|
210
|
-
pb_obj = Meshtastic::Waypoint.decode(payload)
|
211
|
-
# when :ZPS_APP
|
212
|
-
# pb_obj = Meshtastic::Zps.decode(payload)
|
213
|
-
else
|
214
|
-
puts "WARNING: Unknown message type: #{msg_type}"
|
215
|
-
end
|
216
|
-
# Overwrite the payload with the decoded protobuf object
|
217
|
-
# message[:decoded][:payload] = pb_obj.to_h unless msg_type == :TRACEROUTE_APP
|
218
|
-
message[:decoded][:payload] = pb_obj.to_h
|
219
|
-
if message[:decoded][:payload].keys.include?(:latitude_i) &&
|
220
|
-
message[:decoded][:payload].keys.include?(:longitude_i) &&
|
221
|
-
gps_metadata
|
222
|
-
|
223
|
-
latitude = pb_obj.to_h[:latitude_i] * 0.0000001
|
224
|
-
longitude = pb_obj.to_h[:longitude_i] * 0.0000001
|
225
|
-
message[:decoded][:payload][:gps_metadata] = gps_search(
|
226
|
-
lat: latitude,
|
227
|
-
lon: longitude
|
228
|
-
).first.data
|
229
|
-
end
|
230
|
-
|
231
|
-
# If we there's a mac address, make it look like one.
|
232
|
-
if message[:decoded][:payload].keys.include?(:macaddr)
|
233
|
-
macaddr = message[:decoded][:payload][:macaddr]
|
234
|
-
macaddr_fmt = macaddr.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join(':')
|
235
|
-
message[:decoded][:payload][:macaddr] = macaddr_fmt
|
236
|
-
end
|
237
|
-
# puts pb_obj.public_methods
|
238
|
-
# message[:decoded][:pb_obj] = pb_obj
|
249
|
+
message[:decoded][:payload] = decode_payload(
|
250
|
+
payload: payload,
|
251
|
+
msg_type: msg_type,
|
252
|
+
gps_metadata: gps_metadata
|
253
|
+
)
|
239
254
|
end
|
240
255
|
|
241
|
-
|
256
|
+
message[:raw_packet] = raw_packet if include_raw
|
242
257
|
decoded_payload_hash[:packet] = message
|
243
258
|
unless block_given?
|
244
259
|
message[:stdout] = 'pretty'
|
@@ -249,8 +264,11 @@ module Meshtastic
|
|
249
264
|
ArgumentError => e
|
250
265
|
|
251
266
|
message[:decrypted] = e.message if e.message.include?('key must be')
|
267
|
+
message[:decrypted] = 'unable to decrypt - psk?' if e.message.include?('occurred during parsing')
|
252
268
|
decoded_payload_hash[:packet] = message
|
253
269
|
unless block_given?
|
270
|
+
puts "WARNING: #{e.inspect} - MSG IS >>>"
|
271
|
+
# puts e.backtrace
|
254
272
|
message[:stdout] = 'inspect'
|
255
273
|
stdout_message = decoded_payload_hash.inspect
|
256
274
|
end
|
data/lib/meshtastic/version.rb
CHANGED
data/lib/meshtastic.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# Plugin used to interact with Meshtastic nodes
|
4
4
|
module Meshtastic
|
5
|
+
require 'base64'
|
5
6
|
# Protocol Buffers for Meshtastic
|
6
7
|
require 'meshtastic/admin_pb'
|
7
8
|
require 'nanopb_pb'
|
@@ -25,6 +26,7 @@ module Meshtastic
|
|
25
26
|
require 'meshtastic/telemetry_pb'
|
26
27
|
require 'meshtastic/version'
|
27
28
|
require 'meshtastic/xmodem_pb'
|
29
|
+
require 'openssl'
|
28
30
|
|
29
31
|
autoload :Admin, 'meshtastic/admin'
|
30
32
|
autoload :Apponly, 'meshtastic/apponly'
|
@@ -47,6 +49,191 @@ module Meshtastic
|
|
47
49
|
autoload :Telemetry, 'meshtastic/telemetry'
|
48
50
|
autoload :Xmodem, 'meshtastic/xmodem'
|
49
51
|
|
52
|
+
# Supported Method Parameters::
|
53
|
+
# Meshtastic.send_text(
|
54
|
+
# from_id: 'optional - Source ID (Default: 0)',
|
55
|
+
# dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
|
56
|
+
# text: 'optional - Text Message (Default: SYN)',
|
57
|
+
# want_ack: 'optional - Want Acknowledgement (Default: false)',
|
58
|
+
# want_response: 'optional - Want Response (Default: false)',
|
59
|
+
# hop_limit: 'optional - Hop Limit (Default: 3)',
|
60
|
+
# on_response: 'optional - Callback on Response',
|
61
|
+
# psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
|
62
|
+
# )
|
63
|
+
public_class_method def self.send_text(opts = {})
|
64
|
+
# Send a text message to a node
|
65
|
+
from_id = opts[:from_id].to_i
|
66
|
+
dest_id = opts[:dest_id] ||= 0xFFFFFFFF
|
67
|
+
text = opts[:text] ||= 'SYN'
|
68
|
+
want_ack = opts[:want_ack] ||= false
|
69
|
+
want_response = opts[:want_response] ||= false
|
70
|
+
hop_limit = opts[:hop_limit] ||= 3
|
71
|
+
on_response = opts[:on_response]
|
72
|
+
psk = opts[:psk]
|
73
|
+
|
74
|
+
# TODO: verify text length validity
|
75
|
+
max_txt_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
|
76
|
+
raise "ERROR: Text Length > #{max_txt_len} Bytes" if text.length > max_txt_len
|
77
|
+
|
78
|
+
port_num = Meshtastic::PortNum::TEXT_MESSAGE_APP
|
79
|
+
|
80
|
+
data = Meshtastic::Data.new
|
81
|
+
data.payload = text
|
82
|
+
data.portnum = port_num
|
83
|
+
data.want_response = want_response
|
84
|
+
puts data.to_h
|
85
|
+
|
86
|
+
send_data(
|
87
|
+
from_id: from_id,
|
88
|
+
dest_id: dest_id,
|
89
|
+
data: data,
|
90
|
+
want_ack: want_ack,
|
91
|
+
want_response: want_response,
|
92
|
+
hop_limit: hop_limit,
|
93
|
+
port_num: port_num,
|
94
|
+
on_response: on_response,
|
95
|
+
psk: psk
|
96
|
+
)
|
97
|
+
rescue StandardError => e
|
98
|
+
raise e
|
99
|
+
end
|
100
|
+
|
101
|
+
# Supported Method Parameters::
|
102
|
+
# Meshtastic.send_data(
|
103
|
+
# from_id: 'optional - Source ID (Default: 0)',
|
104
|
+
# dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
|
105
|
+
# data: 'required - Data to Send',
|
106
|
+
# want_ack: 'optional - Want Acknowledgement (Default: false)',
|
107
|
+
# hop_limit: 'optional - Hop Limit (Default: 3)',
|
108
|
+
# port_num: 'optional - (Default: Meshtastic::PortNum::PRIVATE_APP)',
|
109
|
+
# psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
|
110
|
+
# )
|
111
|
+
public_class_method def self.send_data(opts = {})
|
112
|
+
# Send a text message to a node
|
113
|
+
from_id = opts[:from_id].to_i
|
114
|
+
dest_id = opts[:dest_id] ||= 0xFFFFFFFF
|
115
|
+
data = opts[:data]
|
116
|
+
want_ack = opts[:want_ack] ||= false
|
117
|
+
hop_limit = opts[:hop_limit] ||= 3
|
118
|
+
port_num = opts[:port_num] ||= Meshtastic::PortNum::PRIVATE_APP
|
119
|
+
max_port_num = Meshtastic::PortNum::MAX
|
120
|
+
raise "ERROR: Invalid port_num" unless port_num.positive? && port_num < max_port_num
|
121
|
+
|
122
|
+
psk = opts[:psk]
|
123
|
+
|
124
|
+
data_len = data.payload.length
|
125
|
+
max_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
|
126
|
+
raise "ERROR: Data Length > #{max_len} Bytes" if data_len > max_len
|
127
|
+
|
128
|
+
mesh_packet = Meshtastic::MeshPacket.new
|
129
|
+
mesh_packet.decoded = data
|
130
|
+
|
131
|
+
send_packet(
|
132
|
+
mesh_packet: mesh_packet,
|
133
|
+
from_id: from_id,
|
134
|
+
dest_id: dest_id,
|
135
|
+
want_ack: want_ack,
|
136
|
+
hop_limit: hop_limit,
|
137
|
+
psk: psk
|
138
|
+
)
|
139
|
+
rescue StandardError => e
|
140
|
+
raise e
|
141
|
+
end
|
142
|
+
|
143
|
+
# Supported Method Parameters::
|
144
|
+
# Meshtastic.send_packet(
|
145
|
+
# mesh_packet: 'required - Mesh Packet to Send',
|
146
|
+
# from_id: 'optional - Source ID (Default: 0)',
|
147
|
+
# dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
|
148
|
+
# want_ack: 'optional - Want Acknowledgement (Default: false)',
|
149
|
+
# hop_limit: 'optional - Hop Limit (Default: 3)',
|
150
|
+
# psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
|
151
|
+
# )
|
152
|
+
public_class_method def self.send_packet(opts = {})
|
153
|
+
mesh_packet = opts[:mesh_packet]
|
154
|
+
from_id = opts[:from_id] ||= 0
|
155
|
+
dest_id = opts[:dest_id] ||= 0xFFFFFFFF
|
156
|
+
want_ack = opts[:want_ack] ||= false
|
157
|
+
hop_limit = opts[:hop_limit] ||= 3
|
158
|
+
psk = opts[:psk]
|
159
|
+
|
160
|
+
# my_info = Meshtastic::FromRadio.my_info
|
161
|
+
# wait_connected if dest_id != my_info.my_node_num && my_info.is_a(Meshtastic::Deviceonly::MyInfo)
|
162
|
+
|
163
|
+
node_num = dest_id
|
164
|
+
node_num_hex = dest_id.bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if dest_id.is_a?(String)
|
165
|
+
node_num = node_num_hex.to_i(16) if node_num_hex
|
166
|
+
|
167
|
+
mesh_packet.from = from_id
|
168
|
+
mesh_packet.to = node_num
|
169
|
+
mesh_packet.want_ack = want_ack
|
170
|
+
mesh_packet.hop_limit = hop_limit
|
171
|
+
|
172
|
+
mesh_packet.id = generate_packet_id if mesh_packet.id.zero?
|
173
|
+
|
174
|
+
if psk
|
175
|
+
nonce_packet_id = [mesh_packet.id].pack('V').ljust(8, "\x00")
|
176
|
+
nonce_from_node = [from_id].pack('V').ljust(8, "\x00")
|
177
|
+
nonce = "#{nonce_packet_id}#{nonce_from_node}".b
|
178
|
+
|
179
|
+
dec_psk = Base64.strict_decode64(psk)
|
180
|
+
cipher = OpenSSL::Cipher.new('AES-128-CTR')
|
181
|
+
cipher = OpenSSL::Cipher.new('AES-256-CTR') if dec_psk.length == 32
|
182
|
+
cipher.encrypt
|
183
|
+
cipher.key = dec_psk
|
184
|
+
cipher.iv = nonce
|
185
|
+
|
186
|
+
decrypted_payload = mesh_packet.decoded.to_s
|
187
|
+
encrypted_payload = cipher.update(decrypted_payload) + cipher.final
|
188
|
+
|
189
|
+
mesh_packet.encrypted = encrypted_payload
|
190
|
+
end
|
191
|
+
# puts mesh_packet.to_h
|
192
|
+
|
193
|
+
# to_radio = Meshtastic::ToRadio.new
|
194
|
+
# to_radio.packet = mesh_packet
|
195
|
+
# send_to_radio(to_radio: to_radio)
|
196
|
+
|
197
|
+
mesh_packet
|
198
|
+
rescue StandardError => e
|
199
|
+
raise e
|
200
|
+
end
|
201
|
+
|
202
|
+
# Supported Method Parameters::
|
203
|
+
# packet_id = Meshtastic.generate_packet_id(
|
204
|
+
# last_packet_id: 'optional - Last Packet ID (Default: 0)'
|
205
|
+
# )
|
206
|
+
public_class_method def self.generate_packet_id(opts = {})
|
207
|
+
last_packet_id = opts[:last_packet_id] ||= 0
|
208
|
+
|
209
|
+
packet_id = last_packet_id + 1 if last_packet_id.positive?
|
210
|
+
packet_id = rand(2**32) if last_packet_id.zero?
|
211
|
+
|
212
|
+
packet_id
|
213
|
+
end
|
214
|
+
|
215
|
+
# Supported Method Parameters::
|
216
|
+
# Meshtastic.send_to_radio(
|
217
|
+
# to_radio: 'required - ToRadio Message to Send'
|
218
|
+
# )
|
219
|
+
public_class_method def self.send_to_radio(opts = {})
|
220
|
+
to_radio = opts[:to_radio]
|
221
|
+
|
222
|
+
raise 'ERROR: Invalid ToRadio Message' unless to_radio.is_a?(Meshtastic::ToRadio)
|
223
|
+
|
224
|
+
to_radio.to_proto
|
225
|
+
rescue StandardError => e
|
226
|
+
raise e
|
227
|
+
end
|
228
|
+
|
229
|
+
# Author(s):: 0day Inc. <support@0dayinc.com>
|
230
|
+
|
231
|
+
public_class_method def self.authors
|
232
|
+
"AUTHOR(S):
|
233
|
+
0day Inc. <support@0dayinc.com>
|
234
|
+
"
|
235
|
+
end
|
236
|
+
|
50
237
|
# Display a List of Every Meshtastic Module
|
51
238
|
|
52
239
|
public_class_method def self.help
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meshtastic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.43
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 0day Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.8.
|
47
|
+
version: 1.8.3
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.8.
|
54
|
+
version: 1.8.3
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: google-protobuf
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,14 +156,14 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - '='
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 2.29.
|
159
|
+
version: 2.29.2
|
160
160
|
type: :runtime
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - '='
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: 2.29.
|
166
|
+
version: 2.29.2
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: rvm
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|