Skip to content

Commit

Permalink
Initial work on icons.
Browse files Browse the repository at this point in the history
  • Loading branch information
afourney committed Nov 11, 2024
1 parent 8cd9618 commit 324fd9a
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 64 deletions.
46 changes: 25 additions & 21 deletions src/aprstastic/_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def __init__(self, config):
self._next_serial_check_time = 0
self._last_meshtastic_packet_time = 0

self._id_to_call_signs = CallSignRegistry(config.get("data_dir"))
self._registry = CallSignRegistry(config.get("data_dir"))

def run(self):
# For measuring uptime
Expand Down Expand Up @@ -113,15 +113,17 @@ def on_recv(packet):
for node in self._interface.nodesByNum.values():
presumptive_id = f"!{node['num']:08x}"

if presumptive_id not in self._id_to_call_signs:
if presumptive_id not in self._registry:
continue

# Heard more than a day ago
last_heard = node.get("lastHeard")
if last_heard is None or last_heard + 3600 * 24 < time.time():
continue

self._filtered_call_signs.append(self._id_to_call_signs[presumptive_id])
self._filtered_call_signs.append(
self._registry[presumptive_id]["call_sign"]
)

# Connect to APRS IS
aprsis_passcode = self._config.get("aprsis_passcode")
Expand Down Expand Up @@ -269,12 +271,12 @@ def _process_meshtastic_packet(self, packet):
should_announce = self._spotted(fromId)

if portnum == "POSITION_APP":
if fromId not in self._id_to_call_signs:
if fromId not in self._registry:
return

