aprsd 3.2.2__py2.py3-none-any.whl → 3.3.1__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.
- aprsd/cli_helper.py +35 -1
- aprsd/client.py +3 -3
- aprsd/clients/aprsis.py +0 -1
- aprsd/cmds/dev.py +31 -2
- aprsd/cmds/fetch_stats.py +5 -1
- aprsd/cmds/list_plugins.py +87 -10
- aprsd/cmds/server.py +10 -1
- aprsd/cmds/webchat.py +245 -45
- aprsd/conf/common.py +68 -1
- aprsd/conf/log.py +8 -10
- aprsd/conf/plugin_common.py +108 -0
- aprsd/log/log.py +74 -66
- aprsd/main.py +11 -11
- aprsd/packets/__init__.py +2 -2
- aprsd/packets/core.py +20 -1
- aprsd/plugin_utils.py +1 -0
- aprsd/plugins/fortune.py +4 -1
- aprsd/plugins/location.py +88 -6
- aprsd/plugins/weather.py +7 -3
- aprsd/threads/__init__.py +1 -1
- aprsd/threads/registry.py +56 -0
- aprsd/threads/rx.py +13 -5
- aprsd/threads/tx.py +40 -0
- aprsd/utils/__init__.py +19 -0
- aprsd/utils/objectstore.py +7 -1
- aprsd/web/chat/static/images/globe.svg +3 -0
- aprsd/web/chat/static/js/send-message.js +107 -29
- aprsd/web/chat/templates/index.html +6 -2
- aprsd/wsgi.py +4 -39
- {aprsd-3.2.2.dist-info → aprsd-3.3.1.dist-info}/METADATA +106 -98
- {aprsd-3.2.2.dist-info → aprsd-3.3.1.dist-info}/RECORD +36 -35
- {aprsd-3.2.2.dist-info → aprsd-3.3.1.dist-info}/WHEEL +1 -1
- aprsd-3.3.1.dist-info/pbr.json +1 -0
- aprsd/log/rich.py +0 -160
- aprsd-3.2.2.dist-info/pbr.json +0 -1
- {aprsd-3.2.2.dist-info → aprsd-3.3.1.dist-info}/LICENSE +0 -0
- {aprsd-3.2.2.dist-info → aprsd-3.3.1.dist-info}/entry_points.txt +0 -0
- {aprsd-3.2.2.dist-info → aprsd-3.3.1.dist-info}/top_level.txt +0 -0
aprsd/cmds/webchat.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import datetime
|
2
2
|
import json
|
3
3
|
import logging
|
4
|
-
|
4
|
+
import math
|
5
5
|
import signal
|
6
6
|
import sys
|
7
7
|
import threading
|
@@ -11,17 +11,20 @@ from aprslib import util as aprslib_util
|
|
11
11
|
import click
|
12
12
|
import flask
|
13
13
|
from flask import request
|
14
|
-
from flask.logging import default_handler
|
15
14
|
from flask_httpauth import HTTPBasicAuth
|
16
15
|
from flask_socketio import Namespace, SocketIO
|
16
|
+
from geopy.distance import geodesic
|
17
17
|
from oslo_config import cfg
|
18
18
|
from werkzeug.security import check_password_hash, generate_password_hash
|
19
19
|
import wrapt
|
20
20
|
|
21
21
|
import aprsd
|
22
|
-
from aprsd import
|
23
|
-
|
22
|
+
from aprsd import (
|
23
|
+
cli_helper, client, packets, plugin_utils, stats, threads, utils,
|
24
|
+
)
|
25
|
+
from aprsd.log import log
|
24
26
|
from aprsd.main import cli
|
27
|
+
from aprsd.threads import aprsd as aprsd_threads
|
25
28
|
from aprsd.threads import rx, tx
|
26
29
|
from aprsd.utils import trace
|
27
30
|
|
@@ -32,6 +35,16 @@ auth = HTTPBasicAuth()
|
|
32
35
|
users = {}
|
33
36
|
socketio = None
|
34
37
|
|
38
|
+
# List of callsigns that we don't want to track/fetch their location
|
39
|
+
callsign_no_track = [
|
40
|
+
"REPEAT", "WB4BOR-11", "APDW16", "WXNOW", "WXBOT", "BLN0", "BLN1", "BLN2",
|
41
|
+
"BLN3", "BLN4", "BLN5", "BLN6", "BLN7", "BLN8", "BLN9",
|
42
|
+
]
|
43
|
+
|
44
|
+
# Callsign location information
|
45
|
+
# callsign: {lat: 0.0, long: 0.0, last_update: datetime}
|
46
|
+
callsign_locations = {}
|
47
|
+
|
35
48
|
flask_app = flask.Flask(
|
36
49
|
"aprsd",
|
37
50
|
static_url_path="/static",
|
@@ -121,8 +134,188 @@ def verify_password(username, password):
|
|
121
134
|
return username
|
122
135
|
|
123
136
|
|
137
|
+
def calculate_initial_compass_bearing(point_a, point_b):
|
138
|
+
"""
|
139
|
+
Calculates the bearing between two points.
|
140
|
+
The formulae used is the following:
|
141
|
+
θ = atan2(sin(Δlong).cos(lat2),
|
142
|
+
cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong))
|
143
|
+
:Parameters:
|
144
|
+
- `pointA: The tuple representing the latitude/longitude for the
|
145
|
+
first point. Latitude and longitude must be in decimal degrees
|
146
|
+
- `pointB: The tuple representing the latitude/longitude for the
|
147
|
+
second point. Latitude and longitude must be in decimal degrees
|
148
|
+
:Returns:
|
149
|
+
The bearing in degrees
|
150
|
+
:Returns Type:
|
151
|
+
float
|
152
|
+
"""
|
153
|
+
if (type(point_a) is not tuple) or (type(point_b) is not tuple):
|
154
|
+
raise TypeError("Only tuples are supported as arguments")
|
155
|
+
|
156
|
+
lat1 = math.radians(point_a[0])
|
157
|
+
lat2 = math.radians(point_b[0])
|
158
|
+
|
159
|
+
diff_long = math.radians(point_b[1] - point_a[1])
|
160
|
+
|
161
|
+
x = math.sin(diff_long) * math.cos(lat2)
|
162
|
+
y = math.cos(lat1) * math.sin(lat2) - (
|
163
|
+
math.sin(lat1)
|
164
|
+
* math.cos(lat2) * math.cos(diff_long)
|
165
|
+
)
|
166
|
+
|
167
|
+
initial_bearing = math.atan2(x, y)
|
168
|
+
|
169
|
+
# Now we have the initial bearing but math.atan2 return values
|
170
|
+
# from -180° to + 180° which is not what we want for a compass bearing
|
171
|
+
# The solution is to normalize the initial bearing as shown below
|
172
|
+
initial_bearing = math.degrees(initial_bearing)
|
173
|
+
compass_bearing = (initial_bearing + 360) % 360
|
174
|
+
|
175
|
+
return compass_bearing
|
176
|
+
|
177
|
+
|
178
|
+
def _build_location_from_repeat(message):
|
179
|
+
# This is a location message Format is
|
180
|
+
# ^ld^callsign:latitude,longitude,altitude,course,speed,timestamp
|
181
|
+
a = message.split(":")
|
182
|
+
LOG.warning(a)
|
183
|
+
if len(a) == 2:
|
184
|
+
callsign = a[0].replace("^ld^", "")
|
185
|
+
b = a[1].split(",")
|
186
|
+
LOG.warning(b)
|
187
|
+
if len(b) == 6:
|
188
|
+
lat = float(b[0])
|
189
|
+
lon = float(b[1])
|
190
|
+
alt = float(b[2])
|
191
|
+
course = float(b[3])
|
192
|
+
speed = float(b[4])
|
193
|
+
time = int(b[5])
|
194
|
+
data = {
|
195
|
+
"callsign": callsign,
|
196
|
+
"lat": lat,
|
197
|
+
"lon": lon,
|
198
|
+
"altitude": alt,
|
199
|
+
"course": course,
|
200
|
+
"speed": speed,
|
201
|
+
"lasttime": time,
|
202
|
+
}
|
203
|
+
LOG.warning(f"Location data from REPEAT {data}")
|
204
|
+
return data
|
205
|
+
|
206
|
+
|
207
|
+
def _calculate_location_data(location_data):
|
208
|
+
"""Calculate all of the location data from data from aprs.fi or REPEAT."""
|
209
|
+
lat = location_data["lat"]
|
210
|
+
lon = location_data["lon"]
|
211
|
+
alt = location_data["altitude"]
|
212
|
+
speed = location_data["speed"]
|
213
|
+
lasttime = location_data["lasttime"]
|
214
|
+
# now calculate distance from our own location
|
215
|
+
distance = 0
|
216
|
+
if CONF.webchat.latitude and CONF.webchat.longitude:
|
217
|
+
our_lat = float(CONF.webchat.latitude)
|
218
|
+
our_lon = float(CONF.webchat.longitude)
|
219
|
+
distance = geodesic((our_lat, our_lon), (lat, lon)).kilometers
|
220
|
+
bearing = calculate_initial_compass_bearing(
|
221
|
+
(our_lat, our_lon),
|
222
|
+
(lat, lon),
|
223
|
+
)
|
224
|
+
return {
|
225
|
+
"callsign": location_data["callsign"],
|
226
|
+
"lat": lat,
|
227
|
+
"lon": lon,
|
228
|
+
"altitude": alt,
|
229
|
+
"course": f"{bearing:0.1f}",
|
230
|
+
"speed": speed,
|
231
|
+
"lasttime": lasttime,
|
232
|
+
"distance": f"{distance:0.3f}",
|
233
|
+
}
|
234
|
+
|
235
|
+
|
236
|
+
def send_location_data_to_browser(location_data):
|
237
|
+
global socketio
|
238
|
+
callsign = location_data["callsign"]
|
239
|
+
LOG.info(f"Got location for {callsign} {callsign_locations[callsign]}")
|
240
|
+
socketio.emit(
|
241
|
+
"callsign_location", callsign_locations[callsign],
|
242
|
+
namespace="/sendmsg",
|
243
|
+
)
|
244
|
+
|
245
|
+
|
246
|
+
def populate_callsign_location(callsign, data=None):
|
247
|
+
"""Populate the location for the callsign.
|
248
|
+
|
249
|
+
if data is passed in, then we have the location already from
|
250
|
+
an APRS packet. If data is None, then we need to fetch the
|
251
|
+
location from aprs.fi or REPEAT.
|
252
|
+
"""
|
253
|
+
global socketio
|
254
|
+
"""Fetch the location for the callsign."""
|
255
|
+
LOG.debug(f"populate_callsign_location {callsign}")
|
256
|
+
if data:
|
257
|
+
location_data = _calculate_location_data(data)
|
258
|
+
callsign_locations[callsign] = location_data
|
259
|
+
send_location_data_to_browser(location_data)
|
260
|
+
return
|
261
|
+
|
262
|
+
# First we are going to try to get the location from aprs.fi
|
263
|
+
# if there is no internets, then this will fail and we will
|
264
|
+
# fallback to calling REPEAT for the location for the callsign.
|
265
|
+
fallback = False
|
266
|
+
if not CONF.aprs_fi.apiKey:
|
267
|
+
LOG.warning(
|
268
|
+
"Config aprs_fi.apiKey is not set. Can't get location from aprs.fi "
|
269
|
+
" falling back to sending REPEAT to get location.",
|
270
|
+
)
|
271
|
+
fallback = True
|
272
|
+
else:
|
273
|
+
try:
|
274
|
+
aprs_data = plugin_utils.get_aprs_fi(CONF.aprs_fi.apiKey, callsign)
|
275
|
+
if not len(aprs_data["entries"]):
|
276
|
+
LOG.error("Didn't get any entries from aprs.fi")
|
277
|
+
return
|
278
|
+
lat = float(aprs_data["entries"][0]["lat"])
|
279
|
+
lon = float(aprs_data["entries"][0]["lng"])
|
280
|
+
try: # altitude not always provided
|
281
|
+
alt = float(aprs_data["entries"][0]["altitude"])
|
282
|
+
except Exception:
|
283
|
+
alt = 0
|
284
|
+
location_data = {
|
285
|
+
"callsign": callsign,
|
286
|
+
"lat": lat,
|
287
|
+
"lon": lon,
|
288
|
+
"altitude": alt,
|
289
|
+
"lasttime": int(aprs_data["entries"][0]["lasttime"]),
|
290
|
+
"course": float(aprs_data["entries"][0].get("course", 0)),
|
291
|
+
"speed": float(aprs_data["entries"][0].get("speed", 0)),
|
292
|
+
}
|
293
|
+
location_data = _calculate_location_data(location_data)
|
294
|
+
callsign_locations[callsign] = location_data
|
295
|
+
send_location_data_to_browser(location_data)
|
296
|
+
return
|
297
|
+
except Exception as ex:
|
298
|
+
LOG.error(f"Failed to fetch aprs.fi '{ex}'")
|
299
|
+
LOG.error(ex)
|
300
|
+
fallback = True
|
301
|
+
|
302
|
+
if fallback:
|
303
|
+
# We don't have the location data
|
304
|
+
# and we can't get it from aprs.fi
|
305
|
+
# Send a special message to REPEAT to get the location data
|
306
|
+
LOG.info(f"Sending REPEAT to get location for callsign {callsign}.")
|
307
|
+
tx.send(
|
308
|
+
packets.MessagePacket(
|
309
|
+
from_call=CONF.callsign,
|
310
|
+
to_call="REPEAT",
|
311
|
+
message_text=f"ld {callsign}",
|
312
|
+
),
|
313
|
+
)
|
314
|
+
|
315
|
+
|
124
316
|
class WebChatProcessPacketThread(rx.APRSDProcessPacketThread):
|
125
317
|
"""Class that handles packets being sent to us."""
|
318
|
+
|
126
319
|
def __init__(self, packet_queue, socketio):
|
127
320
|
self.socketio = socketio
|
128
321
|
self.connected = False
|
@@ -132,20 +325,53 @@ class WebChatProcessPacketThread(rx.APRSDProcessPacketThread):
|
|
132
325
|
super().process_ack_packet(packet)
|
133
326
|
ack_num = packet.get("msgNo")
|
134
327
|
SentMessages().ack(ack_num)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
328
|
+
msg = SentMessages().get(ack_num)
|
329
|
+
if msg:
|
330
|
+
self.socketio.emit(
|
331
|
+
"ack", msg,
|
332
|
+
namespace="/sendmsg",
|
333
|
+
)
|
139
334
|
self.got_ack = True
|
140
335
|
|
141
336
|
def process_our_message_packet(self, packet: packets.MessagePacket):
|
337
|
+
global callsign_locations
|
142
338
|
LOG.info(f"process MessagePacket {repr(packet)}")
|
339
|
+
# ok lets see if we have the location for the
|
340
|
+
# person we just sent a message to.
|
341
|
+
from_call = packet.get("from_call").upper()
|
342
|
+
if from_call == "REPEAT":
|
343
|
+
# We got a message from REPEAT. Is this a location message?
|
344
|
+
message = packet.get("message_text")
|
345
|
+
if message.startswith("^ld^"):
|
346
|
+
location_data = _build_location_from_repeat(message)
|
347
|
+
callsign = location_data["callsign"]
|
348
|
+
location_data = _calculate_location_data(location_data)
|
349
|
+
callsign_locations[callsign] = location_data
|
350
|
+
send_location_data_to_browser(location_data)
|
351
|
+
return
|
352
|
+
elif (
|
353
|
+
from_call not in callsign_locations
|
354
|
+
and from_call not in callsign_no_track
|
355
|
+
):
|
356
|
+
# We have to ask aprs for the location for the callsign
|
357
|
+
# We send a message packet to wb4bor-11 asking for location.
|
358
|
+
populate_callsign_location(from_call)
|
359
|
+
# Send the packet to the browser.
|
143
360
|
self.socketio.emit(
|
144
361
|
"new", packet.__dict__,
|
145
362
|
namespace="/sendmsg",
|
146
363
|
)
|
147
364
|
|
148
365
|
|
366
|
+
class LocationProcessingThread(aprsd_threads.APRSDThread):
|
367
|
+
"""Class to handle the location processing."""
|
368
|
+
def __init__(self):
|
369
|
+
super().__init__("LocationProcessingThread")
|
370
|
+
|
371
|
+
def loop(self):
|
372
|
+
pass
|
373
|
+
|
374
|
+
|
149
375
|
def set_config():
|
150
376
|
global users
|
151
377
|
|
@@ -181,6 +407,12 @@ def _get_transport(stats):
|
|
181
407
|
return transport, aprs_connection
|
182
408
|
|
183
409
|
|
410
|
+
@flask_app.route("/location/<callsign>", methods=["POST"])
|
411
|
+
def location(callsign):
|
412
|
+
LOG.debug(f"Fetch location for callsign {callsign}")
|
413
|
+
populate_callsign_location(callsign)
|
414
|
+
|
415
|
+
|
184
416
|
@auth.login_required
|
185
417
|
@flask_app.route("/")
|
186
418
|
def index():
|
@@ -216,7 +448,7 @@ def index():
|
|
216
448
|
|
217
449
|
|
218
450
|
@auth.login_required
|
219
|
-
@flask_app.route("
|
451
|
+
@flask_app.route("/send-message-status")
|
220
452
|
def send_message_status():
|
221
453
|
LOG.debug(request)
|
222
454
|
msgs = SentMessages()
|
@@ -331,53 +563,21 @@ class SendMessageNamespace(Namespace):
|
|
331
563
|
def handle_json(self, data):
|
332
564
|
LOG.debug(f"WS json {data}")
|
333
565
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
flask_app.logger.removeHandler(default_handler)
|
338
|
-
flask_log.removeHandler(default_handler)
|
339
|
-
|
340
|
-
log_level = conf.log.LOG_LEVELS[loglevel]
|
341
|
-
flask_log.setLevel(log_level)
|
342
|
-
date_format = CONF.logging.date_format
|
343
|
-
|
344
|
-
if CONF.logging.rich_logging and not quiet:
|
345
|
-
log_format = "%(message)s"
|
346
|
-
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
347
|
-
rh = aprsd_logging.APRSDRichHandler(
|
348
|
-
show_thread=True, thread_width=15,
|
349
|
-
rich_tracebacks=True, omit_repeated_times=False,
|
350
|
-
)
|
351
|
-
rh.setFormatter(log_formatter)
|
352
|
-
flask_log.addHandler(rh)
|
353
|
-
|
354
|
-
log_file = CONF.logging.logfile
|
355
|
-
|
356
|
-
if log_file:
|
357
|
-
log_format = CONF.logging.logformat
|
358
|
-
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
359
|
-
fh = RotatingFileHandler(
|
360
|
-
log_file, maxBytes=(10248576 * 5),
|
361
|
-
backupCount=4,
|
362
|
-
)
|
363
|
-
fh.setFormatter(log_formatter)
|
364
|
-
flask_log.addHandler(fh)
|
566
|
+
def on_get_callsign_location(self, data):
|
567
|
+
LOG.debug(f"on_callsign_location {data}")
|
568
|
+
populate_callsign_location(data["callsign"])
|
365
569
|
|
366
570
|
|
367
571
|
@trace.trace
|
368
572
|
def init_flask(loglevel, quiet):
|
369
573
|
global socketio, flask_app
|
370
574
|
|
371
|
-
setup_logging(
|
575
|
+
log.setup_logging(loglevel, quiet)
|
372
576
|
|
373
577
|
socketio = SocketIO(
|
374
578
|
flask_app, logger=False, engineio_logger=False,
|
375
579
|
async_mode="threading",
|
376
580
|
)
|
377
|
-
# async_mode="gevent",
|
378
|
-
# async_mode="eventlet",
|
379
|
-
# import eventlet
|
380
|
-
# eventlet.monkey_patch()
|
381
581
|
|
382
582
|
socketio.on_namespace(
|
383
583
|
SendMessageNamespace(
|
aprsd/conf/common.py
CHANGED
@@ -24,6 +24,11 @@ webchat_group = cfg.OptGroup(
|
|
24
24
|
title="Settings specific to the webchat command",
|
25
25
|
)
|
26
26
|
|
27
|
+
registry_group = cfg.OptGroup(
|
28
|
+
name="aprs_registry",
|
29
|
+
title="APRS Registry settings",
|
30
|
+
)
|
31
|
+
|
27
32
|
|
28
33
|
aprsd_opts = [
|
29
34
|
cfg.StrOpt(
|
@@ -67,9 +72,35 @@ aprsd_opts = [
|
|
67
72
|
),
|
68
73
|
cfg.IntOpt(
|
69
74
|
"packet_dupe_timeout",
|
70
|
-
default=
|
75
|
+
default=300,
|
71
76
|
help="The number of seconds before a packet is not considered a duplicate.",
|
72
77
|
),
|
78
|
+
cfg.BoolOpt(
|
79
|
+
"enable_beacon",
|
80
|
+
default=False,
|
81
|
+
help="Enable sending of a GPS Beacon packet to locate this service. "
|
82
|
+
"Requires latitude and longitude to be set.",
|
83
|
+
),
|
84
|
+
cfg.IntOpt(
|
85
|
+
"beacon_interval",
|
86
|
+
default=1800,
|
87
|
+
help="The number of seconds between beacon packets.",
|
88
|
+
),
|
89
|
+
cfg.StrOpt(
|
90
|
+
"beacon_symbol",
|
91
|
+
default="/",
|
92
|
+
help="The symbol to use for the GPS Beacon packet. See: http://www.aprs.net/vm/DOS/SYMBOLS.HTM",
|
93
|
+
),
|
94
|
+
cfg.StrOpt(
|
95
|
+
"latitude",
|
96
|
+
default=None,
|
97
|
+
help="Latitude for the GPS Beacon button. If not set, the button will not be enabled.",
|
98
|
+
),
|
99
|
+
cfg.StrOpt(
|
100
|
+
"longitude",
|
101
|
+
default=None,
|
102
|
+
help="Longitude for the GPS Beacon button. If not set, the button will not be enabled.",
|
103
|
+
),
|
73
104
|
]
|
74
105
|
|
75
106
|
watch_list_opts = [
|
@@ -196,6 +227,39 @@ webchat_opts = [
|
|
196
227
|
),
|
197
228
|
]
|
198
229
|
|
230
|
+
registry_opts = [
|
231
|
+
cfg.StrOpt(
|
232
|
+
"enabled",
|
233
|
+
default=False,
|
234
|
+
help="Enable sending aprs registry information. This will let the "
|
235
|
+
"APRS registry know about your service and it's uptime. "
|
236
|
+
"No personal information is sent, just the callsign, uptime and description. "
|
237
|
+
"The service callsign is the callsign set in [DEFAULT] section.",
|
238
|
+
),
|
239
|
+
cfg.StrOpt(
|
240
|
+
"description",
|
241
|
+
default=None,
|
242
|
+
help="Description of the service to send to the APRS registry. "
|
243
|
+
"This is what will show up in the APRS registry."
|
244
|
+
"If not set, the description will be the same as the callsign.",
|
245
|
+
),
|
246
|
+
cfg.StrOpt(
|
247
|
+
"registry_url",
|
248
|
+
default="https://aprs.hemna.com/api/v1/registry",
|
249
|
+
help="The APRS registry domain name to send the information to.",
|
250
|
+
),
|
251
|
+
cfg.StrOpt(
|
252
|
+
"service_website",
|
253
|
+
default=None,
|
254
|
+
help="The website for your APRS service to send to the APRS registry.",
|
255
|
+
),
|
256
|
+
cfg.IntOpt(
|
257
|
+
"frequency_seconds",
|
258
|
+
default=3600,
|
259
|
+
help="The frequency in seconds to send the APRS registry information.",
|
260
|
+
),
|
261
|
+
]
|
262
|
+
|
199
263
|
|
200
264
|
def register_opts(config):
|
201
265
|
config.register_opts(aprsd_opts)
|
@@ -208,6 +272,8 @@ def register_opts(config):
|
|
208
272
|
config.register_opts(rpc_opts, group=rpc_group)
|
209
273
|
config.register_group(webchat_group)
|
210
274
|
config.register_opts(webchat_opts, group=webchat_group)
|
275
|
+
config.register_group(registry_group)
|
276
|
+
config.register_opts(registry_opts, group=registry_group)
|
211
277
|
|
212
278
|
|
213
279
|
def list_opts():
|
@@ -217,4 +283,5 @@ def list_opts():
|
|
217
283
|
watch_list_group.name: watch_list_opts,
|
218
284
|
rpc_group.name: rpc_opts,
|
219
285
|
webchat_group.name: webchat_opts,
|
286
|
+
registry_group.name: registry_opts,
|
220
287
|
}
|
aprsd/conf/log.py
CHANGED
@@ -20,21 +20,19 @@ DEFAULT_LOG_FORMAT = (
|
|
20
20
|
" %(message)s - [%(pathname)s:%(lineno)d]"
|
21
21
|
)
|
22
22
|
|
23
|
+
DEFAULT_LOG_FORMAT = (
|
24
|
+
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
25
|
+
"<yellow>{thread.name: <18}</yellow> | "
|
26
|
+
"<level>{level: <8}</level> | "
|
27
|
+
"<level>{message}</level> | "
|
28
|
+
"<cyan>{name}</cyan>:<cyan>{function:}</cyan>:<magenta>{line:}</magenta>"
|
29
|
+
)
|
30
|
+
|
23
31
|
logging_group = cfg.OptGroup(
|
24
32
|
name="logging",
|
25
33
|
title="Logging options",
|
26
34
|
)
|
27
35
|
logging_opts = [
|
28
|
-
cfg.StrOpt(
|
29
|
-
"date_format",
|
30
|
-
default=DEFAULT_DATE_FORMAT,
|
31
|
-
help="Date format for log entries",
|
32
|
-
),
|
33
|
-
cfg.BoolOpt(
|
34
|
-
"rich_logging",
|
35
|
-
default=True,
|
36
|
-
help="Enable Rich log",
|
37
|
-
),
|
38
36
|
cfg.StrOpt(
|
39
37
|
"logfile",
|
40
38
|
default=None,
|
aprsd/conf/plugin_common.py
CHANGED
@@ -18,6 +18,11 @@ owm_wx_group = cfg.OptGroup(
|
|
18
18
|
title="Options for the OWMWeatherPlugin",
|
19
19
|
)
|
20
20
|
|
21
|
+
location_group = cfg.OptGroup(
|
22
|
+
name="location_plugin",
|
23
|
+
title="Options for the LocationPlugin",
|
24
|
+
)
|
25
|
+
|
21
26
|
aprsfi_opts = [
|
22
27
|
cfg.StrOpt(
|
23
28
|
"apiKey",
|
@@ -62,6 +67,106 @@ avwx_opts = [
|
|
62
67
|
),
|
63
68
|
]
|
64
69
|
|
70
|
+
location_opts = [
|
71
|
+
cfg.StrOpt(
|
72
|
+
"geopy_geocoder",
|
73
|
+
choices=[
|
74
|
+
"ArcGIS", "AzureMaps", "Baidu", "Bing", "GoogleV3", "HERE",
|
75
|
+
"Nominatim", "OpenCage", "TomTom", "USGov", "What3Words", "Woosmap",
|
76
|
+
],
|
77
|
+
default="Nominatim",
|
78
|
+
help="The geopy geocoder to use. Default is Nominatim."
|
79
|
+
"See https://geopy.readthedocs.io/en/stable/#module-geopy.geocoders"
|
80
|
+
"for more information.",
|
81
|
+
),
|
82
|
+
cfg.StrOpt(
|
83
|
+
"user_agent",
|
84
|
+
default="APRSD",
|
85
|
+
help="The user agent to use for the Nominatim geocoder."
|
86
|
+
"See https://geopy.readthedocs.io/en/stable/#module-geopy.geocoders"
|
87
|
+
"for more information.",
|
88
|
+
),
|
89
|
+
cfg.StrOpt(
|
90
|
+
"arcgis_username",
|
91
|
+
default=None,
|
92
|
+
help="The username to use for the ArcGIS geocoder."
|
93
|
+
"See https://geopy.readthedocs.io/en/latest/#arcgis"
|
94
|
+
"for more information."
|
95
|
+
"Only used for the ArcGIS geocoder.",
|
96
|
+
),
|
97
|
+
cfg.StrOpt(
|
98
|
+
"arcgis_password",
|
99
|
+
default=None,
|
100
|
+
help="The password to use for the ArcGIS geocoder."
|
101
|
+
"See https://geopy.readthedocs.io/en/latest/#arcgis"
|
102
|
+
"for more information."
|
103
|
+
"Only used for the ArcGIS geocoder.",
|
104
|
+
),
|
105
|
+
cfg.StrOpt(
|
106
|
+
"azuremaps_subscription_key",
|
107
|
+
help="The subscription key to use for the AzureMaps geocoder."
|
108
|
+
"See https://geopy.readthedocs.io/en/latest/#azuremaps"
|
109
|
+
"for more information."
|
110
|
+
"Only used for the AzureMaps geocoder.",
|
111
|
+
),
|
112
|
+
cfg.StrOpt(
|
113
|
+
"baidu_api_key",
|
114
|
+
help="The API key to use for the Baidu geocoder."
|
115
|
+
"See https://geopy.readthedocs.io/en/latest/#baidu"
|
116
|
+
"for more information."
|
117
|
+
"Only used for the Baidu geocoder.",
|
118
|
+
),
|
119
|
+
cfg.StrOpt(
|
120
|
+
"bing_api_key",
|
121
|
+
help="The API key to use for the Bing geocoder."
|
122
|
+
"See https://geopy.readthedocs.io/en/latest/#bing"
|
123
|
+
"for more information."
|
124
|
+
"Only used for the Bing geocoder.",
|
125
|
+
),
|
126
|
+
cfg.StrOpt(
|
127
|
+
"google_api_key",
|
128
|
+
help="The API key to use for the Google geocoder."
|
129
|
+
"See https://geopy.readthedocs.io/en/latest/#googlev3"
|
130
|
+
"for more information."
|
131
|
+
"Only used for the Google geocoder.",
|
132
|
+
),
|
133
|
+
cfg.StrOpt(
|
134
|
+
"here_api_key",
|
135
|
+
help="The API key to use for the HERE geocoder."
|
136
|
+
"See https://geopy.readthedocs.io/en/latest/#here"
|
137
|
+
"for more information."
|
138
|
+
"Only used for the HERE geocoder.",
|
139
|
+
),
|
140
|
+
cfg.StrOpt(
|
141
|
+
"opencage_api_key",
|
142
|
+
help="The API key to use for the OpenCage geocoder."
|
143
|
+
"See https://geopy.readthedocs.io/en/latest/#opencage"
|
144
|
+
"for more information."
|
145
|
+
"Only used for the OpenCage geocoder.",
|
146
|
+
),
|
147
|
+
cfg.StrOpt(
|
148
|
+
"tomtom_api_key",
|
149
|
+
help="The API key to use for the TomTom geocoder."
|
150
|
+
"See https://geopy.readthedocs.io/en/latest/#tomtom"
|
151
|
+
"for more information."
|
152
|
+
"Only used for the TomTom geocoder.",
|
153
|
+
),
|
154
|
+
cfg.StrOpt(
|
155
|
+
"what3words_api_key",
|
156
|
+
help="The API key to use for the What3Words geocoder."
|
157
|
+
"See https://geopy.readthedocs.io/en/latest/#what3words"
|
158
|
+
"for more information."
|
159
|
+
"Only used for the What3Words geocoder.",
|
160
|
+
),
|
161
|
+
cfg.StrOpt(
|
162
|
+
"woosmap_api_key",
|
163
|
+
help="The API key to use for the Woosmap geocoder."
|
164
|
+
"See https://geopy.readthedocs.io/en/latest/#woosmap"
|
165
|
+
"for more information."
|
166
|
+
"Only used for the Woosmap geocoder.",
|
167
|
+
),
|
168
|
+
]
|
169
|
+
|
65
170
|
|
66
171
|
def register_opts(config):
|
67
172
|
config.register_group(aprsfi_group)
|
@@ -72,6 +177,8 @@ def register_opts(config):
|
|
72
177
|
config.register_opts(owm_wx_opts, group=owm_wx_group)
|
73
178
|
config.register_group(avwx_group)
|
74
179
|
config.register_opts(avwx_opts, group=avwx_group)
|
180
|
+
config.register_group(location_group)
|
181
|
+
config.register_opts(location_opts, group=location_group)
|
75
182
|
|
76
183
|
|
77
184
|
def list_opts():
|
@@ -80,4 +187,5 @@ def list_opts():
|
|
80
187
|
query_group.name: query_plugin_opts,
|
81
188
|
owm_wx_group.name: owm_wx_opts,
|
82
189
|
avwx_group.name: avwx_opts,
|
190
|
+
location_group.name: location_opts,
|
83
191
|
}
|