Files
presence/README.md
T

128 lines
4.8 KiB
Markdown
Raw Normal View History

# presence
BLE proximity events for a kiosk screen, in the spirit of swayidle: you give
it commands to run when someone comes near and when nobody has been near for a
while. It listens to Bluetooth LE advertisements through BlueZ and treats their
signal strength as a rough distance.
```
exec presence --rssi -58 \
present 'swaymsg "output * power on"' \
absent 600 'swaymsg "output * power off"'
```
## How it decides
Every advertisement BlueZ hears is one RSSI sample for that device. A device
is **near** when at least `--hits` of its last `--window` samples are at or
above `--rssi` (defaults: 3 of 5 at -58 dBm). Samples older than `--stale`
seconds are forgotten, so a phone that stops advertising stops counting.
Someone is **present** while any device is near. `present` commands run when
that becomes true. Each `absent <seconds> <command>` runs once, after nobody
has been near for that many seconds. `present` fires again only after at
least one `absent` has fired, so a phone flickering around the threshold does
not run it over and over. With no `absent` events, `present` fires on every
arrival.
## What it cannot do
RSSI is a poor ruler. The same phone reads 15 dB apart in a hand and in a back
pocket, bodies in between swing it by 10 dB, and phone models differ in
transmit power. A phone held out at 4 m and one pocketed at 1.5 m can read the
same. Not every phone advertises: iPhones do constantly, many Androids only
while their screen is on. Departure is only visible as silence, hence the
timeout.
Treat it as "someone with a phone is around", with a threshold you tune on
site. For a real 2 m boundary with millisecond response use a 24 GHz mmWave
module (HLK-LD2410) instead; this tool exists to try the cheap thing first.
Addresses are randomized by modern phones and are never written anywhere by
`presence`. It keeps a few seconds of samples in memory and counts. If you
log `--calibrate` output, you are the one storing identifiers; do not keep it.
## Calibrating
Run it at the installed screen, with the display mounted where it will hang,
because the wall and the panel change the readings:
```
presence --calibrate
```
It prints every advertisement as `seconds address rssi NEAR name` and runs
nothing. Stand where a visitor stands with your phone in a pocket, then at 4 m,
then walk past. Pick `--rssi` from what you see; the default -58 is a starting
point, not a measurement. `--ignore <addr>` drops the staff phones and the
kiosk's own peripherals once you know their addresses.
## Options
```
--rssi <dBm> sample counts as near at or above this (default -58)
--window <n> samples kept per device (default 5)
--hits <n> near samples out of the window that make a device near (default 3)
--stale <secs> forget samples older than this (default 15)
--adapter <hciN> which adapter (default: the first)
--ignore <addr> repeatable; addresses to skip
--calibrate print advertisements, run nothing
-w, --wait wait for each command to finish, like swayidle -w
```
Commands run through `sh -c` as the user who started `presence`, which in a
sway config is the session user, so `swaymsg` finds the right socket.
## Config file and user service
The same words, one per line, with the rest of the line as the command and no
quoting rules. The package ships `/usr/share/doc/presence/config.example`:
```
rssi -58
present swaymsg "output * power on"
absent 600 swaymsg "output * power off"
```
`presence --config ~/.config/presence/config` reads it; command-line options
override the file, events from both are used. A `systemd --user` unit is
installed that runs exactly that, so as the kiosk user:
```
mkdir -p ~/.config/presence && cp /usr/share/doc/presence/config.example ~/.config/presence/config
systemctl --user enable --now presence
```
It restarts on failure and only starts when the config exists. Starting it
from the sway config with `exec presence ...` works just as well; pick one.
## Requirements
`bluetoothd` must be running (`systemctl enable --now bluetooth`). BlueZ's
default D-Bus policy lets any user start discovery, so no root is needed. On a
Raspberry Pi 3 the BLE controller shares the antenna with wifi; scanning costs
wifi throughput, which is one more reason to wire the kiosk.
## Install
From the `[uhhm]` Arch registry on x86_64 and aarch64:
```
sudo pacman -S presence
```
Or build it: `cargo build --release`, or `makepkg` at the repo root.
## Release
2026-10-08 21:39:39 +02:00
```
cargo release patch --execute # or minor / major
```
cargo-release bumps `Cargo.toml`, mirrors the version into `PKGBUILD` with
`pkgrel=1` (see `release.toml`), commits, tags `vX.Y.Z` and pushes. CI refuses
a tag that disagrees with either file, so edit neither by hand. Gitea
Actions then builds the package natively on both arches and uploads it to the
registry, authenticated with the uhhm org's `REGISTRY_TOKEN` action secret,
which every repo in the org inherits.