meshtastic 0.0.49 → 0.0.51

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: a3016d1056f76c627d22c28fbc587bd7e78cccd5fdd889c90fbc67fa0fe962cf
4
- data.tar.gz: 2077c497f71e339c87ac47e7c019a89dba3d29b98200400c8cd2368b92866a7f
3
+ metadata.gz: a3b25733439f146126ff2350cde20e82173b75f4a7118df83cbd7ac09715832a
4
+ data.tar.gz: f9296bf726f9399867171a02c5f261f3bd5c5fc264efc6323029d3a959d9a3e8
5
5
  SHA512:
6
- metadata.gz: f34b555c4a422f8763c0a69c62cb7b06897e3b3905809ec15d26b76cd332f985687829f3ddfec48e1673853458073f97e9ef90eb0bf18188eaccbc6101062824
7
- data.tar.gz: 61aedf5607903a604238c7eac1e0dc1d8ec514fcbfd51a42f38becaa1b0f6900ae27faef170075bdc246da275c66972b7c2d583ec265833348f52ad719e0d1ba
6
+ metadata.gz: 7d1ab98506914f93b8fc3e6e5a792a7c8bd8e0c70cb8a88046ceaf7fb1f7617a127d1f450d175f90aec7bb22e7c605cd442e2d0db5efffcefb362a660ad65152
7
+ data.tar.gz: 114f31f2563fb18099ddfba9b915b1b540c1b463098648a70530f60c81891cd522ffe7adceef632f82320490b8bd8fcb0110a77a6f3b5fc5b00dc8e60e12f7f1
@@ -151,8 +151,10 @@ module Meshtastic
151
151
 
152
152
  if payload.keys.include?(:time)
153
153
  time_int = payload[:time]
154
- time_utc = Time.at(time_int).utc.to_s if time_int.is_a?(Integer)
155
- payload[:time_utc] = time_utc
154
+ if time_int.is_a?(Integer)
155
+ time_utc = Time.at(time_int).utc.to_s
156
+ payload[:time_utc] = time_utc
157
+ end
156
158
  end
157
159
 
158
160
  if gps_metadata && payload[:latitude] && payload[:longitude]
@@ -237,14 +239,18 @@ module Meshtastic
237
239
  message[:node_id_to] = "!#{message[:to].to_i.to_s(16)}"
238
240
  if message.keys.include?(:rx_time)
239
241
  rx_time_int = message[:rx_time]
240
- rx_time_utc = Time.at(rx_time_int).utc.to_s if rx_time_int.is_a?(Integer)
241
- message[:rx_time_utc] = rx_time_utc
242
+ if rx_time_int.is_a?(Integer)
243
+ rx_time_utc = Time.at(rx_time_int).utc.to_s
244
+ message[:rx_time_utc] = rx_time_utc
245
+ end
242
246
  end
243
247
 
244
248
  # If encrypted_message is not nil, then decrypt
245
249
  # the message prior to decoding.
246
250
  encrypted_message = message[:encrypted]
247
- if encrypted_message.to_s.length.positive?
251
+ if encrypted_message.to_s.length.positive? &&
252
+ message[:topic]
253
+
248
254
  packet_id = message[:id]
249
255
  packet_from = message[:from]
250
256
 
@@ -253,8 +259,10 @@ module Meshtastic
253
259
  nonce = "#{nonce_packet_id}#{nonce_from_node}".b
254
260
 
255
261
  psk = psks[:LongFast]
256
- target_channel = decoded_payload_hash[:channel_id].to_s.to_sym
262
+ target_channel = message[:topic].split('/')[-2].to_sym
263
+ puts "Target Channel: #{target_channel}"
257
264
  psk = psks[target_channel] if psks.keys.include?(target_channel)
265
+ puts "PSK: #{psk}"
258
266
  dec_psk = Base64.strict_decode64(psk)
259
267
 
260
268
  cipher = OpenSSL::Cipher.new('AES-128-CTR')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Meshtastic
4
- VERSION = '0.0.49'
4
+ VERSION = '0.0.51'
5
5
  end
data/lib/meshtastic.rb CHANGED
@@ -51,26 +51,40 @@ module Meshtastic
51
51
 
52
52
  # Supported Method Parameters::
53
53
  # Meshtastic.send_text(
54
- # from_id: 'optional - Source ID (Default: 0)',
55
- # dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
54
+ # from: 'required - From ID (String or Integer)',
55
+ # to: 'optional - Destination ID (Default: 0xFFFFFFFF)',
56
+ # channel: 'optional - Channel ID (Default: 0)',
56
57
  # text: 'optional - Text Message (Default: SYN)',
57
58
  # want_ack: 'optional - Want Acknowledgement (Default: false)',
58
59
  # want_response: 'optional - Want Response (Default: false)',
59
60
  # hop_limit: 'optional - Hop Limit (Default: 3)',
60
61
  # on_response: 'optional - Callback on Response',
61
- # psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
62
+ # psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: "AQ==" })'
62
63
  # )
63
64
  public_class_method def self.send_text(opts = {})
64
65
  # Send a text message to a node
65
- from_id = opts[:from_id].to_i
66
- dest_id = opts[:dest_id] ||= 0xFFFFFFFF
66
+ from = opts[:from]
67
+ from_hex = from.delete('!').bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if from.is_a?(String)
68
+ from = from_hex.to_i(16) if from_hex
69
+ raise 'ERROR: from parameter is required.' unless from
70
+
71
+ to = opts[:to] ||= 0xFFFFFFFF
72
+ to_hex = to.delete('!').bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if to.is_a?(String)
73
+ to = to_hex.to_i(16) if to_hex
74
+
75
+ channel = opts[:channel] ||= 0
67
76
  text = opts[:text] ||= 'SYN'
68
77
  want_ack = opts[:want_ack] ||= false
69
78
  want_response = opts[:want_response] ||= false
70
79
  hop_limit = opts[:hop_limit] ||= 3
71
80
  on_response = opts[:on_response]
72
- psk = opts[:psk]
73
-
81
+
82
+ public_psk = '1PG7OiApB1nwvP+rz05pAQ=='
83
+ psks = opts[:psks] ||= { LongFast: public_psk }
84
+ raise 'ERROR: psks parameter must be a hash of :channel => psk key value pairs' unless psks.is_a?(Hash)
85
+
86
+ psks[:LongFast] = public_psk if psks[:LongFast] == 'AQ=='
87
+
74
88
  # TODO: verify text length validity
75
89
  max_txt_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
76
90
  raise "ERROR: Text Length > #{max_txt_len} Bytes" if text.length > max_txt_len
@@ -84,15 +98,16 @@ module Meshtastic
84
98
  puts data.to_h
85
99
 
86
100
  send_data(
87
- from_id: from_id,
88
- dest_id: dest_id,
101
+ from: from,
102
+ to: to,
103
+ channel: channel,
89
104
  data: data,
90
105
  want_ack: want_ack,
91
106
  want_response: want_response,
92
107
  hop_limit: hop_limit,
93
108
  port_num: port_num,
94
109
  on_response: on_response,
95
- psk: psk
110
+ psks: psks
96
111
  )
97
112
  rescue StandardError => e
98
113
  raise e
@@ -100,18 +115,27 @@ module Meshtastic
100
115
 
101
116
  # Supported Method Parameters::
102
117
  # Meshtastic.send_data(
103
- # from_id: 'optional - Source ID (Default: 0)',
104
- # dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
118
+ # from: 'required - From ID (String or Integer)',
119
+ # to: 'optional - Destination ID (Default: 0xFFFFFFFF)',
120
+ # channel: 'optional - Channel ID (Default: 0)',
105
121
  # data: 'required - Data to Send',
106
122
  # want_ack: 'optional - Want Acknowledgement (Default: false)',
107
123
  # hop_limit: 'optional - Hop Limit (Default: 3)',
108
124
  # port_num: 'optional - (Default: Meshtastic::PortNum::PRIVATE_APP)',
109
- # psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
125
+ # psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: "AQ==" })'
110
126
  # )
111
127
  public_class_method def self.send_data(opts = {})
112
128
  # Send a text message to a node
113
- from_id = opts[:from_id].to_i
114
- dest_id = opts[:dest_id] ||= 0xFFFFFFFF
129
+ from = opts[:from]
130
+ from_hex = from.delete('!').bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if from.is_a?(String)
131
+ from = from_hex.to_i(16) if from_hex
132
+ raise 'ERROR: from parameter is required.' unless from
133
+
134
+ to = opts[:to] ||= 0xFFFFFFFF
135
+ to_hex = to.delete('!').bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if to.is_a?(String)
136
+ to = to_hex.to_i(16) if to_hex
137
+
138
+ channel = opts[:channel] ||= 0
115
139
  data = opts[:data]
116
140
  want_ack = opts[:want_ack] ||= false
117
141
  hop_limit = opts[:hop_limit] ||= 3
@@ -119,7 +143,11 @@ module Meshtastic
119
143
  max_port_num = Meshtastic::PortNum::MAX
120
144
  raise "ERROR: Invalid port_num" unless port_num.positive? && port_num < max_port_num
121
145
 
122
- psk = opts[:psk]
146
+ public_psk = '1PG7OiApB1nwvP+rz05pAQ=='
147
+ psks = opts[:psks] ||= { LongFast: public_psk }
148
+ raise 'ERROR: psks parameter must be a hash of :channel => psk key value pairs' unless psks.is_a?(Hash)
149
+
150
+ psks[:LongFast] = public_psk if psks[:LongFast] == 'AQ=='
123
151
 
124
152
  data_len = data.payload.length
125
153
  max_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
@@ -130,11 +158,12 @@ module Meshtastic
130
158
 
131
159
  send_packet(
132
160
  mesh_packet: mesh_packet,
133
- from_id: from_id,
134
- dest_id: dest_id,
161
+ from: from,
162
+ to: to,
163
+ channel: channel,
135
164
  want_ack: want_ack,
136
165
  hop_limit: hop_limit,
137
- psk: psk
166
+ psks: psks
138
167
  )
139
168
  rescue StandardError => e
140
169
  raise e
