A launched process is not a serving one
Starting Kismet or Node-RED is one line: Popen a command, or systemctl start a unit. Neither of those tells you the thing actually works. A process
can spawn and then crash three seconds later reading a stale config; a
systemd unit can report success while the app inside it is still binding
its listener. None of that is "ready" in any sense a person standing at the
bench cares about — ready means the browser UI will actually load if they
type the address in.
So kilodash/webapp.py doesn't trust the launch call. It trusts a TCP probe
against the app's own port. probe() is nothing exotic — socket. create_connection with a 0.4-second timeout — but it's the one signal that
can't be faked by a process existing. Only once that connection succeeds does
a tile's status card turn green and say "Web UI confirmed."

A small state machine, not a boolean
WebApp tracks four states: STOPPED, STARTING, UP, ERROR. launch()
moves it to STARTING and stamps the time. From there, poll() — called
once a tick from the screen's own tick() — does the throttled checking: a
probe every 0.5 s while STARTING, promoting to UP the moment the port
answers; a check of the child process's own exit code in the same window, so
a crash reports "Process exited (code …)" instead of just sitting in
STARTING forever; and a 30-second ready_timeout past which a slow app
just gets marked ERROR with a plain "Timed out waiting for web UI" rather
than a tile that hangs on "Launching…" indefinitely. Once UP, the probe
backs off to every 2 s — enough to notice the app died mid-session without
hammering the port while everything is fine.
Adopt, don't duplicate
launch() probes before it starts anything. If the port already answers —
the app autostarted at boot, or was left running from a previous visit to the
screen — kilodash adopts it as UP instead of spawning a second copy. That
one check is what makes leaving a screen and coming back to it safe, and it's
also why Node-RED and Kismet don't stop themselves on on_leave by default:
the app keeps serving, and the next time the tile opens, launch() finds it
already up and just confirms it.
What every screen gets for free
None of this is app-specific — the app-specific part is one subclass of
WebAppScreen setting app_name, port, and a service or start_cmd.
In exchange it gets the same compact status card everywhere: border color is
the state, body text is the exact IP:port to type into a phone or laptop
once the state is UP, and a tap on the card stops the app (with a confirm)
or launches it again. The probe itself always dials 127.0.0.1 — the app
binds locally on the Pi — but the displayed address comes from a separate
lan_ip() lookup, so the thing you'd type from another device is never
localhost.
That's the whole contract: launch, wait for a real answer on the wire, show where to point a browser. Kismet and Node-RED both run on it today without either of them knowing the other exists.
Scottsky
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.