Improve adherence to python coding standards and clear up IDE static analysis warnings

This commit is contained in:
Ian Renton
2026-02-27 19:17:04 +00:00
parent 6b18ec6f88
commit 6982354364
53 changed files with 633 additions and 626 deletions

View File

@@ -15,27 +15,27 @@ class APRSIS(SpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config)
self.thread = Thread(target=self.connect)
self.thread.daemon = True
self.aprsis = None
self._thread = Thread(target=self._connect)
self._thread.daemon = True
self._aprsis = None
def start(self):
self.thread.start()
self._thread.start()
def connect(self):
self.aprsis = aprslib.IS(SERVER_OWNER_CALLSIGN)
def _connect(self):
self._aprsis = aprslib.IS(SERVER_OWNER_CALLSIGN)
self.status = "Connecting"
logging.info("APRS-IS connecting...")
self.aprsis.connect()
self.aprsis.consumer(self.handle)
self._aprsis.connect()
self._aprsis.consumer(self._handle)
logging.info("APRS-IS connected.")
def stop(self):
self.status = "Shutting down"
self.aprsis.close()
self.thread.join()
self._aprsis.close()
self._thread.join()
def handle(self, data):
def _handle(self, data):
# Split SSID in "from" call and store separately
from_parts = data["from"].split("-").upper()
dx_call = from_parts[0]
@@ -55,7 +55,7 @@ class APRSIS(SpotProvider):
pytz.UTC).timestamp()) # APRS-IS spots are live so we can assume spot time is "now"
# Add to our list
self.submit(spot)
self._submit(spot)
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)

View File

@@ -16,62 +16,62 @@ class DXCluster(SpotProvider):
"""Spot provider for a DX Cluster. Hostname, port, login_prompt, login_callsign and allow_rbn_spots are provided in config.
See config-example.yml for examples."""
CALLSIGN_PATTERN = "([a-z|0-9|/]+)"
FREQUENCY_PATTERN = "([0-9|.]+)"
LINE_PATTERN_EXCLUDE_RBN = re.compile(
"^DX de " + CALLSIGN_PATTERN + ":\\s+" + FREQUENCY_PATTERN + "\\s+" + CALLSIGN_PATTERN + "\\s+(.*)\\s+(\\d{4}Z)",
_CALLSIGN_PATTERN = "([a-z|0-9|/]+)"
_FREQUENCY_PATTERN = "([0-9|.]+)"
_LINE_PATTERN_EXCLUDE_RBN = re.compile(
"^DX de " + _CALLSIGN_PATTERN + ":\\s+" + _FREQUENCY_PATTERN + "\\s+" + _CALLSIGN_PATTERN + "\\s+(.*)\\s+(\\d{4}Z)",
re.IGNORECASE)
LINE_PATTERN_ALLOW_RBN = re.compile(
"^DX de " + CALLSIGN_PATTERN + "-?#?:\\s+" + FREQUENCY_PATTERN + "\\s+" + CALLSIGN_PATTERN + "\\s+(.*)\\s+(\\d{4}Z)",
_LINE_PATTERN_ALLOW_RBN = re.compile(
"^DX de " + _CALLSIGN_PATTERN + "-?#?:\\s+" + _FREQUENCY_PATTERN + "\\s+" + _CALLSIGN_PATTERN + "\\s+(.*)\\s+(\\d{4}Z)",
re.IGNORECASE)
def __init__(self, provider_config):
"""Constructor requires hostname and port"""
super().__init__(provider_config)
self.hostname = provider_config["host"]
self.port = provider_config["port"]
self.login_prompt = provider_config["login_prompt"] if "login_prompt" in provider_config else "login:"
self.login_callsign = provider_config[
self._hostname = provider_config["host"]
self._port = provider_config["port"]
self._login_prompt = provider_config["login_prompt"] if "login_prompt" in provider_config else "login:"
self._login_callsign = provider_config[
"login_callsign"] if "login_callsign" in provider_config else SERVER_OWNER_CALLSIGN
self.allow_rbn_spots = provider_config["allow_rbn_spots"] if "allow_rbn_spots" in provider_config else False
self.spot_line_pattern = self.LINE_PATTERN_ALLOW_RBN if self.allow_rbn_spots else self.LINE_PATTERN_EXCLUDE_RBN
self.telnet = None
self.thread = Thread(target=self.handle)
self.thread.daemon = True
self.run = True
self._allow_rbn_spots = provider_config["allow_rbn_spots"] if "allow_rbn_spots" in provider_config else False
self._spot_line_pattern = self._LINE_PATTERN_ALLOW_RBN if self._allow_rbn_spots else self._LINE_PATTERN_EXCLUDE_RBN
self._telnet = None
self._thread = Thread(target=self._handle)
self._thread.daemon = True
self._running = True
def start(self):
self.thread.start()
self._thread.start()
def stop(self):
self.run = False
self.telnet.close()
self.thread.join()
self._running = False
self._telnet.close()
self._thread.join()
def handle(self):
while self.run:
def _handle(self):
while self._running:
connected = False
while not connected and self.run:
while not connected and self._running:
try:
self.status = "Connecting"
logging.info("DX Cluster " + self.hostname + " connecting...")
self.telnet = telnetlib3.Telnet(self.hostname, self.port)
self.telnet.read_until(self.login_prompt.encode("latin-1"))
self.telnet.write((self.login_callsign + "\n").encode("latin-1"))
logging.info("DX Cluster " + self._hostname + " connecting...")
self._telnet = telnetlib3.Telnet(self._hostname, self._port)
self._telnet.read_until(self._login_prompt.encode("latin-1"))
self._telnet.write((self._login_callsign + "\n").encode("latin-1"))
connected = True
logging.info("DX Cluster " + self.hostname + " connected.")
except Exception as e:
logging.info("DX Cluster " + self._hostname + " connected.")
except Exception:
self.status = "Error"
logging.exception("Exception while connecting to DX Cluster Provider (" + self.hostname + ").")
logging.exception("Exception while connecting to DX Cluster Provider (" + self._hostname + ").")
sleep(5)
self.status = "Waiting for Data"
while connected and self.run:
while connected and self._running:
try:
# Check new telnet info against regular expression
telnet_output = self.telnet.read_until("\n".encode("latin-1"))
match = self.spot_line_pattern.match(telnet_output.decode("latin-1"))
telnet_output = self._telnet.read_until("\n".encode("latin-1"))
match = self._spot_line_pattern.match(telnet_output.decode("latin-1"))
if match:
spot_time = datetime.strptime(match.group(5), "%H%MZ")
spot_datetime = datetime.combine(datetime.today(), spot_time.time()).replace(tzinfo=pytz.UTC)
@@ -83,20 +83,20 @@ class DXCluster(SpotProvider):
time=spot_datetime.timestamp())
# Add to our list
self.submit(spot)
self._submit(spot)
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Data received from DX Cluster " + self.hostname + ".")
logging.debug("Data received from DX Cluster " + self._hostname + ".")
except Exception as e:
except Exception:
connected = False
if self.run:
if self._running:
self.status = "Error"
logging.exception("Exception in DX Cluster Provider (" + self.hostname + ")")
logging.exception("Exception in DX Cluster Provider (" + self._hostname + ")")
sleep(5)
else:
logging.info("DX Cluster " + self.hostname + " shutting down...")
logging.info("DX Cluster " + self._hostname + " shutting down...")
self.status = "Shutting down"
self.status = "Disconnected"

View File

@@ -21,7 +21,7 @@ class GMA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
for source_spot in http_response.json()["RCD"]:
@@ -77,7 +77,7 @@ class GMA(HTTPSpotProvider):
spot.sig_refs[0].sig = "MOTA"
spot.sig = "MOTA"
case _:
logging.warn("GMA spot found with ref type " + ref_info[
logging.warning("GMA spot found with ref type " + ref_info[
"reftype"] + ", developer needs to add support for this!")
spot.sig_refs[0].sig = ref_info["reftype"]
spot.sig = ref_info["reftype"]
@@ -86,6 +86,6 @@ class GMA(HTTPSpotProvider):
# that for us.
new_spots.append(spot)
except:
logging.warn("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[
logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[
"REF"] + ", ignoring this spot for now")
return new_spots

View File

@@ -24,13 +24,13 @@ class HEMA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOT_SEED_URL, self.POLL_INTERVAL_SEC)
self.spot_seed = ""
self._spot_seed = ""
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
# OK, source data is actually just the spot seed at this point. We'll then go on to fetch real data if we know
# this has changed.
spot_seed_changed = http_response.text != self.spot_seed
self.spot_seed = http_response.text
spot_seed_changed = http_response.text != self._spot_seed
self._spot_seed = http_response.text
new_spots = []
# OK, if the spot seed actually changed, now we make the real request for data.

View File

@@ -15,14 +15,15 @@ class HTTPSpotProvider(SpotProvider):
def __init__(self, provider_config, url, poll_interval):
super().__init__(provider_config)
self.url = url
self.poll_interval = poll_interval
self._url = url
self._poll_interval = poll_interval
self._thread = None
self._stop_event = Event()
def start(self):
# Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between
# subsequent polls, so start() returns immediately and the application can continue starting.
logging.info("Set up query of " + self.name + " spot API every " + str(self.poll_interval) + " seconds.")
logging.info("Set up query of " + self.name + " spot API every " + str(self._poll_interval) + " seconds.")
self._thread = Thread(target=self._run, daemon=True)
self._thread.start()
@@ -32,30 +33,30 @@ class HTTPSpotProvider(SpotProvider):
def _run(self):
while True:
self._poll()
if self._stop_event.wait(timeout=self.poll_interval):
if self._stop_event.wait(timeout=self._poll_interval):
break
def _poll(self):
try:
# Request data from API
logging.debug("Polling " + self.name + " spot API...")
http_response = requests.get(self.url, headers=HTTP_HEADERS)
http_response = requests.get(self._url, headers=HTTP_HEADERS)
# Pass off to the subclass for processing
new_spots = self.http_response_to_spots(http_response)
new_spots = self._http_response_to_spots(http_response)
# Submit the new spots for processing. There might not be any spots for the less popular programs.
if new_spots:
self.submit_batch(new_spots)
self._submit_batch(new_spots)
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " spot API.")
except Exception as e:
except Exception:
self.status = "Error"
logging.exception("Exception in HTTP JSON Spot Provider (" + self.name + ")")
self._stop_event.wait(timeout=1)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
"""Convert an HTTP response returned by the API into spot data. The whole response is provided here so the subclass
implementations can check for HTTP status codes if necessary, and handle the response as JSON, XML, text, whatever
the API actually provides."""

View File

@@ -14,7 +14,7 @@ class LLOTA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
for source_spot in http_response.json():

View File

@@ -19,7 +19,7 @@ class ParksNPeaks(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
for source_spot in http_response.json():
@@ -50,7 +50,7 @@ class ParksNPeaks(HTTPSpotProvider):
# Log a warning for the developer if PnP gives us an unknown programme we've never seen before
if spot.sig_refs[0].sig not in ["POTA", "SOTA", "WWFF", "SIOTA", "ZLOTA", "KRMNPA"]:
logging.warn("PNP spot found with sig " + spot.sig + ", developer needs to add support for this!")
logging.warning("PNP spot found with sig " + spot.sig + ", developer needs to add support for this!")
# If this is POTA, SOTA, WWFF or ZLOTA data we already have it through other means, so ignore. Otherwise,
# add to the spot list.

View File

@@ -16,7 +16,7 @@ class POTA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
for source_spot in http_response.json():

View File

@@ -16,53 +16,53 @@ class RBN(SpotProvider):
"""Spot provider for the Reverse Beacon Network. Connects to a single port, if you want both CW/RTTY (port 7000) and FT8
(port 7001) you need to instantiate two copies of this. The port is provided as an argument to the constructor."""
CALLSIGN_PATTERN = "([a-z|0-9|/]+)"
FREQUENCY_PATTERM = "([0-9|.]+)"
LINE_PATTERN = re.compile(
"^DX de " + CALLSIGN_PATTERN + "-.*:\\s+" + FREQUENCY_PATTERM + "\\s+" + CALLSIGN_PATTERN + "\\s+(.*)\\s+(\\d{4}Z)",
_CALLSIGN_PATTERN = "([a-z|0-9|/]+)"
_FREQUENCY_PATTERM = "([0-9|.]+)"
_LINE_PATTERN = re.compile(
"^DX de " + _CALLSIGN_PATTERN + "-.*:\\s+" + _FREQUENCY_PATTERM + "\\s+" + _CALLSIGN_PATTERN + "\\s+(.*)\\s+(\\d{4}Z)",
re.IGNORECASE)
def __init__(self, provider_config):
"""Constructor requires port number."""
super().__init__(provider_config)
self.port = provider_config["port"]
self.telnet = None
self.thread = Thread(target=self.handle)
self.thread.daemon = True
self.run = True
self._port = provider_config["port"]
self._telnet = None
self._thread = Thread(target=self._handle)
self._thread.daemon = True
self._running = True
def start(self):
self.thread.start()
self._thread.start()
def stop(self):
self.run = False
self.telnet.close()
self.thread.join()
self._running = False
self._telnet.close()
self._thread.join()
def handle(self):
while self.run:
def _handle(self):
while self._running:
connected = False
while not connected and self.run:
while not connected and self._running:
try:
self.status = "Connecting"
logging.info("RBN port " + str(self.port) + " connecting...")
self.telnet = telnetlib3.Telnet("telnet.reversebeacon.net", self.port)
telnet_output = self.telnet.read_until("Please enter your call: ".encode("latin-1"))
self.telnet.write((SERVER_OWNER_CALLSIGN + "\n").encode("latin-1"))
logging.info("RBN port " + str(self._port) + " connecting...")
self._telnet = telnetlib3.Telnet("telnet.reversebeacon.net", self._port)
telnet_output = self._telnet.read_until("Please enter your call: ".encode("latin-1"))
self._telnet.write((SERVER_OWNER_CALLSIGN + "\n").encode("latin-1"))
connected = True
logging.info("RBN port " + str(self.port) + " connected.")
except Exception as e:
logging.info("RBN port " + str(self._port) + " connected.")
except Exception:
self.status = "Error"
logging.exception("Exception while connecting to RBN (port " + str(self.port) + ").")
logging.exception("Exception while connecting to RBN (port " + str(self._port) + ").")
sleep(5)
self.status = "Waiting for Data"
while connected and self.run:
while connected and self._running:
try:
# Check new telnet info against regular expression
telnet_output = self.telnet.read_until("\n".encode("latin-1"))
match = self.LINE_PATTERN.match(telnet_output.decode("latin-1"))
telnet_output = self._telnet.read_until("\n".encode("latin-1"))
match = self._LINE_PATTERN.match(telnet_output.decode("latin-1"))
if match:
spot_time = datetime.strptime(match.group(5), "%H%MZ")
spot_datetime = datetime.combine(datetime.today(), spot_time.time()).replace(tzinfo=pytz.UTC)
@@ -74,20 +74,20 @@ class RBN(SpotProvider):
time=spot_datetime.timestamp())
# Add to our list
self.submit(spot)
self._submit(spot)
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Data received from RBN on port " + str(self.port) + ".")
logging.debug("Data received from RBN on port " + str(self._port) + ".")
except Exception as e:
except Exception:
connected = False
if self.run:
if self._running:
self.status = "Error"
logging.exception("Exception in RBN provider (port " + str(self.port) + ")")
logging.exception("Exception in RBN provider (port " + str(self._port) + ")")
sleep(5)
else:
logging.info("RBN provider (port " + str(self.port) + ") shutting down...")
logging.info("RBN provider (port " + str(self._port) + ") shutting down...")
self.status = "Shutting down"
self.status = "Disconnected"

View File

@@ -22,13 +22,13 @@ class SOTA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.EPOCH_URL, self.POLL_INTERVAL_SEC)
self.api_epoch = ""
self._api_epoch = ""
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
# OK, source data is actually just the epoch at this point. We'll then go on to fetch real data if we know this
# has changed.
epoch_changed = http_response.text != self.api_epoch
self.api_epoch = http_response.text
epoch_changed = http_response.text != self._api_epoch
self._api_epoch = http_response.text
new_spots = []
# OK, if the epoch actually changed, now we make the real request for data.

View File

@@ -16,21 +16,21 @@ class SpotProvider:
self.last_update_time = datetime.min.replace(tzinfo=pytz.UTC)
self.last_spot_time = datetime.min.replace(tzinfo=pytz.UTC)
self.status = "Not Started" if self.enabled else "Disabled"
self.spots = None
self.web_server = None
self._spots = None
self._web_server = None
def setup(self, spots, web_server):
"""Set up the provider, e.g. giving it the spot list to work from"""
self.spots = spots
self.web_server = web_server
self._spots = spots
self._web_server = web_server
def start(self):
"""Start the provider. This should return immediately after spawning threads to access the remote resources"""
raise NotImplementedError("Subclasses must implement this method")
def submit_batch(self, spots):
def _submit_batch(self, spots):
"""Submit a batch of spots retrieved from the provider. Only spots that are newer than the last spot retrieved
by this provider will be added to the spot list, to prevent duplications. Spots passing the check will also have
their infer_missing() method called to complete their data set. This is called by the API-querying
@@ -38,30 +38,30 @@ class SpotProvider:
# Sort the batch so that earliest ones go in first. This helps keep the ordering correct when spots are fired
# off to SSE listeners.
spots = sorted(spots, key=lambda spot: (spot.time if spot and spot.time else 0))
spots = sorted(spots, key=lambda s: (s.time if s and s.time else 0))
for spot in spots:
if datetime.fromtimestamp(spot.time, pytz.UTC) > self.last_spot_time:
# Fill in any blanks and add to the list
spot.infer_missing()
self.add_spot(spot)
self._add_spot(spot)
self.last_spot_time = datetime.fromtimestamp(max(map(lambda s: s.time, spots)), pytz.UTC)
def submit(self, spot):
def _submit(self, spot):
"""Submit a single spot retrieved from the provider. This will be added to the list regardless of its age. Spots
passing the check will also have their infer_missing() method called to complete their data set. This is called by
the data streaming subclasses, which can be relied upon not to re-provide old spots."""
# Fill in any blanks and add to the list
spot.infer_missing()
self.add_spot(spot)
self._add_spot(spot)
self.last_spot_time = datetime.fromtimestamp(spot.time, pytz.UTC)
def add_spot(self, spot):
def _add_spot(self, spot):
if not spot.expired():
self.spots.add(spot.id, spot, expire=MAX_SPOT_AGE)
self._spots.add(spot.id, spot, expire=MAX_SPOT_AGE)
# Ping the web server in case we have any SSE connections that need to see this immediately
if self.web_server:
self.web_server.notify_new_spot(spot)
if self._web_server:
self._web_server.notify_new_spot(spot)
def stop(self):
"""Stop any threads and prepare for application shutdown"""

View File

@@ -15,25 +15,25 @@ class SSESpotProvider(SpotProvider):
def __init__(self, provider_config, url):
super().__init__(provider_config)
self.url = url
self.event_source = None
self.thread = None
self.stopped = False
self.last_event_id = None
self._url = url
self._event_source = None
self._thread = None
self._stopped = False
self._last_event_id = None
def start(self):
logging.info("Set up SSE connection to " + self.name + " spot API.")
self.stopped = False
self.thread = Thread(target=self.run)
self.thread.daemon = True
self.thread.start()
self._stopped = False
self._thread = Thread(target=self._run)
self._thread.daemon = True
self._thread.start()
def stop(self):
self.stopped = True
if self.event_source:
self.event_source.close()
if self.thread:
self.thread.join()
self._stopped = True
if self._event_source:
self._event_source.close()
if self._thread:
self._thread.join()
def _on_open(self):
self.status = "Waiting for Data"
@@ -41,38 +41,38 @@ class SSESpotProvider(SpotProvider):
def _on_error(self):
self.status = "Connecting"
def run(self):
while not self.stopped:
def _run(self):
while not self._stopped:
try:
logging.debug("Connecting to " + self.name + " spot API...")
self.status = "Connecting"
with EventSource(self.url, headers=HTTP_HEADERS, latest_event_id=self.last_event_id, timeout=30,
with EventSource(self._url, headers=HTTP_HEADERS, latest_event_id=self._last_event_id, timeout=30,
on_open=self._on_open, on_error=self._on_error) as event_source:
self.event_source = event_source
for event in self.event_source:
self._event_source = event_source
for event in self._event_source:
if event.type == 'message':
try:
self.last_event_id = event.last_event_id
new_spot = self.sse_message_to_spot(event.data)
self._last_event_id = event.last_event_id
new_spot = self._sse_message_to_spot(event.data)
if new_spot:
self.submit(new_spot)
self._submit(new_spot)
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " spot API.")
except Exception as e:
except Exception:
logging.exception(
"Exception processing message from SSE Spot Provider (" + self.name + ")")
except Exception as e:
except Exception:
self.status = "Error"
logging.exception("Exception in SSE Spot Provider (" + self.name + ")")
else:
self.status = "Disconnected"
sleep(5) # Wait before trying to reconnect
def sse_message_to_spot(self, message_data):
def _sse_message_to_spot(self, message_data):
"""Convert an SSE message received from the API into a spot. The whole message data is provided here so the subclass
implementations can handle the message as JSON, XML, text, whatever the API actually provides."""

View File

@@ -16,7 +16,7 @@ class UKPacketNet(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
nodes = http_response.json()["nodes"]

View File

@@ -15,25 +15,25 @@ class WebsocketSpotProvider(SpotProvider):
def __init__(self, provider_config, url):
super().__init__(provider_config)
self.url = url
self.ws = None
self.thread = None
self.stopped = False
self.last_event_id = None
self._url = url
self._ws = None
self._thread = None
self._stopped = False
self._last_event_id = None
def start(self):
logging.info("Set up websocket connection to " + self.name + " spot API.")
self.stopped = False
self.thread = Thread(target=self.run)
self.thread.daemon = True
self.thread.start()
self._stopped = False
self._thread = Thread(target=self._run)
self._thread.daemon = True
self._thread.start()
def stop(self):
self.stopped = True
if self.ws:
self.ws.close()
if self.thread:
self.thread.join()
self._stopped = True
if self._ws:
self._ws.close()
if self._thread:
self._thread.join()
def _on_open(self):
self.status = "Waiting for Data"
@@ -41,25 +41,25 @@ class WebsocketSpotProvider(SpotProvider):
def _on_error(self):
self.status = "Connecting"
def run(self):
while not self.stopped:
def _run(self):
while not self._stopped:
try:
logging.debug("Connecting to " + self.name + " spot API...")
self.status = "Connecting"
self.ws = create_connection(self.url, header=HTTP_HEADERS)
self._ws = create_connection(self._url, header=HTTP_HEADERS)
self.status = "Connected"
data = self.ws.recv()
data = self._ws.recv()
if data:
try:
new_spot = self.ws_message_to_spot(data)
new_spot = self._ws_message_to_spot(data)
if new_spot:
self.submit(new_spot)
self._submit(new_spot)
self.status = "OK"
self.last_update_time = datetime.now(pytz.UTC)
logging.debug("Received data from " + self.name + " spot API.")
except Exception as e:
except Exception:
logging.exception(
"Exception processing message from Websocket Spot Provider (" + self.name + ")")
@@ -70,7 +70,7 @@ class WebsocketSpotProvider(SpotProvider):
self.status = "Disconnected"
sleep(5) # Wait before trying to reconnect
def ws_message_to_spot(self, bytes):
def _ws_message_to_spot(self, b):
"""Convert a WS message received from the API into a spot. The exact message data (in bytes) is provided here so the
subclass implementations can handle the message as string, JSON, XML, whatever the API actually provides."""

View File

@@ -21,7 +21,7 @@ class WOTA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
rss = RSSParser.parse(http_response.content.decode())
# Iterate through source data
@@ -48,6 +48,7 @@ class WOTA(HTTPSpotProvider):
freq_mode = desc_split[0].replace("Frequencies/modes:", "").strip()
freq_mode_split = re.split(r'[\-\s]+', freq_mode)
freq_hz = float(freq_mode_split[0]) * 1000000
mode = None
if len(freq_mode_split) > 1:
mode = freq_mode_split[1].upper()

View File

@@ -14,7 +14,7 @@ class WWBOTA(SSESpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL)
def sse_message_to_spot(self, message):
def _sse_message_to_spot(self, message):
source_spot = json.loads(message)
# Convert to our spot format. First we unpack references, because WWBOTA spots can have more than one for
# n-fer activations.

View File

@@ -16,7 +16,7 @@ class WWFF(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
for source_spot in http_response.json():

View File

@@ -16,7 +16,7 @@ class WWTOTA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
response_fixed = http_response.text.replace("\\/", "/")
response_json = json.loads(response_fixed)

View File

@@ -36,8 +36,8 @@ class XOTA(WebsocketSpotProvider):
except:
logging.exception("Could not look up location data for XOTA source.")
def ws_message_to_spot(self, bytes):
string = bytes.decode("utf-8")
def _ws_message_to_spot(self, b):
string = b.decode("utf-8")
source_spot = json.loads(string)
ref_id = source_spot["reference"]["title"]
lat = float(self.LOCATION_DATA[ref_id]["lat"]) if ref_id in self.LOCATION_DATA else None

View File

@@ -17,7 +17,7 @@ class ZLOTA(HTTPSpotProvider):
def __init__(self, provider_config):
super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_spots(self, http_response):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
for source_spot in http_response.json():