meshtastic 0.0.179 → 0.0.180

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/documentation/README.md +3 -2
  3. data/documentation/admin-channel.md +33 -0
  4. data/documentation/admin-config.md +28 -0
  5. data/documentation/admin-firmware.md +69 -0
  6. data/documentation/admin.md +2 -1
  7. data/documentation/atak.md +93 -10
  8. data/documentation/channel.md +1 -28
  9. data/documentation/config.md +1 -27
  10. data/documentation/meshtastic.md +2 -2
  11. data/lib/meshtastic/admin/channel.rb +90 -0
  12. data/lib/meshtastic/admin/config.rb +154 -0
  13. data/lib/meshtastic/admin/firmware.rb +173 -0
  14. data/lib/meshtastic/admin.rb +458 -28
  15. data/lib/meshtastic/apponly.rb +19 -7
  16. data/lib/meshtastic/atak.rb +295 -17
  17. data/lib/meshtastic/bluetooth/bluez.rb +20 -1
  18. data/lib/meshtastic/bluetooth.rb +70 -59
  19. data/lib/meshtastic/cannedmessages.rb +13 -6
  20. data/lib/meshtastic/channel.rb +3 -25
  21. data/lib/meshtastic/clientonly.rb +19 -7
  22. data/lib/meshtastic/config.rb +3 -24
  23. data/lib/meshtastic/connection_status.rb +12 -5
  24. data/lib/meshtastic/deviceonly.rb +19 -7
  25. data/lib/meshtastic/localonly.rb +19 -7
  26. data/lib/meshtastic/mesh_interface.rb +11 -0
  27. data/lib/meshtastic/module_config.rb +13 -7
  28. data/lib/meshtastic/mqtt.rb +72 -35
  29. data/lib/meshtastic/paxcount.rb +17 -6
  30. data/lib/meshtastic/portnums.rb +13 -6
  31. data/lib/meshtastic/position.rb +16 -6
  32. data/lib/meshtastic/remote_hardware.rb +27 -11
  33. data/lib/meshtastic/rtttl.rb +17 -8
  34. data/lib/meshtastic/serial.rb +81 -70
  35. data/lib/meshtastic/storeforward.rb +13 -6
  36. data/lib/meshtastic/stream_interface.rb +11 -0
  37. data/lib/meshtastic/tcp.rb +73 -22
  38. data/lib/meshtastic/telemetry.rb +15 -6
  39. data/lib/meshtastic/traceroute.rb +15 -6
  40. data/lib/meshtastic/util.rb +3 -1
  41. data/lib/meshtastic/version.rb +1 -1
  42. data/lib/meshtastic/xmodem.rb +12 -5
  43. data/lib/meshtastic.rb +22 -3
  44. data/spec/conventions_spec.rb +321 -0
  45. data/spec/lib/meshtastic/admin/channel_spec.rb +45 -0
  46. data/spec/lib/meshtastic/admin/config_spec.rb +48 -0
  47. data/spec/lib/meshtastic/admin/firmware_spec.rb +118 -0
  48. data/spec/lib/meshtastic/admin_spec.rb +40 -0
  49. data/spec/lib/meshtastic/apponly_spec.rb +1 -1
  50. data/spec/lib/meshtastic/atak_spec.rb +129 -4
  51. data/spec/lib/meshtastic/bluetooth_spec.rb +1 -1
  52. data/spec/lib/meshtastic/channel_spec.rb +2 -20
  53. data/spec/lib/meshtastic/config_spec.rb +2 -20
  54. data/spec/lib/meshtastic/connection_status_spec.rb +1 -1
  55. data/spec/lib/meshtastic/deviceonly_spec.rb +1 -1
  56. data/spec/lib/meshtastic/localonly_spec.rb +1 -1
  57. data/spec/lib/meshtastic/portnums_spec.rb +2 -2
  58. metadata +11 -1
@@ -16,13 +16,138 @@ describe Meshtastic::ATAK do
16
16
  { serial_conn: serial_conn, written: written, my_node_num: 0xb0b }
17
17
  end
18
18
 
19
- it 'sends a GeoChat TAKPacket' do
20
- serial_obj = fake_serial_obj
21
- described_class.send(serial_obj: serial_obj, message: 'ATAK chat')
19
+ def decode_to_radio(serial_obj)
22
20
  frame = serial_obj[:written]
23
- packet = Meshtastic::ToRadio.decode(frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))).packet
21
+ body = frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))
22
+ Meshtastic::ToRadio.decode(body).packet
23
+ end
24
+
25
+ it 'encodes V1 GeoChat with contact, group, and status' do
26
+ packet = described_class.encode_v1(
27
+ message: 'ATAK chat',
28
+ to: 'ANDROID-aabbccdd',
29
+ callsign: 'ALPHA',
30
+ device_callsign: 'RADIO-1',
31
+ team: :Cyan,
32
+ role: :TeamMember,
33
+ battery: 87
34
+ )
35
+ expect(packet).to be_a(Meshtastic::TAKPacket)
36
+ expect(packet.chat.message).to eq('ATAK chat')
37
+ expect(packet.chat.to).to eq('ANDROID-aabbccdd')
38
+ expect(packet.contact.callsign).to eq('ALPHA')
39
+ expect(packet.group.team).to eq(:Cyan)
40
+ expect(packet.group.role).to eq(:TeamMember)
41
+ expect(packet.status.battery).to eq(87)
42
+ end
43
+
44
+ it 'encodes V1 PLI with 1e7-scaled coordinates' do
45
+ packet = described_class.encode_v1(
46
+ lat: 37.7749,
47
+ lon: -122.4194,
48
+ altitude: 12,
49
+ speed: 3,
50
+ course: 90
51
+ )
52
+ expect(packet.pli.latitude_i).to eq(377_749_000)
53
+ expect(packet.pli.longitude_i).to eq(-1_224_194_000)
54
+ expect(packet.pli.altitude).to eq(12)
55
+ expect(packet.pli.speed).to eq(3)
56
+ expect(packet.pli.course).to eq(90)
57
+ end
58
+
59
+ it 'sends V1 GeoChat on ATAK_PLUGIN' do
60
+ serial_obj = fake_serial_obj
61
+ described_class.send_chat(serial_obj: serial_obj, message: 'ATAK chat')
62
+ packet = decode_to_radio(serial_obj)
24
63
  expect(packet.decoded.portnum).to eq(:ATAK_PLUGIN)
25
64
  tak = Meshtastic::TAKPacket.decode(packet.decoded.payload)
26
65
  expect(tak.chat.message).to eq('ATAK chat')
27
66
  end
67
+
68
+ it 'sends V1 PLI on ATAK_PLUGIN' do
69
+ serial_obj = fake_serial_obj
70
+ described_class.send_pli(serial_obj: serial_obj, lat: 37.7749, lon: -122.4194)
71
+ packet = decode_to_radio(serial_obj)
72
+ expect(packet.decoded.portnum).to eq(:ATAK_PLUGIN)
73
+ tak = Meshtastic::TAKPacket.decode(packet.decoded.payload)
74
+ expect(tak.pli.latitude_i).to eq(377_749_000)
75
+ end
76
+
77
+ it 'encodes uncompressed V2 GeoChat wire frames with flags 0xFF' do
78
+ wire = described_class.encode_v2(
79
+ callsign: 'ALPHA',
80
+ team: :Cyan,
81
+ role: :TeamMember,
82
+ lat: 37.7749,
83
+ lon: -122.4194,
84
+ message: 'v2 chat'
85
+ )
86
+ expect(wire.getbyte(0)).to eq(0xFF)
87
+ v2 = Meshtastic::TAKPacketV2.decode(wire.byteslice(1..))
88
+ expect(v2.chat.message).to eq('v2 chat')
89
+ expect(v2.callsign).to eq('ALPHA')
90
+ expect(v2.latitude_i).to eq(377_749_000)
91
+ end
92
+
93
+ it 'round-trips V2 typed payloads' do
94
+ {
95
+ aircraft: Meshtastic::AircraftTrack.new(icao: 'ABC123', flight: 'N1'),
96
+ shape: Meshtastic::DrawnShape.new(kind: :Kind_Circle, major_cm: 1000),
97
+ marker: Meshtastic::Marker.new(kind: :Kind_Spot),
98
+ route: Meshtastic::Route.new(prefix: 'R1'),
99
+ casevac: Meshtastic::CasevacReport.new(title: 'CASEVAC'),
100
+ emergency: Meshtastic::EmergencyAlert.new(type: :Type_Alert911),
101
+ task: Meshtastic::TaskRequest.new(task_type: 'recon', note: 'look'),
102
+ taktalk: Meshtastic::TakTalkMessage.new(text: 'voice', chatroom_id: 'room1'),
103
+ taktalk_room: Meshtastic::TakTalkRoomData.new(room_id: 'room1', room_name: 'Ops'),
104
+ rab: Meshtastic::RangeAndBearing.new(range_cm: 500)
105
+ }.each do |field, value|
106
+ v2 = described_class.build_v2(field => value, callsign: 'ALPHA')
107
+ expect(v2.public_send(field)).to eq(value)
108
+ decoded = described_class.decode_v2(wire: described_class.wrap_v2(packet: v2))
109
+ expect(decoded.public_send(field).to_h).to eq(value.to_h)
110
+ end
111
+ end
112
+
113
+ it 'sends V2 packets on ATAK_PLUGIN_V2' do
114
+ serial_obj = fake_serial_obj
115
+ described_class.send_v2(serial_obj: serial_obj, message: 'v2 chat', callsign: 'ALPHA')
116
+ packet = decode_to_radio(serial_obj)
117
+ expect(packet.decoded.portnum).to eq(:ATAK_PLUGIN_V2)
118
+ decoded = described_class.decode_v2(payload: packet.decoded.payload)
119
+ expect(decoded.chat.message).to eq('v2 chat')
120
+ end
121
+
122
+ it 'zlib-compresses CoT XML on ATAK_FORWARDER' do
123
+ serial_obj = fake_serial_obj
124
+ cot = '<event type="b-m-p-s-m" uid="marker-1"><point lat="37.77" lon="-122.41"/></event>'
125
+ described_class.send_cot(serial_obj: serial_obj, cot: cot)
126
+ packet = decode_to_radio(serial_obj)
127
+ expect(packet.decoded.portnum).to eq(:ATAK_FORWARDER)
128
+ xml = described_class.decompress_cot(payload: packet.decoded.payload)
129
+ expect(xml).to include('marker-1')
130
+ end
131
+
132
+ it 'decodes inbound payloads by portnum' do
133
+ v1 = described_class.encode_v1(message: 'hi')
134
+ expect(described_class.decode(payload: v1.to_proto, portnum: :ATAK_PLUGIN).chat.message).to eq('hi')
135
+
136
+ v2 = described_class.encode_v2(message: 'v2')
137
+ expect(described_class.decode(payload: v2, portnum: :ATAK_PLUGIN_V2).chat.message).to eq('v2')
138
+
139
+ cot = described_class.compress_cot(cot: '<event uid="x"/>')
140
+ expect(described_class.decode(payload: cot, portnum: :ATAK_FORWARDER)).to include('uid="x"')
141
+ end
142
+
143
+ it 'keeps send as a V1 GeoChat alias' do
144
+ serial_obj = fake_serial_obj
145
+ described_class.send(serial_obj: serial_obj, message: 'ATAK chat')
146
+ packet = decode_to_radio(serial_obj)
147
+ expect(packet.decoded.portnum).to eq(:ATAK_PLUGIN)
148
+ end
149
+
150
+ it 'prints usage without raising' do
151
+ expect { described_class.help }.to output(/ATAK_PLUGIN_V2/).to_stdout
152
+ end
28
153
  end
