aprsd 3.3.4__tar.gz → 3.4.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.
Files changed (232) hide show
  1. {aprsd-3.3.4 → aprsd-3.4.0}/ChangeLog +104 -7
  2. {aprsd-3.3.4 → aprsd-3.4.0}/PKG-INFO +14 -16
  3. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/client.py +133 -20
  4. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/clients/aprsis.py +6 -3
  5. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/clients/fake.py +1 -1
  6. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/clients/kiss.py +1 -1
  7. aprsd-3.4.0/aprsd/cmds/completion.py +22 -0
  8. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/fetch_stats.py +53 -57
  9. aprsd-3.4.0/aprsd/cmds/healthcheck.py +86 -0
  10. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/list_plugins.py +2 -2
  11. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/listen.py +33 -17
  12. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/send_message.py +2 -2
  13. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/server.py +26 -9
  14. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/webchat.py +34 -29
  15. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/conf/common.py +46 -31
  16. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/log/log.py +28 -6
  17. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/main.py +4 -17
  18. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/packets/__init__.py +3 -2
  19. aprsd-3.4.0/aprsd/packets/collector.py +56 -0
  20. aprsd-3.4.0/aprsd/packets/core.py +823 -0
  21. aprsd-3.4.0/aprsd/packets/log.py +143 -0
  22. aprsd-3.4.0/aprsd/packets/packet_list.py +116 -0
  23. aprsd-3.4.0/aprsd/packets/seen_list.py +54 -0
  24. aprsd-3.4.0/aprsd/packets/tracker.py +109 -0
  25. aprsd-3.4.0/aprsd/packets/watch_list.py +122 -0
  26. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugin.py +41 -16
  27. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/email.py +35 -7
  28. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/time.py +3 -2
  29. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/version.py +4 -5
  30. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/weather.py +0 -1
  31. aprsd-3.4.0/aprsd/stats/__init__.py +20 -0
  32. aprsd-3.4.0/aprsd/stats/app.py +46 -0
  33. aprsd-3.4.0/aprsd/stats/collector.py +38 -0
  34. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/threads/__init__.py +3 -2
  35. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/threads/aprsd.py +67 -36
  36. aprsd-3.4.0/aprsd/threads/keep_alive.py +121 -0
  37. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/threads/log_monitor.py +46 -0
  38. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/threads/rx.py +43 -24
  39. aprsd-3.4.0/aprsd/threads/stats.py +44 -0
  40. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/threads/tx.py +36 -17
  41. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/utils/__init__.py +12 -0
  42. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/utils/counter.py +6 -3
  43. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/utils/json.py +20 -0
  44. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/utils/objectstore.py +22 -17
  45. aprsd-3.4.0/aprsd/web/admin/static/css/prism.css +4 -0
  46. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/js/charts.js +9 -7
  47. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/js/echarts.js +71 -9
  48. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/js/main.js +47 -6
  49. aprsd-3.4.0/aprsd/web/admin/static/js/prism.js +12 -0
  50. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/templates/index.html +18 -7
  51. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/gps.js +3 -1
  52. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/main.js +4 -3
  53. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/send-message.js +5 -2
  54. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/templates/index.html +1 -0
  55. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/wsgi.py +62 -127
  56. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd.egg-info/PKG-INFO +14 -16
  57. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd.egg-info/SOURCES.txt +7 -10
  58. aprsd-3.4.0/aprsd.egg-info/pbr.json +1 -0
  59. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd.egg-info/requires.txt +13 -15
  60. {aprsd-3.3.4 → aprsd-3.4.0}/dev-requirements.txt +24 -25
  61. {aprsd-3.3.4 → aprsd-3.4.0}/docker/Dockerfile +5 -4
  62. {aprsd-3.3.4 → aprsd-3.4.0}/docker/Dockerfile-dev +7 -5
  63. aprsd-3.4.0/docker/bin/setup.sh +50 -0
  64. {aprsd-3.3.4 → aprsd-3.4.0}/docker/build.sh +1 -1
  65. {aprsd-3.3.4 → aprsd-3.4.0}/requirements.in +1 -3
  66. {aprsd-3.3.4 → aprsd-3.4.0}/requirements.txt +13 -15
  67. {aprsd-3.3.4 → aprsd-3.4.0}/tests/cmds/test_webchat.py +2 -5
  68. {aprsd-3.3.4 → aprsd-3.4.0}/tests/fake.py +13 -2
  69. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/test_version.py +5 -8
  70. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/test_weather.py +1 -1
  71. aprsd-3.4.0/tests/test_packets.py +285 -0
  72. {aprsd-3.3.4 → aprsd-3.4.0}/tests/test_plugin.py +2 -6
  73. {aprsd-3.3.4 → aprsd-3.4.0}/tox.ini +1 -1
  74. aprsd-3.3.4/aprsd/cmds/completion.py +0 -36
  75. aprsd-3.3.4/aprsd/cmds/healthcheck.py +0 -84
  76. aprsd-3.3.4/aprsd/packets/core.py +0 -688
  77. aprsd-3.3.4/aprsd/packets/packet_list.py +0 -99
  78. aprsd-3.3.4/aprsd/packets/seen_list.py +0 -43
  79. aprsd-3.3.4/aprsd/packets/tracker.py +0 -111
  80. aprsd-3.3.4/aprsd/packets/watch_list.py +0 -96
  81. aprsd-3.3.4/aprsd/plugins/query.py +0 -81
  82. aprsd-3.3.4/aprsd/rpc/__init__.py +0 -14
  83. aprsd-3.3.4/aprsd/rpc/client.py +0 -165
  84. aprsd-3.3.4/aprsd/rpc/server.py +0 -99
  85. aprsd-3.3.4/aprsd/stats.py +0 -266
  86. aprsd-3.3.4/aprsd/threads/keep_alive.py +0 -115
  87. aprsd-3.3.4/aprsd/web/admin/static/css/prism.css +0 -189
  88. aprsd-3.3.4/aprsd/web/admin/static/js/prism.js +0 -2247
  89. aprsd-3.3.4/aprsd/web/admin/static/json-viewer/jquery.json-viewer.css +0 -57
  90. aprsd-3.3.4/aprsd/web/admin/static/json-viewer/jquery.json-viewer.js +0 -158
  91. aprsd-3.3.4/aprsd/web/chat/static/json-viewer/jquery.json-viewer.css +0 -57
  92. aprsd-3.3.4/aprsd/web/chat/static/json-viewer/jquery.json-viewer.js +0 -158
  93. aprsd-3.3.4/aprsd.egg-info/pbr.json +0 -1
  94. aprsd-3.3.4/tests/plugins/test_query.py +0 -54
  95. aprsd-3.3.4/tests/test_packets.py +0 -102
  96. {aprsd-3.3.4 → aprsd-3.4.0}/.coveragerc +0 -0
  97. {aprsd-3.3.4 → aprsd-3.4.0}/.github/workflows/codeql.yml +0 -0
  98. {aprsd-3.3.4 → aprsd-3.4.0}/.github/workflows/manual_build.yml +0 -0
  99. {aprsd-3.3.4 → aprsd-3.4.0}/.github/workflows/master-build.yml +0 -0
  100. {aprsd-3.3.4 → aprsd-3.4.0}/.github/workflows/python.yml +0 -0
  101. {aprsd-3.3.4 → aprsd-3.4.0}/.github/workflows/release_build.yml +0 -0
  102. {aprsd-3.3.4 → aprsd-3.4.0}/.pre-commit-config.yaml +0 -0
  103. {aprsd-3.3.4 → aprsd-3.4.0}/.readthedocs.yaml +0 -0
  104. {aprsd-3.3.4 → aprsd-3.4.0}/AUTHORS +0 -0
  105. {aprsd-3.3.4 → aprsd-3.4.0}/INSTALL.txt +0 -0
  106. {aprsd-3.3.4 → aprsd-3.4.0}/LICENSE +0 -0
  107. {aprsd-3.3.4 → aprsd-3.4.0}/MANIFEST.in +0 -0
  108. {aprsd-3.3.4 → aprsd-3.4.0}/Makefile +0 -0
  109. {aprsd-3.3.4 → aprsd-3.4.0}/README.rst +0 -0
  110. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/__init__.py +0 -0
  111. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cli_helper.py +0 -0
  112. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/clients/__init__.py +0 -0
  113. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/__init__.py +0 -0
  114. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/cmds/dev.py +0 -0
  115. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/conf/__init__.py +0 -0
  116. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/conf/client.py +0 -0
  117. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/conf/log.py +0 -0
  118. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/conf/opts.py +0 -0
  119. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/conf/plugin_common.py +0 -0
  120. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/conf/plugin_email.py +0 -0
  121. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/exception.py +0 -0
  122. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/log/__init__.py +0 -0
  123. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/messaging.py +0 -0
  124. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugin_utils.py +0 -0
  125. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/__init__.py +0 -0
  126. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/fortune.py +0 -0
  127. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/location.py +0 -0
  128. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/notify.py +0 -0
  129. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/plugins/ping.py +0 -0
  130. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/threads/registry.py +0 -0
  131. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/utils/fuzzyclock.py +0 -0
  132. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/utils/ring_buffer.py +0 -0
  133. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/utils/trace.py +0 -0
  134. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/__init__.py +0 -0
  135. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/__init__.py +0 -0
  136. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/css/index.css +0 -0
  137. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/css/tabs.css +0 -0
  138. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/images/Untitled.png +0 -0
  139. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
  140. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
  141. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
  142. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
  143. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
  144. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/js/logs.js +0 -0
  145. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/js/send-message.js +0 -0
  146. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/admin/static/js/tabs.js +0 -0
  147. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/chat.css +0 -0
  148. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/index.css +0 -0
  149. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/style.css.map +0 -0
  150. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/tabs.css +0 -0
  151. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/bootstrap.min.css +0 -0
  152. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
  153. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/google-fonts.css +0 -0
  154. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/jquery-ui.css +0 -0
  155. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/jquery.toast.css +0 -0
  156. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  157. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  158. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  159. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  160. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
  161. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
  162. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
  163. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  164. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/images/Untitled.png +0 -0
  165. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
  166. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
  167. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
  168. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
  169. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
  170. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/images/globe.svg +0 -0
  171. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/tabs.js +0 -0
  172. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +0 -0
  173. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +0 -0
  174. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/upstream/jquery-ui.min.js +0 -0
  175. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/upstream/jquery.toast.js +0 -0
  176. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/upstream/semantic.min.js +0 -0
  177. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd/web/chat/static/js/upstream/socket.io.min.js +0 -0
  178. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd-lnav.json +0 -0
  179. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd.egg-info/dependency_links.txt +0 -0
  180. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd.egg-info/entry_points.txt +0 -0
  181. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd.egg-info/not-zip-safe +0 -0
  182. {aprsd-3.3.4 → aprsd-3.4.0}/aprsd.egg-info/top_level.txt +0 -0
  183. {aprsd-3.3.4 → aprsd-3.4.0}/dev-requirements.in +0 -0
  184. {aprsd-3.3.4 → aprsd-3.4.0}/docker/bin/admin.sh +0 -0
  185. {aprsd-3.3.4 → aprsd-3.4.0}/docker/bin/listen.sh +0 -0
  186. {aprsd-3.3.4 → aprsd-3.4.0}/docker/bin/run.sh +0 -0
  187. {aprsd-3.3.4 → aprsd-3.4.0}/docker/docker-compose.yml +0 -0
  188. {aprsd-3.3.4 → aprsd-3.4.0}/docs/_static/.keep +0 -0
  189. {aprsd-3.3.4 → aprsd-3.4.0}/docs/_static/aprsd_overview.png +0 -0
  190. {aprsd-3.3.4 → aprsd-3.4.0}/docs/_static/aprsd_overview.svg +0 -0
  191. {aprsd-3.3.4 → aprsd-3.4.0}/docs/_templates/.keep +0 -0
  192. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.clients.rst +0 -0
  193. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.cmds.rst +0 -0
  194. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.conf.rst +0 -0
  195. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.packets.rst +0 -0
  196. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.plugins.rst +0 -0
  197. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.rpc.rst +0 -0
  198. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.rst +0 -0
  199. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.threads.rst +0 -0
  200. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.utils.rst +0 -0
  201. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.web.admin.rst +0 -0
  202. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/aprsd.web.rst +0 -0
  203. {aprsd-3.3.4 → aprsd-3.4.0}/docs/apidoc/modules.rst +0 -0
  204. {aprsd-3.3.4 → aprsd-3.4.0}/docs/aprsd.drawio +0 -0
  205. {aprsd-3.3.4 → aprsd-3.4.0}/docs/changelog.rst +0 -0
  206. {aprsd-3.3.4 → aprsd-3.4.0}/docs/clean_docs.py +0 -0
  207. {aprsd-3.3.4 → aprsd-3.4.0}/docs/conf.py +0 -0
  208. {aprsd-3.3.4 → aprsd-3.4.0}/docs/configure.rst +0 -0
  209. {aprsd-3.3.4 → aprsd-3.4.0}/docs/index.rst +0 -0
  210. {aprsd-3.3.4 → aprsd-3.4.0}/docs/install.rst +0 -0
  211. {aprsd-3.3.4 → aprsd-3.4.0}/docs/links.rst +0 -0
  212. {aprsd-3.3.4 → aprsd-3.4.0}/docs/plugin.rst +0 -0
  213. {aprsd-3.3.4 → aprsd-3.4.0}/docs/readme.rst +0 -0
  214. {aprsd-3.3.4 → aprsd-3.4.0}/docs/server.rst +0 -0
  215. {aprsd-3.3.4 → aprsd-3.4.0}/examples/plugins/__init__.py +0 -0
  216. {aprsd-3.3.4 → aprsd-3.4.0}/examples/plugins/example_plugin.py +0 -0
  217. {aprsd-3.3.4 → aprsd-3.4.0}/gray.conf +0 -0
  218. {aprsd-3.3.4 → aprsd-3.4.0}/pyproject.toml +0 -0
  219. {aprsd-3.3.4 → aprsd-3.4.0}/setup.cfg +0 -0
  220. {aprsd-3.3.4 → aprsd-3.4.0}/setup.py +0 -0
  221. {aprsd-3.3.4 → aprsd-3.4.0}/tests/__init__.py +0 -0
  222. {aprsd-3.3.4 → aprsd-3.4.0}/tests/cmds/__init__.py +0 -0
  223. {aprsd-3.3.4 → aprsd-3.4.0}/tests/cmds/test_send_message.py +0 -0
  224. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/__init__.py +0 -0
  225. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/test_fortune.py +0 -0
  226. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/test_location.py +0 -0
  227. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/test_notify.py +0 -0
  228. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/test_ping.py +0 -0
  229. {aprsd-3.3.4 → aprsd-3.4.0}/tests/plugins/test_time.py +0 -0
  230. {aprsd-3.3.4 → aprsd-3.4.0}/tests/test_email.py +0 -0
  231. {aprsd-3.3.4 → aprsd-3.4.0}/tests/test_main.py +0 -0
  232. {aprsd-3.3.4 → aprsd-3.4.0}/tools/fast8.sh +0 -0
