raygatherer 0.2.0 → 0.3.0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -0
  3. data/lib/raygatherer/api_client.rb +71 -23
  4. data/lib/raygatherer/cli.rb +27 -18
  5. data/lib/raygatherer/commands/alerts.rb +2 -8
  6. data/lib/raygatherer/commands/analysis/report.rb +2 -6
  7. data/lib/raygatherer/commands/analysis/run.rb +2 -6
  8. data/lib/raygatherer/commands/analysis/status.rb +1 -9
  9. data/lib/raygatherer/commands/base.rb +20 -1
  10. data/lib/raygatherer/commands/config/set.rb +1 -4
  11. data/lib/raygatherer/commands/config/show.rb +1 -9
  12. data/lib/raygatherer/commands/config/test_notification.rb +1 -4
  13. data/lib/raygatherer/commands/debug/display_state.rb +1 -4
  14. data/lib/raygatherer/commands/gps/set.rb +69 -0
  15. data/lib/raygatherer/commands/gps/show.rb +54 -0
  16. data/lib/raygatherer/commands/log.rb +1 -4
  17. data/lib/raygatherer/commands/recording/delete.rb +2 -5
  18. data/lib/raygatherer/commands/recording/download.rb +1 -4
  19. data/lib/raygatherer/commands/recording/list.rb +1 -9
  20. data/lib/raygatherer/commands/recording/start.rb +1 -4
  21. data/lib/raygatherer/commands/recording/stop.rb +1 -4
  22. data/lib/raygatherer/commands/stats.rb +4 -9
  23. data/lib/raygatherer/commands/time/show.rb +1 -9
  24. data/lib/raygatherer/commands/time/sync.rb +1 -4
  25. data/lib/raygatherer/commands/update/status.rb +62 -0
  26. data/lib/raygatherer/commands/wifi/scan.rb +54 -0
  27. data/lib/raygatherer/commands/wifi/show.rb +55 -0
  28. data/lib/raygatherer/formatters/config_human.rb +39 -2
  29. data/lib/raygatherer/formatters/gps_human.rb +13 -0
  30. data/lib/raygatherer/formatters/gps_json.rb +13 -0
  31. data/lib/raygatherer/formatters/update_status_human.rb +25 -0
  32. data/lib/raygatherer/formatters/update_status_json.rb +13 -0
  33. data/lib/raygatherer/formatters/wifi_scan_human.rb +17 -0
  34. data/lib/raygatherer/formatters/wifi_scan_json.rb +13 -0
  35. data/lib/raygatherer/formatters/wifi_status_human.rb +19 -0
  36. data/lib/raygatherer/formatters/wifi_status_json.rb +13 -0
  37. data/lib/raygatherer/version.rb +6 -1
  38. data/lib/raygatherer.rb +13 -0
  39. metadata +29 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9324c6a164aa329514f237d51e7942f8d2399e698fcb500de86440aef9320f0
4
- data.tar.gz: 5412fc1973d54829b42684ea37d73df8ab4cf6a85a2ecfd2b1248e9c45833817
3
+ metadata.gz: 19099779bda496591260a273c3d6db20c9203f1c046393c2ce6b7c6b90b77db5
4
+ data.tar.gz: 7105312abb9d3d27aafbd452e386f87343b8ebce08308e30e4b7330128d260aa
5
5
  SHA512:
6
- metadata.gz: e34d2c21750b31b1d0e33c8334233748b6d7aca62eb6879912226e644f8cc7f2076b815937a3f05b1853595b8aa28f25b61ae06f5e3e13d0ce8fe794ee58fe73
7
- data.tar.gz: e7e7c1bbb88a75a9b588713dfec1692fcbf2f9e0bbf126a82dc517b86d1d7279cf48ccd8a3e1fba7b210d89e2507d64777ca17b14617fba88e9bc52e27aa80f7
6
+ metadata.gz: 68524f1ebfc17554e8fed101331eca036e25217f23c8ada0922e0da5c9f257257314126bebf671a37bb7a8a50ebffca355d2df24cd7dbf8d677951414d865288
7
+ data.tar.gz: d5453cb08bbe54cc9d07fbd1a32d05bce51f9e25ba866ced69bd657fcd3d067c3fe5210aec77f6e193c7a3421867fb0cc4cdfc26eefb61a9f1b8792f4e124c39
data/README.md CHANGED
@@ -17,6 +17,9 @@ Currently implemented:
17
17
  - system stats and raw log output
18
18
  - device clock show and sync
19
19
  - config show/set/test-notification
