Contributing to ESPHome
This is a guide to contributing to the ESPHome codebase.
Codebase standards
ESPHome's maintainers work hard to maintain a high standard for its code. We try our best to adhere to these standards:
-
The C++ code style is based on the Google C++ Style Guide with a few modifications as follows:
- Function, method and variable names are
lower_snake_case
- Class/struct/enum names should be
UpperCamelCase
- Constants should be
UPPER_SNAKE_CASE
- Fields should be
protected
andlower_snake_case_with_trailing_underscore_
(DO NOT useprivate
) - It's preferred to use long variable/function names over short and non-descriptive ones.
- All uses of class members and member functions should be prefixed with
this->
to distinguish them from global functions/variables. - Use two spaces, not tabs.
- Using
#define
is discouraged and should be replaced with constants or enums (if appropriate). - Use
using type_t = int;
instead oftypedef int type_t;
- Wrap lines in all files at no more than 120 characters. This makes reviewing PRs faster and easier. Exceptions should be made only for lines where wrapping them would result in a syntax issue.
- Function, method and variable names are
-
Components should dump their configuration using
ESP_LOGCONFIG
at startup indump_config()
. - ESPHome uses a unified formatting tool for all source files (but this tool can be difficult to install). When creating a new PR in GitHub, be sure to check the GitHub Actions output to see what formatting needs to be changed and what potential problems are detected.
-
Use of external libraries should be kept to a minimum:
- If the component you're developing has a simple communication interface, please consider implementing the library natively in ESPHome.
- Libraries which directly manipulate pins or don't do any I/O generally do not cause problems.
- Libraries which use hardware interfaces (I²C, for example), should be configured/wrapped to use ESPHome's own communication abstractions.
- If the library accesses a global variable/state (
Wire
is a good example) then there's likely a problem because the component may not be modular. Put another way, this approach may mean that it's not possible to create multiple instances of the component for use within ESPHome.
-
Components must use the provided abstractions like
sensor
,switch
, etc. Components specifically should not directly access other components -- for example, to publish to MQTT topics. - Implementations for new devices should contain reference links for the datasheet and other sample implementations.
- If you have used
delay()
or constructed code which blocks for a duration longer than ten milliseconds, be sure to read a note about delays in code. - Comments in code should be used as appropriate, such as to help explain some complexity or to provide a brief summary of what a class, method, etc. is doing. PRs which include large blocks of commented-out code will not be accepted. Single lines of commented code may be useful from time to time (for example, to call out something which was deliberately omitted for some reason) but should generally be avoided.
- Please test your changes :)
Note
For testing, you can use external components.
Please be sure your work is consistent with the standards outlined above before submitting your work for integration into ESPHome.
Running CI checks locally
You can run the lint and GitHub Actions checks via a docker image:
# Full lint+test suite
docker run --rm -v "${PWD}/":/esphome -it ghcr.io/esphome/esphome-lint script/fulltest
# Run lint only over changed files
docker run --rm -v "${PWD}/":/esphome -it ghcr.io/esphome/esphome-lint script/quicklint
If you are using Windows and have docker installed, the syntax is slightly different.
If you have cloned ESPHome to c:\edev\esphome
the path will be c/edev/esphome
# convert the volume format
$current_dir=(Get-Location).Path.ToLower().Replace(':','').Replace('\','/')
# Run lint only over changed files from powershell
docker run --rm -v "$($current_dir):/esphome" -it ghcr.io/esphome/esphome-lint script/quicklint