fix(reader): correct the QR reader's identity — Dingtian DT-008, not "GEE"
Build desktop / desktop (push) Successful in 4m16s
Build & push images / images (push) Successful in 2m46s
CI / check (push) Successful in 38s

An early wrong assumption named the QR/RFID access reader "GEE" /
"GEE/Fondvision" / "GEE-QR-ER80" (and summarized a raw GEE PDF as its
datasheet). There is no GEE device — it's the Dingtian DT-008
(dingtian-tech.com/en_us/qr_code_reader.html), the same vendor as the relay
board, which is why it integrates the identical HTTP-GET-push way.

Code:
- Driver symbol geeQrReaderDriver → dingtianQrReaderDriver; label →
  "Dingtian DT-008 QR/RFID reader (HTTP push)"; comments/description rewritten
  to the real DT-008 facts (Wiegand 26/34, TCP/IP, USB, RS485 — not RS-232;
  QR/barcode + ID/IC/NFC — not DataMatrix/1D).
- Persisted driverId "gee-qr-reader" → "dingtian-qr-reader" (the registry
  lookup key + the row created on assign in qr-reader.ts).
- Migration 0015 rewrites existing devices.driver_id rows so configured readers
  keep resolving (applied to the dev DB — 2 rows; the booth applies it on boot).
  Behaviour is unchanged: naming + the persisted id only.

Wiki + memory:
- Renamed entities/gee-qr-er80.md → dingtian-dt008-reader.md and
  sources/gee-qr-er80.md → dingtian-dt008.md; rewrote both to the real DT-008
  product-page specs while KEEPING all the verified-on-hardware protocol facts
  (cjihao serial, .jsp path, Connection: close). Fixed every cross-reference +
  "GEE" mention in 6 other pages. Memory gee-reader-serial-binding →
  dingtian-reader-serial-binding. The only surviving "GEE" mentions are
  deliberate naming-correction notes, the raw PDF filename, and the
  append-only log history.

Full workspace build/lint/test green; dev DB readers verified resolving to the
registered dingtian-qr-reader driver.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-28 17:39:15 +02:00
parent 96acd6b662
commit f6e35bbebf
20 changed files with 138 additions and 101 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ talks only to the adapter interfaces ([[device-adapter-pattern]]), never a drive
> an **unprivileged ICMP ping** (`drivers/icmp.ts`, shells `/bin/ping` in SOCK_DGRAM mode — no
> CAP_NET_RAW, no native dep; the booth compose sets `net.ipv4.ping_group_range`). Reply → `ready`,
> no reply → `offline`; **no IP set → `degraded` ("set IP to monitor")**, never a false green.
> Verified on hardware: pinged the real readers on the device VLAN. See [[gee-qr-er80]].
> Verified on hardware: pinged the real readers on the device VLAN. See [[dingtian-dt008-reader]].
Both collapse to one **traffic-light**: `ready | degraded | offline`, plus a `detail` string. Fail
**toward "there's a problem"**, never false-healthy: a probe that throws or times out reads
+2 -2
View File
@@ -22,9 +22,9 @@ There are **two populations** of users, and they map to **two integration paths*
| [[wiegand]] reader → UHPPOTE port | The controller | Controller (onboard card list) | **Yes** — works if host down |
| Pure TCP/IP reader | Host only | Host, then UDP `open` to relay | No — host on critical path |
| [[lpr-camera|LPR]] / QR scanner | Host only | Host | No |
| **[[gee-qr-er80]] QR reader (serial)** | Host only | Host (reads serial → `read` bus) | No |
| **[[dingtian-dt008-reader|Dingtian DT-008]] QR reader** | Host only | Host (reads serial → `read` bus) | No |
> Concrete host-side reader on hand: the **[[gee-qr-er80]]** (QR over RS-232/RS-485). Note autonomy
> Concrete host-side reader on hand: the **[[dingtian-dt008-reader|Dingtian DT-008]]** (QR/RFID over HTTP push). Note autonomy
> is moot here anyway — the current relay ([[dingtian-relay]]) has **no onboard card list**, so even
> a Wiegand reader would be host-decided. So we take the serial/QR path straight to the host's
> `read` bus.
+2 -2
View File
@@ -80,9 +80,9 @@ must have:
- The **scanner is a device behind an adapter** ([[device-adapter-pattern]]): a new `ReaderDevice`
kind (QR/barcode imager) — likely the same `IdentitySource = "ticket"` / `"qr"` path. Keeps the
app device-agnostic; hardware model is procurement ([[bom]], [[open-questions]]).
- **On hand:** the **[[gee-qr-er80]]** QR access reader (`-Q-W`: QR scanner, Wiegand/RS-232/RS-485,
- **On hand:** the **[[dingtian-dt008-reader|Dingtian DT-008]]** QR/RFID reader (Wiegand/TCP-IP/USB/RS485,
Linux-supported) — the concrete scanner for this path. A serial `ReaderDevice` adapter feeds the
`read` bus; pending the reader's RS-232 frame/baud (see [[gee-qr-er80]] open questions).
`read` bus; via HTTP-GET push (see [[dingtian-dt008-reader]]).
## Ticketless alternative (plate as the ticket)
@@ -1,27 +1,34 @@
---
type: entity
tags: [parking, hardware, readers, qr]
sources: [gee-qr-er80]
updated: 2026-06-16
tags: [parking, hardware, readers, qr, dingtian]
sources: [dingtian-dt008]
updated: 2026-06-28
status: open
---
# GEE-QR-ER80 (QR access reader)
# Dingtian DT-008 (QR/RFID access reader)
The project's **QR-code reader** (GEE NFC LIMITED). A static optical scanner for **QR /
DataMatrix / 1D barcode**, optional ID/IC card. This is the **[[ticket-encoding|QR ticket]]
scanner** the design called for — read at the pay station and exit lane — and a path for **QR
[[subscription]]** credentials. On hand: variant **`-Q-W`** (QR scanner; Wiegand/RS-232/RS-485).
(See [[gee-qr-er80|datasheet summary]] / `raw/`.)
The project's **QR / barcode / RFID reader** — Dingtian **DT-008**, the **same vendor as the
[[dingtian-relay|relay board]]** (which is why it integrates the same HTTP-push way). A static
optical + card scanner for **QR / barcode (simple)** and **ID/IC/NFC cards**. This is the
**[[ticket-encoding|QR ticket]]** scanner (read at the pay station / exit lane) and a path for **QR
or RFID [[subscription]]** credentials. Product page: dingtian-tech.com/en_us/qr_code_reader.html.
(See [[dingtian-dt008|spec summary]] / `raw/`.)
## What it is (and isn't)
> **Naming correction (2026-06-28).** Through most of this project this reader was wrongly called
> **"GEE" / "GEE/Fondvision" / "GEE-QR-ER80"** — a bad early assumption. There is no GEE device; it
> is the **Dingtian DT-008**. The driver id was renamed `gee-qr-reader` → `dingtian-qr-reader`
> (migration 0015 rewrites existing rows). All *protocol/behaviour* facts below were observed on the
> real hardware and remain correct — only the device identity was wrong.
- **Optical, not RFID-prox.** Earlier we *assumed* "ER80-EM" = a 125 kHz EM4100 card reader — the
datasheet corrects that: it's a **QR/barcode scanner**. The `-EM` in the original label was a
mis-id; the real model is **GEE-QR-ER80**. Optional `D`/`C` variants add ID/IC card, but the unit
on hand is **QR-only** (`-Q`).
- **Multi-interface** (Wiegand 26/34, RS-232, RS-485, USB, TCP/IP); the `-W` variant exposes
**Wiegand + RS-232/RS-485**.
## What it is
- **QR/barcode + card.** Reads **QR / simple barcode** AND **ID/IC/NFC cards** (~0.2 s, 0–10 cm).
(An earlier note guessed "ER80-EM = 125 kHz EM4100 prox" — that was part of the same mis-id.)
- **Interfaces: Wiegand 26/34, TCP/IP, USB, RS485.** (NOT RS-232 — that was a fictional-datasheet
claim.) For our integration it talks **HTTP over TCP/IP**; Wiegand is the reader's *output line*
on a valid read, not the host transport.
- **Power** 9–16 V DC, 800 mA; **86×86×42 mm**; −30…70 °C. Fits the [[disk-os-hardening|appliance]].
## How it integrates — HTTP-GET push, server replies the verdict (confirmed via SDK)
@@ -48,7 +55,7 @@ barrier. ([[device-input-flow]] is the analogous push pattern; this one also ret
- Pushes over plain **HTTP** to our `10.0.10.x` host (on the device subnet); no serial wiring, no
Wiegand-decode hardware. Suits the host-in-the-loop model; autonomy is moot anyway
([[dingtian-relay]] has no onboard ACL).
- **Linux-supported**, 4–15 VDC, default IP `192.168.1.99` — fits the [[disk-os-hardening|appliance]].
- Default IP `192.168.1.99` (set its server IP/port to this host in the vendor tool).
## Resolved (2026-06-16)
@@ -93,18 +100,18 @@ from 10.0.10.7 (referer: http://www.fondvision.com — the OEM is Fondvision)
## Assignment (as-built 2026-06-16)
A dedicated **`gee-qr-reader`** driver ([[device-registry]], reader category) models the push reader:
A dedicated **`dingtian-qr-reader`** driver ([[device-registry]], reader category) models the push reader:
its one config field is **`serial`** (the `cjihao` the device reports). The admin assigns it in the
[[first-run-setup|setup wizard]] like any device (normal UUID row id), enters the serial, and binds
it to a controller relay. The QR endpoint resolves the reader by **matching `config.serial` to the
scan's `cjihao`** — not by row id — so no DB hand-editing. Set the reader's server IP/port to this
host in the **vendor tool**; assign + enter its serial + bind it here.
- Verified via inject: assign `gee-qr-reader` {serial:"H05M2AFA"} bound to an access relay →
- Verified via inject: assign `dingtian-qr-reader` {serial:"H05M2AFA"} bound to an access relay →
a `.jsp` scan with that serial + a matching permit QR → `status:1` (2-beep accept) + open; re-scan
→ permit exit; unknown card → `status:0`; unassigned serial → `status:0` (no relay, graceful).
- Note `tcpip-reader` is the WRONG model for this device (host-connects-out, a stub) — use
`gee-qr-reader`.
`dingtian-qr-reader`.
## Open / next
+3 -3
View File
@@ -237,7 +237,7 @@ LPR/ANPR plate identity** (the plate binding below):
(a chosen value could be guessable or collide). The UI sends a blank QR credential; the server
mints the value and returns it (so the UI can print it). **An RF credential, by contrast, carries
the physical card id, so it is operator-entered.**
- **Reader output = TCP/IP full string** (decided 2026-06-18, the [[gee-qr-er80|host-in-the-loop
- **Reader output = TCP/IP full string** (decided 2026-06-18, the [[dingtian-dt008-reader|host-in-the-loop
QR reader]] path): the reader delivers the whole decoded string, so the code length is free
(unguessable token). *If a site ever wires the reader as **Wiegand 26/34** instead, a scanned
QR truncates to a 24-/32-bit number — the generated code would then have to be a numeric id in
@@ -255,7 +255,7 @@ LPR/ANPR plate identity** (the plate binding below):
- **RF tag / chip / card — selectable later, NOT live yet.** An RFID/proximity credential, read
**host-side** (reader → host → `pulseOpen`). **LIVE since 2026-06-18** — the operator selects RFID
and **reads the card off a physical reader** (see "Enrolling a card" below) rather than typing the
number. The GEE readers are combo QR + RFID (ID/IC/NFC), so the same device captures both. A
number. The Dingtian DT-008 readers are combo QR + RFID (ID/IC/NFC), so the same device captures both. A
Wiegand-out reader keeps a future autonomous path open ([[entry-exit-readers]]); the
[[dingtian-relay]] has no onboard card list.
- **Plate (LPR/ANPR) — matching is BUILT, the live SOURCE is the one missing wire.** When plate-bound
@@ -296,7 +296,7 @@ traffic, so capture must **borrow one reader briefly without blocking the other*
A subscription is a one-to-many aggregate: it may hold **several credentials at once** — e.g. a QR
**and** an RFID card (and later NFC). Each is its own `subscription_credentials` row; any of them
resolves the same subscription at the barrier. (NFC works today as an `rf` credential on the combo
GEE reader; a distinct `nfc` `kind` is a small future labelling-only addition.)
Dingtian DT-008 reader; a distinct `nfc` `kind` is a small future labelling-only addition.)
Crucially, **entry and exit are NOT bound to the same credential.** Originally the session was keyed
by the exact credential value read, so you had to leave with whatever you arrived with — an
+2 -2
View File
@@ -16,7 +16,7 @@ Counts: 4 sources · 19 entities · 45 concepts · 7 decision records.
## Sources
- [[parking-system-architecture]] — design notes: stack, threat model, devices, UHPPOTE, ESP32, readers, BOM, open decisions.
- [[gee-qr-er80]] — datasheet: GEE QR access reader (QR/DM/1D; Wiegand/RS-232/485/USB/TCP; Linux).
- [[dingtian-dt008]] — Dingtian DT-008 product page: QR/RFID access reader (QR/barcode + ID/IC/NFC; Wiegand/TCP-IP/USB/RS485; HTTP-GET push).
- [[qrcode-sdk]] — QRCode SDK v1.6.5: the reader's HTTP-GET-poll protocol + JSON verdict (beep/output).
- [[parksql2017-legacy-schema]] — predecessor SQL Server schema (Albanian market): legacy tariff/discount/membership/shift/fiscal model; confirms blocks, adds time-windows + categories, lacks postpaid sponsors.
@@ -40,7 +40,7 @@ Counts: 4 sources · 19 entities · 45 concepts · 7 decision records.
- [[atecc608]] — secure element; non-extractable signing key (host events + controller auth).
- [[wiegand]] — reader standard feeding the controller directly (autonomous permit-holder path).
- [[lpr-camera]] — edge-AI plate recognition; host-side casual-identity source.
- [[gee-qr-er80]] — QR access reader on hand; host-side serial → `read` bus (the QR-ticket scanner).
- [[dingtian-dt008-reader]] — Dingtian DT-008 QR/RFID reader on hand; host-side serial → `read` bus (the QR-ticket scanner).
- [[zkteco-controller]] — ❌ rejected/historical; aux-input path was a contender, not pursued.
- [[dingtian-relay]] — ✅ CHOSEN access controller; decoupled inputs solve the button blocker (driver verified on hardware); spare relays drive aux outputs (`setAux`).
- [[hikvision-radar]] — vehicle-presence radar on a Dingtian input; the entry presence gate (per-input active-level caveat).
+17
View File
@@ -1884,3 +1884,20 @@ snapshots from it before I caught it. The signed ledger was untouched (snapshots
advisory; `PRAGMA integrity_check: ok`, ledger_events/sessions/subscriptions intact) and it was dev
not prod — but it violated the never-touch-the-live-DB rule. Lesson: pass the scratch path
explicitly + guard-refuse any non-scratch path BEFORE any destructive op (the corrected harness does).
## [2026-06-28] fix | The QR reader is a Dingtian DT-008, NOT "GEE" (correct an early mis-id)
A wrong early assumption named the QR/RFID access reader "GEE" / "GEE/Fondvision" / "GEE-QR-ER80"
(and summarized a `raw/GEE-QR-ER80….pdf` as its datasheet). There is no GEE device — it's the
**Dingtian DT-008** (https://www.dingtian-tech.com/en_us/qr_code_reader.html), the SAME vendor as
the relay board, which is why it integrates the identical HTTP-GET-push way. Corrected across the
codebase + wiki: driver symbol `geeQrReaderDriver`→`dingtianQrReaderDriver`, **persisted driverId
`gee-qr-reader`→`dingtian-qr-reader`** (migration **0015** rewrites the existing `devices.driver_id`
rows so readers keep resolving — applied to the dev DB, 2 rows; the booth applies it on boot), label
"Dingtian DT-008 QR/RFID reader (HTTP push)", and all server/test/comment refs. Wiki: renamed
`entities/gee-qr-er80.md`→`entities/dingtian-dt008-reader.md` and `sources/gee-qr-er80.md`→
`sources/dingtian-dt008.md`, rewrote both to the REAL DT-008 specs (Wiegand26/34 + TCP/IP + USB +
RS485 — NOT RS-232; QR/barcode + ID/IC/NFC — NOT DataMatrix/1D; 9–16 V/800 mA) while keeping all the
verified-on-hardware protocol facts (cjihao serial, `.jsp` path, `Connection: close`). Memory
`gee-reader-serial-binding`→`dingtian-reader-serial-binding`. The only surviving "GEE" mentions are
deliberate naming-correction notes + the raw PDF filename. Behaviour unchanged — naming + the
persisted id only. build/lint/test green.
+37
View File
@@ -0,0 +1,37 @@
---
type: source
tags: [parking, hardware, readers, qr, dingtian, datasheet]
sources: [dingtian-dt008]
updated: 2026-06-28
---
# Source: Dingtian DT-008 QR Code Reader (product page)
Vendor product page for the **Dingtian DT-008** QR/barcode/RFID access reader —
https://www.dingtian-tech.com/en_us/qr_code_reader.html. This is the reader the project
actually has on hand for the [[ticket-encoding|QR ticket]] / RFID-[[subscription]] paths.
Entity: [[dingtian-dt008-reader]]. Same vendor as the [[dingtian-relay|Dingtian relay board]].
> **Supersedes a mis-identified source.** The project originally summarized
> `raw/GEE-QR-ER80 QR Code Access Control Reader.pdf` as if the reader were a "GEE-QR-ER80"
> (GEE NFC LIMITED). That was a wrong early assumption — the device is the Dingtian DT-008.
> Several facts in that PDF (RS-232, DataMatrix/1D, a Linux SDK) do **not** match the real
> hardware and were dropped. The HTTP-push protocol facts, however, were later **verified on the
> actual device** (see [[dingtian-dt008-reader]] "Verified on hardware").
## Key takeaways (DT-008 product page)
- **Scans:** QR / barcode (simple) **and** ID/IC/NFC cards. Read ~**0.2 s**, range **0–10 cm**.
- **Interfaces:** **Wiegand 26/34, TCP/IP, USB, RS485.** (For our integration: HTTP over TCP/IP.)
- **Power:** 9–16 V DC, 800 mA. **Size:** 86×86×42 mm. **Temp:** −30…70 °C.
- **Wiegand pinout:** Red(power), Black(GND), Green(D0), White(D1), Blue(LED), Yellow(BEEP).
- **Reporting:** "QR Code HTTP GET" — the reader HTTP-GETs a configured host on each scan; the host
replies the verdict (drives beep/output). Vendor ships a **QR Code config tool** + HTTP-GET demo
code (C++/C#/PHP) and an SDK (`QRCode_sdk.zip`). See [[qrcode-sdk]] for the protocol we implement.
## How we use it
Host-in-the-loop, synchronous HTTP: reader GETs `/qa/mcardsea.<ext>` with its serial (`cjihao`);
our endpoint (`apps/server/src/routes/qr-reader.ts`) decides and replies the SDK verdict, and emits
a `read` event for the access flows. Full integration + the on-hardware findings (the `.jsp` path
extension, `Connection: close` requirement, serial resolution) are in [[dingtian-dt008-reader]].
-37
View File
@@ -1,37 +0,0 @@
---
type: source
tags: [parking, hardware, readers, qr, datasheet]
sources: [gee-qr-er80]
updated: 2026-06-16
---
# Source: GEE-QR-ER80 QR Code Access Control Reader (datasheet)
Vendor datasheet (GEE NFC LIMITED, ©2007–2019) for the **GEE-QR-ER80** — a static
**QR-code access-control reader**, optional ID/IC card. The reader the project has
on hand for the [[ticket-encoding|QR ticket]] path. Raw:
`raw/GEE-QR-ER80 QR Code Access Control Reader.pdf` (3 pages). Entity: [[gee-qr-er80]].
## Key takeaways
- **Optical scanner**, not a prox-card reader: reads **QR, DataMatrix, 1D barcode** (static).
Optional add-ons for **IC card UID / ID card**.
- **Multi-interface:** **Wiegand 26/34, RS-232, RS-485, USB, TCP/IP** — selectable by variant.
- **Power:** 4–15 VDC, < 800 mA. **Read direction:** 360°. Built-in scanner LED.
- **OS:** Windows XP/7/8/10 **and Linux** (explicit) — fits the [[disk-os-hardening|Linux appliance]].
- **Wiring (Wiegand/RS-232/485 variant):** VCC(+12V), GND, **D0/D1** (Wiegand), **TX/R+ , RX/R-**
(RS-232 / RS-485), plus **LED** and **BEEP** control lines (host can drive feedback).
- **Order code** `GEE-QR-ER80-<scanner>-<interface>`: `Q`=QR scanner / `D`=ID reader / `C`=IC reader;
`W`=WG·RS232·RS485 / `U`=USB / `T`=RJ45 (TCP/IP). **On hand: `-Q-W`** (QR scanner; Wiegand/RS-232/RS-485).
## Section map
- p1 — overview, physical + feature table (interfaces, power, read direction).
- p2 — supported types (QR/DM/1D + optional IC/ID), OS, environment; **wire definition** (pin table).
- p3 — order-code breakdown, applications (access control / vacation rentals / time attendance).
## Not in this datasheet (open)
- The **RS-232/RS-485 data protocol**: baud rate, frame format, and whether a QR scan is emitted as
an **ASCII string** (expected) vs. some framed protocol. Decides the host-side adapter — see
[[gee-qr-er80]] open questions. Resolve by vendor docs or by observing the port on a scan.
+4 -4
View File
@@ -5,13 +5,13 @@ sources: [qrcode-sdk]
updated: 2026-06-16
---
# Source: QRCode SDK v1.6.5 (GEE/Dingtian QR reader)
# Source: QRCode SDK v1.6.5 (Dingtian DT-008 QR reader)
Vendor SDK for the QR access reader ([[gee-qr-er80]]; also branded Dingtian). Defines the
Vendor SDK for the QR access reader (the Dingtian [[dingtian-dt008-reader]]). Defines the
reader↔server **HTTP protocol** — the missing piece the datasheet omitted. Files at
`QRCode_sdk - QRCode_v1_6_5/sdk/` (config tool `QRCode_v1_6_5.exe`, demos in C#/PHP/VC++, protocol
docs `readme.txt`, `qrcode_HTTP_GET.txt`, `VC++/how to.txt`). **Not copied into `raw/`** — bulky +
binaries; this summary is the faithful capture. Entity: [[gee-qr-er80]].
binaries; this summary is the faithful capture. Entity: [[dingtian-dt008-reader]].
## The protocol — HTTP GET poll, server replies the verdict
@@ -52,7 +52,7 @@ GET /qa/mcardsea.php?cardid=445D2C&mjihao=1&cjihao=HW256097&status=11&time=15404
This is **host-in-the-loop, synchronous**: the GET *is* the access query; our JSON reply *is* the
decision. So the backend endpoint must **decide (valid/invalid + direction) and reply** — richer
than a fire-and-forget read. Direction comes from the `status` low digit. See [[gee-qr-er80]] +
than a fire-and-forget read. Direction comes from the `status` low digit. See [[dingtian-dt008-reader]] +
[[device-input-flow]].
## Defaults / misc