agentlytics
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
ESPHome Builder for Desktop
git clone https://github.com/esphome/esphome-desktop.gitesphome/esphome-desktopA cross-platform desktop application that bundles ESPHome with Python and runs the ESPHome Device Builder as a background daemon with system tray integration.
Status: stable and ready for production use. With the 1.0.0 release, the recovery system works the same on every platform (Windows was the last to gain it). Issues and feedback welcome; please check existing issues first, and join the Discord channel for live discussion.
Download the latest release for your platform from desktop.esphome.io, or grab the files directly from the GitHub releases page.
| Platform | Installer |
|---|---|
| macOS (Apple Silicon) | ESPHome.Device.Builder_x.x.x_aarch64.dmg |
| macOS (Intel) | ESPHome.Device.Builder_x.x.x_x64.dmg |
| Windows | ESPHome.Device.Builder_x.x.x_x64-setup.exe |
| Linux (x86_64) | ESPHome.Device.Builder_x.x.x_amd64.AppImage or .deb |
| Linux (aarch64) | ESPHome.Device.Builder_x.x.x_aarch64.AppImage or _arm64.deb |
If you were running a pre-release 0.x version on Windows, do a one-time clean
upgrade: uninstall the app, reboot, then install the latest release. The reboot
ensures no backend process from the old install is left running
(#322). This only
affects Windows; macOS and Linux installs update in place as usual. Your
ESPHome configurations in ~/esphome are not touched by the uninstaller, and the
C:\esphb build data folder is also kept (see
Data Locations), so builds stay warm.
On first launch, ESPHome Device Builder will:
http://localhost:6052This initial setup may take a few minutes depending on your internet connection.
Simply launch ESPHome Device Builder. It will:
Right-click (or left-click on some platforms) the tray icon to access:
The tray menu's actions are also available as esphome-desktop subcommands,
which control the running app (useful over SSH, in scripts, or on Linux
desktops without a tray):
esphome-desktop open # open the dashboard (starts the app if needed) esphome-desktop status # app/backend state, versions, ports, paths (--json for scripts) esphome-desktop update # update the desktop app, ESPHome, and the device builder esphome-desktop restart # restart the dashboard backend esphome-desktop logs # show recent dashboard log output (-f to follow) esphome-desktop release-channel # show the ESPHome channel; pass stable|beta|dev to switch esphome-desktop backend # show the device-builder channel; pass stable|beta to switch esphome-desktop startup # show launch-at-login; pass on|off to change esphome-desktop quit # quit the running app
Unlike the tray's confirmation dialogs, the CLI applies changes immediately;
running the command is the consent. logs and status also work when the app
is not running, and status prints the config and log directory paths.
Running esphome-desktop with no arguments in a terminal prints this command
list instead of launching another app instance; use open to start the app.
The ESPHome Device Builder dashboard (the backend the app runs) can show an
"update available" banner and trigger the update itself through a stable,
versioned JSON interface, separate from the human commands above so their
wording can change without breaking the dashboard. The app sets
ESPHOME_DESKTOP_BIN in the backend's environment, pointing at the CLI to call:
esphome-desktop api version # {"schema_version":1} (no running app needed)
esphome-desktop api check-update # one JSON line: per-component update availability
esphome-desktop api update # trigger the full update; streams JSON, then a terminal line
esphome-desktop api status # status as one JSON object
Every api command prints newline-delimited JSON only, one object per line,
valid JSON even on error ({"type":"err","code":"not_running",...}). Gate on
schema_version before using the others. check-update returns
{"type":"update_check","any_available":bool,"app":{...},"esphome":{...},"device_builder":{...}}
where each component carries available, installed, latest, and error;
route on the type field, which every server reply carries (the one exception
is version, a local handshake that prints only {"schema_version":N}).
update is fully non-interactive; it stops and restarts the backend without any
confirmation, so an unattended remote builder always comes back on its own even
with no one at the keyboard. Trigger it detached (e.g. Python
Popen([...], start_new_session=True)) and poll check-update/status
afterward; the update completes in the app even if the caller is torn down when
the backend restarts. Unlike the human status --json, api commands other
than version require a running app; when it is down they emit
{"type":"err","code":"not_running",...} and exit 3 rather than an offline
summary (the backend only calls the API while the app is up).
On Linux the deb/rpm/AUR packages put esphome-desktop on your PATH. On
macOS the app installs the command on launch, as a small launcher in
/opt/homebrew/bin or /usr/local/bin when one is writable; it removes
itself if you later delete the app. If neither directory is writable, call
the binary inside the bundle
(/Applications/ESPHome Device Builder.app/Contents/MacOS/esphome-desktop).
On Windows run it from the install directory.
To keep a machine acting as an always-on builder, leave Startup → Launch at Login enabled (the default) so the app relaunches after a reboot. It registers a per-user login item (a macOS LaunchAgent, a Windows HKCU\...\Run entry, or a Linux ~/.config/autostart entry), not a system service, so it starts when a desktop session logs in rather than at boot. For an unattended box that reboots on its own, enable your OS's automatic login so a session starts without someone at the keyboard; otherwise the builder stays offline until someone logs in. The login launch is silent (tray only, no browser).
Turn autostart off with Startup → Don't Launch at Login, not the OS's own login-items UI: the app reconciles the login item to its saved preference on every launch, so an entry removed through System Settings → Login Items (macOS), Startup Apps (Windows), or ~/.config/autostart (Linux) is re-created on the next start.
Application data (bundled Python, logs, settings):
| Platform | Location |
|---|---|
| macOS | ~/Library/Application Support/io.esphome.builder/ |
| Windows | %APPDATA%\io.esphome.builder\ |
| Linux | ~/.local/share/io.esphome.builder/ |
This directory contains:
python/ - Bundled Python runtimelogs/ - Application logssettings.json - User preferencesYour ESPHome configuration files are stored at ~/esphome/ on all platforms by default (configurable via config_dir in settings.json).
On Windows, the application itself is installed to %LOCALAPPDATA%\ESPHome Device Builder\.
Windows build data (C:\esphb\<id>\). On Windows the ESPHome Device Builder backend puts its build tree and PlatformIO toolchain under a short folder nested in one C:\esphb parent, C:\esphb\<id>\ (one per config dir), instead of under your profile or config dir. This keeps deep ESP-IDF build paths under Windows' 260-character path limit and clear of spaces in your profile name (e.g. C:\Users\First Last\…), both of which otherwise break the build. This folder is not removed when you uninstall ESPHome Device Builder, so a reinstall keeps the (multi-GB) toolchain warm and avoids a long re-download. If you want to reclaim the disk space, delete the C:\esphb folder by hand after uninstalling. (Only native Windows is affected; running the backend in a Linux container uses the normal data dir.)
libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-devClone the repository:
git clone https://github.com/esphome/esphome-desktop.git cd esphome-desktop
Install Tauri CLI:
cargo install tauri-cli --locked
Download bundled Python for development:
./build-scripts/prepare_bundle.sh
Build:
cargo tauri build
The installer will be in src-tauri/target/release/bundle/.
For development with hot-reload:
cargo tauri dev
The desktop app uses tauri-plugin-updater to check
https://github.com/esphome/esphome-desktop/releases/latest/download/latest.json
for new versions and install them in-place. When a release builds via
.github/workflows/build.yml, every supported installer is signed with the
Tauri Ed25519 key and a latest.json manifest is uploaded to the draft
release. Once the release is published, existing installs pick it up on their
next update check.
Linux .deb / .rpm and AUR installs do not self-update — those users
update through their system package manager.
Settings are stored in settings.json:
{
"port": 6052,
"config_dir": null,
"open_on_start": true,
"launch_at_startup": true,
"check_updates": true
}
port - Dashboard port (default: 6052)config_dir - Custom config directory (null = use default)open_on_start - Open browser when app startslaunch_at_startup - Launch the app automatically at login (default: true; see Running as a remote builder)check_updates - Check for ESPHome updates automaticallyThe app's UI (tray menu, dialogs, and notifications) is translated on Lokalise. Want to help translate the app into your language? Join the project here:
https://app.lokalise.com/public/756339856a4b2415a30c62.63256624/
The UI language is auto-detected from the system locale, falling back to
English per string when a translation is missing. Set the
ESPHOME_DESKTOP_LANGUAGE environment variable (e.g. fr, zh-CN) to
override the detection.
How it works:
src-tauri/translations/en.json is the
in-repo source of truth for the English strings. It is the only committed
translation file; edit it via normal pull requests.main, the
Upload translations workflow
pushes the keys to Lokalise so translators always see the latest strings..github/scripts/translations.py) and
embed them into the binaries, so a new language added on Lokalise ships
with the next release without any code change. Development builds are
English-only.The app is code-signed and notarized. If you still encounter this message (e.g., from a development build), run:
xattr -c "/Applications/ESPHome Device Builder.app"
Then try opening the app again.
dialout group:
sudo usermod -a -G dialout $USERThen log out and back in.
ESPHome uses Git to download external components, remote (github://)
packages, dashboard imports, voice models, and other dependencies, so many
configurations won't compile without it. The app bundles Python but not Git,
so these builds fail on machines without Git installed. Install
Git, then restart the app so it can detect it
(Git is only checked at startup). The app shows a notification at startup when
Git can't be found on your PATH.
The app uses pip to update ESPHome. If updates fail:
The tray needs a working libayatana-appindicator and a desktop that hosts the
StatusNotifier protocol. The AppImage bundles the library and detects it even
when it lives only inside the bundle, so trays now appear on hosts that support
SNI (including KDE Plasma). If your desktop has no StatusNotifier host at all
(some minimal/GNOME setups), the app detects this and falls back to opening the
dashboard in your browser, and update notifications say so instead of pointing at
a tray menu that isn't there.
Without a tray you can still control the app from a terminal — see
Command Line: esphome-desktop update, restart, status,
logs, and the rest cover the tray menu's actions.
If the tray still doesn't appear, install the deb/rpm/AUR package for your
distribution — it depends on the system libayatana-appindicator3 package, so
the tray and in-app updater work wherever a StatusNotifier host is present.
Apache License 2.0 - see LICENSE for details.
Contributions are welcome! Please see the Contributing Guide for details.
more like this
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
Free open-source Cloudflare R2 desktop client and S3 GUI for macOS, Windows, and Linux. Manage Cloudflare R2, AWS S3, M…
search projects, people, and tags