aprsd 4.1.0__py3-none-any.whl → 4.1.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.
aprsd/plugins/weather.py CHANGED
@@ -9,7 +9,7 @@ from aprsd import plugin, plugin_utils
9
9
  from aprsd.utils import trace
10
10
 
11
11
  CONF = cfg.CONF
12
- LOG = logging.getLogger("APRSD")
12
+ LOG = logging.getLogger('APRSD')
13
13
 
14
14
 
15
15
  class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
@@ -26,22 +26,22 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin)
26
26
  """
27
27
 
28
28
  # command_regex = r"^([w][x]|[w][x]\s|weather)"
29
- command_regex = r"^[wW]"
29
+ command_regex = r'^[wW]'
30
30
 
31
- command_name = "USWeather"
32
- short_description = "Provide USA only weather of GPS Beacon location"
31
+ command_name = 'USWeather'
32
+ short_description = 'Provide USA only weather of GPS Beacon location'
33
33
 
34
34
  def setup(self):
35
35
  self.ensure_aprs_fi_key()
36
36
 
37
37
  @trace.trace
38
38
  def process(self, packet):
39
- LOG.info("Weather Plugin")
39
+ LOG.info('Weather Plugin')
40
40
  fromcall = packet.from_call
41
- message = packet.get("message_text", None)
41
+ message = packet.get('message_text', None)
42
42
  # message = packet.get("message_text", None)
43
43
  # ack = packet.get("msgNo", "0")
44
- a = re.search(r"^.*\s+(.*)", message)
44
+ a = re.search(r'^.*\s+(.*)', message)
45
45
  if a is not None:
46
46
  searchcall = a.group(1)
47
47
  searchcall = searchcall.upper()
@@ -51,34 +51,34 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin)
51
51
  try:
52
52
  aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
53
53
  except Exception as ex:
54
- LOG.error(f"Failed to fetch aprs.fi data {ex}")
55
- return "Failed to fetch aprs.fi location"
54
+ LOG.error(f'Failed to fetch aprs.fi data {ex}')
55
+ return 'Failed to fetch aprs.fi location'
56
56
 
57
- LOG.debug(f"LocationPlugin: aprs_data = {aprs_data}")
58
- if not len(aprs_data["entries"]):
57
+ LOG.debug(f'LocationPlugin: aprs_data = {aprs_data}')
58
+ if not len(aprs_data['entries']):
59
59
  LOG.error("Didn't get any entries from aprs.fi")
60
- return "Failed to fetch aprs.fi location"
60
+ return 'Failed to fetch aprs.fi location'
61
61
 
62
- lat = aprs_data["entries"][0]["lat"]
63
- lon = aprs_data["entries"][0]["lng"]
62
+ lat = aprs_data['entries'][0]['lat']
63
+ lon = aprs_data['entries'][0]['lng']
64
64
 
65
65
  try:
66
66
  wx_data = plugin_utils.get_weather_gov_for_gps(lat, lon)
67
67
  except Exception as ex:
68
68
  LOG.error(f"Couldn't fetch forecast.weather.gov '{ex}'")
69
- return "Unable to get weather"
69
+ return 'Unable to get weather'
70
70
 
71
- LOG.info(f"WX data {wx_data}")
71
+ LOG.info(f'WX data {wx_data}')
72
72
 
73
73
  reply = (
74
- "%sF(%sF/%sF) %s. %s, %s."
74
+ '%sF(%sF/%sF) %s. %s, %s.'
75
75
  % (
76
- wx_data["currentobservation"]["Temp"],
77
- wx_data["data"]["temperature"][0],
78
- wx_data["data"]["temperature"][1],
79
- wx_data["data"]["weather"][0],
80
- wx_data["time"]["startPeriodName"][1],
81
- wx_data["data"]["weather"][1],
76
+ wx_data['currentobservation']['Temp'],
77
+ wx_data['data']['temperature'][0],
78
+ wx_data['data']['temperature'][1],
79
+ wx_data['data']['weather'][0],
80
+ wx_data['time']['startPeriodName'][1],
81
+ wx_data['data']['weather'][1],
82
82
  )
83
83
  ).rstrip()
84
84
  LOG.debug(f"reply: '{reply}' ")
@@ -100,31 +100,31 @@ class USMetarPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
100
100
 
101
101
  """