@@ -136,7 +136,7 @@ describe Meshtastic::Bluetooth do
136
136
 
137
137
  it 'scans with the selected adapter and bounded discovery timeout' do
138
138
  devices = [{ address: 'AA:BB:CC:DD:EE:FF', name: 'Meshtastic_test', paired: true }]
139
- expect(described_class::BlueZ).to receive(:scan).with(adapter: 'hci1', timeout: 2).and_return(devices)
139
+ expect(described_class::BlueZ).to receive(:scan).with({ adapter: 'hci1', timeout: 2 }).and_return(devices)
140
140
  expect(described_class.scan(adapter: 'hci1', timeout: 2)).to eq(devices)
141
141
  end
142
142
 
@@ -3,25 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Meshtastic::Channel do
6
- def fake_serial_obj
7
- written = +''.b
8
- serial_conn = Object.new
9
- serial_conn.define_singleton_method(:write) do |b|
10
- written << b
11
- b.bytesize
12
- end
13
- serial_conn.define_singleton_method(:flush) { true }
14
- serial_conn.define_singleton_method(:closed?) { false }
15
- serial_conn.define_singleton_method(:close) { true }
16
- { serial_conn: serial_conn, written: written, my_node_num: 0xb0b }
17
- end
18
-
19
- it 'requests a channel via Admin' do
20
- serial_obj = fake_serial_obj
21
- described_class.get(serial_obj: serial_obj, index: 1)
22
- frame = serial_obj[:written]
23
- packet = Meshtastic::ToRadio.decode(frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))).packet
24
- admin = Meshtastic::AdminMessage.decode(packet.decoded.payload)
25
- expect(admin.get_channel_request).to eq(1)
6
+ it 'is the generated Channel protobuf' do
7
+ expect(described_class.new).to be_a(Google::Protobuf::MessageExts)
26
8
  end
27
9
  end
