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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -175
  3. data/documentation/README.md +60 -0
  4. data/documentation/admin-channel.md +33 -0
  5. data/documentation/admin-config.md +28 -0
  6. data/documentation/admin-firmware.md +69 -0
  7. data/documentation/admin.md +55 -0
  8. data/documentation/apponly.md +22 -0
  9. data/documentation/atak.md +110 -0
  10. data/documentation/bluetooth-bluez.md +35 -0
  11. data/documentation/bluetooth.md +87 -0
  12. data/documentation/cannedmessages.md +25 -0
  13. data/documentation/channel.md +3 -0
  14. data/documentation/clientonly.md +21 -0
  15. data/documentation/config.md +3 -0
  16. data/documentation/connection-status.md +22 -0
  17. data/documentation/deviceonly.md +22 -0
  18. data/documentation/localonly.md +21 -0
  19. data/documentation/mesh-interface.md +52 -0
  20. data/documentation/meshtastic.md +45 -0
  21. data/documentation/module-config.md +26 -0
  22. data/documentation/mqtt.md +101 -0
  23. data/documentation/paxcount.md +25 -0
  24. data/documentation/portnums.md +67 -0
  25. data/documentation/position.md +34 -0
  26. data/documentation/protobufs.md +162 -0
  27. data/documentation/remote-hardware.md +33 -0
  28. data/documentation/rtttl.md +24 -0
  29. data/documentation/serial.md +78 -0
  30. data/documentation/storeforward.md +24 -0
  31. data/documentation/stream-interface.md +15 -0
  32. data/documentation/tcp.md +43 -0
  33. data/documentation/telemetry.md +32 -0
  34. data/documentation/traceroute.md +28 -0
  35. data/documentation/util.md +27 -0
  36. data/documentation/xmodem.md +22 -0
  37. data/lib/meshtastic/admin/channel.rb +90 -0
  38. data/lib/meshtastic/admin/config.rb +154 -0
  39. data/lib/meshtastic/admin/firmware.rb +173 -0
  40. data/lib/meshtastic/admin.rb +495 -7
  41. data/lib/meshtastic/apponly.rb +26 -8
  42. data/lib/meshtastic/atak.rb +298 -7
  43. data/lib/meshtastic/bluetooth/bluez.rb +20 -1
  44. data/lib/meshtastic/bluetooth.rb +70 -59
  45. data/lib/meshtastic/cannedmessages.rb +23 -8
  46. data/lib/meshtastic/channel.rb +3 -21
  47. data/lib/meshtastic/clientonly.rb +26 -8
  48. data/lib/meshtastic/config.rb +3 -21
  49. data/lib/meshtastic/connection_status.rb +13 -8
  50. data/lib/meshtastic/deviceonly.rb +23 -8
  51. data/lib/meshtastic/localonly.rb +23 -8
  52. data/lib/meshtastic/mesh_interface.rb +11 -0
  53. data/lib/meshtastic/module_config.rb +18 -9
  54. data/lib/meshtastic/mqtt.rb +72 -35
  55. data/lib/meshtastic/paxcount.rb +30 -9
  56. data/lib/meshtastic/portnums.rb +18 -8
  57. data/lib/meshtastic/position.rb +45 -0
  58. data/lib/meshtastic/remote_hardware.rb +50 -9
  59. data/lib/meshtastic/rtttl.rb +26 -8
  60. data/lib/meshtastic/serial.rb +81 -70
  61. data/lib/meshtastic/storeforward.rb +23 -8
  62. data/lib/meshtastic/stream_interface.rb +11 -0
  63. data/lib/meshtastic/tcp.rb +153 -0
  64. data/lib/meshtastic/telemetry.rb +27 -9
  65. data/lib/meshtastic/traceroute.rb +42 -0
  66. data/lib/meshtastic/util.rb +3 -1
  67. data/lib/meshtastic/version.rb +1 -1
  68. data/lib/meshtastic/xmodem.rb +17 -8
  69. data/lib/meshtastic.rb +46 -6
  70. data/spec/conventions_spec.rb +321 -0
  71. data/spec/lib/meshtastic/admin/channel_spec.rb +45 -0
  72. data/spec/lib/meshtastic/admin/config_spec.rb +48 -0
  73. data/spec/lib/meshtastic/admin/firmware_spec.rb +118 -0
  74. data/spec/lib/meshtastic/admin_pb_spec.rb +1 -1
  75. data/spec/lib/meshtastic/admin_spec.rb +79 -0
  76. data/spec/lib/meshtastic/apponly_pb_spec.rb +1 -1
  77. data/spec/lib/meshtastic/apponly_spec.rb +4 -0
  78. data/spec/lib/meshtastic/atak_pb_spec.rb +1 -1
  79. data/spec/lib/meshtastic/atak_spec.rb +147 -0
  80. data/spec/lib/meshtastic/bluetooth_spec.rb +1 -1
  81. data/spec/lib/meshtastic/cannedmessages_pb_spec.rb +1 -1
  82. data/spec/lib/meshtastic/cannedmessages_spec.rb +3 -0
  83. data/spec/lib/meshtastic/channel_pb_spec.rb +1 -1
  84. data/spec/lib/meshtastic/channel_spec.rb +3 -0
  85. data/spec/lib/meshtastic/clientonly_pb_spec.rb +1 -1
  86. data/spec/lib/meshtastic/clientonly_spec.rb +5 -0
  87. data/spec/lib/meshtastic/config_pb_spec.rb +1 -1
  88. data/spec/lib/meshtastic/config_spec.rb +4 -1
  89. data/spec/lib/meshtastic/connection_status_pb_spec.rb +1 -1
  90. data/spec/lib/meshtastic/connection_status_spec.rb +3 -0
  91. data/spec/lib/meshtastic/deviceonly_legacy_pb_spec.rb +1 -1
  92. data/spec/lib/meshtastic/deviceonly_pb_spec.rb +1 -1
  93. data/spec/lib/meshtastic/deviceonly_spec.rb +3 -0
  94. data/spec/lib/meshtastic/localonly_pb_spec.rb +1 -1
  95. data/spec/lib/meshtastic/localonly_spec.rb +3 -0
  96. data/spec/lib/meshtastic/mesh_beacon_pb_spec.rb +1 -1
  97. data/spec/lib/meshtastic/mesh_pb_spec.rb +1 -1
  98. data/spec/lib/meshtastic/module_config_pb_spec.rb +1 -1
  99. data/spec/lib/meshtastic/module_config_spec.rb +21 -0
  100. data/spec/lib/meshtastic/mqtt_pb_spec.rb +1 -1
  101. data/spec/lib/meshtastic/paxcount_pb_spec.rb +1 -1
  102. data/spec/lib/meshtastic/paxcount_spec.rb +23 -0
  103. data/spec/lib/meshtastic/portnums_pb_spec.rb +1 -1
  104. data/spec/lib/meshtastic/portnums_spec.rb +4 -0
  105. data/spec/lib/meshtastic/position_spec.rb +33 -0
  106. data/spec/lib/meshtastic/remote_hardware_pb_spec.rb +1 -1
  107. data/spec/lib/meshtastic/remote_hardware_spec.rb +24 -0
  108. data/spec/lib/meshtastic/rtttl_pb_spec.rb +1 -1
  109. data/spec/lib/meshtastic/rtttl_spec.rb +21 -0
  110. data/spec/lib/meshtastic/storeforward_pb_spec.rb +1 -1
  111. data/spec/lib/meshtastic/storeforward_spec.rb +21 -0
  112. data/spec/lib/meshtastic/tcp_spec.rb +48 -0
  113. data/spec/lib/meshtastic/telemetry_pb_spec.rb +1 -1
  114. data/spec/lib/meshtastic/telemetry_spec.rb +21 -0
  115. data/spec/lib/meshtastic/traceroute_spec.rb +27 -0
  116. data/spec/lib/meshtastic/xmodem_pb_spec.rb +1 -1
  117. data/spec/lib/meshtastic/xmodem_spec.rb +6 -1
  118. data/spec/lib/meshtastic_spec.rb +3 -0
  119. metadata +48 -1
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'meshtastic/config_pb'
4
+
5
+ module Meshtastic
6
+ module Admin
7
+ module Config
8
+ public_class_method def self.get(opts = {})
9
+ Admin.get_config(opts.merge(config_type: opts[:config_type] || :DEVICE_CONFIG))
10
+ end
11
+
12
+ public_class_method def self.set(opts = {})
13
+ Admin.set_config(opts.merge(config: opts[:config]))
14
+ end
15
+
16
+ public_class_method def self.get_device(opts = {})
17
+ get(opts.merge(config_type: :DEVICE_CONFIG))
18
+ end
19
+
20
+ public_class_method def self.get_position(opts = {})
21
+ get(opts.merge(config_type: :POSITION_CONFIG))
22
+ end
23
+
24
+ public_class_method def self.get_power(opts = {})
25
+ get(opts.merge(config_type: :POWER_CONFIG))
26
+ end
27
+
28
+ public_class_method def self.get_network(opts = {})
29
+ get(opts.merge(config_type: :NETWORK_CONFIG))
30
+ end
31
+
32
+ public_class_method def self.get_display(opts = {})
33
+ get(opts.merge(config_type: :DISPLAY_CONFIG))
34
+ end
35
+
36
+ public_class_method def self.get_lora(opts = {})
37
+ get(opts.merge(config_type: :LORA_CONFIG))
38
+ end
39
+
40
+ public_class_method def self.get_bluetooth(opts = {})
41
+ get(opts.merge(config_type: :BLUETOOTH_CONFIG))
42
+ end
43
+
44
+ public_class_method def self.get_security(opts = {})
45
+ get(opts.merge(config_type: :SECURITY_CONFIG))
46
+ end
47
+
48
+ public_class_method def self.get_sessionkey(opts = {})
49
+ get(opts.merge(config_type: :SESSIONKEY_CONFIG))
50
+ end
51
+
52
+ public_class_method def self.get_device_ui(opts = {})
53
+ get(opts.merge(config_type: :DEVICEUI_CONFIG))
54
+ end
55
+
56
+ public_class_method def self.set_device(opts = {})
57
+ config = Meshtastic::Config.new
58
+ config.device = opts[:device]
59
+ set(opts.merge(config: config))
60
+ end
61
+
62
+ public_class_method def self.set_lora(opts = {})
63
+ config = Meshtastic::Config.new
64
+ config.lora = opts[:lora]
65
+ set(opts.merge(config: config))
66
+ end
67
+
68
+ public_class_method def self.authors
69
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n "
70
+ end
71
+
72
+ public_class_method def self.help
73
+ puts "USAGE:
74
+ # Request a radio Config section by ConfigType.
75
+ #{self}.get(
76
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
77
+ config_type: 'optional - :DEVICE_CONFIG or another ConfigType (default: :DEVICE_CONFIG)'
78
+ )
79
+
80
+ # Write a Config protobuf to the node.
81
+ #{self}.set(
82
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
83
+ config: 'required - Meshtastic::Config protobuf to write'
84
+ )
85
+
86
+ # Request DEVICE_CONFIG from the node.
87
+ #{self}.get_device(
88
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
89
+ )
90
+
91
+ # Request POSITION_CONFIG from the node.
92
+ #{self}.get_position(
93
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
94
+ )
95
+
96
+ # Request POWER_CONFIG from the node.
97
+ #{self}.get_power(
98
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
99
+ )
100
+
101
+ # Request NETWORK_CONFIG from the node.
102
+ #{self}.get_network(
103
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
104
+ )
105
+
106
+ # Request DISPLAY_CONFIG from the node.
107
+ #{self}.get_display(
108
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
109
+ )
110
+
111
+ # Request LORA_CONFIG from the node.
112
+ #{self}.get_lora(
113
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
114
+ )
115
+
116
+ # Request BLUETOOTH_CONFIG from the node.
117
+ #{self}.get_bluetooth(
118
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
119
+ )
120
+
121
+ # Request SECURITY_CONFIG from the node.
122
+ #{self}.get_security(
123
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
124
+ )
125
+
126
+ # Request SESSIONKEY_CONFIG from the node.
127
+ #{self}.get_sessionkey(
128
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
129
+ )
130
+
131
+ # Request DEVICEUI_CONFIG from the node.
132
+ #{self}.get_device_ui(
133
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect'
134
+ )
135
+
136
+ # Write DeviceConfig wrapped in a Config protobuf.
137
+ #{self}.set_device(
138
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
139
+ device: 'required - Meshtastic::Config::DeviceConfig protobuf to write'
140
+ )
141
+
142
+ # Write LoRaConfig wrapped in a Config protobuf.
143
+ #{self}.set_lora(
144
+ serial_obj: 'optional - serial handle from Meshtastic::Serial.connect',
145
+ lora: 'required - Meshtastic::Config::LoRaConfig protobuf to write'
146
+ )
147
+
148
+ # Print the AUTHOR(S) string for this module.
149
+ #{self}.authors
150
+ "
151
+ end
152
+ end
153
+ end
154
+ end
@@ -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