Etap 5 — Kiosk hardware integration¶
Etap 5 took the Flutter kiosk from a MockLockerHardware proof-of-concept to a Modbus-driven production kiosk on LOCKER-SZB-SDI-001. It also closed several lifecycle bugs that only surfaced once a real lock + sensor was on the bus.
Production state
LOCKER-SZB-SDI-001 na TEMREXLOCKERBOX runs Modbus TCP locker driver since v0.78, hardened through v0.83. The mock driver is still in the tree for dev / CI but production never touches it.
Hardware¶
- Locker rig: TEMREXLOCKERBOX with Modbus TCP latch controller on
172.31.0.201. - Driver:
lib/hardware/modbus_locker_hardware.dart. Implements the sameLockerHardwareinterface as the mock — pulses the lock coil for ~300 ms to release, then polls the door-contact register at ~5 Hz to emitLockerEvent.openedandLockerEvent.closed. - App wiring:
app.dartconstructsModbusLockerHardwarein production builds (config-driven; mock kept available for dev / CI).
Lifecycle bugs that landed in v0.78 … v0.83¶
Numbered by the version that fixed them:
v0.78— first Modbus build wired into the production kiosk; replacesMockLockerHardwareend-to-end.v0.79— "kliknąłem otwórz ponownie a była otwarta skrytka":LockerOpenScreenwould deadlock if the user re-tapped "Otwórz ponownie" while the cell was already open. Fix:open()is now idempotent on an already-released latch (no-op + immediateopenedevent).v0.80— broken contact sensor flicker would emit a phantomclosedevent mid-pickup. Fix: debounce on the sensor poll — close needs two consecutive sampled-closed reads.v0.81— "chcę nadać przesyłkę, skrytka 10 wolna": an orphanedASSIGNEDshipment could grab a locker that had been physically reused. Fix:/deposit/begin-existingnow refuses if the existing reservation points to a different machine, and the kiosk reconciles itscached_lockersview againstavailableFunctionsfrom/machines/me.v0.82— UX hardening drop:- Bigger touch targets on
Wyloguj/Anuluj/Cofnij(footer buttons). - PIN buffer is wiped on auth failure (was previously left intact, leading to silent re-attempts on top of stale digits).
Pickupis gated when the locker isn't reportingclosed— refuses to sendconfirm-pickupuntil the latch sensor agrees.Depositis gated when the cell hasn't returned toclosedsince the previous transaction.v0.83— "poszło w otwartą skrzynkę nie wykrył otwartej skrytki i jest w pętli": a deposit could go into an already-open cell because theisClosedcheck was racing the latch open/close cycle. Fix:LockerOpenScreenblocksNadajif the cell is reporting open, and the deposit flow pollsisClosed()before flipping to the success screen.
All of the above are persisted in Machine.currentVersion after the OTA agent installs the corresponding release.
Operational notes¶
- The Modbus driver shares the kiosk's network with the backend — check the kiosk's
externalIpinMachineand confirm it can reach the latch controller before reporting "kiosk hangs". - A latch that won't acknowledge will leave the locker in
RESERVEDuntilShipmentWorker.shipment.auto-expirefrees it onexpiresAt. To free it sooner, use the panel's emergency-open (POST /machines/:id/lockers/:lockerId/emergency-open) — that spawns anOPEN_LOCKERtask and the kiosk drives the cell open. - The
MockLockerHardwareis still in the tree for dev — toggle by env var or by editingapp.dart. CI uses the mock; production never does.
What's next¶
Etap 5 closes out kiosk hardware. Etap 6 (ErgoFlow integration, see etap-6-ergoflow-integration.md) was the next chunk shipped. Etap 7 (multi-machine sync, machine-selection engine, document/key-rental flows) and etap 8 (real notifications, OIDC code-flow, Gitea Actions, monitoring) remain.