Guide

How to use PiPup

PiPup listens on a small HTTP server on your TV. Anything on your network can trigger a notification with a single request — from curl, a script, or Home Assistant. Here are copy-paste examples for both.

Before you start

  • Find your TV's address. Open PiPup on the TV — the main screen shows it (e.g. 192.168.1.50:7979) and a QR code. All requests go to http://<tv-ip>:7979.
  • Grant "Display over other apps". On first launch PiPup shows a Grant display permission button — notifications can't appear without it.
  • Token (optional). If you set a token in PiPup's settings, add an Authorization: Bearer <token> header to every request.

Tip: replace <tv-ip> with your TV's IP in the examples below. Test reachability first with curl http://<tv-ip>:7979/ping — it should reply pipup <version>.

Pure HTTP requests curl

Every example is a plain HTTP call — use them from curl, a shell script, Node, Python, or any HTTP client. The body is JSON unless noted.

GET /ping · /status

Check the server is up and read a JSON status snapshot (version, active popups, uptime).

# plain-text reachability check
curl http://<tv-ip>:7979/ping

# JSON status
curl http://<tv-ip>:7979/status

POST /notify — a basic notification

Title, message, and how long it stays on screen (seconds).

curl -X POST http://<tv-ip>:7979/notify \
  -H "Content-Type: application/json" \
  -d '{"title": "Hello", "message": "PiPup is working", "duration": 10}'

Camera snapshot

Embed a still image — e.g. a camera snapshot URL. It also accepts an inline data: base64 URI.

curl -X POST http://<tv-ip>:7979/notify \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Front door",
    "message": "Motion detected",
    "duration": 15,
    "media": { "image": { "uri": "http://<camera>/snapshot.jpg", "width": 640 } }
  }'

Live doorbell / camera video

Embed a live stream. The most compatible source is an HTTP-MP4 URL from go2rtc (bundled with Home Assistant); plain rtsp:// and HLS also work.

curl -X POST http://<tv-ip>:7979/notify \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Doorbell",
    "duration": 20,
    "media": { "video": { "uri": "http://<go2rtc-host>:1984/api/stream.mp4?src=doorbell", "width": 760 } }
  }'

Note: encrypted rtsps:// (RTSP over TLS / SRTP) is not supported by the player — this affects UniFi Protect's default stream. Restream it through go2rtc (recommended) or enable a plain rtsp:// stream on the camera.

Spoken announcement (text-to-speech)

Add tts to speak the notification aloud. Use it with or without an on-screen message.

curl -X POST http://<tv-ip>:7979/notify \
  -H "Content-Type: application/json" \
  -d '{"message": "The laundry is done", "tts": "The laundry is done", "duration": 8}'

Styling & a countdown bar

Colors use #[AA]RRGGBB. Add rounded corners, a border, and a progress bar that drains over duration.

curl -X POST http://<tv-ip>:7979/notify \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Weather alert",
    "message": "Severe thunderstorm warning",
    "duration": 12,
    "position": 0,
    "backgroundColor": "#CC161616",
    "titleColor": "#FBBF24",
    "cornerRadius": 18,
    "borderColor": "#FBBF24",
    "borderWidth": 2,
    "showProgress": true
  }'

position: 0 top-right · 1 top-left · 2 bottom-right · 3 bottom-left · 4 center. Set "duration": 0 to keep a popup on screen until you dismiss it.

POST /cancel — dismiss notifications

/notify returns {"status":"ok","id":"…"}. Dismiss everything, or just one popup by its id.

# dismiss all popups
curl -X POST http://<tv-ip>:7979/cancel

# dismiss a single popup by the id /notify returned
curl -X POST "http://<tv-ip>:7979/cancel?id=<id>"

Upload a local image file

Use multipart/form-data to upload an image from disk instead of a URL.

curl -X POST http://<tv-ip>:7979/notify \
  -F "title=Snapshot" \
  -F "duration=15" \
  -F "image=@/path/to/snapshot.jpg"

Authenticated request

If a token is configured, send it as a bearer header (or ?token= query parameter).

curl -X POST http://<tv-ip>:7979/notify \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -d '{"title": "Secure", "message": "Authenticated request"}'

Home Assistant YAML

Wrap PiPup in a rest_command, then call it from any automation or script. Add the IP of your TV; templated fields let one command serve many alerts.

Prefer a native integration? The PiPup Home Assistant integration (HACS) auto-discovers your TVs over mDNS and adds a proper notify.send_message / pipup.send service and a per-TV status sensor — no IPs or rest_command to maintain. The examples below are the no-install alternative.

1 · Define the command

In configuration.yaml (restart HA after adding it):

# configuration.yaml
rest_command:
  pipup:
    url: "http://<tv-ip>:7979/notify"
    method: POST
    content_type: "application/json"
    # uncomment only if you set a token in PiPup:
    # headers:
    #   authorization: "Bearer <your-token>"
    payload: >-
      {"title": "{{ title }}", "message": "{{ message }}",
       "duration": {{ duration | default(15) }}}

2 · Call it from an automation

Show a notification when the doorbell is pressed:

automation:
  - alias: "Doorbell → TV"
    trigger:
      - platform: state
        entity_id: binary_sensor.front_doorbell
        to: "on"
    action:
      - service: rest_command.pipup
        data:
          title: "Front door"
          message: "Someone is at the door"

Live doorbell video

A dedicated command that embeds the go2rtc HTTP-MP4 stream (works even when the camera's native stream is rtsps://):

rest_command:
  pipup_doorbell:
    url: "http://<tv-ip>:7979/notify"
    method: POST
    content_type: "application/json"
    payload: >-
      {"title": "Front door", "message": "Someone is at the door", "duration": 20,
       "media": {"video": {"uri": "http://<go2rtc-host>:1984/api/stream.mp4?src=doorbell", "width": 760}}}

Spoken announcement

Speak an alert aloud — great for "laundry done" or "garage left open":

rest_command:
  pipup_say:
    url: "http://<tv-ip>:7979/notify"
    method: POST
    content_type: "application/json"
    payload: >-
      {"message": "{{ message }}", "tts": "{{ message }}", "duration": 8}
# …then in an automation:
      - service: rest_command.pipup_say
        data:
          message: "The washing machine has finished"

More fields — beyond the examples above, a notification also supports a leading iconUri, a sound to play, borders (borderColor + borderWidth), cornerRadius, media placement (mediaPosition), title/message alignment, entrance/exit animations, and shown/dismissed callback URLs. Add any of them to the JSON body.

Ready to try it?

Install PiPup on your Android TV, open it once to grant the display permission, and send your first request.