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