102
102
 
103
- command_regex = r"^([m]|[M]|[m]\s|metar)"
104
- command_name = "USMetar"
105
- short_description = "USA only METAR of GPS Beacon location"
103
+ command_regex = r'^([m]|[M]|[m]\s|metar)'
104
+ command_name = 'USMetar'
105
+ short_description = 'USA only METAR of GPS Beacon location'
106
106
 
107
107
  def setup(self):
108
108
  self.ensure_aprs_fi_key()
109
109
 
110
110
  @trace.trace
111
111
  def process(self, packet):
112
- fromcall = packet.get("from")
113
- message = packet.get("message_text", None)
112
+ fromcall = packet.get('from')
113
+ message = packet.get('message_text', None)
114
114
  # ack = packet.get("msgNo", "0")
115
115
  LOG.info(f"WX Plugin '{message}'")
116
- a = re.search(r"^.*\s+(.*)", message)
116
+ a = re.search(r'^.*\s+(.*)', message)
117
117
  if a is not None:
118
118
  searchcall = a.group(1)
119
119
  station = searchcall.upper()
120
120
  try:
121
121
  resp = plugin_utils.get_weather_gov_metar(station)
122
122
  except Exception as e:
123
- LOG.debug(f"Weather failed with: {str(e)}")
124
- reply = "Unable to find station METAR"
123
+ LOG.debug(f'Weather failed with: {str(e)}')
124
+ reply = 'Unable to find station METAR'
125
125
  else:
126
126
  station_data = json.loads(resp.text)
127
- reply = station_data["properties"]["rawMessage"]
127
+ reply = station_data['properties']['rawMessage']
128
128
 
129
129
  return reply
130
130
  else:
@@ -136,36 +136,36 @@ class USMetarPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
136
136
  try:
137
137
  aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
138
138
  except Exception as ex:
139
- LOG.error(f"Failed to fetch aprs.fi data {ex}")
140
- return "Failed to fetch aprs.fi location"
139
+ LOG.error(f'Failed to fetch aprs.fi data {ex}')
140
+ return 'Failed to fetch aprs.fi location'
141
141
 
142
142
  # LOG.debug("LocationPlugin: aprs_data = {}".format(aprs_data))
143
- if not len(aprs_data["entries"]):
144
- LOG.error("Found no entries from aprs.fi!")
145
- return "Failed to fetch aprs.fi location"
143
+ if not len(aprs_data['entries']):
144
+ LOG.error('Found no entries from aprs.fi!')
145
+ return 'Failed to fetch aprs.fi location'
146
146
 
147
- lat = aprs_data["entries"][0]["lat"]
148
- lon = aprs_data["entries"][0]["lng"]
147
+ lat = aprs_data['entries'][0]['lat']
148
+ lon = aprs_data['entries'][0]['lng']
149
149
 
150
150
  try:
151
151
  wx_data = plugin_utils.get_weather_gov_for_gps(lat, lon)
152
152
  except Exception as ex:
153
153
  LOG.error(f"Couldn't fetch forecast.weather.gov '{ex}'")
154
- return "Unable to metar find station."
154
+ return 'Unable to metar find station.'
155
155
 
156
- if wx_data["location"]["metar"]:
157
- station = wx_data["location"]["metar"]
156
+ if wx_data['location']['metar']:
157
+ station = wx_data['location']['metar']
158
158
  try:
159
159
  resp = plugin_utils.get_weather_gov_metar(station)
160
160
  except Exception as e:
161
- LOG.debug(f"Weather failed with: {str(e)}")
162
- reply = "Failed to get Metar"
161
+ LOG.debug(f'Weather failed with: {str(e)}')
162
+ reply = 'Failed to get Metar'
163
163
  else:
164
164
  station_data = json.loads(resp.text)
165
- reply = station_data["properties"]["rawMessage"]
165
+ reply = station_data['properties']['rawMessage']
166
166
  else:
167
167
  # Couldn't find a station
168
- reply = "No Metar station found"
168
+ reply = 'No Metar station found'
169
169
 
170
170
  return reply
