12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
# File 'lib/meshtastic/admin/firmware/ble.rb', line 12
public_class_method def self.install(opts = {})
unknown = opts.keys - %i[protocol bytes firmware backend address adapter timeout]
raise ArgumentError, "Unsupported BLE options: #{unknown.join(', ')}" unless unknown.empty?
raise ArgumentError, 'protocol must be :unified_ble' unless opts.fetch(:protocol, :unified_ble) == :unified_ble
timeout = opts.fetch(:timeout, 120)
raise ArgumentError, 'timeout must be positive finite seconds' unless timeout.is_a?(Numeric) && timeout.real? && timeout.finite? && timeout.positive?
bytes = Firmware.__send__(:firmware_bytes, opts)
backend = opts[:backend] || BlueZ.new(address: opts[:address], adapter: opts.fetch(:adapter, 'hci0'), timeout: timeout, service_uuid: SERVICE_UUID).connect
Timeout.timeout(timeout) do
backend.subscribe(uuid: TX_UUID)
stream = Stream.new(backend)
stream.command("VERSION\n")
version = stream.line
raise IOError, "Invalid loader VERSION: #{version}" unless version.match?(/\AOK \d+ \S+ \d+ \S+\z/)
digest = Digest::SHA256.hexdigest(bytes)
stream.command("OTA #{bytes.bytesize} #{digest}\n")
reply = stream.line
reply = stream.line if reply == 'ERASING'
raise IOError, "OTA handshake rejected: #{reply}" unless reply == 'OK'
offset = 0
while offset < bytes.bytesize
chunk = bytes.byteslice(offset, 20)
backend.write(uuid: OTA_UUID, bytes: chunk, response: true)
offset += chunk.bytesize
reply = stream.line
expected = offset == bytes.bytesize ? 'OK' : 'ACK'
raise IOError, "OTA transfer expected #{expected}, received: #{reply}" unless reply == expected
end
{ status: :verified, bytes: bytes.bytesize, sha256: digest, loader_version: version.delete_prefix('OK ') }
end
ensure
backend&.close
end
|