blinkwell — eye-fatigue monitor CLI
The Eye Fatigue Detector web app's analysis pipeline as a local process — no browser, no hidden-tab problem. Webcam → MediaPipe Face Landmarker → blink/eye metrics → verdicts, a composite fatigue score, and 20-20-20-style reminders. Windows-first.
This is a faithful port of src/lib/analysis/* + src/lib/vision/features.ts
— same constants, same state machine, same quirks — not a redesign.
Install
Requires Python ≥ 3.11 (mediapipe's safest Windows target is 3.11):
cd cli
py -3.11 -m venv .venv
.venv\Scripts\activate
pip install -e .
:: or, for the exact verified pin set:
pip install -r requirements.txt
blinkwell is then on PATH inside the venv; python -m blinkwell works too.
The face model is the repo's own public/models/face_landmarker.task —
nothing is downloaded.
Commands
blinkwell check --seconds 90 --camera 0 :: live quick check → result card
blinkwell monitor --interval-min 15 --window-s 60 --break-every-min 20
blinkwell replay <video> :: run a video file through the pipeline
blinkwell trace <trace.json> --labels <labels.json> [--tol 300]
check— camera on for--seconds, then the result card (verdict, fatigue score, per-metric statuses, tips, disclaimer). Saved to history + baseline, same as a saved web-app check — except low-confidence checks, which "abstain" (printed but not saved) exactly like the app.monitor— the core feature. Every--interval-minminutes the camera turns on for a--window-scheck window and is fully released after it:cv2.VideoCaptureopens at window start,release()at window end, so the webcam LED is off between windows. Reminder rules (break/tired/blink) print to the console and fire an OS toast via plyer (falls back to a[REMINDER]console line). Ctrl+C stops cleanly.replay— processes a video file at media-time timestamps; screen distance isn/alike the app's file source.trace— parity tool: replays a recorded app trace ({"schema", "frames": [...FrameFeatures]}, also thetrace.framesshape ofeval-data/diag-*.json) throughQualityCtx+BlinkDetectorand prints events + the result; with--labelsit prints TP/FP/FN + P/R/F1 with the same matching semantics asscripts/eval-trace.ts(default tol 300 ms).--jsononcheck/monitor/replay/traceprints machine-readable lines (one JSON object per result/window/reminder/trace).
Data lives at ~/.blinkwell/: baseline.json (per-metric reservoirs + median) and
history.jsonl (one saved result per line). Set EYEFD_HOME to relocate.
Your webcam light
The camera LED is on during checks and monitor windows — that's the honest signal: if the light is on, frames are being read; if it's off, nothing is looking at you. Between monitor windows the capture device is released, so the light visibly turns off.
Privacy
Same rule as the app: no frames or images ever leave the device or get
persisted. Only summary metrics (QuickCheckResult JSON) are written, to
~/.blinkwell/ on your own disk. The face-landmarker model loads from a local
file; the code makes no network calls. (mediapipe is pinned at 0.10.21 —
later builds embed a native telemetry uploader that attempts upload;
0.10.21 has no such uploader.)
Output meaning
Wellness guidance, not a medical diagnosis. Verdicts use the app's two-axis rule: fatigue signals (PERCLOS, blink duration, lid-reopen slowness, partial blinks) outrank task-context signals (blink rate, gaps, distance, light).
Parity with the app
The CLI's detector output was verified event-for-event against
npm run eval:trace on tests/traces/synthetic.trace.json (4/4 events,
F1 1.000) and two real eval-data/diag-* traces (F1 identical to the TS
pipeline: 0.710 and 0.370). When config.ts is retuned, edit
cli/blinkwell/config.py to match — the module header says so too.
Dev
.venv\Scripts\python -m pytest tests :: unit tests (port of tests/*.test.ts)
Layout mirrors the TS modules: config.py features.py quality.py blink.py session.py thresholds.py baseline.py fatigueScore.py monitorRules.py exercises.py eval.py are 1:1 ports; capture.py (cv2 + mediapipe),
storage.py (~/.blinkwell), scoreSession.py, notify.py, cli.py are
process-side plumbing.
Metadata
Release files for blinkwell 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| blinkwell-0.1.0.tar.gz | 41.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| blinkwell-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 82.3 kB
Release files / blinkwell-0.1.0.tar.gz
| Download URL | blinkwell-0.1.0.tar.gz |
|---|---|
| Size | 41.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4c72dafec90fda000797e146b3ffc086f41ee9ddd10d540be41422d75f7edf93
|
|
BLAKE2b-256 checksum How to use checksums |
d3914332b64fa788994c1ffd10a2e59d26c37e4271e6db9097dd541ed48d7a3b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / blinkwell-0.1.0-py3-none-any.whl
| Download URL | blinkwell-0.1.0-py3-none-any.whl |
|---|---|
| Size | 40.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a0404451fdf8f1da288ae8ed5e65fdbf46450526633ad09b380fc90c67fdbe01
|
|
BLAKE2b-256 checksum How to use checksums |
e779255deb862196f92a9b09653b62f7401289d597eca17f80c24034668a7aa7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for blinkwell 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.