This release is a pre-release and may not be stable for production use.
Un-cloud your Whisker devices. Fully-local MQTT control and telemetry for the Whisker Litter-Robot 4 — no cloud account, no internet round-trip, no third-party servers. Your robot talks to your broker, and that's it.
Primary repository: developed at forgejo.bryantserver.com/SisyphusMD/whiskerless. The GitHub copy is a read-only mirror (HACS installs from it). Please file issues and pull requests on GitHub — the Forgejo repository does not take external issues.
Status: beta. The local protocol was recovered by reverse-engineering and validated against a real robot. Re-provisioning, telemetry, and settings are proven on hardware, and the panel actions (clean cycle, reset, empty, power, WiFi) were recovered in August 2026 — see what is not here for what is still open.
Why
Out of the box, a Litter-Robot 4 only works through Whisker's AWS cloud: every status update and every button press makes a round-trip to the internet, and Whisker actively blocks third-party clients. whiskerless cuts the cloud out entirely. The robot keeps its firmware; you just re-point its MQTT trust + broker at your own, over its own BLE provisioning channel — no teardown, no UART, no reflash, and fully reversible.
You get:
- a Home Assistant integration (HACS) built against the platinum checklist — fully local, push-first, fully typed. (The quality scale is only awarded to core integrations, so that is the bar it was written to, not a badge it holds.)
- a
whiskerlessCLI + Python library to provision, monitor, read, and control a robot directly; - a complete, public protocol reference — the first published map of the LR4 local MQTT protocol.
What using it looks like
One guided session, next to the robot, and you never touch it again:
$ whiskerless setup
broker IP (e.g. 192.168.1.10): 192.168.1.10
NO CERTIFICATE AUTHORITY ON THIS MACHINE
Your robot has to be told which broker certificate to trust, and that
means a certificate authority. There is no way around it.
1 Generate one for me (recommended)
2 I already have one — I will give you the files
Which? [1]:
✓ generating a certificate authority — done
Your broker needs three files:
~/whiskerless/ca/ca.crt → cafile
~/whiskerless/broker/server.crt → certfile
~/whiskerless/broker/server.key → keyfile
Back up ~/whiskerless somewhere safe. It holds the key that signs
certificates for your robots.
Next: install the files above on your broker, restart it, then
whiskerless provision with a robot in pairing mode.
$ whiskerless provision
robot serial (the unhyphenated LR4C… line on the label, …): LR4C123456
Hold Connect on the robot now, until it beeps, and keep
holding until the connected line appears — it only advertises
while you do, and the link is opened after the scan finds it.
⠹ scanning for robots over BLE — done (3s)
1 ▸ connected to F8:B3:B7:xx:xx:xx (MTU=500, cert chunk=100)
2 ▸ endpoints: ['mqtt-config', 'proto-ver', 'prov-config', 'prov-scan', …]
3 ▸ device MAC: f8:b3:b7:xx:xx:xx
4 ▸ asking the robot which networks it can see
networks the robot can see, strongest first:
0 MyIoT * |||| ch 6
1 HomeNet * |||| ch 1
2 Guest || ch 11
- not listed (hidden network)
select [0-2, or -]: 0
WiFi password for 'MyIoT':
RE-PROVISION — this re-points the robot away from Whisker's cloud
robot F8:B3:B7:xx:xx:xx (MAC f8:b3:b7:xx:xx:xx)
serial LR4C123456
broker 192.168.1.10
wifi MyIoT
identity issued by your CA, CN=LR4C123456
reversible — re-onboard the robot in the Whisker app
Proceed? Type 'yes': yes
5 ▸ DEVICE_ID_SET LR4C123456
6 ▸ WiFi SetConfig+Apply ssid=MyIoT; verifying join (≤20s)
7 ▸ WiFi connected (ip=192.168.1.42)
8 ▸ endpoints: host=192.168.1.10 sub=prod/LR4/LR4C123456/command
9 ▸ CERT_AWS_ROOT_CERT written (1188 bytes)
10 ▸ CERT_DEVICE_CERT written (1493 bytes)
11 ▸ CERT_DEVICE_KEY written (1704 bytes)
12 ▸ APPLY_CONFIG committed
13 ▸ DEVICE_REBOOT
reprovisioned; the robot should reconnect MQTT to 192.168.1.10
saved as LR4C123456 — later commands need no flags:
whiskerless state
(A second robot is much shorter — the broker and the certificate authority are already settled, so it asks only for the serial and which network to join.)
The robot reboots, joins your broker, and from then on every check and every button press is local:
whiskerless state # full decoded status, on demand
whiskerless monitor # live telemetry as it happens
— and if you run Home Assistant, the robot appears on its own as a discovered device the moment it reaches the broker. Fourteen sensors, the buttons, and every writable setting, all local.
What you need
Physical prerequisites, gathered up front — everything else is prompted for:
-
The robot's serial, printed on its label (also in the Whisker app under the robot's settings). The label carries two lines that both start with "LR4"; the serial is the unhyphenated one:
LR4C123456 ← the serial (LR4C + six digits) — this is what you type LR4-0301-00-US ← the model number — not per-unit, not acceptedThe serial becomes the robot's MQTT identity, so it must match the label exactly.
-
An MQTT broker on your LAN with TLS (port 8883) that you control — Mosquitto in a container, the HA add-on, anything. Setup, including making the certificates, is walked through in docs/setup/mqtt-broker.md and docs/setup/certificates.md.
-
Your CA certificate as a PEM file — the one that signed the broker's certificate. You created it during broker setup; it gets written into the robot, which then trusts your broker and nothing else.
-
The WiFi network and password the robot should join (2.4 GHz).
-
A computer with Bluetooth within a few meters of the robot, for the one-time provisioning. macOS, Linux, or Windows — installs below.
How it works (30 seconds)
BLE (one-time) re-point trust + broker runtime (forever after)
your laptop ───► CA + host + topics over protocomm ───► robot ──MQTT/TLS──► your broker ──► Home Assistant
The robot stores all of its cloud identity in NVS and exposes esp-idf
protocomm provisioning over BLE with no PIN. whiskerless writes your CA into
its root-CA slot, your broker as its host and topics, and your WiFi details,
then commits. From then on the robot connects to your broker over TLS and speaks
plain JSON — requestState, settings writes, and a live telemetry stream. Full
detail in docs/how-it-works.md.
Install
Home Assistant (HACS)
- HACS → ⋮ → Custom repositories → add
https://github.com/SisyphusMD/whiskerlessas an Integration. - Install Whiskerless, restart Home Assistant.
- Make sure Home Assistant's MQTT integration is connected to your broker.
- Provision each robot onto that broker (below). It then appears on its own under Settings → Devices & Services as a Discovered device — click Add and give it a name. No broker details or serials to type.
See docs/setup/ for the broker, certificate, and discovery details.
The provisioning CLI
Runs on the computer near the robot. Every channel ships the same tool; none of them needs a system Python except PyPI's.
Homebrew (macOS and Linux):
brew install sisyphusmd/tap/whiskerless
macOS signed installer — download the .pkg for your chip from the
releases
(whiskerless-<version>-macos-arm64.pkg for Apple Silicon, …-x86_64.pkg for
Intel) and double-click. It's signed and notarized by Apple, so there's no
"unidentified developer" warning. The first time it scans, macOS asks to let
your terminal use Bluetooth — allow it.
Debian, Ubuntu, Raspberry Pi OS (64-bit) — an apt repository, so upgrades arrive with the rest of the system:
sudo install -d /etc/apt/keyrings
curl -fsSL https://forgejo.bryantserver.com/api/packages/SisyphusMD/debian/repository.key \
| sudo tee /etc/apt/keyrings/whiskerless.asc >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/whiskerless.asc] https://forgejo.bryantserver.com/api/packages/SisyphusMD/debian stable main" \
| sudo tee /etc/apt/sources.list.d/whiskerless.list >/dev/null
sudo apt update && sudo apt install whiskerless
That first step is the one part that cannot come from the repository: apt will not install a package to obtain the key it needs to trust that package. Fetch it over HTTPS once and apt verifies everything afterwards on its own.
Swap stable for testing to track release candidates. A release lands in
both, so a testing subscriber receives it too and is never stranded on the
last candidate.
(No 32-bit build — a Pi on 32-bit Raspberry Pi OS should use the PyPI route below.)
Fedora, RHEL — a dnf repository:
sudo dnf config-manager --add-repo \
https://forgejo.bryantserver.com/SisyphusMD/whiskerless/raw/branch/main/packaging/whiskerless.repo
sudo dnf install whiskerless
(whiskerless-testing.repo in place of whiskerless.repo tracks release
candidates. On dnf4, --add-repo is the same flag; on dnf5 it is
dnf config-manager addrepo --from-repofile=<url>.)
That file pins our signing key, 4BBACD5A6FF38564, and dnf verifies every
package against it on every install. Do not substitute the .repo file
Forgejo generates at …/rpm/stable.repo: it names Forgejo's own key, which
cannot verify a package we signed, so the install fails with GPG check FAILED.
Adding that key alongside ours "to be safe" is worse still — dnf accepts a package
signed by any listed key, which would let the machine hosting the packages sign
its own.
openSUSE: the same repository —
sudo rpm --import https://forgejo.bryantserver.com/SisyphusMD/whiskerless/raw/branch/main/packaging/whiskerless-signing-key.asc
sudo zypper install ./whiskerless-<version>.x86_64.rpm
The repository is apt and dnf only. zypper insists on verifying the
repository index even with repo_gpgcheck=0 (checked — it fails with Signature verification failed for repomd.xml), and the key that would satisfy it is
Forgejo's, which we deliberately do not ask you to trust. Downloading the .rpm
and verifying it against our key is the same guarantee without that trade.
A single file instead — every .deb and .rpm is also attached to each
release, if you would rather not point a package manager at another host:
sudo apt install ./whiskerless_<version>_amd64.deb # arm64 for a Pi
sudo dnf install ./whiskerless-<version>.x86_64.rpm # aarch64 for ARM
Verify one before installing it with rpm -K ./whiskerless-<version>.x86_64.rpm
(after importing the key above) or against the release's SHA256SUMS. Note that
dpkg/apt do not check package signatures for a local file at all — that is
what the repository above is for.
Raw Linux binary — whiskerless-<version>-linux-x86_64 / …-arm64 from the
same releases page:
chmod +x ./whiskerless-<version>-linux-x86_64
./whiskerless-<version>-linux-x86_64 provision
Windows — no standalone binary, but the PyPI CLI works natively; bleak
drives Windows' built-in Bluetooth:
uvx --from 'whiskerless[ble]' whiskerless provision
(Don't run the Linux binary under WSL: WSL can't reach the Bluetooth adapter, so provisioning won't work there.)
PyPI — one-shot with no install, or on your PATH:
uvx --from 'whiskerless[ble]' whiskerless provision # one-shot
pipx install 'whiskerless[ble]' # CLI on PATH (provisioning included)
pip install 'whiskerless[ble]' # library + BLE provisioning
The releases live on Forgejo (primary) and the GitHub mirror — same artifacts either way.
Set up this machine
Once, before any robot:
whiskerless setup
It asks for your broker's address, offers to create a certificate authority, and
prints the three files to install on your broker. Install them and restart the
broker before going further — that is why this is a separate command from
provision: a robot in pairing mode holds a short window open, and it should not
be spent waiting on a broker restart.
Provision the robot
Put the robot in pairing mode — hold its Connect button for about three seconds, until the light blinks yellow — then, near it:
⚠️ Hold it, do not tap it. A short press toggles the robot's WiFi off. The light turns white and the robot vanishes from your broker, which looks exactly like a dead unit. Press Connect once more to bring it back.
whiskerless provision
It prompts for everything in What you need, checks each answer
as you give it, shows exactly what it is about to write, and asks before
touching anything. When it finishes, the robot reboots onto your broker;
whiskerless state is the proof. Add --dry-run to watch the whole flow with
nothing written.
That's the only step that needs details. whiskerless remembers your broker, your
CA and each robot under ~/whiskerless, so everything afterwards is bare.
Provisioning a second robot only asks for its serial and which network it should
join — the broker and the CA are already settled.
whiskerless sets up the certificates for you. The robot cannot send a username or a password — it was built for AWS IoT, which authenticates by certificate — so certificates are the only authentication it has. The first
provisionon a machine offers to create a certificate authority, your broker's server certificate, and an identity for this machine. Press enter and it is done; it then prints the three files your broker needs:~/whiskerless/ca/ca.crt → cafile ~/whiskerless/broker/server.crt → certfile ~/whiskerless/broker/server.key → keyfileAlready have a CA? Choose "I already have one" and give it the files, or pass
--ca. Add--ca-keyand whiskerless will issue each robot a certificate of its own; leave it out and the robot keeps its Whisker certificate, which works — but then your broker's listener has to accept anonymous clients, and provisioning says so before it writes anything.Back up
~/whiskerless. It holds the key that signs certificates for your robots. Losing it does not stop robots that already work; it costs you the ability to add or re-provision one without visiting every robot you own. The WiFi passphrase is never stored anywhere — it is asked for while you are standing at the robot, and that is the only time it is needed.whiskerless backup ~/Documents # one file: your CA, broker and robots whiskerless restore <that file> # on the machine that replaces this oneLeave the path off either one and it asks —
restorelists the backups it can see and takes a number. It offers to encrypt the file, and asks before it would write your signing key in the clear. Unencrypted it is an ordinary.tar.gz, sotarcan open it on a machine that has never heard of whiskerless.Each backup is named for the moment it was made (
whiskerless-backup-20260816-204915.tar.gz) and never replaces an earlier one — that earlier file may be the copy from before whatever you are about to change. The timestamp is in the name because it is the only part that survives being copied to a stick or pulled out of a snapshot; modification time becomes "just now" for every file at once.restorewill not replace a setup that is already there unless you pass--force, and it tells you first which robots that would strand.
Everyday use
Most people live in Home Assistant afterwards — see docs/setup/home-assistant.md for the entities and what they mean. The CLI covers the same ground from a terminal — the everyday controls, the raw telemetry, and the derived view of what the robot is actually doing:
Anything that talks to the robot needs a route to your broker. Provisioning is Bluetooth, and
robots,useandforgetonly touch files on your machine — butstate,monitor,set,statusand the buttons all open an MQTT connection. If your robots live on an isolated IoT VLAN, your everyday machine may have no way in. Acannot reach broker at …:8883 (timed out)is most often that boundary rather than a whiskerless fault — though a wrong host or port, a stopped broker or a firewall look identical from here, so check those too. Home Assistant is already on that network, which is why it stays the control surface for most people.
whiskerless status # the robot in plain terms, one reading
whiskerless state # full decoded status
whiskerless monitor # live telemetry (ctrl-c to stop)
whiskerless calibrate full # store your own litter reference
whiskerless set night-light-mode auto # writes, then reads back to confirm
whiskerless clean-cycle # start a cycle (asks first)
whiskerless robots # every robot saved on this machine
whiskerless use LR4Cxxxxxx # pick the default of several
whiskerless state --serial LR4Cyyyyyy # or name one per command
whiskerless backup ~/Documents # your CA and robots, in one file
There are no per-command broker flags: one machine points at one broker, behind
one CA, and a flag naming a different one would still present this store's
certificates — so it could only fail confusingly. A genuinely separate broker is
a separate store; set WHISKERLESS_HOME to it. whiskerless forget <serial>
drops a robot's saved details; the robot keeps running. whiskerless --help
lists the rest — including read and send for protocol work.
Upgrading
- Home Assistant: HACS shows the update; install it and restart HA. The integration pins the exact library version it was released with, so the pair always upgrades together.
- Homebrew:
brew upgrade whiskerless. - macOS .pkg: download the newer
.pkgand double-click — it installs over the old one in place. - .deb / .rpm:
sudo apt upgrade whiskerless/sudo dnf upgrade whiskerlessif you added the repository; otherwise install the newer file the same way as the first one. - PyPI:
pipx upgrade whiskerless/pip install -U whiskerless.
whiskerless --version says what is on your PATH.
Release candidates (and switching back to stable)
Release candidates go out before each stable release for testing on real hardware:
- Homebrew:
brew install sisyphusmd/tap/whiskerless-rctracks the newest candidate (it conflicts with the stable formula — one or the other). When the stable release lands, the rc formula is re-pointed at it, so staying onwhiskerless-rcconverges to stable by itself; to switch channels explicitly,brew uninstall whiskerless-rc && brew install sisyphusmd/tap/whiskerless. - HACS: in the integration's page, ⋮ → Redownload and enable showing beta versions to pick a candidate; redownload again without it to go back to stable.
- Releases page: candidates are marked pre-release and never "latest".
Uninstalling
The robot needs nothing installed anywhere to keep running — these only remove the tools:
- Home Assistant: Settings → Devices & Services → Whiskerless → ⋮ → Delete (per robot), then uninstall Whiskerless in HACS and restart. The full walkthrough is in docs/setup/home-assistant.md.
- Homebrew:
brew uninstall whiskerless(orwhiskerless-rc). - macOS .pkg:
sudo rm /usr/local/bin/whiskerless— the installer places that one file. - .deb / .rpm:
sudo apt remove whiskerless/sudo dnf remove whiskerless. - PyPI:
pipx uninstall whiskerless/pip uninstall whiskerless.
Your certificate authority and saved robots stay in ~/whiskerless; delete that
folder to remove them. Run whiskerless backup first if you ever want to add a
robot without re-provisioning every one you own. To put a robot back on the Whisker cloud, re-onboard
it in the Whisker app — the round trip is proven and documented in
docs/recovery.md.
Safety first
This library talks straight to a robot's controller, and some opcodes can reset it or, in the worst case, brick a control board. So it guards every send:
- Four opcodes are refused unconditionally (
0xA3,0xA4,0xAC,0xAD— reset / main-board-OTA orchestrator, globe-motor OTA, flash erase, hardware reset). No flag lets them through. - The destructive panel combos are refused too — factory reset, plug pull and
onboarding mode are all one write away from the clean cycle, so
0x01is whitelisted by value, not opened as a register. - Power and WiFi need an explicit opt-in, because a robot that is switched off, or has had its radio switched off, has left the network — and nothing over MQTT reaches it there.
- Untraced / control-band / calibration writes are refused unless you override them on purpose.
The routine presses — clean cycle, reset, empty — are ungated. Writing the panel button register reproduces the exact code the panel emits, so the robot cannot tell it from a finger, and the firmware's pinch, cat-detect and bonnet interlocks apply either way.
The guard lives in safety.py and both the CLI and
the integration funnel through it — see docs/devices/litter-robot-4/.
What is not here
The filter-change wizard, and it is not coming. Its panel chord is a long press, and the firmware performs short presses over MQTT while silently declining long ones — so every hold-only function is out of reach by this route. Whisker's own cloud has no long-press command either; it reaches those settings by writing registers, which is what whiskerless already does for panel lockout, the night light, the cycle delay and the sleep schedule.
Empty, Power and WiFi ship disabled by default. Power is now proven — written to
a live robot, which powered off and emitted the same code a finger does — but it
still ships disabled, because a robot switched off has left the network and only
someone standing at it can bring it back. WiFi (the panel's Connect button) ends
the same way and gets there in under a second: the robot was gone 0.8 s after the
write, panel light white. Empty's code is captured from a physical press and has
still never been written; it costs a litter refill to try. Enable them deliberately
or use the CLI (empty-cycle, power, wifi-toggle), which prompts — and for the
two that can end the connection, --yes does not exist.
See the reverse-engineering writeup. Contributions welcome.
Repository layout
whiskerless/
├─ src/whiskerless/ # the pip library (codec, MQTT, BLE, safety, CLI)
│ └─ devices/litter_robot_4/ # LR4 protocol: codec, commands, state model, link
├─ custom_components/whiskerless/ # the Home Assistant integration (depends on the lib)
├─ docs/ # protocol reference + setup + recovery guides
├─ examples/ # example automations
└─ tests/ # codec / safety / command / integration tests
Documentation
- How it works · Reverse-engineering writeup · Recovery
- Setup: MQTT broker · Certificates · Home Assistant
- LR4 protocol: protocol · commands · registers · compatibility · capture notebook
Adding another Whisker device
The library is structured so a new robot drops in under
src/whiskerless/devices/<x>/ (codec + commands + state model) and
custom_components/whiskerless/devices/<x>.py, reusing the shared MQTT transport,
BLE provisioning, and safety guard. See CONTRIBUTING.md.
License
MIT. Not affiliated with or endorsed by Whisker. "Litter-Robot" is a trademark of its respective owner; this project is independent and interoperates with hardware you own.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file whiskerless-0.2.0rc33.tar.gz.
File metadata
- Download URL: whiskerless-0.2.0rc33.tar.gz
- Upload date:
- Size: 825.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b38103aa7d95ff729b17772c6320ad49dbbda098d087c1e43f9d2b07fc64ee1
|
|
| MD5 |
1fae985735aa6511b73dbaf12f509416
|
|
| BLAKE2b-256 |
ed73a9bf5fa9b49b2149b5ba74cdd519e2535b02eb54172a952add483a79b932
|
File details
Details for the file whiskerless-0.2.0rc33-py3-none-any.whl.
File metadata
- Download URL: whiskerless-0.2.0rc33-py3-none-any.whl
- Upload date:
- Size: 140.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa4a707de148ecb78718164c9840f511a3c659cffda34633448e7681761c708e
|
|
| MD5 |
9f2325359ce6954544af1af627b2f54f
|
|
| BLAKE2b-256 |
cbdb121a823b4c041427cf167e5f3289f55019bd7776f5947cdc0043d6722965
|