171
171
 
@@ -190,35 +190,36 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
190
190
  """
191
191
 
192
192
  # command_regex = r"^([w][x]|[w][x]\s|weather)"
193
- command_regex = r"^[wW]"
193
+ command_regex = r'^[wW]'
194
194
 
195
- command_name = "OpenWeatherMap"
196
- short_description = "OpenWeatherMap weather of GPS Beacon location"
195
+ command_name = 'OpenWeatherMap'
196
+ short_description = 'OpenWeatherMap weather of GPS Beacon location'
197
197
 
198
198
  def setup(self):
199
199
  if not CONF.owm_weather_plugin.apiKey:
200
- LOG.error("Config.owm_weather_plugin.apiKey is not set. Disabling")
200
+ LOG.error('Config.owm_weather_plugin.apiKey is not set. Disabling')
201
201
  self.enabled = False
202
202
  else:
203
203
  self.enabled = True
204
204
 
205
205
  def help(self):
206
206
  _help = [
207
- "openweathermap: Send {} to get weather " "from your location".format(
207
+ 'openweathermap: Send {} to get weather from your location'.format(
208
+ self.command_regex
209
+ ),
210
+ 'openweathermap: Send {} <callsign> to get weather from <callsign>'.format(
208
211
  self.command_regex
209
212
  ),
210
- "openweathermap: Send {} <callsign> to get "
211
- "weather from <callsign>".format(self.command_regex),
212
213
  ]
213
214
  return _help
214
215
 
215
216
  @trace.trace
216
217
  def process(self, packet):
217
- fromcall = packet.get("from_call")
218
- message = packet.get("message_text", None)
218
+ fromcall = packet.get('from_call')
219
+ message = packet.get('message_text', None)
219
220
  # ack = packet.get("msgNo", "0")
220
221
  LOG.info(f"OWMWeather Plugin '{message}'")
221
- a = re.search(r"^.*\s+(.*)", message)
222
+ a = re.search(r'^.*\s+(.*)', message)
222
223
  if a is not None:
223
224
  searchcall = a.group(1)
224
225
  searchcall = searchcall.upper()
@@ -230,16 +231,16 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
230
231
  try:
231
232
  aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
232
233
  except Exception as ex:
233
- LOG.error(f"Failed to fetch aprs.fi data {ex}")
234
- return "Failed to fetch location"
234
+ LOG.error(f'Failed to fetch aprs.fi data {ex}')
235
+ return 'Failed to fetch location'
235
236
 
236
237
  # LOG.debug("LocationPlugin: aprs_data = {}".format(aprs_data))
237
- if not len(aprs_data["entries"]):
238
- LOG.error("Found no entries from aprs.fi!")
239
- return "Failed to fetch location"
238
+ if not len(aprs_data['entries']):
239
+ LOG.error('Found no entries from aprs.fi!')
240
+ return 'Failed to fetch location'
240
241
 
241
- lat = aprs_data["entries"][0]["lat"]
242
- lon = aprs_data["entries"][0]["lng"]
242
+ lat = aprs_data['entries'][0]['lat']
243
+ lon = aprs_data['entries'][0]['lng']
243
244
 
244
245
  units = CONF.units
245
246
  api_key = CONF.owm_weather_plugin.apiKey
@@ -249,40 +250,40 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
249
250
  lat,
250
251
  lon,
251
252
  units=units,
252
- exclude="minutely,hourly",
253
+ exclude='minutely,hourly',
253
254
  )
254
255
  except Exception as ex:
255
256
  LOG.error(f"Couldn't fetch openweathermap api '{ex}'")
256
257
  # default to UTC
257
- return "Unable to get weather"
258
+ return 'Unable to get weather'
258
259
 
259
- if units == "metric":
260
- degree = "C"
260
+ if units == 'metric':
261
+ degree = 'C'
261
262
  else:
262
- degree = "F"
263
+ degree = 'F'
263
264
 
264
- if "wind_gust" in wx_data["current"]:
265
- wind = "{:.0f}@{}G{:.0f}".format(
266
- wx_data["current"]["wind_speed"],
267
- wx_data["current"]["wind_deg"],
268
- wx_data["current"]["wind_gust"],
265
+ if 'wind_gust' in wx_data['current']:
266
+ wind = '{:.0f}@{}G{:.0f}'.format(
267
+ wx_data['current']['wind_speed'],
268
+ wx_data['current']['wind_deg'],
269
+ wx_data['current']['wind_gust'],
269
270
  )
270
271
  else:
271
- wind = "{:.0f}@{}".format(
272
- wx_data["current"]["wind_speed"],
273
- wx_data["current"]["wind_deg"],
272
+ wind = '{:.0f}@{}'.format(
273
+ wx_data['current']['wind_speed'],
274
+ wx_data['current']['wind_deg'],
274
275
  )
275
276
 
276
277
  # LOG.debug(wx_data["current"])
277
278
  # LOG.debug(wx_data["daily"])
278
- reply = "{} {:.1f}{}/{:.1f}{} Wind {} {}%".format(
279
- wx_data["current"]["weather"][0]["description"],
280
- wx_data["current"]["temp"],
279
+ reply = '{} {:.1f}{}/{:.1f}{} Wind {} {}%'.format(
280
+ wx_data['current']['weather'][0]['description'],
281
+ wx_data['current']['temp'],
281
282
  degree,
282
- wx_data["current"]["dew_point"],
283
+ wx_data['current']['dew_point'],
283
284
  degree,
284
285
  wind,
285
- wx_data["current"]["humidity"],
286
+ wx_data['current']['humidity'],
286
287
  )
287
288
 
288
289
  return reply
@@ -311,26 +312,26 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
311
312
  docker build -f Dockerfile -t avwx-api:master .
312
313
  """
313
314
 
314
- command_regex = r"^([m]|[m]|[m]\s|metar)"
315
- command_name = "AVWXWeather"
316
- short_description = "AVWX weather of GPS Beacon location"
315
+ command_regex = r'^([m]|[m]|[m]\s|metar)'
316
+ command_name = 'AVWXWeather'
317
+ short_description = 'AVWX weather of GPS Beacon location'
317
318
 
318
319
  def setup(self):
319
320
  if not CONF.avwx_plugin.base_url:
320
- LOG.error("Config avwx_plugin.base_url not specified. Disabling")
321
+ LOG.error('Config avwx_plugin.base_url not specified. Disabling')
321
322
  return False
322
323
  elif not CONF.avwx_plugin.apiKey:
323
- LOG.error("Config avwx_plugin.apiKey not specified. Disabling")
324
+ LOG.error('Config avwx_plugin.apiKey not specified. Disabling')
324
325
  return False
325
326
  else:
326
327
  return True
327
328
 
328
329
  def help(self):
329
330
  _help = [
330
- "avwxweather: Send {} to get weather " "from your location".format(
331
+ 'avwxweather: Send {} to get weather from your location'.format(
331
332
  self.command_regex
332
333
  ),
333
- "avwxweather: Send {} <callsign> to get " "weather from <callsign>".format(
334
+ 'avwxweather: Send {} <callsign> to get weather from <callsign>'.format(
334
335
  self.command_regex
335
336
  ),
336
337
  ]
@@ -338,11 +339,11 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
338
339
 
339
340
  @trace.trace
340
341
  def process(self, packet):
341
- fromcall = packet.get("from")
342
- message = packet.get("message_text", None)
342
+ fromcall = packet.get('from')
343
+ message = packet.get('message_text', None)
343
344
  # ack = packet.get("msgNo", "0")
344
345
  LOG.info(f"AVWXWeather Plugin '{message}'")
345
- a = re.search(r"^.*\s+(.*)", message)
346
+ a = re.search(r'^.*\s+(.*)', message)
346
347
  if a is not None:
347
348
  searchcall = a.group(1)
348
349
  searchcall = searchcall.upper()
@@ -353,43 +354,43 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
353
354
  try:
354
355
  aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
355
356
  except Exception as ex:
356
- LOG.error(f"Failed to fetch aprs.fi data {ex}")
357
- return "Failed to fetch location"
357
+ LOG.error(f'Failed to fetch aprs.fi data {ex}')
358
+ return 'Failed to fetch location'
358
359
 
359
360
  # LOG.debug("LocationPlugin: aprs_data = {}".format(aprs_data))
360
- if not len(aprs_data["entries"]):
361
- LOG.error("Found no entries from aprs.fi!")
362
- return "Failed to fetch location"
361
+ if not len(aprs_data['entries']):
362
+ LOG.error('Found no entries from aprs.fi!')
363
+ return 'Failed to fetch location'
363
364
 
364
- lat = aprs_data["entries"][0]["lat"]
365
- lon = aprs_data["entries"][0]["lng"]
365
+ lat = aprs_data['entries'][0]['lat']
366
+ lon = aprs_data['entries'][0]['lng']
366
367
 
367
368
  api_key = CONF.avwx_plugin.apiKey
368
369
  base_url = CONF.avwx_plugin.base_url
369
- token = f"TOKEN {api_key}"
370
- headers = {"Authorization": token}
370
+ token = f'TOKEN {api_key}'
371
+ headers = {'Authorization': token}
371
372
  try:
372
- coord = f"{lat},{lon}"
373
+ coord = f'{lat},{lon}'
373
374
  url = (
374
- "{}/api/station/near/{}?"
375
- "n=1&airport=false&reporting=true&format=json".format(base_url, coord)
375
+ '{}/api/station/near/{}?'
376
+ 'n=1&airport=false&reporting=true&format=json'.format(base_url, coord)
376
377
  )
377
378
 
378
379
  LOG.debug(f"Get stations near me '{url}'")
379
380
  response = requests.get(url, headers=headers)
380
381
  except Exception as ex:
381
382
  LOG.error(ex)
382
- raise Exception(f"Failed to get the weather '{ex}'")
383
+ raise Exception(f"Failed to get the weather '{ex}'") from ex
383
384
  else:
384
385
  wx_data = json.loads(response.text)
385
386
 
386
387
  # LOG.debug(wx_data)
387
- station = wx_data[0]["station"]["icao"]
388
+ station = wx_data[0]['station']['icao']
388
389
 
389
390
  try:
390
391
  url = (
391
- "{}/api/metar/{}?options=info,translate,summary"
392
- "&airport=true&reporting=true&format=json&onfail=cache".format(
392
+ '{}/api/metar/{}?options=info,translate,summary'
393
+ '&airport=true&reporting=true&format=json&onfail=cache'.format(
393
394
  base_url,
394
395
  station,
395
396
  )
@@ -399,9 +400,9 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
399
400
  response = requests.get(url, headers=headers)
400
401
  except Exception as ex:
401
402
  LOG.error(ex)
402
- raise Exception(f"Failed to get metar {ex}")
403
+ raise Exception(f'Failed to get metar {ex}') from ex
403
404
  else:
404
405
  metar_data = json.loads(response.text)
405
406
 
406
407
  # LOG.debug(metar_data)
407
- return metar_data["raw"]
408
+ return metar_data['raw']
@@ -0,0 +1,42 @@
1
+ # aprsd/aprsd/threads/service.py
2
+ #
3
+ # This module is used to register threads that the service command runs.
4
+ #
5
+ # The service command is used to start and stop the APRS service.
6
+ # This is a mechanism to register threads that the service or command
7
+ # needs to run, and then start stop them as needed.
8
+
9
+ from aprsd.threads import aprsd as aprsd_threads
10
+ from aprsd.utils import singleton
11
+
12
+
13
+ @singleton
14
+ class ServiceThreads:
15
+ """Registry for threads that the service command runs.
16
+
17
+ This enables extensions to register a thread to run during
18
+ the service command.
19
+ """
20
+
21
+ def __init__(self):
22
+ self.threads: list[aprsd_threads.APRSDThread] = []
23
+
24
+ def register(self, thread: aprsd_threads.APRSDThread):
25
+ if not isinstance(thread, aprsd_threads.APRSDThread):
26
+ raise TypeError(f'Thread {thread} is not an APRSDThread')
27
+ self.threads.append(thread)
28
+
29
+ def unregister(self, thread: aprsd_threads.APRSDThread):
30
+ if not isinstance(thread, aprsd_threads.APRSDThread):
31
+ raise TypeError(f'Thread {thread} is not an APRSDThread')
32
+ self.threads.remove(thread)
33
+
34
+ def start(self):
35
+ """Start all threads in the list."""
36
+ for thread in self.threads:
37
+ thread.start()
38
+
39
+ def join(self):
40
+ """Join all the threads in the list"""
41
+ for thread in self.threads:
42
+ thread.join()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: aprsd
3
- Version: 4.1.0
3
+ Version: 4.1.1
4
4
  Summary: APRSd is a APRS-IS server that can be used to connect to APRS-IS and send and receive APRS packets.
5
5
  Author-email: Craig Lamparter <craig@craiger.org>, "Walter A. Boring IV" <waboring@hemna.com>, Emre Saglam <emresaglam@gmail.com>, Jason Martin <jhmartin@toger.us>, John <johng42@users.noreply.github.com>, Martiros Shakhzadyan <vrzh@vrzh.net>, Zoe Moore <zoenb@mailbox.org>, ranguli <hello@joshmurphy.ca>
6
6
  Maintainer-email: Craig Lamparter <craig@craiger.org>, "Walter A. Boring IV" <waboring@hemna.com>
@@ -202,13 +202,12 @@ Description-Content-Type: text/markdown
202
202
  License-File: LICENSE
203
203
  License-File: AUTHORS
204
204
  Requires-Dist: aprslib==0.7.2
205
- Requires-Dist: attrs==24.3.0
205
+ Requires-Dist: attrs==25.1.0
206
206
  Requires-Dist: ax253==0.1.5.post1
207
- Requires-Dist: bitarray==3.0.0
208
- Requires-Dist: certifi==2024.12.14
207
+ Requires-Dist: bitarray==3.1.0
208
+ Requires-Dist: certifi==2025.1.31
209
209
  Requires-Dist: charset-normalizer==3.4.1
210
210
  Requires-Dist: click==8.1.8
211
- Requires-Dist: commonmark==0.9.1
212
211
  Requires-Dist: dataclasses-json==0.6.7
213
212
  Requires-Dist: debtcollector==3.0.0
214
213
  Requires-Dist: haversine==2.9.0
@@ -216,39 +215,42 @@ Requires-Dist: idna==3.10
216
215
  Requires-Dist: importlib-metadata==8.6.1
217
216
  Requires-Dist: kiss3==8.0.0
218
217
  Requires-Dist: loguru==0.7.3
219
- Requires-Dist: marshmallow==3.26.0
218
+ Requires-Dist: markdown-it-py==3.0.0
219
+ Requires-Dist: marshmallow==3.26.1
220
+ Requires-Dist: mdurl==0.1.2
220
221
  Requires-Dist: mypy-extensions==1.0.0
221
222
  Requires-Dist: netaddr==1.3.0
222
- Requires-Dist: oslo-config==9.7.0
223
- Requires-Dist: oslo-i18n==6.5.0
223
+ Requires-Dist: oslo-config==9.7.1
224
+ Requires-Dist: oslo-i18n==6.5.1
224
225
  Requires-Dist: packaging==24.2
225
- Requires-Dist: pbr==6.1.0
226
+ Requires-Dist: pbr==6.1.1
226
227
  Requires-Dist: pluggy==1.5.0
227
228
  Requires-Dist: pygments==2.19.1
228
229
  Requires-Dist: pyserial==3.5
229
230
  Requires-Dist: pyserial-asyncio==0.6
230
- Requires-Dist: pytz==2024.2
231
+ Requires-Dist: pytz==2025.1
231
232
  Requires-Dist: pyyaml==6.0.2
232
233
  Requires-Dist: requests==2.32.3
233
234
  Requires-Dist: rfc3986==2.0.0
234
- Requires-Dist: rich==12.6.0
235
+ Requires-Dist: rich==13.9.4
235
236
  Requires-Dist: rush==2021.4.0
236
- Requires-Dist: stevedore==5.4.0
237
+ Requires-Dist: setuptools==75.8.2
238
+ Requires-Dist: stevedore==5.4.1
237
239
  Requires-Dist: thesmuggler==1.0.1
238
240
  Requires-Dist: timeago==1.0.16
239
241
  Requires-Dist: typing-extensions==4.12.2
240
242
  Requires-Dist: typing-inspect==0.9.0
241
- Requires-Dist: tzlocal==5.2
243
+ Requires-Dist: tzlocal==5.3
242
244
  Requires-Dist: update-checker==0.18.0
243
245
  Requires-Dist: urllib3==2.3.0
244
246
  Requires-Dist: wrapt==1.17.2
245
247
  Requires-Dist: zipp==3.21.0
246
248
  Provides-Extra: dev
247
249
  Requires-Dist: alabaster==1.0.0; extra == "dev"
248
- Requires-Dist: babel==2.16.0; extra == "dev"
250
+ Requires-Dist: babel==2.17.0; extra == "dev"
249
251
  Requires-Dist: build==1.2.2.post1; extra == "dev"
250
- Requires-Dist: cachetools==5.5.1; extra == "dev"
251
- Requires-Dist: certifi==2024.12.14; extra == "dev"
252
+ Requires-Dist: cachetools==5.5.2; extra == "dev"
253
+ Requires-Dist: certifi==2025.1.31; extra == "dev"
252
254
  Requires-Dist: cfgv==3.4.0; extra == "dev"
253
255
  Requires-Dist: chardet==5.2.0; extra == "dev"
254
256
  Requires-Dist: charset-normalizer==3.4.1; extra == "dev"
@@ -257,7 +259,7 @@ Requires-Dist: colorama==0.4.6; extra == "dev"
257
259
  Requires-Dist: distlib==0.3.9; extra == "dev"
258
260
  Requires-Dist: docutils==0.21.2; extra == "dev"
259
261
  Requires-Dist: filelock==3.17.0; extra == "dev"
260
- Requires-Dist: identify==2.6.6; extra == "dev"
262
+ Requires-Dist: identify==2.6.8; extra == "dev"
261
263
  Requires-Dist: idna==3.10; extra == "dev"
262
264
  Requires-Dist: imagesize==1.4.1; extra == "dev"
263
265
  Requires-Dist: jinja2==3.1.5; extra == "dev"
@@ -266,7 +268,7 @@ Requires-Dist: markupsafe==3.0.2; extra == "dev"
266
268
  Requires-Dist: mistune==0.8.4; extra == "dev"
267
269
  Requires-Dist: nodeenv==1.9.1; extra == "dev"
268
270
  Requires-Dist: packaging==24.2; extra == "dev"
269
- Requires-Dist: pip==24.3.1; extra == "dev"
271
+ Requires-Dist: pip==25.0.1; extra == "dev"
270
272
  Requires-Dist: pip-tools==7.4.1; extra == "dev"
271
273
  Requires-Dist: platformdirs==4.3.6; extra == "dev"
272
274
  Requires-Dist: pluggy==1.5.0; extra == "dev"
@@ -276,7 +278,7 @@ Requires-Dist: pyproject-api==1.9.0; extra == "dev"
276
278
  Requires-Dist: pyproject-hooks==1.2.0; extra == "dev"
277
279
  Requires-Dist: pyyaml==6.0.2; extra == "dev"
278
280
  Requires-Dist: requests==2.32.3; extra == "dev"
279
- Requires-Dist: setuptools==75.8.0; extra == "dev"
281
+ Requires-Dist: setuptools==75.8.2; extra == "dev"
280
282
  Requires-Dist: snowballstemmer==2.2.0; extra == "dev"
281
283
  Requires-Dist: sphinx==8.1.3; extra == "dev"
282
284
  Requires-Dist: sphinxcontrib-applehelp==2.0.0; extra == "dev"
@@ -289,7 +291,7 @@ Requires-Dist: tomli==2.2.1; extra == "dev"
289
291
  Requires-Dist: tox==4.24.1; extra == "dev"
290
292
  Requires-Dist: typing-extensions==4.12.2; extra == "dev"
291
293
  Requires-Dist: urllib3==2.3.0; extra == "dev"
292
- Requires-Dist: virtualenv==20.29.1; extra == "dev"
294
+ Requires-Dist: virtualenv==20.29.2; extra == "dev"
293
295
  Requires-Dist: wheel==0.45.1; extra == "dev"
294
296
 
295
297
  # APRSD - Ham radio APRS-IS Message platform software