aprsd 3.4.4__py3-none-any.whl → 4.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. aprsd/cli_helper.py +12 -5
  2. aprsd/client/aprsis.py +31 -9
  3. aprsd/client/base.py +13 -2
  4. aprsd/client/drivers/aprsis.py +6 -3
  5. aprsd/client/drivers/fake.py +15 -20
  6. aprsd/client/factory.py +0 -2
  7. aprsd/client/fake.py +0 -2
  8. aprsd/client/kiss.py +17 -2
  9. aprsd/client/stats.py +1 -3
  10. aprsd/cmds/completion.py +7 -4
  11. aprsd/cmds/dev.py +38 -42
  12. aprsd/cmds/fetch_stats.py +140 -143
  13. aprsd/cmds/healthcheck.py +5 -3
  14. aprsd/cmds/list_plugins.py +140 -134
  15. aprsd/cmds/listen.py +13 -9
  16. aprsd/cmds/server.py +53 -27
  17. aprsd/conf/__init__.py +1 -2
  18. aprsd/conf/client.py +3 -4
  19. aprsd/conf/common.py +19 -93
  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 +10 -4
  26. aprsd/packets/__init__.py +14 -4
  27. aprsd/packets/core.py +57 -67
  28. aprsd/packets/log.py +8 -8
  29. aprsd/packets/packet_list.py +9 -6
  30. aprsd/plugin.py +22 -11
  31. aprsd/plugins/notify.py +1 -4
  32. aprsd/plugins/weather.py +10 -8
  33. aprsd/stats/collector.py +5 -2
  34. aprsd/threads/__init__.py +3 -2
  35. aprsd/threads/aprsd.py +12 -7
  36. aprsd/threads/{keep_alive.py → keepalive.py} +14 -45
  37. aprsd/threads/registry.py +3 -3
  38. aprsd/threads/rx.py +9 -6
  39. aprsd/threads/stats.py +2 -2
  40. aprsd/threads/tx.py +3 -4
  41. aprsd/utils/__init__.py +42 -10
  42. aprsd/utils/json.py +9 -4
  43. aprsd/utils/keepalive_collector.py +55 -0
  44. aprsd/utils/trace.py +0 -2
  45. aprsd-4.0.1.dist-info/AUTHORS +1 -0
  46. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/METADATA +307 -408
  47. aprsd-4.0.1.dist-info/RECORD +74 -0
  48. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/WHEEL +1 -1
  49. aprsd/cmds/admin.py +0 -57
  50. aprsd/cmds/webchat.py +0 -662
  51. aprsd/conf/plugin_email.py +0 -105
  52. aprsd/plugins/email.py +0 -715
  53. aprsd/plugins/location.py +0 -181
  54. aprsd/threads/log_monitor.py +0 -121
  55. aprsd/web/__init__.py +0 -0
  56. aprsd/web/admin/__init__.py +0 -0
  57. aprsd/web/admin/static/css/index.css +0 -84
  58. aprsd/web/admin/static/css/prism.css +0 -4
  59. aprsd/web/admin/static/css/tabs.css +0 -35
  60. aprsd/web/admin/static/images/Untitled.png +0 -0
  61. aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
  62. aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
  63. aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
  64. aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
  65. aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
  66. aprsd/web/admin/static/js/charts.js +0 -235
  67. aprsd/web/admin/static/js/echarts.js +0 -465
  68. aprsd/web/admin/static/js/logs.js +0 -26
  69. aprsd/web/admin/static/js/main.js +0 -231
  70. aprsd/web/admin/static/js/prism.js +0 -12
  71. aprsd/web/admin/static/js/send-message.js +0 -114
  72. aprsd/web/admin/static/js/tabs.js +0 -28
  73. aprsd/web/admin/templates/index.html +0 -196
  74. aprsd/web/chat/static/css/chat.css +0 -115
  75. aprsd/web/chat/static/css/index.css +0 -66
  76. aprsd/web/chat/static/css/style.css.map +0 -1
  77. aprsd/web/chat/static/css/tabs.css +0 -41
  78. aprsd/web/chat/static/css/upstream/bootstrap.min.css +0 -6
  79. aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
  80. aprsd/web/chat/static/css/upstream/google-fonts.css +0 -23
  81. aprsd/web/chat/static/css/upstream/jquery-ui.css +0 -1311
  82. aprsd/web/chat/static/css/upstream/jquery.toast.css +0 -28
  83. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  84. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  85. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  86. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  87. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
  88. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
  89. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
  90. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  91. aprsd/web/chat/static/images/Untitled.png +0 -0
  92. aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
  93. aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
  94. aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
  95. aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
  96. aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
  97. aprsd/web/chat/static/images/globe.svg +0 -3
  98. aprsd/web/chat/static/js/gps.js +0 -84
  99. aprsd/web/chat/static/js/main.js +0 -45
  100. aprsd/web/chat/static/js/send-message.js +0 -612
  101. aprsd/web/chat/static/js/tabs.js +0 -28
  102. aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +0 -7
  103. aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +0 -2
  104. aprsd/web/chat/static/js/upstream/jquery-ui.min.js +0 -13
  105. aprsd/web/chat/static/js/upstream/jquery.toast.js +0 -374
  106. aprsd/web/chat/static/js/upstream/semantic.min.js +0 -11
  107. aprsd/web/chat/static/js/upstream/socket.io.min.js +0 -7
  108. aprsd/web/chat/templates/index.html +0 -139
  109. aprsd/wsgi.py +0 -322
  110. aprsd-3.4.4.dist-info/AUTHORS +0 -13
  111. aprsd-3.4.4.dist-info/RECORD +0 -134
  112. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/LICENSE +0 -0
  113. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/entry_points.txt +0 -0
  114. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/top_level.txt +0 -0
