Update contributing.rst (#4188)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Keith Burzinski 2024-08-26 21:23:56 -05:00 committed by GitHub
parent a963bb3e60
commit b6cacbdbbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 375 additions and 234 deletions

View File

@ -5,75 +5,80 @@ Contributing
:description: Getting started guide for contributing to the ESPHome project :description: Getting started guide for contributing to the ESPHome project
:image: github-circle.svg :image: github-circle.svg
Contributions to the ESPHome suite are very welcome! All the code for the projects We welcome contributions to the ESPHome suite of code and documentation! All of the project code is hosted on GitHub:
is hosted on GitHub and you can find the sources here:
- `ESPHome <https://github.com/esphome/esphome>`__ (Project Source Code) - `ESPHome <https://github.com/esphome/esphome>`__ (Project Source Code)
- `ESPHome-Docs <https://github.com/esphome/esphome-docs>`__ (The documentation which you're reading here) - `ESPHome-Docs <https://github.com/esphome/esphome-docs>`__ (The documentation which you're reading here)
Just clone the repository locally, do the changes for your new feature/bug fix and submit Just clone the repository locally, make changes for your new feature/bug fix and submit a pull request.
a pull request.
Contributing to ESPHome-Docs Contributing to ``esphome-docs``
---------------------------- --------------------------------
.. image:: /images/logo-docs.svg .. image:: /images/logo-docs.svg
:align: center :align: center
:width: 60.0% :width: 60.0%
:class: dark-invert :class: dark-invert
One of the areas of ESPHome that can always be improved is the documentation. Our documentation can always be improved. We rely on contributions from our users to do so. If you notice an issue (for
If you see an issue somewhere, a spelling mistakes or if you want to share your awesome example, spelling/grammar mistakes) or if you want to share your awesome new setup, we encourage you to submit a pull
setup, please feel free to submit a pull request. request (PR).
The ESPHome documentation is built using `sphinx <http://www.sphinx-doc.org/>`__ and uses The ESPHome documentation is built using `sphinx <http://www.sphinx-doc.org/>`__ and uses
`reStructuredText <http://docutils.sourceforge.net/rst.html>`__ for all source files. `reStructuredText <http://docutils.sourceforge.net/rst.html>`__ for all source (``.rst``) files.
If you're not familiar with writing rST, see :ref:`rst-syntax` for a quick refresher. If you're not familiar with rST, see :ref:`rst-syntax` for a quick refresher.
Through Github Through GitHub
************** **************
This guide essentially goes over the same material found in `GitHub's Editing files in another user's repository <https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files#editing-files-in-another-users-repository>`__. You may also find that page helpful to read. This guide essentially goes over the same material found in
`GitHub's Editing files in another user's repository <https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files#editing-files-in-another-users-repository>`__.
You may also find that page helpful to read.
At the bottom of each page in the docs, there is a "Edit this page on GitHub" link. Click this link and you'll see something like this: At the bottom of each page in the docs, there is a "Edit this page on GitHub" link. Click this link and you'll see
something like this:
.. figure:: images/docs_ghedit_1.png .. figure:: images/docs_ghedit_1.png
:align: center :align: center
:width: 80.0% :width: 80.0%
:alt: a screenshot of an rST file opened in GitHub, with the edit button circled :alt: a screenshot of an rST file opened in GitHub, with the edit button circled
Click the edit button to start making changes. If you're not sure about some element of syntax, see the quick-start :ref:`rst-syntax` guide. Click the edit button to start making changes. If you're unsure about syntax, see the quick-start :ref:`rst-syntax`
guide.
Once you've made your changes, give them a useful name and press "Propose changes". At this point, you've made the changes on your own personal copy of the docs in GitHub, but you still need to submit them to us. Once you've made your changes, give them a useful name and press "Propose changes". At this point, you've made the
changes on your own personal copy of the docs in GitHub, but you still need to submit them to us.
.. figure:: images/docs_ghedit_2.png .. figure:: images/docs_ghedit_2.png
:align: center :align: center
:width: 80.0% :width: 80.0%
:alt: the commit creation screen in GitHub, with the commit title and "Propose changes" button circled :alt: the commit creation screen in GitHub, with the commit title and "Propose changes" button circled
To do that, you need to create a "Pull request": To do that, you need to create a "Pull request" (PR):
.. figure:: images/docs_ghedit_3.png .. figure:: images/docs_ghedit_3.png
:align: center :align: center
:width: 80.0% :width: 80.0%
:alt: the pull request prompt screen in GitHub with the "Create pull request" button circled :alt: the pull request prompt screen in GitHub with the "Create pull request" button circled
Fill out the new pull request form, replacing the ``[ ]`` with ``[x]`` to indicate that you have followed the instructions. Fill out the new pull request form, replacing the ``[ ]`` with ``[x]`` to indicate that you have followed the
instructions.
.. figure:: images/docs_ghedit_4.png .. figure:: images/docs_ghedit_4.png
:align: center :align: center
:width: 80.0% :width: 80.0%
:alt: the pull request creation screen in GitHub with the "Create pull request" button circled :alt: the pull request creation screen in GitHub with the "Create pull request" button circled
After waiting a while, you might see a green or a red mark next to your commit in your pull request: After a few minutes, you'll see either a green check ✅ or a red ❌ next to your commit in your pull request:
.. figure:: images/docs_ghedit_ci_failed.png .. figure:: images/docs_ghedit_ci_failed.png
:align: center :align: center
:width: 80.0% :width: 80.0%
:alt: the pull request with a commit with a red x next to it :alt: the pull request with a commit with a red x next to it
This means that there is some error stopping your pull request from being fully processed. Click on the X, click on "Details" next to the lint step, and look and see what's causing your change to fail. If you see the red ❌, there is at least one error preventing your pull request from being fully processed. Click on the
❌, then click on "Details" next to the lint step to determine what's wrong with your change(s).
.. figure:: images/docs_ghedit_ci_details.png .. figure:: images/docs_ghedit_ci_details.png
:align: center :align: center
@ -85,9 +90,15 @@ This means that there is some error stopping your pull request from being fully
:width: 80.0% :width: 80.0%
:alt: log messages showing reason for failed build :alt: log messages showing reason for failed build
For example, in this case, you'd want to go to line 136 of ``pzemac.rst`` and adjust the number of ``===`` so that it completely underlines the section heading. In this example, you need to go to line 136 of ``pzemac.rst`` and adjust the number of ``===`` so that it completely
underlines the section heading.
Once you make that change, the pull request will be built again, and hopefully this time where will be no other errors. Once you make that change, the pull request will be tested & built again; ideally, this time where will be no remaining
errors. If, however, more errors are discovered, simply repeat the process above to correct them.
.. note::
All tests must be passing before we will review (and merge) a pull request.
Build Build
***** *****
@ -100,7 +111,8 @@ Build
docker run --rm -v "${PWD}/":/workspaces/esphome-docs -p 8000:8000 -it ghcr.io/esphome/esphome-docs docker run --rm -v "${PWD}/":/workspaces/esphome-docs -p 8000:8000 -it ghcr.io/esphome/esphome-docs
With ``PWD`` referring to the root of the ``esphome-docs`` git repository. Then go to ``<CONTAINER_IP>:8000`` in your browser. ...with ``PWD`` referring to the root of the ``esphome-docs`` git repository. Then, to see the preview, go to
``<HOST_IP>:8000`` in your browser.
This way, you don't have to install the dependencies to build the documentation. This way, you don't have to install the dependencies to build the documentation.
@ -121,16 +133,21 @@ Then, use the provided Makefile to build the changes and start a live-updating w
Notes Notes
***** *****
Some notes about the docs:
- Use the English language (duh...) - Use the English language (duh...)
- An image tells a thousand words, please use them wherever possible. But also don't forget to shrink them, for example - An image is worth a thousand words, please use them wherever possible. Regardless, don't forget to optimize them so
I often use https://tinypng.com/ pages load quickly; you might try using https://tinypng.com/
- Try to use examples as often as possible (also while it's great to use highly accurate, - Try to use examples as often as possible.
and domain-specific lingo, it should not interfere with new users understanding the content) - When using highly accurate, domain-specific terminology, be sure that it does not interfere with new users
- Fixes/improvements for the docs themselves should go to the ``current`` branch of the understanding the content.
esphomedocs repository. New features should be added against the ``next`` branch. - Be sure to target the correct **base branch** of the ``esphome-docs`` repository:
- Always create new branches in your fork for each pull request.
- **Fixes/improvements** for documentation must target the ``current`` branch.
- **New features** must target the ``next`` branch.
- **Create new branches in your fork** for each pull request; to avoid confusion (and other potential issues), do not
make changes directly in the ``next`` and ``current`` branches in your fork.
- 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 which contain long links or other specific content/metadata that cannot be wrapped.
.. _rst-syntax: .. _rst-syntax:
@ -139,10 +156,12 @@ Syntax
Here's a quick RST primer: Here's a quick RST primer:
Title hierarchy is based on order of occurrence, not on type of character used to underline it. This Title hierarchy is based on order of occurrence, not on type of character used to underline it. For consistency, we
documents establish the following character order for better consistency. adhere to the following order:
- **Headers**: You can write titles like this: - **Headers**:
You can write titles like this:
.. code-block:: rst .. code-block:: rst
@ -153,20 +172,27 @@ documents establish the following character order for better consistency.
.. code-block:: rst .. code-block:: rst
My Sub Section My Section
-------------- ----------
and sub-section headers like this: and sub-section headers like this:
.. code-block:: rst .. code-block:: rst
My Sub-sub Section My Sub-section
****************** **************
and sub-sub-section headers like this:
.. code-block:: rst
My Sub-sub-section
^^^^^^^^^^^^^^^^^^
.. note:: .. note::
The length of the bar below the text **must** match the title Text length. - The length of the bar below the text **must** match the title text length.
Also, titles should be in Title Case - Section titles should use Title Case.
- **Links**: To create a link to an external resource (for example https://www.google.com), use - **Links**: To create a link to an external resource (for example https://www.google.com), use
``\`Link text <link_url>\`__``. For example: ``\`Link text <link_url>\`__``. For example:
@ -177,8 +203,8 @@ documents establish the following character order for better consistency.
`Google.com <https://www.google.com>`__ `Google.com <https://www.google.com>`__
- **References**: To reference another document, use the ``:doc:`` and ``:ref:`` roles (references - **References**: To reference another document, use the ``:doc:`` and ``:ref:`` roles (references are set up globally
are set up globally and can be used between documents): and can be used between documents):
.. code-block:: rst .. code-block:: rst
@ -223,9 +249,9 @@ documents establish the following character order for better consistency.
.. note:: .. note::
Please note the empty line after the ``code-block`` directive. That is necessary. Note that a blank line is *required* after every ``code-block`` directive.
- **Images**: To show images, use the ``figure`` directive: - **Images**: Use the ``figure`` directive to display an image:
.. code-block:: rst .. code-block:: rst
@ -243,13 +269,11 @@ documents establish the following character order for better consistency.
.. note:: .. note::
All images in the documentation need to be as small as possible to ensure All images in the documentation need to be as small as possible to minimize page load times. Typically, the
fast page load times. For normal figures the maximum size should be at most maximum size should be roughly 1000x800 px or so. Additionally, please use online tools like
about 1000x800 px or so. Additionally, please use online tools like
https://tinypng.com/ or https://tinyjpg.com/ to further compress images. https://tinypng.com/ or https://tinyjpg.com/ to further compress images.
- **Notes and warnings**: You can create simple notes and warnings using the ``note`` and ``warning`` - **Notes and warnings**: You can create simple notes and warnings using the ``note`` and ``warning`` directives:
directives:
.. code-block:: rst .. code-block:: rst
@ -305,28 +329,30 @@ documents establish the following character order for better consistency.
2. Ordered item #2 2. Ordered item #2
- **imgtable**: ESPHome uses a custom RST directive to show the table on the front page (see - **imgtable**: ESPHome uses a custom RST directive to show the table on the front page (see
`index.rst <https://github.com/esphome/esphome-docs/blob/current/index.rst>`__). `index.rst <https://github.com/esphome/esphome-docs/blob/current/index.rst>`__). New pages need to be added to the
New pages need to be added to the ``imgtable`` list. The syntax is CSV with <PAGE NAME>, <FILE NAME> (without RST), ``imgtable`` list. The syntax is CSV with <PAGE NAME>, <FILE NAME> (without RST), <IMAGE> (in the top-level
<IMAGE> (in top-level images/ directory), <COMMENT> (optional - short text to describe the component). The aspect ratio of these images should be 8:10 (or 10:8) but exceptions are possible. ``images/`` directory), <COMMENT> (optional; short text to describe the component). The aspect ratio of these images
should be 8:10 (or 10:8) but exceptions are possible.
Because these images are served on the main page, they need to be compressed heavily. SVGs are preferred over JPGs Because these images are served on the main page, they need to be compressed heavily. SVGs are preferred over JPGs
and JPGs should be max. 300x300px. and JPGs should be no more than 300x300px.
If you have imagemagick installed, you can use this command to convert the thumbnail: If you have imagemagick installed, you can use this command to convert the thumbnail:
.. code-block:: bash .. code-block:: bash
convert -sampling-factor 4:2:0 -strip -interlace Plane -quality 80% -resize 300x300 in.jpg out.jpg convert -sampling-factor 4:2:0 -strip -interlace Plane -quality 80% -resize 300x300 in.jpg out.jpg
reStructured text can do a lot more than this, so if you're looking for a more complete guide reStructured text can do a lot more than this; if you're looking for a more complete guide, please have a look at the
please have a look at the `Sphinx reStructuredText Primer <http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`__. `Sphinx reStructuredText Primer <http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`__.
.. _setup_dev_env: .. _setup_dev_env:
Setting Up Development Environment Setting Up a Development Environment
---------------------------------- ------------------------------------
For developing new features to ESPHome, you will first need to set up a development environment. If you want to develop (a) new feature(s) for ESPHome, you need to set up a development environment.
This is only possible for ``pip`` installs. Note that ``pip`` must be installed before running the ``setup`` script.
.. code-block:: bash .. code-block:: bash
@ -341,31 +367,29 @@ This is only possible for ``pip`` installs.
git checkout -b my-new-feature git checkout -b my-new-feature
cd .. cd ..
The environment is now ready for use, but you need to activate the Python virtual environment The environment is now ready for use, but you'll need to activate the Python virtual environment (venv) each time you
every time you are using it. wish to use it.
.. code-block:: bash .. code-block:: bash
# Activate venv # Activate venv
source venv/bin/activate source venv/bin/activate
Now you can open ESPHome in your IDE of choice (mine is CLion) with the PlatformIO Now you can open ESPHome in your IDE of choice (many of us are using `VSCode <https://code.visualstudio.com/download>`__)
addons (see PlatformIO docs for more info). Then develop the new feature with the with the PlatformIO addons (see PlatformIO docs for more info) and develop the new feature with the guidelines below.
guidelines below.
All PRs are automatically checked for some basic formatting/code mistakes with Github Actions. All PRs are automatically checked and tested for some common formatting/code errors with Github Actions. *These checks*
These checks *must* pass for your PR to be mergeable. **must all pass** *before we will review (and eventually merge) your PR.*
Setting Up Git Environment Setting Up Git Environment
-------------------------- --------------------------
ESPHome's code-base is hosted on GitHub, and contributing is done exclusively through ESPHome's codebase is hosted on GitHub; contributing is done exclusively through "Pull Requests" (PRs) in the
"Pull Requests" (PRs) in the GitHub interface. So you need to set up your git environment GitHub interface. To use this, you need to set up your ``git`` environment first.
first.
When you want to create a patch for ESPHome, first go to the repository you want to contribute to If you want to contribute changes/fixes you've made back to ESPHome, first, go to the repository you want to contribute
(esphome, etc) and click fork in the top right corner. This will create to (``esphome``, for example) and click "fork" in the top right corner. This will create a fork of the repository that
a fork of the repository that you can modify and create git branches on. you can modify and create branches on.
.. code-block:: bash .. code-block:: bash
@ -396,27 +420,155 @@ a fork of the repository that you can modify and create git branches on.
git push -u origin <BRANCH_NAME> git push -u origin <BRANCH_NAME>
# For example: git push -u origin gpio-switch-fix # For example: git push -u origin gpio-switch-fix
Then go to your repository fork in GitHub and wait for a create pull request message to show Submitting a Pull Request
up in the top (alternatively go to branches and create it from there). Fill out the *************************
Pull Request template outlining your changes; if your PR is not ready to merge yet please
mark it as a draft PR in the dropdown of the green "create PR" button.
**Review Process:** ESPHome's code base tries to have a high code standard. At the bottom After you have pushed your changes to GitHub, go to your repository fork and look for a "create pull request" button
of the Pull Request you will be able to see the "Github Actions" continuous integration check which near the top of the page (or, alternatively, go to branches and create it from there). As you create the PR:
will automatically go through your patch and try to spot errors. If the CI check fails,
please see the Github Actions log and fix all errors that appear there. Only PRs that pass the automated
checks can be merged!
**Catching up with reality**: Sometimes other commits have been made to the same files - Complete the Pull Request template:
you edited. Then your changes need to be re-applied on top of the latest changes with
a "rebase". More info `here <https://developers.home-assistant.io/docs/en/development_catching_up.html>`__. - Include a brief (but complete) summary of your changes.
- PRs without a descrption/summary of the changes will not be reviewed or merged, although exceptions may
occasionally be made for small PRs and/or PRs made by frequent contributors/maintainers.
- **Do not delete the template.**
- **Mark your PR as a draft** if it is not ready to be reviewed or merged yet. Your PR should be considered a draft if:
- You still plan to make more changes to the code/documentation.
- Changes have been requested to the PR but you have not completed making (or discussing) the requested changes yet.
- You are waiting on feedback from the community and/or maintainers to complete your changes.
This lets reviewers know that the PR may continue to change so they will not spend valuable time reviewing it until
it is ready. We do this because, if a PR is reviewed and then it changes, it must be re-reviewed. Reviewing a single
PR multiple times is not a productive use of time and we try as much as possible to avoid doing so.
Review Process
**************
ESPHome's maintainers work hard to maintain a high standard for its code, so reviews can take some time. At the bottom
of each pull request you will see the "Github Actions" continuous integration (CI) checks which will automatically
analyize all code changed in your branch. These checks try to spot (and suggest corrections for) errors. If any CI
check fails, please look at the Github Actions log and fix all errors that appear there.
**All automated checks must be passing** before a given PR will be reviewed and (eventually) merged!
**When will my PR be reviewed/merged?**
ESPHome is a big project; we encourage everybody to test, review and comment on PRs. Despite this, reviews can (and
often do) take some time.
**But howwww looonnnggg???**
Small PRs are easier to review and are often reviewed first. If you want your PR to be reviewed (and merged) quickly,
here are some tips:
- We would rather review ten ten-line PRs than one 100-line PR.
- Be sure to follow all :ref:`codebase_standards` as you make changes -- when reviewers have to spend time
commenting on/correcting your PR because you didn't name variables correctly or didn't prefix member variable
accesses with ``this->``, it wastes time we could be using to review other PRs which *do* follow the standards.
- If you wish to take on a big project, such as refactoring a substantial section of the codebase or integrating
another open source project with ESPHome, please discuss this with us on `Discord <https://discord.gg/KhAMKrd>`__ or
`create a discussion on GitHub <https://github.com/esphome/esphome/discussions>`__ **before** you do all the work and
attempt to submit a massive PR.
- While we realize it's not *always* possible, avoid submitting PRs which are thousands of lines in size. Such PRs are
simply too complex and take excessive amounts of time to review. Break your work into multiple, smaller PRs to make
the changes more tenable for reviewers.
- If you are not sure about how you should proceed with some changes, **please**
`discuss it with us on Discord <https://discord.gg/KhAMKrd>`__ before you go do a bunch of work that we can't (for
whatever reason) accept...and then you have to go back and re-do it all to get your PR merged. It's easier to make
corrections early-on -- and we want to help you!
.. _prs-are-being-drafted-when-changes-are-needed:
Why Was My PR was Marked as a Draft?
************************************
If your PR was reviewed and changes were requested, our bot will automatically mark your PR as a draft. This means that
the PR is not ready to be merged or further reviewed for the moment.
When a PR is marked as a draft, it tells other reviewers that this particular PR is a work-in-progress and it doesn't
require their attention yet.
Once you have made the requested changes, you can mark the PR as ready for review again by clicking the "Ready for
review" button:
.. figure:: images/pr-draft-ready.png
:align: center
:width: 100.0%
:alt: The ready for review button in the bottom of a PR in draft mode
Before you click the "Ready for review" button, ensure that:
- You have addressed all requested changes
- There are no merge conflicts
- All CI jobs and checks are passing successfully
Once you've clicked the "Ready for review" button, the PR will return to a normal state again and our bot will
automatically notify the reviewers who requested the changes that the PR is ready to go!
Updating Your Branches
**********************
Sometimes you'll want (or need) to bring changes that were made in ESPHome's ``dev`` branch back into your (local copy
of a) branch.
The examples that follow in this section assume that you have:
- already used ``git remote`` to add ``upstream`` as shown earlier, and
- your feature branch (the branch from which you created your PR) currently checked out
.. _feature_branches:
Feature Branches
^^^^^^^^^^^^^^^^
There are a couple of ways you can update your (local) feature branch. The easiest is by clicking the "Update branch"
button in GitHub:
.. image:: images/update_branch.png
:align: center
:width: 80.0%
:class: light-invert
...then run ``git pull`` to pull these changes back down from GitHub.
If you prefer to do it the command-line/terminal way, you can do this, instead:
.. code-block:: bash .. code-block:: bash
# Fetch the latest upstream changes and apply them # Fetch the latest upstream changes
git fetch upstream dev
# Merge in the changes we fetched above
git merge upstream/dev
Your Local Copy of ``dev``
^^^^^^^^^^^^^^^^^^^^^^^^^^
As you create new branches for your work, you'll want to be sure they include all of the latest changes from ESPHome's
``dev`` branch -- it's not a good practice to create a new feature branch from an outdated ``dev`` branch.
For this reason, you'll periodically want to update your local ``dev`` branch. A more detailed explanation can be found
`here <https://developers.home-assistant.io/docs/en/development_catching_up.html>`__, but here's the TL;DR:
.. code-block:: bash
# Fetch the latest upstream changes
git fetch upstream dev git fetch upstream dev
git rebase upstream/dev git rebase upstream/dev
Note that you can use this procedure for other branches, too, such as ``next`` or ``current`` from ``esphome-docs``.
.. warning::
Using ``git rebase`` will result in your changes having to be *force-pushed* back up to GitHub.
**Do not force-push** your branch once your PR is being reviewed; GitHub allows reviewers to mark files a "viewed"
and, when you force-push, this history **is lost**, forcing your reviewer to re-review files they may have already
reviewed!
If you must update your branch, use a method described in :ref:`feature_branches`, instead.
.. _contributing_to_esphome: .. _contributing_to_esphome:
Contributing to ESPHome Contributing to ESPHome
@ -427,21 +579,19 @@ Contributing to ESPHome
:width: 60.0% :width: 60.0%
:class: dark-invert :class: dark-invert
This is a guide to contributing to the ESPHome codebase. ESPHome uses two languages for its project: This is a guide to contributing to the ESPHome codebase. ESPHome uses two languages for its project: Python and C++.
Python and C++.
The user configuration is read, validated and transformed into a custom firmware The Python side of ESPHome reads a YAML configuration file, validates it and transforms it into a custom firmware which
with the Python side of the firmware. includes only the code needed to perform as defined in the configuration file.
The C++ codebase is what's actually running on the ESP and called the "runtime". This part of The C++ part of the codebase is what's actually running on the microcontroller; this is called the "runtime". This part
the codebase should first set up the communication interface to a sensor/component/etc. and of the codebase should first set up the communication interface to a sensor/component/etc. and then communicate with
communicate with the ESPHome core via the defined interfaces (like Sensor, BinarySensor, Switch). the ESPHome core via the defined interfaces (like ``Sensor``, ``BinarySensor`` and ``Switch``, among others).
1. Directory Structure Directory Structure
********************** *******************
After you've :ref:`set up development environment <setup_dev_env>`, you will have a folder structure After you've :ref:`set up a development environment <setup_dev_env>`, you will have a folder structure like this:
like this:
.. code-block:: text .. code-block:: text
@ -464,10 +614,10 @@ like this:
│ │ ├── switch.py │ │ ├── switch.py
│ ... │ ...
As you can see, all components are in the "components" folder. Each component is in its own All components are in the "components" folder. Each component is in its own subfolder which contains the Python code
subfolder which contains the Python code (.py) and the C++ code (.h and .cpp). (``.py``) and the C++ code (``.h`` and ``.cpp``).
Suppose the user types in the following: Consider a YAML configuration file containing the following:
.. code-block:: yaml .. code-block:: yaml
@ -476,34 +626,35 @@ Suppose the user types in the following:
sensor: sensor:
- platform: hello2 - platform: hello2
In both cases, ESPHome will automatically look for corresponding entries in the "components" In both cases, ESPHome will automatically look for corresponding entries in the "components" folder and find the
folder and find the directory with the given name. For example the first entry will make ESPHome directory with the given name. In this example, the first entry causes ESPHome to look for the
look at the ``esphome/components/hello1/__init__.py`` file and the second entry will result in ``esphome/components/hello1/__init__.py`` file and the second entry tells ESPHome to look for
``esphome/components/hello2/sensor.py``. ``esphome/components/hello2/sensor.py`` or ``esphome/components/hello2/sensor/__init__.py``.
Let's leave what's written in those files for (2.), but for now you should also know that Let's leave what's written in those files for :ref:`the next section <config_validation>`, but for now you should also
whenever a component is loaded, all the C++ source files in the folder of the component know that, whenever a component is loaded, all the C++ source files in the folder of the component are automatically
are automatically copied into the generated PlatformIO project. So you just need to add the C++ copied into the generated PlatformIO project. All you need to do is add the C++ source files in the component's folder
source files in the folder and the ESPHome core will copy them with no additional code required and the ESPHome core will copy them with no additional code required by the component developer.
by the component developer.
.. note:: .. note::
For testing you can use :doc:`/components/external_components`. For testing, you can use :doc:`/components/external_components`.
ESPHome also has a ``custom_components`` mechanism like `Home Assistant does ESPHome also has a ``custom_components`` mechanism like `Home Assistant does
<https://developers.home-assistant.io/docs/creating_component_index>`__. However this is <https://developers.home-assistant.io/docs/creating_component_index>`__. Note, however, that
discouraged in favor of :doc:`/components/external_components`. **custom componenets are deprecated** in favor of :doc:`/components/external_components`.
2. Config Validation .. _config_validation:
********************
The first thing ESPHome does is read and validate the user config. For this ESPHome has a powerful Config Validation
"config validation" mechanism. Each component defines a config schema that is validated against *****************
the user config.
To do this, all ESPHome Python modules that can be configured by the user have a special field The first task ESPHome performs is to read and validate the provided YAML configuration file. ESPHome has a powerful
called ``CONFIG_SCHEMA``. An example of such a schema is shown below: "config validation" mechanism for this purpose. Each component defines a config schema which is used to validate the
provided configuration file.
To do this, all ESPHome Python modules that can be configured by the user define a special variable named
``CONFIG_SCHEMA``. An example of such a schema is shown below:
.. code-block:: python .. code-block:: python
@ -517,70 +668,71 @@ called ``CONFIG_SCHEMA``. An example of such a schema is shown below:
cv.Optional(CONF_MY_OPTIONAL_KEY, default=10): cv.int_, cv.Optional(CONF_MY_OPTIONAL_KEY, default=10): cv.int_,
}).extend(cv.COMPONENT_SCHEMA) }).extend(cv.COMPONENT_SCHEMA)
This variable is automatically loaded by the ESPHome core and validated against. This variable is automatically loaded by the ESPHome core and is used to validate the provided configuration.
The underlying system ESPHome uses for this is `voluptuous <https://github.com/alecthomas/voluptuous>`__. The underlying system ESPHome uses for this is `voluptuous <https://github.com/alecthomas/voluptuous>`__.
Going into how to validate is out of scope for this guide, but the best way to learn is to look How validation works is out of scope for this guide; the easiest way to learn is to look at how similar components
at examples of how similar components validate user input. validate user input.
A few point on validation: A few notes on validation:
- ESPHome puts a lot of effort into **strict validation** - If possible, all validation methods should be as strict - ESPHome puts a lot of effort into **strict validation**. All validation methods should be as strict as possible and
as possible and detect wrong user input at the validation stage (and not later). detect incorrect user input at the validation stage, mitigating compiler warnings and/or errors.
- All default values should be defined in the schema (and not in C++ codebase or other code parts). - All default values should be defined in the schema -- not in C++ codebase.
- Config keys should be descriptive - If the meaning of a key is not immediately obvious you should - Prefer naming configuration keys in a way which is descriptive instead of short. Put another way, if the meaning of a
always prefer long_but_descriptive_keys. key is not immediately obvious, don't be afraid to use ``long_but_descriptive_keys``. There is no reason to use
obscure shorthand. As an example, ``scrn_btn_inpt`` is indeed shorter but more difficult to understand, particularly
for new users; avoid naming keys and variables in this way.
3. Code Generation Code Generation
****************** ***************
After the user input has been successfully validated, the last step of the Python codebase The last step the Python codebase performs is called *code generation*. This runs only after the user input has been
is called: Code generation. successfully validated.
As you may know, ESPHome converts the user's configuration into C++ code (you can see the generated As you may know, ESPHome "converts" the user's YAML configuration into C++ code (you can see the generated code under
code under ``<NODE_NAME>/src/main.cpp``). Each component must define its own ``to_code`` method ``<NODE_NAME>/src/main.cpp``). Each component must define its own ``to_code`` method that "converts" the user input to
that converts the user input to C++ code. C++ code.
This method is also automatically loaded and invoked by the ESPHome core. An example of This method is also automatically loaded and invoked by the ESPHome core. Here's an example of such a method:
such a method can be seen below:
.. code-block:: python .. code-block:: python
import esphome.codegen as cg import esphome.codegen as cg
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var) await cg.register_component(var, config)
cg.add(var.set_my_required_key(config[CONF_MY_REQUIRED_KEY])) cg.add(var.set_my_required_key(config[CONF_MY_REQUIRED_KEY]))
Again, going into all the details of ESPHome code generation would be out-of-scope. However, The details of ESPHome code generation is out-of-scope for this document. However, ESPHome's code generation is 99%
ESPHome's code generation is 99% syntactic sugar - and again it's probably best to study other syntactic sugar - and (again) it's probably best to study similar components and just copy what they do.
components and just copy what they do.
There's one important concept for the ``to_code`` method: coroutines with ``yield``. There's one important concept for the ``to_code`` method: coroutines with ``await``.
First the problem that leads to coroutines: In ESPHome, components can declare (via ``cg.Pvariable``) and access variables
(``cg.get_variable()``) - but sometimes when one part of the code base requests a variable
it has not been declared yet because the code for the component creating the variable has not run yet.
To allow for ID references, ESPHome uses so-called ``coroutines``. When you see a ``yield`` statement The problem that necessitates coroutines is this: in ESPHome, components can declare (via ``cg.Pvariable``) and access
in a ``to_code`` method, ESPHome will call the provided method - and if that method needs to wait variables (``cg.get_variable()``) -- but sometimes, when one part of the codebase requests a variable, it has not been
for a variable to be declared first, ``yield`` will wait until that variable has been declared. declared yet because the code for the component creating the variable has not yet run.
After that, ``yield`` returns and the method will execute on the next line.
Next, there's a special method - ``cg.add`` - that you will often use. ``cg.add()`` does a very simple To allow for ID references, ESPHome uses so-called ``coroutines``. When you see an ``await`` statement in a ``to_code``
thing: Any C++ declared in the parentheses of ``cg.add()`` will be added to the generated code. method, ESPHome will call the provided method and, if that method needs to wait for a variable to be declared first,
If you do not call "add" a piece of code explicitly, it will not be added to the main.cpp file! ``await`` will wait until that variable has been declared. After that, ``await`` returns and the method will execute on
the next line.
4. Runtime Next, there's a special method - ``cg.add`` - that you will often use. ``cg.add()`` performs a very simple task: Any
********** C++ declared in the parentheses of ``cg.add()`` will be added to the generated code. Note that, if you do not call
"add" to insert a piece of code explicitly, it will not be added to the ``main.cpp`` file!
Okay, the Python part of the codebase is now complete - now let's talk about the C++ part of Runtime
creating a new component. *******
The two major parts of any component roughly are: At this point, the Python part of the codebase has completed its work. Let's move on and discuss the C++ part of
components.
- Setup Phase Most components consist of two primary parts/steps:
- Run Phase
- Setup Phase
- Run Phase
When you create a new component, your new component will inherit from :apiclass:`Component`. When you create a new component, your new component will inherit from :apiclass:`Component`.
That class has a special ``setup()`` method that will be called once to set up the component - That class has a special ``setup()`` method that will be called once to set up the component -
@ -592,83 +744,96 @@ if communication works (if not, it should call ``mark_failed()``).
Again, look at examples of other components to learn more. Again, look at examples of other components to learn more.
The next thing that will be called with your component is ``loop()`` (or ``update()`` for a The next method that will be called with your component is ``loop()`` (or ``update()`` for a
:apiclass:`PollingComponent`). In these methods you should retrieve the latest data from the :apiclass:`PollingComponent`). These methods should retrieve the latest data from your component and publish them with
component and publish them with the provided methods. One thing to note in these methods the provided methods.
is that anything in ``loop()`` or ``setup()`` **should not block**. Specifically methods like
``delay(10)`` should be avoided and delays above ~10ms are not permitted. The reason for this
is that ESPHome uses a central single-threaded loop for all components - if your component
blocks the whole loop will be slowed down.
Finally, your component should have a ``dump_config`` method that prints the user configuration. Finally, your component must have a ``dump_config`` method that prints the complete user configuration.
5. Extras A Note About Delays in Code
********* ***************************
**Code in** ``loop()``, ``update()`` **and** ``setup()`` **must not block**.
Methods like ``delay()`` should be avoided and **delays longer than 10 ms are not permitted**. Because ESPHome uses a
single-threaded loop for all components, if your component blocks, it will delay the whole loop, negatively impacting
other components. This can result in a variety of problems such as network connections being lost.
If your code **must** wait for something to happen (for example, your sensor requires hundreds of milliseconds to
initialize and/or take a reading), then you'll need to implement a state machine to facilitate this. For example, your
code can send the "take reading" command, return, and, when the next iteration of ``loop()`` or ``update()`` is called,
it then attempts to read back the measurement from the sensor.
``loop()`` is called every 16 ms (assuming no other components delay this, which may happen from time to time) and
``update()`` is called at an interval defined in the user configuration for the component, but only for
:apiclass:`PollingComponent`.
For any :apiclass:`Component` (which is nearly everything), the well-known ``set_timeout`` method is also available;
this can be a handy alternative to implemeting a state machine.
Extras
******
.. note:: .. note::
This serves as documentation for some of ESPHome's internals and is not necessarily part of the This serves as documentation for some of ESPHome's internals and is not necessarily part of the development guide.
development guide.
All Python modules have some magic symbols that will automatically be loaded by the ESPHome All Python modules have some magic symbols that will automatically be loaded by the ESPHome loader. These are:
loader. These are:
- ``CONFIG_SCHEMA``: The configuration schema to validate the user config against. - ``CONFIG_SCHEMA``: The configuration schema to validate the user config against.
- ``to_code``: The function that will be called with the validated configuration and should - ``to_code``: The function that will be called with the validated configuration and should create the necessary C++
create the necessary C++ source code. source code.
- ``DEPENDENCIES``: Mark the component to depend on other components. If the user hasn't explicitly - ``DEPENDENCIES``: Mark the component to depend on other components. If the user hasn't explicitly added these
added these components in their configuration, a validation error will be generated. components in their configuration, a validation error will be generated.
- ``AUTO_LOAD``: Automatically load a component if the user hasn't added it manually. - ``AUTO_LOAD``: Automatically load a component if the user hasn't added it manually.
- ``MULTI_CONF``: Mark this component to accept an array of configurations. If this is an - ``MULTI_CONF``: Mark this component to accept an array of configurations. If this is an integer instead of a boolean,
integer instead of a boolean, validation will only permit the given number of entries. validation will only permit the given number of entries.
- ``CONFLICTS_WITH``: Mark a list of components as conflicting with this component. If the user - ``CONFLICTS_WITH``: Mark a list of components as conflicting with this component. If the user has one of them in
has one of them in the config, a validation error will be generated. their config, a validation error will be generated.
- ``CODEOWNERS``: GitHub usernames or team names of people that are responsible for this component. You should add at
least your GitHub username here, as well as anyone who helped you to write code that is being included.
- ``ESP_PLATFORMS``: Provide a list of allowed ESP types this component works with. .. _codebase_standards:
- ``CODEOWNERS``: GitHub usernames or team names of people that are responsible for this component.
You should add at least your GitHub username here, as well as anyone who helped you to write code
that is being included.
Codebase Standards Codebase Standards
------------------ ------------------
Standard for the esphome-core codebase: 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 - The C++ code style is based on the
`Google C++ Style Guide <https://google.github.io/styleguide/cppguide.html>`__ with a few modifications. `Google C++ Style Guide <https://google.github.io/styleguide/cppguide.html>`__ with a few modifications.
- function, method and variable names are ``lower_snake_case`` - Function, method and variable names are ``lower_snake_case``
- class/struct/enum names should be ``UpperCamelCase`` - Class/struct/enum names should be ``UpperCamelCase``
- constants should be ``UPPER_SNAKE_CASE`` - Constants should be ``UPPER_SNAKE_CASE``
- fields should be ``protected`` and ``lower_snake_case_with_trailing_underscore_`` (DO NOT use private) - Fields should be ``protected`` and ``lower_snake_case_with_trailing_underscore_`` (DO NOT use ``private``)
- It's preferred to use long variable/function names over short and non-descriptive ones. - 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 - All uses of class members and member functions should be prefixed with ``this->`` to distinguish them from global
``this->`` to distinguish them from global functions in code review. functions/variables.
- Use two spaces, not tabs. - Use two spaces, not tabs.
- Using ``#define`` s is discouraged and should be replaced with constants. - Using ``#define`` is discouraged and should be replaced with constants or enums (if appropriate).
- Use ``using type_t = int;`` instead of ``typedef int type_t;`` - Use ``using type_t = int;`` instead of ``typedef 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.
- New components should dump their configuration using ``ESP_LOGCONFIG`` - Components should dump their configuration using ``ESP_LOGCONFIG`` at startup in ``dump_config()``.
at startup in ``dump_config()``
- ESPHome uses a unified formatting tool for all source files (but this tool can be difficult to install). - 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, see the Github Actions output to see what formatting needs to be changed When creating a new PR in GitHub, see the Github Actions output to see what formatting needs to be changed
and what potential problems are detected. and what potential problems are detected.
- Use of external libraries should be kept to a minimum:
- The number of external libraries should be kept to a minimum. If the component you're developing has a simple - If the component you're developing has a simple communication interface, please consider implementing the library
communication interface, please consider implementing the library natively in ESPHome. 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 wihtin ESPHome.
- This depends on the communication interface of course - if the library is directly working - Components **must** use the provided abstractions like ``sensor``, ``switch``, etc. Components specifically should
with pins or doesn't do any I/O itself, it's ok. However if it's something like I²C, then ESPHome's **not** directly access other components -- for example, to publish to MQTT topics.
own communication abstractions should be used. Especially if the library accesses a global variable/state - Implementations for new devices should contain reference links for the datasheet and other sample implementations.
like ``Wire`` there's a problem because then the component may not modular (i.e. not possible
to create two instances of a component on one ESP)
- Components **must** use the provided abstractions like ``sensor``, ``switch`` etc.
Components should specifically **not** directly access other components like for example
publish to MQTT topics.
- Implementations for new devices should contain reference links for the datasheet and other sample
implementations.
- Please test your changes :) - Please test your changes :)
.. note:: .. note::
@ -694,30 +859,6 @@ Standard for the esphome-core codebase:
# Run lint only over changed files from powershell # Run lint only over changed files from powershell
docker run --rm -v "$($current_dir):/esphome" -it ghcr.io/esphome/esphome-lint script/quicklint docker run --rm -v "$($current_dir):/esphome" -it ghcr.io/esphome/esphome-lint script/quicklint
PRs are being drafted when changes are needed
---------------------------------------------
If there have been changes requested to your PR, our bot will automatically mark your PR as a draft.
This means that the PR is not ready to be merged or further reviewed for the moment.
Draft PRs tell other reviewers that look at the list of all PRs that this PR is currently in progress and doesn't require their attention yet.
Once you have made the requested changes, you can mark the PR as ready for review again by clicking the "Ready for review button":
.. figure:: images/pr-draft-ready.png
:align: center
:width: 100.0%
:alt: The ready for review button in the bottom of a PR in draft mode
Before you click the "Ready for review" button, ensure you have addressed all requested changes,
there are no merge conflicts, and that all our CI jobs and checks are passing successfully.
Once you've clicked the "Ready for review" button, the PR will return to a normal state again,
and our bot will automatically notify the reviewers who requested the changes that the PR is ready to go!
See Also See Also
-------- --------

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB