diff --git a/server/handlers/api/addspot.py b/server/handlers/api/addspot.py index 95509cd..f4e03ba 100644 --- a/server/handlers/api/addspot.py +++ b/server/handlers/api/addspot.py @@ -1,6 +1,7 @@ import json import logging import re +import threading from datetime import datetime import pytz @@ -175,8 +176,8 @@ class APISpotHandler(tornado.web.RequestHandler): try: # Submit spot to the upstream provider provider.submit_spot(spot, upstream_credentials) - # Trigger an immediate re-poll so the spot appears quickly - provider.force_poll() + # Trigger a re-poll after 1 second so the spot appears quickly + threading.Timer(1.0, lambda: provider.force_poll()).start() except NotImplementedError as e: upstream_warning = str(e) except Exception as e: diff --git a/spotproviders/sota.py b/spotproviders/sota.py index f34d69d..15f8345 100644 --- a/spotproviders/sota.py +++ b/spotproviders/sota.py @@ -21,7 +21,6 @@ class SOTA(HTTPSpotProvider): SUMMIT_URL_ROOT = "https://api-db2.sota.org.uk/api/summits/" SUBMIT_URL = "https://api-db2.sota.org.uk/api/spots" - VALID_MODES = ["AM", "CW", "Data", "DV", "FM", "SSB"] def __init__(self, provider_config): @@ -77,23 +76,24 @@ class SOTA(HTTPSpotProvider): # Figure out a valid mode. Borrowed this from PoLo :) # https://github.com/ham2k/app-polo/blob/main/src/extensions/activities/sota/SOTAPostSelfSpot.js - if spot.mode and spot.mode not in self.VALID_MODES: - if spot.mode in SSB_SUB_MODES: - spot.mode = "SSB" - elif spot.mode in DV_SUB_MODES: - spot.mode = "DV" + mode = spot.mode + if mode and mode not in self.VALID_MODES: + if mode in SSB_SUB_MODES: + mode = "SSB" + elif mode in DV_SUB_MODES: + mode = "DV" else: - spot.mode = "Data" + mode = "Data" body = { "activatorCallsign": spot.dx_call, "associationCode": ref_split[0], "summitCode": ref_split[1], - "frequency": str(spot.freq / 1000000.0), - "mode": spot.mode or "", - "posterCallsign": spot.de_call, + "frequency": spot.freq / 1000000.0, + "mode": mode or "", + "callsign": spot.de_call, "comments": spot.comment or "", - "type": "TEST" # todo remove once testing complete + "type": "TEST" # todo replatce with NORMAL/QRT once testing complete } headers = {**HTTP_HEADERS, "Authorization": "bearer " + access_token, "id_token": id_token, "Content-Type": "application/json"} response = requests.post(self.SUBMIT_URL, json=body, headers=headers, timeout=(5, 30)) diff --git a/spotproviders/tiles.py b/spotproviders/tiles.py index 406af8c..2779593 100644 --- a/spotproviders/tiles.py +++ b/spotproviders/tiles.py @@ -1,5 +1,8 @@ from datetime import datetime +import requests + +from core.constants import HTTP_HEADERS, SSB_SUB_MODES from data.sig_ref import SIGRef from data.spot import Spot from spotproviders.http_spot_provider import HTTPSpotProvider @@ -10,6 +13,8 @@ class Tiles(HTTPSpotProvider): POLL_INTERVAL_SEC = 120 SPOTS_URL = "https://icneuzxitdqtofutxbla.supabase.co/functions/v1/spots?active_hours=24" + SUBMIT_URL = "https://icneuzxitdqtofutxbla.supabase.co/functions/v1/self-spot" + VALID_MODES = ["SSB", "CW", "FT8", "FT4", "FM", "DMR", "D-STAR", "M17", "AX.25", "JS8Call", "PSK31", "Olivia", "VarAC", "Other"] def __init__(self, provider_config): super().__init__(provider_config, self.SPOTS_URL, self.POLL_INTERVAL_SEC) @@ -41,6 +46,48 @@ class Tiles(HTTPSpotProvider): new_spots.append(spot) return new_spots + def can_submit_spot(self, sig): + return sig == "Tiles" + + def submit_spot(self, spot, credentials): + # Tiles on the air currently only supports *self* spots + if spot.dx_call == spot.de_call: + + # Figure out a valid mode. Borrowed this from PoLo :) + # https://github.com/ham2k/app-polo/blob/main/src/extensions/activities/sota/SOTAPostSelfSpot.js + if spot.mode: + mode = spot.mode + if mode not in self.VALID_MODES: + if mode in SSB_SUB_MODES: + mode = "SSB" + elif mode == "OLIVIA": + mode = "Olivia" + elif mode == "JS8": + mode = "JS8Call" + else: + mode = "Other" + + body = { + "call_sign": spot.dx_call, + "frequency": str(spot.freq / 1000000.0), + "mode": mode or "", + "grid": spot.dx_grid or "", + "comment": spot.comment or "", + "lat": spot.dx_latitude or None, + "lon": spot.dx_longitude or None, + "qrt": spot.qrt or False, + "pin": credentials.get("offline_spot_gateway_pin", "") + } + headers = {**HTTP_HEADERS, "Content-Type": "application/json"} + response = requests.post(self.SUBMIT_URL, json=body, headers=headers, timeout=(5, 30)) + if not response.ok: + raise RuntimeError("Tiles on the Air API returned " + str(response.status_code) + ": " + response.text) + else: + raise RuntimeError("The Tiles on the Air API requires a mode to be set.") + else: + raise RuntimeError("The Tiles on the Air API only supports self-spots, the DX call and spotter call must match.") + + # Utility function to keep the first decimal point in a given string but remove any others. Used to parse Tiles' # strange frequency format where we can sometimes have e.g. "14.123.5". def strip_extra_decimal_points(s): diff --git a/templates/about.html b/templates/about.html index 2672326..b36c287 100644 --- a/templates/about.html +++ b/templates/about.html @@ -69,7 +69,7 @@
This software is dedicated to the memory of Tom G1PJB, SK, a friend and colleague who sadly passed away around the time I started writing it in Autumn 2025. I was looking forward to showing it to you when it was done.
- + {% end %} \ No newline at end of file diff --git a/templates/add_spot.html b/templates/add_spot.html index 838f5b2..5661274 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -109,8 +109,8 @@ - - + + {% end %} diff --git a/templates/alerts.html b/templates/alerts.html index da63985..532c686 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -70,8 +70,8 @@ - - + + {% end %} \ No newline at end of file diff --git a/templates/bands.html b/templates/bands.html index 734b3f6..db6d520 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -76,9 +76,9 @@ - - - + + + {% end %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 24a863d..359b550 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,6 @@ {% extends "skeleton.html" %} {% block head_extra %} - + @@ -19,9 +19,9 @@ integrity="sha384-L1eE4eD41kpBIWe2I0eHy+GnEUC4RIpcvibVW2JCminuPlTl+2Bc528iPdVMg5Dn" crossorigin="anonymous"> - - - + + + {% end %} {% block body %}