aprsd 4.1.2__py3-none-any.whl → 4.2.1__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/client/__init__.py +5 -13
- aprsd/client/client.py +156 -0
- aprsd/client/drivers/__init__.py +10 -0
- aprsd/client/drivers/aprsis.py +174 -255
- aprsd/client/drivers/fake.py +59 -11
- aprsd/client/drivers/lib/__init__.py +0 -0
- aprsd/client/drivers/lib/aprslib.py +296 -0
- aprsd/client/drivers/registry.py +86 -0
- aprsd/client/drivers/tcpkiss.py +423 -0
- aprsd/client/stats.py +2 -2
- aprsd/cmds/dev.py +6 -4
- aprsd/cmds/fetch_stats.py +2 -0
- aprsd/cmds/list_plugins.py +6 -133
- aprsd/cmds/listen.py +5 -3
- aprsd/cmds/send_message.py +8 -5
- aprsd/cmds/server.py +7 -11
- aprsd/conf/common.py +7 -1
- aprsd/exception.py +7 -0
- aprsd/log/log.py +1 -1
- aprsd/main.py +0 -7
- aprsd/packets/core.py +168 -169
- aprsd/packets/log.py +69 -59
- aprsd/plugin.py +3 -2
- aprsd/plugin_utils.py +2 -2
- aprsd/plugins/weather.py +2 -2
- aprsd/stats/collector.py +5 -4
- aprsd/threads/rx.py +13 -11
- aprsd/threads/tx.py +32 -31
- aprsd/utils/keepalive_collector.py +7 -5
- aprsd/utils/package.py +176 -0
- {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/METADATA +48 -48
- {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/RECORD +37 -37
- {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/WHEEL +1 -1
- aprsd/client/aprsis.py +0 -183
- aprsd/client/base.py +0 -156
- aprsd/client/drivers/kiss.py +0 -144
- aprsd/client/factory.py +0 -91
- aprsd/client/fake.py +0 -49
- aprsd/client/kiss.py +0 -143
- {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/entry_points.txt +0 -0
- {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info/licenses}/AUTHORS +0 -0
- {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info/licenses}/LICENSE +0 -0
- {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/top_level.txt +0 -0
aprsd/utils/package.py
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
import fnmatch
|
2
|
+
import importlib
|
3
|
+
import inspect
|
4
|
+
import logging
|
5
|
+
import os
|
6
|
+
import pkgutil
|
7
|
+
import sys
|
8
|
+
from traceback import print_tb
|
9
|
+
|
10
|
+
import requests
|
11
|
+
from thesmuggler import smuggle
|
12
|
+
|
13
|
+
from aprsd import plugin as aprsd_plugin
|
14
|
+
|
15
|
+
LOG = logging.getLogger()
|
16
|
+
|
17
|
+
|
18
|
+
def onerror(name):
|
19
|
+
type, value, traceback = sys.exc_info()
|
20
|
+
print_tb(traceback)
|
21
|
+
|
22
|
+
|
23
|
+
def plugin_type(obj):
|
24
|
+
for c in inspect.getmro(obj):
|
25
|
+
if issubclass(c, aprsd_plugin.APRSDRegexCommandPluginBase):
|
26
|
+
return 'RegexCommand'
|
27
|
+
if issubclass(c, aprsd_plugin.APRSDWatchListPluginBase):
|
28
|
+
return 'WatchList'
|
29
|
+
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
30
|
+
return 'APRSDPluginBase'
|
31
|
+
|
32
|
+
return 'Unknown'
|
33
|
+
|
34
|
+
|
35
|
+
def is_plugin(obj):
|
36
|
+
for c in inspect.getmro(obj):
|
37
|
+
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
38
|
+
return True
|
39
|
+
|
40
|
+
return False
|
41
|
+
|
42
|
+
|
43
|
+
def walk_package(package):
|
44
|
+
return pkgutil.walk_packages(
|
45
|
+
package.__path__,
|
46
|
+
package.__name__ + '.',
|
47
|
+
onerror=onerror,
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
def get_module_info(package_name, module_name, module_path):
|
52
|
+
if not os.path.exists(module_path):
|
53
|
+
return None
|
54
|
+
|
55
|
+
dir_path = os.path.realpath(module_path)
|
56
|
+
pattern = '*.py'
|
57
|
+
|
58
|
+
obj_list = []
|
59
|
+
|
60
|
+
for path, _subdirs, files in os.walk(dir_path):
|
61
|
+
for name in files:
|
62
|
+
if fnmatch.fnmatch(name, pattern):
|
63
|
+
module = smuggle(f'{path}/{name}')
|
64
|
+
for mem_name, obj in inspect.getmembers(module):
|
65
|
+
if inspect.isclass(obj) and is_plugin(obj):
|
66
|
+
obj_list.append(
|
67
|
+
{
|
68
|
+
'package': package_name,
|
69
|
+
'name': mem_name,
|
70
|
+
'obj': obj,
|
71
|
+
'version': obj.version,
|
72
|
+
'path': f'{".".join([module_name, obj.__name__])}',
|
73
|
+
},
|
74
|
+
)
|
75
|
+
|
76
|
+
return obj_list
|
77
|
+
|
78
|
+
|
79
|
+
def is_aprsd_package(name):
|
80
|
+
if name.startswith('aprsd_'):
|
81
|
+
return True
|
82
|
+
|
83
|
+
|
84
|
+
def is_aprsd_extension(name):
|
85
|
+
if name.startswith('aprsd_') and 'extension' in name:
|
86
|
+
# This is an installed package that is an extension of
|
87
|
+
# APRSD
|
88
|
+
return True
|
89
|
+
else:
|
90
|
+
# We might have an editable install of an extension
|
91
|
+
# of APRSD.
|
92
|
+
return '__editable__' in name and 'aprsd_' in name and 'extension' in name
|
93
|
+
|
94
|
+
|
95
|
+
def get_installed_aprsd_items():
|
96
|
+
# installed plugins
|
97
|
+
plugins = {}
|
98
|
+
extensions = {}
|
99
|
+
for _finder, name, ispkg in pkgutil.iter_modules():
|
100
|
+
if ispkg and is_aprsd_package(name):
|
101
|
+
module = importlib.import_module(name)
|
102
|
+
pkgs = walk_package(module)
|
103
|
+
for pkg in pkgs:
|
104
|
+
pkg_info = get_module_info(
|
105
|
+
module.__name__, pkg.name, module.__path__[0]
|
106
|
+
)
|
107
|
+
if 'plugin' in name:
|
108
|
+
plugins[name] = pkg_info
|
109
|
+
elif 'extension' in name:
|
110
|
+
mod = importlib.import_module(name)
|
111
|
+
extensions[name] = mod
|
112
|
+
elif is_aprsd_extension(name):
|
113
|
+
# This isn't a package, so it could be an editable install
|
114
|
+
module = importlib.import_module(name)
|
115
|
+
key_name = next(iter(module.MAPPING.keys()))
|
116
|
+
module = importlib.import_module(key_name)
|
117
|
+
pkg_info = get_module_info(module.__name__, key_name, module.__path__[0])
|
118
|
+
extensions[key_name] = module
|
119
|
+
return plugins, extensions
|
120
|
+
|
121
|
+
|
122
|
+
def get_installed_plugins():
|
123
|
+
# installed plugins
|
124
|
+
plugins, _ = get_installed_aprsd_items()
|
125
|
+
return plugins
|
126
|
+
|
127
|
+
|
128
|
+
def get_installed_extensions():
|
129
|
+
# installed plugins
|
130
|
+
_, extensions = get_installed_aprsd_items()
|
131
|
+
return extensions
|
132
|
+
|
133
|
+
|
134
|
+
def get_pypi_packages():
|
135
|
+
if simple_r := requests.get(
|
136
|
+
'https://pypi.org/simple',
|
137
|
+
headers={'Accept': 'application/vnd.pypi.simple.v1+json'},
|
138
|
+
):
|
139
|
+
simple_response = simple_r.json()
|
140
|
+
else:
|
141
|
+
simple_response = {}
|
142
|
+
|
143
|
+
key = 'aprsd'
|
144
|
+
matches = [
|
145
|
+
p['name'] for p in simple_response['projects'] if p['name'].startswith(key)
|
146
|
+
]
|
147
|
+
|
148
|
+
packages = []
|
149
|
+
for pkg in matches:
|
150
|
+
# Get info for first match
|
151
|
+
if r := requests.get(
|
152
|
+
f'https://pypi.org/pypi/{pkg}/json',
|
153
|
+
headers={'Accept': 'application/json'},
|
154
|
+
):
|
155
|
+
packages.append(r.json())
|
156
|
+
|
157
|
+
return packages
|
158
|
+
|
159
|
+
|
160
|
+
def log_installed_extensions_and_plugins():
|
161
|
+
plugins, extensions = get_installed_aprsd_items()
|
162
|
+
|
163
|
+
for name in extensions:
|
164
|
+
ext = extensions[name]
|
165
|
+
# print(f"Extension: {ext}")
|
166
|
+
# print(f"Extension: {ext.__dict__}")
|
167
|
+
if hasattr(ext, '__version__'):
|
168
|
+
version = ext.__version__
|
169
|
+
elif hasattr(ext, 'version'):
|
170
|
+
version = ext.version
|
171
|
+
else:
|
172
|
+
version = ext['version']
|
173
|
+
LOG.info(f'Extension: {name} version: {version}')
|
174
|
+
|
175
|
+
for plugin in plugins:
|
176
|
+
LOG.info(f'Plugin: {plugin} version: {plugins[plugin][0]["version"]}')
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: aprsd
|
3
|
-
Version: 4.1
|
3
|
+
Version: 4.2.1
|
4
4
|
Summary: APRSd is a APRS-IS server that can be used to connect to APRS-IS and send and receive APRS packets.
|
5
5
|
Author-email: Craig Lamparter <craig@craiger.org>, "Walter A. Boring IV" <waboring@hemna.com>, Emre Saglam <emresaglam@gmail.com>, Jason Martin <jhmartin@toger.us>, John <johng42@users.noreply.github.com>, Martiros Shakhzadyan <vrzh@vrzh.net>, Zoe Moore <zoenb@mailbox.org>, ranguli <hello@joshmurphy.ca>
|
6
6
|
Maintainer-email: Craig Lamparter <craig@craiger.org>, "Walter A. Boring IV" <waboring@hemna.com>
|
@@ -202,84 +202,83 @@ Description-Content-Type: text/markdown
|
|
202
202
|
License-File: LICENSE
|
203
203
|
License-File: AUTHORS
|
204
204
|
Requires-Dist: aprslib==0.7.2
|
205
|
-
Requires-Dist: attrs==25.
|
205
|
+
Requires-Dist: attrs==25.3.0
|
206
206
|
Requires-Dist: ax253==0.1.5.post1
|
207
|
-
Requires-Dist: bitarray==3.1
|
208
|
-
Requires-Dist: certifi==2025.
|
209
|
-
Requires-Dist: charset-normalizer==3.4.
|
210
|
-
Requires-Dist: click==8.1
|
207
|
+
Requires-Dist: bitarray==3.6.1
|
208
|
+
Requires-Dist: certifi==2025.8.3
|
209
|
+
Requires-Dist: charset-normalizer==3.4.3
|
210
|
+
Requires-Dist: click==8.2.1
|
211
211
|
Requires-Dist: dataclasses-json==0.6.7
|
212
|
-
Requires-Dist: debtcollector==3.0.0
|
213
212
|
Requires-Dist: haversine==2.9.0
|
214
213
|
Requires-Dist: idna==3.10
|
215
|
-
Requires-Dist: importlib-metadata==8.
|
214
|
+
Requires-Dist: importlib-metadata==8.7.0
|
216
215
|
Requires-Dist: kiss3==8.0.0
|
217
216
|
Requires-Dist: loguru==0.7.3
|
218
|
-
Requires-Dist: markdown-it-py==
|
217
|
+
Requires-Dist: markdown-it-py==4.0.0
|
219
218
|
Requires-Dist: marshmallow==3.26.1
|
220
219
|
Requires-Dist: mdurl==0.1.2
|
221
|
-
Requires-Dist: mypy-extensions==1.
|
220
|
+
Requires-Dist: mypy-extensions==1.1.0
|
222
221
|
Requires-Dist: netaddr==1.3.0
|
223
|
-
Requires-Dist: oslo-config==
|
222
|
+
Requires-Dist: oslo-config==10.0.0
|
224
223
|
Requires-Dist: oslo-i18n==6.5.1
|
225
|
-
Requires-Dist: packaging==
|
224
|
+
Requires-Dist: packaging==25.0
|
226
225
|
Requires-Dist: pbr==6.1.1
|
227
|
-
Requires-Dist: pluggy==1.
|
228
|
-
Requires-Dist: pygments==2.19.
|
226
|
+
Requires-Dist: pluggy==1.6.0
|
227
|
+
Requires-Dist: pygments==2.19.2
|
229
228
|
Requires-Dist: pyserial==3.5
|
230
229
|
Requires-Dist: pyserial-asyncio==0.6
|
231
|
-
Requires-Dist: pytz==2025.
|
230
|
+
Requires-Dist: pytz==2025.2
|
232
231
|
Requires-Dist: pyyaml==6.0.2
|
233
|
-
Requires-Dist: requests==2.32.
|
232
|
+
Requires-Dist: requests==2.32.4
|
234
233
|
Requires-Dist: rfc3986==2.0.0
|
235
|
-
Requires-Dist: rich==
|
234
|
+
Requires-Dist: rich==14.1.0
|
236
235
|
Requires-Dist: rush==2021.4.0
|
237
|
-
Requires-Dist: setuptools==
|
236
|
+
Requires-Dist: setuptools==80.9.0
|
238
237
|
Requires-Dist: stevedore==5.4.1
|
239
238
|
Requires-Dist: thesmuggler==1.0.1
|
240
239
|
Requires-Dist: timeago==1.0.16
|
241
|
-
Requires-Dist: typing-extensions==4.
|
240
|
+
Requires-Dist: typing-extensions==4.14.1
|
242
241
|
Requires-Dist: typing-inspect==0.9.0
|
243
|
-
Requires-Dist: tzlocal==5.3
|
242
|
+
Requires-Dist: tzlocal==5.3.1
|
244
243
|
Requires-Dist: update-checker==0.18.0
|
245
|
-
Requires-Dist: urllib3==2.
|
246
|
-
Requires-Dist: wrapt==1.17.
|
247
|
-
Requires-Dist: zipp==3.
|
244
|
+
Requires-Dist: urllib3==2.5.0
|
245
|
+
Requires-Dist: wrapt==1.17.3
|
246
|
+
Requires-Dist: zipp==3.23.0
|
248
247
|
Provides-Extra: dev
|
249
248
|
Requires-Dist: alabaster==1.0.0; extra == "dev"
|
250
249
|
Requires-Dist: babel==2.17.0; extra == "dev"
|
251
|
-
Requires-Dist: build==1.
|
252
|
-
Requires-Dist: cachetools==
|
253
|
-
Requires-Dist: certifi==2025.
|
250
|
+
Requires-Dist: build==1.3.0; extra == "dev"
|
251
|
+
Requires-Dist: cachetools==6.1.0; extra == "dev"
|
252
|
+
Requires-Dist: certifi==2025.8.3; extra == "dev"
|
254
253
|
Requires-Dist: cfgv==3.4.0; extra == "dev"
|
255
254
|
Requires-Dist: chardet==5.2.0; extra == "dev"
|
256
|
-
Requires-Dist: charset-normalizer==3.4.
|
257
|
-
Requires-Dist: click==8.1
|
255
|
+
Requires-Dist: charset-normalizer==3.4.3; extra == "dev"
|
256
|
+
Requires-Dist: click==8.2.1; extra == "dev"
|
258
257
|
Requires-Dist: colorama==0.4.6; extra == "dev"
|
259
|
-
Requires-Dist: distlib==0.
|
258
|
+
Requires-Dist: distlib==0.4.0; extra == "dev"
|
260
259
|
Requires-Dist: docutils==0.21.2; extra == "dev"
|
261
|
-
Requires-Dist: filelock==3.
|
262
|
-
Requires-Dist: identify==2.6.
|
260
|
+
Requires-Dist: filelock==3.18.0; extra == "dev"
|
261
|
+
Requires-Dist: identify==2.6.13; extra == "dev"
|
263
262
|
Requires-Dist: idna==3.10; extra == "dev"
|
264
263
|
Requires-Dist: imagesize==1.4.1; extra == "dev"
|
265
|
-
Requires-Dist: jinja2==3.1.
|
264
|
+
Requires-Dist: jinja2==3.1.6; extra == "dev"
|
266
265
|
Requires-Dist: m2r==0.3.1; extra == "dev"
|
267
266
|
Requires-Dist: markupsafe==3.0.2; extra == "dev"
|
268
267
|
Requires-Dist: mistune==0.8.4; extra == "dev"
|
269
268
|
Requires-Dist: nodeenv==1.9.1; extra == "dev"
|
270
|
-
Requires-Dist: packaging==
|
271
|
-
Requires-Dist: pip==25.
|
272
|
-
Requires-Dist: pip-tools==7.
|
273
|
-
Requires-Dist: platformdirs==4.3.
|
274
|
-
Requires-Dist: pluggy==1.
|
275
|
-
Requires-Dist: pre-commit==4.
|
276
|
-
Requires-Dist: pygments==2.19.
|
277
|
-
Requires-Dist: pyproject-api==1.9.
|
269
|
+
Requires-Dist: packaging==25.0; extra == "dev"
|
270
|
+
Requires-Dist: pip==25.2; extra == "dev"
|
271
|
+
Requires-Dist: pip-tools==7.5.0; extra == "dev"
|
272
|
+
Requires-Dist: platformdirs==4.3.8; extra == "dev"
|
273
|
+
Requires-Dist: pluggy==1.6.0; extra == "dev"
|
274
|
+
Requires-Dist: pre-commit==4.3.0; extra == "dev"
|
275
|
+
Requires-Dist: pygments==2.19.2; extra == "dev"
|
276
|
+
Requires-Dist: pyproject-api==1.9.1; extra == "dev"
|
278
277
|
Requires-Dist: pyproject-hooks==1.2.0; extra == "dev"
|
279
278
|
Requires-Dist: pyyaml==6.0.2; extra == "dev"
|
280
|
-
Requires-Dist: requests==2.32.
|
281
|
-
Requires-Dist: setuptools==
|
282
|
-
Requires-Dist: snowballstemmer==
|
279
|
+
Requires-Dist: requests==2.32.4; extra == "dev"
|
280
|
+
Requires-Dist: setuptools==80.9.0; extra == "dev"
|
281
|
+
Requires-Dist: snowballstemmer==3.0.1; extra == "dev"
|
283
282
|
Requires-Dist: sphinx==8.1.3; extra == "dev"
|
284
283
|
Requires-Dist: sphinxcontrib-applehelp==2.0.0; extra == "dev"
|
285
284
|
Requires-Dist: sphinxcontrib-devhelp==2.0.0; extra == "dev"
|
@@ -288,11 +287,12 @@ Requires-Dist: sphinxcontrib-jsmath==1.0.1; extra == "dev"
|
|
288
287
|
Requires-Dist: sphinxcontrib-qthelp==2.0.0; extra == "dev"
|
289
288
|
Requires-Dist: sphinxcontrib-serializinghtml==2.0.0; extra == "dev"
|
290
289
|
Requires-Dist: tomli==2.2.1; extra == "dev"
|
291
|
-
Requires-Dist: tox==4.
|
292
|
-
Requires-Dist: typing-extensions==4.
|
293
|
-
Requires-Dist: urllib3==2.
|
294
|
-
Requires-Dist: virtualenv==20.
|
290
|
+
Requires-Dist: tox==4.28.4; extra == "dev"
|
291
|
+
Requires-Dist: typing-extensions==4.14.1; extra == "dev"
|
292
|
+
Requires-Dist: urllib3==2.5.0; extra == "dev"
|
293
|
+
Requires-Dist: virtualenv==20.33.1; extra == "dev"
|
295
294
|
Requires-Dist: wheel==0.45.1; extra == "dev"
|
295
|
+
Dynamic: license-file
|
296
296
|
|
297
297
|
# APRSD - Ham radio APRS-IS Message platform software
|
298
298
|
|
@@ -1,42 +1,41 @@
|
|
1
1
|
aprsd/__init__.py,sha256=ci_49KK2a4GXyxcM2lFZfNAOsBfXzh0yayIGQazw56I,687
|
2
2
|
aprsd/cli_helper.py,sha256=mKHww_cStwFBThjntFylrSnF6n9jOPHlR8r9SsrCxdY,4605
|
3
|
-
aprsd/exception.py,sha256=
|
4
|
-
aprsd/main.py,sha256=
|
5
|
-
aprsd/plugin.py,sha256=
|
6
|
-
aprsd/plugin_utils.py,sha256=
|
7
|
-
aprsd/client/__init__.py,sha256=
|
8
|
-
aprsd/client/
|
9
|
-
aprsd/client/
|
10
|
-
aprsd/client/
|
11
|
-
aprsd/client/
|
12
|
-
aprsd/client/
|
13
|
-
aprsd/client/
|
14
|
-
aprsd/client/drivers/
|
15
|
-
aprsd/client/drivers/
|
16
|
-
aprsd/client/drivers/
|
17
|
-
aprsd/client/drivers/kiss.py,sha256=vS6bnp_6cf5gi6vKKgeKloUMseEbLiB5KOvWsnDHNF4,4193
|
3
|
+
aprsd/exception.py,sha256=bzln7sP9USQ67tz5nQh6e4KMaS8X2uhPJMNVmCkKV30,679
|
4
|
+
aprsd/main.py,sha256=sOzG4tKtm7FGxwvYWIwiPujzo5TV3F0cSkR7IWHlb8M,4719
|
5
|
+
aprsd/plugin.py,sha256=pdyvKhSugFo4vRy7VflnaZ-BKozGa3GTTAHErskW3WU,17848
|
6
|
+
aprsd/plugin_utils.py,sha256=beXQ1n_YI38rqDwswtE8NjFwFzjpaeve9W_JotX1n1A,2544
|
7
|
+
aprsd/client/__init__.py,sha256=lh7mKBoSo2Tt82QoqlAARGm4LY6ffsn7d8x6M4B7n9g,154
|
8
|
+
aprsd/client/client.py,sha256=7Y79BDaD0S28lziTgD6xl9C4f6rRrOgLJXNx_c__Wa8,4430
|
9
|
+
aprsd/client/stats.py,sha256=dUqRZao04B2TTj9TGMSygdLHa3if3pEy86eK-fK4sj4,353
|
10
|
+
aprsd/client/drivers/__init__.py,sha256=IWSSrAEMrJQzulCIAx-N7Du0HK9sQOXIh98aJRCtfdg,421
|
11
|
+
aprsd/client/drivers/aprsis.py,sha256=8VhKjnfAfxaXdBLKvC4guTU2WQsdHAvIogoxHX57gxc,6651
|
12
|
+
aprsd/client/drivers/fake.py,sha256=b3q9-4O9Gtd7XnEzgZ2NzR5Zv6szxKBe07nsXJXmSHw,3249
|
13
|
+
aprsd/client/drivers/registry.py,sha256=AWDWqxjOYHDz8ow4fNSSJ7JR6Tgy1axG6r0I9Yk2nJ8,2213
|
14
|
+
aprsd/client/drivers/tcpkiss.py,sha256=cjdVL88aeOst7tlxZ15ti5EBJNiXqvWHWFVrZMrk23Y,13527
|
15
|
+
aprsd/client/drivers/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
+
aprsd/client/drivers/lib/aprslib.py,sha256=9KTEKlivGT1f1js-u-w1WNLhOs1kQxkr3COa1CI95oE,9439
|
18
17
|
aprsd/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
18
|
aprsd/cmds/completion.py,sha256=BMM8dSYUihYmj2fSsefo6LzgJZKbxHnNfRIhwVZW_U8,816
|
20
|
-
aprsd/cmds/dev.py,sha256=
|
21
|
-
aprsd/cmds/fetch_stats.py,sha256=
|
19
|
+
aprsd/cmds/dev.py,sha256=9znqGv8qUd1hRV_-Iit8ktxFhYFIIB4rOnluhurrv28,4296
|
20
|
+
aprsd/cmds/fetch_stats.py,sha256=L8l-43GfZiWu9c9BgxpUV1PdrB8IG8z1FlsryAvPeAI,9684
|
22
21
|
aprsd/cmds/healthcheck.py,sha256=swXb4LE1qdr3Z-417fv4LhWvscE4WUrqF9So1sWXiCU,2635
|
23
|
-
aprsd/cmds/list_plugins.py,sha256=
|
24
|
-
aprsd/cmds/listen.py,sha256=
|
25
|
-
aprsd/cmds/send_message.py,sha256=
|
26
|
-
aprsd/cmds/server.py,sha256=
|
22
|
+
aprsd/cmds/list_plugins.py,sha256=PzvoyXfV26xAiTHtgSiJyWAmdPd2BEua5ta5fGuspVQ,7010
|
23
|
+
aprsd/cmds/listen.py,sha256=3M8eXIneEuM5wfskAFFYUZSfHTQveTAFSmnRZAfe9WY,9519
|
24
|
+
aprsd/cmds/send_message.py,sha256=EVJ-VayxcSNYAJ8dM8-LM9HLbsanC2mOWA6bfZwcLn4,4830
|
25
|
+
aprsd/cmds/server.py,sha256=hB3Tu0MbzEvg4XkoCFomvfMZqIitDKdIejQ6uwHDm0o,4138
|
27
26
|
aprsd/conf/__init__.py,sha256=2ikrZahcS2EULQEJ5xErnzMVR2vcsDdMvixEPuNE-xE,1678
|
28
27
|
aprsd/conf/client.py,sha256=9oheIUnotnbHmpKtxlw-00quW0o-3pZYtFmaim9bsjs,3013
|
29
|
-
aprsd/conf/common.py,sha256=
|
28
|
+
aprsd/conf/common.py,sha256=l5tUw6G5CNOiFMZQPoStkpsNa3d9xr4BunzZK0FNgV8,7605
|
30
29
|
aprsd/conf/log.py,sha256=qBoF8ptGHK7G6NPHNZCE2PBH7S-L587wFkT-9ikUtfE,1656
|
31
30
|
aprsd/conf/opts.py,sha256=kbZELePpBDoQrKbowbWHgmLksbREARU8T8UhX9FIhq8,2725
|
32
31
|
aprsd/conf/plugin_common.py,sha256=H7LxiJL0Sl1D_dpAmJHEHSbPxaOK938M1WCOcTpOYN8,1990
|
33
32
|
aprsd/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
|
-
aprsd/log/log.py,sha256=
|
33
|
+
aprsd/log/log.py,sha256=zldqJCHj5J-JRTnVsXkLepmhFkPYr9wQWi3RWKMmFMI,3099
|
35
34
|
aprsd/packets/__init__.py,sha256=IW61OJOAJ8_Deio72OEOngDuHHm7KzHjj5Bergr7e5g,1110
|
36
35
|
aprsd/packets/collector.py,sha256=VhuHj2CRxVtaC6QDKiYMnxRzNVj-fc7wkMyPpTl83Og,2322
|
37
|
-
aprsd/packets/core.py,sha256=
|
36
|
+
aprsd/packets/core.py,sha256=B_eTvBkUe5EMqGAaLru5F_qbb4tO_qJ4MV4Z0WiT9NQ,27807
|
38
37
|
aprsd/packets/filter.py,sha256=RmYSNepi_kxK2PeQwrRODQcml5yfUdJpBasuQw4W7Q4,2135
|
39
|
-
aprsd/packets/log.py,sha256=
|
38
|
+
aprsd/packets/log.py,sha256=R0hSg-SHBBHvwbnJklKBm1HHVXvBzaswfMyq7QUzdE4,5568
|
40
39
|
aprsd/packets/packet_list.py,sha256=BnaFokRiPOP9jUW0sXTQPXUquhiQ0qw8gt2Dh0JWAIM,3506
|
41
40
|
aprsd/packets/seen_list.py,sha256=fV87eyXeFxaZKqWfY1GOCQ6zfnjI1Or6i3KS_qJLuoA,1375
|
42
41
|
aprsd/packets/tracker.py,sha256=Fr7B9Ex2kikmAkUz0HNUEsx9p60ap9L7A96bkv9WaO0,2950
|
@@ -50,30 +49,31 @@ aprsd/plugins/notify.py,sha256=MHR9y_iCokOSNShI7DjW93tsP0tFj8_1hHf9elMjASo,1922
|
|
50
49
|
aprsd/plugins/ping.py,sha256=A3yLMPUDU-PA1Q98xStd0WM04MZvGn3FzVpGKjyoXDc,763
|
51
50
|
aprsd/plugins/time.py,sha256=59-tMl7ogccLobWl9hMDm8mUVXdqW6kB5javrtp0rok,3535
|
52
51
|
aprsd/plugins/version.py,sha256=NAPFKQ0lQqgjoDyTVy8tZcRGmdcJE_ZogRElDz27V2U,839
|
53
|
-
aprsd/plugins/weather.py,sha256=
|
52
|
+
aprsd/plugins/weather.py,sha256=XOspEoAumUb35TW2RYqjRV0lp6hrR_kOMIT41iEUGQQ,13399
|
54
53
|
aprsd/stats/__init__.py,sha256=ltAtUiEvpokBEtOpq0sxpDGOLQT2evgeZSVBzDzjkSo,808
|
55
54
|
aprsd/stats/app.py,sha256=axqMA137zKqU03yO8XI5f1QE8ajmr9YK0J9O9m4iSzo,1378
|
56
|
-
aprsd/stats/collector.py,sha256=
|
55
|
+
aprsd/stats/collector.py,sha256=7nx_KSP_PUeuX5q8xJhsB9G_JfgQFCAvrPNzO7uK4FM,1502
|
57
56
|
aprsd/threads/__init__.py,sha256=KPqAOhS4q995NCEWDnPCHiuu3guyuMZlyDUGx_h50z8,259
|
58
57
|
aprsd/threads/aprsd.py,sha256=13AS7jAhcQBmTervBiADaC1Ins9C-6TrMDYjW1fQJkg,4794
|
59
58
|
aprsd/threads/keepalive.py,sha256=E48_erNnqik5AAllMGx5gexAwPIaznCp0HCG45NltFw,3939
|
60
59
|
aprsd/threads/registry.py,sha256=zWG4-SMBSx46NY93__Bt_F8vFrljxQvMHbi80WP5kY8,1690
|
61
|
-
aprsd/threads/rx.py,sha256=
|
60
|
+
aprsd/threads/rx.py,sha256=u0W3YgU83OaZcKyw60C3B-Chb6LXYXpDv9RsXKSKHcg,14937
|
62
61
|
aprsd/threads/service.py,sha256=fGjyr34i2-CJdBynxachNaBrBsULwmAnewLHdTujfmk,1375
|
63
62
|
aprsd/threads/stats.py,sha256=drKqHV2WxgXhyWXGrmRHQx7oKlrJ9bwEMtCInCzulPY,884
|
64
|
-
aprsd/threads/tx.py,sha256=
|
63
|
+
aprsd/threads/tx.py,sha256=spuWk4E5HvTWYNyhKB1kxqVbVvxNapzYvi9tNScTAHs,9264
|
65
64
|
aprsd/utils/__init__.py,sha256=TsuSiHFK5VQW6w4Q1epTOyfnr58n4ZVKNe4obbxl-5Y,7333
|
66
65
|
aprsd/utils/counter.py,sha256=GQH2E1FEBshR5PROhCNS13I6CaYZLW7VJumaYd7yQcY,1378
|
67
66
|
aprsd/utils/fuzzyclock.py,sha256=qKV8SYZhQGOHG9biF8TeueLb6RMppspx1Zg4IOy1Z10,3265
|
68
67
|
aprsd/utils/json.py,sha256=eHoBfXGcchO4Q1MXj6uKK9YU-H8HKPJ2cThLZ1dM_vo,2578
|
69
|
-
aprsd/utils/keepalive_collector.py,sha256=
|
68
|
+
aprsd/utils/keepalive_collector.py,sha256=r0oKCpKPGsivOKTQkTfwcJRESqMfbNawCyqrcHoJo8M,1807
|
70
69
|
aprsd/utils/objectstore.py,sha256=0OivUeagncWGH7eWjTwZhauf-etweTabp8Oykt0hoF4,3541
|
70
|
+
aprsd/utils/package.py,sha256=BtyIT6F5tPTFIATy5zDtYnB2k-rSFey5K2t9RPGenck,5025
|
71
71
|
aprsd/utils/ring_buffer.py,sha256=lWWuw7lEbc2URhqAJfRLjpXBDLiK6UUWzk3j2VFnERQ,1111
|
72
72
|
aprsd/utils/trace.py,sha256=lIuDZHOjvWbL2IMJ2tN_XW4Ch8oe4uQ7uLGylUByli0,5687
|
73
|
-
aprsd-4.1.
|
74
|
-
aprsd-4.1.
|
75
|
-
aprsd-4.1.
|
76
|
-
aprsd-4.1.
|
77
|
-
aprsd-4.1.
|
78
|
-
aprsd-4.1.
|
79
|
-
aprsd-4.1.
|
73
|
+
aprsd-4.2.1.dist-info/licenses/AUTHORS,sha256=fGZhgXFMCfDPbp0hHllwmPyfiKPobLNA3sJA3BMIJVE,22
|
74
|
+
aprsd-4.2.1.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
|
75
|
+
aprsd-4.2.1.dist-info/METADATA,sha256=TKX8daOdMLu42sayX71q88zaAZfCBy_e3gyjsBOsBeg,45583
|
76
|
+
aprsd-4.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
77
|
+
aprsd-4.2.1.dist-info/entry_points.txt,sha256=4fReoJUB-bFqOUK6eeXYYCvTdVLprL7KVH0hWQRP9eM,171
|
78
|
+
aprsd-4.2.1.dist-info/top_level.txt,sha256=v1O96niUcJOTMh9aQnFRknbScJ6mMOwqurdbxeaeSjs,6
|
79
|
+
aprsd-4.2.1.dist-info/RECORD,,
|
aprsd/client/aprsis.py
DELETED
@@ -1,183 +0,0 @@
|
|
1
|
-
import datetime
|
2
|
-
import logging
|
3
|
-
import time
|
4
|
-
|
5
|
-
import timeago
|
6
|
-
from aprslib.exceptions import LoginError
|
7
|
-
from loguru import logger
|
8
|
-
from oslo_config import cfg
|
9
|
-
|
10
|
-
from aprsd import client, exception
|
11
|
-
from aprsd.client import base
|
12
|
-
from aprsd.client.drivers import aprsis
|
13
|
-
from aprsd.packets import core
|
14
|
-
|
15
|
-
CONF = cfg.CONF
|
16
|
-
LOG = logging.getLogger("APRSD")
|
17
|
-
LOGU = logger
|
18
|
-
|
19
|
-
|
20
|
-
class APRSISClient(base.APRSClient):
|
21
|
-
_client = None
|
22
|
-
_checks = False
|
23
|
-
|
24
|
-
def __init__(self):
|
25
|
-
max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0}
|
26
|
-
self.max_delta = datetime.timedelta(**max_timeout)
|
27
|
-
|
28
|
-
def stats(self, serializable=False) -> dict:
|
29
|
-
stats = {}
|
30
|
-
if self.is_configured():
|
31
|
-
if self._client:
|
32
|
-
keepalive = self._client.aprsd_keepalive
|
33
|
-
server_string = self._client.server_string
|
34
|
-
if serializable:
|
35
|
-
keepalive = keepalive.isoformat()
|
36
|
-
else:
|
37
|
-
keepalive = "None"
|
38
|
-
server_string = "None"
|
39
|
-
stats = {
|
40
|
-
"connected": self.is_connected,
|
41
|
-
"filter": self.filter,
|
42
|
-
"login_status": self.login_status,
|
43
|
-
"connection_keepalive": keepalive,
|
44
|
-
"server_string": server_string,
|
45
|
-
"transport": self.transport(),
|
46
|
-
}
|
47
|
-
|
48
|
-
return stats
|
49
|
-
|
50
|
-
def keepalive_check(self):
|
51
|
-
# Don't check the first time through.
|
52
|
-
if not self.is_alive() and self._checks:
|
53
|
-
LOG.warning("Resetting client. It's not alive.")
|
54
|
-
self.reset()
|
55
|
-
self._checks = True
|
56
|
-
|
57
|
-
def keepalive_log(self):
|
58
|
-
if ka := self._client.aprsd_keepalive:
|
59
|
-
keepalive = timeago.format(ka)
|
60
|
-
else:
|
61
|
-
keepalive = "N/A"
|
62
|
-
LOGU.opt(colors=True).info(f"<green>Client keepalive {keepalive}</green>")
|
63
|
-
|
64
|
-
@staticmethod
|
65
|
-
def is_enabled():
|
66
|
-
# Defaults to True if the enabled flag is non existent
|
67
|
-
try:
|
68
|
-
return CONF.aprs_network.enabled
|
69
|
-
except KeyError:
|
70
|
-
return False
|
71
|
-
|
72
|
-
@staticmethod
|
73
|
-
def is_configured():
|
74
|
-
if APRSISClient.is_enabled():
|
75
|
-
# Ensure that the config vars are correctly set
|
76
|
-
if not CONF.aprs_network.login:
|
77
|
-
LOG.error("Config aprs_network.login not set.")
|
78
|
-
raise exception.MissingConfigOptionException(
|
79
|
-
"aprs_network.login is not set.",
|
80
|
-
)
|
81
|
-
if not CONF.aprs_network.password:
|
82
|
-
LOG.error("Config aprs_network.password not set.")
|
83
|
-
raise exception.MissingConfigOptionException(
|
84
|
-
"aprs_network.password is not set.",
|
85
|
-
)
|
86
|
-
if not CONF.aprs_network.host:
|
87
|
-
LOG.error("Config aprs_network.host not set.")
|
88
|
-
raise exception.MissingConfigOptionException(
|
89
|
-
"aprs_network.host is not set.",
|
90
|
-
)
|
91
|
-
|
92
|
-
return True
|
93
|
-
return True
|
94
|
-
|
95
|
-
def _is_stale_connection(self):
|
96
|
-
delta = datetime.datetime.now() - self._client.aprsd_keepalive
|
97
|
-
if delta > self.max_delta:
|
98
|
-
LOG.error(f"Connection is stale, last heard {delta} ago.")
|
99
|
-
return True
|
100
|
-
return False
|
101
|
-
|
102
|
-
def is_alive(self):
|
103
|
-
if not self._client:
|
104
|
-
LOG.warning(f"APRS_CLIENT {self._client} alive? NO!!!")
|
105
|
-
return False
|
106
|
-
return self._client.is_alive() and not self._is_stale_connection()
|
107
|
-
|
108
|
-
def close(self):
|
109
|
-
if self._client:
|
110
|
-
self._client.stop()
|
111
|
-
self._client.close()
|
112
|
-
|
113
|
-
@staticmethod
|
114
|
-
def transport():
|
115
|
-
return client.TRANSPORT_APRSIS
|
116
|
-
|
117
|
-
def decode_packet(self, *args, **kwargs):
|
118
|
-
"""APRS lib already decodes this."""
|
119
|
-
return core.factory(args[0])
|
120
|
-
|
121
|
-
def setup_connection(self):
|
122
|
-
user = CONF.aprs_network.login
|
123
|
-
password = CONF.aprs_network.password
|
124
|
-
host = CONF.aprs_network.host
|
125
|
-
port = CONF.aprs_network.port
|
126
|
-
self.connected = False
|
127
|
-
backoff = 1
|
128
|
-
aprs_client = None
|
129
|
-
retries = 3
|
130
|
-
retry_count = 0
|
131
|
-
while not self.connected:
|
132
|
-
retry_count += 1
|
133
|
-
if retry_count >= retries:
|
134
|
-
break
|
135
|
-
try:
|
136
|
-
LOG.info(
|
137
|
-
f"Creating aprslib client({host}:{port}) and logging in {user}."
|
138
|
-
)
|
139
|
-
aprs_client = aprsis.Aprsdis(
|
140
|
-
user, passwd=password, host=host, port=port
|
141
|
-
)
|
142
|
-
# Force the log to be the same
|
143
|
-
aprs_client.logger = LOG
|
144
|
-
aprs_client.connect()
|
145
|
-
self.connected = self.login_status["success"] = True
|
146
|
-
self.login_status["message"] = aprs_client.server_string
|
147
|
-
backoff = 1
|
148
|
-
except LoginError as e:
|
149
|
-
LOG.error(f"Failed to login to APRS-IS Server '{e}'")
|
150
|
-
self.connected = self.login_status["success"] = False
|
151
|
-
self.login_status["message"] = e.message
|
152
|
-
LOG.error(e.message)
|
153
|
-
time.sleep(backoff)
|
154
|
-
except Exception as e:
|
155
|
-
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
156
|
-
self.connected = self.login_status["success"] = False
|
157
|
-
self.login_status["message"] = e.message
|
158
|
-
time.sleep(backoff)
|
159
|
-
# Don't allow the backoff to go to inifinity.
|
160
|
-
if backoff > 5:
|
161
|
-
backoff = 5
|
162
|
-
else:
|
163
|
-
backoff += 1
|
164
|
-
continue
|
165
|
-
self._client = aprs_client
|
166
|
-
return aprs_client
|
167
|
-
|
168
|
-
def consumer(self, callback, blocking=False, immortal=False, raw=False):
|
169
|
-
if self._client:
|
170
|
-
try:
|
171
|
-
self._client.consumer(
|
172
|
-
callback,
|
173
|
-
blocking=blocking,
|
174
|
-
immortal=immortal,
|
175
|
-
raw=raw,
|
176
|
-
)
|
177
|
-
except Exception as e:
|
178
|
-
LOG.error(e)
|
179
|
-
LOG.info(e.__cause__)
|
180
|
-
raise e
|
181
|
-
else:
|
182
|
-
LOG.warning("client is None, might be resetting.")
|
183
|
-
self.connected = False
|