aprsd 3.3.3__py2.py3-none-any.whl → 3.4.0__py2.py3-none-any.whl

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 (72) hide show
  1. aprsd/client.py +133 -20
  2. aprsd/clients/aprsis.py +6 -3
  3. aprsd/clients/fake.py +1 -1
  4. aprsd/clients/kiss.py +1 -1
  5. aprsd/cmds/completion.py +13 -27
  6. aprsd/cmds/fetch_stats.py +53 -57
  7. aprsd/cmds/healthcheck.py +32 -30
  8. aprsd/cmds/list_plugins.py +2 -2
  9. aprsd/cmds/listen.py +33 -17
  10. aprsd/cmds/send_message.py +2 -2
  11. aprsd/cmds/server.py +26 -9
  12. aprsd/cmds/webchat.py +34 -29
  13. aprsd/conf/common.py +46 -31
  14. aprsd/log/log.py +28 -6
  15. aprsd/main.py +20 -18
  16. aprsd/packets/__init__.py +3 -2
  17. aprsd/packets/collector.py +56 -0
  18. aprsd/packets/core.py +456 -321
  19. aprsd/packets/log.py +143 -0
  20. aprsd/packets/packet_list.py +83 -66
  21. aprsd/packets/seen_list.py +30 -19
  22. aprsd/packets/tracker.py +60 -62
  23. aprsd/packets/watch_list.py +64 -38
  24. aprsd/plugin.py +41 -16
  25. aprsd/plugins/email.py +35 -7
  26. aprsd/plugins/time.py +3 -2
  27. aprsd/plugins/version.py +4 -5
  28. aprsd/plugins/weather.py +0 -1
  29. aprsd/stats/__init__.py +20 -0
  30. aprsd/stats/app.py +46 -0
  31. aprsd/stats/collector.py +38 -0
  32. aprsd/threads/__init__.py +3 -2
  33. aprsd/threads/aprsd.py +67 -36
  34. aprsd/threads/keep_alive.py +55 -49
  35. aprsd/threads/log_monitor.py +46 -0
  36. aprsd/threads/rx.py +43 -24
  37. aprsd/threads/stats.py +44 -0
  38. aprsd/threads/tx.py +36 -17
  39. aprsd/utils/__init__.py +12 -0
  40. aprsd/utils/counter.py +6 -3
  41. aprsd/utils/json.py +20 -0
  42. aprsd/utils/objectstore.py +22 -17
  43. aprsd/web/admin/static/css/prism.css +4 -189
  44. aprsd/web/admin/static/js/charts.js +9 -7
  45. aprsd/web/admin/static/js/echarts.js +71 -9
  46. aprsd/web/admin/static/js/main.js +47 -6
  47. aprsd/web/admin/static/js/prism.js +11 -2246
  48. aprsd/web/admin/templates/index.html +18 -7
  49. aprsd/web/chat/static/js/gps.js +3 -1
  50. aprsd/web/chat/static/js/main.js +4 -3
  51. aprsd/web/chat/static/js/send-message.js +5 -2
  52. aprsd/web/chat/templates/index.html +1 -0
  53. aprsd/wsgi.py +62 -127
  54. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/METADATA +14 -16
  55. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/RECORD +60 -65
  56. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/WHEEL +1 -1
  57. aprsd-3.4.0.dist-info/pbr.json +1 -0
  58. aprsd/plugins/query.py +0 -81
  59. aprsd/rpc/__init__.py +0 -14
  60. aprsd/rpc/client.py +0 -165
  61. aprsd/rpc/server.py +0 -99
  62. aprsd/stats.py +0 -266
  63. aprsd/threads/store.py +0 -30
  64. aprsd/utils/converters.py +0 -15
  65. aprsd/web/admin/static/json-viewer/jquery.json-viewer.css +0 -57
  66. aprsd/web/admin/static/json-viewer/jquery.json-viewer.js +0 -158
  67. aprsd/web/chat/static/json-viewer/jquery.json-viewer.css +0 -57
  68. aprsd/web/chat/static/json-viewer/jquery.json-viewer.js +0 -158
  69. aprsd-3.3.3.dist-info/pbr.json +0 -1
  70. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/LICENSE +0 -0
  71. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/entry_points.txt +0 -0
  72. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/top_level.txt +0 -0
