{"id":50967463,"url":"https://github.com/ergut/mac-lookout","last_synced_at":"2026-06-18T22:01:18.223Z","repository":{"id":362372966,"uuid":"1254454369","full_name":"ergut/mac-lookout","owner":"ergut","description":"macOS motion-activated security camera using the built-in webcam, with iCloud snapshot sync and a Telegram bot for real-time alerts and remote control (photo, pause, say, alarm).","archived":false,"fork":false,"pushed_at":"2026-06-03T22:24:24.000Z","size":137,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T00:06:18.773Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ergut.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T15:31:27.000Z","updated_at":"2026-06-03T22:22:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ergut/mac-lookout","commit_stats":null,"previous_names":["ergut/mac-lookout"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ergut/mac-lookout","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ergut%2Fmac-lookout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ergut%2Fmac-lookout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ergut%2Fmac-lookout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ergut%2Fmac-lookout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ergut","download_url":"https://codeload.github.com/ergut/mac-lookout/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ergut%2Fmac-lookout/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34508867,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-18T22:01:16.236Z","updated_at":"2026-06-18T22:01:18.178Z","avatar_url":"https://github.com/ergut.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/readme-banner.png\" alt=\"mac-lookout\" width=\"720\"\u003e\n\u003c/p\u003e\n\n# mac-lookout — macOS Motion Security Monitor\n\nTurn a Mac and its **built-in FaceTime camera** into a motion-activated security\ncamera — no extra hardware, no Raspberry Pi, no IP camera. When motion is\ndetected it saves timestamped snapshots locally, mirrors them to **iCloud\nDrive**, and pushes them to your phone in real time over a **Telegram bot** that\nyou can also command remotely (`/photo`, `/status`, `/pause`, `/resume`).\n\nIt keeps working while the screen is locked, takes a periodic \"heartbeat\"\nsnapshot so you know it's alive, and waits a configurable delay after you press\nstart so you can leave the room without tripping it.\n\n\u003e Built for the travel / hotel-room case — *\"is anyone going into my room while\n\u003e I'm out?\"* — but it's just a webcam motion monitor, so the home-office,\n\u003e front-door, workshop, pet-cam, and \"did the courier come?\" cases all work too.\n\n---\n\n## The story\n\nOn a trip to Prague, I had the usual traveler's worry: a hotel room with my\nlaptop, passport, and other valuables in it, and no good way to know whether\nanyone — housekeeping, or anyone else with a key — had been in while I was out.\n\nThen it clicked: the most valuable thing in the room was *also* a camera, a\ncomputer, and an internet connection. Why not make the laptop watch the room\nitself?\n\nThat single idea set the requirements, and each one shaped the tool:\n\n- **Use only the laptop — no extra gear.** I was traveling, not carrying a\n  Raspberry Pi or an IP camera. → built around the **built-in FaceTime camera**.\n- **Get evidence off the device immediately.** If the laptop itself walked out\n  the door, local snapshots would walk with it. → every snapshot is **mirrored\n  to iCloud Drive**, so I can see it from my phone even if the Mac is gone.\n- **Alert me in real time, wherever I am.** A folder I check later isn't an\n  alarm. → a **Telegram bot** pushes each snapshot instantly, and lets me pull a\n  live photo, sound an alarm, or pause it on command.\n- **Keep watching with the screen locked.** I'd lock the screen and leave. → it\n  runs under **`caffeinate`** so the system stays awake and keeps capturing while\n  the display sleeps.\n- **Don't trip on me as I leave.** → an **arming delay** to walk out first.\n- **Catch a face, not a blur.** The first seconds of an event are when identity\n  is visible. → **dense capture up front** (plus optional face detection), then\n  it slows down for long activity like housekeeping.\n- **Tell me it's still alive.** A camera that silently died is worse than none.\n  → a **heartbeat** snapshot every 30 minutes.\n\nThe original plan was the open-source `motion` daemon — but on macOS that's a\ndead end (Homebrew's `motion` is an unrelated to-do app, and the real one can't\nread the built-in camera). So this is that idea, rebuilt natively for a Mac.\nIt's framed around the hotel-room case, but it's just a webcam motion monitor —\nhome-office, front door, workshop, pet-cam, \"did the courier come?\" all work too.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/sample-face-detected.png\" alt=\"Face detected alert in Telegram\" width=\"360\"\u003e\n  \u003cbr\u003e\n  \u003cem\u003eThe real thing in Prague: walking back into the room trips motion, and the\n  Telegram bot pushes a \"Face detected\" snapshot to my phone in seconds.\u003c/em\u003e\n\u003c/p\u003e\n\n---\n\n## Features\n\n- 🎥 **Built-in camera, no extra gear** — uses the Mac's FaceTime camera via OpenCV.\n- 🏃 **Motion detection** — frame differencing against a running-average\n  background, tunable like the classic `motion.conf` knobs (threshold, noise\n  floor, minimum motion frames), with an optional region-of-interest mask.\n- 🔥 **Adaptive event capture** — on motion it saves a *sequence*: **dense at the\n  start** (catch the face/identity in the first seconds), then automatically\n  **slows down** during sustained activity (e.g. housekeeping) so you don't get\n  hundreds of near-identical frames. Capturing continues ~10 s after motion stops.\n- 👤 **Optional face detection** — when a face is found in a frame it's captioned\n  \"Face detected\" and pushed even if the throttle would skip it (Haar cascade\n  bundled with OpenCV — no extra dependencies; set `SM_FACE_DETECT=0` to disable).\n- ☁️ **iCloud Drive mirroring** — every snapshot is copied to\n  `iCloud Drive/mac-lookout/` so there's an off-device copy.\n- 📲 **Telegram alerts + two-way control** — front-loaded photo push on motion\n  (first frames sent immediately so you actually see who arrived), plus remote\n  commands restricted to **your chat only**:\n  - `/photo` — grab a picture right now (even with no motion)\n  - `/status` — armed/paused state, event count, uptime\n  - `/pause [min]` — pause detection for N minutes (default 10), **auto-rearms**\n  - `/resume` — resume immediately\n  - `/say \u003ctext\u003e` — speak text aloud in the room (deterrence: *\"I can see you\"*)\n  - `/alarm` — sound an alarm in the room\n  - 🎙️ **send a voice message** — it plays aloud in the room (intercom)\n  - `/help` — list commands\n- ⏲️ **Arming delay** — `./start.sh 5` waits 5 minutes before detecting so you\n  can walk out; you get an \"ARMED\" ping when it goes live.\n- 💓 **Heartbeat** — a proof-of-life snapshot every 30 minutes; if heartbeats\n  stop, you know the monitor died.\n- 🔒 **Survives screen lock** — runs under `caffeinate` so the system stays\n  awake and keeps capturing while the display sleeps and the screen is locked.\n\n---\n\n## How it works\n\nA single Python process (`motion_detect.py`) owns the camera and runs the\ndetection loop. Because macOS allows only **one** process to use the camera at a\ntime, everything that needs a frame — motion snapshots, the periodic heartbeat,\nand on-demand `/photo` — is served from that one loop. A daemon thread\nlong-polls Telegram for your commands and answers using the latest frame.\n`start.sh` wraps the whole thing in `caffeinate` and backgrounds it; `stop.sh`\nshuts it down and releases the camera cleanly.\n\n```\nstart.sh ── caffeinate ── python motion_detect.py\n                              ├── detection loop (owns camera)\n                              │     ├── motion → snapshots/ + iCloud + Telegram push\n                              │     └── heartbeat → heartbeat/ + iCloud\n                              └── Telegram listener thread (/photo /status /pause …)\n```\n\n---\n\n## Requirements\n\n- macOS (tested on Apple Silicon, macOS 26).\n- [`uv`](https://github.com/astral-sh/uv) recommended for the Python environment —\n  `start.sh` auto-creates the venv with it. Any `python3` with OpenCV also works.\n- [Homebrew](https://brew.sh) with `ffmpeg` (optional — powers the voice intercom\n  and the standalone `heartbeat.sh` test grab).\n- iCloud Drive enabled (optional) to mirror snapshots off-device.\n- A Telegram account (optional, only if you want phone alerts/commands).\n- Camera permission for your terminal (macOS will prompt on first run).\n\n---\n\n## Setup\n\n```bash\ngit clone https://github.com/ergut/mac-lookout\ncd mac-lookout\n./start.sh\n```\n\nOn first run, if `.venv` is missing and [`uv`](https://github.com/astral-sh/uv) is\ninstalled, `start.sh` offers to create the virtualenv and install the Python\ndependencies (OpenCV + numpy) automatically. It also runs preflight checks and\nwarns about anything missing (OpenCV, ffmpeg, Telegram credentials, iCloud Drive).\n\nFor the optional voice intercom (and the standalone `heartbeat.sh` test grab),\ninstall ffmpeg:\n\n```bash\nbrew install ffmpeg\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eManual Python setup (no \u003ccode\u003euv\u003c/code\u003e, or you prefer to do it yourself)\u003c/summary\u003e\n\n```bash\nuv venv .venv\nsource .venv/bin/activate\nuv pip install opencv-python-headless numpy\n```\n\n`start.sh` also accepts an already-activated venv or any `python3`/`python` on\nyour PATH — it just needs OpenCV importable there.\n\u003c/details\u003e\n\n### Telegram (optional but recommended)\n\n1. In Telegram, message **@BotFather** → `/newbot` → copy the **bot token**.\n2. Send your new bot any message (so it may reply to you).\n3. Message **@userinfobot** to get your numeric **chat ID**.\n4. Create `secrets.env` from the template and fill both in:\n\n   ```bash\n   cp secrets.env.example secrets.env\n   chmod 600 secrets.env\n   # edit: SM_TELEGRAM_BOT_TOKEN=...  and  SM_TELEGRAM_CHAT_ID=...\n   ```\n\n`secrets.env` is gitignored and never committed. The token is passed to `curl`\nvia stdin, so it does not appear in process listings.\n\nWithout Telegram configured, mac-lookout runs **local-only**: motion and\nheartbeat snapshots are still saved to `snapshots/` and `heartbeat/` (and\nmirrored to iCloud Drive), but there are no phone alerts, no remote commands,\nand no voice intercom. `start.sh` prints a warning when it starts in this mode.\n\n---\n\n## Usage\n\n```bash\n./start.sh        # default 5-minute arming delay, then watches the room\n./start.sh 0      # arm immediately (handy for testing)\n./start.sh 2      # 2-minute delay\n./stop.sh         # stop and release the camera\n```\n\nFor a real deployment: run `./start.sh`, **lock the screen** (Ctrl+Cmd+Q), and\nleave — **keep the lid open and the power connected** so the camera keeps\ncapturing while the display sleeps.\n\nWatch what's happening:\n\n```bash\ntail -f motion.log\n```\n\n### Where files go\n\n| Kind | Local | iCloud |\n|------|-------|--------|\n| Motion snapshots | `snapshots/` | `iCloud Drive/mac-lookout/snapshots/` |\n| Heartbeats | `heartbeat/` | `iCloud Drive/mac-lookout/heartbeat/` |\n\nThe iCloud copy goes to a fixed path\n(`~/Library/Mobile Documents/com~apple~CloudDocs/mac-lookout/`), **independent of\nwhere you clone the repo** — so getting evidence off-device just requires being\nsigned into iCloud with iCloud Drive enabled. If it's not, snapshots are still\nsaved locally under the project folder; the iCloud copy is skipped and `start.sh`\nwarns you at launch.\n\n---\n\n## Configuration\n\nOverride any of these as environment variables (e.g. `SM_THRESHOLD=3000 ./start.sh`):\n\n| Variable | Default | Meaning |\n|----------|---------|---------|\n| `SM_THRESHOLD` | `1500` | Min changed-pixel area to count as motion (↑ = less sensitive) |\n| `SM_NOISE_LEVEL` | `32` | Per-pixel diff intensity floor |\n| `SM_MIN_FRAMES` | `2` | Consecutive frames required to confirm motion |\n| `SM_EVENT_FAST_INTERVAL` | `0.6` | Seconds between frames during the dense start-of-event phase |\n| `SM_EVENT_FAST_WINDOW` | `10` | How long (s) the dense phase lasts from the start of an event |\n| `SM_EVENT_SLOW_INTERVAL` | `3` | Seconds between frames during sustained activity |\n| `SM_EVENT_TAIL` | `10` | Seconds to keep capturing after motion stops |\n| `SM_FACE_DETECT` | `1` | Face detection on (`1`) / off (`0`) |\n| `SM_ARM_DELAY` | `0` | Arming delay in seconds (`start.sh` sets this from its minutes argument) |\n| `SM_HEARTBEAT_SECONDS` | `1800` | Heartbeat interval; `0` disables |\n| `SM_TELEGRAM_BURST` | `4` | Frames pushed to Telegram unthrottled at the start of an event |\n| `SM_TELEGRAM_MIN_INTERVAL` | `30` | Min seconds between pushes after the initial burst |\n| `SM_CAMERA_INDEX` | `0` | Camera index (`0` = built-in) |\n| `SM_WIDTH` / `SM_HEIGHT` / `SM_FRAMERATE` | `1280` / `720` / `15` | Capture settings |\n| `SM_MASK_FILE` | `mask.png` | Optional ROI mask: white = watch, black = ignore |\n\n### Tuning\n\n- **Too many false triggers** (light changes, AC, shadows): raise `SM_THRESHOLD`\n  (try 2500–4000).\n- **Missing real motion**: lower `SM_THRESHOLD` (try 800–1200).\n- **Only watch part of the frame** (e.g. just the door): create a `mask.png` the\n  same size as the frame — white where you want detection, black elsewhere.\n\n---\n\n## Maintenance\n\nSnapshots and heartbeats accumulate indefinitely — by design, so evidence is\nnever auto-deleted. Clean them up yourself after confirming iCloud has what you\nneed:\n\n```bash\nrm -f snapshots/*.jpg heartbeat/*.jpg\n```\n\n---\n\n## Prior art\n\nThe \"camera + Telegram alerts\" space is well-trodden; if you want a different\nplatform, these are worth a look:\n\n- [scaidermern/piCamBot](https://github.com/scaidermern/piCamBot) — Raspberry Pi\n  + Telegram, rich `/arm` `/disarm` `/capture` `/status` command set.\n- [pchinea/telegram-surveillance-bot](https://github.com/pchinea/telegram-surveillance-bot)\n  — cross-platform OpenCV webcam bot driven entirely from Telegram.\n- [nicofirst1/MotionBot](https://github.com/nicofirst1/MotionBot) — OpenCV +\n  Telegram with face recognition.\n\nWhat mac-lookout does differently: it targets the **macOS built-in camera**\nspecifically, mirrors to **iCloud Drive**, survives **screen lock** via\n`caffeinate`, adds a **timed `/pause` that auto-rearms** (vs. plain on/off), and\nemits a **heartbeat** proof-of-life — a combination we didn't find in an\nexisting project. If you know of one that overlaps more, an issue/PR is welcome.\n\n---\n\n## ⚠️ Responsible use \u0026 disclaimer\n\nThis is a tool for monitoring **your own space, on your own device, with the\nconsent of anyone who may be recorded.** Like any camera software, it can be\nmisused — and that is on the user, not the authors.\n\n- **Know the law.** Recording people — especially audio, or in places where\n  there is a reasonable expectation of privacy (bathrooms, bedrooms, someone\n  else's home, hotel common areas) — is regulated and varies by country, state,\n  and context. It is your responsibility to comply with all applicable privacy,\n  surveillance, wiretapping, and consent laws where you are.\n- **Do not** use this to surveil people without their knowledge and consent, to\n  stalk or harass, in any location or manner that is illegal, or in any way that\n  violates someone's privacy or rights.\n- **No warranty.** This software is provided \"as is\", without warranty of any\n  kind. It may miss events, false-trigger, fail to upload, or stop without\n  notice. **Do not rely on it as your sole security measure.**\n- **No liability.** The authors and contributors accept no responsibility or\n  liability for any misuse, damage, loss, legal consequence, or harm arising\n  from the use of this software. By using it, you accept full responsibility for\n  how you deploy it and for complying with the law.\n\nEvery technology can be used well or badly. Please use this one well.\n\n---\n\n## License\n\n[MIT](LICENSE) — do whatever you want, no warranty. See the disclaimer above.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fergut%2Fmac-lookout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fergut%2Fmac-lookout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fergut%2Fmac-lookout/lists"}