Home-Screen
  • Python 44.9%
  • JavaScript 22%
  • HTML 20.6%
  • CSS 10.9%
  • Shell 1.6%
Find a file
tschnurre 7a9a9a07ec Radio: simplify skip to a hard cut, drop crossfade attempt
The acrossfade-based transition broke ffmpeg's realtime pacing when
mixing two -re inputs in one -filter_complex, streaming a whole track
to Icecast in a few seconds instead of its real duration. A Liquidsoap
replacement was also evaluated but rejected: the Debian Trixie package
segfaults on this Pi due to an ABI mismatch with the Raspberry Pi's
own ffmpeg build, and downgrading ffmpeg system-wide would risk
breaking baresip's hardware-accelerated video calls.

Skip now just kills the running ffmpeg process; radioplay.sh's
existing end-of-track queue-advance logic takes it from there, exactly
as it already does when a track finishes naturally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 18:54:51 +02:00
app Radio: simplify skip to a hard cut, drop crossfade attempt 2026-08-22 18:54:51 +02:00
config Radio: fix Icecast stream URL after Pi IP change 2026-08-19 20:51:29 +02:00
scripts Radio: simplify skip to a hard cut, drop crossfade attempt 2026-08-22 18:54:51 +02:00
skills/homescreen-implementation Docs: update README and specs for Radio, Ämtliplan and Glücksrad 2026-08-02 20:57:32 +02:00
specs Radio: simplify skip to a hard cut, drop crossfade attempt 2026-08-22 18:54:51 +02:00
static Radio: add Weiter button to skip to next queued track with crossfade 2026-08-19 20:51:37 +02:00
tests Ämtliplan: bigger centered celebration + Glücksrad mini-game 2026-07-12 20:24:24 +02:00
.gitignore Add gamified Ämtliplan (chore chart) screen 2026-07-12 19:17:28 +02:00
pyproject.toml Docs: update README and specs for Radio, Ämtliplan and Glücksrad 2026-08-02 20:57:32 +02:00
README.md Docs: note Verbatim PMT-17 touchscreen and confirmed touch support 2026-08-02 21:03:54 +02:00

Homescreen

Touchscreen homescreen for a Raspberry Pi with five main pages:

  • Kalender
  • Telefon
  • Radio
  • PV-Anlage
  • Ämtliplan

Start

Install dependencies and start the local server:

python3.13 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
uvicorn app.main:app --reload

Then open http://127.0.0.1:8000.

Run API smoke tests with:

pytest -q

Current status

  • Python backend with live calendar, contacts, radio, energy, and chores endpoints
  • Calls run in production over baresip (audio and video), with a linphonecsh fallback
  • Static fullscreen-oriented frontend
  • Config files for calendars, contacts, radio stations, chores, and ioBroker

Python version

The project now targets Python 3.10+.

Recommended local setup:

  • Python 3.13
  • virtual environment in .venv

Environments

  • Development: macOS on Intel
  • Production target: Raspberry Pi 5 on Raspberry Pi OS, with a Verbatim PMT-17 portable touchscreen as the display. Touch input on this screen has been tested and confirmed working.

The backend is structured so integrations stay behind Python services. Frontend code remains platform-neutral, while Raspberry-Pi-specific concerns such as Linphone control and kiosk startup can be added later without changing the UI contract.

For telephony, video calls are a product requirement, not an optional enhancement. The production SIP path must support video reliably, with H.264 preferred for Linphone interoperability and VP8 kept as a fallback where possible.

Deployment

Production is deployed from Git only.

Recommended flow:

git add ...
git commit -m "Your change"
git push origin main
ssh pi@<raspberry-pi>
sudo git -C /opt/homescreen pull --ff-only
sudo systemctl restart homescreen.service

Do not update /opt/homescreen via scp, rsync, or tar-over-SSH. The Pi should stay on a normal Git checkout so deployed code matches repository history.

Kiosk Browser

The Raspberry Pi kiosk browser is Chromium started from ~pi/.config/autostart/frontend.desktop.

Use --password-store=basic in the Chromium command line so the kiosk session does not prompt for GNOME Keyring or Secret Service access during unattended startup.

To avoid Chromium restoring stale kiosk sessions after updates or reboots, start it through scripts/launch-kiosk.sh from the Git checkout. The script uses a fresh dedicated temporary Chromium profile on each start.

Secrets

Copy config/secrets.example.json to config/secrets.json and fill in the real credentials.

Current secret blocks:

  • nextcloud for CalDAV access
  • iobroker for the REST API on zephyr.schnurri.ch:8083
  • linphone for Raspberry-Pi-side dialing commands
  • baresip as a pragmatic SIP CLI fallback when the Debian linphonecsh path is unreliable

For the productive Raspberry Pi setup, the current baresip path expects PulseAudio/PipeWire style audio devices. Recommended defaults:

  • baresip.audioPlayer = "pulse,default"
  • baresip.audioSource = "pulse,default"
  • baresip.audioAlert = "pulse,default"

The current production path also offers PCMU, PCMA, G722, and Opus for audio plus H.264 for video.

For Raspberry-Pi phone dialing, linphone.sip can hold the SIP registration data used by linphonecsh register. If your PBX has broken or incomplete IPv6 reachability, set linphone.disableIpv6 to true and use the IPv4 host directly.

Contact photos

Store MVP contact photos in:

static/assets/contacts/

Filename rule:

  • file name must match the contact id
  • preferred format is .jpg
  • examples:
    • static/assets/contacts/mama.jpg
    • static/assets/contacts/arzt.jpg
    • static/assets/contacts/yorick.jpg

If a contact has no image yet, keep the photoUrl empty or point it to a later file and the UI can fall back to initials.

Radio

Configured in config/radio.json:

  • stations: fixed online stations (Icecast/HTTP streams)
  • nfsMountPath: NFS share that can be browsed and played from the Radio tab
  • configFile/serviceName: the local radioplay systemd service is driven through this config file and restarted on playback changes

If another NFS track is tapped while something is already playing, it is added to a queue instead of interrupting playback. A stop action clears both playback and the queue.

Ämtliplan (Chores)

Configured in config/chores.json (chore catalog with point value and icon). Per-member scores live in config/chores_state.json (untracked in Git, see .gitignore) and reset automatically every Sunday at 20:00.

Once a member collects 20 points without spinning in between, a spin of the "Glücksrad" (wheel of fortune) unlocks. Spinning (POST /api/chores/spin) randomly awards 5 bonus points, another spin, or nothing.

A long-press on a person's score opens a correction dialog for parents (POST /api/chores/adjust).