meshtastic 0.0.42 → 0.0.44

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ffe0a1e8528090281aedce5b132f1f57aa82db0c8c46131cff53d00004e4dcd
4
- data.tar.gz: 56bb82c58cdcbced0c401802180931aa4625e4314e4954ab30349811a614d399
3
+ metadata.gz: 5dec0934f3c9c639e3af0cf5f8b88447dd83a96c5ed8082ff954ce13c5b758dd
4
+ data.tar.gz: c4a3e692fdc5f92a8e1ddf997ae9d5f7c6e33915dd239546d52f3adcee690a44
5
5
  SHA512:
6
- metadata.gz: dd84aa7e0607992de00be53a0f8ccb2d69077004dd21ebc2838be38582064a797c0f13317c0abd375ab67199dd494640187895384ea74110c77c9b478a218654
7
- data.tar.gz: a1135c741f44f38765421ea2020173b40395478fffdbb24da760d75f8483920f99cf2f0d110f7173fb9617e7522a644dc418f937aef79a35f1b04d89ed94737c
6
+ metadata.gz: 8c20d4c63ffdf13b5903f2e5605d5296268613b7bd7cc3380f48e4405e7b4aabda8216775f6664b42e5886dffdac42cfcb66b13bc1a48068e19f3152e1fa9598
7
+ data.tar.gz: a2ff5b9076561cb0b43b798d4bc6471f7ee2a4387be99bd53b98b389b4344954fbf451ce41be341c6e3a945b12adc02ba531883466fbb46cde30d8f7e0651c02
@@ -64,120 +64,108 @@ module Meshtastic
64
64
  end
65
65
 
66
66
  # Supported Method Parameters::
67
- # Meshtastic::MQQT.recursively_decode_payloads(
68
- # object: 'required - object to recursively decode',
67
+ # Meshtastic::MQQT.decode_payload(
68
+ # payload: 'required - payload to recursively decode',
69
69
  # msg_type: 'required - message type (e.g. :TEXT_MESSAGE_APP)',
70
- # gps_metadata: 'optional - include GPS metadata in output (default: false)'
70
+ # gps_metadata: 'optional - include GPS metadata in output (default: false)',
71
71
  # )
72
72
 
73
- private_class_method def self.recursively_decode_payloads(opts = {})
74
- object = opts[:object]
73
+ public_class_method def self.decode_payload(opts = {})
74
+ payload = opts[:payload]
75
75
  msg_type = opts[:msg_type]
76
76
  gps_metadata = opts[:gps_metadata]
77
77
 
78
- # puts "OBJECT: #{object.inspect}"
79
- object = Meshtastic::Data.decode(object).to_h if object.is_a?(String)
80
-
81
- object.each do |key, value|
82
- if value.is_a?(Hash)
83
- # Recursive call if the value is a hash
84
- recursively_decode_payloads(object: value)
85
- elsif value.is_a?(Array)
86
- # Process each element if it's an array
87
- value.each do |item|
88
- recursively_decode_payloads(object: item) if item.is_a?(Hash)
89
- end
90
- end
91
- # Check if the key matches 'payload' and decode
92
- case key
93
- when :latitude_i
94
- object[:latitude] = object[:latitude_i] * 0.0000001
95
- when :longitude_i
96
- object[:longitude] = object[:longitude_i] * 0.0000001
97
- when :macaddr
98
- object[key] = object[key].bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join(':')
99
- when :payload
100
- case msg_type
101
- when :ADMIN_APP
102
- decoder = Meshtastic::AdminMessage.decode(payload)
103
- when :ATAK_FORWARDER, :ATAK_PLUGIN
104
- decoder = Meshtastic::TAKPacket
105
- # when :AUDIO_APP
106
- # decoder = Meshtastic::Audio
107
- when :DETECTION_SENSOR_APP
108
- decoder = Meshtastic::DeviceState
109
- # when :IP_TUNNEL_APP
110
- # decoder = Meshtastic::IpTunnel
111
- when :MAP_REPORT_APP
112
- decoder = Meshtastic::MapReport
113
- # when :MAX
114
- # decoder = Meshtastic::Max
115
- when :NEIGHBORINFO_APP
116
- decoder = Meshtastic::NeighborInfo
117
- when :NODEINFO_APP
118
- decoder = Meshtastic::User
119
- when :PAXCOUNTER_APP
120
- decoder = Meshtastic::Paxcount
121
- when :POSITION_APP
122
- decoder = Meshtastic::Position
123
- # when :PRIVATE_APP
124
- # decoder = Meshtastic::Private
125
- when :RANGE_TEST_APP
126
- # Unsure if this is the correct protobuf object
127
- decoder = Meshtastic::FromRadio
128
- when :REMOTE_HARDWARE_APP
129
- decoder = Meshtastic::HardwareMessage
130
- # when :REPLY_APP
131
- # decoder = Meshtastic::Reply
132
- when :ROUTING_APP
133
- decoder = Meshtastic::Routing
134
- when :SERIAL_APP
135
- decoder = Meshtastic::SerialConnectionStatus
136
- when :SIMULATOR_APP
137
- decoder = Meshtastic::Compressed
138
- when :STORE_FORWARD_APP
139
- decoder = Meshtastic::StoreAndForward
140
- when :TEXT_MESSAGE_APP
141
- # Unsure if this is the correct protobuf object
142
- # decoder = Meshtastic::MqttClientProxyMessage
143
- decoder = Meshtastic::Data
144
- when :TELEMETRY_APP
145
- decoder = Meshtastic::Telemetry
146
- when :TRACEROUTE_APP
147
- decoder = Meshtastic::RouteDiscovery
148
- when :UNKNOWN_APP
149
- decoder = Meshtastic::Data.decode
150
- when :WAYPOINT_APP
151
- decoder = Meshtastic::Waypoint
152
- # when :ZPS_APP
153
- # decoder = Meshtastic::Zps
154
- else
155
- puts "WARNING: Unknown message type: #{msg_type}"
156
- decoder = Meshtastic::Data.decode
157
- end
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
158
131
 
