Forward new TalkingPoints (Families) school messages to Home Assistant, so you can route them wherever you like instead of living in the TalkingPoints app.
It keeps one logged-in browser alive, watches the messages the web app fetches, and POSTs new ones to a Home Assistant webhook.
TalkingPoints' web app authenticates every API call with an x-token header
that is the raw Meteor login token. That token lives only in the app's
in-memory JavaScript: it is never in a response body, never written to disk, and
injected by in-app code. So it cannot be extracted and replayed from a script.
Consequences:
- A fresh process / page reload starts logged out (in-memory session).
- The only way to make authenticated calls is to keep the real logged-in app
running and intercept the
/inboxresponses it already makes.
Hence: one persistent, headed Chromium (under Xvfb), logged in via noVNC.
GET /api/parents/v3/inbox returns, per conversation: teacher (with.user),
student (with.student), lastMessageId, lastMessageDate, lastMessage
(preview), and unreadCount. The poller tracks lastMessageId per conversation
and forwards on change. The app self-polls /inbox, so detection is near
real-time.
Webhook payload:
{ "source": "talkingpoints", "event": "message", "from": "Teacher Name",
"student": "Kid Name", "preview": "…", "unread": 1,
"at": "2026-07-13T12:32:39.596Z", "conversationId": "…" }A {"event":"session_expired"} payload is sent if the login drops, so you can
alert yourself to re-auth.
npm install
npx playwright install chromium
node ha-mock.mjs & # stand-in HA receiver on :8123
FORWARD_ON_START=1 node poller.mjs # headed browser; log in + open your inboxFORWARD_ON_START=1 forwards the current latest message per conversation once,
to prove the pipe. Leave it off in production.
- Create a Home Assistant
webhookautomation trigger and copy its URL. cp .env.example .envand setHA_WEBHOOK_URL.- Build + start on the NAS (native amd64):
docker compose up -d --build
- Open noVNC on the LAN only:
http://<nas>:6080/vnc.html, then log in in the Chromium window (phone number → SMS code) and open your inbox. - Done. It forwards new messages to HA and survives restarts (seen-state is
persisted in
./data).
The session is in-memory, so it does not survive a container restart, and may
expire on its own after a while. When it drops, the poller sends a
session_expired webhook. To recover, open noVNC and log in again in the same
running container — no rebuild needed.
data/(session, tokens, message content) and.envare gitignored. Never commit them.- The noVNC port is a live, authenticated view of your child's school messages. Bind it to LAN/VPN only (Tailscale, or the NAS reverse proxy with auth). Do not expose it to the internet.
| File | Purpose |
|---|---|
poller.mjs |
Keep-alive browser, intercepts /inbox, forwards to HA |
ha-mock.mjs |
Local stand-in for the HA webhook (dev only) |
Dockerfile / entrypoint.sh |
Headed Chromium + Xvfb + noVNC |
docker-compose.yml |
NAS deployment |
login.mjs, capture*.mjs, probe.mjs, … |
Discovery/reverse-engineering scripts kept for reference |