20
+ - rayhunter update status, with a dedicated exit code for scripting
21
+ - GPS position show/set (requires the device's gps_mode config to be set to Api)
22
+ - wifi client status and network scan
20
23
  - JSON output mode for scriptable commands
21
24
  - optional basic auth and config file support
22
25
  - debug utilities (display-state)
@@ -31,6 +34,10 @@ gem install raygatherer
31
34
 
32
35
  Requires Ruby >= 3.2.
33
36
 
37
+ ## Compatibility
38
+
39
+ Tested against [rayhunter](https://github.com/EFForg/rayhunter/) v0.12.0 (last synced 2026-09-07). Older rayhunter versions may be missing some fields or endpoints raygatherer expects — commands that already have the connected daemon's version on hand (`stats`, `update status`) print a one-line warning to stderr when it's older than that, without blocking anything.
40
+
34
41
  ### From source
35
42
 
36
43
  ```bash
@@ -161,6 +168,11 @@ Main commands:
161
168
  - `config test-notification`
162
169
  - `stats`
163
170
  - `log`
171
+ - `update status`
172
+ - `gps show`
173
+ - `gps set LAT LON` (requires gps_mode=Api on the device)
174
+ - `wifi show`
175
+ - `wifi scan`
164
176
  - `debug display-state <recording|paused|warning> [--severity low|medium|high]`
165
177
 
166
178
  For command-specific help:
@@ -195,6 +207,21 @@ code=$?
195
207
  [ "$code" -ge 11 ] && echo "medium or high alert"
196
208
  ```
197
209
 
210
+ ## Update Status Exit Codes
211
+
212
+ `update status` returns a dedicated code so a cron job can react without parsing output:
213
+
214
+ - `0`: up to date (or `auto_check_updates` is disabled on the device)
215
+ - `1`: error
216
+ - `20`: update available
217
+
218
+ Example:
219
+
220
+ ```bash
221
+ raygatherer --host http://192.168.1.1 update status
222
+ [ $? -eq 20 ] && echo "rayhunter update available"
223
+ ```
224
+
198
225
  ## JSON Output
199
226
 
200
227
  Commands that support `--json` return machine-readable output to `stdout`. This is intended for `jq` and/or scripts.
@@ -79,6 +79,32 @@ module Raygatherer
79
79
  end
80
80
  end
81
81
 
82
+ def fetch_gps
83
+ get_optional("/api/gps") { |body| parse_json(body) }
84
+ end
85
+
86
+ def set_gps(latitude, longitude)
87
+ post("/api/gps", expected_code: "200",
88
+ body: ::JSON.generate({latitude: latitude, longitude: longitude}),
89
+ content_type: "application/json")
90
+ end
91
+
92
+ def fetch_wifi_status
93
+ get("/api/wifi-status") do |body|
94
+ parse_json(body)
95
+ end
96
+ end
97
+
98
+ def scan_wifi
99
+ parse_json(post("/api/wifi-scan", expected_code: "200"))
100
+ end
101
+
102
+ def fetch_update_status
103
+ get("/api/update-status") do |body|
104
+ parse_json(body)
105
+ end
106
+ end
107
+
82
108
  def set_time_offset(offset_seconds)
83
109
  post("/api/time-offset", expected_code: "200",
84
110
  body: ::JSON.generate({offset_seconds: offset_seconds}),
@@ -151,6 +177,26 @@ module Raygatherer
151
177
  raise
152
178
  end
153
179
 
180
+ def get_optional(path, not_found_code: "404")
181
+ response, body = request(:get, path, ok_code: "200", ok_status_text: "OK")
182
+
183
+ return nil if response.code == not_found_code
184
+
185
+ unless response.is_a?(Net::HTTPSuccess)
186
+ raise ApiError, server_error_message(response, body)
187
+ end
188
+
189
+ yield body
190
+ rescue ParseError => e
191
+ log_verbose "Parse failed: #{e.message}"
192
+ raise
193
+ end
194
+
195
+ # expected_code defaults to "202" (Accepted), matching the daemon's behavior for most POST
196
+ # actions (recordings, analysis, config). A handful of endpoints (gps, wifi-scan, time-offset,
197
+ # test-notification, display-state) respond "200" instead — pass expected_code: "200" for
198
+ # those. This isn't documented by rayhunter anywhere; it's observed per-endpoint behavior, so
199
+ # verify against the real device if you're adding a new one and unsure which applies.
154
200
  def post(path, expected_code: "202", body: nil, content_type: nil)
155
201
  ok_status_text = (expected_code == "202") ? "Accepted" : "OK"
156
202
  response, resp_body = request(:post, path, ok_code: expected_code,
@@ -164,16 +210,20 @@ module Raygatherer
164
210
  end
165
211
 
166
212
  def request(method, path, ok_code:, ok_status_text:, body: nil, content_type: nil)
167
- url = "#{@host}#{path}"
168
- uri = URI.parse(url)
213
+ with_connection_error_handling do
214
+ url = "#{@host}#{path}"
215
+ uri = URI.parse(url)
169
216
 
170
- log_verbose "HTTP #{method.to_s.upcase} #{url}"
171
- log_verbose "Basic Auth: user=#{@username}" if @username
217
+ log_verbose "HTTP #{method.to_s.upcase} #{url}"
218
+ log_verbose "Basic Auth: user=#{@username}" if @username
172
219
 
173
- req = build_request(method, uri, body: body, content_type: content_type)
174
- response, resp_body = execute_request(uri, req, ok_code: ok_code, ok_status_text: ok_status_text)
220
+ req = build_request(method, uri, body: body, content_type: content_type)
221
+ execute_request(uri, req, ok_code: ok_code, ok_status_text: ok_status_text)
222
+ end
223
+ end
175
224
 
176
- [response, resp_body]
225
+ def with_connection_error_handling
226
+ yield
177
227
  rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout => e
178
228
  log_verbose "Connection error: #{e.class} - #{e.message}"
179
229
  raise ConnectionError, "Failed to connect to #{@host}: #{e.message}"
@@ -220,28 +270,26 @@ module Raygatherer
220
270
  start_time = Time.now
221
271
  log_verbose "Request started at: #{start_time.utc}"
222
272
 
223
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
224
- request = Net::HTTP::Get.new(uri.request_uri)
225
- request.basic_auth(@username, @password) if @username && @password
273
+ with_connection_error_handling do
274
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
275
+ req = build_request(:get, uri)
226
276
 
227
- http.request(request) do |response|
228
- elapsed = Time.now - start_time
229
- status_text = (response.code == "200") ? "OK" : response.message.to_s
230
- log_verbose "Response received: #{response.code} #{status_text} (#{format("%.3f", elapsed)}s)"
277
+ http.request(req) do |response|
278
+ elapsed = Time.now - start_time
279
+ status_text = (response.code == "200") ? "OK" : response.message.to_s
280
+ log_verbose "Response received: #{response.code} #{status_text} (#{format("%.3f", elapsed)}s)"
231
281
 
232
- unless response.is_a?(Net::HTTPSuccess)
233
- error_body = response.read_body
234
- raise ApiError, server_error_message(response, error_body)
235
- end
282
+ unless response.is_a?(Net::HTTPSuccess)
283
+ error_body = response.read_body
284
+ raise ApiError, server_error_message(response, error_body)
285
+ end
236
286
 
237
- response.read_body do |chunk|
238
- io.write(chunk)
287
+ response.read_body do |chunk|
288
+ io.write(chunk)
289
+ end
239
290
  end
240
291
  end
241
292
  end
242
- rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout => e
243
- log_verbose "Connection error: #{e.class} - #{e.message}"
244
- raise ConnectionError, "Failed to connect to #{@host}: #{e.message}"
245
293
  end
246
294
 
247
295
  def server_error_message(response, body)
@@ -16,23 +16,28 @@ module Raygatherer
16
16
  end
17
17
 
18
18
  ROUTES = {
19
- ["log", nil] => {file: "commands/log", klass: "Commands::Log", json: false},
20
- ["stats", nil] => {file: "commands/stats", klass: "Commands::Stats", json: true},
21
- ["alerts", nil] => {file: "commands/alerts", klass: "Commands::Alerts", json: true},
22
- ["recording", "list"] => {file: "commands/recording/list", klass: "Commands::Recording::List", json: true},
23
- ["recording", "download"] => {file: "commands/recording/download", klass: "Commands::Recording::Download", json: false},
24
- ["recording", "delete"] => {file: "commands/recording/delete", klass: "Commands::Recording::Delete", json: false},
25
- ["recording", "stop"] => {file: "commands/recording/stop", klass: "Commands::Recording::Stop", json: false},
26
- ["recording", "start"] => {file: "commands/recording/start", klass: "Commands::Recording::Start", json: false},
27
- ["analysis", "status"] => {file: "commands/analysis/status", klass: "Commands::Analysis::Status", json: true},
28
- ["analysis", "run"] => {file: "commands/analysis/run", klass: "Commands::Analysis::Run", json: true},
29
- ["analysis", "report"] => {file: "commands/analysis/report", klass: "Commands::Analysis::Report", json: true},
30
- ["config", "show"] => {file: "commands/config/show", klass: "Commands::Config::Show", json: true},
31
- ["config", "set"] => {file: "commands/config/set", klass: "Commands::Config::Set", json: false},
32
- ["config", "test-notification"] => {file: "commands/config/test_notification", klass: "Commands::Config::TestNotification", json: false},
33
- ["debug", "display-state"] => {file: "commands/debug/display_state", klass: "Commands::Debug::DisplayState", json: false},
34
- ["time", "show"] => {file: "commands/time/show", klass: "Commands::Time::Show", json: true},
35
- ["time", "sync"] => {file: "commands/time/sync", klass: "Commands::Time::Sync", json: false}
19
+ ["log", nil] => {klass: "Commands::Log", json: false},
20
+ ["stats", nil] => {klass: "Commands::Stats", json: true},
21
+ ["alerts", nil] => {klass: "Commands::Alerts", json: true},
22
+ ["recording", "list"] => {klass: "Commands::Recording::List", json: true},
23
+ ["recording", "download"] => {klass: "Commands::Recording::Download", json: false},
24
+ ["recording", "delete"] => {klass: "Commands::Recording::Delete", json: false},
25
+ ["recording", "stop"] => {klass: "Commands::Recording::Stop", json: false},
26
+ ["recording", "start"] => {klass: "Commands::Recording::Start", json: false},
27
+ ["analysis", "status"] => {klass: "Commands::Analysis::Status", json: true},
28
+ ["analysis", "run"] => {klass: "Commands::Analysis::Run", json: true},
29
+ ["analysis", "report"] => {klass: "Commands::Analysis::Report", json: true},
30
+ ["config", "show"] => {klass: "Commands::Config::Show", json: true},
31
+ ["config", "set"] => {klass: "Commands::Config::Set", json: false},
32
+ ["config", "test-notification"] => {klass: "Commands::Config::TestNotification", json: false},
33
+ ["debug", "display-state"] => {klass: "Commands::Debug::DisplayState", json: false},
34
+ ["time", "show"] => {klass: "Commands::Time::Show", json: true},
35
+ ["time", "sync"] => {klass: "Commands::Time::Sync", json: false},
36
+ ["update", "status"] => {klass: "Commands::Update::Status", json: true},
37
+ ["gps", "show"] => {klass: "Commands::Gps::Show", json: true},
38
+ ["gps", "set"] => {klass: "Commands::Gps::Set", json: false},
39
+ ["wifi", "show"] => {klass: "Commands::Wifi::Show", json: true},
40
+ ["wifi", "scan"] => {klass: "Commands::Wifi::Scan", json: true}
36
41
  }.freeze
37
42
 
38
43
  def self.run(argv, stdout: $stdout, stderr: $stderr, config: Config.new)
@@ -91,7 +96,6 @@ module Raygatherer
91
96
  return Commands::EXIT_CODE_ERROR
92
97
  end
93
98
 
94
- require_relative route[:file]
95
99
  return Commands::EXIT_CODE_ERROR unless require_host!
96
100
 
97
101
  kwargs = {stdout: @stdout, stderr: @stderr, api_client: build_api_client}
@@ -189,6 +193,11 @@ module Raygatherer
189
193
  output.puts " debug display-state STATE Set device display state (recording, paused, warning)"
190
194
  output.puts " time show Show device time (system, adjusted, offset)"
191
195
  output.puts " time sync Sync device clock to this machine's time"
196
+ output.puts " update status Show rayhunter daemon update status"
197
+ output.puts " gps show Show last known GPS position"
198
+ output.puts " gps set LAT LON Submit a GPS position (requires gps_mode=Api on device)"
199
+ output.puts " wifi show Show wifi client connection status"
200
+ output.puts " wifi scan Scan for available wifi networks"
192
201
  output.puts " log Download the device log"
193
202
  output.puts " stats Show device system stats"
194
203
  output.puts ""
@@ -21,8 +21,7 @@ module Raygatherer
21
21
  EXIT_CODE_HIGH_SEVERITY = 12
22
22
 
23
23
  def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
24
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
25
- @json = json
24
+ super
26
25
  @latest = false
27
26
  @after = nil
28
27
  @recording = nil
@@ -41,11 +40,9 @@ module Raygatherer
41
40
  alerts = filter_after(alerts) if @after
42
41
  alerts = filter_latest(alerts, data[:rows]) if @latest
43
42
 
44
- # Select formatter based on --json flag
45
43
  formatter = @json ? Formatters::AlertsJSON.new : Formatters::AlertsHuman.new
46
44
  @stdout.puts formatter.format(alerts)
47
45
 
48
- # Return severity-based exit code
49
46
  severity_exit_code(alerts)
50
47
  end
51
48
  end
@@ -70,10 +67,7 @@ module Raygatherer
70
67
  @after = parse_timestamp(ts)
71
68
  end
72
69
 
73
- opts.on("-h", "--help", "Show this help message") do
74
- show_help
75
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
76
- end
70
+ add_help_option(opts)
77
71
  end.parse!(@argv)
78
72
  end
79
73
 
@@ -10,8 +10,7 @@ module Raygatherer
10
10
  module Analysis
11
11
  class Report < Base
12
12
  def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
13
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
14
- @json = json
13
+ super
15
14
  @live = false
16
15
  end
17
16
 
@@ -52,10 +51,7 @@ module Raygatherer
52
51
  @live = true
53
52
  end
54
53
 
55
- opts.on("-h", "--help", "Show this help message") do
56
- show_help
57
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
58
- end
54
+ add_help_option(opts)
59
55
  end.parse!(@argv)
60
56
  end
61
57
 
@@ -10,8 +10,7 @@ module Raygatherer
10
10
  module Analysis
11
11
  class Run < Base
12
12
  def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
13
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
14
- @json = json
13
+ super
15
14
  @all = false
16
15
  end
17
16
 
@@ -59,10 +58,7 @@ module Raygatherer
59
58
  @all = true
60
59
  end
61
60
 
62
- opts.on("-h", "--help", "Show this help message") do
63
- show_help
64
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
65
- end
61
+ add_help_option(opts)
66
62
  end.parse!(@argv)
67
63
  end
68
64
 
@@ -9,11 +9,6 @@ module Raygatherer
9
9
  module Commands
10
10
  module Analysis
11
11
  class Status < Base
12
- def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
13
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
14
- @json = json
15
- end
16
-
17
12
  def run
18
13
  with_error_handling do
19
14
  parse_options
@@ -35,10 +30,7 @@ module Raygatherer
35
30
  opts.separator ""
36
31
  opts.separator "Options:"
37
32
 
38
- opts.on("-h", "--help", "Show this help message") do
39
- show_help
40
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
41
- end
33
+ add_help_option(opts)
42
34
  end.parse!(@argv)
43
35
  end
44
36
 
@@ -10,15 +10,23 @@ module Raygatherer
10
10
  new(argv, **kwargs).run
11
11
  end
12
12
 
13
- def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, **_kwargs)
13
+ def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false, **_kwargs)
14
14
  @argv = argv
15
15
  @stdout = stdout
16
16
  @stderr = stderr
17
17
  @api_client = api_client
18
+ @json = json
18
19
  end
19
20
 
20
21
  private
21
22
 
23
+ def add_help_option(opts)
24
+ opts.on("-h", "--help", "Show this help message") do
25
+ show_help
26
+ raise CLI::EarlyExit, EXIT_CODE_SUCCESS
27
+ end
28
+ end
29
+
22
30
  def print_global_options(output, json: false)
23
31
  output.puts "Global options (see 'raygatherer --help'):"
24
32
  if json
@@ -36,6 +44,17 @@ module Raygatherer
36
44
  @stderr.puts "Error: #{e.message}"
37
45
  EXIT_CODE_ERROR
38
46
  end
47
+
48
+ def warn_if_rayhunter_outdated(rayhunter_version)
49
+ actual = Gem::Version.new(rayhunter_version)
50
+ tested = Gem::Version.new(Raygatherer::TESTED_RAYHUNTER_VERSION)
51
+ return unless actual < tested
52
+
53
+ @stderr.puts "Warning: connected rayhunter is v#{rayhunter_version}; raygatherer was last " \
54
+ "tested against v#{Raygatherer::TESTED_RAYHUNTER_VERSION} — some fields or commands may not be supported."
55
+ rescue ArgumentError
56
+ nil
57
+ end
39
58
  end
40
59
  end
41
60
  end
@@ -48,10 +48,7 @@ module Raygatherer
48
48
  opts.separator ""
49
49
  opts.separator "Options:"
50
50
 
51
- opts.on("-h", "--help", "Show this help message") do
52
- show_help
53
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
54
- end
51
+ add_help_option(opts)
55
52
  end.parse!(@argv)
56
53
  end
57
54
 
@@ -9,11 +9,6 @@ module Raygatherer
9
9
  module Commands
10
10
  module Config
11
11
  class Show < Base
12
- def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
13
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
14
- @json = json
15
- end
16
-
17
12
  def run
18
13
  with_error_handling do
19
14
  parse_options
@@ -35,10 +30,7 @@ module Raygatherer
35
30
  opts.separator ""
36
31
  opts.separator "Options:"
37
32
 
38
- opts.on("-h", "--help", "Show this help message") do
39
- show_help
40
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
41
- end
33
+ add_help_option(opts)
42
34
  end.parse!(@argv)
43
35
  end
44
36
 
@@ -26,10 +26,7 @@ module Raygatherer
26
26
  opts.separator ""
27
27
  opts.separator "Options:"
28
28
 
29
- opts.on("-h", "--help", "Show this help message") do
30
- show_help
31
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
32
- end
29
+ add_help_option(opts)
33
30
  end.parse!(@argv)
34
31
  end
35
32
 
@@ -64,10 +64,7 @@ module Raygatherer
64
64
  @severity = s
65
65
  end
66
66
 
67
- opts.on("-h", "--help", "Show this help message") do
68
- show_help
69
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
70
- end
67
+ add_help_option(opts)
71
68
  end.parse!(@argv)
72
69
  end
73
70
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base"
4
+
5
+ module Raygatherer
6
+ module Commands
7
+ module Gps
8
+ class Set < Base
9
+ def run
10
+ with_error_handling do
11
+ parse_options
12
+
13
+ unless @argv.length == 2
14
+ @stderr.puts "Error: latitude and longitude are required (usage: gps set LAT LON)"
15
+ return EXIT_CODE_ERROR
16
+ end
17
+
18
+ lat_str, lon_str = @argv
19
+
20
+ latitude = parse_coordinate(lat_str, "latitude")
21
+ return EXIT_CODE_ERROR unless latitude
22
+
23
+ longitude = parse_coordinate(lon_str, "longitude")
24
+ return EXIT_CODE_ERROR unless longitude
25
+
26
+ @api_client.set_gps(latitude, longitude)
27
+ @stdout.puts "GPS position set: #{latitude}, #{longitude}"
28
+
29
+ EXIT_CODE_SUCCESS
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def parse_coordinate(str, label)
36
+ Float(str)
37
+ rescue ArgumentError
38
+ @stderr.puts "Error: #{label} must be a number (got #{str.inspect})"
39
+ nil
40
+ end
41
+
42
+ # Deliberately not using OptionParser#parse! here: a negative longitude like "-0.1"
43
+ # looks like an unrecognized flag to it and raises OptionParser::InvalidOption. This
44
+ # command's only "option" is help, so just scan for it directly instead.
45
+ def parse_options
46
+ return unless @argv.include?("-h") || @argv.include?("--help")
47
+
48
+ show_help
49
+ raise CLI::EarlyExit, EXIT_CODE_SUCCESS
50
+ end
51
+
52
+ def show_help(output = @stdout)
53
+ output.puts "Usage: raygatherer [global options] gps set [options] LAT LON"
54
+ output.puts ""
55
+ output.puts "Submit a GPS position to the device, to be embedded into the current recording."
56
+ output.puts "Requires the device's gps_mode config to be set to 'Api' (see 'config show'/'config set')."
57
+ output.puts ""
58
+ output.puts "Options:"
59
+ output.puts " -h, --help Show this help message"
60
+ output.puts ""
61
+ print_global_options(output)
62
+ output.puts ""
63
+ output.puts "Examples:"
64
+ output.puts " raygatherer --host http://192.168.1.100:8080 gps set 51.5074 -0.1278"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../base"
5
+ require_relative "../../formatters/gps_json"
6
+ require_relative "../../formatters/gps_human"
7
+
8
+ module Raygatherer
9
+ module Commands
10
+ module Gps
11
+ class Show < Base
12
+ def run
13
+ with_error_handling do
14
+ parse_options
15
+
16
+ gps = @api_client.fetch_gps
17
+
18
+ formatter = @json ? Formatters::GpsJSON.new : Formatters::GpsHuman.new
19
+ @stdout.puts formatter.format(gps)
20
+
21
+ EXIT_CODE_SUCCESS
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def parse_options
28
+ OptionParser.new do |opts|
29
+ opts.banner = "Usage: raygatherer gps show [options]"
30
+ opts.separator ""
31
+ opts.separator "Options:"
32
+
33
+ add_help_option(opts)
34
+ end.parse!(@argv)
35
+ end
36
+
37
+ def show_help(output = @stdout)
38
+ output.puts "Usage: raygatherer [global options] gps show [options]"
39
+ output.puts ""
40
+ output.puts "Show the last GPS position submitted to the device (see 'gps set')."
41
+ output.puts ""
42
+ output.puts "Options:"
43
+ output.puts " -h, --help Show this help message"
44
+ output.puts ""
45
+ print_global_options(output, json: true)
46
+ output.puts ""
47
+ output.puts "Examples:"
48
+ output.puts " raygatherer --host http://192.168.1.100:8080 gps show"
49
+ output.puts " raygatherer --host http://192.168.1.100:8080 --json gps show"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -18,10 +18,7 @@ module Raygatherer
18
18
 
19
19
  def parse_options
20
20
  OptionParser.new do |opts|
21
- opts.on("-h", "--help", "Show this help message") do
22
- show_help
23
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
24
- end
21
+ add_help_option(opts)
25
22
  end.parse!(@argv)
26
23
  end
27
24
 
@@ -20,7 +20,7 @@ module Raygatherer
20
20
 
21
21
  if @all && !@argv.empty?
22
22
  @stderr.puts "Error: cannot specify both a recording name and --all"
23
- next EXIT_CODE_ERROR
23
+ return EXIT_CODE_ERROR
24
24
  end
25
25
 
26
26
  if @all
@@ -72,10 +72,7 @@ module Raygatherer
72
72
  @force = true
73
73
  end
74
74
 
75
- opts.on("-h", "--help", "Show this help message") do
76
- show_help
77
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
78
- end
75
+ add_help_option(opts)
79
76
  end.parse!(@argv)
80
77
  end
81
78
 
@@ -87,10 +87,7 @@ module Raygatherer
87
87
  @save_as = path
88
88
  end
89
89
 
90
- opts.on("-h", "--help", "Show this help message") do
91
- show_help
92
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
93
- end
90
+ add_help_option(opts)
94
91
  end.parse!(@argv)
95
92
  end
96
93
 
@@ -9,11 +9,6 @@ module Raygatherer
9
9
  module Commands
10
10
  module Recording
11
11
  class List < Base
12
- def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
13
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
14
- @json = json
15
- end
16
-
17
12
  def run
18
13
  with_error_handling do
19
14
  parse_options
@@ -35,10 +30,7 @@ module Raygatherer
35
30
  opts.separator ""
36
31
  opts.separator "Options:"
37
32
 
38
- opts.on("-h", "--help", "Show this help message") do
39
- show_help
40
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
41
- end
33
+ add_help_option(opts)
42
34
  end.parse!(@argv)
43
35
  end
44
36
 
@@ -30,10 +30,7 @@ module Raygatherer
30
30
  opts.separator ""
31
31
  opts.separator "Options:"
32
32
 
33
- opts.on("-h", "--help", "Show this help message") do
34
- show_help
35
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
36
- end
33
+ add_help_option(opts)
37
34
  end.parse!(@argv)
38
35
  end
39
36
 
@@ -30,10 +30,7 @@ module Raygatherer
30
30
  opts.separator ""
31
31
  opts.separator "Options:"
32
32
 
33
- opts.on("-h", "--help", "Show this help message") do
34
- show_help
35
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
36
- end
33
+ add_help_option(opts)
37
34
  end.parse!(@argv)
38
35
  end
39
36
 
@@ -8,17 +8,15 @@ require_relative "../formatters/stats_human"
8
8
  module Raygatherer
9
9
  module Commands
10
10
  class Stats < Base
11
- def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
12
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
13
- @json = json
14
- end
15
-
16
11
  def run
17
12
  with_error_handling do
18
13
  parse_options
19
14
 
20
15
  stats = @api_client.fetch_system_stats
21
16
 
17
+ rayhunter_version = stats.dig("runtime_metadata", "rayhunter_version")
18
+ warn_if_rayhunter_outdated(rayhunter_version) if rayhunter_version
19
+
22
20
  formatter = @json ? Formatters::StatsJSON.new : Formatters::StatsHuman.new
23
21
  @stdout.puts formatter.format(stats)
24
22
 
@@ -34,10 +32,7 @@ module Raygatherer
34
32
  opts.separator ""
35
33
  opts.separator "Options:"
36
34
 
37
- opts.on("-h", "--help", "Show this help message") do
38
- show_help
39
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
40
- end
35
+ add_help_option(opts)
41
36
  end.parse!(@argv)
42
37
  end
43
38
 
@@ -9,11 +9,6 @@ module Raygatherer
9
9
  module Commands
10
10
  module Time
11
11
  class Show < Base
12
- def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
13
- super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
14
- @json = json
15
- end
16
-
17
12
  def run
18
13
  with_error_handling do
19
14
  parse_options
@@ -35,10 +30,7 @@ module Raygatherer
35
30
  opts.separator ""
36
31
  opts.separator "Options:"
37
32
 
38
- opts.on("-h", "--help", "Show this help message") do
39
- show_help
40
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
41
- end
33
+ add_help_option(opts)
42
34
  end.parse!(@argv)
43
35
  end
44
36
 
@@ -30,10 +30,7 @@ module Raygatherer
30
30
  opts.separator ""
31
31
  opts.separator "Options:"
32
32
 
33
- opts.on("-h", "--help", "Show this help message") do
34
- show_help
35
- raise CLI::EarlyExit, EXIT_CODE_SUCCESS
36
- end
33
+ add_help_option(opts)
37
34
  end.parse!(@argv)
38
35
  end
39
36
 
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../base"
5
+ require_relative "../../formatters/update_status_json"
6
+ require_relative "../../formatters/update_status_human"
7
+
8
+ module Raygatherer
9
+ module Commands
10
+ module Update
11
+ class Status < Base
12
+ EXIT_CODE_UPDATE_AVAILABLE = 20
13
+
14
+ def run
15
+ with_error_handling do
16
+ parse_options
17
+
18
+ status = @api_client.fetch_update_status
19
+
20
+ warn_if_rayhunter_outdated(status["current_version"]) if status["current_version"]
21
+
22
+ formatter = @json ? Formatters::UpdateStatusJSON.new : Formatters::UpdateStatusHuman.new
23
+ @stdout.puts formatter.format(status)
24
+
25
+ status["update_available"] ? EXIT_CODE_UPDATE_AVAILABLE : EXIT_CODE_SUCCESS
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def parse_options
32
+ OptionParser.new do |opts|
33
+ opts.banner = "Usage: raygatherer update status [options]"
34
+ opts.separator ""
35
+ opts.separator "Options:"
36
+
37
+ add_help_option(opts)
38
+ end.parse!(@argv)
39
+ end
40
+
41
+ def show_help(output = @stdout)
42
+ output.puts "Usage: raygatherer [global options] update status [options]"
43
+ output.puts ""
44
+ output.puts "Options:"
45
+ output.puts " -h, --help Show this help message"
46
+ output.puts ""
47
+ print_global_options(output, json: true)
48
+ output.puts ""
49
+ output.puts "Exit Codes:"
50
+ output.puts " #{EXIT_CODE_SUCCESS} Up to date (or auto_check_updates is disabled on the device)"
51
+ output.puts " #{EXIT_CODE_ERROR} Error (connection, parse, missing --host, etc.)"
52
+ output.puts " #{EXIT_CODE_UPDATE_AVAILABLE} Update available"
53
+ output.puts ""
54
+ output.puts "Examples:"
55
+ output.puts " raygatherer --host http://192.168.1.100:8080 update status"
56
+ output.puts " raygatherer --host http://192.168.1.100:8080 --json update status"
57
+ output.puts " raygatherer --host http://rayhunter update status; [ $? -eq #{EXIT_CODE_UPDATE_AVAILABLE} ] && telegram-send 'Rayhunter update available!'"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../base"
5
+ require_relative "../../formatters/wifi_scan_json"
6
+ require_relative "../../formatters/wifi_scan_human"
7
+
8
+ module Raygatherer
9
+ module Commands
10
+ module Wifi
11
+ class Scan < Base
12
+ def run
13
+ with_error_handling do
14
+ parse_options
15
+
16
+ networks = @api_client.scan_wifi
17
+
18
+ formatter = @json ? Formatters::WifiScanJSON.new : Formatters::WifiScanHuman.new
19
+ @stdout.puts formatter.format(networks)
20
+
21
+ EXIT_CODE_SUCCESS
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def parse_options
28
+ OptionParser.new do |opts|
29
+ opts.banner = "Usage: raygatherer wifi scan [options]"
30
+ opts.separator ""
31
+ opts.separator "Options:"
32
+
33
+ add_help_option(opts)
34
+ end.parse!(@argv)
35
+ end
36
+
37
+ def show_help(output = @stdout)
38
+ output.puts "Usage: raygatherer [global options] wifi scan [options]"
39
+ output.puts ""
40
+ output.puts "Scan for nearby wifi networks the device could connect to as a client."
41
+ output.puts ""
42
+ output.puts "Options:"
43
+ output.puts " -h, --help Show this help message"
44
+ output.puts ""
45
+ print_global_options(output, json: true)
46
+ output.puts ""
47
+ output.puts "Examples:"
48
+ output.puts " raygatherer --host http://192.168.1.100:8080 wifi scan"
49
+ output.puts " raygatherer --host http://192.168.1.100:8080 --json wifi scan"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../base"
5
+ require_relative "../../formatters/wifi_status_json"
6
+ require_relative "../../formatters/wifi_status_human"
7
+
8
+ module Raygatherer
9
+ module Commands
10
+ module Wifi
11
+ class Show < Base
12
+ def run
13
+ with_error_handling do
14
+ parse_options
15
+
16
+ status = @api_client.fetch_wifi_status
17
+
18
+ formatter = @json ? Formatters::WifiStatusJSON.new : Formatters::WifiStatusHuman.new
19
+ @stdout.puts formatter.format(status)
20
+
21
+ EXIT_CODE_SUCCESS
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def parse_options
28
+ OptionParser.new do |opts|
29
+ opts.banner = "Usage: raygatherer wifi show [options]"
30
+ opts.separator ""
31
+ opts.separator "Options:"
32
+
33
+ add_help_option(opts)
34
+ end.parse!(@argv)
35
+ end
36
+
37
+ def show_help(output = @stdout)
38
+ output.puts "Usage: raygatherer [global options] wifi show [options]"
39
+ output.puts ""
40
+ output.puts "Show the device's wifi client connection status (the daemon's own outbound"
41
+ output.puts "connection to an upstream network, separate from the AP it hosts)."
42
+ output.puts ""
43
+ output.puts "Options:"
44
+ output.puts " -h, --help Show this help message"
45
+ output.puts ""
46
+ print_global_options(output, json: true)
47
+ output.puts ""
48
+ output.puts "Examples:"
49
+ output.puts " raygatherer --host http://192.168.1.100:8080 wifi show"
50
+ output.puts " raygatherer --host http://192.168.1.100:8080 --json wifi show"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -7,12 +7,49 @@ module Raygatherer
7
7
  lines = []
8
8
 
9
9
  lines << "Port: #{config["port"]}"
10
- lines << "Readonly port: #{config["readonly_port"]}"
11
10
  lines << "QMDL store path: #{config["qmdl_store_path"]}"
11
+ lines << "Device: #{config["device"]}" if config.key?("device")
12
+ lines << "Debug mode: #{config["debug_mode"]}" if config.key?("debug_mode")
13
+ lines << "UI level: #{config["ui_level"]}" if config.key?("ui_level")
14
+ lines << "Colorblind mode: #{config["colorblind_mode"]}" if config.key?("colorblind_mode")
15
+ lines << "Key input mode: #{config["key_input_mode"]}" if config.key?("key_input_mode")
12
16
 
13
- notification_url = config["notification_url"]
17
+ notification_url = config["ntfy_url"] || config["notification_url"]
14
18
  lines << "Notification URL: #{notification_url || "(not set)"}"
15
19
 
20
+ if config.key?("enabled_notifications")
21
+ lines << "Notifications enabled: #{config["enabled_notifications"].join(", ")}"
22
+ end
23
+
24
+ if config.key?("min_space_to_start_recording_mb")
25
+ lines << "Min space to start recording: #{config["min_space_to_start_recording_mb"]} MB"
26
+ end
27
+ if config.key?("min_space_to_continue_recording_mb")
28
+ lines << "Min space to continue recording: #{config["min_space_to_continue_recording_mb"]} MB"
29
+ end
30
+
31
+ lines << "Auto check updates: #{config["auto_check_updates"]}" if config.key?("auto_check_updates")
32
+ lines << "Clock sync mode: #{config["clock_sync_mode"]}" if config.key?("clock_sync_mode")
33
+ lines << "GPS mode: #{config["gps_mode"]}" if config.key?("gps_mode")
34
+ lines << "GPS fixed latitude: #{config["gps_fixed_latitude"]}" if config["gps_fixed_latitude"]
35
+ lines << "GPS fixed longitude: #{config["gps_fixed_longitude"]}" if config["gps_fixed_longitude"]
36
+ lines << "WiFi enabled: #{config["wifi_enabled"]}" if config.key?("wifi_enabled")
37
+ lines << "WiFi SSID: #{config["wifi_ssid"]}" if config["wifi_ssid"]
38
+ lines << "WiFi security: #{config["wifi_security"]}" if config["wifi_security"]
39
+ lines << "DNS servers: #{config["dns_servers"].join(", ")}" if config["dns_servers"]&.any?
40
+
41
+ webdav = config["webdav"]
42
+ if webdav && !webdav["url"].to_s.empty?
43
+ lines << ""
44
+ lines << "WebDAV:"
45
+ lines << " URL: #{webdav["url"]}"
46
+ lines << " Username: #{webdav["username"]}" if webdav["username"]
47
+ lines << " Upload timeout: #{webdav["upload_timeout_secs"]}s" if webdav.key?("upload_timeout_secs")
48
+ lines << " Poll interval: #{webdav["poll_interval_secs"]}s" if webdav.key?("poll_interval_secs")
49
+ lines << " Min age: #{webdav["min_age_secs"]}s" if webdav.key?("min_age_secs")
50
+ lines << " Delete on upload: #{webdav["delete_on_upload"]}" if webdav.key?("delete_on_upload")
51
+ end
52
+
16
53
  analyzers = config["analyzers"]
17
54
  if analyzers
18
55
  lines << ""
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raygatherer
4
+ module Formatters
5
+ class GpsHuman
6
+ def format(gps)
7
+ return "No GPS position recorded yet (nothing has been submitted to /api/gps this session)." if gps.nil?
8
+
9
+ "Latitude: #{gps["latitude"]}\nLongitude: #{gps["longitude"]}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Raygatherer
6
+ module Formatters
7
+ class GpsJSON
8
+ def format(gps)
9
+ ::JSON.generate(gps)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raygatherer
4
+ module Formatters
5
+ class UpdateStatusHuman
6
+ def format(status)
7
+ lines = []
8
+
9
+ lines << "Current version: #{status["current_version"]}"
10
+ lines << "Update available: #{status["update_available"] ? "yes" : "no"}"
11
+ lines << "Latest version: #{status["latest_version"]}" if status["latest_version"]
12
+ lines << "Release notes: #{status["latest_release_url"]}" if status["latest_release_url"]
13
+ lines << "Last checked: #{status["last_checked"]}" if status["last_checked"]
14
+ lines << "Last error: #{status["last_error"]}" if status["last_error"]
15
+
16
+ if status["latest_version"].nil? && status["last_checked"].nil?
17
+ lines << ""
18
+ lines << "No update check has run yet — check that auto_check_updates is enabled in rayhunter's config."
19
+ end
20
+
21
+ lines.join("\n")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Raygatherer
6
+ module Formatters
7
+ class UpdateStatusJSON
8
+ def format(status)
9
+ ::JSON.generate(status)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raygatherer
4
+ module Formatters
5
+ class WifiScanHuman
6
+ def format(networks)
7
+ return "No networks found" if networks.empty?
8
+
9
+ lines = ["Networks: #{networks.length}", ""]
10
+ networks.each do |network|
11
+ lines << "#{network["ssid"]} (#{network["signal_dbm"]} dBm, #{network["security"]})"
12
+ end
13
+ lines.join("\n")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Raygatherer
6
+ module Formatters
7
+ class WifiScanJSON
8
+ def format(networks)
9
+ ::JSON.generate(networks)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raygatherer
4
+ module Formatters
5
+ class WifiStatusHuman
6
+ def format(status)
7
+ lines = []
8
+
9
+ lines << "State: #{status["state"]}"
10
+ lines << "SSID: #{status["ssid"]}" if status["ssid"]
11
+ lines << "IP: #{status["ip"]}" if status["ip"]
12
+ lines << "TX packets: #{status["txPackets"]}" if status["txPackets"]
13
+ lines << "Error: #{status["error"]}" if status["error"]
14
+
15
+ lines.join("\n")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Raygatherer
6
+ module Formatters
7
+ class WifiStatusJSON
8
+ def format(status)
9
+ ::JSON.generate(status)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raygatherer
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
+
6
+ # The rayhunter daemon version this release was last verified against. Used only to warn
7
+ # when talking to an older daemon that may lack fields/endpoints raygatherer expects;
8
+ # never enforced.
9
+ TESTED_RAYHUNTER_VERSION = "0.12.0"
5
10
  end
data/lib/raygatherer.rb CHANGED
@@ -18,6 +18,14 @@ require_relative "raygatherer/formatters/config_json"
18
18
  require_relative "raygatherer/formatters/config_human"
19
19
  require_relative "raygatherer/formatters/time_json"
20
20
  require_relative "raygatherer/formatters/time_human"
21
+ require_relative "raygatherer/formatters/update_status_json"
22
+ require_relative "raygatherer/formatters/update_status_human"
23
+ require_relative "raygatherer/formatters/gps_json"
24
+ require_relative "raygatherer/formatters/gps_human"
25
+ require_relative "raygatherer/formatters/wifi_status_json"
26
+ require_relative "raygatherer/formatters/wifi_status_human"
27
+ require_relative "raygatherer/formatters/wifi_scan_json"
28
+ require_relative "raygatherer/formatters/wifi_scan_human"
21
29
  require_relative "raygatherer/spinner"
22
30
  require_relative "raygatherer/commands/base"
23
31
  require_relative "raygatherer/commands/alerts"
@@ -37,6 +45,11 @@ require_relative "raygatherer/commands/config/test_notification"
37
45
  require_relative "raygatherer/commands/debug/display_state"
38
46
  require_relative "raygatherer/commands/time/show"
39
47
  require_relative "raygatherer/commands/time/sync"
48
+ require_relative "raygatherer/commands/update/status"
49
+ require_relative "raygatherer/commands/gps/show"
50
+ require_relative "raygatherer/commands/gps/set"
51
+ require_relative "raygatherer/commands/wifi/show"
52
+ require_relative "raygatherer/commands/wifi/scan"
40
53
 
41
54
  module Raygatherer
42
55
  class Error < StandardError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: raygatherer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Stallard
@@ -37,6 +37,20 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: bundler-audit
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.9'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.9'
40
54
  description: 'Ruby CLI tool for interacting with Rayhunter, a cell tower analysis
41
55
  device for detecting IMSI catchers and other cellular network anomalies. '
42
56
  email:
@@ -65,6 +79,8 @@ files:
65
79
  - lib/raygatherer/commands/config/show.rb
66
80
  - lib/raygatherer/commands/config/test_notification.rb
67
81
  - lib/raygatherer/commands/debug/display_state.rb
82
+ - lib/raygatherer/commands/gps/set.rb
83
+ - lib/raygatherer/commands/gps/show.rb
68
84
  - lib/raygatherer/commands/log.rb
69
85
  - lib/raygatherer/commands/recording/delete.rb
70
86
  - lib/raygatherer/commands/recording/download.rb
@@ -74,6 +90,9 @@ files:
74
90
  - lib/raygatherer/commands/stats.rb
75
91
  - lib/raygatherer/commands/time/show.rb
76
92
  - lib/raygatherer/commands/time/sync.rb
93
+ - lib/raygatherer/commands/update/status.rb
94
+ - lib/raygatherer/commands/wifi/scan.rb
95
+ - lib/raygatherer/commands/wifi/show.rb
77
96
  - lib/raygatherer/config.rb
78
97
  - lib/raygatherer/format_helpers.rb
79
98
  - lib/raygatherer/formatters/alerts_human.rb
@@ -84,12 +103,20 @@ files:
84
103
  - lib/raygatherer/formatters/analysis_status_json.rb
85
104
  - lib/raygatherer/formatters/config_human.rb
86
105
  - lib/raygatherer/formatters/config_json.rb
106
+ - lib/raygatherer/formatters/gps_human.rb
107
+ - lib/raygatherer/formatters/gps_json.rb
87
108
  - lib/raygatherer/formatters/recording_list_human.rb
88
109
  - lib/raygatherer/formatters/recording_list_json.rb
89
110
  - lib/raygatherer/formatters/stats_human.rb
90
111
  - lib/raygatherer/formatters/stats_json.rb
91
112
  - lib/raygatherer/formatters/time_human.rb
92
113
  - lib/raygatherer/formatters/time_json.rb
114
+ - lib/raygatherer/formatters/update_status_human.rb
115
+ - lib/raygatherer/formatters/update_status_json.rb
116
+ - lib/raygatherer/formatters/wifi_scan_human.rb
117
+ - lib/raygatherer/formatters/wifi_scan_json.rb
118
+ - lib/raygatherer/formatters/wifi_status_human.rb
119
+ - lib/raygatherer/formatters/wifi_status_json.rb
93
120
  - lib/raygatherer/spinner.rb
94
121
  - lib/raygatherer/version.rb
95
122
  homepage: https://github.com/mjstallard/raygatherer
@@ -113,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
140
  - !ruby/object:Gem::Version
114
141
  version: '0'
115
142
  requirements: []
116
- rubygems_version: 3.6.7
143
+ rubygems_version: 4.0.9
117
144
  specification_version: 4
118
145
  summary: CLI for fetching and displaying alerts from Rayhunter
119
146
  test_files: []