meshtastic 0.0.178 → 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/README.md +3 -175
- data/documentation/README.md +60 -0
- 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 +55 -0
- data/documentation/apponly.md +22 -0
- data/documentation/atak.md +110 -0
- data/documentation/bluetooth-bluez.md +35 -0
- data/documentation/bluetooth.md +87 -0
- data/documentation/cannedmessages.md +25 -0
- data/documentation/channel.md +3 -0
- data/documentation/clientonly.md +21 -0
- data/documentation/config.md +3 -0
- data/documentation/connection-status.md +22 -0
- data/documentation/deviceonly.md +22 -0
- data/documentation/localonly.md +21 -0
- data/documentation/mesh-interface.md +52 -0
- data/documentation/meshtastic.md +45 -0
- data/documentation/module-config.md +26 -0
- data/documentation/mqtt.md +101 -0
- data/documentation/paxcount.md +25 -0
- data/documentation/portnums.md +67 -0
- data/documentation/position.md +34 -0
- data/documentation/protobufs.md +162 -0
- data/documentation/remote-hardware.md +33 -0
- data/documentation/rtttl.md +24 -0
- data/documentation/serial.md +78 -0
- data/documentation/storeforward.md +24 -0
- data/documentation/stream-interface.md +15 -0
- data/documentation/tcp.md +43 -0
- data/documentation/telemetry.md +32 -0
- data/documentation/traceroute.md +28 -0
- data/documentation/util.md +27 -0
- data/documentation/xmodem.md +22 -0
- 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 +495 -7
- data/lib/meshtastic/apponly.rb +26 -8
- data/lib/meshtastic/atak.rb +298 -7
- data/lib/meshtastic/bluetooth/bluez.rb +20 -1
- data/lib/meshtastic/bluetooth.rb +70 -59
- data/lib/meshtastic/cannedmessages.rb +23 -8
- data/lib/meshtastic/channel.rb +3 -21
- data/lib/meshtastic/clientonly.rb +26 -8
- data/lib/meshtastic/config.rb +3 -21
- data/lib/meshtastic/connection_status.rb +13 -8
- data/lib/meshtastic/deviceonly.rb +23 -8
- data/lib/meshtastic/localonly.rb +23 -8
- data/lib/meshtastic/mesh_interface.rb +11 -0
- data/lib/meshtastic/module_config.rb +18 -9
- data/lib/meshtastic/mqtt.rb +72 -35
- data/lib/meshtastic/paxcount.rb +30 -9
- data/lib/meshtastic/portnums.rb +18 -8
- data/lib/meshtastic/position.rb +45 -0
- data/lib/meshtastic/remote_hardware.rb +50 -9
- data/lib/meshtastic/rtttl.rb +26 -8
- data/lib/meshtastic/serial.rb +81 -70
- data/lib/meshtastic/storeforward.rb +23 -8
- data/lib/meshtastic/stream_interface.rb +11 -0
- data/lib/meshtastic/tcp.rb +153 -0
- data/lib/meshtastic/telemetry.rb +27 -9
- data/lib/meshtastic/traceroute.rb +42 -0
- data/lib/meshtastic/util.rb +3 -1
- data/lib/meshtastic/version.rb +1 -1
- data/lib/meshtastic/xmodem.rb +17 -8
- data/lib/meshtastic.rb +46 -6
- 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_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/admin_spec.rb +79 -0
- data/spec/lib/meshtastic/apponly_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/apponly_spec.rb +4 -0
- data/spec/lib/meshtastic/atak_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/atak_spec.rb +147 -0
- data/spec/lib/meshtastic/bluetooth_spec.rb +1 -1
- data/spec/lib/meshtastic/cannedmessages_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/cannedmessages_spec.rb +3 -0
- data/spec/lib/meshtastic/channel_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/channel_spec.rb +3 -0
- data/spec/lib/meshtastic/clientonly_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/clientonly_spec.rb +5 -0
- data/spec/lib/meshtastic/config_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/config_spec.rb +4 -1
- data/spec/lib/meshtastic/connection_status_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/connection_status_spec.rb +3 -0
- data/spec/lib/meshtastic/deviceonly_legacy_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/deviceonly_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/deviceonly_spec.rb +3 -0
- data/spec/lib/meshtastic/localonly_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/localonly_spec.rb +3 -0
- data/spec/lib/meshtastic/mesh_beacon_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/mesh_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/module_config_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/module_config_spec.rb +21 -0
- data/spec/lib/meshtastic/mqtt_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/paxcount_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/paxcount_spec.rb +23 -0
- data/spec/lib/meshtastic/portnums_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/portnums_spec.rb +4 -0
- data/spec/lib/meshtastic/position_spec.rb +33 -0
- data/spec/lib/meshtastic/remote_hardware_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/remote_hardware_spec.rb +24 -0
- data/spec/lib/meshtastic/rtttl_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/rtttl_spec.rb +21 -0
- data/spec/lib/meshtastic/storeforward_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/storeforward_spec.rb +21 -0
- data/spec/lib/meshtastic/tcp_spec.rb +48 -0
- data/spec/lib/meshtastic/telemetry_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/telemetry_spec.rb +21 -0
- data/spec/lib/meshtastic/traceroute_spec.rb +27 -0
- data/spec/lib/meshtastic/xmodem_pb_spec.rb +1 -1
- data/spec/lib/meshtastic/xmodem_spec.rb +6 -1
- data/spec/lib/meshtastic_spec.rb +3 -0
- metadata +48 -1
|
@@ -3,4 +3,83 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe Meshtastic::Admin 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
|
+
def decode_admin(serial_obj)
|
|
20
|
+
frame = serial_obj[:written]
|
|
21
|
+
body = frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))
|
|
22
|
+
packet = Meshtastic::ToRadio.decode(body).packet
|
|
23
|
+
[packet, Meshtastic::AdminMessage.decode(packet.decoded.payload)]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'sends a reboot AdminMessage on ADMIN_APP' do
|
|
27
|
+
serial_obj = fake_serial_obj
|
|
28
|
+
described_class.reboot(serial_obj: serial_obj, seconds: 7)
|
|
29
|
+
packet, admin = decode_admin(serial_obj)
|
|
30
|
+
expect(packet.decoded.portnum).to eq(:ADMIN_APP)
|
|
31
|
+
expect(admin.reboot_seconds).to eq(7)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'sends a set_owner AdminMessage' do
|
|
35
|
+
serial_obj = fake_serial_obj
|
|
36
|
+
described_class.set_owner(serial_obj: serial_obj, long_name: 'Test Node', short_name: 'TN')
|
|
37
|
+
_packet, admin = decode_admin(serial_obj)
|
|
38
|
+
expect(admin.set_owner.long_name).to eq('Test Node')
|
|
39
|
+
expect(admin.set_owner.short_name).to eq('TN')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'covers remaining AdminMessage request fields' do
|
|
43
|
+
serial_obj = fake_serial_obj
|
|
44
|
+
examples = {
|
|
45
|
+
get_module_config: %i[get_module_config_request MQTT_CONFIG],
|
|
46
|
+
get_canned_messages: [:get_canned_message_module_messages_request, true],
|
|
47
|
+
get_device_metadata: [:get_device_metadata_request, true],
|
|
48
|
+
get_ringtone: [:get_ringtone_request, true],
|
|
49
|
+
get_device_connection_status: [:get_device_connection_status_request, true],
|
|
50
|
+
get_node_remote_hardware_pins: [:get_node_remote_hardware_pins_request, true],
|
|
51
|
+
get_ui_config: [:get_ui_config_request, true],
|
|
52
|
+
enter_dfu: [:enter_dfu_mode_request, true],
|
|
53
|
+
begin_edit: [:begin_edit_settings, true],
|
|
54
|
+
commit_edit: [:commit_edit_settings, true],
|
|
55
|
+
remove_fixed_position: [:remove_fixed_position, true],
|
|
56
|
+
exit_simulator: [:exit_simulator, true],
|
|
57
|
+
nodedb_reset: [:nodedb_reset, true]
|
|
58
|
+
}
|
|
59
|
+
examples.each do |meth, (field, expected)|
|
|
60
|
+
serial_obj[:written].clear
|
|
61
|
+
described_class.public_send(meth, serial_obj: serial_obj)
|
|
62
|
+
_packet, admin = decode_admin(serial_obj)
|
|
63
|
+
expect(admin.public_send(field)).to eq(expected), meth.to_s
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'sends factory resets, file delete, favorites, and edit transactions' do
|
|
68
|
+
serial_obj = fake_serial_obj
|
|
69
|
+
described_class.factory_reset_config(serial_obj: serial_obj)
|
|
70
|
+
expect(decode_admin(serial_obj).last.factory_reset_config).to eq(1)
|
|
71
|
+
serial_obj[:written].clear
|
|
72
|
+
described_class.factory_reset_device(serial_obj: serial_obj)
|
|
73
|
+
expect(decode_admin(serial_obj).last.factory_reset_device).to eq(1)
|
|
74
|
+
serial_obj[:written].clear
|
|
75
|
+
described_class.delete_file(serial_obj: serial_obj, path: '/prefs.json')
|
|
76
|
+
expect(decode_admin(serial_obj).last.delete_file_request).to eq('/prefs.json')
|
|
77
|
+
serial_obj[:written].clear
|
|
78
|
+
described_class.set_favorite_node(serial_obj: serial_obj, node_num: 0xb0b)
|
|
79
|
+
expect(decode_admin(serial_obj).last.set_favorite_node).to eq(0xb0b)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'prints usage without raising' do
|
|
83
|
+
expect { described_class.help }.to output(/USAGE/).to_stdout
|
|
84
|
+
end
|
|
6
85
|
end
|
|
@@ -3,4 +3,151 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe Meshtastic::ATAK 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
|
+
def decode_to_radio(serial_obj)
|
|
20
|
+
frame = serial_obj[:written]
|
|
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)
|
|
63
|
+
expect(packet.decoded.portnum).to eq(:ATAK_PLUGIN)
|
|
64
|
+
tak = Meshtastic::TAKPacket.decode(packet.decoded.payload)
|
|
65
|
+
expect(tak.chat.message).to eq('ATAK chat')
|
|
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
|
|
6
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,4 +3,25 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe Meshtastic::ModuleConfig 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 MQTT module config via Admin' do
|
|
20
|
+
serial_obj = fake_serial_obj
|
|
21
|
+
described_class.get(serial_obj: serial_obj, module_config_type: :MQTT_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_module_config_request).to eq(:MQTT_CONFIG)
|
|
26
|
+
end
|
|
6
27
|
end
|
|
@@ -3,4 +3,27 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe Meshtastic::Paxcount 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 'transmits PAXCOUNTER_APP counts' do
|
|
20
|
+
serial_obj = fake_serial_obj
|
|
21
|
+
described_class.transmit(serial_obj: serial_obj, wifi: 3, ble: 2)
|
|
22
|
+
frame = serial_obj[:written]
|
|
23
|
+
packet = Meshtastic::ToRadio.decode(frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))).packet
|
|
24
|
+
expect(packet.decoded.portnum).to eq(:PAXCOUNTER_APP)
|
|
25
|
+
count = Meshtastic::Paxcount.decode(packet.decoded.payload)
|
|
26
|
+
expect(count.wifi).to eq(3)
|
|
27
|
+
expect(count.ble).to eq(2)
|
|
28
|
+
end
|
|
6
29
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Meshtastic::Position 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 'sends a POSITION_APP packet with scaled lat/lon' do
|
|
20
|
+
serial_obj = fake_serial_obj
|
|
21
|
+
described_class.transmit(serial_obj: serial_obj, lat: 37.7749, lon: -122.4194)
|
|
22
|
+
frame = serial_obj[:written]
|
|
23
|
+
packet = Meshtastic::ToRadio.decode(frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))).packet
|
|
24
|
+
expect(packet.decoded.portnum).to eq(:POSITION_APP)
|
|
25
|
+
position = Meshtastic::Position.decode(packet.decoded.payload)
|
|
26
|
+
expect(position.latitude_i).to eq(377_749_000)
|
|
27
|
+
expect(position.longitude_i).to eq(-1_224_194_000)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'prints usage without raising' do
|
|
31
|
+
expect { described_class.help }.to output(/USAGE/).to_stdout
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -3,4 +3,28 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe Meshtastic::RemoteHardware 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 'writes GPIO bits on REMOTE_HARDWARE_APP' do
|
|
20
|
+
serial_obj = fake_serial_obj
|
|
21
|
+
described_class.write_gpios(serial_obj: serial_obj, gpio_mask: 0x01, gpio_value: 0x01)
|
|
22
|
+
frame = serial_obj[:written]
|
|
23
|
+
packet = Meshtastic::ToRadio.decode(frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))).packet
|
|
24
|
+
expect(packet.decoded.portnum).to eq(:REMOTE_HARDWARE_APP)
|
|
25
|
+
hw = Meshtastic::HardwareMessage.decode(packet.decoded.payload)
|
|
26
|
+
expect(hw.type).to eq(:WRITE_GPIOS)
|
|
27
|
+
expect(hw.gpio_mask).to eq(1)
|
|
28
|
+
expect(hw.gpio_value).to eq(1)
|
|
29
|
+
end
|
|
6
30
|
end
|
|
@@ -3,4 +3,25 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe Meshtastic::RTTTL 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 'sets a ringtone via Admin' do
|
|
20
|
+
serial_obj = fake_serial_obj
|
|
21
|
+
described_class.set(serial_obj: serial_obj, ringtone: 'beep:d=4,o=5,b=120:16c6')
|
|
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.set_ringtone_message).to eq('beep:d=4,o=5,b=120:16c6')
|
|
26
|
+
end
|
|
6
27
|
end
|
|
@@ -3,4 +3,25 @@
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
5
|
describe Meshtastic::Storeforward 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 'sends STORE_FORWARD_APP history request' do
|
|
20
|
+
serial_obj = fake_serial_obj
|
|
21
|
+
described_class.send(serial_obj: serial_obj, rr: :CLIENT_HISTORY)
|
|
22
|
+
frame = serial_obj[:written]
|
|
23
|
+
packet = Meshtastic::ToRadio.decode(frame.byteslice(4, (frame.getbyte(2) << 8) + frame.getbyte(3))).packet
|
|
24
|
+
expect(packet.decoded.portnum).to eq(:STORE_FORWARD_APP)
|
|
25
|
+
expect(Meshtastic::StoreAndForward.decode(packet.decoded.payload).rr).to eq(:CLIENT_HISTORY)
|
|
26
|
+
end
|
|
6
27
|
end
|