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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94567b95fcf287b1a47b7af7ea3efb4b6f15a5a0daf81137127b6f5dede9a8fd
4
- data.tar.gz: aa07f4271ac1cb1953492ab508f2f8f4f2c7c0c51fb306195b096b950b917c25
3
+ metadata.gz: 105d223bd94e02d4473ac5fd66ed0b8dc7eef73d44578eb427fc3d55eb51ae21
4
+ data.tar.gz: 216a3957a439408579d1d7b48dd552d394885bbc09b8dc3410d776e4c1eb37d6
5
5
  SHA512:
6
- metadata.gz: 77093a3977ef63ad7c15d255c679771e5fe02448741147acbc608f19c057d9b9f8f47fe214e0f0a2799cca331726450f978bbda99248fb5d3f592e4d37b09a43
7
- data.tar.gz: 4c617b139c876db4b3fff9f4357fba3785972e47e88e85a9a9a1656db8c5ced40e42699b737aa7b84f3cc86ced3f66480a3def02e6e1a2272bdd23ac70d82858
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.2'
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.1'
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'
@@ -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
- # raw_packet = packet_bytes.to_s.b
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
- target_chanel = decoded_payload_hash[:channel_id].to_s.to_sym
139
- psk = psks[target_chanel] if psks.keys.include?(target_chanel)
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[:decrypted] = decrypted
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
- case msg_type
157
- when :ADMIN_APP
158
- pb_obj = Meshtastic::AdminMessage.decode(payload)
159
- when :ATAK_FORWARDER, :ATAK_PLUGIN
160
- pb_obj = Meshtastic::TAKPacket.decode(payload)
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
- # message[:raw_packet] = raw_packet if block_given?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Meshtastic
4
- VERSION = '0.0.41'
4
+ VERSION = '0.0.43'
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.41
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-02 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
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 1.8.2
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.2
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.1
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.1
166
+ version: 2.29.2
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: rvm
169
169
  requirement: !ruby/object:Gem::Requirement