aprsd/packets/core.py CHANGED
@@ -1,19 +1,22 @@
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
- from aprsd.utils import counter, trace
16
-
19
+ from aprsd.utils import counter
17
20
 
18
21
  # For mypy to be happy
19
22
  A = TypeVar("A", bound="DataClassJsonMixin")
@@ -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
@@ -84,14 +87,16 @@ class Packet:
84
87
  to_call: Optional[str] = field(default=None)
85
88
  addresse: Optional[str] = field(default=None)
86
89
  format: Optional[str] = field(default=None)
87
- msgNo: Optional[str] = field(default=None) # noqa: N815
88
- 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
89
92
  packet_type: Optional[str] = field(default=None)
90
93
  timestamp: float = field(default_factory=_init_timestamp, compare=False, hash=False)
91
94
  # Holds the raw text string to be sent over the wire
92
95
  # or holds the raw string from input packet
93
96
  raw: Optional[str] = field(default=None, compare=False, hash=False)
94
- 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
+ )
95
100
  # Built by calling prepare(). raw needs this built first.
96
101
  payload: Optional[str] = field(default=None)
97
102
 
@@ -129,7 +134,6 @@ class Packet:
129
134
  msg = self._filter_for_send(self.raw).rstrip("\n")
130
135
  return msg
131
136
 
132
- @trace.trace
133
137
  def prepare(self, create_msg_number=False) -> None:
134
138
  """Do stuff here that is needed prior to sending over the air."""
135
139
  # now build the raw message for sending
@@ -141,12 +145,12 @@ class Packet:
141
145
  def _build_payload(self) -> None:
142
146
  """The payload is the non headers portion of the packet."""
143
147
  if not self.to_call:
144
- 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
+ )
145
151
 
146
152
  # The base packet class has no real payload
147
- self.payload = (
148
- f":{self.to_call.ljust(9)}"
149
- )
153
+ self.payload = f":{self.to_call.ljust(9)}"
150
154
 
151
155
  def _build_raw(self) -> None:
152
156
  """Build the self.raw which is what is sent over the air."""
@@ -167,8 +171,10 @@ class Packet:
167
171
  message = msg[:67]
168
172
  # We all miss George Carlin
169
173
  return re.sub(
170
- "fuck|shit|cunt|piss|cock|bitch", "****",
171
- message, flags=re.IGNORECASE,
174
+ "fuck|shit|cunt|piss|cock|bitch",
175
+ "****",
176
+ message,
177
+ flags=re.IGNORECASE,
172
178
  )
173
179
 
174
180
  def __str__(self) -> str:
@@ -215,10 +221,7 @@ class BulletinPacket(Packet):
215
221
  return f"BLN{self.bid} {self.message_text}"
216
222
 
217
223
  def _build_payload(self) -> None:
218
- self.payload = (
219
- f":BLN{self.bid:<9}"
220
- f":{self.message_text}"
221
- )
224
+ self.payload = f":BLN{self.bid:<9}" f":{self.message_text}"
222
225
 
223
226
 
224
227
  @dataclass_json
@@ -336,10 +339,7 @@ class GPSPacket(Packet):
336
339
  self.payload = "".join(payload)
337
340
 
338
341
  def _build_raw(self):
339
- self.raw = (
340
- f"{self.from_call}>{self.to_call},WIDE2-1:"
341
- f"{self.payload}"
342
- )
342
+ self.raw = f"{self.from_call}>{self.to_call},WIDE2-1:" f"{self.payload}"
343
343
 
344
344
  @property
345
345
  def human_info(self) -> str:
@@ -371,10 +371,7 @@ class BeaconPacket(GPSPacket):
371
371
  lat = aprslib_util.latitude_to_ddm(self.latitude)
372
372
  lon = aprslib_util.longitude_to_ddm(self.longitude)
373
373
 
374
- self.payload = (
375
- f"@{time_zulu}z{lat}{self.symbol_table}"
376
- f"{lon}"
377
- )
374
+ self.payload = f"@{time_zulu}z{lat}{self.symbol_table}" f"{lon}"
378
375
 
379
376
  if self.comment:
380
377
  comment = self._filter_for_send(self.comment)
@@ -383,10 +380,7 @@ class BeaconPacket(GPSPacket):
383
380
  self.payload = f"{self.payload}{self.symbol}APRSD Beacon"
384
381
 
385
382
  def _build_raw(self):
386
- self.raw = (
387
- f"{self.from_call}>APZ100:"
388
- f"{self.payload}"
389
- )
383
+ self.raw = f"{self.from_call}>APZ100:" f"{self.payload}"
390
384
 
391
385
  @property
392
386
  def key(self) -> str:
@@ -475,10 +469,7 @@ class ObjectPacket(GPSPacket):
475
469
  lat = aprslib_util.latitude_to_ddm(self.latitude)
476
470
  long = aprslib_util.longitude_to_ddm(self.longitude)
477
471
 
478
- self.payload = (
479
- f"*{time_zulu}z{lat}{self.symbol_table}"
480
- f"{long}{self.symbol}"
481
- )
472
+ self.payload = f"*{time_zulu}z{lat}{self.symbol_table}" f"{long}{self.symbol}"
482
473
 
483
474
  if self.comment:
484
475
  comment = self._filter_for_send(self.comment)
@@ -495,10 +486,7 @@ class ObjectPacket(GPSPacket):
495
486
  The frequency, uplink_tone, offset is part of the comment
496
487
  """
497
488
 
498
- self.raw = (
499
- f"{self.from_call}>APZ100:;{self.to_call:9s}"
500
- f"{self.payload}"
501
- )
489
+ self.raw = f"{self.from_call}>APZ100:;{self.to_call:9s}" f"{self.payload}"
502
490
 
503
491
  @property
504
492
  def human_info(self) -> str:
@@ -548,11 +536,13 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
548
536
  if "speed" in raw:
549
537
  del raw["speed"]
550
538
  # Let's adjust the rain numbers as well, since it's wrong
551
- 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)
552
540
  raw["weather"]["rain_1h"] = raw["rain_1h"]
553
- 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)
554
542
  raw["weather"]["rain_24h"] = raw["rain_24h"]
555
- 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
+ )
556
546
  raw["weather"]["rain_since_midnight"] = raw["rain_since_midnight"]
557
547
 
558
548
  if "wind_direction" not in raw:
@@ -594,26 +584,26 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
594
584
  def _build_payload(self):
595
585
  """Build an uncompressed weather packet
596
586
 
597
- Format =
587
+ Format =
598
588
 
599
- _CSE/SPDgXXXtXXXrXXXpXXXPXXXhXXbXXXXX%type NEW FORMAT APRS793 June 97
600
- NOT BACKWARD COMPATIBLE
589
+ _CSE/SPDgXXXtXXXrXXXpXXXPXXXhXXbXXXXX%type NEW FORMAT APRS793 June 97
590
+ NOT BACKWARD COMPATIBLE
601
591
 
602
592
 
603
- Where: CSE/SPD is wind direction and sustained 1 minute speed
604
- t is in degrees F
593
+ Where: CSE/SPD is wind direction and sustained 1 minute speed
594
+ t is in degrees F
605
595
 
606
- r is Rain per last 60 minutes
607
- 1.04 inches of rain will show as r104
608
- p is precipitation per last 24 hours (sliding 24 hour window)
609
- P is precip per last 24 hours since midnight
610
- b is Baro in tenths of a mb
611
- h is humidity in percent. 00=100
612
- g is Gust (peak winds in last 5 minutes)
613
- # is the raw rain counter for remote WX stations
614
- See notes on remotes below
615
- % shows software type d=Dos, m=Mac, w=Win, etc
616
- 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
617
607
 
618
608
  """
619
609
  time_zulu = self._build_time_zulu()
@@ -623,7 +613,8 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
623
613
  f"{self.longitude}{self.symbol}",
624
614
  f"{self.wind_direction:03d}",
625
615
  # Speed = sustained 1 minute wind speed in mph
626
- f"{self.symbol_table}", f"{self.wind_speed:03.0f}",
616
+ f"{self.symbol_table}",
617
+ f"{self.wind_speed:03.0f}",
627
618
  # wind gust (peak wind speed in mph in the last 5 minutes)
628
619
  f"g{self.wind_gust:03.0f}",
629
620
  # Temperature in degrees F
@@ -645,11 +636,7 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
645
636
  self.payload = "".join(contents)
646
637
 
647
638
  def _build_raw(self):
648
-
649
- self.raw = (
650
- f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:"
651
- f"{self.payload}"
652
- )
639
+ self.raw = f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:" f"{self.payload}"
653
640
 
654
641
 
655
642
  @dataclass(unsafe_hash=True)
@@ -693,14 +680,17 @@ class UnknownPacket:
693
680
 
694
681
  All of the unknown attributes are stored in the unknown_fields
695
682
  """
683
+
696
684
  unknown_fields: CatchAll
697
685
  _type: str = "UnknownPacket"
698
686
  from_call: Optional[str] = field(default=None)
699
687
  to_call: Optional[str] = field(default=None)
700
- msgNo: str = field(default_factory=_init_msgNo) # noqa: N815
688
+ msgNo: str = field(default_factory=_init_msgNo) # noqa: N815
701
689
  format: Optional[str] = field(default=None)
702
690
  raw: Optional[str] = field(default=None)
703
- 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
+ )
704
694
  path: List[str] = field(default_factory=list, compare=False, hash=False)
705
695
  packet_type: Optional[str] = field(default=None)
706
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))
@@ -1,18 +1,18 @@
1
- from collections import OrderedDict
2
1
  import logging
2
+ from collections import OrderedDict
3
3
 
4
4
  from oslo_config import cfg
5
5
 
6
6
  from aprsd.packets import core
7
7
  from aprsd.utils import objectstore
8
8
 
9
-
10
9
  CONF = cfg.CONF
11
10
  LOG = logging.getLogger("APRSD")
12
11
 
13
12
 
14
13
  class PacketList(objectstore.ObjectStoreMixin):
15
14
  """Class to keep track of the packets we tx/rx."""
15
+
16
16
  _instance = None
17
17
  _total_rx: int = 0
18
18
  _total_tx: int = 0
@@ -38,7 +38,8 @@ class PacketList(objectstore.ObjectStoreMixin):
38
38
  self._add(packet)
39
39
  ptype = packet.__class__.__name__
40
40
  type_stats = self.data["types"].setdefault(
41
- ptype, {"tx": 0, "rx": 0},
41
+ ptype,
42
+ {"tx": 0, "rx": 0},
42
43
  )
43
44
  type_stats["rx"] += 1
44
45
 
@@ -49,7 +50,8 @@ class PacketList(objectstore.ObjectStoreMixin):
49
50
  self._add(packet)
50
51
  ptype = packet.__class__.__name__
51
52
  type_stats = self.data["types"].setdefault(
52
- ptype, {"tx": 0, "rx": 0},
53
+ ptype,
54
+ {"tx": 0, "rx": 0},
53
55
  )
54
56
  type_stats["tx"] += 1
55
57
 
@@ -86,10 +88,11 @@ class PacketList(objectstore.ObjectStoreMixin):
86
88
  with self.lock:
87
89
  # Get last N packets directly using list slicing
88
90
  packets_list = list(self.data.get("packets", {}).values())
89
- pkts = packets_list[-CONF.packet_list_stats_maxlen:][::-1]
91
+ pkts = packets_list[-CONF.packet_list_stats_maxlen :][::-1]
90
92
 
91
93
  stats = {
92
- "total_tracked": self._total_rx + self._total_tx, # Fixed typo: was rx + rx
94
+ "total_tracked": self._total_rx
95
+ + self._total_tx, # Fixed typo: was rx + rx
93
96
  "rx": self._total_rx,
94
97
  "tx": self._total_tx,
95
98
  "types": self.data.get("types", {}), # Changed default from [] to {}
aprsd/plugin.py CHANGED
@@ -8,14 +8,13 @@ import re
8
8
  import textwrap
9
9
  import threading
10
10
 
11
- from oslo_config import cfg
12
11
  import pluggy
12
+ from oslo_config import cfg
13
13
 
14
14
  import aprsd
15
15
  from aprsd import client, packets, threads
16
16
  from aprsd.packets import watch_list
17
17
 
18
-
19
18
  # setup the global logger
20
19
  CONF = cfg.CONF
21
20
  LOG = logging.getLogger("APRSD")
@@ -166,7 +165,8 @@ class APRSDWatchListPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
166
165
  except Exception as ex:
167
166
  LOG.error(
168
167
  "Plugin {} failed to process packet {}".format(
169
- self.__class__, ex,
168
+ self.__class__,
169
+ ex,
170
170
  ),
171
171
  )
172
172
  if result:
@@ -214,7 +214,9 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
214
214
  return result
215
215
 
216
216
  if not isinstance(packet, packets.MessagePacket):
217
- LOG.warning(f"{self.__class__.__name__} Got a {packet.__class__.__name__} ignoring")
217
+ LOG.warning(
218
+ f"{self.__class__.__name__} Got a {packet.__class__.__name__} ignoring"
219
+ )
218
220
  return packets.NULL_MESSAGE
219
221
 
220
222
  result = None
@@ -236,7 +238,8 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
236
238
  except Exception as ex:
237
239
  LOG.error(
238
240
  "Plugin {} failed to process packet {}".format(
239
- self.__class__, ex,
241
+ self.__class__,
242
+ ex,
240
243
  ),
241
244
  )
242
245
  LOG.exception(ex)
@@ -286,7 +289,8 @@ class HelpPlugin(APRSDRegexCommandPluginBase):
286
289
  reply = None
287
290
  for p in pm.get_plugins():
288
291
  if (
289
- p.enabled and isinstance(p, APRSDRegexCommandPluginBase)
292
+ p.enabled
293
+ and isinstance(p, APRSDRegexCommandPluginBase)
290
294
  and p.command_name.lower() == command_name
291
295
  ):
292
296
  reply = p.help()
@@ -345,6 +349,7 @@ class PluginManager:
345
349
 
346
350
  def stats(self, serializable=False) -> dict:
347
351
  """Collect and return stats for all plugins."""
352
+
348
353
  def full_name_with_qualname(obj):
349
354
  return "{}.{}".format(
350
355
  obj.__class__.__module__,
@@ -354,7 +359,6 @@ class PluginManager:
354
359
  plugin_stats = {}
355
360
  plugins = self.get_plugins()
356
361
  if plugins:
357
-
358
362
  for p in plugins:
359
363
  plugin_stats[full_name_with_qualname(p)] = {
360
364
  "enabled": p.enabled,
@@ -439,7 +443,9 @@ class PluginManager:
439
443
  )
440
444
  self._watchlist_pm.register(plugin_obj)
441
445
  else:
442
- LOG.warning(f"Plugin {plugin_obj.__class__.__name__} is disabled")
446
+ LOG.warning(
447
+ f"Plugin {plugin_obj.__class__.__name__} is disabled"
448
+ )
443
449
  elif isinstance(plugin_obj, APRSDRegexCommandPluginBase):
444
450
  if plugin_obj.enabled:
445
451
  LOG.info(
@@ -451,7 +457,9 @@ class PluginManager:
451
457
  )
452
458
  self._pluggy_pm.register(plugin_obj)
453
459
  else:
454
- LOG.warning(f"Plugin {plugin_obj.__class__.__name__} is disabled")
460
+ LOG.warning(
461
+ f"Plugin {plugin_obj.__class__.__name__} is disabled"
462
+ )
455
463
  elif isinstance(plugin_obj, APRSDPluginBase):
456
464
  if plugin_obj.enabled:
457
465
  LOG.info(
@@ -462,7 +470,9 @@ class PluginManager:
462
470
  )
463
471
  self._pluggy_pm.register(plugin_obj)
464
472
  else:
465
- LOG.warning(f"Plugin {plugin_obj.__class__.__name__} is disabled")
473
+ LOG.warning(
474
+ f"Plugin {plugin_obj.__class__.__name__} is disabled"
475
+ )
466
476
  except Exception as ex:
467
477
  LOG.error(f"Couldn't load plugin '{plugin_name}'")
468
478
  LOG.exception(ex)
@@ -473,7 +483,8 @@ class PluginManager:
473
483
  self.setup_plugins(load_help_plugin=CONF.load_help_plugin)
474
484
 
475
485
  def setup_plugins(
476
- self, load_help_plugin=True,
486
+ self,
487
+ load_help_plugin=True,
477
488
  plugin_list=[],
478
489
  ):
479
490
  """Create the plugin manager and register plugins."""
aprsd/plugins/notify.py CHANGED
@@ -4,7 +4,6 @@ from oslo_config import cfg
4
4
 
5
5
  from aprsd import packets, plugin
6
6
 
7
-
8
7
  CONF = cfg.CONF
9
8
  LOG = logging.getLogger("APRSD")
10
9
 
@@ -43,9 +42,7 @@ class NotifySeenPlugin(plugin.APRSDWatchListPluginBase):
43
42
  pkt = packets.MessagePacket(
44
43
  from_call=CONF.callsign,
45
44
  to_call=notify_callsign,
46
- message_text=(
47
- f"{fromcall} was just seen by type:'{packet_type}'"
48
- ),
45
+ message_text=(f"{fromcall} was just seen by type:'{packet_type}'"),
49
46
  allow_delay=False,
50
47
  )
51
48
  pkt.allow_delay = False
aprsd/plugins/weather.py CHANGED
@@ -2,13 +2,12 @@ import json
2
2
  import logging
3
3
  import re
4
4
 
5
- from oslo_config import cfg
6
5
  import requests
6
+ from oslo_config import cfg
7
7
 
8
8
  from aprsd import plugin, plugin_utils
9
9
  from aprsd.utils import trace
10
10
 
11
-
12
11
  CONF = cfg.CONF
13
12
  LOG = logging.getLogger("APRSD")
14
13
 
@@ -205,8 +204,9 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
205
204
 
206
205
  def help(self):
207
206
  _help = [
208
- "openweathermap: Send {} to get weather "
209
- "from your location".format(self.command_regex),
207
+ "openweathermap: Send {} to get weather " "from your location".format(
208
+ self.command_regex
209
+ ),
210
210
  "openweathermap: Send {} <callsign> to get "
211
211
  "weather from <callsign>".format(self.command_regex),
212
212
  ]
@@ -327,10 +327,12 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
327
327
 
328
328
  def help(self):
329
329
  _help = [
330
- "avwxweather: Send {} to get weather "
331
- "from your location".format(self.command_regex),
332
- "avwxweather: Send {} <callsign> to get "
333
- "weather from <callsign>".format(self.command_regex),
330
+ "avwxweather: Send {} to get weather " "from your location".format(
331
+ self.command_regex
332
+ ),
333
+ "avwxweather: Send {} <callsign> to get " "weather from <callsign>".format(
334
+ self.command_regex
335
+ ),
334
336
  ]
335
337
  return _help
336
338
 
aprsd/stats/collector.py CHANGED
@@ -3,13 +3,13 @@ from typing import Callable, Protocol, runtime_checkable
3
3
 
4
4
  from aprsd.utils import singleton
5
5
 
6
-
7
6
  LOG = logging.getLogger("APRSD")
8
7
 
9
8
 
10
9
  @runtime_checkable
11
10
  class StatsProducer(Protocol):
12
11
  """The StatsProducer protocol is used to define the interface for collecting stats."""
12
+
13
13
  def stats(self, serializable=False) -> dict:
14
14
  """provide stats in a dictionary format."""
15
15
  ...
@@ -18,6 +18,7 @@ class StatsProducer(Protocol):
18
18
  @singleton
19
19
  class Collector:
20
20
  """The Collector class is used to collect stats from multiple StatsProducer instances."""
21
+
21
22
  def __init__(self):
22
23
  self.producers: list[Callable] = []
23
24
 
@@ -26,7 +27,9 @@ class Collector:
26
27
  for name in self.producers:
27
28
  cls = name()
28
29
  try:
29
- stats[cls.__class__.__name__] = cls.stats(serializable=serializable).copy()
30
+ stats[cls.__class__.__name__] = cls.stats(
31
+ serializable=serializable
32
+ ).copy()
30
33
  except Exception as e:
31
34
  LOG.error(f"Error in producer {name} (stats): {e}")
32
35
  return stats
aprsd/threads/__init__.py CHANGED
@@ -4,8 +4,9 @@ import queue
4
4
  # aprsd.threads
5
5
  from .aprsd import APRSDThread, APRSDThreadList # noqa: F401
6
6
  from .rx import ( # noqa: F401
7
- APRSDDupeRXThread, APRSDProcessPacketThread, APRSDRXThread,
7
+ APRSDDupeRXThread,
8
+ APRSDProcessPacketThread,
9
+ APRSDRXThread,
8
10
  )
9
11
 
10
-
11
12
  packet_queue = queue.Queue(maxsize=20)
aprsd/threads/aprsd.py CHANGED
@@ -7,7 +7,6 @@ from typing import List
7
7
 
8
8
  import wrapt
9
9
 
10
-
11
10
  LOG = logging.getLogger("APRSD")
12
11
 
13
12
 
@@ -25,7 +24,7 @@ class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
25
24
  self._last_loop = datetime.datetime.now()
26
25
 
27
26
  def _should_quit(self):
28
- """ see if we have a quit message from the global queue."""
27
+ """see if we have a quit message from the global queue."""
29
28
  if self.thread_stop:
30
29
  return True
31
30
 
@@ -51,7 +50,9 @@ class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
51
50
  """Add code to subclass to do any cleanup"""
52
51
 
53
52
  def __str__(self):
54
- out = f"Thread <{self.__class__.__name__}({self.name}) Alive? {self.is_alive()}>"
53
+ out = (
54
+ f"Thread <{self.__class__.__name__}({self.name}) Alive? {self.is_alive()}>"
55
+ )
55
56
  return out
56
57
 
57
58
  def loop_age(self):
@@ -124,7 +125,7 @@ class APRSDThreadList:
124
125
  for th in self.threads_list:
125
126
  LOG.info(f"Stopping Thread {th.name}")
126
127
  if hasattr(th, "packet"):
127
- LOG.info(F"{th.name} packet {th.packet}")
128
+ LOG.info(f"{th.name} packet {th.packet}")
128
129
  th.stop()
129
130
 
130
131
  @wrapt.synchronized
@@ -133,7 +134,7 @@ class APRSDThreadList:
133
134
  for th in self.threads_list:
134
135
  LOG.info(f"Pausing Thread {th.name}")
135
136
  if hasattr(th, "packet"):
136
- LOG.info(F"{th.name} packet {th.packet}")
137
+ LOG.info(f"{th.name} packet {th.packet}")
137
138
  th.pause()
138
139
 
139
140
  @wrapt.synchronized
@@ -142,7 +143,7 @@ class APRSDThreadList:
142
143
  for th in self.threads_list:
143
144
  LOG.info(f"Resuming Thread {th.name}")
144
145
  if hasattr(th, "packet"):
145
- LOG.info(F"{th.name} packet {th.packet}")
146
+ LOG.info(f"{th.name} packet {th.packet}")
146
147
  th.unpause()
147
148
 
148
149
  @wrapt.synchronized(lock)
@@ -153,7 +154,11 @@ class APRSDThreadList:
153
154
  alive = thread.is_alive()
154
155
  age = thread.loop_age()
155
156
  key = thread.__class__.__name__
156
- info[key] = {"alive": True if alive else False, "age": age, "name": thread.name}
157
+ info[key] = {
158
+ "alive": True if alive else False,
159
+ "age": age,
160
+ "name": thread.name,
161
+ }
157
162
  return info
158
163
 
159
164
  @wrapt.synchronized(lock)