159
- # begin
160
- # # If the value is base64 encoded, base64 decode and then decode
161
- # base64_decoded_value = Base64.strict_decode64(value)
162
- # object[key] = decoder.decode(base64_decoded_value).to_h
163
- # rescue ArgumentError => e
164
- # # Otherwise, just decode the value
165
- object[key] = decoder.decode(value).to_h
166
- # end
132
+ payload = decoder.decode(payload).to_h
167
133
 
168
- end
134
+ if payload.keys.include?(:latitude_i)
135
+ lat = payload[:latitude_i] * 0.0000001
136
+ payload[:latitude] = lat
137
+ end
138
+
139
+ if payload.keys.include?(:longitude_i)
140
+ lon = payload[:longitude_i] * 0.0000001
141
+ payload[:longitude] = lon
142
+ end
169
143
 
170
- next unless gps_metadata && object[:latitude] && object[:longitude]
144
+ if payload.keys.include?(:macaddr)
145
+ mac_raw = payload[:macaddr]
146
+ mac_hex_arr = mac_raw.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }
147
+ mac_hex_str = mac_hex_arr.join(':')
148
+ payload[:macaddr_hex] = mac_hex_str
149
+ end
171
150
 
172
- object[:gps_metadata] = gps_search(
173
- lat: object[:latitude],
174
- lon: object[:longitude]
175
- ).first.data
151
+ if payload.keys.include?(:time)
152
+ time_int = payload[:time]
153
+ time_utc = Time.at(time_int).utc if time_int.is_a?(Integer)
154
+ payload[:time_utc] = time_utc
155
+ end
156
+
157
+ if gps_metadata && payload[:latitude] && payload[:longitude]
158
+ lat = payload[:latitude]
159
+ lon = payload[:longitude]
160
+ unless lat.zero? && lon.zero?
161
+ gps_search_resp = gps_search(lat: lat, lon: lon)
162
+ payload[:gps_metadata] = gps_search_resp
163
+ end
176
164
  end
177
165
 
178
- object
166
+ payload
179
167
  rescue Google::Protobuf::ParseError
180
- object
168
+ payload
181
169
  rescue StandardError => e
182
170
  raise e
183
171
  end
@@ -256,8 +244,8 @@ module Meshtastic
256
244
  nonce = "#{nonce_packet_id}#{nonce_from_node}".b
257
245
 
258
246
  psk = psks[:LongFast]
259
- target_chanel = decoded_payload_hash[:channel_id].to_s.to_sym
260
- psk = psks[target_chanel] if psks.keys.include?(target_chanel)
247
+ target_channel = decoded_payload_hash[:channel_id].to_s.to_sym
248
+ psk = psks[target_channel] if psks.keys.include?(target_channel)
261
249
  dec_psk = Base64.strict_decode64(psk)
262
250
 
263
251
  cipher = OpenSSL::Cipher.new('AES-128-CTR')
@@ -275,8 +263,8 @@ module Meshtastic
275
263
  # payload = Meshtastic::Data.decode(message[:decoded][:payload]).to_h
276
264
  payload = message[:decoded][:payload]
277
265
  msg_type = message[:decoded][:portnum]
278
- message[:decoded][:payload] = recursively_decode_payloads(
279
- object: payload,
266
+ message[:decoded][:payload] = decode_payload(
267
+ payload: payload,
280
268
  msg_type: msg_type,
281
269
  gps_metadata: gps_metadata
282
270
  )
@@ -347,7 +335,7 @@ module Meshtastic
347
335
 
348
336
  gps_arr = [lat.to_f, lon.to_f]
349
337
 
350
- Geocoder.search(gps_arr)
338
+ Geocoder.search(gps_arr).first.data
351
339
  rescue StandardError => e
352
340
  raise e
353
341
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Meshtastic
4
- VERSION = '0.0.42'
4
+ VERSION = '0.0.44'
5
5
  end
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.42
4
+ version: 0.0.44
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-03 00:00:00.000000000 Z
11
+ date: 2024-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -348,7 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
348
348
  - !ruby/object:Gem::Version
349
349
  version: '0'
350
350
  requirements: []
351
- rubygems_version: 3.5.9
351
+ rubygems_version: 3.5.10
352
352
  signing_key:
353
353
  specification_version: 4
354
354
  summary: Ruby gem for Meshtastic