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.
Files changed (43) hide show
  1. aprsd/client/__init__.py +5 -13
  2. aprsd/client/client.py +156 -0
  3. aprsd/client/drivers/__init__.py +10 -0
  4. aprsd/client/drivers/aprsis.py +174 -255
  5. aprsd/client/drivers/fake.py +59 -11
  6. aprsd/client/drivers/lib/__init__.py +0 -0
  7. aprsd/client/drivers/lib/aprslib.py +296 -0
  8. aprsd/client/drivers/registry.py +86 -0
  9. aprsd/client/drivers/tcpkiss.py +423 -0
  10. aprsd/client/stats.py +2 -2
  11. aprsd/cmds/dev.py +6 -4
  12. aprsd/cmds/fetch_stats.py +2 -0
  13. aprsd/cmds/list_plugins.py +6 -133
  14. aprsd/cmds/listen.py +5 -3
  15. aprsd/cmds/send_message.py +8 -5
  16. aprsd/cmds/server.py +7 -11
  17. aprsd/conf/common.py +7 -1
  18. aprsd/exception.py +7 -0
  19. aprsd/log/log.py +1 -1
  20. aprsd/main.py +0 -7
  21. aprsd/packets/core.py +168 -169
  22. aprsd/packets/log.py +69 -59
  23. aprsd/plugin.py +3 -2
  24. aprsd/plugin_utils.py +2 -2
  25. aprsd/plugins/weather.py +2 -2
  26. aprsd/stats/collector.py +5 -4
  27. aprsd/threads/rx.py +13 -11
  28. aprsd/threads/tx.py +32 -31
  29. aprsd/utils/keepalive_collector.py +7 -5
  30. aprsd/utils/package.py +176 -0
  31. {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/METADATA +48 -48
  32. {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/RECORD +37 -37
  33. {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/WHEEL +1 -1
  34. aprsd/client/aprsis.py +0 -183
  35. aprsd/client/base.py +0 -156
  36. aprsd/client/drivers/kiss.py +0 -144
  37. aprsd/client/factory.py +0 -91
  38. aprsd/client/fake.py +0 -49
  39. aprsd/client/kiss.py +0 -143
  40. {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info}/entry_points.txt +0 -0
  41. {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info/licenses}/AUTHORS +0 -0
  42. {aprsd-4.1.2.dist-info → aprsd-4.2.1.dist-info/licenses}/LICENSE +0 -0
  43. {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.2
1
+ Metadata-Version: 2.4
2
2
  Name: aprsd
3
- Version: 4.1.2
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.1.0
205
+ Requires-Dist: attrs==25.3.0
206
206
  Requires-Dist: ax253==0.1.5.post1
207
- Requires-Dist: bitarray==3.1.0
208
- Requires-Dist: certifi==2025.1.31
209
- Requires-Dist: charset-normalizer==3.4.1
210
- Requires-Dist: click==8.1.8
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.6.1
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==3.0.0
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.0.0
220
+ Requires-Dist: mypy-extensions==1.1.0
222
221
  Requires-Dist: netaddr==1.3.0
223
- Requires-Dist: oslo-config==9.7.1
222
+ Requires-Dist: oslo-config==10.0.0
224
223
  Requires-Dist: oslo-i18n==6.5.1
225
- Requires-Dist: packaging==24.2
224
+ Requires-Dist: packaging==25.0
226
225
  Requires-Dist: pbr==6.1.1
227
- Requires-Dist: pluggy==1.5.0
228
- Requires-Dist: pygments==2.19.1
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.1
230
+ Requires-Dist: pytz==2025.2
232
231
  Requires-Dist: pyyaml==6.0.2
233
- Requires-Dist: requests==2.32.3
232
+ Requires-Dist: requests==2.32.4
234
233
  Requires-Dist: rfc3986==2.0.0
235
- Requires-Dist: rich==13.9.4
234
+ Requires-Dist: rich==14.1.0
236
235
  Requires-Dist: rush==2021.4.0
237
- Requires-Dist: setuptools==75.8.2
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.12.2
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.3.0
246
- Requires-Dist: wrapt==1.17.2
247
- Requires-Dist: zipp==3.21.0
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.2.2.post1; extra == "dev"
252
- Requires-Dist: cachetools==5.5.2; extra == "dev"
253
- Requires-Dist: certifi==2025.1.31; extra == "dev"
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.1; extra == "dev"
257
- Requires-Dist: click==8.1.8; extra == "dev"
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.3.9; extra == "dev"
258
+ Requires-Dist: distlib==0.4.0; extra == "dev"
260
259
  Requires-Dist: docutils==0.21.2; extra == "dev"
261
- Requires-Dist: filelock==3.17.0; extra == "dev"
262
- Requires-Dist: identify==2.6.8; extra == "dev"
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.5; extra == "dev"
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==24.2; extra == "dev"
271
- Requires-Dist: pip==25.0.1; extra == "dev"
272
- Requires-Dist: pip-tools==7.4.1; extra == "dev"
273
- Requires-Dist: platformdirs==4.3.6; extra == "dev"
274
- Requires-Dist: pluggy==1.5.0; extra == "dev"
275
- Requires-Dist: pre-commit==4.1.0; extra == "dev"
276
- Requires-Dist: pygments==2.19.1; extra == "dev"
277
- Requires-Dist: pyproject-api==1.9.0; extra == "dev"
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.3; extra == "dev"
281
- Requires-Dist: setuptools==75.8.2; extra == "dev"
282
- Requires-Dist: snowballstemmer==2.2.0; extra == "dev"
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.24.1; extra == "dev"
292
- Requires-Dist: typing-extensions==4.12.2; extra == "dev"
293
- Requires-Dist: urllib3==2.3.0; extra == "dev"
294
- Requires-Dist: virtualenv==20.29.2; extra == "dev"
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=Oi6w0ISPemX0UpLB40BYnZBT1YOU-Pnpj8yf-A0E4-E,504
4
- aprsd/main.py,sha256=Mkovb7a5NaOKO-a0brnT-lWsz-nSucidEIE1x-XfU4s,4855
5
- aprsd/plugin.py,sha256=ZeGuXUlmBKkixQ_kZEjXzUCekJkIiWTCcPma63OkT1Y,17836
6
- aprsd/plugin_utils.py,sha256=2NUfNYA3XXOUoqI1QtM7YfPZtGnTVUfwY9l5iNhe5nE,2544
7
- aprsd/client/__init__.py,sha256=aajKndFPk259RLTUuobVDleJmOENHvdRVodfhY5BmX8,348
8
- aprsd/client/aprsis.py,sha256=ncGzA5q_3x2BsjEXP8WaZliGL0lhqwz9OrWhwDFvB7E,6181
9
- aprsd/client/base.py,sha256=PiNHJw8UE0sc3a8Y993FVmiPHO8yfxhR-ArGqr2e8sM,3842
10
- aprsd/client/factory.py,sha256=uuQmUreEKo8dVqqYQTZWtha7E5gVATAQGwB6W56a4Zk,2305
11
- aprsd/client/fake.py,sha256=RVWqvNVFoSGGiqPC_Z4OHdgX6uSnZ9Y8hxlDFABhPo8,1119
12
- aprsd/client/kiss.py,sha256=dVpQ2UM2LPmAMm6ue-AsHh97pyk9YR6OZKtaO6F4spw,4588
13
- aprsd/client/stats.py,sha256=VRexKAS3yD-kXyR3Xwe-lArAKx5BmrKYafrLdSeXr_s,351
14
- aprsd/client/drivers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- aprsd/client/drivers/aprsis.py,sha256=GBuZWRUkRyCB6nY-YI1HRbsYqrtV5m8HCUEDmRFnNp0,9103
16
- aprsd/client/drivers/fake.py,sha256=doDPjkdAIjT05zpXwiefrcMQgwNB5ZUu9MelDmm24Oo,2126
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=MTy6O85ZdptfPmyw4COWhU0vVGM7SV_12NfSe6uEWOg,4150
21
- aprsd/cmds/fetch_stats.py,sha256=rE99ir67udprRLxOhWkZ1btgF5Vs8UX74S4ssjYi4lM,9621
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=ongeK7gy_IxzLw_EfhclKMVHptyzIubV4PYxCxDMIqs,10415
24
- aprsd/cmds/listen.py,sha256=dii-DNo950kWgWDmY4pSlokJKBRVQnXkbN18yoiRTDI,9431
25
- aprsd/cmds/send_message.py,sha256=hOFLoZTDuGbMhAkSxcCgNJ8mTG5V4_WyVwfe-aeqU8Y,4742
26
- aprsd/cmds/server.py,sha256=sWbK2GJmnd-K2LxEn6z_CNTCfsXIGXbCvhr6bcLTlbk,4277
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=6bYna2bn0js6cq3c0h3KzbzgOWcpqeui5JEijx5rDj8,7386
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=W_vRKJ3P4VQaxxCjNdWCl7UmrRB8l7m_pCUi4XiOk6w,3085
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=uv2BfM6deHUbyC2aj20hxeZTsNZbjes6_MDQzxmDvys,27741
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=JGYRhwrTE_Gr49IXiOmXQffxYrqSEhp0PUhJwvE-Aek,5382
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=yGeyUV7R6EfOM87T8xDe-P2SUZqPTBA6pO_lZCGZaFY,13408
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=UIyOM4ZUdPud4UhXQLMhB_vK9P7aDeKqyvXfy2BStuw,1478
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=DyxAvRcBaqge3hYifxfeKWQ4L7p3oMLAADPtsRJlaKA,14875
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=5kIOKj5O3EJeGVbb88Y3Wl9J0NwnHOLDs2cj-87h5BI,9240
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=oAjxXbem00w1GImyWBqSasrdrruSzUyIUU-qDTxRA2g,1759
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.2.dist-info/AUTHORS,sha256=fGZhgXFMCfDPbp0hHllwmPyfiKPobLNA3sJA3BMIJVE,22
74
- aprsd-4.1.2.dist-info/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
75
- aprsd-4.1.2.dist-info/METADATA,sha256=VKJ51Jnmilt8MsnTlt2YFAV8VQfJRyX85jHPHqPe3Jk,45603
76
- aprsd-4.1.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
77
- aprsd-4.1.2.dist-info/entry_points.txt,sha256=4fReoJUB-bFqOUK6eeXYYCvTdVLprL7KVH0hWQRP9eM,171
78
- aprsd-4.1.2.dist-info/top_level.txt,sha256=v1O96niUcJOTMh9aQnFRknbScJ6mMOwqurdbxeaeSjs,6
79
- aprsd-4.1.2.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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