This is a Leicas/OpenNeato fork focused on the Home Assistant
integration. The HACS-installable integration under
custom_components/openneato/ is the reason this fork exists — it turns the
upstream ESP32 bridge into a full HA device with vacuum / camera / sensor / switch / button entities, no YAML.
Important
If you want the standalone web UI, use upstream renjfk/OpenNeato directly — it has the original maintainer, the release cadence, and the live demo.
Use this fork if you run Home Assistant and want the robot exposed as a first-class HA device. Firmware
changes in this fork are minor and exist only to support the integration (e.g. /api/sensors,
/api/history hardening, ntfy server/token/on-start extensions). The firmware, frontend, and flash tool
otherwise track upstream closely.
The upstream project is an open-source replacement for Neato's discontinued cloud and mobile app: an ESP32 bridge that talks to Botvac robots (D3-D7) over UART and exposes a local web UI over WiFi — no cloud, no app, no account required.
Note
Both upstream and this fork are early beta. Rough edges are expected. For HA-integration bugs, open an issue here; for firmware/frontend/flash-tool bugs, file against upstream.
Tip
Want to get a feel for the underlying web UI without hardware? Open the upstream
live demo. Demo states can be selected with ?scenario=...; see
mock scenarios.
| Dashboard | Manual Drive | Cleaning History |
|---|---|---|
![]() |
![]() |
![]() |
| Clean Map | Schedule | Settings |
|---|---|---|
![]() |
![]() |
![]() |
The sections below describe the upstream renjfk/OpenNeato project that this fork builds on — the ESP32 bridge firmware, standalone web UI, and flash tool. None of this is unique to this fork; it's reproduced here so HA users have full context on what's running behind the integration. If you only want the HA bits, you can skip ahead to the Home Assistant section below.
Neato shut down their cloud services and mobile app, leaving perfectly functional robot vacuums without remote control or scheduling. OpenNeato brings them back to life with a small ESP32 board wired to the robot's debug port, giving you a local web interface that works without any external dependencies.
- Dashboard with live robot status, battery level, cleaning state, WiFi signal, and storage usage
- House and spot cleaning with pause/resume/stop/dock controls that adapt to the current state
- Manual driving mode with a virtual joystick, live LIDAR map visualization, motor toggles (brush, vacuum, side brush), bumper/wheel-lift/stall safety warnings
- Live cleaning map — watch the robot's path during an active cleaning session in the History view, rendered on a canvas with coverage overlay
- 7-day cleaning scheduler with two slots per day, managed entirely on the ESP32 (doesn't use the robot's built-in schedule commands)
- Cleaning history with recorded robot paths rendered as coverage maps, session stats like duration, distance, area covered, and battery usage; individual session import/export for backup and restore
- Push notifications via ntfy.sh; get notified when cleaning is done, an error occurs, a maintenance alert triggers, or the robot docks; fully optional, configurable per event
- OTA firmware updates from the browser with SHA-256 download verification (against published
checksums.txt), MD5 transfer integrity, dual-partition layout with auto-rollback, and automatic new version notifications when a release is available on GitHub - Settings page for hostname, timezone, motor presets, notification topics, UART pins, theme (dark/light/auto), battery diagnostics (cycle count, voltage, temperature, and a "New Battery" calibration trigger), and more
- Event logging with configurable log levels (off/info/debug), compressed JSONL files on SPIFFS, browsable and downloadable from the UI; optional remote syslog (UDP) for long-running diagnostics without flash wear; logging is off by default
- Factory reset via 5-second button hold on the ESP32 or from the settings page
- Robot clock sync — pushes NTP time to the robot automatically, re-syncs every 4 hours
- Flash tool — standalone CLI that auto-detects the USB port, downloads the correct firmware from GitHub Releases, and flashes with zero prerequisites
- Safety watchdog — auto-stops wheels in manual mode if the browser disconnects; task and heap watchdogs restart the ESP32 on hangs
- Crash recovery — orphaned cleaning sessions after unexpected reboots are automatically recovered with full stats
The frontend is a lightweight SPA that gets gzipped and embedded directly into the firmware binary, so a single OTA update covers both firmware and UI. Mobile-friendly, dark theme by default.
This fork ships a HACS-installable Home Assistant custom integration in
custom_components/openneato/. Once installed it discovers your OpenNeato bridge
by IP/hostname and exposes the robot as a full HA device — no YAML, no extra add-ons.
Note
The HA integration is the primary differentiator of this fork. The firmware, frontend, and flash tool track
upstream renjfk/OpenNeato closely. If you only want the standalone web
UI, use upstream directly.
- In HACS, add this fork as a custom repository:
https://github.com/Leicas/OpenNeato(category: Integration). - Search for OpenNeato in HACS and install.
- Restart Home Assistant.
- Settings → Devices & Services → Add Integration → OpenNeato and enter the bridge hostname or IP
(e.g.
neato.localor192.168.1.42).
The integration polls /api/* over your LAN every 5 seconds (local_polling). No cloud round-trip, no
external dependencies. Requires firmware 1.0+; battery diagnostics need firmware 0.13+ (upstream PR #121).
A single device with the following entity groups:
- Vacuum (
vacuum.openneato_<name>) — start/stop/pause/dock/locate/spot-clean, battery level, status, fan speed presets (Eco/Auto/Intense), error reporting. Works with all the standard vacuum cards. - Cameras —
LIDAR map(live 360° scan during cleaning) andCleaning replay(animated GIF time-lapse of the most recent completed session). Both are standard HA camera entities, compatible with picture-entity, picture-glance, and vacuum-card. - Sensors — battery level/voltage/current/temperature, battery cycle count, cumulative cleaning time, WiFi RSSI, free heap, storage used, uptime, motor RPMs, error code/message, plus "last clean" stats (duration, area covered, distance, battery used, mode, end time) pulled from the on-device history.
- Binary sensors — charging, external power, battery over-temp, battery failure, empty fuel, error active, NTP synced, dustbin seated, left/right wheel lifted, dock contact.
- Switches — eco mode, intense clean, bin-full detect, wall follower, schedule on/off, button-click sounds, melodies, warnings, stealth LED, remote syslog, WiFi AP fallback, and per-event push notifications (start/done/error/alert/docking).
- Text — syslog server IP, ntfy topic, ntfy server, ntfy token (full push-notification config from HA).
- Numbers — brush RPM, vacuum speed, side-brush power, stall threshold.
- Select — navigation mode (Normal / Gentle / Deep / Quick).
- Buttons — restart bridge, restart robot, shutdown robot, locate, clear errors, format filesystem (diagnostic, disabled by default), new battery (resets fuel-gauge calibration after a physical pack swap, disabled by default for safety).
Every entity is translated via strings.json, and diagnostic-class entities (voltages, currents, raw
sensor states) are tagged so they cluster cleanly under HA's Diagnostic section.
- Camera entities and vacuum-card — the
LIDAR mapcamera self-manages a 2 s/api/lidarpoll only while the robot is actively cleaning, so it doesn't add load to the coordinator's 5 s cycle. When idle, both cameras fall back to the most recent completed cleaning map. - Coordinator resilience — the integration tolerates a single hung endpoint without going into "requires attention" state. State / charger / system are critical; anything else (errors, motors, history) falls back to the last known value during transient ESP32 serial hangs.
- No Pillow declared dependency — map rendering uses Pillow which already ships with HA Core, so the
integration's
manifest.jsonkeeps"requirements": []. Nothing extra to install. - ntfy + custom servers — point
ntfy_serverat a self-hosted instance andntfy_tokenat a Bearer token for authenticated push. Empty server defaults tontfy.sh; empty token is unauthenticated.
Full per-version notes live in custom_components/openneato/CHANGELOG.md.
Highlights:
- 1.11 — added
notify_on_startandap_fallback_on_disconnectswitches; ntfy topic/server/token text entities for full HA-side push config. - 1.10 — battery diagnostics (current, voltage, cycles, cumulative cleaning time) on top of firmware
PR #121,
New batterycalibration button, UTF-8-tolerant/api/versionparsing. - 1.9 — fixed cameras stuck on the idle placeholder (
get_encoding()crash on streamed bodies). - 1.6 —
Cleaning replaycamera (animated GIF time-lapse),/api/historycorruption-tolerant parsing, history filename validation + 2 MB response cap (LAN-MITM mitigation). - 1.3 —
LIDAR mapcamera ported from the frontend renderer; self-managed polling. - 1.2 — last-clean stats sensors; dropped Pillow from declared deps.
Use this fork's issue tracker for anything that lives under
custom_components/openneato/. For firmware, frontend, or flash-tool issues, upstream
renjfk/OpenNeato is the right place.
Neato Botvac D3 through D7. D8/D9/D10 are NOT supported (different board, password-locked serial port).
- ESP32-C3, ESP32-S3, or original ESP32 board with 4 MB flash (any dev board with USB and exposed GPIOs)
- Download the latest release from the Releases page
- Flash the ESP32 using the flash tool (auto-detects your chip type):
openneato-flash
- Configure your home WiFi via the serial menu (opens automatically after flashing)
- Wire the ESP32 to your robot's debug port
- Open the web UI at
http://neato.localor the IP shown in the serial monitor
For detailed instructions and troubleshooting, see the User Guide.
Requires Node.js 22+, PlatformIO CLI, and Go 1.26+.
git clone https://github.com/renjfk/OpenNeato.git
cd OpenNeato
# Build frontend (generates web_assets.h)
cd frontend && npm ci && npm run build && cd ..
# Build firmware
pio run -e c3-release
# Build flash tool
cd flash && go build -o openneato-flash . && cd ..OpenNeato is open to contributions and ideas! Whether you're a developer wanting to add features or a user with suggestions, your input is valuable.
The most useful contributions are testing prereleases on your hardware, filing clear bug reports, and opening PRs. If you'd rather just say thanks, there's a Ko-fi. Optional, doesn't influence the roadmap, the project stays free and community-driven either way.
Tip
Before opening an issue, consider starting a Discussion first — many questions, setup troubles, and ideas are easier to resolve through conversation.
Important
For anything beyond a small bug fix, please open an issue or Discussion first to align on the design before writing code. This is an embedded project with tight resource constraints (single-core MCU, 320 KB RAM, 1.6 MB flash per OTA slot) and strict architectural rules (non-blocking event loop, zero external dependencies). A quick design conversation upfront avoids large rewrites during review.
When creating issues, please follow our simple naming convention:
Format: type: brief description
feat:- New features or functionalityfix:- Bug fixesenhance:- Improvements to existing featureschore:- Maintenance tasks, dependencies, cleanupdocs:- Documentation updatesbuild:- Build system, CI/CD changes
feat: add CSV export functionalityfix: app crashes when importing large filesenhance: improve data loading performancechore: update dependencies to latest versionsdocs: update README with installation instructionsbuild: update Xcode project settings
- Use lowercase for the description
- Be specific and actionable
- Keep under 60 characters
- No period at the end
For frontend development without hardware, use the mock API scenarios documented in
docs/mock-scenarios.md. The same ?scenario=... URLs work in local Vite dev and the
Cloudflare demo.
Manual releases via opencode; see RELEASE_PROCESS.md.
Prereleases can be triggered from any PR by commenting /prerelease (collaborators only).
This project is licensed under the MIT License.