@@ -1,16 +1,113 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
- v3.3.4
5
- ------
6
-
7
- * Fixed entry\_points
8
- * Fix for entry\_points where python < 3.10
9
-
10
- v3.3.3
4
+ v3.4.0
11
5
  ------
12
6
 
7
+ * Change setup.h
8
+ * Fixed docker setup.sh comparison
9
+ * Fixed unit tests failing with WatchList
10
+ * Added config enable\_packet\_logging
11
+ * Make all the Objectstore children use the same lock
12
+ * Fixed PacketTrack with UnknownPacket
13
+ * Removed the requirement on click-completion
14
+ * Update Dockerfiles
15
+ * Added fox for entry\_points with old python
16
+ * Added config for enable\_seen\_list
17
+ * Fix APRSDStats start\_time
18
+ * Added default\_packet\_send\_count config
19
+ * Call packet collecter after prepare during tx
20
+ * Added PacketTrack to packet collector
21
+ * Webchat Send Beacon uses Path selected in UI
22
+ * Added try except blocks in collectors
23
+ * Remove error logs from watch list
24
+ * Fixed issue with PacketList being empty
25
+ * Added new PacketCollector
26
+ * Fixed Keepalive access to email stats
27
+ * Added support for RX replyacks
28
+ * Changed Stats Collector registration
29
+ * Added PacketList.set\_maxlen()
30
+ * another fix for tx send
31
+ * removed Packet.last\_send\_attempt and just use send\_count
32
+ * Fix access to PacketList.\_maxlen
33
+ * added packet\_count in packet\_list stats
34
+ * force uwsgi to 2.0.24
35
+ * ismall update
36
+ * Added new config optons for PacketList
37
+ * Update requirements
38
+ * Added threads chart to admin ui graphs
39
+ * set packetlist max back to 100
40
+ * ensure thread count is updated
41
+ * Added threads table in the admin web ui
42
+ * Fixed issue with APRSDThreadList stats()
43
+ * Added new default\_ack\_send\_count config option
44
+ * Remove packet from tracker after max attempts
45
+ * Limit packets to 50 in PacketList
46
+ * syncronize the add for StatsStore
47
+ * Lock on stats for PacketList
48
+ * Fixed PacketList maxlen
49
+ * Fixed a problem with the webchat tab notification
50
+ * Another fix for ACK packets
51
+ * Fix issue not tracking RX Ack packets for stats
52
+ * Fix time plugin
53
+ * add GATE route to webchat along with WIDE1, etc
54
+ * Update webchat, include GATE route along with WIDE, ARISS, etc
55
+ * Get rid of some useless warning logs
56
+ * Added human\_info property to MessagePackets
57
+ * Fixed scrolling problem with new webchat sent msg
58
+ * Fix some issues with listen command
59
+ * Admin interface catch empty stats
60
+ * Ensure StatsStore has empty data
61
+ * Ensure latest pip is in docker image
62
+ * LOG failed requests post to admin ui
63
+ * changed admin web\_ip to StrOpt
64
+ * Updated prism to 1.29
65
+ * Removed json-viewer
66
+ * Remove rpyc as a requirement
67
+ * Delete more stats from webchat
68
+ * Admin UI working again
69
+ * Removed RPC Server and client
70
+ * Remove the logging of the conf password if not set
71
+ * Lock around client reset
72
+ * Allow stats collector to serialize upon creation
73
+ * Fixed issues with watch list at startup
74
+ * Fixed access to log\_monitor
75
+ * Got unit tests working again
76
+ * Fixed pep8 errors and missing files
77
+ * Reworked the stats making the rpc server obsolete
78
+ * Update client.py to add consumer in the API
13
79
  * Fix for sample-config warning
80
+ * update requirements
81
+ * Put packet.json back in
82
+ * Change debug log color
83
+ * Fix for filtering curse words
84
+ * added packet counter random int
85
+ * More packet cleanup and tests
86
+ * Show comment in multiline packet output
87
+ * Added new config option log\_packet\_format
88
+ * Some packet cleanup
89
+ * Added new webchat config option for logging
90
+ * Fix some pep8 issues
91
+ * Completely redo logging of packets!!
92
+ * Fixed some logging in webchat
93
+ * Added missing packet types in listen command
94
+ * Don't call stats so often in webchat
95
+ * Eliminated need for from\_aprslib\_dict
96
+ * Fix for micE packet decoding with mbits
97
+ * updated dev-requirements
98
+ * Fixed some tox errors related to mypy
99
+ * Refactored packets
100
+ * removed print
101
+ * small refactor of stats usage in version plugin
102
+ * Added type setting on pluging.py for mypy
103
+ * Moved Threads list for mypy
104
+ * No need to synchronize on stats
105
+ * Start to add types
106
+ * Update tox for mypy runs
107
+ * Bump black from 24.2.0 to 24.3.0
108
+ * replaced access to conf from uwsgi
109
+ * Fixed call to setup\_logging in uwsgi
110
+ * Fixed access to conf.log in logging\_setup
14
111
 
15
112
  v3.3.2
16
113
  ------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aprsd
3
- Version: 3.3.4
3
+ Version: 3.4.0
4
4
  Summary: Amateur radio APRS daemon which listens for messages and responds
5
5
  Home-page: http://aprsd.readthedocs.org
6
6
  Author: Craig Lamparter
@@ -31,14 +31,13 @@ Requires-Dist: click==8.1.7
31
31
  Requires-Dist: click-completion==0.5.2
32
32
  Requires-Dist: click-params==0.5.0
33
33
  Requires-Dist: commonmark==0.9.1
34
- Requires-Dist: dacite2==2.0.0
35
34
  Requires-Dist: dataclasses==0.6
36
35
  Requires-Dist: dataclasses-json==0.6.4
37
36
  Requires-Dist: debtcollector==3.0.0
38
37
  Requires-Dist: deprecated==1.2.14
39
38
  Requires-Dist: dnspython==2.6.1
40
- Requires-Dist: eventlet==0.35.2
41
- Requires-Dist: flask==3.0.2
39
+ Requires-Dist: eventlet==0.36.1
40
+ Requires-Dist: flask==3.0.3
42
41
  Requires-Dist: flask-httpauth==4.8.0
43
42
  Requires-Dist: flask-socketio==5.3.6
44
43
  Requires-Dist: geographiclib==2.0
@@ -46,10 +45,10 @@ Requires-Dist: geopy==2.4.1
46
45
  Requires-Dist: gevent==24.2.1
47
46
  Requires-Dist: greenlet==3.0.3
48
47
  Requires-Dist: h11==0.14.0
49
- Requires-Dist: idna==3.6
48
+ Requires-Dist: idna==3.7
50
49
  Requires-Dist: imapclient==3.0.1
51
- Requires-Dist: importlib-metadata==7.0.1
52
- Requires-Dist: itsdangerous==2.1.2
50
+ Requires-Dist: importlib-metadata==7.1.0
51
+ Requires-Dist: itsdangerous==2.2.0
53
52
  Requires-Dist: jinja2==3.1.3
54
53
  Requires-Dist: kiss3==8.0.0
55
54
  Requires-Dist: loguru==0.7.2
@@ -59,21 +58,19 @@ Requires-Dist: mypy-extensions==1.0.0
59
58
  Requires-Dist: netaddr==1.2.1
60
59
  Requires-Dist: oslo-config==9.4.0
61
60
  Requires-Dist: oslo-i18n==6.3.0
62
- Requires-Dist: packaging==23.2
61
+ Requires-Dist: packaging==24.0
63
62
  Requires-Dist: pbr==6.0.0
64
- Requires-Dist: pluggy==1.4.0
65
- Requires-Dist: plumbum==1.8.2
63
+ Requires-Dist: pluggy==1.5.0
66
64
  Requires-Dist: pygments==2.17.2
67
65
  Requires-Dist: pyserial==3.5
68
66
  Requires-Dist: pyserial-asyncio==0.6
69
67
  Requires-Dist: python-engineio==4.9.0
70
- Requires-Dist: python-socketio==5.11.1
68
+ Requires-Dist: python-socketio==5.11.2
71
69
  Requires-Dist: pytz==2024.1
72
70
  Requires-Dist: pyyaml==6.0.1
73
71
  Requires-Dist: requests==2.31.0
74
72
  Requires-Dist: rfc3986==2.0.0
75
73
  Requires-Dist: rich==12.6.0
76
- Requires-Dist: rpyc==6.0.0
77
74
  Requires-Dist: rush==2021.4.0
78
75
  Requires-Dist: shellingham==1.5.4
79
76
  Requires-Dist: simple-websocket==1.0.0
@@ -82,17 +79,18 @@ Requires-Dist: soupsieve==2.5
82
79
  Requires-Dist: stevedore==5.2.0
83
80
  Requires-Dist: tabulate==0.9.0
84
81
  Requires-Dist: thesmuggler==1.0.1
85
- Requires-Dist: typing-extensions==4.10.0
82
+ Requires-Dist: typing-extensions==4.11.0
86
83
  Requires-Dist: typing-inspect==0.9.0
84
+ Requires-Dist: tzlocal==5.2
87
85
  Requires-Dist: update-checker==0.18.0
88
86
  Requires-Dist: urllib3==2.2.1
89
87
  Requires-Dist: validators==0.22.0
90
- Requires-Dist: werkzeug==3.0.1
88
+ Requires-Dist: werkzeug==3.0.2
91
89
  Requires-Dist: wrapt==1.16.0
92
90
  Requires-Dist: wsproto==1.2.0
93
- Requires-Dist: zipp==3.17.0
91
+ Requires-Dist: zipp==3.18.1
94
92
  Requires-Dist: zope-event==5.0
95
- Requires-Dist: zope-interface==6.2
93
+ Requires-Dist: zope-interface==6.3
96
94
 
97
95
  ===============================================
98
96
  APRSD - Ham radio APRS-IS Message plugin server
@@ -1,15 +1,18 @@
1
1
  import abc
2
+ import datetime
2
3
  import logging
4
+ import threading
3
5
  import time
4
6
 
5
7
  import aprslib
6
8
  from aprslib.exceptions import LoginError
7
9
  from oslo_config import cfg
10
+ import wrapt
8
11
 
9
12
  from aprsd import exception
10
13
  from aprsd.clients import aprsis, fake, kiss
11
- from aprsd.packets import core, packet_list
12
- from aprsd.utils import trace
14
+ from aprsd.packets import core
15
+ from aprsd.utils import singleton, trace
13
16
 
14
17
 
15
18
  CONF = cfg.CONF
@@ -25,6 +28,34 @@ TRANSPORT_FAKE = "fake"
25
28
  factory = None
26
29
 
27
30
 
31
+ @singleton
32
+ class APRSClientStats:
33
+
34
+ lock = threading.Lock()
35
+
36
+ @wrapt.synchronized(lock)
37
+ def stats(self, serializable=False):
38
+ client = factory.create()
39
+ stats = {
40
+ "transport": client.transport(),
41
+ "filter": client.filter,
42
+ "connected": client.connected,
43
+ }
44
+
45
+ if client.transport() == TRANSPORT_APRSIS:
46
+ stats["server_string"] = client.client.server_string
47
+ keepalive = client.client.aprsd_keepalive
48
+ if serializable:
49
+ keepalive = keepalive.isoformat()
50
+ stats["server_keepalive"] = keepalive
51
+ elif client.transport() == TRANSPORT_TCPKISS:
52
+ stats["host"] = CONF.kiss_tcp.host
53
+ stats["port"] = CONF.kiss_tcp.port
54
+ elif client.transport() == TRANSPORT_SERIALKISS:
55
+ stats["device"] = CONF.kiss_serial.device
56
+ return stats
57
+
58
+
28
59
  class Client:
29
60
  """Singleton client class that constructs the aprslib connection."""
30
61
 
@@ -32,16 +63,21 @@ class Client:
32
63
  _client = None
33
64
 
34
65
  connected = False
35
- server_string = None
36
66
  filter = None
67
+ lock = threading.Lock()
37
68
 
38
69
  def __new__(cls, *args, **kwargs):
39
70
  """This magic turns this into a singleton."""
40
71
  if cls._instance is None:
41
72
  cls._instance = super().__new__(cls)
42
73
  # Put any initialization here.
74
+ cls._instance._create_client()
43
75
  return cls._instance
44
76
 
77
+ @abc.abstractmethod
78
+ def stats(self) -> dict:
79
+ pass
80
+
45
81
  def set_filter(self, filter):
46
82
  self.filter = filter
47
83
  if self._client:
@@ -50,21 +86,32 @@ class Client:
50
86
  @property
51
87
  def client(self):
52
88
  if not self._client:
53
- LOG.info("Creating APRS client")
54
- self._client = self.setup_connection()
55
- if self.filter:
56
- LOG.info("Creating APRS client filter")
57
- self._client.set_filter(self.filter)
89
+ self._create_client()
58
90
  return self._client
59
91
 
92
+ def _create_client(self):
93
+ self._client = self.setup_connection()
94
+ if self.filter:
95
+ LOG.info("Creating APRS client filter")
96
+ self._client.set_filter(self.filter)
97
+
98
+ def stop(self):
99
+ if self._client:
100
+ LOG.info("Stopping client connection.")
101
+ self._client.stop()
102
+
60
103
  def send(self, packet: core.Packet):
61
- packet_list.PacketList().tx(packet)
104
+ """Send a packet to the network."""
62
105
  self.client.send(packet)
63
106
 
107
+ @wrapt.synchronized(lock)
64
108
  def reset(self):
65
109
  """Call this to force a rebuild/reconnect."""
110
+ LOG.info("Resetting client connection.")
66
111
  if self._client:
112
+ self._client.close()
67
113
  del self._client
114
+ self._create_client()
68
115
  else:
69
116
  LOG.warning("Client not initialized, nothing to reset.")
70
117
 
@@ -89,11 +136,38 @@ class Client:
89
136
  def decode_packet(self, *args, **kwargs):
90
137
  pass
91
138
 
139
+ @abc.abstractmethod
140
+ def consumer(self, callback, blocking=False, immortal=False, raw=False):
141
+ pass
142
+
143
+ @abc.abstractmethod
144
+ def is_alive(self):
145
+ pass
146
+
147
+ @abc.abstractmethod
148
+ def close(self):
149
+ pass
150
+
92
151
 
93
152
  class APRSISClient(Client):
94
153
 
95
154
  _client = None
96
155
 
156
+ def __init__(self):
157
+ max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0}
158
+ self.max_delta = datetime.timedelta(**max_timeout)
159
+
160
+ def stats(self) -> dict:
161
+ stats = {}
162
+ if self.is_configured():
163
+ stats = {
164
+ "server_string": self._client.server_string,
165
+ "sever_keepalive": self._client.aprsd_keepalive,
166
+ "filter": self.filter,
167
+ }
168
+
169
+ return stats
170
+
97
171
  @staticmethod
98
172
  def is_enabled():
99
173
  # Defaults to True if the enabled flag is non existent
@@ -125,44 +199,56 @@ class APRSISClient(Client):
125
199
  return True
126
200
  return True
127
201
 
202
+ def _is_stale_connection(self):
203
+ delta = datetime.datetime.now() - self._client.aprsd_keepalive
204
+ if delta > self.max_delta:
205
+ LOG.error(f"Connection is stale, last heard {delta} ago.")
206
+ return True
207
+
128
208
  def is_alive(self):
129
209
  if self._client:
130
- return self._client.is_alive()
210
+ return self._client.is_alive() and not self._is_stale_connection()
131
211
  else:
212
+ LOG.warning(f"APRS_CLIENT {self._client} alive? NO!!!")
132
213
  return False
133
214
 
215
+ def close(self):
216
+ if self._client:
217
+ self._client.stop()
218
+ self._client.close()
219
+
134
220
  @staticmethod
135
221
  def transport():
136
222
  return TRANSPORT_APRSIS
137
223
 
138
224
  def decode_packet(self, *args, **kwargs):
139
225
  """APRS lib already decodes this."""
140
- return core.Packet.factory(args[0])
226
+ return core.factory(args[0])
141
227
 
142
228
  def setup_connection(self):
143
229
  user = CONF.aprs_network.login
144
230
  password = CONF.aprs_network.password
145
231
  host = CONF.aprs_network.host
146
232
  port = CONF.aprs_network.port
147
- connected = False
233
+ self.connected = False
148
234
  backoff = 1
149
235
  aprs_client = None
150
- while not connected:
236
+ while not self.connected:
151
237
  try:
152
- LOG.info("Creating aprslib client")
238
+ LOG.info(f"Creating aprslib client({host}:{port}) and logging in {user}.")
153
239
  aprs_client = aprsis.Aprsdis(user, passwd=password, host=host, port=port)
154
240
  # Force the log to be the same
155
241
  aprs_client.logger = LOG
156
242
  aprs_client.connect()
157
- connected = True
243
+ self.connected = True
158
244
  backoff = 1
159
245
  except LoginError as e:
160
246
  LOG.error(f"Failed to login to APRS-IS Server '{e}'")
161
- connected = False
247
+ self.connected = False
162
248
  time.sleep(backoff)
163
249
  except Exception as e:
164
250
  LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
165
- connected = False
251
+ self.connected = False
166
252
  time.sleep(backoff)
167
253
  # Don't allow the backoff to go to inifinity.
168
254
  if backoff > 5:
@@ -170,15 +256,28 @@ class APRSISClient(Client):
170
256
  else:
171
257
  backoff += 1
172
258
  continue
173
- LOG.debug(f"Logging in to APRS-IS with user '{user}'")
174
259
  self._client = aprs_client
175
260
  return aprs_client
176
261
 
262
+ def consumer(self, callback, blocking=False, immortal=False, raw=False):
263
+ self._client.consumer(
264
+ callback, blocking=blocking,
265
+ immortal=immortal, raw=raw,
266
+ )
267
+
177
268
 
178
269
  class KISSClient(Client):
179
270
 
180
271
  _client = None
181
272
 
273
+ def stats(self) -> dict:
274
+ stats = {}
275
+ if self.is_configured():
276
+ return {
277
+ "transport": self.transport(),
278
+ }
279
+ return stats
280
+
182
281
  @staticmethod
183
282
  def is_enabled():
184
283
  """Return if tcp or serial KISS is enabled."""
@@ -217,6 +316,10 @@ class KISSClient(Client):
217
316
  else:
218
317
  return False
219
318
 
319
+ def close(self):
320
+ if self._client:
321
+ self._client.stop()
322
+
220
323
  @staticmethod
221
324
  def transport():
222
325
  if CONF.kiss_serial.enabled:
@@ -238,7 +341,7 @@ class KISSClient(Client):
238
341
  # LOG.debug(f"Decoding {msg}")
239
342
 
240
343
  raw = aprslib.parse(str(frame))
241
- packet = core.Packet.factory(raw)
344
+ packet = core.factory(raw)
242
345
  if isinstance(packet, core.ThirdParty):
243
346
  return packet.subpacket
244
347
  else:
@@ -246,11 +349,18 @@ class KISSClient(Client):
246
349
 
247
350
  def setup_connection(self):
248
351
  self._client = kiss.KISS3Client()
352
+ self.connected = True
249
353
  return self._client
250
354
 
355
+ def consumer(self, callback, blocking=False, immortal=False, raw=False):
356
+ self._client.consumer(callback)
357
+
251
358
 
252
359
  class APRSDFakeClient(Client, metaclass=trace.TraceWrapperMetaclass):
253
360
 
361
+ def stats(self) -> dict:
362
+ return {}
363
+
254
364
  @staticmethod
255
365
  def is_enabled():
256
366
  if CONF.fake_client.enabled:
@@ -264,7 +374,11 @@ class APRSDFakeClient(Client, metaclass=trace.TraceWrapperMetaclass):
264
374
  def is_alive(self):
265
375
  return True
266
376
 
377
+ def close(self):
378
+ pass
379
+
267
380
  def setup_connection(self):
381
+ self.connected = True
268
382
  return fake.APRSDFakeClient()
269
383
 
270
384
  @staticmethod
@@ -304,7 +418,6 @@ class ClientFactory:
304
418
  key = TRANSPORT_FAKE
305
419
 
306
420
  builder = self._builders.get(key)
307
- LOG.debug(f"Creating client {key}")
308
421
  if not builder:
309
422
  raise ValueError(key)
310
423
  return builder()
@@ -1,3 +1,4 @@
1
+ import datetime
1
2
  import logging
2
3
  import select
3
4
  import threading
@@ -11,7 +12,6 @@ from aprslib.exceptions import (
11
12
  import wrapt
12
13
 
13
14
  import aprsd
14
- from aprsd import stats
15
15
  from aprsd.packets import core
16
16
 
17
17
 
@@ -24,6 +24,9 @@ class Aprsdis(aprslib.IS):
24
24
  # flag to tell us to stop
25
25
  thread_stop = False
26
26
 
27
+ # date for last time we heard from the server
28
+ aprsd_keepalive = datetime.datetime.now()
29
+
27
30
  # timeout in seconds
28
31
  select_timeout = 1
29
32
  lock = threading.Lock()
@@ -142,7 +145,6 @@ class Aprsdis(aprslib.IS):
142
145
 
143
146
  self.logger.info(f"Connected to {server_string}")
144
147
  self.server_string = server_string
145
- stats.APRSDStats().set_aprsis_server(server_string)
146
148
 
147
149
  except LoginError as e:
148
150
  self.logger.error(str(e))
@@ -176,13 +178,14 @@ class Aprsdis(aprslib.IS):
176
178
  try:
177
179
  for line in self._socket_readlines(blocking):
178
180
  if line[0:1] != b"#":
181
+ self.aprsd_keepalive = datetime.datetime.now()
179
182
  if raw:
180
183
  callback(line)
181
184
  else:
182
185
  callback(self._parse(line))
183
186
  else:
184
187
  self.logger.debug("Server: %s", line.decode("utf8"))
185
- stats.APRSDStats().set_aprsis_keepalive()
188
+ self.aprsd_keepalive = datetime.datetime.now()
186
189
  except ParseError as exp:
187
190
  self.logger.log(
188
191
  11,
@@ -67,7 +67,7 @@ class APRSDFakeClient(metaclass=trace.TraceWrapperMetaclass):
67
67
  # Generate packets here?
68
68
  raw = "GTOWN>APDW16,WIDE1-1,WIDE2-1:}KM6LYW-9>APZ100,TCPIP,GTOWN*::KM6LYW :KM6LYW: 19 Miles SW"
69
69
  pkt_raw = aprslib.parse(raw)
70
- pkt = core.Packet.factory(pkt_raw)
70
+ pkt = core.factory(pkt_raw)
71
71
  callback(packet=pkt)
72
72
  LOG.debug(f"END blocking FAKE consumer {self}")
73
73
  time.sleep(8)
@@ -81,7 +81,7 @@ class KISS3Client:
81
81
  LOG.error("Failed to parse bytes received from KISS interface.")
82
82
  LOG.exception(ex)
83
83
 
84
- def consumer(self, callback, blocking=False, immortal=False, raw=False):
84
+ def consumer(self, callback):
85
85
  LOG.debug("Start blocking KISS consumer")
86
86
  self._parse_callback = callback
87
87
  self.kiss.read(callback=self.parse_frame, min_frames=None)
@@ -0,0 +1,22 @@
1
+ import click
2
+ import click.shell_completion
3
+
4
+ from aprsd.main import cli
5
+
6
+
7
+ CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
8
+
9
+
10
+ @cli.command()
11
+ @click.argument("shell", type=click.Choice(list(click.shell_completion._available_shells)))
12
+ def completion(shell):
13
+ """Show the shell completion code"""
14
+ from click.utils import _detect_program_name
15
+
16
+ cls = click.shell_completion.get_completion_class(shell)
17
+ prog_name = _detect_program_name()
18
+ complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").upper()
19
+ print(cls(cli, {}, prog_name, complete_var).source())
20
+ print("# Add the following line to your shell configuration file to have aprsd command line completion")
21
+ print("# but remove the leading '#' character.")
22
+ print(f"# eval \"$(aprsd completion {shell})\"")