LinkyLink
The phone app I wrote when Pushbullet stopped being worth it
A phone app I wrote so my phone’s notifications, alarms, reminders and shopping lists show up on my computers and my servers, without going through anyone else’s cloud.
| Platform | Android, Kotlin, Jetpack Compose (Material 3) |
|---|---|
| Transport | A self-hosted ntfy server on the homelab; notifications out over HTTP, commands back over a WebSocket |
| Storage | Nextcloud WebDAV for the shopping lists and the Obsidian vault; everything else on the phone |
| Releases | Signed builds published on my Forgejo; version number is the commit count |
| Tabs | Today · Tasks · Shopping · Claude · More (Alarms, Reminders, Captures) |
Why
For years Pushbullet carried my phone’s notifications to my computer. When it turned into a subscription with a shrinking free tier, I looked at what I actually used it for: see what the phone is saying while I’m at a keyboard, and occasionally push something to the phone. That is a small program. I had a self-hosted ntfy server already. So I wrote the app.
Then, as these things go, it grew.
What it does now
Notifications out. A notification listener forwards every notification to a dedicated ntfy instance on the homelab, where a wrapper on my laptop or on the Claude host reads them. Per-app rules decide which notifications may be replied to or acted on from the computer.
Commands in. The app holds a WebSocket open to the same server and accepts a command envelope: list or dismiss notifications, reply to a message, read or set audio and display state, speak text, take a screenshot, send an SMS, toggle settings. Each command is validated the same way whether a thumb on the screen or a script on the server sent it. That was the single best design decision: one module per feature, one envelope format, and the UI is just another client of it.
The alarm clock. It replaced the phone’s alarm app. Alarms are set from the phone or from the computer, ring over the lock screen, and show up as entities my morning routine can see.
Reminders and geofences. Time-based and place-based reminders, with a radius slider and arrive/leave triggers. “Remind me to bring the trash cans up when I get home” works.
Shopping lists. Plain markdown lists in Nextcloud. Photograph a receipt or a handwritten list and Claude on the homelab turns it into items a minute or two later; the list refills itself when the answer lands.
The daily note and the master to-do list. Since version 1.2 the app opens on today’s checklist from my Obsidian vault, read and written straight over WebDAV. Ticking a line writes the note there and then; edits made without signal are queued on disk and sent when the phone is back online.
A Claude tab. A conversation with the Claude Code host in the basement, in a thread, with a button to run my check-in. Share a link, a photo or a PDF to the app and it goes into Claude’s capture queue.
What it took
More Android plumbing than application code. Doze mode kills a background WebSocket unless the app is battery-unrestricted. Android 13 refuses to enable a sideloaded app’s accessibility service until “restricted settings” are allowed for it. The first signed release can’t install over a debug build. Edge-to-edge drawing silently disabled the keyboard inset, so the compose box hid under the keyboard until I padded for it by hand.
The release process is deliberately boring: a signing keystore in one place, a build script that stamps the version from the commit count, an APK attached to a Forgejo release, and a Firefox companion add-on that shares links the same way.
What’s next
A home-screen widget for the Today list, and letting the Claude tab show its work (the files it changed) rather than only its answer. I keep a list; the app shows it to me every morning.