aprsd 4.1.2__tar.gz → 4.2.0__tar.gz
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-4.1.2 → aprsd-4.2.0}/ChangeLog.md +23 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/PKG-INFO +48 -48
- aprsd-4.2.0/aprsd/client/__init__.py +5 -0
- aprsd-4.2.0/aprsd/client/client.py +141 -0
- aprsd-4.2.0/aprsd/client/drivers/__init__.py +10 -0
- {aprsd-4.1.2/aprsd/client → aprsd-4.2.0/aprsd/client/drivers}/aprsis.py +104 -82
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/client/drivers/fake.py +59 -11
- aprsd-4.1.2/aprsd/client/drivers/aprsis.py → aprsd-4.2.0/aprsd/client/drivers/lib/aprslib.py +13 -3
- aprsd-4.2.0/aprsd/client/drivers/registry.py +86 -0
- aprsd-4.2.0/aprsd/client/drivers/tcpkiss.py +408 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/client/stats.py +2 -2
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/dev.py +0 -3
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/listen.py +3 -3
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/send_message.py +4 -4
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/server.py +4 -10
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/log/log.py +1 -1
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/main.py +0 -7
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/core.py +168 -169
- aprsd-4.2.0/aprsd/packets/log.py +171 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugin.py +3 -2
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugin_utils.py +2 -2
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugins/weather.py +2 -2
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/stats/collector.py +5 -4
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/rx.py +12 -10
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/tx.py +32 -31
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/keepalive_collector.py +7 -5
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd.egg-info/PKG-INFO +48 -48
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd.egg-info/SOURCES.txt +12 -9
- aprsd-4.2.0/aprsd.egg-info/requires.txt +92 -0
- aprsd-4.2.0/docker/docker-compose.yml +27 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/requirements-dev.in +1 -1
- {aprsd-4.1.2 → aprsd-4.2.0}/requirements-dev.txt +24 -24
- {aprsd-4.1.2 → aprsd-4.2.0}/requirements.txt +21 -22
- aprsd-4.2.0/tests/client/drivers/test_aprsis_driver.py +440 -0
- aprsd-4.2.0/tests/client/drivers/test_fake_driver.py +191 -0
- aprsd-4.2.0/tests/client/drivers/test_tcpkiss_driver.py +498 -0
- aprsd-4.2.0/tests/client/test_registry.py +100 -0
- aprsd-4.2.0/tests/cmds/__init__.py +0 -0
- aprsd-4.2.0/tests/mock_client_driver.py +76 -0
- aprsd-4.2.0/tests/plugins/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/plugins/test_notify.py +43 -36
- aprsd-4.2.0/tests/plugins/test_version.py +64 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/test_plugin.py +38 -18
- {aprsd-4.1.2 → aprsd-4.2.0}/tox.ini +1 -1
- aprsd-4.2.0/uv.lock +1346 -0
- aprsd-4.1.2/aprsd/client/__init__.py +0 -13
- aprsd-4.1.2/aprsd/client/base.py +0 -156
- aprsd-4.1.2/aprsd/client/drivers/kiss.py +0 -144
- aprsd-4.1.2/aprsd/client/factory.py +0 -91
- aprsd-4.1.2/aprsd/client/fake.py +0 -49
- aprsd-4.1.2/aprsd/client/kiss.py +0 -143
- aprsd-4.1.2/aprsd/packets/log.py +0 -161
- aprsd-4.1.2/aprsd.egg-info/requires.txt +0 -93
- aprsd-4.1.2/docker/docker-compose.yml +0 -14
- aprsd-4.1.2/tests/client/test_aprsis.py +0 -89
- aprsd-4.1.2/tests/client/test_client_base.py +0 -141
- aprsd-4.1.2/tests/client/test_factory.py +0 -75
- aprsd-4.1.2/tests/plugins/test_version.py +0 -35
- aprsd-4.1.2/uv.lock +0 -1340
- {aprsd-4.1.2 → aprsd-4.2.0}/.coveragerc +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.github/workflows/authors.yml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.github/workflows/codeql.yml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.github/workflows/manual_build.yml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.github/workflows/master-build.yml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.github/workflows/python.yml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.github/workflows/release_build.yml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.mailmap +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.pre-commit-config.yaml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/.readthedocs.yaml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/AUTHORS +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/CONTRIBUTING.md +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/INSTALL.txt +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/LICENSE +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/MANIFEST.in +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/Makefile +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/README.md +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cli_helper.py +0 -0
- {aprsd-4.1.2/aprsd/client/drivers → aprsd-4.2.0/aprsd/client/drivers/lib}/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/completion.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/fetch_stats.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/healthcheck.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/cmds/list_plugins.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/conf/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/conf/client.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/conf/common.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/conf/log.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/conf/opts.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/conf/plugin_common.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/exception.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/log/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/collector.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/filter.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/filters/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/filters/dupe_filter.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/filters/packet_type.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/packet_list.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/seen_list.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/tracker.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/packets/watch_list.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugins/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugins/fortune.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugins/notify.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugins/ping.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugins/time.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/plugins/version.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/stats/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/stats/app.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/aprsd.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/keepalive.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/registry.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/service.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/threads/stats.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/counter.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/fuzzyclock.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/json.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/objectstore.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/ring_buffer.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd/utils/trace.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd-lnav.json +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd.egg-info/dependency_links.txt +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd.egg-info/entry_points.txt +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd.egg-info/top_level.txt +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/aprsd_logo.png +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docker/Dockerfile +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docker/bin/admin.sh +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docker/bin/healthcheck.sh +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docker/bin/listen.sh +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docker/bin/run.sh +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docker/bin/setup.sh +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docker/build.sh +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/_static/.keep +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/_static/aprsd_overview.png +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/_static/aprsd_overview.svg +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/_templates/.keep +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.client.drivers.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.client.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.cmds.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.conf.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.log.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.packets.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.plugins.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.stats.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.threads.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/aprsd.utils.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/apidoc/modules.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/aprsd.drawio +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/changelog.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/clean_docs.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/conf.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/configure.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/index.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/install.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/links.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/plugin.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/readme.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/docs/server.rst +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/examples/plugins/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/examples/plugins/example_plugin.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/gray.conf +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/pyproject.toml +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/requirements.in +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/setup.cfg +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/setup.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/__init__.py +0 -0
- {aprsd-4.1.2/tests/cmds → aprsd-4.2.0/tests/client}/__init__.py +0 -0
- {aprsd-4.1.2/tests/plugins → aprsd-4.2.0/tests/client/drivers}/__init__.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/cmds/test_send_message.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/fake.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/plugins/test_fortune.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/plugins/test_ping.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/plugins/test_time.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/plugins/test_weather.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tests/test_packets.py +0 -0
- {aprsd-4.1.2 → aprsd-4.2.0}/tools/fast8.sh +0 -0
@@ -4,11 +4,34 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
4
4
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
6
6
|
|
7
|
+
#### [4.2.0](https://github.com/craigerl/aprsd/compare/4.1.2...4.2.0)
|
8
|
+
|
9
|
+
> 12 August 2025
|
10
|
+
|
11
|
+
- Reworked the entire client and drivers [`#191`](https://github.com/craigerl/aprsd/pull/191)
|
12
|
+
- Update the docker-compose.yml [`#193`](https://github.com/craigerl/aprsd/pull/193)
|
13
|
+
- Use OpenWeatherMap One Call v3.0 API [`#186`](https://github.com/craigerl/aprsd/pull/186)
|
14
|
+
- use OWM onecall v3.0 [`16bfda4`](https://github.com/craigerl/aprsd/commit/16bfda431c54a2ec6fbd011edd710c1d787ae1de)
|
15
|
+
- Fixed a problem with WeatherPacket [`5469610`](https://github.com/craigerl/aprsd/commit/54696107797c3d788e5d0b2dad164e1a7cf439f2)
|
16
|
+
- Ensure filter is set [`4c53c13`](https://github.com/craigerl/aprsd/commit/4c53c13e793356228f97e26a275d3d4c54b4bf54)
|
17
|
+
- Remove flask_enabled [`ce79f01`](https://github.com/craigerl/aprsd/commit/ce79f0112ff0f80db8adfff03c9ec8e62655bcc0)
|
18
|
+
- Make some catchall fields non hashable. [`acd639a`](https://github.com/craigerl/aprsd/commit/acd639a9100893a2e1d08d9ba65ef65ce6ed87e6)
|
19
|
+
- Fixed setup for AVWXWeatherPlugin [`2fcd574`](https://github.com/craigerl/aprsd/commit/2fcd574f12d48eaec122f96b6b15e9408d395b72)
|
20
|
+
- removed old flask_enabled global [`8f471c2`](https://github.com/craigerl/aprsd/commit/8f471c229c1073834c2ac66522fbaf2a561e296a)
|
21
|
+
- Updated requirements [`211ac11`](https://github.com/craigerl/aprsd/commit/211ac1132b2f1e2736b1c680936711e114556e90)
|
22
|
+
- Don't log an IOError on shutdown [`d41064b`](https://github.com/craigerl/aprsd/commit/d41064ba05faf6167c0d26db2ab525d0bb410bf8)
|
23
|
+
- Honor quiet setting for log.setup_logging [`1ae4375`](https://github.com/craigerl/aprsd/commit/1ae437581f753d903c20a6ef54868438eb034800)
|
24
|
+
- Make sure packet has addressee field [`034f11b`](https://github.com/craigerl/aprsd/commit/034f11b4f9f3392e003cfbd0e5f8b9fb5f594089)
|
25
|
+
- Fix tox failures [`74887af`](https://github.com/craigerl/aprsd/commit/74887af507755cd7ee27f13129b177f4d58ca8e0)
|
26
|
+
- log the exception when tx fails. [`fa5d0c6`](https://github.com/craigerl/aprsd/commit/fa5d0c643ae85fc9ad3f615f9d0afc590f422283)
|
27
|
+
- Updated requirements for 4.2.0 [`2c476d8`](https://github.com/craigerl/aprsd/commit/2c476d8a04df8cebbaa50780688e58d8b9df2bc9)
|
28
|
+
|
7
29
|
#### [4.1.2](https://github.com/craigerl/aprsd/compare/4.1.1...4.1.2)
|
8
30
|
|
9
31
|
> 6 March 2025
|
10
32
|
|
11
33
|
- Allow passing in a custom handler to setup_logging [`d262589`](https://github.com/craigerl/aprsd/commit/d2625893134f498748859da3b1684b04d456f790)
|
34
|
+
- Changelog for 4.1.2 [`6dba56f`](https://github.com/craigerl/aprsd/commit/6dba56f74d27e418ed2e124e7a3592c274b2da1e)
|
12
35
|
|
13
36
|
#### [4.1.1](https://github.com/craigerl/aprsd/compare/4.1.0...4.1.1)
|
14
37
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: aprsd
|
3
|
-
Version: 4.
|
3
|
+
Version: 4.2.0
|
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
|
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import logging
|
2
|
+
import threading
|
3
|
+
from typing import Callable
|
4
|
+
|
5
|
+
import timeago
|
6
|
+
import wrapt
|
7
|
+
from loguru import logger
|
8
|
+
from oslo_config import cfg
|
9
|
+
|
10
|
+
from aprsd.client import drivers # noqa - ensure drivers are registered
|
11
|
+
from aprsd.client.drivers.registry import DriverRegistry
|
12
|
+
from aprsd.packets import core
|
13
|
+
from aprsd.utils import keepalive_collector
|
14
|
+
|
15
|
+
CONF = cfg.CONF
|
16
|
+
LOG = logging.getLogger('APRSD')
|
17
|
+
LOGU = logger
|
18
|
+
|
19
|
+
|
20
|
+
class APRSDClient:
|
21
|
+
"""APRSD client class.
|
22
|
+
|
23
|
+
This is a singleton class that provides a single instance of the APRSD client.
|
24
|
+
It is responsible for connecting to the appropriate APRSD client driver based on
|
25
|
+
the configuration.
|
26
|
+
|
27
|
+
"""
|
28
|
+
|
29
|
+
_instance = None
|
30
|
+
driver = None
|
31
|
+
lock = threading.Lock()
|
32
|
+
filter = None
|
33
|
+
|
34
|
+
def __new__(cls, *args, **kwargs):
|
35
|
+
"""This magic turns this into a singleton."""
|
36
|
+
if cls._instance is None:
|
37
|
+
cls._instance = super().__new__(cls)
|
38
|
+
keepalive_collector.KeepAliveCollector().register(cls)
|
39
|
+
return cls._instance
|
40
|
+
|
41
|
+
def __init__(self):
|
42
|
+
self.connected = False
|
43
|
+
self.login_status = {
|
44
|
+
'success': False,
|
45
|
+
'message': None,
|
46
|
+
}
|
47
|
+
if not self.driver:
|
48
|
+
self.driver = DriverRegistry().get_driver()
|
49
|
+
self.driver.setup_connection()
|
50
|
+
|
51
|
+
def stats(self, serializable=False) -> dict:
|
52
|
+
stats = {}
|
53
|
+
if self.driver:
|
54
|
+
stats = self.driver.stats(serializable=serializable)
|
55
|
+
return stats
|
56
|
+
|
57
|
+
@property
|
58
|
+
def is_enabled(self):
|
59
|
+
if not self.driver:
|
60
|
+
return False
|
61
|
+
return self.driver.is_enabled()
|
62
|
+
|
63
|
+
@property
|
64
|
+
def is_configured(self):
|
65
|
+
if not self.driver:
|
66
|
+
return False
|
67
|
+
return self.driver.is_configured()
|
68
|
+
|
69
|
+
# @property
|
70
|
+
# def is_connected(self):
|
71
|
+
# if not self.driver:
|
72
|
+
# return False
|
73
|
+
# return self.driver.is_connected()
|
74
|
+
|
75
|
+
@property
|
76
|
+
def login_success(self):
|
77
|
+
if not self.driver:
|
78
|
+
return False
|
79
|
+
return self.driver.login_success
|
80
|
+
|
81
|
+
@property
|
82
|
+
def login_failure(self):
|
83
|
+
if not self.driver:
|
84
|
+
return None
|
85
|
+
return self.driver.login_failure
|
86
|
+
|
87
|
+
def set_filter(self, filter):
|
88
|
+
self.filter = filter
|
89
|
+
if not self.driver:
|
90
|
+
return
|
91
|
+
self.driver.set_filter(filter)
|
92
|
+
|
93
|
+
def get_filter(self):
|
94
|
+
if not self.driver:
|
95
|
+
return None
|
96
|
+
return self.driver.filter
|
97
|
+
|
98
|
+
def is_alive(self):
|
99
|
+
return self.driver.is_alive()
|
100
|
+
|
101
|
+
def close(self):
|
102
|
+
if not self.driver:
|
103
|
+
return
|
104
|
+
self.driver.close()
|
105
|
+
|
106
|
+
@wrapt.synchronized(lock)
|
107
|
+
def reset(self):
|
108
|
+
"""Call this to force a rebuild/reconnect."""
|
109
|
+
LOG.info('Resetting client connection.')
|
110
|
+
if self.driver:
|
111
|
+
self.driver.close()
|
112
|
+
self.driver.setup_connection()
|
113
|
+
if self.filter:
|
114
|
+
self.driver.set_filter(self.filter)
|
115
|
+
else:
|
116
|
+
LOG.warning('Client not initialized, nothing to reset.')
|
117
|
+
|
118
|
+
def send(self, packet: core.Packet) -> bool:
|
119
|
+
return self.driver.send(packet)
|
120
|
+
|
121
|
+
# For the keepalive collector
|
122
|
+
def keepalive_check(self):
|
123
|
+
# Don't check the first time through.
|
124
|
+
if not self.driver.is_alive and self._checks:
|
125
|
+
LOG.warning("Resetting client. It's not alive.")
|
126
|
+
self.reset()
|
127
|
+
self._checks = True
|
128
|
+
|
129
|
+
# For the keepalive collector
|
130
|
+
def keepalive_log(self):
|
131
|
+
if ka := self.driver.keepalive:
|
132
|
+
keepalive = timeago.format(ka)
|
133
|
+
else:
|
134
|
+
keepalive = 'N/A'
|
135
|
+
LOGU.opt(colors=True).info(f'<green>Client keepalive {keepalive}</green>')
|
136
|
+
|
137
|
+
def consumer(self, callback: Callable, raw: bool = False):
|
138
|
+
return self.driver.consumer(callback=callback, raw=raw)
|
139
|
+
|
140
|
+
def decode_packet(self, *args, **kwargs) -> core.Packet:
|
141
|
+
return self.driver.decode_packet(*args, **kwargs)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# All client drivers must be registered here
|
2
|
+
from aprsd.client.drivers.aprsis import APRSISDriver
|
3
|
+
from aprsd.client.drivers.fake import APRSDFakeDriver
|
4
|
+
from aprsd.client.drivers.registry import DriverRegistry
|
5
|
+
from aprsd.client.drivers.tcpkiss import TCPKISSDriver
|
6
|
+
|
7
|
+
driver_registry = DriverRegistry()
|
8
|
+
driver_registry.register(APRSDFakeDriver)
|
9
|
+
driver_registry.register(APRSISDriver)
|
10
|
+
driver_registry.register(TCPKISSDriver)
|
@@ -1,65 +1,39 @@
|
|
1
1
|
import datetime
|
2
2
|
import logging
|
3
3
|
import time
|
4
|
+
from typing import Callable
|
4
5
|
|
5
|
-
import timeago
|
6
6
|
from aprslib.exceptions import LoginError
|
7
7
|
from loguru import logger
|
8
8
|
from oslo_config import cfg
|
9
9
|
|
10
10
|
from aprsd import client, exception
|
11
|
-
from aprsd.client import
|
12
|
-
from aprsd.client.drivers import aprsis
|
11
|
+
from aprsd.client.drivers.lib.aprslib import APRSLibClient
|
13
12
|
from aprsd.packets import core
|
14
13
|
|
15
14
|
CONF = cfg.CONF
|
16
|
-
LOG = logging.getLogger(
|
15
|
+
LOG = logging.getLogger('APRSD')
|
17
16
|
LOGU = logger
|
18
17
|
|
19
18
|
|
20
|
-
class
|
19
|
+
# class APRSISDriver(metaclass=trace.TraceWrapperMetaclass):
|
20
|
+
class APRSISDriver:
|
21
|
+
"""This is the APRS-IS driver for the APRSD client.
|
22
|
+
|
23
|
+
This driver uses our modified aprslib.IS class to connect to the APRS-IS server.
|
24
|
+
|
25
|
+
"""
|
26
|
+
|
21
27
|
_client = None
|
22
28
|
_checks = False
|
23
29
|
|
24
30
|
def __init__(self):
|
25
|
-
max_timeout = {
|
31
|
+
max_timeout = {'hours': 0.0, 'minutes': 2, 'seconds': 0}
|
26
32
|
self.max_delta = datetime.timedelta(**max_timeout)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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>")
|
33
|
+
self.login_status = {
|
34
|
+
'success': False,
|
35
|
+
'message': None,
|
36
|
+
}
|
63
37
|
|
64
38
|
@staticmethod
|
65
39
|
def is_enabled():
|
@@ -71,37 +45,31 @@ class APRSISClient(base.APRSClient):
|
|
71
45
|
|
72
46
|
@staticmethod
|
73
47
|
def is_configured():
|
74
|
-
if
|
48
|
+
if APRSISDriver.is_enabled():
|
75
49
|
# Ensure that the config vars are correctly set
|
76
50
|
if not CONF.aprs_network.login:
|
77
|
-
LOG.error(
|
51
|
+
LOG.error('Config aprs_network.login not set.')
|
78
52
|
raise exception.MissingConfigOptionException(
|
79
|
-
|
53
|
+
'aprs_network.login is not set.',
|
80
54
|
)
|
81
55
|
if not CONF.aprs_network.password:
|
82
|
-
LOG.error(
|
56
|
+
LOG.error('Config aprs_network.password not set.')
|
83
57
|
raise exception.MissingConfigOptionException(
|
84
|
-
|
58
|
+
'aprs_network.password is not set.',
|
85
59
|
)
|
86
60
|
if not CONF.aprs_network.host:
|
87
|
-
LOG.error(
|
61
|
+
LOG.error('Config aprs_network.host not set.')
|
88
62
|
raise exception.MissingConfigOptionException(
|
89
|
-
|
63
|
+
'aprs_network.host is not set.',
|
90
64
|
)
|
91
65
|
|
92
66
|
return True
|
93
67
|
return True
|
94
68
|
|
95
|
-
|
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
|
-
|
69
|
+
@property
|
102
70
|
def is_alive(self):
|
103
71
|
if not self._client:
|
104
|
-
LOG.warning(f
|
72
|
+
LOG.warning(f'APRS_CLIENT {self._client} alive? NO!!!')
|
105
73
|
return False
|
106
74
|
return self._client.is_alive() and not self._is_stale_connection()
|
107
75
|
|
@@ -110,13 +78,8 @@ class APRSISClient(base.APRSClient):
|
|
110
78
|
self._client.stop()
|
111
79
|
self._client.close()
|
112
80
|
|
113
|
-
|
114
|
-
|
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])
|
81
|
+
def send(self, packet: core.Packet) -> bool:
|
82
|
+
return self._client.send(packet)
|
120
83
|
|
121
84
|
def setup_connection(self):
|
122
85
|
user = CONF.aprs_network.login
|
@@ -125,7 +88,6 @@ class APRSISClient(base.APRSClient):
|
|
125
88
|
port = CONF.aprs_network.port
|
126
89
|
self.connected = False
|
127
90
|
backoff = 1
|
128
|
-
aprs_client = None
|
129
91
|
retries = 3
|
130
92
|
retry_count = 0
|
131
93
|
while not self.connected:
|
@@ -134,27 +96,29 @@ class APRSISClient(base.APRSClient):
|
|
134
96
|
break
|
135
97
|
try:
|
136
98
|
LOG.info(
|
137
|
-
f
|
99
|
+
f'Creating aprslib client({host}:{port}) and logging in {user}.'
|
138
100
|
)
|
139
|
-
|
101
|
+
self._client = APRSLibClient(
|
140
102
|
user, passwd=password, host=host, port=port
|
141
103
|
)
|
142
104
|
# Force the log to be the same
|
143
|
-
|
144
|
-
|
145
|
-
self.connected = self.login_status[
|
146
|
-
self.login_status[
|
105
|
+
self._client.logger = LOG
|
106
|
+
self._client.connect()
|
107
|
+
self.connected = self.login_status['success'] = True
|
108
|
+
self.login_status['message'] = self._client.server_string
|
147
109
|
backoff = 1
|
148
110
|
except LoginError as e:
|
149
111
|
LOG.error(f"Failed to login to APRS-IS Server '{e}'")
|
150
|
-
self.connected = self.login_status[
|
151
|
-
self.login_status[
|
152
|
-
|
112
|
+
self.connected = self.login_status['success'] = False
|
113
|
+
self.login_status['message'] = (
|
114
|
+
e.message if hasattr(e, 'message') else str(e)
|
115
|
+
)
|
116
|
+
LOG.error(self.login_status['message'])
|
153
117
|
time.sleep(backoff)
|
154
118
|
except Exception as e:
|
155
119
|
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
156
|
-
self.connected = self.login_status[
|
157
|
-
self.login_status[
|
120
|
+
self.connected = self.login_status['success'] = False
|
121
|
+
self.login_status['message'] = getattr(e, 'message', str(e))
|
158
122
|
time.sleep(backoff)
|
159
123
|
# Don't allow the backoff to go to inifinity.
|
160
124
|
if backoff > 5:
|
@@ -162,16 +126,50 @@ class APRSISClient(base.APRSClient):
|
|
162
126
|
else:
|
163
127
|
backoff += 1
|
164
128
|
continue
|
165
|
-
self._client = aprs_client
|
166
|
-
return aprs_client
|
167
129
|
|
168
|
-
def
|
130
|
+
def set_filter(self, filter):
|
131
|
+
self._client.set_filter(filter)
|
132
|
+
|
133
|
+
def login_success(self) -> bool:
|
134
|
+
return self.login_status.get('success', False)
|
135
|
+
|
136
|
+
def login_failure(self) -> str:
|
137
|
+
return self.login_status.get('message', None)
|
138
|
+
|
139
|
+
@property
|
140
|
+
def filter(self):
|
141
|
+
return self._client.filter
|
142
|
+
|
143
|
+
@property
|
144
|
+
def server_string(self):
|
145
|
+
return self._client.server_string
|
146
|
+
|
147
|
+
@property
|
148
|
+
def keepalive(self):
|
149
|
+
return self._client.aprsd_keepalive
|
150
|
+
|
151
|
+
def _is_stale_connection(self):
|
152
|
+
delta = datetime.datetime.now() - self._client.aprsd_keepalive
|
153
|
+
if delta > self.max_delta:
|
154
|
+
LOG.error(f'Connection is stale, last heard {delta} ago.')
|
155
|
+
return True
|
156
|
+
return False
|
157
|
+
|
158
|
+
@staticmethod
|
159
|
+
def transport():
|
160
|
+
return client.TRANSPORT_APRSIS
|
161
|
+
|
162
|
+
def decode_packet(self, *args, **kwargs):
|
163
|
+
"""APRS lib already decodes this."""
|
164
|
+
return core.factory(args[0])
|
165
|
+
|
166
|
+
def consumer(self, callback: Callable, raw: bool = False):
|
169
167
|
if self._client:
|
170
168
|
try:
|
171
169
|
self._client.consumer(
|
172
170
|
callback,
|
173
|
-
blocking=
|
174
|
-
immortal=
|
171
|
+
blocking=False,
|
172
|
+
immortal=False,
|
175
173
|
raw=raw,
|
176
174
|
)
|
177
175
|
except Exception as e:
|
@@ -179,5 +177,29 @@ class APRSISClient(base.APRSClient):
|
|
179
177
|
LOG.info(e.__cause__)
|
180
178
|
raise e
|
181
179
|
else:
|
182
|
-
LOG.warning(
|
180
|
+
LOG.warning('client is None, might be resetting.')
|
183
181
|
self.connected = False
|
182
|
+
|
183
|
+
def stats(self, serializable=False) -> dict:
|
184
|
+
stats = {}
|
185
|
+
if self.is_configured():
|
186
|
+
if self._client:
|
187
|
+
keepalive = self._client.aprsd_keepalive
|
188
|
+
server_string = self._client.server_string
|
189
|
+
if serializable:
|
190
|
+
keepalive = keepalive.isoformat()
|
191
|
+
filter = self.filter
|
192
|
+
else:
|
193
|
+
keepalive = 'None'
|
194
|
+
server_string = 'None'
|
195
|
+
filter = 'None'
|
196
|
+
stats = {
|
197
|
+
'connected': self.is_alive,
|
198
|
+
'filter': filter,
|
199
|
+
'login_status': self.login_status,
|
200
|
+
'connection_keepalive': keepalive,
|
201
|
+
'server_string': server_string,
|
202
|
+
'transport': self.transport(),
|
203
|
+
}
|
204
|
+
|
205
|
+
return stats
|