The Problem

Local‑area‑network monitoring that repurposes unused Android devices often stalls because the capture pipeline, AI inference, and dashboard are built as separate, poorly‑documented pieces. Teams end up stitching together video streams, manually parsing frames, and lack a reproducible way to lock dependency versions, which breaks CI and makes onboarding new engineers painful.

What This Does

Sentinel is a LAN‑based, smartphone‑powered AI monitoring framework that moves image acquisition to Android phones and does PC‑side processing. The repo is split into three logical layers:

  • Capture layer – PhoneCamSender/ (Android Kotlin) runs MainActivity.kt, discovers the PC on the LAN via NetworkDiscover.kt, and streams JPEG frames over HTTP.
  • Processing layer – The PC side (server.py, the entry point) imports app.core.frame_buffer, app.core.logger, app.net.net_discovery and the AI stack (app/ai/vision_client.py, app/ai/ai_monitor_worker.py, app/ai/ai_store.py, app/ai/motion_trigger.py). Frames are buffered, motion‑triggered events are generated, and results are stored locally or shipped to a downstream analytics pipeline.
  • Control layer – A Flask‑based web dashboard (app/web/webapp.py, templates in app/web/templates/, static JS/CSS) lets an operator start/stop recording, view live preview, and inspect structured event output (JSON lines written by app/ai/ai_monitor_worker.py).

Files such as requirements.txt, app/config/config_store.py and app/core/runtime.py centralise version metadata and runtime configuration (e.g., API keys, recording codecs). The Android APKs are built with Gradle (PhoneCamSender/build.gradle.kts, PhoneCamSender/gradle/) and the source is versioned in version.properties.

How It Is Wired

Execution flow (entry → work → side‑effects):

  1. server.py starts a Flask app (flask.Flask) bound to 0.0.0.0 on a port read from config. On startup it calls app.net.net_discovery.discover() to announce itself on the LAN and loads app/config/config_store.py for any API keys.
  2. The dashboard (app/web/webapp.py) opens a WebSocket connection to /stream; the Flask route handler forwards incoming JPEG frames from connected Android clients to app.core.frame_buffer.update(frame).
  3. app.core.frame_buffer (a simple circular buffer) pushes each new frame to app/ai/vision_client.py, which sends the image to a user‑supplied model (e.g., an Ark‑AI endpoint) and returns a JSON event.
  4. app/ai/ai_monitor_worker.py consumes the event, fires motion_trigger.py if confidence exceeds a threshold, and writes a structured record to app/ai/ai_store.py (SQLite or CSV depending on config).
  5. The recorder worker (app/recorder/recorder_worker.py) periodically flushes the buffer to an MP4 file using the codec selected in app/config/config_store.py; fallback to libx264 is provided for Windows compatibility.
  6. All runtime logs (including redacted API keys) go to app/core/logger.py.

Files that cross process boundaries:

  • server.py – orchestrates the PC side, imports most core modules.
  • app/core/frame_buffer.py – shared mutable state between network receiver and AI pipeline.
  • app/ai/vision_client.py – the only code that leaves the LAN to call an external model API.
  • app/recorder/recorder.py – writes video to disk; called by the UI via recorder_worker.py.

A hub exists at server.py; any change to the discovery protocol or frame format ripples to the Android client (NetworkDiscover.kt), the buffer, and the UI. No cycles were detected, but the centrality of server.py means a rewrite of the networking layer requires coordinated updates on both sides.

How To Use It

StepCommand / ActionReference
Install Python depspip install -r requirements.txtrequirements.txt lists Flask, OpenCV, etc.
Start PC serverpython server.pyserver.py is the entry point; it reads app/config/config_store.py for host/port.
ConfigureEdit app/config/config_store.py (or copy config/ defaults) to set LAN IP, model endpoint, recording path.config_store.py holds MODEL_URL, RECORDING_CODEC, MAX_UPLOAD_SIZE.
Build Android clientFrom repo root: cd PhoneCamSender && ./gradlew assembleDebugbuild.gradle.kts and gradle/ present.
Run Android appInstall the generated CamFlow‑v1.1.1.apk on a phone (Android 8.0+). Open the app; it will auto‑discover the PC IP via NetworkDiscover.kt and start streaming.NetworkDiscover.kt uses mDNS/LAN broadcast.
Open dashboardPoint a browser to http://<PC‑IP>:<PORT>; the dashboard loads app/web/templates/dashboard.html and connects to the WebSocket stream.dashboard.html references static/dashboard.js.

Missing: A Dockerfile or Makefile for containerised deployment; the README only describes manual Python/gradle runs.

Real‑World Use

A factory floor wants to monitor assembly quality without installing expensive CCTV. Engineers spin up a single‑board PC (Raspberry Pi or x86) running server.py, attach four old Android phones running the CamFlow APK, and point the dashboard at the PC. Motion events from ai_monitor_worker.py trigger an alarm in the factory’s MES system via a webhook written to app/ai/ai_store.py. The operator can review recorded MP4 clips and export the JSON event log for downstream quality‑analysis scripts.

Code Health & Issues

Low – dependencies declared without a lockfile – requirements.txt: Packages are listed without a requirements.lock or pip-compiled file, so pip install -r requirements.txt may pull different versions across machines, breaking reproducibility of the AI model pipeline. Adding a requirements.lock (generated via pip freeze > requirements.lock) or migrating to a pyproject.toml with uv/poetry would mitigate this.

No other measured findings were flagged by the static analysis; the repo contains 9 test files under tests/ and 7 doc files, but CI coverage is not enforced (no coverage report in the .github/workflows/ci.yml excerpt).

The Bottom Line

Sentinel provides a functional, LAN‑based vision pipeline that turns spare Android phones into a distributed camera network with AI‑driven event output. The codebase is reasonably modular (capture → buffer → AI → dashboard) and the README gives clear, if manual, deployment steps. The main risk is the un‑locked Python dependency list and the single‑point‑of‑failure nature of server.py; teams that need reproducible builds or high‑availability clustering will want to add a lockfile and consider containerising the Flask process. It’s a solid prototype platform for local visual data acquisition and quick AI prototyping, but production roll‑out should address dependency management and redundancy.