@@ -143,39 +172,52 @@ module Meshtastic
143
172
  # Supported Method Parameters::
144
173
  # Meshtastic.send_packet(
145
174
  # mesh_packet: 'required - Mesh Packet to Send',
146
- # from_id: 'optional - Source ID (Default: 0)',
147
- # dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
175
+ # from: 'required - From ID (String or Integer)',
176
+ # to: 'optional - Destination ID (Default: 0xFFFFFFFF)',
177
+ # channel: 'optional - Channel ID (Default: 0)',
148
178
  # want_ack: 'optional - Want Acknowledgement (Default: false)',
149
179
  # hop_limit: 'optional - Hop Limit (Default: 3)',
150
- # psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
180
+ # psks: 'optional - hash of :channel => psk key value pairs (default: { LongFast: "AQ==" })'
151
181
  # )
152
182
  public_class_method def self.send_packet(opts = {})
153
183
  mesh_packet = opts[:mesh_packet]
154
- from_id = opts[:from_id] ||= 0
155
- dest_id = opts[:dest_id] ||= 0xFFFFFFFF
184
+ from = opts[:from]
185
+ from_hex = from.delete('!').bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if from.is_a?(String)
186
+ from = from_hex.to_i(16) if from_hex
187
+ raise 'ERROR: from parameter is required.' unless from
188
+
189
+ to = opts[:to] ||= 0xFFFFFFFF
190
+ to_hex = to.delete('!').bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if to.is_a?(String)
191
+ to = to_hex.to_i(16) if to_hex
192
+
193
+ channel = opts[:channel] ||= 0
156
194
  want_ack = opts[:want_ack] ||= false
157
195
  hop_limit = opts[:hop_limit] ||= 3
158
- psk = opts[:psk]
196
+
197
+ public_psk = '1PG7OiApB1nwvP+rz05pAQ=='
198
+ psks = opts[:psks] ||= { LongFast: public_psk }
199
+ raise 'ERROR: psks parameter must be a hash of :channel => psk key value pairs' unless psks.is_a?(Hash)
200
+
201
+ psks[:LongFast] = public_psk if psks[:LongFast] == 'AQ=='
159
202
 
160
203
  # 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
204
+ # wait_connected if to != my_info.my_node_num && my_info.is_a(Meshtastic::Deviceonly::MyInfo)
205
+
206
+ mesh_packet.from = from
207
+ mesh_packet.to = to
208
+ mesh_packet.channel = channel
169
209
  mesh_packet.want_ack = want_ack
170
210
  mesh_packet.hop_limit = hop_limit
171
-
172
211
  mesh_packet.id = generate_packet_id if mesh_packet.id.zero?
212
+ # mesh_packet.channel_id = psks.keys.first
213
+ # mesh_packet.gateway_id = "!#{from.to_s(16).downcase}"
173
214
 
174
- if psk
215
+ if psks
175
216
  nonce_packet_id = [mesh_packet.id].pack('V').ljust(8, "\x00")
176
- nonce_from_node = [from_id].pack('V').ljust(8, "\x00")
217
+ nonce_from_node = [from].pack('V').ljust(8, "\x00")
177
218
  nonce = "#{nonce_packet_id}#{nonce_from_node}".b
178
219
 
220
+ psk = psks[psks.keys.first]
179
221
  dec_psk = Base64.strict_decode64(psk)
180
222
  cipher = OpenSSL::Cipher.new('AES-128-CTR')
181
223
  cipher = OpenSSL::Cipher.new('AES-256-CTR') if dec_psk.length == 32
@@ -183,18 +225,17 @@ module Meshtastic
183
225
  cipher.key = dec_psk
184
226
  cipher.iv = nonce
185
227
 
186
- decrypted_payload = mesh_packet.decoded.to_s
228
+ decrypted_payload = mesh_packet.decoded.to_proto
187
229
  encrypted_payload = cipher.update(decrypted_payload) + cipher.final
188
230
 
189
231
  mesh_packet.encrypted = encrypted_payload
190
232
  end
191
- # puts mesh_packet.to_h
233
+ puts mesh_packet.to_h
192
234
 
193
- # to_radio = Meshtastic::ToRadio.new
194
- # to_radio.packet = mesh_packet
195
- # send_to_radio(to_radio: to_radio)
235
+ to_radio = Meshtastic::ToRadio.new
236
+ to_radio.packet = mesh_packet
196
237
 
197
- mesh_packet
238
+ send_to_radio(to_radio: to_radio)
198
239
  rescue StandardError => e
199
240
  raise e
200
241
  end
@@ -205,11 +246,9 @@ module Meshtastic
205
246
  # )
206
247
  public_class_method def self.generate_packet_id(opts = {})
207
248
  last_packet_id = opts[:last_packet_id] ||= 0
249
+ last_packet_id = 0 if last_packet_id.negative?
208
250
 
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
251
+ (last_packet_id + 1) & 0xffffffff
213
252
  end
214
253
 
215
254
  # Supported Method Parameters::
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.49
4
+ version: 0.0.51
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-09 00:00:00.000000000 Z
11
+ date: 2024-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler