![]()
Sentree is the window that opens on my Mac when someone is outside. A camera detects a person, and a few seconds later I see the live stream and the clips of the last 24 hours, with the newest one playing once from the start. I glance at it, press Esc, and go back to work.
The name is "sentry" in the -ee family of my other apps. I built it with Claude Code on 16th October 2026, in 1 day, and it runs inside Hammerspoon, next to Tilee: my own tiling window manager in Hammerspoon, my window manager.
Why I replaced the web app
Since July 2026, a detection opened a Safari web app of the camera maker's cloud viewer. It worked, but it was slow and it got in the way:
- 10 to 20 s per alert. The web app started cold against the cloud every time.
- The window moved. My window manager tiled the web app like any other window and shuffled my screens.
- The cloud in the middle. Every alert went out to the internet and back, and needed a browser.
Sentree replaced it the same day I built it.
What it does
| Moment | What happens |
|---|---|
| A person is detected | The window opens on my portrait screen and takes the focus |
| On show | The live stream starts, and the newest clip plays once from the start |
| The person stays | Within 5 min, a new detection refreshes the list without taking the focus again |
| The clip isn't ready | Sentree checks every 6 s for up to 90 s, then jumps to the new clip |
| Nobody closes it | It hides itself after 5 min |
| On hide | The stream stops. A hidden window costs nothing |
The camera system lists a clip only once the event is over. So the window shows the live stream at once and adds the clip of the current visit as soon as it exists.
Keys
| Action | Key |
|---|---|
| Show or hide by hand | ⌃⌥⌘C, or the Sentree key on my Stream Deck |
| Close | Esc, the × button, or the same hotkey |
| Another clip | ← and →, or click a row |
| Replay the clip | Space |
| Sound on or off | M (off by default) |
| Back to another app | ⌘Tab: the window doesn't float above everything |
Speed
| Metric | Old web app | Sentree, first build (HLS) | Sentree now (WebRTC) |
|---|---|---|---|
| Alert to window | 10 to 20 s | About 1 s | About 1 s |
| Window to the first live frame | - | 10 to 25 s | About 3 s |
| Delay behind live | - | About 15 s | Under 1 s |
The window is fast because it never closes. Hammerspoon builds it once, hidden, with the page loaded, and a show only fills in the data and brings it forward.
How it's built
4 small pieces, about 1,250 lines in total, plus the tests:
| Piece | Language | Job |
|---|---|---|
listener.py |
Python | A launchd agent that waits for the "person detected" event |
sentree.py |
Python | Fetches the clip list and the current detection, writes a JSON cache |
sentree.lua |
Lua | The Hammerspoon window, the hotkey, the 5 min timer, the page bridge |
index.html |
HTML/JS | The page: live stream, clip player, 24-hour list, in my NicAI design |
Home Assistant sends the detection event. Everything else runs on my Mac. The Python side uses the standard library only.
Decisions and gotchas
- A Hammerspoon webview, not a browser. My window manager leaves Hammerspoon windows alone, the page stays preloaded, and nothing needs a login.
- WKWebView blocks autoplay. A
video.play()that the page calls by itself is refused. One that Hammerspoon injects withevaluateJavaScript, or one that comes from a click, is allowed. So playback starts from the injectedonShow()call. - HLS was too slow, WebRTC fixed it. The first build played an HLS stream: 10 to 25 s to the first frame, about 15 s behind live, and a stream token that expired while the window was paused, so the page then held a dead link. hls.js didn't attach in this webview. WebRTC fixed all 3 problems the same evening.
- The stream runs only while the window shows. An always-on version, for instant shows, ran for 1 evening and was dropped. Show starts the connection, hide closes it, and a watchdog reconnects when no video arrives for 10 s.
- No secret in the page. The page never talks to the home server. The WebRTC signalling goes through the Hammerspoon bridge to a small Python relay, so the access token stays in my environment file.
- Compare clips by event, not by link. Every refresh signs new clip links. Comparing links restarted the clip on every refresh, which looked like a loop. Comparing event IDs fixed it.
- No thumbnails. The thumbnails need an auth header that a plain
imgtag can't send. The list shows the time, the label and the length instead. - A normal window, not a floating one. A floating window would sit above everything. Sentree takes the focus, and any app can come back over it.
- Wait after a Hammerspoon reload. A reload while scripts start and exit can stop
init.luahalf-way. After a reload, I wait 10 s and check that all the hotkeys came back.
Agent interface
My NicAI agents use Sentree through a CLI. Every action takes --json, and the window actions report confirmed: true only once Hammerspoon says the window state matches:
sentree status --json # listener running, last detection, window visible, clip count
sentree show --json # show the window, confirmed
sentree toggle --json # the Stream Deck key's action
No output holds the access token or a signed clip link. Exit code 4 means Hammerspoon isn't running. The tests use a fake Hammerspoon, never the real window.
How I built it
| Date | Step |
|---|---|
| 24th July | The old setup: a listener plus the Safari web app of the cloud viewer |
| 16th October | Rebuilt as a Hammerspoon webview, named Sentree, given its own folder; WebRTC that evening |
| 18th October | The Stream Deck key, my own clip player bar, Play and Stop on each row |
| 24th October | The agent interface |
The first version on 16th October was a shared script called "camera feed". By the afternoon it had its own name and folder. By the evening the stream ran on WebRTC, the clip played once per show, and the old web app was in the Bin.
Further reading
The automation layer it runs in:

Where the detection event comes from:

The tool I built it with:

- Hammerspoon - macOS automation in Lua: my Hammerspoon setup.
- Tilee: my own tiling window manager in Hammerspoon: the window manager that no longer shuffles the camera window.
- Home Assistant: my Home Assistant journey.
- The era of personal software is here: why I now build the apps I use.
- All my apps: see My apps: everything I built for myself