SlopScore
00 crowd

lockbox

App development for a lockbox
Open repo on GitHubgithub.com/emah-maker/lockbox
TypeScript · ★ 1 · 0 forks · MIT · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)other
listed 39 minutes ago by emah-maker · last checked 39 minutes ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-10-03: App development for a lockbox; its own README says "ware) · Engineering ( engineering-problems-we-solved) · Get started ( getting-started) · Testing ( testing) · Built with Claude Code ( built". 1 stars; MIT license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as emah-maker. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
App development for a lockbox
created
2026-08-06 · pushed 6 hours ago · 250 commits · 3 contributors
languages
TypeScript 60%Python 17%JavaScript 16%Shell 3%CSS 2%HTML 2%
paperwork
licensereadme 42% health
dependencies
no dependency graph (no manifest, or disabled) · OSV.dev, checked 39 minutes ago

Disclosures, inferred by the Cap'm

slopbucket
vibe-coded
category
other
ai_generated
mostly
human_touch
light
status
works-on-my-machine
language (detected)
csshtmljavascriptpythonrubyshellswifttypescript
license (detected)
mit

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: App development for a lockbox; its own README says "ware) · Engineering ( engineering-problems-we-solved) · Get started ( getting-started) · Testing ( testing) · Built with Claude Code ( built". It carries the MIT license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README — the repo's own words, folded up so the grading fits on one screen

Phone Box: a phone lockbox with a real lock. Servo latch, touchscreen countdown, tunable emergency override, and an iOS companion app over Bluetooth.



Live site 2,500+ tests passing Status: working prototype MIT license

ESP32-S3 CircuitPython React Native Expo TypeScript Swift Firebase Bluetooth LE SolidWorks


Why · Features · How it works · Architecture · Hardware · Engineering · Get started · Testing · Built with Claude Code · License


2,500+ tests passing, 9K lines of firmware, 32K lines of app code, 8 BLE characteristics, 2 enclosure revisions



Setting the timer on the box's touchscreen The box's battery screen, fed by the MAX17043 fuel gauge The emergency override screen counting presses toward 25
SET THE TIMER
Swipe hours, minutes and seconds
REAL BATTERY GAUGE
A hand-modified MAX17043 on I²C
EMERGENCY OVERRIDE
Press 25× to get out early

Why a box, not an app

App blockers are one settings toggle from defeat. A drawer is one weak moment away. The only thing that reliably works is putting the phone somewhere you genuinely can't reach it.

A screen-time toggle can be disabled in two taps. This lock is real, so it can't.

The hard design problem isn't "make a box that closes". It's the emergency override. Make it too rigid and you can't take a call that matters. Make it too loose and the lock is theater. So the override has tunable friction: you choose how many presses (5 to 500) it takes to get out early. Incoming calls can also light the screen while the box stays locked.

Phone Box kSafe Generic Amazon box Brick Opal GoAro
Physically locks the phone away ✅ ✅ Partial ❌ ❌ ✅
Touchscreen interface ✅ Dial Dial App App App
Tunable emergency override ✅ None Holes ❌ Toggle App
Rechargeable & opaque ✅ Clear Varies n/a n/a ✅

To be fair, a commodity box is cheaper and Brick fits in a pocket. Neither has a touchscreen or a tunable override, and building one ourselves was half the point.


Features

📦 On the box

Fully offline. No account needed.

🔒 A real lock. A servo-driven latch holds the lid shut until the timer ends.

👆 Touchscreen UI. A 1.47″ 172×320 display. Swipe to set up to 9 hours, then watch a live countdown.

🆘 Tunable override. 5 to 500 presses in steps of 5 (default 25). Every early release is logged.

🔋 True battery %. From a hand-modified MAX17043 fuel gauge, not a guess from ADC voltage.

💾 Survives power cuts. Settings, schedules and streaks live in on-chip NVM.

⚡ Brownout recovery. A safemode.py handler and an NVM counter recover automatically if the servo browns out the board mid-lock.

📱 In the app

iOS · React Native + Expo

📡 Live status over BLE. Lock state, countdown and battery, streamed from the box.

📞 Call alert-through. An incoming call flashes the box's screen while the latch stays shut. Unlock-on-call is a separate setting, off by default.

📊 Focus stats. Streaks, totals, a calendar, and custom labels and topics per session.

🎯 Goals and reminders. Scheduled sessions with push reminders from Cloud Functions.

🔐 Sign in anywhere. Apple, Google or email, with cross-device sync through Firestore.

🌐 On the web

Firebase Hosting

🪧 Marketing site. The live site at phonebox-d14b7.web.app.

📈 Signed-in dashboard. Mirrors the app's stats, goals and planned sessions in the browser.

🔔 Web Push. Session reminders reach the browser too.

♿ Audited. Includes a UX and accessibility review (docs/quality-assurance/).


How it works

Step 1: set the timer. Step 2: drop the phone in. Step 3: it locks, for real. Step 4: unlock when done, or press override 25 times.

Under the hood: the firmware state machine
stateDiagram-v2
    [*] --> idle
    idle --> closed: lid closed (servo latches)
    closed --> idle: status-bar tap (open)
    idle --> picking: tap LOCK
    closed --> picking: tap LOCK
    idle --> confirming: tap LOCK with a topic pushed from the app
    closed --> confirming: tap LOCK with a topic pushed from the app
    confirming --> picking: CHANGE
    confirming --> running: CONFIRM
    picking --> running: select topic
    picking --> idle: cancel
    picking --> closed: cancel (lid was closed)
    running --> done: timer expires (COMPLETED)
    running --> done: N override presses (OVERRIDDEN)
    done --> idle: unlock animation / OPEN
Loading

The state names are the exact values the firmware sends over BLE. The app's BoxState type in app/src/ble/protocol.ts mirrors them one-for-one.


System architecture

Three runtimes that never import from each other: Python on a microcontroller, TypeScript and Swift on a phone, and Node in the cloud. One custom Bluetooth protocol and one Firestore schema hold them together.

flowchart LR
    subgraph Box["📦 Phone Box (ESP32-S3 · CircuitPython)"]
        direction TB
        UI["Touch UI<br/>AXS5106L driver"]
        SM["Lock state machine<br/>lock_controller"]
        SERVO["Servo latch<br/>GPIO5 PWM"]
        GAUGE["MAX17043<br/>fuel gauge (I²C)"]
        NVM[("NVM<br/>settings · streaks")]
        UI --> SM --> SERVO
        GAUGE --> SM
        SM <--> NVM
    end

    subgraph Phone["📱 iOS app (React Native · Expo)"]
        direction TB
        BLE["PhoneBoxClient<br/>react-native-ble-plx"]
        CALL["CallObserver<br/>native Swift module"]
        WAKE["BackgroundWake<br/>native Swift module"]
        STORE["zustand store<br/>+ Firestore sync"]
        CALL --> BLE
        WAKE --> BLE
        BLE <--> STORE
    end

    subgraph Cloud["☁️ Firebase"]
        direction TB
        AUTH["Auth<br/>Apple · Google · email"]
        FS[("Firestore<br/>owner-scoped rules")]
        FN["Cloud Functions<br/>reminders · push receipts"]
        HOST["Hosting<br/>site + dashboard"]
        FN --> FS
    end

    SM <==>|"Custom BLE GATT service<br/>(8 characteristics)"| BLE
    STORE <--> FS
    STORE --> AUTH
    HOST <--> FS
    FN -- "Expo / Web Push" --> Phone
Loading
Layer Stack Size
Firmware CircuitPython 10 on a Waveshare ESP32-S3-Touch-LCD-1.47. Servo lock state machine, custom AXS5106L touch driver, MAX17043 fuel gauge ~9k lines
App React Native 0.86 / Expo 57, TypeScript, react-native-ble-plx, zustand, two native Swift modules ~32k lines
Backend Firebase Auth + Firestore, Cloud Functions on Node 20 (scheduled reminders, Expo and Web Push) ~1k lines
Web Static HTML/CSS/ES modules on Firebase Hosting, with a signed-in dashboard ~7k lines
Enclosure SolidWorks, FDM printed 2 full revisions
The BLE GATT protocol: 1 service, 8 characteristics

One custom service (6b9a7e00-…-0001). The UUIDs are defined twice, in firmware/lib/lock_config.py and app/src/ble/protocol.ts, and a contract test (tests/contracts/bleUuids.test.js) fails if the two ever drift apart.

Characteristic Direction Purpose
status box → app · read / notify State, remaining time, battery, config
history box → app · read / notify RAM queue of sessions finished while no phone was connected
command app → box · write lock, unlock, start, dur, historyAck… (rate-limited to 1/s)
settings round-trip · read / write Override count, brightness, servo angles, sleep…
timeSync app → box · write Epoch seconds, sets the box clock for session timestamps
alert app → box · write Incoming-call label, which flashes the screen
labels app → box · write Custom session labels as compact JSON
pendingTopic app → box · write A topic suggestion for the next session

Hardware

Electrical wiring diagram: ESP32-S3 board with 1.47-inch touchscreen, MAX17043 fuel gauge on I2C, LiPo cell and charge path, servo on GPIO5 powered from the battery rail

Tip

The servo runs from the battery rail with bulk capacitance, not from 3.3 V. The MAX17043 shares the touchscreen's I²C bus at 0x36, so it costs no extra GPIO.

Bill of materials · ~$44 per prototype

Part Spec
MCU + display Waveshare ESP32-S3-Touch-LCD-1.47
Battery 3.7 V 1S LiPo
Lock actuator Hobby servo, PWM on GPIO5
Fuel gauge MAX17043 breakout, hand-modified
Buttons Momentary switches (lock + override)
Enclosure 3D-printed main case + lid

Full breakdown and cost-down levers in docs/procurement/bom.md.

Enclosure CAD

Two full revisions in SolidWorks, drawn for FDM printing. Printed fit-test coupons (hinge, port, screen bezel, power switch, locking tab) were checked against the real parts before each full assembly.

📐 hardware/cad/: .sldprt sources and .step exports for the main case and lid.


Engineering problems we solved

ProblemWhat was actually wrongFix
⚙️ The servo kept dying
Only on real hardware, only sometimes
Bench-cycling the lock until the pattern appeared showed two faults at once. The CPU clock was scaling out from under the servo's PWM timer, and a motor drawing close to an amp was hanging off a 3.3 V rail that couldn't supply it. The firmware re-asserts 50 Hz on every move. The servo now runs from the battery rail with bulk capacitance, and an NVM brownout counter drives automatic recovery.
🔋 No battery reading
The board has no fuel gauge
A raw ADC voltage is a poor proxy for a LiPo's state of charge, and the voltage divider read consistently off against a multimeter. We added a MAX17043 breakout, modified by hand (cut the traces tying its 3 V rail to the battery rail, then soldered and crimped it in), on the existing I²C bus.
👆 A touch chip with no driver
AXS5106L
No CircuitPython driver existed, and the controller intermittently drops frames mid-touch. A custom I²C driver with a debounce fix for the frames the controller drops.
🔗 One protocol, two languages
Python ↔ TypeScript
The box and the app share a wire contract but never share code, so a UUID or payload change on one side silently breaks the other. Both sides are documented against each other, a cross-project contract test guards the UUIDs, and a project skill teaches the AI agent the contract.

Repository layout

.
├── firmware/            CircuitPython firmware (copied to the CIRCUITPY drive)
│   ├── code.py          Entry point and main loop
│   ├── safemode.py      Brownout auto-recovery
│   └── lib/             lock_* modules, AXS5106L touch + MAX17043 drivers, vendored Adafruit libs
├── app/                 Expo / React Native iOS companion app
│   ├── src/             ble/, screens/, sync/, auth/, stats/, goals/, push/, ui/ …
│   ├── modules/         Native Swift modules: call-observer, background-wake
│   ├── firestore.rules  Security rules (deployed via firebase.json)
│   └── tests/           Firestore rules suites (run against the emulator)
├── functions/           Firebase Cloud Functions: scheduled reminders, push delivery
├── website/             Marketing site + signed-in web dashboard (Firebase Hosting root)
├── hardware/
│   ├── cad/             SolidWorks (.sldprt) and STEP enclosure parts
│   └── wiring-diagram.svg
├── tests/               Host-side firmware tests, website tests, cross-project contract tests
├── scripts/             Demo-account seeding, app-icon generation, doc conversion
├── docs/                RFCs, design reviews, BOM and procurement, handoffs (see docs/README.md)
├── .claude/             Claude Code project skills and subagents
├── fraim/               FRAIM agent-workflow config and project learnings
├── firebase.json        Hosting, Functions and Firestore config
└── AGENTS.md, CLAUDE.md, DESIGN.md, PRODUCT.md   Context files read by AI coding tools

Getting started

Important

BLE and the native modules do not run in Expo Go. The app needs a development build on a real iPhone (macOS, Xcode and an Apple Developer account). You also need your own Firebase project and the Firebase CLI. Host-side tests need Python 3.11+ and Node 20+.

📟 Firmware
  1. Flash CircuitPython 10 onto the Waveshare ESP32-S3-Touch-LCD-1.47.
  2. Copy the firmware onto the CIRCUITPY drive:
    cp -r firmware/code.py firmware/safemode.py firmware/lib /Volumes/CIRCUITPY/
  3. The board auto-reloads. Pins, colours and every tunable live in firmware/lib/lock_config.py.

More in firmware/README.md.

📱 iOS app
cd app
npm ci
cp .env.example .env         # fill in your own Firebase web config
npx expo prebuild --clean    # generates ios/ and links the native modules
npx expo run:ios --device    # build onto a real iPhone

More in app/README.md.

☁️ Backend and website
cd functions && npm ci && npm run build
firebase deploy --only functions,firestore,hosting --project <your-project-id>

website/ is served as-is. Point website/js/firebaseConfig.js at your own project.


Testing

Suite Command Size
📟 Firmware (host-side) python3 tests/run_firmware_tests.py 17 files · 672 checks
🌐 Website + BLE contract npm test 261 tests
📱 iOS app cd app && npm run test:app 112 suites · 1,548 tests
🔐 Firestore

Read the rest on GitHub

Scan report · 2026-10-03
  • ✓ Prohibited terms or links
  • ✓ Repository eligibility
  • ✓ slopscore.md paperwork
  • ✓ Content policy
  • ✓ Risk review

From the balcony · 0 of 3 clapped

    Princess, Crusoe and Schnitzel read it and passed. Their reasons are on the balcony, with every other verdict.

    Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.

    0 comments

    log in to comment.

    report this listing — log in to report