CatGuard: on-device cat recognition on a spare phone
An Android app that turns an old phone into an always-on camera which recognises my own cat on the device and sends a Telegram photo alert when an unknown cat comes in, with a web dashboard for the event history. It's tuned to run all day on a low-end Android 8 phone.
The problem
A neighbour's cat kept coming in through our cat flap. I wanted to know when it happened without paying for a smart camera, and without training a model for one specific cat. An old Android phone left on charge by the door turned out to be enough.
How it recognises my cat
Instead of training a classifier, CatGuard uses few-shot recognition. During enrolment you take 12 to 25 photos of your cat, and a pretrained MobileNet-family network turns each one into an embedding, a vector describing what the cat looks like. Every later sighting is embedded the same way and compared with those vectors by cosine similarity: close enough to an enrolled cat means "known", otherwise "intruder". If it gets one wrong, tapping "That's my cat" adds that sighting to the cat's set, so it improves with use.
A pipeline built for a cheap phone
The target was a 2 GB Android 8 phone, so the expensive models only run when there's a reason to:
Detection pipeline: cheapest check first
- 01CaptureCameraX, 640 × 480 frames, always keeping only the latest
- 02Light and motion gateDrop dark frames; compare a 32 × 32 brightness grid every 350 ms
- 03BurstOn motion, collect 5 frames (or 1.5 s)
- 04DetectCOCO detector keeps cat boxes with confidence ≥ 0.45; best crop by size × sharpness
- 05Embed and matchL2-normalised embedding vs the enrolled cats; similarity below 0.72 means intruder
- 06Alert and syncRoom log, 2-minute cooldown, Telegram photo, Firestore event for the dashboard
- Motion gate. A 32 × 32 brightness grid is compared between frames every 350 ms; frames that are too dark are dropped.
- Burst. When motion trips, it grabs a burst of 5 frames.
- Detect, then recognise. A COCO object detector runs only on that burst; "cat" boxes with confidence of at least 0.45 are scored by size and sharpness, and the best crop goes to the embedder.
- Debounce. A cooldown (2 minutes by default) stops one visit from producing a flood of alerts.
Built to run unattended
- A foreground camera service with a wake lock that restarts after reboots, and a watchdog that rebinds the camera if frames stop for 30 seconds.
- Thermal throttling: the motion check slows to half and quarter speed as the phone warms up, and pauses when it's critical.
- A heartbeat every 60 seconds, so the dashboard can show whether the camera is live.
- Battery-optimisation guidance for Xiaomi, Huawei, Samsung and Oppo, whose power managers otherwise kill background apps.
Source: core/Constants.kt, ml/, camera/ and app/src/test in the repository
Alerts and dashboard
Intruder events are saved locally, uploaded to Firestore with a compressed thumbnail, and sent as a Telegram photo message (respecting quiet hours). A web dashboard on Firebase Hosting signs in with the same Google account and shows live status, a gallery, a timeline with the busiest time of day, CSV export and a PDF report. Firestore rules restrict every household's data to its own signed-in user, and the app and dashboard are available in English and Russian.
Looking back
The thresholds are tuned by hand rather than measured on a labelled set, so a proper evaluation of false alarms is the next step, along with push notifications to the owner's phone.