deskradar-pi-services

Supporting services running on the Raspberry Pi.

Overview

This repository contains four systemd services that supplement the main deskradar application. They handle LCD display output, web-based configuration, and boot-time network setup. The code is deployed to /opt/deskradar-pi-services/ on the Pi.

ServicePortRole
deskradar-lcd8010HTTP API → I2C LCD display
deskradar-configurator5000Web UI for settings and WiFi
deskradar-bootstrapOne-shot: resolves MatrixPortal IP at boot
deskradarManaged by this repo's service file; code lives in /opt/deskradar/

LCD Service

A FastAPI server at port 8010 that accepts text messages and renders them to a 16×2 character LCD connected via I2C.

Endpoint

POST /display
Content-Type: application/json

{
  "line1": "BAW123   /",
  "line2": ""
}

Both fields are optional. Each line is truncated to 16 characters. Consecutive identical messages are silently skipped (no redundant I2C writes).

Response (new message):

{"status": "ok", "displayed": {"line1": "BAW123   /", "line2": ""}}

Response (no change):

{"status": "ok", "skipped": true, "reason": "no change"}

Hardware

DeviceI2C BusAddressRole
LCD160210x7c16×2 character LCD display
SN319310x6BRGB LED backlight controller

The LCD library (lcd/lib/LCD1602.py) operates the display in 4-bit mode, 2 lines, 5×8 font. It supports custom characters in CGRAM (used for a backslash glyph). The SN3193 backlight controller supports per-channel PWM brightness and breathing mode.

Source

Web Configurator

A Flask application at port 5000 that provides a browser-based UI for changing DeskRadar settings and switching WiFi networks. Reachable at http://deskradar.local:5000 when on the same network.

Routes

MethodPathPurpose
GET/Home page. Shows a form to join a WiFi network (enter SSID + password).
POST/switch-to-lanConnects to the specified WiFi network. On failure, restores the AP and reboots.
GET/configConfig editor. Reads /etc/deskradar/config.json and renders a form.
POST/configSaves updated config values and restarts the main deskradar service.

Config Form Fields

The form exposes all user-facing config fields. Three fields are intentionally hidden from the UI (GET_URL, LCD_SEND_URL, MATRIX_HTTP_URL) as they are managed by the boot process or rarely need manual changes.

The config form includes a browser geolocation button to automatically populate LAT and LON, and a color picker for CLOSEST_COLOUR (stored as an RGB dict internally).

WiFi Switch Workflow

  1. User submits SSID + password via the home form
  2. Configurator deletes any existing NetworkManager connection with that SSID
  3. Disconnects all active WiFi interfaces
  4. Connects via nmcli device wifi connect
  5. Writes SSID and password to wifi.txt on the CIRCUITPY volume at /media/deskradar/ — so the MatrixPortal S3 also joins the new network on its next boot
  6. On failure: restores the deskradarAP access point and reboots
The CIRCUITPY volume path is auto-detected. It may appear as CIRCUITPY, CIRCUITPY1, etc. The configurator searches /media/deskradar/ for any matching mount.

Config Save Workflow

Saving config does not write files directly — the configurator calls update_config.py as a subprocess, passing new values as JSON on stdin. That subprocess writes /etc/deskradar/config.json and then runs sudo systemctl restart deskradar. This separation allows the subprocess to run with elevated privileges without the configurator needing root access.

Source

Boot Check (deskradar-bootstrap)

A one-shot Python script that runs once per boot. Its job is to locate the MatrixPortal S3 on the network and write its IP address into the config file before the main app starts.

Sequence

  1. Waits 10 seconds for the network stack to fully initialise
  2. Resolves deskradar-portal.local via mDNS (socket lookup), retrying every 2 seconds for up to 30 seconds total
  3. Posts status messages to the LCD service after each attempt (e.g. "resolving...", "found: 10.42.0.50")
  4. On success: writes MATRIX_HTTP_URL = http://<ip>:80 to /etc/deskradar/config.json
  5. On timeout: logs error, shows failure message on LCD, exits with code 1 (systemd may restart)

Set the environment variable DESKRADAR_BYPASS_BOOT=true to skip mDNS resolution (useful for development or when the MatrixPortal is unreachable).

Source

WiFi Access Point Setup

Run once during initial Pi setup:

bash scripts/setup_ap.sh

Creates a NetworkManager connection profile:

SettingValue
Connection namedeskradarAP
SSIDdeskradar
Passworddeskradaradsb
Pi IP address10.42.0.50/24
ModeAP (shared IPv4 — acts as DHCP server)
Priority999 (lowest priority; LAN connections take precedence)

The script is idempotent — it exits cleanly if the profile already exists.

mDNS Setup

Run once during initial Pi setup to make the Pi reachable as deskradar.local:

bash configurator/setup_mdns.sh

This script:

Installation

cd /opt/deskradar-pi-services
pip install -r requirements.txt
bash install_services.sh

install_services.sh copies service files from services/ to /etc/systemd/system/ and enables all four services to start on boot.

Deploy Paths

PathContents
/opt/deskradar-pi-services/This repository
/opt/deskradar/Main deskradar application
/etc/deskradar/config.jsonRuntime configuration file
/etc/systemd/system/deskradar*.serviceService unit files

Dependencies

PackageUsed by
flaskConfigurator web UI
waitressConfigurator WSGI server
python-dotenvConfigurator env vars (.envAP_NAME)
requestsBoot check (LCD status posts)
fastapiLCD service API
uvicornLCD service ASGI server
smbus2I2C communication (pure Python)
smbusI2C alternative (hardware-native)