Automated deployment of ESPHome configuration files to devices
Project description
esphome-deployment
A CLI Tool for managing compilation and deployment of ESPHome Device configurations.
Features
- 🚀 Batch Deployment: Deploy to multiple devices with a single command.
- 📦 Smart Caching: Avoids redundant compiles by tracking configuration hashes.
- 🕵️ Change Detection: Only uploads binaries if the resulting build actually changed.
- 🔄 VCS State Tracking: Store deployment metadata in Git to sync state across CI/CD or multiple machines.
- 📋 Detailed Logging: Automatic per-device log capture for easy debugging.
Setup
Prerequisites
- ESPHome CLI must be installed in your
PATH. - A Git repository for your ESPHome device configurations.
Reproducible Builds
For the caching mechanism of esphome-deployment to work across machines, the firmware images produced by ESPHome need to be made reproducible. This means that given the same input, the build process should always produce the exact same output binary, bit for bit. Unfortunately, ESPHome does not currently guarantee reproducible builds out of the box, but there are a couple of options we can add to our configuration YAML to remedy this:
esphome: Section
packages/esphome.yaml
esphome:
platformio_options:
build_flags:
# 1. Prevent warnings for redefining built-in macros
- -Wno-builtin-macro-redefined
# 2. Force a static date and time
- '-D__DATE__="\"Dec 29 2025\""'
- '-D__TIME__="\"23:00:00\""'
See: https://esphome.io/components/esphome/
esp32: Section
packages/chip/esp32-esp-idf.yaml
esp32:
framework:
type: esp-idf
sdkconfig_options:
# options to ensure reproducible builds (exact binary match)
CONFIG_APP_REPRODUCIBLE_BUILD: y
CONFIG_APP_COMPILE_TIME_DATE: n
CONFIG_APP_EXCLUDE_PROJECT_VER_VAR: y
CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR: y
See: https://esphome.io/components/esp32/
[!NOTE] The
esp8266does not support or require any special options for reproducible builds at this time. Only the generalesphomesection as shown above is needed.
Docker
If you prefer to use Docker, you can use the published image ghcr.io/markusressel/esphome-deployment from ghcr. Just ensure you mount your ESPHome configurations path
into it, and run the CLI commands as shown in the usage section below.
docker run --rm -it \
-v ~/esphome-configs:/config \
-u $(id -u):$(id -g) \
-e ESPHOME_DEPLOYMENT_DEPLOYMENT_COORDINATOR_MAX_WORKERS=1 \
ghcr.io/markusressel/esphome-deployment:latest \
esphome-deployment --help
Usage
Usage: esphome-deployment [OPTIONS] COMMAND [ARGS]...
Options:
--version Show the version and exit.
-h, --help Show this message and exit.
Commands:
clean Clean
compile Compile the given deployment(s)
config Print the current configuration
deploy Deploy (compile + upload) the given deployment(s)
upload Upload the given deployment(s)
Configuration
Main configuration
The main configuration for esphome-deployment can be specified in a file named .esphome_deployment.yaml located next to your ESPHome configurations (within your working
directory).
See .esphome_deployment.yaml for all available options.
Per-Device configuration
Each ESPHome configuration can optionally contain a .esphome_deployment: section (note the leading dot) to adjust the deployment behavior for that specific device.
Example:
.esphome_deployment:
deploy: true
tags:
- bluetooth_proxy
packages:
esphome: !include packages/esphome.yaml
chip: !include packages/chip/esp32-esp-idf.yaml
... rest of your esp home config ...
Repository layout, state files and logs
Recommended layout for a repository using esphome-deployment to deploy multiple ESPHome configurations:
<repo-root>/
├── packages/ # reusable package files (recommended)
│ ├── esphome.yaml
│ └── chip/...
├── .deployment-state/ # (generated) per-deployment JSON state files, commit this to VCS to share state across machines
├── .deployment-logs/ # (generated) CLI logs for compile/upload runs
├── .esphome_deployment.yaml # global config for esphome-deployment
├── secrets.yaml
├── your-device-1.yaml # individual deployment yamls (put in repo root)
└── your-device-2.yaml
[!TIP] Commit your state! By committing the
.deployment-state/*.jsonfiles, esphome-deployment will know exactly which devices are already up-to-date even when running it on a different machine.
Add this to your .gitignore:
# Ignore generated log files from esphome-deployment, but keep the deployment state files for VCS tracking
.deployment-logs/
Deployment State
esphome-deployment remembers the compilation and upload state for each deployment configuration in a JSON file within .deployment-state/.
- What they are: For every esphome invocation the CLI writes a log into
.deployment-logs/in the same directory as your device YAMLs. Filenames are like<deployment>_<command>_YYYYMMDD_HHMMSS.logand contain merged stdout/stderr from the esphome CLI. These logs are invaluable for debugging compile/upload issues.
Logs
esphome-deployment captures the output of each esphome CLI invocation and saves it to a log file in .deployment-logs/ for later inspection. This includes both stdout and stderr,
merged together with timestamps for easier debugging.
# list recent logs
ls -lt .deployment-logs/
# view a specific log
less .deployment-logs/<filename>.log
# follow a log in real time
tail -f .deployment-logs/<filename>.log
[!WARNING] Privacy note: Logs may contain IPs, device identifiers or other runtime information. Treat them as potentially sensitive when sharing.
Contributing
Contributions to esphome-deployment are very welcome! If you have an idea for a new feature, found a bug or want to help out with documentation, please open an issue or submit a pull request.
Local Development
### Installation
1. Clone this repository as a git submodule next to your ESPHome configurations
* e.g. `git submodule add https://github.com/markusressel/esphome-deployment`
2. Ensure your configurations utilize the reproducible build options as shown above
* e.g. via packages for both ESPHome and ESP32 devices containing the options above
3. Use `poetry` (or any other method of your choice) to run esphome-deployment (see below)
```bash
python3 -m venv venv
. venv/bin/activate && pip install --upgrade pip poetry
poetry install -P ./esphome-deployment
License
esphome-deployment is licensed under the AGPLv3 License. By using or contributing to this project, you agree to comply with the terms of this license. Please review the LICENSE file for more details.
Project details
Release history Release notifications | RSS feed
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 esphome_deployment-0.1.0.tar.gz.
File metadata
- Download URL: esphome_deployment-0.1.0.tar.gz
- Upload date:
- Size: 33.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e7e350700e06b7ee791322773fbf08c17e841b5ed74fe95b97db32f76537b76
|
|
| MD5 |
556e72fb50468b14423b6fab5501d4ac
|
|
| BLAKE2b-256 |
cfd11b2764a552b040a881d66d61267fad915f023c37ac7aa4e58fd5db5925b8
|
Provenance
The following attestation bundles were made for esphome_deployment-0.1.0.tar.gz:
Publisher:
python_publish.yaml on markusressel/esphome-deployment
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
esphome_deployment-0.1.0.tar.gz -
Subject digest:
3e7e350700e06b7ee791322773fbf08c17e841b5ed74fe95b97db32f76537b76 - Sigstore transparency entry: 1181686082
- Sigstore integration time:
-
Permalink:
markusressel/esphome-deployment@85b284fd04c18d075b7a20948537c000d099e5a8 -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/markusressel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python_publish.yaml@85b284fd04c18d075b7a20948537c000d099e5a8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file esphome_deployment-0.1.0-py3-none-any.whl.
File metadata
- Download URL: esphome_deployment-0.1.0-py3-none-any.whl
- Upload date:
- Size: 36.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00090d8bcecca8060e8f84e0901a1de7c853d32c0f958cdffbcb934643a9dbc7
|
|
| MD5 |
e97294bdb5c95cbcbb34be66d07a6676
|
|
| BLAKE2b-256 |
7c8d704b0bf47bdfd902b434268f0a9cf13787c8eb0bfc5799626b6f1ddd0763
|
Provenance
The following attestation bundles were made for esphome_deployment-0.1.0-py3-none-any.whl:
Publisher:
python_publish.yaml on markusressel/esphome-deployment
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
esphome_deployment-0.1.0-py3-none-any.whl -
Subject digest:
00090d8bcecca8060e8f84e0901a1de7c853d32c0f958cdffbcb934643a9dbc7 - Sigstore transparency entry: 1181686085
- Sigstore integration time:
-
Permalink:
markusressel/esphome-deployment@85b284fd04c18d075b7a20948537c000d099e5a8 -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/markusressel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python_publish.yaml@85b284fd04c18d075b7a20948537c000d099e5a8 -
Trigger Event:
push
-
Statement type: