aprsd 3.4.3__py3-none-any.whl → 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. aprsd/cli_helper.py +12 -5
  2. aprsd/client/aprsis.py +68 -17
  3. aprsd/client/base.py +60 -12
  4. aprsd/client/drivers/aprsis.py +11 -5
  5. aprsd/client/drivers/fake.py +15 -20
  6. aprsd/client/factory.py +6 -3
  7. aprsd/client/fake.py +5 -4
  8. aprsd/client/kiss.py +43 -7
  9. aprsd/client/stats.py +2 -22
  10. aprsd/cmds/completion.py +7 -4
  11. aprsd/cmds/dev.py +39 -43
  12. aprsd/cmds/fetch_stats.py +221 -69
  13. aprsd/cmds/healthcheck.py +7 -5
  14. aprsd/cmds/list_plugins.py +140 -134
  15. aprsd/cmds/listen.py +102 -11
  16. aprsd/cmds/server.py +71 -37
  17. aprsd/conf/__init__.py +1 -2
  18. aprsd/conf/client.py +3 -4
  19. aprsd/conf/common.py +29 -92
  20. aprsd/conf/log.py +2 -4
  21. aprsd/conf/opts.py +5 -4
  22. aprsd/conf/plugin_common.py +11 -121
  23. aprsd/exception.py +2 -0
  24. aprsd/log/log.py +7 -46
  25. aprsd/main.py +19 -9
  26. aprsd/packets/__init__.py +14 -4
  27. aprsd/packets/core.py +82 -91
  28. aprsd/packets/log.py +8 -8
  29. aprsd/packets/packet_list.py +18 -25
  30. aprsd/plugin.py +33 -15
  31. aprsd/plugins/fortune.py +2 -2
  32. aprsd/plugins/notify.py +1 -4
  33. aprsd/plugins/weather.py +10 -8
  34. aprsd/stats/__init__.py +0 -2
  35. aprsd/stats/collector.py +11 -3
  36. aprsd/threads/__init__.py +3 -2
  37. aprsd/threads/aprsd.py +57 -10
  38. aprsd/threads/{keep_alive.py → keepalive.py} +14 -37
  39. aprsd/threads/registry.py +3 -3
  40. aprsd/threads/rx.py +48 -17
  41. aprsd/threads/stats.py +2 -2
  42. aprsd/threads/tx.py +34 -10
  43. aprsd/utils/__init__.py +62 -15
  44. aprsd/utils/counter.py +15 -12
  45. aprsd/utils/json.py +9 -4
  46. aprsd/utils/keepalive_collector.py +55 -0
  47. aprsd/utils/trace.py +4 -4
  48. aprsd-4.0.0.dist-info/AUTHORS +1 -0
  49. aprsd-4.0.0.dist-info/METADATA +293 -0
  50. aprsd-4.0.0.dist-info/RECORD +74 -0
  51. {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/WHEEL +1 -1
  52. aprsd/cmds/webchat.py +0 -674
  53. aprsd/conf/plugin_email.py +0 -105
  54. aprsd/plugins/email.py +0 -709
  55. aprsd/plugins/location.py +0 -179
  56. aprsd/threads/log_monitor.py +0 -121
  57. aprsd/web/__init__.py +0 -0
  58. aprsd/web/admin/__init__.py +0 -0
  59. aprsd/web/admin/static/css/index.css +0 -84
  60. aprsd/web/admin/static/css/prism.css +0 -4
  61. aprsd/web/admin/static/css/tabs.css +0 -35
  62. aprsd/web/admin/static/images/Untitled.png +0 -0
  63. aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
  64. aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
  65. aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
  66. aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
  67. aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
  68. aprsd/web/admin/static/js/charts.js +0 -235
  69. aprsd/web/admin/static/js/echarts.js +0 -465
  70. aprsd/web/admin/static/js/logs.js +0 -26
  71. aprsd/web/admin/static/js/main.js +0 -231
  72. aprsd/web/admin/static/js/prism.js +0 -12
  73. aprsd/web/admin/static/js/send-message.js +0 -114
  74. aprsd/web/admin/static/js/tabs.js +0 -28
  75. aprsd/web/admin/templates/index.html +0 -196
  76. aprsd/web/chat/static/css/chat.css +0 -115
  77. aprsd/web/chat/static/css/index.css +0 -66
  78. aprsd/web/chat/static/css/style.css.map +0 -1
  79. aprsd/web/chat/static/css/tabs.css +0 -41
  80. aprsd/web/chat/static/css/upstream/bootstrap.min.css +0 -6
  81. aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
  82. aprsd/web/chat/static/css/upstream/google-fonts.css +0 -23
  83. aprsd/web/chat/static/css/upstream/jquery-ui.css +0 -1311
  84. aprsd/web/chat/static/css/upstream/jquery.toast.css +0 -28
  85. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  86. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  87. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  88. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  89. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
  90. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
  91. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
  92. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  93. aprsd/web/chat/static/images/Untitled.png +0 -0
  94. aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
  95. aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
  96. aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
  97. aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
  98. aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
  99. aprsd/web/chat/static/images/globe.svg +0 -3
  100. aprsd/web/chat/static/js/gps.js +0 -84
  101. aprsd/web/chat/static/js/main.js +0 -45
  102. aprsd/web/chat/static/js/send-message.js +0 -585
  103. aprsd/web/chat/static/js/tabs.js +0 -28
  104. aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +0 -7
  105. aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +0 -2
  106. aprsd/web/chat/static/js/upstream/jquery-ui.min.js +0 -13
  107. aprsd/web/chat/static/js/upstream/jquery.toast.js +0 -374
  108. aprsd/web/chat/static/js/upstream/semantic.min.js +0 -11
  109. aprsd/web/chat/static/js/upstream/socket.io.min.js +0 -7
  110. aprsd/web/chat/templates/index.html +0 -139
  111. aprsd/wsgi.py +0 -315
  112. aprsd-3.4.3.dist-info/AUTHORS +0 -13
  113. aprsd-3.4.3.dist-info/METADATA +0 -793
  114. aprsd-3.4.3.dist-info/RECORD +0 -133
  115. {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/LICENSE +0 -0
  116. {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/entry_points.txt +0 -0
  117. {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/top_level.txt +0 -0
aprsd/log/log.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from logging.handlers import QueueHandler
3
2
  import queue
4
3
  import sys
5
4
 
@@ -8,7 +7,6 @@ from oslo_config import cfg
8
7
 
9
8
  from aprsd.conf import log as conf_log
10
9
 
11
-
12
10
  CONF = cfg.CONF
13
11
  # LOG = logging.getLogger("APRSD")
14
12
  LOG = logger
@@ -19,6 +17,7 @@ class QueueLatest(queue.Queue):
19
17
 
20
18
  This prevents the queue from blowing up in size.
21
19
  """
20
+
22
21
  def put(self, *args, **kwargs):
23
22
  try:
24
23
  super().put(*args, **kwargs)
@@ -44,7 +43,9 @@ class InterceptHandler(logging.Handler):
44
43
  frame = frame.f_back
45
44
  depth += 1
46
45
 
47
- logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
46
+ logger.opt(depth=depth, exception=record.exc_info).log(
47
+ level, record.getMessage()
48
+ )
48
49
 
49
50
 
50
51
  # Setup the log faciility
@@ -60,43 +61,18 @@ def setup_logging(loglevel=None, quiet=False):
60
61
  logging.root.handlers = [InterceptHandler()]
61
62
  logging.root.setLevel(log_level)
62
63
 
63
- imap_list = [
64
- "imapclient.imaplib", "imaplib", "imapclient",
65
- "imapclient.util",
66
- ]
67
- aprslib_list = [
64
+ # We don't really want to see the aprslib parsing debug output.
65
+ disable_list = [
68
66
  "aprslib",
69
67
  "aprslib.parsing",
70
68
  "aprslib.exceptions",
71
69
  ]
72
- webserver_list = [
73
- "werkzeug",
74
- "werkzeug._internal",
75
- "socketio",
76
- "urllib3.connectionpool",
77
- "chardet",
78
- "chardet.charsetgroupprober",
79
- "chardet.eucjpprober",
80
- "chardet.mbcharsetprober",
81
- ]
82
-
83
- # We don't really want to see the aprslib parsing debug output.
84
- disable_list = imap_list + aprslib_list + webserver_list
85
70
 
86
71
  # remove every other logger's handlers
87
72
  # and propagate to root logger
88
73
  for name in logging.root.manager.loggerDict.keys():
89
74
  logging.getLogger(name).handlers = []
90
- if name in disable_list:
91
- logging.getLogger(name).propagate = False
92
- else:
93
- logging.getLogger(name).propagate = True
94
-
95
- if CONF.webchat.disable_url_request_logging:
96
- for name in webserver_list:
97
- logging.getLogger(name).handlers = []
98
- logging.getLogger(name).propagate = True
99
- logging.getLogger(name).setLevel(logging.ERROR)
75
+ logging.getLogger(name).propagate = name not in disable_list
100
76
 
101
77
  handlers = [
102
78
  {
@@ -118,21 +94,6 @@ def setup_logging(loglevel=None, quiet=False):
118
94
  },
119
95
  )
120
96
 
121
- if CONF.email_plugin.enabled and CONF.email_plugin.debug:
122
- for name in imap_list:
123
- logging.getLogger(name).propagate = True
124
-
125
- if CONF.admin.web_enabled:
126
- qh = QueueHandler(logging_queue)
127
- handlers.append(
128
- {
129
- "sink": qh, "serialize": False,
130
- "format": CONF.logging.logformat,
131
- "level": log_level,
132
- "colorize": False,
133
- },
134
- )
135
-
136
97
  # configure loguru
137
98
  logger.configure(handlers=handlers)
138
99
  logger.level("DEBUG", color="<fg #BABABA>")
aprsd/main.py CHANGED
@@ -22,11 +22,11 @@
22
22
  # python included libs
23
23
  import datetime
24
24
  import importlib.metadata as imp
25
- from importlib.metadata import version as metadata_version
26
25
  import logging
27
26
  import signal
28
27
  import sys
29
28
  import time
29
+ from importlib.metadata import version as metadata_version
30
30
 
31
31
  import click
32
32
  from oslo_config import cfg, generator
@@ -36,7 +36,6 @@ import aprsd
36
36
  from aprsd import cli_helper, packets, threads, utils
37
37
  from aprsd.stats import collector
38
38
 
39
-
40
39
  # setup the global logger
41
40
  # log.basicConfig(level=log.DEBUG) # level=10
42
41
  CONF = cfg.CONF
@@ -54,8 +53,14 @@ def cli(ctx):
54
53
 
55
54
  def load_commands():
56
55
  from .cmds import ( # noqa
57
- completion, dev, fetch_stats, healthcheck, list_plugins, listen,
58
- send_message, server, webchat,
56
+ completion,
57
+ dev,
58
+ fetch_stats,
59
+ healthcheck,
60
+ list_plugins,
61
+ listen,
62
+ send_message,
63
+ server,
59
64
  )
60
65
 
61
66
 
@@ -79,11 +84,15 @@ def signal_handler(sig, frame):
79
84
  ),
80
85
  )
81
86
  time.sleep(1.5)
82
- packets.PacketTrack().save()
83
- packets.WatchList().save()
84
- packets.SeenList().save()
85
- packets.PacketList().save()
86
- collector.Collector().collect()
87
+ try:
88
+ packets.PacketTrack().save()
89
+ packets.WatchList().save()
90
+ packets.SeenList().save()
91
+ packets.PacketList().save()
92
+ collector.Collector().collect()
93
+ except Exception as e:
94
+ LOG.error(f"Failed to save data: {e}")
95
+ sys.exit(0)
87
96
  # signal.signal(signal.SIGTERM, sys.exit(0))
88
97
  # sys.exit(0)
89
98
 
@@ -111,6 +120,7 @@ def sample_config(ctx):
111
120
 
112
121
  def _get_selected_entry_points():
113
122
  import sys
123
+
114
124
  if sys.version_info < (3, 10):
115
125
  all = imp.entry_points()
116
126
  selected = []
aprsd/packets/__init__.py CHANGED
@@ -1,15 +1,25 @@
1
1
  from aprsd.packets import collector
2
2
  from aprsd.packets.core import ( # noqa: F401
3
- AckPacket, BeaconPacket, BulletinPacket, GPSPacket, MessagePacket,
4
- MicEPacket, ObjectPacket, Packet, RejectPacket, StatusPacket,
5
- ThirdPartyPacket, UnknownPacket, WeatherPacket, factory,
3
+ AckPacket,
4
+ BeaconPacket,
5
+ BulletinPacket,
6
+ GPSPacket,
7
+ MessagePacket,
8
+ MicEPacket,
9
+ ObjectPacket,
10
+ Packet,
11
+ RejectPacket,
12
+ StatusPacket,
13
+ ThirdPartyPacket,
14
+ UnknownPacket,
15
+ WeatherPacket,
16
+ factory,
6
17
  )
7
18
  from aprsd.packets.packet_list import PacketList # noqa: F401
8
19
  from aprsd.packets.seen_list import SeenList # noqa: F401
9
20
  from aprsd.packets.tracker import PacketTrack # noqa: F401
10
21
  from aprsd.packets.watch_list import WatchList # noqa: F401
11
22
 
12
-
13
23
  # Register all the packet tracking objects.
14
24
  collector.PacketCollector().register(PacketList)
15
25
  collector.PacketCollector().register(SeenList)
aprsd/packets/core.py CHANGED
@@ -1,20 +1,23 @@
1
- from dataclasses import dataclass, field
2
- from datetime import datetime
3
1
  import logging
4
2
  import re
5
3
  import time
4
+ from dataclasses import dataclass, field
5
+ from datetime import datetime
6
+
6
7
  # Due to a failure in python 3.8
7
8
  from typing import Any, List, Optional, Type, TypeVar, Union
8
9
 
9
10
  from aprslib import util as aprslib_util
10
11
  from dataclasses_json import (
11
- CatchAll, DataClassJsonMixin, Undefined, dataclass_json,
12
+ CatchAll,
13
+ DataClassJsonMixin,
14
+ Undefined,
15
+ dataclass_json,
12
16
  )
13
17
  from loguru import logger
14
18
 
15
19
  from aprsd.utils import counter
16
20
 
17
-
18
21
  # For mypy to be happy
19
22
  A = TypeVar("A", bound="DataClassJsonMixin")
20
23
  Json = Union[dict, list, str, int, float, bool, None]
@@ -51,7 +54,7 @@ def _init_send_time():
51
54
  return NO_DATE
52
55
 
53
56
 
54
- def _init_msgNo(): # noqa: N802
57
+ def _init_msgNo(): # noqa: N802
55
58
  """For some reason __post__init doesn't get called.
56
59
 
57
60
  So in order to initialize the msgNo field in the packet
@@ -63,15 +66,11 @@ def _init_msgNo(): # noqa: N802
63
66
 
64
67
 
65
68
  def _translate_fields(raw: dict) -> dict:
66
- translate_fields = {
67
- "from": "from_call",
68
- "to": "to_call",
69
- }
70
- # First translate some fields
71
- for key in translate_fields:
72
- if key in raw:
73
- raw[translate_fields[key]] = raw[key]
74
- del raw[key]
69
+ # Direct key checks instead of iteration
70
+ if "from" in raw:
71
+ raw["from_call"] = raw.pop("from")
72
+ if "to" in raw:
73
+ raw["to_call"] = raw.pop("to")
75
74
 
76
75
  # addresse overrides to_call
77
76
  if "addresse" in raw:
@@ -88,14 +87,16 @@ class Packet:
88
87
  to_call: Optional[str] = field(default=None)
89
88
  addresse: Optional[str] = field(default=None)
90
89
  format: Optional[str] = field(default=None)
91
- msgNo: Optional[str] = field(default=None) # noqa: N815
92
- ackMsgNo: Optional[str] = field(default=None) # noqa: N815
90
+ msgNo: Optional[str] = field(default=None) # noqa: N815
91
+ ackMsgNo: Optional[str] = field(default=None) # noqa: N815
93
92
  packet_type: Optional[str] = field(default=None)
94
93
  timestamp: float = field(default_factory=_init_timestamp, compare=False, hash=False)
95
94
  # Holds the raw text string to be sent over the wire
96
95
  # or holds the raw string from input packet
97
96
  raw: Optional[str] = field(default=None, compare=False, hash=False)
98
- raw_dict: dict = field(repr=False, default_factory=lambda: {}, compare=False, hash=False)
97
+ raw_dict: dict = field(
98
+ repr=False, default_factory=lambda: {}, compare=False, hash=False
99
+ )
99
100
  # Built by calling prepare(). raw needs this built first.
100
101
  payload: Optional[str] = field(default=None)
101
102
 
@@ -103,6 +104,8 @@ class Packet:
103
104
  send_count: int = field(repr=False, default=0, compare=False, hash=False)
104
105
  retry_count: int = field(repr=False, default=3, compare=False, hash=False)
105
106
  last_send_time: float = field(repr=False, default=0, compare=False, hash=False)
107
+ # Was the packet acked?
108
+ acked: bool = field(repr=False, default=False, compare=False, hash=False)
106
109
 
107
110
  # Do we allow this packet to be saved to send later?
108
111
  allow_delay: bool = field(repr=False, default=True, compare=False, hash=False)
@@ -110,11 +113,7 @@ class Packet:
110
113
  via: Optional[str] = field(default=None, compare=False, hash=False)
111
114
 
112
115
  def get(self, key: str, default: Optional[str] = None):
113
- """Emulate a getter on a dict."""
114
- if hasattr(self, key):
115
- return getattr(self, key)
116
- else:
117
- return default
116
+ return getattr(self, key, default)
118
117
 
119
118
  @property
120
119
  def key(self) -> str:
@@ -135,10 +134,10 @@ class Packet:
135
134
  msg = self._filter_for_send(self.raw).rstrip("\n")
136
135
  return msg
137
136
 
138
- def prepare(self) -> None:
137
+ def prepare(self, create_msg_number=False) -> None:
139
138
  """Do stuff here that is needed prior to sending over the air."""
140
139
  # now build the raw message for sending
141
- if not self.msgNo:
140
+ if not self.msgNo and create_msg_number:
142
141
  self.msgNo = _init_msgNo()
143
142
  self._build_payload()
144
143
  self._build_raw()
@@ -146,12 +145,12 @@ class Packet:
146
145
  def _build_payload(self) -> None:
147
146
  """The payload is the non headers portion of the packet."""
148
147
  if not self.to_call:
149
- raise ValueError("to_call isn't set. Must set to_call before calling prepare()")
148
+ raise ValueError(
149
+ "to_call isn't set. Must set to_call before calling prepare()"
150
+ )
150
151
 
151
152
  # The base packet class has no real payload
152
- self.payload = (
153
- f":{self.to_call.ljust(9)}"
154
- )
153
+ self.payload = f":{self.to_call.ljust(9)}"
155
154
 
156
155
  def _build_raw(self) -> None:
157
156
  """Build the self.raw which is what is sent over the air."""
@@ -172,8 +171,10 @@ class Packet:
172
171
  message = msg[:67]
173
172
  # We all miss George Carlin
174
173
  return re.sub(
175
- "fuck|shit|cunt|piss|cock|bitch", "****",
176
- message, flags=re.IGNORECASE,
174
+ "fuck|shit|cunt|piss|cock|bitch",
175
+ "****",
176
+ message,
177
+ flags=re.IGNORECASE,
177
178
  )
178
179
 
179
180
  def __str__(self) -> str:
@@ -220,10 +221,7 @@ class BulletinPacket(Packet):
220
221
  return f"BLN{self.bid} {self.message_text}"
221
222
 
222
223
  def _build_payload(self) -> None:
223
- self.payload = (
224
- f":BLN{self.bid:<9}"
225
- f":{self.message_text}"
226
- )
224
+ self.payload = f":BLN{self.bid:<9}" f":{self.message_text}"
227
225
 
228
226
 
229
227
  @dataclass_json
@@ -252,11 +250,17 @@ class MessagePacket(Packet):
252
250
  return self._filter_for_send(self.message_text).rstrip("\n")
253
251
 
254
252
  def _build_payload(self):
255
- self.payload = ":{}:{}{{{}".format(
256
- self.to_call.ljust(9),
257
- self._filter_for_send(self.message_text).rstrip("\n"),
258
- str(self.msgNo),
259
- )
253
+ if self.msgNo:
254
+ self.payload = ":{}:{}{{{}".format(
255
+ self.to_call.ljust(9),
256
+ self._filter_for_send(self.message_text).rstrip("\n"),
257
+ str(self.msgNo),
258
+ )
259
+ else:
260
+ self.payload = ":{}:{}".format(
261
+ self.to_call.ljust(9),
262
+ self._filter_for_send(self.message_text).rstrip("\n"),
263
+ )
260
264
 
261
265
 
262
266
  @dataclass_json
@@ -335,10 +339,7 @@ class GPSPacket(Packet):
335
339
  self.payload = "".join(payload)
336
340
 
337
341
  def _build_raw(self):
338
- self.raw = (
339
- f"{self.from_call}>{self.to_call},WIDE2-1:"
340
- f"{self.payload}"
341
- )
342
+ self.raw = f"{self.from_call}>{self.to_call},WIDE2-1:" f"{self.payload}"
342
343
 
343
344
  @property
344
345
  def human_info(self) -> str:
@@ -370,10 +371,7 @@ class BeaconPacket(GPSPacket):
370
371
  lat = aprslib_util.latitude_to_ddm(self.latitude)
371
372
  lon = aprslib_util.longitude_to_ddm(self.longitude)
372
373
 
373
- self.payload = (
374
- f"@{time_zulu}z{lat}{self.symbol_table}"
375
- f"{lon}"
376
- )
374
+ self.payload = f"@{time_zulu}z{lat}{self.symbol_table}" f"{lon}"
377
375
 
378
376
  if self.comment:
379
377
  comment = self._filter_for_send(self.comment)
@@ -382,10 +380,7 @@ class BeaconPacket(GPSPacket):
382
380
  self.payload = f"{self.payload}{self.symbol}APRSD Beacon"
383
381
 
384
382
  def _build_raw(self):
385
- self.raw = (
386
- f"{self.from_call}>APZ100:"
387
- f"{self.payload}"
388
- )
383
+ self.raw = f"{self.from_call}>APZ100:" f"{self.payload}"
389
384
 
390
385
  @property
391
386
  def key(self) -> str:
@@ -393,7 +388,7 @@ class BeaconPacket(GPSPacket):
393
388
  if self.raw_timestamp:
394
389
  return f"{self.from_call}:{self.raw_timestamp}"
395
390
  else:
396
- return f"{self.from_call}:{self.human_info.replace(' ','')}"
391
+ return f"{self.from_call}:{self.human_info.replace(' ', '')}"
397
392
 
398
393
  @property
399
394
  def human_info(self) -> str:
@@ -449,7 +444,7 @@ class TelemetryPacket(GPSPacket):
449
444
  if self.raw_timestamp:
450
445
  return f"{self.from_call}:{self.raw_timestamp}"
451
446
  else:
452
- return f"{self.from_call}:{self.human_info.replace(' ','')}"
447
+ return f"{self.from_call}:{self.human_info.replace(' ', '')}"
453
448
 
454
449
  @property
455
450
  def human_info(self) -> str:
@@ -474,10 +469,7 @@ class ObjectPacket(GPSPacket):
474
469
  lat = aprslib_util.latitude_to_ddm(self.latitude)
475
470
  long = aprslib_util.longitude_to_ddm(self.longitude)
476
471
 
477
- self.payload = (
478
- f"*{time_zulu}z{lat}{self.symbol_table}"
479
- f"{long}{self.symbol}"
480
- )
472
+ self.payload = f"*{time_zulu}z{lat}{self.symbol_table}" f"{long}{self.symbol}"
481
473
 
482
474
  if self.comment:
483
475
  comment = self._filter_for_send(self.comment)
@@ -494,10 +486,7 @@ class ObjectPacket(GPSPacket):
494
486
  The frequency, uplink_tone, offset is part of the comment
495
487
  """
496
488
 
497
- self.raw = (
498
- f"{self.from_call}>APZ100:;{self.to_call:9s}"
499
- f"{self.payload}"
500
- )
489
+ self.raw = f"{self.from_call}>APZ100:;{self.to_call:9s}" f"{self.payload}"
501
490
 
502
491
  @property
503
492
  def human_info(self) -> str:
@@ -547,11 +536,13 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
547
536
  if "speed" in raw:
548
537
  del raw["speed"]
549
538
  # Let's adjust the rain numbers as well, since it's wrong
550
- raw["rain_1h"] = round((raw.get("rain_1h", 0) / .254) * .01, 3)
539
+ raw["rain_1h"] = round((raw.get("rain_1h", 0) / 0.254) * 0.01, 3)
551
540
  raw["weather"]["rain_1h"] = raw["rain_1h"]
552
- raw["rain_24h"] = round((raw.get("rain_24h", 0) / .254) * .01, 3)
541
+ raw["rain_24h"] = round((raw.get("rain_24h", 0) / 0.254) * 0.01, 3)
553
542
  raw["weather"]["rain_24h"] = raw["rain_24h"]
554
- raw["rain_since_midnight"] = round((raw.get("rain_since_midnight", 0) / .254) * .01, 3)
543
+ raw["rain_since_midnight"] = round(
544
+ (raw.get("rain_since_midnight", 0) / 0.254) * 0.01, 3
545
+ )
555
546
  raw["weather"]["rain_since_midnight"] = raw["rain_since_midnight"]
556
547
 
557
548
  if "wind_direction" not in raw:
@@ -593,26 +584,26 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
593
584
  def _build_payload(self):
594
585
  """Build an uncompressed weather packet
595
586
 
596
- Format =
587
+ Format =
597
588
 
598
- _CSE/SPDgXXXtXXXrXXXpXXXPXXXhXXbXXXXX%type NEW FORMAT APRS793 June 97
599
- NOT BACKWARD COMPATIBLE
589
+ _CSE/SPDgXXXtXXXrXXXpXXXPXXXhXXbXXXXX%type NEW FORMAT APRS793 June 97
590
+ NOT BACKWARD COMPATIBLE
600
591
 
601
592
 
602
- Where: CSE/SPD is wind direction and sustained 1 minute speed
603
- t is in degrees F
593
+ Where: CSE/SPD is wind direction and sustained 1 minute speed
594
+ t is in degrees F
604
595
 
605
- r is Rain per last 60 minutes
606
- 1.04 inches of rain will show as r104
607
- p is precipitation per last 24 hours (sliding 24 hour window)
608
- P is precip per last 24 hours since midnight
609
- b is Baro in tenths of a mb
610
- h is humidity in percent. 00=100
611
- g is Gust (peak winds in last 5 minutes)
612
- # is the raw rain counter for remote WX stations
613
- See notes on remotes below
614
- % shows software type d=Dos, m=Mac, w=Win, etc
615
- type shows type of WX instrument
596
+ r is Rain per last 60 minutes
597
+ 1.04 inches of rain will show as r104
598
+ p is precipitation per last 24 hours (sliding 24 hour window)
599
+ P is precip per last 24 hours since midnight
600
+ b is Baro in tenths of a mb
601
+ h is humidity in percent. 00=100
602
+ g is Gust (peak winds in last 5 minutes)
603
+ # is the raw rain counter for remote WX stations
604
+ See notes on remotes below
605
+ % shows software type d=Dos, m=Mac, w=Win, etc
606
+ type shows type of WX instrument
616
607
 
617
608
  """
618
609
  time_zulu = self._build_time_zulu()
@@ -622,17 +613,18 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
622
613
  f"{self.longitude}{self.symbol}",
623
614
  f"{self.wind_direction:03d}",
624
615
  # Speed = sustained 1 minute wind speed in mph
625
- f"{self.symbol_table}", f"{self.wind_speed:03.0f}",
616
+ f"{self.symbol_table}",
617
+ f"{self.wind_speed:03.0f}",
626
618
  # wind gust (peak wind speed in mph in the last 5 minutes)
627
619
  f"g{self.wind_gust:03.0f}",
628
620
  # Temperature in degrees F
629
621
  f"t{self.temperature:03.0f}",
630
622
  # Rainfall (in hundredths of an inch) in the last hour
631
- f"r{self.rain_1h*100:03.0f}",
623
+ f"r{self.rain_1h * 100:03.0f}",
632
624
  # Rainfall (in hundredths of an inch) in last 24 hours
633
- f"p{self.rain_24h*100:03.0f}",
625
+ f"p{self.rain_24h * 100:03.0f}",
634
626
  # Rainfall (in hundredths of an inch) since midnigt
635
- f"P{self.rain_since_midnight*100:03.0f}",
627
+ f"P{self.rain_since_midnight * 100:03.0f}",
636
628
  # Humidity
637
629
  f"h{self.humidity:02d}",
638
630
  # Barometric pressure (in tenths of millibars/tenths of hPascal)
@@ -644,11 +636,7 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
644
636
  self.payload = "".join(contents)
645
637
 
646
638
  def _build_raw(self):
647
-
648
- self.raw = (
649
- f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:"
650
- f"{self.payload}"
651
- )
639
+ self.raw = f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:" f"{self.payload}"
652
640
 
653
641
 
654
642
  @dataclass(unsafe_hash=True)
@@ -692,14 +680,17 @@ class UnknownPacket:
692
680
 
693
681
  All of the unknown attributes are stored in the unknown_fields
694
682
  """
683
+
695
684
  unknown_fields: CatchAll
696
685
  _type: str = "UnknownPacket"
697
686
  from_call: Optional[str] = field(default=None)
698
687
  to_call: Optional[str] = field(default=None)
699
- msgNo: str = field(default_factory=_init_msgNo) # noqa: N815
688
+ msgNo: str = field(default_factory=_init_msgNo) # noqa: N815
700
689
  format: Optional[str] = field(default=None)
701
690
  raw: Optional[str] = field(default=None)
702
- raw_dict: dict = field(repr=False, default_factory=lambda: {}, compare=False, hash=False)
691
+ raw_dict: dict = field(
692
+ repr=False, default_factory=lambda: {}, compare=False, hash=False
693
+ )
703
694
  path: List[str] = field(default_factory=list, compare=False, hash=False)
704
695
  packet_type: Optional[str] = field(default=None)
705
696
  via: Optional[str] = field(default=None, compare=False, hash=False)
aprsd/packets/log.py CHANGED
@@ -1,14 +1,13 @@
1
1
  import logging
2
2
  from typing import Optional
3
3
 
4
- from geopy.distance import geodesic
4
+ from haversine import Unit, haversine
5
5
  from loguru import logger
6
6
  from oslo_config import cfg
7
7
 
8
8
  from aprsd import utils
9
9
  from aprsd.packets.core import AckPacket, GPSPacket, RejectPacket
10
10
 
11
-
12
11
  LOG = logging.getLogger()
13
12
  LOGU = logger
14
13
  CONF = cfg.CONF
@@ -22,7 +21,9 @@ DISTANCE_COLOR = "fg #FF5733"
22
21
  DEGREES_COLOR = "fg #FFA900"
23
22
 
24
23
 
25
- def log_multiline(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> None:
24
+ def log_multiline(
25
+ packet, tx: Optional[bool] = False, header: Optional[bool] = True
26
+ ) -> None:
26
27
  """LOG a packet to the logfile."""
27
28
  if not CONF.enable_packet_logging:
28
29
  return
@@ -121,8 +122,7 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
121
122
  via_color = "green"
122
123
  arrow = f"<{via_color}>-></{via_color}>"
123
124
  logit.append(
124
- f"<cyan>{name}</cyan>"
125
- f":{packet.msgNo}",
125
+ f"<cyan>{name}</cyan>" f":{packet.msgNo}",
126
126
  )
127
127
 
128
128
  tmp = None
@@ -145,8 +145,8 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
145
145
 
146
146
  # is there distance information?
147
147
  if isinstance(packet, GPSPacket) and CONF.latitude and CONF.longitude:
148
- my_coords = (CONF.latitude, CONF.longitude)
149
- packet_coords = (packet.latitude, packet.longitude)
148
+ my_coords = (float(CONF.latitude), float(CONF.longitude))
149
+ packet_coords = (float(packet.latitude), float(packet.longitude))
150
150
  try:
151
151
  bearing = utils.calculate_initial_compass_bearing(my_coords, packet_coords)
152
152
  except Exception as e:
@@ -154,7 +154,7 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
154
154
  bearing = 0
155
155
  logit.append(
156
156
  f" : <{DEGREES_COLOR}>{utils.degrees_to_cardinal(bearing, full_string=True)}</{DEGREES_COLOR}>"
157
- f"<{DISTANCE_COLOR}>@{geodesic(my_coords, packet_coords).miles:.2f}miles</{DISTANCE_COLOR}>",
157
+ f"<{DISTANCE_COLOR}>@{haversine(my_coords, packet_coords, unit=Unit.MILES):.2f}miles</{DISTANCE_COLOR}>",
158
158
  )
159
159
 
160
160
  LOGU.opt(colors=True).info(" ".join(logit))