Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions carwatch/trips.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from __future__ import annotations

import os
import subprocess
import time
from dataclasses import dataclass
Expand All @@ -33,16 +34,45 @@ class Event:
at: float


def _iw_ssid() -> str | None:
"""SSID from `iw dev <if> link` on each wireless interface. The VTA-439
(27 Sep 2026) had no wireless-tools, so iwgetid did not exist and every
"am I at home" check read False: the dash asked for a password on the
home wifi and trips could not see home. iw ships with the kernel's
wireless stack on Ubuntu and Pi OS alike."""
try:
ifaces = [n for n in os.listdir("/sys/class/net")
if os.path.isdir(f"/sys/class/net/{n}/wireless")]
except OSError:
return None
for iface in ifaces:
try:
out = subprocess.run(["iw", "dev", iface, "link"], capture_output=True,
text=True, timeout=5).stdout
except Exception:
continue
for line in out.splitlines():
line = line.strip()
if line.startswith("SSID:"):
ssid = line[len("SSID:"):].strip()
if ssid:
return ssid
return None


def current_ssid() -> str | None:
"""SSID the Pi is associated with, or None. Uses iwgetid (wireless-tools)."""
"""SSID the box is associated with, or None. iwgetid (wireless-tools)
first, as installed by install.sh; `iw` when iwgetid is missing."""
try:
out = subprocess.run(
["iwgetid", "-r"], capture_output=True, text=True, timeout=5
)
ssid = out.stdout.strip()
return ssid or None
if ssid:
return ssid
except Exception:
return None
pass
return _iw_ssid()


class TripTracker:
Expand Down
4 changes: 4 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ echo ">> apt: updating package lists"
sudo apt-get update -qq
sudo apt-get install -y wireless-tools poppler-utils bluez bluez-alsa-utils \
alsa-utils ffmpeg network-manager rsync curl git >/dev/null
# The voice listener opens /dev/snd, which is group audio. Pi OS puts the first
# user in audio; Ubuntu (the VTA-439, 27 Sep 2026) does not, and the listener
# then cannot open the USB speakerphone at all.
id -nG "$RUN_USER" | grep -qw audio || sudo usermod -aG audio "$RUN_USER"

# 5) Every systemd unit, user/home rewritten to yours.
for u in "$DEST"/systemd/carwatch-*.service "$DEST"/systemd/carwatch-*.timer; do
Expand Down
50 changes: 50 additions & 0 deletions tests/test_current_ssid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""current_ssid on boxes without wireless-tools (VTA-439, 27 Sep 2026): the
dash asked for a password on the home wifi because iwgetid did not exist.
subprocess and /sys are mocked; nothing touches real networking."""
import os
import sys
import unittest
from unittest import mock

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from carwatch import trips # noqa: E402

IW_LINK = ("Connected to 3c:7c:3f:34:62:44 (on wlp98s0)\n"
"\tSSID: wifi router\n\tfreq: 5180.0\n")


def fake_run(iwgetid=None, iw=IW_LINK):
def run(cmd, **kw):
if cmd[0] == "iwgetid":
if iwgetid is None:
raise FileNotFoundError("iwgetid")
return mock.Mock(stdout=iwgetid)
if cmd[0] == "iw":
return mock.Mock(stdout=iw)
raise AssertionError(cmd)
return run


class CurrentSsid(unittest.TestCase):
def _call(self, **kw):
with mock.patch.object(trips.subprocess, "run", side_effect=fake_run(**kw)), \
mock.patch.object(trips.os, "listdir", return_value=["lo", "wlp98s0"]), \
mock.patch.object(trips.os.path, "isdir",
side_effect=lambda p: p.endswith("wlp98s0/wireless")):
return trips.current_ssid()

def test_iwgetid_wins_when_present(self):
self.assertEqual(self._call(iwgetid="Pi Home\n"), "Pi Home")

def test_falls_back_to_iw_when_iwgetid_missing(self):
self.assertEqual(self._call(iwgetid=None), "wifi router")

def test_falls_back_to_iw_when_iwgetid_empty(self):
self.assertEqual(self._call(iwgetid=""), "wifi router")

def test_not_connected_is_none(self):
self.assertIsNone(self._call(iwgetid=None, iw="Not connected.\n"))


if __name__ == "__main__":
unittest.main()
Loading