position = packet.get("decoded", {}).get("position")
self._send_aprs_position(
self._id_to_call_signs[fromId],
self._registry[fromId]["call_sign"],
position.get("latitude"),
position.get("longitude"),
position.get("time"),
Expand All @@ -298,7 +300,7 @@ def _process_meshtastic_packet(self, packet):

if message_string.strip() == "?":
# Different call signs for registered and non-registered devices
if fromId not in self._id_to_call_signs:
if fromId not in self._registry:
self._send_mesh_message(
fromId,
"Send and receive APRS messages by registering your call sign. HAM license required.\n\nReply with:\n!register [CALLSIGN]-[SSID]\nE.g.,\n!register N0CALL-1\n\nSee https://github.com/afourney/aprstastic for more.",
Expand Down Expand Up @@ -327,13 +329,13 @@ def _process_meshtastic_packet(self, packet):
)
if m:
call_sign = m.group(1).upper()
if fromId in self._id_to_call_signs:
if fromId in self._registry:
# Update
self._id_to_call_signs.add_registration(fromId, call_sign, True)
self._registry.add_registration(fromId, call_sign, True)
self._send_mesh_message(fromId, "Registration updated.")
else:
# New
self._id_to_call_signs.add_registration(fromId, call_sign, True)
self._registry.add_registration(fromId, call_sign, True)
self._spotted(fromId)
self._send_mesh_message(
fromId,
Expand All @@ -356,14 +358,14 @@ def _process_meshtastic_packet(self, packet):
return

if message_string.lower().strip().startswith("!unregister"):
if fromId not in self._id_to_call_signs:
if fromId not in self._registry:
self._send_mesh_message(
fromId, "Device is not registered. Nothing to do."
)
return
call_sign = self._id_to_call_signs[fromId]
self._id_to_call_signs.add_registration(fromId, None, True)
self._id_to_call_signs.add_registration(None, call_sign, True)
call_sign = self._registry[fromId]["call_sign"]
self._registry.add_registration(fromId, None, True)
self._registry.add_registration(None, call_sign, True)

if self._beacon_registrations:
logger.info(
Expand All @@ -382,7 +384,7 @@ def _process_meshtastic_packet(self, packet):
self._send_mesh_message(fromId, "Device unregistered.")
return

if fromId not in self._id_to_call_signs:
if fromId not in self._registry:
self._send_mesh_message(
fromId,
"Unknown device. HAM license required!\nRegister by replying with:\n!register [CALLSIGN]-[SSID]\nE.g.,\n!register N0CALL-1",
Expand All @@ -394,11 +396,13 @@ def _process_meshtastic_packet(self, packet):
tocall = m.group(1)
self._reply_to[fromId] = tocall
message = m.group(3).strip()
self._send_aprs_message(self._id_to_call_signs[fromId], tocall, message)
self._send_aprs_message(
self._registry[fromId]["call_sign"], tocall, message
)
return
elif fromId in self._reply_to:
self._send_aprs_message(
self._id_to_call_signs[fromId],
self._registry[fromId]["call_sign"],
self._reply_to[fromId],
message_string,
)
Expand Down Expand Up @@ -441,7 +445,7 @@ def _process_aprs_packet(self, packet):
logger.info(
f"Observed registration beacon: {mesh_id}: {fromcall}",
)
self._id_to_call_signs.add_registration(mesh_id, fromcall, False)
self._registry.add_registration(mesh_id, fromcall, False)
else:
# Not necessarily and error. Could be from a future version
logger.debug(
Expand All @@ -463,8 +467,8 @@ def _process_aprs_packet(self, packet):

# Figure out where the packet is going
toId = None
for k in self._id_to_call_signs:
if tocall == self._id_to_call_signs[k].strip().upper():
for k in self._registry:
if tocall == self._registry[k]["call_sign"].strip().upper():
toId = k
break
if toId is None:
Expand Down Expand Up @@ -585,10 +589,10 @@ def _spotted(self, node_id):
"""

# We spotted them, but they aren't registered
if node_id not in self._id_to_call_signs:
if node_id not in self._registry:
return False

call_sign = self._id_to_call_signs[node_id]
call_sign = self._registry[node_id]["call_sign"]

if call_sign in self._filtered_call_signs:
return False
Expand Down
44 changes: 32 additions & 12 deletions src/aprstastic/_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
OVERRIDES_FILE = "registration_overrides.json"
PRECOMPILED_FILE = "precompiled_registrations.json"

COL_DEVICE_ID = 0
COL_CALL_SIGN = 1
COL_SETTINGS = 2
COL_TIMESTAMP = 3


class CallSignRegistry(object):
"""
Expand Down Expand Up @@ -142,23 +147,38 @@ def _rebuild(self):
)
rows = cursor.fetchall()
for row in rows:
operations.append((row[0], row[1], row[2], row[3]))
operations.append(
(
row[COL_DEVICE_ID],
row[COL_CALL_SIGN],
row[COL_SETTINGS],
row[COL_TIMESTAMP],
)
)

cursor.execute(
"SELECT device_id, call_sign, settings_json, timestamp FROM LocalRegistrations;"
)
rows = cursor.fetchall()
for row in rows:
operations.append((row[0], row[1], row[2], row[3]))
operations.append(
(
row[COL_DEVICE_ID],
row[COL_CALL_SIGN],
row[COL_SETTINGS],
row[COL_TIMESTAMP],
)
)

# Sort by date, ascending
operations.sort(key=lambda x: x[3])
operations.sort(key=lambda x: x[COL_TIMESTAMP])

self._merged = dict()
for op in operations:
d_id = op[0]
cs = op[1]
cs_key = self._get_key(self._merged, cs)
d_id = op[COL_DEVICE_ID]
icon = op[COL_SETTINGS]
cs = op[COL_CALL_SIGN]
cs_key = self._get_device_id(self._merged, cs)

# Delete the prior value(s)
if d_id is not None and d_id in self._merged:
Expand All @@ -172,7 +192,7 @@ def _rebuild(self):
continue

# Update
self._merged[d_id] = cs
self._merged[d_id] = {"call_sign": cs, "icon": icon}

cursor.close()

Expand All @@ -196,7 +216,7 @@ def _load_overrides(self, file_path):
# Return tuples in the expected format
tuples = override_data.get("tuples")
for t in tuples:
t[3] = future
t[COL_TIMESTAMP] = future
return tuples

def _load_precompiled(self, file_path):
Expand Down Expand Up @@ -256,15 +276,15 @@ def _load_precompiled(self, file_path):
# Return tuples in the expected format
tuples = precompiled_data.get("tuples")
for t in tuples:
t[3] = min(now, t[3])
t[COL_TIMESTAMP] = min(now, t[COL_TIMESTAMP])
return tuples

def _get_key(self, d, v):
def _get_device_id(self, d, call_sign):
"""
Return the first dictionary key that maps to a value.
Return the first device id that maps to the given call sign
"""
for k in d:
if d[k] == v:
if d[k]["call_sign"] == call_sign:
return k
return None

Expand Down
Loading

0 comments on commit 324fd9a

Please sign in to comment.