@@ -30,7 +30,6 @@
30
30
  var color = Chart.helpers.color;
31
31
 
32
32
  $(document).ready(function() {
33
- console.log(initial_stats);
34
33
  start_update();
35
34
  start_charts();
36
35
  init_messages();
@@ -82,6 +81,7 @@
82
81
  <div class="item" data-tab="seen-tab">Seen List</div>
83
82
  <div class="item" data-tab="watch-tab">Watch List</div>
84
83
  <div class="item" data-tab="plugin-tab">Plugins</div>
84
+ <div class="item" data-tab="threads-tab">Threads</div>
85
85
  <div class="item" data-tab="config-tab">Config</div>
86
86
  <div class="item" data-tab="log-tab">LogFile</div>
87
87
  <!-- <div class="item" data-tab="oslo-tab">OSLO CONFIG</div> //-->
@@ -99,21 +99,25 @@
99
99
  </div>
100
100
  <div class="row">
101
101
  <div class="column">
102
- <div class="ui segment" style="height: 300px" id="packetTypesChart"></div>
102
+ <div class="ui segment" style="height: 300px" id="messagesChart"></div>
103
+ </div>
104
+ <div class="column">
105
+ <div class="ui segment" style="height: 300px" id="acksChart"></div>
103
106
  </div>
104
107
  </div>
105
108
  <div class="row">
106
109
  <div class="column">
107
- <div class="ui segment" style="height: 300px" id="messagesChart"></div>
110
+ <div class="ui segment" style="height: 300px" id="packetTypesChart"></div>
108
111
  </div>
112
+ </div>
113
+ <div class="row">
109
114
  <div class="column">
110
- <div class="ui segment" style="height: 300px" id="acksChart"></div>
115
+ <div class="ui segment" style="height: 300px" id="threadChart"></div>
111
116
  </div>
112
117
  </div>
113
118
  <div class="row">
114
119
  <div class="column">
115
- <div class="ui segment" style="height: 300px" id="memChart">
116
- </div>
120
+ <div class="ui segment" style="height: 300px" id="memChart"></div>
117
121
  </div>
118
122
  </div>
119
123
  <!-- <div class="row">
@@ -156,6 +160,13 @@
156
160
  <div id="pluginDiv" class="ui mini text">Loading</div>
157
161
  </div>
158
162
 
163
+ <div class="ui bottom attached tab segment" data-tab="threads-tab">
164
+ <h3 class="ui dividing header">
165
+ Threads Loaded (<span id="thread_count">{{ thread_count }}</span>)
166
+ </h3>
167
+ <div id="threadsDiv" class="ui mini text">Loading</div>
168
+ </div>
169
+
159
170
  <div class="ui bottom attached tab segment" data-tab="config-tab">
160
171
  <h3 class="ui dividing header">Config</h3>
161
172
  <pre id="configjson" class="language-json">{{ config_json|safe }}</pre>
@@ -174,7 +185,7 @@
174
185
 
175
186
  <div class="ui bottom attached tab segment" data-tab="raw-tab">
176
187
  <h3 class="ui dividing header">Raw JSON</h3>
177
- <pre id="jsonstats" class="language-yaml" style="height:600px;overflow-y:auto;">{{ stats|safe }}</pre>
188
+ <pre id="jsonstats" class="language-yaml" style="height:600px;overflow-y:auto;">{{ initial_stats|safe }}</pre>
178
189
  </div>
179
190
 
180
191
  <div class="ui text container">
@@ -64,9 +64,11 @@ function showError(error) {
64
64
 
65
65
  function showPosition(position) {
66
66
  console.log("showPosition Called");
67
+ path = $('#pkt_path option:selected').val();
67
68
  msg = {
68
69
  'latitude': position.coords.latitude,
69
- 'longitude': position.coords.longitude
70
+ 'longitude': position.coords.longitude,
71
+ 'path': path,
70
72
  }
71
73
  console.log(msg);
72
74
  $.toast({
@@ -19,9 +19,10 @@ function show_aprs_icon(item, symbol) {
19
19
  function ord(str){return str.charCodeAt(0);}
20
20
 
21
21
  function update_stats( data ) {
22
- $("#version").text( data["stats"]["aprsd"]["version"] );
22
+ console.log(data);
23
+ $("#version").text( data["stats"]["APRSDStats"]["version"] );
23
24
  $("#aprs_connection").html( data["aprs_connection"] );
24
- $("#uptime").text( "uptime: " + data["stats"]["aprsd"]["uptime"] );
25
+ $("#uptime").text( "uptime: " + data["stats"]["APRSDStats"]["uptime"] );
25
26
  short_time = data["time"].split(/\s(.+)/)[1];
26
27
  }
27
28
 
@@ -37,7 +38,7 @@ function start_update() {
37
38
  update_stats(data);
38
39
  },
39
40
  complete: function() {
40
- setTimeout(statsworker, 10000);
41
+ setTimeout(statsworker, 60000);
41
42
  }
42
43
  });
43
44
  })();
@@ -313,6 +313,7 @@ function create_callsign_tab(callsign, active=false) {
313
313
  //item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">';
314
314
  item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link position-relative '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">';
315
315
  item_html += callsign+'&nbsp;&nbsp;';
316
+ item_html += '<span id="'+tab_notify_id+'" class="position-absolute top-0 start-80 translate-middle badge bg-danger border border-light rounded-pill visually-hidden">0</span>';
316
317
  item_html += '<span onclick="delete_tab(\''+callsign+'\');">×</span>';
317
318
  item_html += '</button></li>'
318
319
 
@@ -407,13 +408,15 @@ function append_message(callsign, msg, msg_html) {
407
408
  tab_notify_id = tab_notification_id(callsign, true);
408
409
  // get the current count of notifications
409
410
  count = parseInt($(tab_notify_id).text());
411
+ if (isNaN(count)) {
412
+ count = 0;
413
+ }
410
414
  count += 1;
411
415
  $(tab_notify_id).text(count);
412
416
  $(tab_notify_id).removeClass('visually-hidden');
413
417
  }
414
418
 
415
419
  // Find the right div to place the html
416
-
417
420
  new_callsign = add_callsign(callsign, msg);
418
421
  update_callsign_path(callsign, msg);
419
422
  append_message_html(callsign, msg_html, new_callsign);
@@ -502,7 +505,7 @@ function sent_msg(msg) {
502
505
  msg_html = create_message_html(d, t, msg['from_call'], msg['to_call'], msg['message_text'], ack_id, msg, false);
503
506
  append_message(msg['to_call'], msg, msg_html);
504
507
  save_data();
505
- scroll_main_content(msg['from_call']);
508
+ scroll_main_content(msg['to_call']);
506
509
  }
507
510
 
508
511
  function from_msg(msg) {
@@ -103,6 +103,7 @@
103
103
  <option value="WIDE1-1">WIDE1-1</option>
104
104
  <option value="WIDE1-1,WIDE2-1">WIDE1-1,WIDE2-1</option>
105
105
  <option value="ARISS">ARISS</option>
106
+ <option value="GATE">GATE</option>
106
107
  </select>
107
108
  </div>
108
109
  <div class="col-sm-3">
aprsd/wsgi.py CHANGED
@@ -3,10 +3,10 @@ import importlib.metadata as imp
3
3
  import io
4
4
  import json
5
5
  import logging
6
- import time
6
+ import queue
7
7
 
8
8
  import flask
9
- from flask import Flask
9
+ from flask import Flask, request
10
10
  from flask_httpauth import HTTPBasicAuth
11
11
  from oslo_config import cfg, generator
12
12
  import socketio
@@ -15,14 +15,16 @@ from werkzeug.security import check_password_hash
15
15
  import aprsd
16
16
  from aprsd import cli_helper, client, conf, packets, plugin, threads
17
17
  from aprsd.log import log
18
- from aprsd.rpc import client as aprsd_rpc_client
18
+ from aprsd.threads import stats as stats_threads
19
+ from aprsd.utils import json as aprsd_json
19
20
 
20
21
 
21
22
  CONF = cfg.CONF
22
23
  LOG = logging.getLogger("gunicorn.access")
24
+ logging_queue = queue.Queue()
23
25
 
24
26
  auth = HTTPBasicAuth()
25
- users = {}
27
+ users: dict[str, str] = {}
26
28
  app = Flask(
27
29
  "aprsd",
28
30
  static_url_path="/static",
@@ -45,114 +47,40 @@ def verify_password(username, password):
45
47
 
46
48
 
47
49
  def _stats():
48
- track = aprsd_rpc_client.RPCClient().get_packet_track()
50
+ stats_obj = stats_threads.StatsStore()
51
+ stats_obj.load()
49
52
  now = datetime.datetime.now()
50
-
51
53
  time_format = "%m-%d-%Y %H:%M:%S"
52
-
53
- stats_dict = aprsd_rpc_client.RPCClient().get_stats_dict()
54
- if not stats_dict:
55
- stats_dict = {
56
- "aprsd": {},
57
- "aprs-is": {"server": ""},
58
- "messages": {
59
- "sent": 0,
60
- "received": 0,
61
- },
62
- "email": {
63
- "sent": 0,
64
- "received": 0,
65
- },
66
- "seen_list": {
67
- "sent": 0,
68
- "received": 0,
69
- },
70
- }
71
-
72
- # Convert the watch_list entries to age
73
- wl = aprsd_rpc_client.RPCClient().get_watch_list()
74
- new_list = {}
75
- if wl:
76
- for call in wl.get_all():
77
- # call_date = datetime.datetime.strptime(
78
- # str(wl.last_seen(call)),
79
- # "%Y-%m-%d %H:%M:%S.%f",
80
- # )
81
-
82
- # We have to convert the RingBuffer to a real list
83
- # so that json.dumps works.
84
- # pkts = []
85
- # for pkt in wl.get(call)["packets"].get():
86
- # pkts.append(pkt)
87
-
88
- new_list[call] = {
89
- "last": wl.age(call),
90
- # "packets": pkts
91
- }
92
-
93
- stats_dict["aprsd"]["watch_list"] = new_list
94
- packet_list = aprsd_rpc_client.RPCClient().get_packet_list()
95
- rx = tx = 0
96
- types = {}
97
- if packet_list:
98
- rx = packet_list.total_rx()
99
- tx = packet_list.total_tx()
100
- types_copy = packet_list.types.copy()
101
-
102
- for key in types_copy:
103
- types[str(key)] = dict(types_copy[key])
104
-
105
- stats_dict["packets"] = {
106
- "sent": tx,
107
- "received": rx,
108
- "types": types,
109
- }
110
- if track:
111
- size_tracker = len(track)
112
- else:
113
- size_tracker = 0
114
-
115
- result = {
54
+ stats = {
116
55
  "time": now.strftime(time_format),
117
- "size_tracker": size_tracker,
118
- "stats": stats_dict,
56
+ "stats": stats_obj.data,
119
57
  }
120
-
121
- return result
58
+ return stats
122
59
 
123
60
 
124
61
  @app.route("/stats")
125
62
  def stats():
126
63
  LOG.debug("/stats called")
127
- return json.dumps(_stats())
64
+ return json.dumps(_stats(), cls=aprsd_json.SimpleJSONEncoder)
128
65
 
129
66
 
130
67
  @app.route("/")
131
68
  def index():
132
69
  stats = _stats()
133
- wl = aprsd_rpc_client.RPCClient().get_watch_list()
134
- if wl and wl.is_enabled():
135
- watch_count = len(wl)
136
- watch_age = wl.max_delta()
137
- else:
138
- watch_count = 0
139
- watch_age = 0
140
-
141
- sl = aprsd_rpc_client.RPCClient().get_seen_list()
142
- if sl:
143
- seen_count = len(sl)
144
- else:
145
- seen_count = 0
146
-
147
70
  pm = plugin.PluginManager()
148
71
  plugins = pm.get_plugins()
149
72
  plugin_count = len(plugins)
73
+ client_stats = stats["stats"].get("APRSClientStats", {})
150
74
 
151
75
  if CONF.aprs_network.enabled:
152
76
  transport = "aprs-is"
77
+ if client_stats:
78
+ aprs_connection = client_stats.get("server_string", "")
79
+ else:
80
+ aprs_connection = "APRS-IS"
153
81
  aprs_connection = (
154
82
  "APRS-IS Server: <a href='http://status.aprs2.net' >"
155
- "{}</a>".format(stats["stats"]["aprs-is"]["server"])
83
+ "{}</a>".format(aprs_connection)
156
84
  )
157
85
  else:
158
86
  # We might be connected to a KISS socket?
@@ -173,13 +101,20 @@ def index():
173
101
  )
174
102
  )
175
103
 
176
- stats["transport"] = transport
177
- stats["aprs_connection"] = aprs_connection
104
+ if client_stats:
105
+ stats["stats"]["APRSClientStats"]["transport"] = transport
106
+ stats["stats"]["APRSClientStats"]["aprs_connection"] = aprs_connection
178
107
  entries = conf.conf_to_dict()
179
108
 
109
+ thread_info = stats["stats"].get("APRSDThreadList", {})
110
+ if thread_info:
111
+ thread_count = len(thread_info)
112
+ else:
113
+ thread_count = "unknown"
114
+
180
115
  return flask.render_template(
181
116
  "index.html",
182
- initial_stats=stats,
117
+ initial_stats=json.dumps(stats, cls=aprsd_json.SimpleJSONEncoder),
183
118
  aprs_connection=aprs_connection,
184
119
  callsign=CONF.callsign,
185
120
  version=aprsd.__version__,
@@ -187,10 +122,8 @@ def index():
187
122
  entries, indent=4,
188
123
  sort_keys=True, default=str,
189
124
  ),
190
- watch_count=watch_count,
191
- watch_age=watch_age,
192
- seen_count=seen_count,
193
125
  plugin_count=plugin_count,
126
+ thread_count=thread_count,
194
127
  # oslo_out=generate_oslo()
195
128
  )
196
129
 
@@ -209,19 +142,10 @@ def messages():
209
142
  @auth.login_required
210
143
  @app.route("/packets")
211
144
  def get_packets():
212
- LOG.debug("/packets called")
213
- packet_list = aprsd_rpc_client.RPCClient().get_packet_list()
214
- if packet_list:
215
- tmp_list = []
216
- pkts = packet_list.copy()
217
- for key in pkts:
218
- pkt = packet_list.get(key)
219
- if pkt:
220
- tmp_list.append(pkt.json)
221
-
222
- return json.dumps(tmp_list)
223
- else:
224
- return json.dumps([])
145
+ stats = _stats()
146
+ stats_dict = stats["stats"]
147
+ packets = stats_dict.get("PacketList", {})
148
+ return json.dumps(packets, cls=aprsd_json.SimpleJSONEncoder)
225
149
 
226
150
 
227
151
  @auth.login_required
@@ -273,23 +197,34 @@ def save():
273
197
  return json.dumps({"messages": "saved"})
274
198
 
275
199
 
200
+ @app.route("/log_entries", methods=["POST"])
201
+ def log_entries():
202
+ """The url that the server can call to update the logs."""
203
+ entries = request.json
204
+ LOG.info(f"Log entries called {len(entries)}")
205
+ for entry in entries:
206
+ logging_queue.put(entry)
207
+ return json.dumps({"messages": "saved"})
208
+
209
+
276
210
  class LogUpdateThread(threads.APRSDThread):
277
211
 
278
- def __init__(self):
212
+ def __init__(self, logging_queue=None):
279
213
  super().__init__("LogUpdate")
214
+ self.logging_queue = logging_queue
280
215
 
281
216
  def loop(self):
282
217
  if sio:
283
- log_entries = aprsd_rpc_client.RPCClient().get_log_entries()
284
-
285
- if log_entries:
286
- LOG.info(f"Sending log entries! {len(log_entries)}")
287
- for entry in log_entries:
218
+ try:
219
+ log_entry = self.logging_queue.get(block=True, timeout=1)
220
+ if log_entry:
288
221
  sio.emit(
289
- "log_entry", entry,
222
+ "log_entry",
223
+ log_entry,
290
224
  namespace="/logs",
291
225
  )
292
- time.sleep(5)
226
+ except queue.Empty:
227
+ pass
293
228
  return True
294
229
 
295
230
 
@@ -297,17 +232,17 @@ class LoggingNamespace(socketio.Namespace):
297
232
  log_thread = None
298
233
 
299
234
  def on_connect(self, sid, environ):
300
- global sio
301
- LOG.debug(f"LOG on_connect {sid}")
235
+ global sio, logging_queue
236
+ LOG.info(f"LOG on_connect {sid}")
302
237
  sio.emit(
303
238
  "connected", {"data": "/logs Connected"},
304
239
  namespace="/logs",
305
240
  )
306
- self.log_thread = LogUpdateThread()
241
+ self.log_thread = LogUpdateThread(logging_queue=logging_queue)
307
242
  self.log_thread.start()
308
243
 
309
244
  def on_disconnect(self, sid):
310
- LOG.debug(f"LOG Disconnected {sid}")
245
+ LOG.info(f"LOG Disconnected {sid}")
311
246
  if self.log_thread:
312
247
  self.log_thread.stop()
313
248
 
@@ -332,8 +267,8 @@ if __name__ == "__main__":
332
267
  async_mode = "threading"
333
268
  sio = socketio.Server(logger=True, async_mode=async_mode)
334
269
  app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app)
335
- log_level = init_app(log_level="DEBUG")
336
- log.setup_logging(app, log_level)
270
+ log_level = init_app()
271
+ log.setup_logging(log_level)
337
272
  sio.register_namespace(LoggingNamespace("/logs"))
338
273
  CONF.log_opt_values(LOG, logging.DEBUG)
339
274
  app.run(
@@ -352,12 +287,12 @@ if __name__ == "uwsgi_file_aprsd_wsgi":
352
287
  sio = socketio.Server(logger=True, async_mode=async_mode)
353
288
  app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app)
354
289
  log_level = init_app(
355
- log_level="DEBUG",
290
+ # log_level="DEBUG",
356
291
  config_file="/config/aprsd.conf",
357
292
  # Commented out for local development.
358
293
  # config_file=cli_helper.DEFAULT_CONFIG_FILE
359
294
  )
360
- log.setup_logging(app, log_level)
295
+ log.setup_logging(log_level)
361
296
  sio.register_namespace(LoggingNamespace("/logs"))
362
297
  CONF.log_opt_values(LOG, logging.DEBUG)
363
298
 
@@ -371,10 +306,10 @@ if __name__ == "aprsd.wsgi":
371
306
  app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app)
372
307
 
373
308
  log_level = init_app(
374
- log_level="DEBUG",
309
+ # log_level="DEBUG",
375
310
  config_file="/config/aprsd.conf",
376
311
  # config_file=cli_helper.DEFAULT_CONFIG_FILE,
377
312
  )
378
- log.setup_logging(app, log_level)
313
+ log.setup_logging(log_level)
379
314
  sio.register_namespace(LoggingNamespace("/logs"))
380
315
  CONF.log_opt_values(LOG, logging.DEBUG)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aprsd
3
- Version: 3.3.3
3
+ Version: 3.4.0
4
4
  Summary: Amateur radio APRS daemon which listens for messages and responds
5
5
  Home-page: http://aprsd.readthedocs.org
6
6
  Author: Craig Lamparter
@@ -31,14 +31,13 @@ Requires-Dist: click ==8.1.7
31
31
  Requires-Dist: click-completion ==0.5.2
32
32
  Requires-Dist: click-params ==0.5.0
33
33
  Requires-Dist: commonmark ==0.9.1
34
- Requires-Dist: dacite2 ==2.0.0
35
34
  Requires-Dist: dataclasses ==0.6
36
35
  Requires-Dist: dataclasses-json ==0.6.4
37
36
  Requires-Dist: debtcollector ==3.0.0
38
37
  Requires-Dist: deprecated ==1.2.14
39
38
  Requires-Dist: dnspython ==2.6.1
40
- Requires-Dist: eventlet ==0.35.2
41
- Requires-Dist: flask ==3.0.2
39
+ Requires-Dist: eventlet ==0.36.1
40
+ Requires-Dist: flask ==3.0.3
42
41
  Requires-Dist: flask-httpauth ==4.8.0
43
42
  Requires-Dist: flask-socketio ==5.3.6
44
43
  Requires-Dist: geographiclib ==2.0
@@ -46,10 +45,10 @@ Requires-Dist: geopy ==2.4.1
46
45
  Requires-Dist: gevent ==24.2.1
47
46
  Requires-Dist: greenlet ==3.0.3
48
47
  Requires-Dist: h11 ==0.14.0
49
- Requires-Dist: idna ==3.6
48
+ Requires-Dist: idna ==3.7
50
49
  Requires-Dist: imapclient ==3.0.1
51
- Requires-Dist: importlib-metadata ==7.0.1
52
- Requires-Dist: itsdangerous ==2.1.2
50
+ Requires-Dist: importlib-metadata ==7.1.0
51
+ Requires-Dist: itsdangerous ==2.2.0
53
52
  Requires-Dist: jinja2 ==3.1.3
54
53
  Requires-Dist: kiss3 ==8.0.0
55
54
  Requires-Dist: loguru ==0.7.2
@@ -59,21 +58,19 @@ Requires-Dist: mypy-extensions ==1.0.0
59
58
  Requires-Dist: netaddr ==1.2.1
60
59
  Requires-Dist: oslo-config ==9.4.0
61
60
  Requires-Dist: oslo-i18n ==6.3.0
62
- Requires-Dist: packaging ==23.2
61
+ Requires-Dist: packaging ==24.0
63
62
  Requires-Dist: pbr ==6.0.0
64
- Requires-Dist: pluggy ==1.4.0
65
- Requires-Dist: plumbum ==1.8.2
63
+ Requires-Dist: pluggy ==1.5.0
66
64
  Requires-Dist: pygments ==2.17.2
67
65
  Requires-Dist: pyserial ==3.5
68
66
  Requires-Dist: pyserial-asyncio ==0.6
69
67
  Requires-Dist: python-engineio ==4.9.0
70
- Requires-Dist: python-socketio ==5.11.1
68
+ Requires-Dist: python-socketio ==5.11.2
71
69
  Requires-Dist: pytz ==2024.1
72
70
  Requires-Dist: pyyaml ==6.0.1
73
71
  Requires-Dist: requests ==2.31.0
74
72
  Requires-Dist: rfc3986 ==2.0.0
75
73
  Requires-Dist: rich ==12.6.0
76
- Requires-Dist: rpyc ==6.0.0
77
74
  Requires-Dist: rush ==2021.4.0
78
75
  Requires-Dist: shellingham ==1.5.4
79
76
  Requires-Dist: simple-websocket ==1.0.0
@@ -82,17 +79,18 @@ Requires-Dist: soupsieve ==2.5
82
79
  Requires-Dist: stevedore ==5.2.0
83
80
  Requires-Dist: tabulate ==0.9.0
84
81
  Requires-Dist: thesmuggler ==1.0.1
85
- Requires-Dist: typing-extensions ==4.10.0
82
+ Requires-Dist: typing-extensions ==4.11.0
86
83
  Requires-Dist: typing-inspect ==0.9.0
84
+ Requires-Dist: tzlocal ==5.2
87
85
  Requires-Dist: update-checker ==0.18.0
88
86
  Requires-Dist: urllib3 ==2.2.1
89
87
  Requires-Dist: validators ==0.22.0
90
- Requires-Dist: werkzeug ==3.0.1
88
+ Requires-Dist: werkzeug ==3.0.2
91
89
  Requires-Dist: wrapt ==1.16.0
92
90
  Requires-Dist: wsproto ==1.2.0
93
- Requires-Dist: zipp ==3.17.0
91
+ Requires-Dist: zipp ==3.18.1
94
92
  Requires-Dist: zope-event ==5.0
95
- Requires-Dist: zope-interface ==6.2
93
+ Requires-Dist: zope-interface ==6.3
96
94
 
97
95
  ===============================================
98
96
  APRSD - Ham radio APRS-IS Message plugin server