From 2bdb59b49a3e5514c50d2207fec2886dcf6d6222 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 16 Jul 2026 19:08:49 +0000 Subject: [PATCH] v0.2.6: fix kiosk-code scan resolution + add zone QR asset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ScanScreen.lookupCode tried getMetersByScannerCode first and let its thrown error-envelope abort the whole lookup, so the getMetersByZoneName fallback never ran; it also sent ZoneName in the scanned case (the API is case-sensitive — the official app lowercases). Confirmed via live mitmproxy capture of the official app: a scanned code resolves through ScannerCode, and ZoneName=dl (lowercase) returns the zone. Now tries ScannerCode -> ZoneName -> zonename(lowercased) -> TerminalSerNo, each independent and non-fatal; only reports "Lookup failed" if every attempt errored. Adds docs/zone-qr/ with the DL-Sandpoint QR (encodes ScannerCode "DL", works in both BigBrainParking and the official app) and a how-to. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/app.json | 4 +-- app/src/screens/ScanScreen.tsx | 48 ++++++++++++++++++++++----------- docs/zone-qr/DL-sandpoint.png | Bin 0 -> 431 bytes docs/zone-qr/README.md | 36 +++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 docs/zone-qr/DL-sandpoint.png create mode 100644 docs/zone-qr/README.md diff --git a/app/app.json b/app/app.json index d7f9dba..4c83119 100644 --- a/app/app.json +++ b/app/app.json @@ -3,14 +3,14 @@ "name": "BigBrainParking", "slug": "bigbrainparking", "scheme": "bigbrainparking", - "version": "0.2.5", + "version": "0.2.6", "orientation": "portrait", "userInterfaceStyle": "automatic", "newArchEnabled": true, "icon": "./assets/icon.png", "android": { "package": "top.mowden.bigbrainparking", - "versionCode": 15, + "versionCode": 16, "edgeToEdgeEnabled": true, "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", diff --git a/app/src/screens/ScanScreen.tsx b/app/src/screens/ScanScreen.tsx index 1901a4e..25a602e 100644 --- a/app/src/screens/ScanScreen.tsx +++ b/app/src/screens/ScanScreen.tsx @@ -66,23 +66,41 @@ export function ScanScreen() { const lookupCode = useCallback( async (code: string) => { setBusy(true); - try { - let res = await ps.getMetersByScannerCode(code); - let zone = res.Zones?.[0]; - if (!zone) { - res = await ps.getMetersByZoneName(code); + // A scanned/typed code can resolve by terminal serial, scanner code, or + // zone name. Unmatched lookups return an error envelope (which the client + // throws), and ZoneName is case-sensitive server-side — the official app + // queries it lowercased. So try each strategy independently and don't let + // one failing attempt abort the others. + // ScannerCode is what kiosk QRs encode and what the official app honors; + // zone name (case-sensitive) and terminal serial are extra fallbacks. + const attempts = [ + () => ps.getMetersByScannerCode(code), + () => ps.getMetersByZoneName(code), + () => ps.getMetersByZoneName(code.toLowerCase()), + () => ps.getMetersBySerialNumber(code), + ]; + let zone: + | NonNullable>['Zones']>[number] + | undefined; + let anyResponded = false; + for (const attempt of attempts) { + try { + const res = await attempt(); + anyResponded = true; zone = res.Zones?.[0]; + if (zone) break; + } catch { + // strategy didn't apply (e.g. error envelope) — try the next one } - if (zone) { - setManualOpen(false); - navigation.navigate('MeterDetail', { zone }); - } else { - Alert.alert('Not found', `No meter matches "${code}".`); - } - } catch (e: any) { - Alert.alert('Lookup failed', e?.serverMessage ?? e?.message ?? 'error'); - } finally { - setBusy(false); + } + setBusy(false); + if (zone) { + setManualOpen(false); + navigation.navigate('MeterDetail', { zone }); + } else if (anyResponded) { + Alert.alert('Not found', `No meter matches "${code}".`); + } else { + Alert.alert('Lookup failed', 'Could not reach ParkSmarter. Check your connection.'); } }, [navigation], diff --git a/docs/zone-qr/DL-sandpoint.png b/docs/zone-qr/DL-sandpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..74afee570a9ff74c688fd2033e3c42ea12442e02 GIT binary patch literal 431 zcmeAS@N?(olHy`uVBq!ia0y~yV4McRjLblhL^JcNK#DEEC&U#<|NsAg>CO3ffh?wy zAiv=M|4(eae;ml;EbxddW?p#daSW-L^Y-RJ-opw!4uRgX3d$R+ z++HzE%?P@kpd_`@Jf=2Y;EMk88CD8kzYFlNH6Khckl#{&tQ zB;#M4%-yjp!JzX@@cr)rEdN+^1$fkUc6Ljg*)Os2K*A;I?g^=zv$@zNA8XObE#A(Q zIkoZN6QkrWr)K=?I;diJeD%JovHSIow%@qWkzgR%dvqE1{58|buuWH`Kg*r%sZ=Ngs~;SN($zbpmZV z?a^_GkM(x!IWrr<=95WnyXVN6&HnyG;><6#y0BZ%)=drE|3y#2NAL0Os@Er9e*r4; zOtrD(Nxd{V+2FNw_qWKM?Te@A0{xWf)A4AzTAlmPwd%(c#LV}zz(S#U=5c8YPjkV! T@1ODmLzuzS)z4*}Q$iB}CJVIF literal 0 HcmV?d00001 diff --git a/docs/zone-qr/README.md b/docs/zone-qr/README.md new file mode 100644 index 0000000..aed65dc --- /dev/null +++ b/docs/zone-qr/README.md @@ -0,0 +1,36 @@ +# Zone QR codes + +A parking-zone QR code just encodes the zone's **`ScannerCode`** as plain text. +BigBrainParking's scanner (and the official ParkSmarter app) resolve that code to +the zone. No URL, no wrapper — the raw scanner code is enough. + +## Make one + +```bash +qrencode -o my-zone.png -s 14 -m 4 "" +``` + +`` is the zone's `ScannerCode` field (visible on the meter detail +screen, or in a `/api/Meter` response). For the DL zone in Sandpoint it's `DL`: + +```bash +qrencode -o DL-sandpoint.png -s 14 -m 4 "DL" +``` + +![DL zone, Sandpoint](DL-sandpoint.png) + +## Which field to encode + +Use **`ScannerCode`** — it's what physical kiosk QR stickers encode and the only +field the official ParkSmarter app accepts when scanning. + +- `ScannerCode` — ✅ works in BigBrainParking **and** the official app. +- `ZoneName` (e.g. `dl`) — works as a fallback in BigBrainParking (case-sensitive + server-side; the app tries the lowercased form too). +- `TerminalSerNo` (e.g. `30004246`) — uniquely identifies the terminal and works + in BigBrainParking, but the **official app rejects a bare serial** ("not + recognized as a ParkSmarter zone"), so don't use it for cross-app QR codes. + +When BigBrainParking scans a code it tries, in order: `ScannerCode → ZoneName → +zonename(lowercased) → TerminalSerNo`, each independently, so any of these +resolve.