Raspberry Pi Client Services
Audience: Deployment Administrator, System Admin, Support, Developer
Related documentation: Remote Pi Client, Raspberry Pi deployment, Background jobs and schedules, E-ink rendering
Deployment roles
A Raspberry Pi can run Blackcap in three materially different roles:
- Pi server with attached e-ink — hosts the Flask/Gunicorn application and SQLite database and owns a local physical panel.
- Pi server without attached e-ink — hosts Blackcap and may deliver content to remote clients or hosted displays.
- Client-only Pi — does not host the application database or Admin UI; it links its attached panel to another Blackcap server, commonly GCP.
Do not install or enable every service on every Pi. The deployment scripts choose services according to role, attached hardware, and whether remote Let’s Cook delivery is enabled.
Client-only service topology
blackcap-client.service
This is the primary always-running client service. It launches the client helper in local-attached mode, reads the saved server/display link, polls the server, downloads server-prepared preview or panel transport, verifies it, writes it to the attached Waveshare panel, reports status, and repeats.
Typical behavior:
- starts after networking is available;
- restarts automatically after failure;
- uses the Blackcap client configuration and linked display token;
- performs a baseline poll for ordinary content and a faster cadence during active Let’s Cook sessions;
- reports download, render version, panel readiness, physical-write timing, and error state to the server;
- never owns the server database or normal server scheduler.
Manage it with:
sudo systemctl status blackcap-client.service --no-pager -l
sudo journalctl -u blackcap-client.service -n 200 --no-pager
sudo systemctl restart blackcap-client.service
The service must run as the deployment user with permission to read its configuration and access SPI/GPIO. Keep the linked client token private.
Networking dependency
The client requires working DNS, trusted HTTPS, system time, and outbound access to the Blackcap server. A network-online target does not guarantee that public DNS or the server is already reachable, so the client is designed to retry safely.
Physical panel dependencies
The optimized panel is the Waveshare 13.3-inch Type K four-gray display. The client needs the compatible Waveshare driver, SPI enabled, correct GPIO/SPI access, and the server-prepared transport contract. The display write path must use the shared panel lock so two local processes never drive the panel at the same time.
Server-side services that support remote clients
The following services run on a Blackcap server, not on the client-only Pi.
inky_admin.service or inky_admin.gcp.service
The application service exposes the Admin/Mobile/API surfaces, client-linking and polling endpoints, display state, preview/transport generation, and client diagnostics. On Pi it normally uses the Pi unit and SQLite; on GCP it uses the GCP unit, PostgreSQL through Cloud SQL Auth Proxy, and Gunicorn behind Nginx.
The application service must be healthy for linking, content assignment, and polling. It also authenticates client tokens and prevents a Remote Pi Client from receiving another organization’s content.
Let’s Cook watcher unit names by host
The installer selects the watcher role by deployment mode; the unit names are not identical on every host.
- Full Pi server with an attached panel:
blackcap_lets_cook_timer_refresh.servicerunsrun_lets_cook_timer_refresh.py --watch --interval 10 --quietfor local attached-e-ink refresh coalescing. - Full Pi server serving Remote Pi Clients: optional
blackcap_lets_cook_remote_client_refresh.serviceruns--watch --interval 2 --remote-clients --quietand prepares fast remote-client preview/render versions. - GCP server:
blackcap_lets_cook_timer_refresh.serviceis reused as the remote-client preview watcher and runs--watch --interval 2 --remote-clients --quiet; the Pi-only companionblackcap_lets_cook_remote_client_refresh.serviceis removed. - Client-only Pi: neither server-side watcher is installed. The client polls the server and writes the panel; it has no application database from which to generate Let’s Cook state.
Without the remote-client watcher, a Remote Pi Client session can still start, but timer/progress updates may wait for a slower general refresh path. The UI warns administrators when the selected deployment intentionally disabled that watcher.
Do not treat local and remote watcher roles as interchangeable: the local role owns an attached panel refresh, while the remote role prepares server-side output that a linked client later polls and writes.
Managed schedule profiles
A full Pi or GCP server receives the server schedule in /etc/cron.d/blackcap: display refresh every minute, regression-task resume/start every 15 minutes, daily purge, hourly backup-schedule checks, and optional Menu refresh. These are database-backed server operations, not client polling.
A client-only Pi uses the pi-client schedule mode. That mode explicitly disables server Menu refresh, server Let’s Cook watchers, GeoIP update units, and all database-backed server schedules. It may retain only the optional monthly attached-e-ink deep-clean entry when enabled.
Menu refresh
inky_menu.py runs on the server according to the selected schedule profile. It captures and renders Menu content for displays, including remote clients. The client only receives the resulting assigned transport; it does not run Playwright/OCR against the organization Menu URL.
Other Pi server services
Depending on deployment role, a Pi server may also use:
inky_admin.service— Gunicorn application service;stunnel4.service(or the distribution'sstunnel.service) — the recommended Pi HTTPS front end created bydeploy_pi.sh;blackcap_geoip_update.serviceand.timer— monthly local GeoIP database maintenance;blackcap_lets_cook_timer_refresh.service— local attached-panel timer watcher;blackcap_lets_cook_remote_client_refresh.service— remote-client timer watcher;- managed cron operations for display refresh, Menu refresh, regression tasks, backups, purge, and optional monthly e-ink deep clean.
Only the services relevant to the selected deployment should be enabled.
What blackcap-client.service does on a full Pi server
The deployment script installs blackcap-client.service when the full Pi server directly drives an attached e-ink panel. Its --local-attached mode does not automatically turn the Pi into a remote client. The process sleeps while the attached display is owned by normal local server content. It becomes active only when that local display is placed in Client mode and linked to another Blackcap server. This prevents the local server renderer and the remote pull client from competing for the same panel.
The service checks local ownership state every five seconds while sleeping. When active, its normal server-directed pull interval defaults to 30 seconds, the heartbeat defaults to 60 seconds, and a successful Let’s Cook physical refresh triggers an immediate follow-up poll so a render produced during the slow e-ink write is not delayed by another full interval.
The same process owns last-known-good preview/transport files, transport verification, the shared panel lock, physical-write acknowledgement, retry/backoff, and local client diagnostics. It must not bypass the server's display token, organization, render-version, or panel-ready contract.
Polling and content lifecycle
The remote client normally polls at a baseline cadence. During active Let’s Cook it uses the faster session cadence and performs a post-refresh recheck so an update created while the slow physical panel refresh was underway is not delayed until another full interval.
The server owns:
- organization authorization;
- active/default/scheduled display state;
- recipe, Meal Planner, Menu, and Let’s Cook rendering;
- render version and prepared transport;
- client-link and revocation state.
The client owns:
- local download and integrity verification;
- panel-driver conversion where required by the transport contract;
- the physical e-ink write and lock;
- local retry/backoff;
- reporting its last poll, download, physical update, and error.
Linking and secrets
Link the attached display through the client setup flow using the server URL, organization/display selection, and one-time or issued client credential. The saved credential is equivalent to access to that display and should be readable only by the service user.
Revoking or unlinking the display invalidates the client credential. Backups and diagnostic clones must not create a second usable copy of it.
Installation and enablement
Use the repository Pi deployment script/profile rather than hand-installing units in isolation. The installer places unit files, reloads systemd, enables the selected services, and writes the correct schedule profile. After installation:
sudo systemctl daemon-reload
sudo systemctl enable --now blackcap-client.service
Use that direct command only when the unit and client configuration have already been installed correctly. For unit-file changes, follow Blackcap practice and edit the actual file, for example:
sudo nano /etc/systemd/system/blackcap-client.service
sudo systemctl daemon-reload
sudo systemctl restart blackcap-client.service
Service-by-role matrix
| Component | Pi server + local panel | Pi server, no local panel | Client-only Pi | GCP server |
|---|---|---|---|---|
| Application service | Yes | Yes | No | Yes |
| SQLite database | Usually | Usually | No | No |
| Cloud SQL Auth Proxy | No | No | No | Yes |
blackcap-client.service |
Only when acting as a client | Only when acting as a client | Yes | No |
| Local Let’s Cook watcher | Yes | No | No | No |
| Remote-client Let’s Cook watcher | Optional companion unit | Optional companion unit | No | blackcap_lets_cook_timer_refresh.service in remote mode |
| Server cron schedule | Yes | Yes | No | Yes |
| Menu capture/render | Yes | Yes | No | Yes |
| Physical panel write | Local watcher/render path | No | Client service | No |
| GeoIP update timer | Yes when server schedules are installed | Yes when server schedules are installed | No | Yes when server schedules are installed |
| TLS reverse proxy | Pi TLS approach | Pi TLS approach | No inbound service required | Nginx/Certbot |
Troubleshooting sequence
- Verify power, SPI, cabling, and the correct panel model.
- Verify time, DNS, and HTTPS access from the client.
- Check
blackcap-client.servicestatus and logs. - Confirm the linked display is active, not revoked, and assigned Client-compatible content.
- Compare server Display Details timestamps: last client poll, preview/version, and physical update.
- Confirm the server application service is healthy.
- For Let’s Cook delay, confirm the remote-client watcher is enabled on the server.
- For scheduled content, inspect the server cron/job status rather than the client service.
- For a downloaded image that does not reach the panel, inspect panel-ready, transport hash, lock, driver mode, and physical-write logs.
Changes and restarts
Documentation-only changes require no restart. A changed client Python file normally requires copying the file and restarting blackcap-client.service. A changed systemd unit requires direct file update, daemon-reload, restart, and status verification. A server application change normally requires its migrations if any, then an application restart; do not restart the physical client unless its code or configuration changed.