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.
- checksums.yaml +4 -4
- data/documentation/README.md +3 -2
- data/documentation/admin-channel.md +33 -0
- data/documentation/admin-config.md +28 -0
- data/documentation/admin-firmware.md +69 -0
- data/documentation/admin.md +2 -1
- data/documentation/atak.md +93 -10
- data/documentation/channel.md +1 -28
- data/documentation/config.md +1 -27
- data/documentation/meshtastic.md +2 -2
- data/lib/meshtastic/admin/channel.rb +90 -0
- data/lib/meshtastic/admin/config.rb +154 -0
- data/lib/meshtastic/admin/firmware.rb +173 -0
- data/lib/meshtastic/admin.rb +458 -28
- data/lib/meshtastic/apponly.rb +19 -7
- data/lib/meshtastic/atak.rb +295 -17
- data/lib/meshtastic/bluetooth/bluez.rb +20 -1
- data/lib/meshtastic/bluetooth.rb +70 -59
- data/lib/meshtastic/cannedmessages.rb +13 -6
- data/lib/meshtastic/channel.rb +3 -25
- data/lib/meshtastic/clientonly.rb +19 -7
- data/lib/meshtastic/config.rb +3 -24
- data/lib/meshtastic/connection_status.rb +12 -5
- data/lib/meshtastic/deviceonly.rb +19 -7
- data/lib/meshtastic/localonly.rb +19 -7
- data/lib/meshtastic/mesh_interface.rb +11 -0
- data/lib/meshtastic/module_config.rb +13 -7
- data/lib/meshtastic/mqtt.rb +72 -35
- data/lib/meshtastic/paxcount.rb +17 -6
- data/lib/meshtastic/portnums.rb +13 -6
- data/lib/meshtastic/position.rb +16 -6
- data/lib/meshtastic/remote_hardware.rb +27 -11
- data/lib/meshtastic/rtttl.rb +17 -8
- data/lib/meshtastic/serial.rb +81 -70
- data/lib/meshtastic/storeforward.rb +13 -6
- data/lib/meshtastic/stream_interface.rb +11 -0
- data/lib/meshtastic/tcp.rb +73 -22
- data/lib/meshtastic/telemetry.rb +15 -6
- data/lib/meshtastic/traceroute.rb +15 -6
- data/lib/meshtastic/util.rb +3 -1
- data/lib/meshtastic/version.rb +1 -1
- data/lib/meshtastic/xmodem.rb +12 -5
- data/lib/meshtastic.rb +22 -3
- data/spec/conventions_spec.rb +321 -0
- data/spec/lib/meshtastic/admin/channel_spec.rb +45 -0
- data/spec/lib/meshtastic/admin/config_spec.rb +48 -0
- data/spec/lib/meshtastic/admin/firmware_spec.rb +118 -0
- data/spec/lib/meshtastic/admin_spec.rb +40 -0
- data/spec/lib/meshtastic/apponly_spec.rb +1 -1
- data/spec/lib/meshtastic/atak_spec.rb +129 -4
- data/spec/lib/meshtastic/bluetooth_spec.rb +1 -1
- data/spec/lib/meshtastic/channel_spec.rb +2 -20
- data/spec/lib/meshtastic/config_spec.rb +2 -20
- data/spec/lib/meshtastic/connection_status_spec.rb +1 -1
- data/spec/lib/meshtastic/deviceonly_spec.rb +1 -1
- data/spec/lib/meshtastic/localonly_spec.rb +1 -1
- data/spec/lib/meshtastic/portnums_spec.rb +2 -2
- metadata +11 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
require 'meshtastic/xmodem_pb'
|
|
5
|
+
|
|
6
|
+
module Meshtastic
|
|
7
|
+
module Admin
|
|
8
|
+
module Firmware
|
|
9
|
+
SOH_SIZE = 128
|
|
10
|
+
PAD = 0x1A
|
|
11
|
+
|
|
12
|
+
public_class_method def self.sha256(opts = {})
|
|
13
|
+
Digest::SHA256.digest(firmware_bytes(opts.merge({})))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
public_class_method def self.request_ota(opts = {})
|
|
17
|
+
hash = opts[:ota_hash] || sha256(opts)
|
|
18
|
+
raise ArgumentError, 'ota_hash must be 32 bytes' unless hash.to_s.bytesize == 32
|
|
19
|
+
|
|
20
|
+
event = Meshtastic::AdminMessage::OTAEvent.new(
|
|
21
|
+
reboot_ota_mode: opts[:mode] || :OTA_BLE,
|
|
22
|
+
ota_hash: hash.to_s.b
|
|
23
|
+
)
|
|
24
|
+
Admin.send(opts.merge(ota_request: event))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
public_class_method def self.enter_dfu(opts = {})
|
|
28
|
+
Admin.send(opts.merge(enter_dfu_mode_request: true))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
public_class_method def self.reboot_ota(opts = {})
|
|
32
|
+
Admin.send(opts.merge(reboot_ota_seconds: opts[:seconds] || 10))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
public_class_method def self.xmodem_blocks(opts = {})
|
|
36
|
+
payload = opts[:bytes].to_s.b
|
|
37
|
+
block_size = opts[:block_size] || SOH_SIZE
|
|
38
|
+
raise ArgumentError, 'firmware bytes are empty' if payload.empty?
|
|
39
|
+
|
|
40
|
+
blocks = []
|
|
41
|
+
seq = 1
|
|
42
|
+
offset = 0
|
|
43
|
+
while offset < payload.bytesize
|
|
44
|
+
chunk = payload.byteslice(offset, block_size).to_s.b
|
|
45
|
+
chunk = chunk.ljust(block_size, PAD.chr) if chunk.bytesize < block_size
|
|
46
|
+
blocks << Meshtastic::XModem.new(
|
|
47
|
+
control: block_size > SOH_SIZE ? :STX : :SOH,
|
|
48
|
+
seq: seq,
|
|
49
|
+
crc16: crc16(data: chunk),
|
|
50
|
+
buffer: chunk
|
|
51
|
+
)
|
|
52
|
+
seq += 1
|
|
53
|
+
offset += block_size
|
|
54
|
+
end
|
|
55
|
+
blocks << Meshtastic::XModem.new(control: :EOT, seq: seq)
|
|
56
|
+
blocks
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
public_class_method def self.send_xmodem(opts = {})
|
|
60
|
+
packet = opts[:xmodem]
|
|
61
|
+
to_radio = Meshtastic::ToRadio.new
|
|
62
|
+
to_radio.xmodemPacket = packet
|
|
63
|
+
send_phone(opts.merge(to_radio: to_radio))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
public_class_method def self.install(opts = {})
|
|
67
|
+
bytes = firmware_bytes(opts.merge({}))
|
|
68
|
+
request_ota(opts.merge(bytes: bytes))
|
|
69
|
+
return if opts[:mqtt_obj]
|
|
70
|
+
|
|
71
|
+
xmodem_blocks(bytes: bytes).each do |packet|
|
|
72
|
+
send_xmodem(opts.merge(xmodem: packet))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
public_class_method def self.authors
|
|
77
|
+
"AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n "
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
public_class_method def self.help
|
|
81
|
+
puts "USAGE:
|
|
82
|
+
# SHA-256 the firmware image bytes or file.
|
|
83
|
+
#{self}.sha256(
|
|
84
|
+
firmware: 'optional - path to a firmware .bin on disk',
|
|
85
|
+
bytes: 'optional - raw firmware image bytes if no path is given'
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Send Admin ota_request with the image hash and OTA mode.
|
|
89
|
+
#{self}.request_ota(
|
|
90
|
+
serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
|
|
91
|
+
mqtt_obj: 'optional - MQTT client from Meshtastic::MQTT.connect',
|
|
92
|
+
firmware: 'optional - path to a firmware .bin on disk',
|
|
93
|
+
ota_hash: 'optional - 32-byte SHA-256 digest if not hashing firmware',
|
|
94
|
+
mode: 'optional - :OTA_BLE or :OTA_WIFI (default: :OTA_BLE)'
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Ask the node to enter DFU / UF2 bootloader mode.
|
|
98
|
+
#{self}.enter_dfu(
|
|
99
|
+
serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Send the legacy reboot_ota_seconds admin field.
|
|
103
|
+
#{self}.reboot_ota(
|
|
104
|
+
serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
|
|
105
|
+
seconds: 'optional - delay before OTA reboot in seconds (default: 10)'
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Split firmware bytes into XModem SOH blocks plus EOT.
|
|
109
|
+
#{self}.xmodem_blocks(
|
|
110
|
+
bytes: 'required - raw firmware image bytes to chunk',
|
|
111
|
+
block_size: 'optional - XModem block size in bytes (default: 128)'
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Write one XModem protobuf as a PhoneAPI ToRadio frame.
|
|
115
|
+
#{self}.send_xmodem(
|
|
116
|
+
xmodem: 'required - Meshtastic::XModem protobuf to write',
|
|
117
|
+
serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
|
|
118
|
+
tcp_obj: 'optional - TCP handle from Meshtastic::TCP.connect',
|
|
119
|
+
bluetooth_obj: 'optional - BLE handle from Meshtastic::Bluetooth.connect'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Hash, send ota_request, then stream XModem on serial/TCP/BLE.
|
|
123
|
+
#{self}.install(
|
|
124
|
+
firmware: 'optional - path to a firmware .bin on disk',
|
|
125
|
+
bytes: 'optional - raw firmware image bytes if no path is given',
|
|
126
|
+
serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
|
|
127
|
+
tcp_obj: 'optional - TCP handle from Meshtastic::TCP.connect',
|
|
128
|
+
bluetooth_obj: 'optional - BLE handle from Meshtastic::Bluetooth.connect',
|
|
129
|
+
mqtt_obj: 'optional - MQTT client; publishes ota_request only',
|
|
130
|
+
mode: 'optional - :OTA_BLE or :OTA_WIFI (default: :OTA_BLE)'
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Print the AUTHOR(S) string for this module.
|
|
134
|
+
#{self}.authors
|
|
135
|
+
"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private_class_method def self.firmware_bytes(opts = {})
|
|
139
|
+
if opts[:bytes]
|
|
140
|
+
opts[:bytes].to_s.b
|
|
141
|
+
elsif opts[:firmware]
|
|
142
|
+
File.binread(opts[:firmware])
|
|
143
|
+
else
|
|
144
|
+
raise ArgumentError, 'firmware path or bytes is required'
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private_class_method def self.crc16(opts = {})
|
|
149
|
+
crc = 0
|
|
150
|
+
opts[:data].to_s.b.each_byte do |byte|
|
|
151
|
+
crc ^= byte << 8
|
|
152
|
+
8.times do
|
|
153
|
+
crc = crc[15] == 1 ? ((crc << 1) ^ 0x1021) : (crc << 1)
|
|
154
|
+
crc &= 0xffff
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
crc
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private_class_method def self.send_phone(opts = {})
|
|
161
|
+
if opts[:serial_obj]
|
|
162
|
+
Serial.send_to_radio(opts)
|
|
163
|
+
elsif opts[:tcp_obj]
|
|
164
|
+
TCP.send_to_radio(opts)
|
|
165
|
+
elsif opts[:bluetooth_obj]
|
|
166
|
+
Bluetooth.send_to_radio(opts)
|
|
167
|
+
else
|
|
168
|
+
raise ArgumentError, 'serial_obj, bluetooth_obj, or tcp_obj is required for XModem'
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|