@@ -3,25 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Meshtastic::Config do
6
- def fake_serial_obj
7
- written = +''.b
8
- serial_conn = Object.new
9
- serial_conn.define_singleton_method(:write) do |b|
10
- written << b
11
- b.bytesize
12
- end
13
- serial_conn.define_singleton_method(:flush) { true }
14
- serial_conn.define_singleton_method(:closed?) { false }
15
- serial_conn.define_singleton_method(:close) { true }
16
- { serial_conn: serial_conn, written: written, my_node_num: 0xb0b }
17
- end
18
-
19
- it 'requests LoRa config via Admin' do
20
- serial_obj = fake_serial_obj
21
- described_class.get(serial_obj: serial_obj, config_type: :LORA_CONFIG)
22
- frame = serial_obj[:written]
23
- packet = Meshtastic::ToRadio.decode(frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))).packet
24
- admin = Meshtastic::AdminMessage.decode(packet.decoded.payload)
25
- expect(admin.get_config_request).to eq(:LORA_CONFIG)
6
+ it 'is the generated Config protobuf' do
7
+ expect(described_class.new).to be_a(Google::Protobuf::MessageExts)
26
8
  end
27
9
  end
@@ -4,6 +4,6 @@ require 'spec_helper'
4
4
 
5
5
  describe Meshtastic::ConnectionStatus do
6
6
  it 'decodes DeviceConnectionStatus bytes' do
7
- expect(described_class.decode(Meshtastic::DeviceConnectionStatus.new.to_proto)).to be_a(Meshtastic::DeviceConnectionStatus)
7
+ expect(described_class.decode(bytes: Meshtastic::DeviceConnectionStatus.new.to_proto)).to be_a(Meshtastic::DeviceConnectionStatus)
8
8
  end
9
9
  end
@@ -4,6 +4,6 @@ require 'spec_helper'
4
4
 
5
5
  describe Meshtastic::Deviceonly do
6
6
  it 'decodes an empty DeviceState' do
7
- expect(described_class.decode_state(Meshtastic::DeviceState.new.to_proto)).to be_a(Meshtastic::DeviceState)
7
+ expect(described_class.decode_state(bytes: Meshtastic::DeviceState.new.to_proto)).to be_a(Meshtastic::DeviceState)
8
8
  end
9
9
  end
@@ -4,6 +4,6 @@ require 'spec_helper'
4
4
 
5
5
  describe Meshtastic::Localonly do
6
6
  it 'decodes an empty LocalConfig' do
7
- expect(described_class.decode_config(Meshtastic::LocalConfig.new.to_proto)).to be_a(Meshtastic::LocalConfig)
7
+ expect(described_class.decode_config(bytes: Meshtastic::LocalConfig.new.to_proto)).to be_a(Meshtastic::LocalConfig)
8
8
  end
9
9
  end
@@ -4,7 +4,7 @@ require 'spec_helper'
4
4
 
5
5
  describe Meshtastic::Portnums do
6
6
  it 'resolves names and numbers' do
7
- expect(described_class.lookup(:TEXT_MESSAGE_APP)).to eq(1)
8
- expect(described_class.lookup(1)).to eq(:TEXT_MESSAGE_APP)
7
+ expect(described_class.lookup(value: :TEXT_MESSAGE_APP)).to eq(1)
8
+ expect(described_class.lookup(value: 1)).to eq(:TEXT_MESSAGE_APP)
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meshtastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.179
4
+ version: 0.0.180
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -285,6 +285,9 @@ files:
285
285
  - bin/meshtastic_autoinc_version
286
286
  - build_gem.sh
287
287
  - documentation/README.md
288
+ - documentation/admin-channel.md
289
+ - documentation/admin-config.md
290
+ - documentation/admin-firmware.md
288
291
  - documentation/admin.md
289
292
  - documentation/apponly.md
290
293
  - documentation/atak.md
@@ -318,6 +321,9 @@ files:
318
321
  - git_commit.sh
319
322
  - lib/meshtastic.rb
320
323
  - lib/meshtastic/admin.rb
324
+ - lib/meshtastic/admin/channel.rb
325
+ - lib/meshtastic/admin/config.rb
326
+ - lib/meshtastic/admin/firmware.rb
321
327
  - lib/meshtastic/admin_pb.rb
322
328
  - lib/meshtastic/apponly.rb
323
329
  - lib/meshtastic/apponly_pb.rb
@@ -377,6 +383,10 @@ files:
377
383
  - meshtastic.gemspec
378
384
  - reinstall_gemset.sh
379
385
  - sig/meshtastic.rbs
386
+ - spec/conventions_spec.rb
387
+ - spec/lib/meshtastic/admin/channel_spec.rb
388
+ - spec/lib/meshtastic/admin/config_spec.rb
389
+ - spec/lib/meshtastic/admin/firmware_spec.rb
380
390
  - spec/lib/meshtastic/admin_pb_spec.rb
381
391
  - spec/lib/meshtastic/admin_spec.rb
382
392
  - spec/lib/meshtastic/apponly_pb_spec.rb