diff --git a/.gitignore b/.gitignore index edbf4a6..57c3870 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store .cache/ .docsite *.pyc diff --git a/docs/architecture/components/alarm_control_panel.md b/docs/architecture/components/alarm_control_panel.md index e69de29..be4dc85 100644 --- a/docs/architecture/components/alarm_control_panel.md +++ b/docs/architecture/components/alarm_control_panel.md @@ -0,0 +1 @@ +# Alarm Control Panel diff --git a/docs/architecture/components/binary_sensor.md b/docs/architecture/components/binary_sensor.md index 422e5c4..29d5b15 100644 --- a/docs/architecture/components/binary_sensor.md +++ b/docs/architecture/components/binary_sensor.md @@ -1 +1 @@ -# Sensor +# Binary Sensor diff --git a/docs/architecture/components/button.md b/docs/architecture/components/button.md index e69de29..88423dc 100644 --- a/docs/architecture/components/button.md +++ b/docs/architecture/components/button.md @@ -0,0 +1 @@ +# Button diff --git a/docs/architecture/components/climate.md b/docs/architecture/components/climate.md index 422e5c4..99373c6 100644 --- a/docs/architecture/components/climate.md +++ b/docs/architecture/components/climate.md @@ -1 +1 @@ -# Sensor +# Climate diff --git a/docs/architecture/components/cover.md b/docs/architecture/components/cover.md index 422e5c4..2f4b519 100644 --- a/docs/architecture/components/cover.md +++ b/docs/architecture/components/cover.md @@ -1 +1 @@ -# Sensor +# Cover diff --git a/docs/architecture/components/display.md b/docs/architecture/components/display.md index e69de29..57f7784 100644 --- a/docs/architecture/components/display.md +++ b/docs/architecture/components/display.md @@ -0,0 +1 @@ +# Display diff --git a/docs/architecture/components/event.md b/docs/architecture/components/event.md index e69de29..bdd2b86 100644 --- a/docs/architecture/components/event.md +++ b/docs/architecture/components/event.md @@ -0,0 +1 @@ +# Event diff --git a/docs/architecture/components/fan.md b/docs/architecture/components/fan.md index e69de29..56695d7 100644 --- a/docs/architecture/components/fan.md +++ b/docs/architecture/components/fan.md @@ -0,0 +1 @@ +# Fan diff --git a/docs/architecture/components/index.md b/docs/architecture/components/index.md index 2fc56e3..b9fe361 100644 --- a/docs/architecture/components/index.md +++ b/docs/architecture/components/index.md @@ -1,8 +1,13 @@ -# ESPHome Component Architecture +# Component Architecture +All components within ESPHome have a specific structure. This structure exists because: +- It allows the Python parts of ESPHome to: + - Easily determine which parts of the C++ codebase are required to complete a build. + - Understand how to interact with the component/platform so it can be configured correctly. +- It makes understanding and maintaining the codebase easier. -## Directory Structure +## Directory structure ``` esphome @@ -13,8 +18,8 @@ esphome │ │ ├─ example_component.cpp ``` -This is the most basic component directory structure where the component would be used at the -top-level of the YAML configuration. +This is the most basic component directory structure where the component would be used at the top-level of the YAML +configuration. ```yaml example_component: @@ -27,9 +32,9 @@ example_component: At the heart of every ESPHome component is the `CONFIG_SCHEMA` and the `to_code` method. -The `CONFIG_SCHEMA` is based on and extends [Voluptuous](https://github.com/alecthomas/voluptuous), which is a -data validation library. This allows the YAML to be parsed and converted to a Python object and performs -strong validation against the data types to ensure they match. +The `CONFIG_SCHEMA` is based on and extends [Voluptuous](https://github.com/alecthomas/voluptuous), which is a data +validation library. This allows the YAML to be parsed and converted to a Python object and performs strong validation +against the data types to ensure they match. ```python import esphome.config_validation as cv @@ -73,17 +78,16 @@ import esphome.config_validation as cv import esphome.codegen as cg ``` -`config_validation` is a module that contains all the common validation functions that are used to validate the configuration. -Components may contain their own validations as well and this is very extensible. -`codegen` is a module that contains all the code generation functions that are used to generate the C++ code that is placed -into `main.cpp`. +`config_validation` is a module that contains all the common validation method that are used to validate the +configuration. Components may contain their own validations as well and this is very extensible. `codegen` is a module +that contains all the code generation method that are used to generate the C++ code that is placed into `main.cpp`. ```python example_component_ns = cg.esphome_ns.namespace("example_component") ``` -This is the c++ namespace inside the `esphome` namespace. It is required here so that the codegen knows the exact +This is the C++ namespace inside the `esphome` namespace. It is required here so that the codegen knows the exact namespace of the class that is being created. The namespace **must** match the name of the component. @@ -91,8 +95,8 @@ namespace of the class that is being created. The namespace **must** match the n ExampleComponent = example_component_ns.class_("ExampleComponent", cg.Component) ``` -This is the class that is being created. The first argument is the name of the class, the second and subsequent -arguments are the base classes that this class inherits from. +This is the class that is being created. The first argument is the name of the class and any subsequent arguments are +the base classes that this class inherits from. #### Configuration validation @@ -113,10 +117,10 @@ specify their own `id` in their configuration in the event that they wish to ref #### Code generation -The `to_code` method is called after the entire configuration has been validated. This function is given the -parsed `config` object for this instance of this component. This method is responsible for generating the -C++ code that is placed into `main.cpp` translates the user configuration into the C++ instance method calls, -setting the variables on the object. +The `to_code` method is called after the entire configuration has been validated. It is given the parsed `config` +object for this instance of this component and uses it to determine exactly what C++ code is placed into the generated +`main.cpp` file. It translates the user configuration into the C++ instance method calls, setting variables on the +object as required/specified. ```python var = cg.new_Pvariable(config[CONF_ID]) @@ -131,12 +135,12 @@ constructor of the class. await cg.register_component(var, config) ``` -This line generates `App.register_component(var)` in C++ which registers the component so that its `setup`, `loop` and/or `update` -functions are called correctly. +This line generates `App.register_component(var)` in C++ which registers the component so that its `setup`, `loop` +and/or `update` methods are called correctly. Assuming the user has `foo: true` in their YAML configuration, this line: -```python +```python cg.add(var.set_foo(config[CONF_FOO])) ``` @@ -158,24 +162,26 @@ If the config value is not set, then we do not call the setter function. ### Further information -- `AUTO_LOAD` - A list of components that will be automatically loaded if they are not already specified in the configuration. - This can be a method that can be run with access to the `CORE` information like the target platform. -- `CODEOWNERS` - A list of GitHub usernames that are responsible for this component. `script/build_codeowners.py` will +- `AUTO_LOAD`: A list of components that will be automatically loaded if they are not already specified in the + configuration. This can be a method that can be run with access to the `CORE` information like the target platform. +- `CONFLICTS_WITH`: A list of components which conflict with this component. If the user has one of them in their + config, a validation error will be generated. +- `CODEOWNERS`: A list of GitHub usernames that are responsible for this component. `script/build_codeowners.py` will update the `CODEOWNERS` file. -- `DEPENDENCIES` - A list of components that this component depends on. If these components are not present in the configuration, - validation will fail and the user will be shown an error. -- `MULTI_CONF` - If set to `True`, the user can use this component multiple times in their configuration. If set to a +- `DEPENDENCIES`: A list of components that this component depends on. If these components are not present in the + configuration, validation will fail and the user will be shown an error. +- `MULTI_CONF`: If set to `True`, the user can use this component multiple times in their configuration. If set to a number, the user can use this component that number of times. -- `MULTI_CONF_NO_DEFAULT` - This is a special flag that allows the component to be auto-loaded without an instance of the configuration. - An example of this is the `uart` component. This component can be auto-loaded so that all of the uart headers will be available but - potentially there is no native uart instance, but one provided by another component such an an external i2c UART expander. - +- `MULTI_CONF_NO_DEFAULT`: This is a special flag that allows the component to be auto-loaded without an instance of + the configuration. An example of this is the `uart` component. This component can be auto-loaded so that all of the + UART headers will be available but potentially there is no native UART instance, but one provided by another + component such an an external i2c UART expander. ### Final validation -ESPHome has a mechanism to run a final validation step after all of the configuration is initially deemed to be individually valid. -This final validation gives an instance of a component the ability to check the configuration of any other components and potentially fail -the validation stage if an important dependent configuration does not match. +ESPHome has a mechanism to run a final validation step after all of the configuration is initially deemed to be +individually valid. This final validation gives an instance of a component the ability to check the configuration of +any other components and potentially fail the validation stage if an important dependent configuration does not match. -For example many components that rely on `uart` can use the `FINAL_VALIDATE_SCHEMA` to ensure that the `tx_pin` and/or `rx_pin` are -configured. +For example many components that rely on `uart` can use the `FINAL_VALIDATE_SCHEMA` to ensure that the `tx_pin` and/or +`rx_pin` are configured. diff --git a/docs/architecture/components/light.md b/docs/architecture/components/light.md index e69de29..e86c93d 100644 --- a/docs/architecture/components/light.md +++ b/docs/architecture/components/light.md @@ -0,0 +1 @@ +# Light diff --git a/docs/architecture/components/lock.md b/docs/architecture/components/lock.md index e69de29..e6a8bf0 100644 --- a/docs/architecture/components/lock.md +++ b/docs/architecture/components/lock.md @@ -0,0 +1 @@ +# Lock diff --git a/docs/architecture/components/media_player.md b/docs/architecture/components/media_player.md index e69de29..5ebe2f5 100644 --- a/docs/architecture/components/media_player.md +++ b/docs/architecture/components/media_player.md @@ -0,0 +1 @@ +# Media Player diff --git a/docs/architecture/components/number.md b/docs/architecture/components/number.md index 422e5c4..f0760b0 100644 --- a/docs/architecture/components/number.md +++ b/docs/architecture/components/number.md @@ -1 +1 @@ -# Sensor +# Number diff --git a/docs/architecture/components/output.md b/docs/architecture/components/output.md index e69de29..22e0f66 100644 --- a/docs/architecture/components/output.md +++ b/docs/architecture/components/output.md @@ -0,0 +1 @@ +# Output diff --git a/docs/architecture/components/select.md b/docs/architecture/components/select.md index 422e5c4..9dfe3fd 100644 --- a/docs/architecture/components/select.md +++ b/docs/architecture/components/select.md @@ -1 +1 @@ -# Sensor +# Select diff --git a/docs/architecture/components/speaker.md b/docs/architecture/components/speaker.md index e69de29..1234d1d 100644 --- a/docs/architecture/components/speaker.md +++ b/docs/architecture/components/speaker.md @@ -0,0 +1 @@ +# Speaker diff --git a/docs/architecture/components/switch.md b/docs/architecture/components/switch.md index e69de29..036dfe1 100644 --- a/docs/architecture/components/switch.md +++ b/docs/architecture/components/switch.md @@ -0,0 +1 @@ +# Switch diff --git a/docs/architecture/components/text.md b/docs/architecture/components/text.md index e69de29..bb182be 100644 --- a/docs/architecture/components/text.md +++ b/docs/architecture/components/text.md @@ -0,0 +1 @@ +# Text diff --git a/docs/architecture/components/text_sensor.md b/docs/architecture/components/text_sensor.md index 422e5c4..810e9e5 100644 --- a/docs/architecture/components/text_sensor.md +++ b/docs/architecture/components/text_sensor.md @@ -1 +1 @@ -# Sensor +# Text Sensor diff --git a/docs/architecture/components/time.md b/docs/architecture/components/time.md index e69de29..dc3a17f 100644 --- a/docs/architecture/components/time.md +++ b/docs/architecture/components/time.md @@ -0,0 +1 @@ +# Time diff --git a/docs/architecture/components/touchscreen.md b/docs/architecture/components/touchscreen.md index e69de29..25a1457 100644 --- a/docs/architecture/components/touchscreen.md +++ b/docs/architecture/components/touchscreen.md @@ -0,0 +1 @@ +# Touchscreen diff --git a/docs/architecture/components/valve.md b/docs/architecture/components/valve.md index 422e5c4..b54c3e0 100644 --- a/docs/architecture/components/valve.md +++ b/docs/architecture/components/valve.md @@ -1 +1 @@ -# Sensor +# Valve diff --git a/docs/architecture/core.md b/docs/architecture/core.md index e2945bb..c3f3608 100644 --- a/docs/architecture/core.md +++ b/docs/architecture/core.md @@ -1 +1 @@ -# ESPHome Core Architecture +# Core Architecture diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md new file mode 100644 index 0000000..8814b40 --- /dev/null +++ b/docs/architecture/overview.md @@ -0,0 +1,264 @@ +# Architectural Overview + +ESPHome itself uses two languages: Python and C++. + +As you may already know: + +- The Python side of ESPHome reads a YAML configuration file, validates it and transforms it into a custom firmware + which includes only the code needed to perform as defined in the configuration file. +- The C++ part of the codebase is what's actually running on the microcontroller; this is called the "runtime". This + part of the codebase should first set up the communication interface to a sensor/component/etc. and then communicate + with the ESPHome core via the defined interfaces (like `Sensor`, `BinarySensor` and `Switch`, among others). + +## Directory structure + +ESPHome's directory structure looks like this: + +``` +esphome +├── __main__.py +├── automation.py +├── codegen.py +├── config_validation.py +├── components +│ ├── __init__.py +│ ├── dht12 +│ │ ├── __init__.py +│ │ ├── dht12.cpp +│ │ ├── dht12.h +│ │ ├── sensor.py +│ ├── restart +│ │ ├── __init__.py +│ │ ├── restart_switch.cpp +│ │ ├── restart_switch.h +│ │ ├── switch.py +│ ... +├── tests +│ ├── components +│ │ ├── dht12 +│ │ │ ├── common.yaml +│ │ │ ├── test.esp32-ard.yaml +│ │ │ ├── test.esp32-c3-ard.yaml +│ │ │ ├── test.esp32-c3-idf.yaml +│ │ │ ├── test.esp32-idf.yaml +│ │ │ ├── test.esp8266-ard.yaml +│ │ │ ├── test.rp2040-ard.yaml +│ ... +``` + +All components are in the "components" directory. Each component is in its own subdirectory which contains the Python +code (`.py`) and the C++ code (`.h` and `.cpp`). + +In the "tests" directory, a second "components" directory contains configuration files (`.yaml`) used to perform test +builds of each component. It's structured similarly to the "components" directory mentioned above, with subdirectories +for each component. + +Consider a YAML configuration file containing the following: + +```yaml +hello1: + +sensor: + - platform: hello2 +``` + +In both cases, ESPHome will automatically look for corresponding entries in the "components" directory and find the +directory with the given name. In this example, the first entry causes ESPHome to look for the +`esphome/components/hello1/__init__.py` file and the second entry tells ESPHome to look for +`esphome/components/hello2/sensor.py` or `esphome/components/hello2/sensor/__init__.py`. + +Let's leave the content of those files for [the next section](config-validation), but for now you should also know +that, whenever a component is loaded, all the C++ source files in the directory of the component are automatically +copied into the generated PlatformIO project. All you need to do is add the C++ source files in the component's directory +and the ESPHome core will copy them with no additional code required by the component developer. + +## Config validation + +The first task ESPHome performs is to read and validate the provided YAML configuration file. ESPHome has a powerful +"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: + +```python +import esphome.config_validation as cv + +CONF_MY_REQUIRED_KEY = 'my_required_key' +CONF_MY_OPTIONAL_KEY = 'my_optional_key' + +CONFIG_SCHEMA = cv.Schema({ + cv.Required(CONF_MY_REQUIRED_KEY): cv.string, + cv.Optional(CONF_MY_OPTIONAL_KEY, default=10): cv.int_, +}).extend(cv.COMPONENT_SCHEMA) +``` + +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). How validation works +is out of scope for this guide; the easiest way to learn is to look at how similar components validate user input. + +A few notes on validation: + +- ESPHome puts a lot of effort into **strict validation**. All validation methods should be as strict as possible and + detect incorrect user input at the validation stage, mitigating compiler warnings and/or errors. +- All default values should be defined in the schema -- not in C++ codebase. +- Prefer naming configuration keys in a way which is descriptive instead of short. Put another way, if the meaning of a + 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; do not name keys and variables in this way. + +## Code generation + +The last step the Python codebase performs is called *code generation*. This runs only after the user input has been +successfully validated. + +As you may know, ESPHome "converts" the user's YAML configuration into C++ code (you can see the generated code under +`/src/main.cpp`). Each component must define its own `to_code` method that "converts" the user input to +C++ code. + +This method is also automatically loaded and invoked by the ESPHome core. Here's an example of such a method: + +```python +import esphome.codegen as cg + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + cg.add(var.set_my_required_key(config[CONF_MY_REQUIRED_KEY])) +``` + +The details of ESPHome code generation is out-of-scope for this document. However, ESPHome's code generation is 99% +syntactic sugar - and (again) it's probably best to study similar components and just copy what they do. + +There's one important concept for the `to_code` method: coroutines with `await`. + +The problem that necessitates coroutines is this: in ESPHome, components can declare (via `cg.Pvariable`) and access +variables (`cg.get_variable()`) -- but sometimes, when one part of the codebase requests a variable, it has not been +declared yet because the code for the component creating the variable has not yet run. + +To allow for ID references, ESPHome uses so-called `coroutines`. When you see an `await` statement in a `to_code` +method, ESPHome will call the provided method and, if that method needs to wait for a variable to be declared first, +`await` will wait until that variable has been declared. After that, `await` 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()` 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! + +## Runtime + +At this point, the Python part of the codebase has completed its work. Let's move on and discuss the C++ part of +components. + +Most components consist of two primary parts/steps: + +- Setup Phase +- Run Phase + +When you create a new component, your new component will inherit from the +[`Component`](https://esphome.io/api/classesphome_1_1_component) class. This class has a special `setup()` method that +will be called once to set up the component. At the time the `setup()` method is called, all the setters generated by +the Python codebase have already run and the all fields are set for your class. + +The `setup()` method should set up the communication interface for the component and check if communication works (and, +if not, it should call `mark_failed()`). + +Again, look at examples of other components to learn more. + +The next method that will be called with your component is `loop()` (or `update()` for a +[`PollingComponent`](https://esphome.io/api/classesphome_1_1_polling_component). These methods should retrieve the +latest data from your component and publish them with the provided methods. + +Finally, your component must have a `dump_config` method that prints the complete user configuration. + +## Test configurations + +Each (new) component/platform must have tests. This enables our CI system to perform a test build of the +component/platform to ensure it compiles without any errors. The test file(s) should incorporate the new +component/platform itself as well as all [automations](https://esphome.io/automations/actions) it implements. + +### Overview + +The tests aim to test compilation of the code for each processor architecture: + +- Xtensa (ESP8266, original ESP32 and S-series) +- RISC-V (ESP32 C-series) +- ARM (RP2040) + +...and for each supported framework: + +- Arduino +- [ESP-IDF](https://github.com/espressif/esp-idf/) + +There should be *at least one test* for each framework/architecture combination. We can probably go without saying it, +but some framework/architecture combinations are simply not supported/possible, so tests for those are impossible and, +as such, are (naturally) omitted. + +### General structure + +We try to structure the tests in a way so as to minimize repetition. Let's look at the `dht12` sensor platform as an +example: + +First, you'll find a `common.yaml` file which contains this: + +```yaml +i2c: + - id: i2c_dht12 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: dht12 + temperature: + name: DHT12 Temperature + humidity: + name: DHT12 Humidity + update_interval: 15s +``` + +It's a shared configuration file that defines common settings used across all hardware platforms. Having a "common" +file like this minimizes duplication and ensures test consistency across all platforms. + +To use `common.yaml` in a test configuration, YAML substitutions and the insertion operator are used (see +[substitutions](https://esphome.io/components/substitutions)). This allows the test YAML file to reference and include +the shared configuration. For the `dht12` platform, one of the test files is named `test.esp32-ard.yaml` and it contains +this: + +```yaml +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml +``` + +By including `common.yaml`, all test configurations maintain the same structure while allowing flexibility for +platform-specific substitutions such as pin assignments. This approach simplifies managing multiple test cases across +different hardware platforms. + +### Which tests do I need? + +We require a test for each framework/architecture combination the component/platform supports. *Most* +components/platforms include the following test files: + +- `test.esp32-ard.yaml` - ESP32 (Xtensa)/Arduino +- `test.esp32-idf.yaml` - ESP32 (Xtensa)/IDF +- `test.esp32-c3-ard.yaml` - ESP32-C3 (RISC-V)/Arduino +- `test.esp32-c3-idf.yaml` - ESP32-C3 (RISC-V)/IDF +- `test.esp8266-ard.yaml` - ESP8266 (Xtensa)/Arduino +- `test.rp2040-ard.yaml` - RP2040 (ARM)/Arduino + +In cases where the component/platform implements support for some microcontroller-specific hardware component, tests +should be added to/omitted from the list above as appropriate. The [ADC](https://esphome.io/components/sensor/adc) is +one example of this. + +### Running the tests + +You can run the tests locally simply by invoking the test script: + +```shell +script/test_build_components -e compile -c dht12 +``` + +Our CI will also run this script when you create or update your pull request (PR). diff --git a/docs/blog/posts/2025-01-14-hello-developers.md b/docs/blog/posts/2025-01-14-hello-developers.md index 264d7db..f42985d 100644 --- a/docs/blog/posts/2025-01-14-hello-developers.md +++ b/docs/blog/posts/2025-01-14-hello-developers.md @@ -6,5 +6,5 @@ date: 2025-01-14 Welcome to the first post of this new ESPHome Developer Documentation. This is a place where you can find all the information you need to start developing for ESPHome. -This website will cover everything you need to successfull create components and how to structure your code +This website will cover everything you need to create components and how to structure your code so that it will be easy to maintain and understand. diff --git a/docs/contributing/code-notes.md b/docs/contributing/code-notes.md new file mode 100644 index 0000000..8a0fedd --- /dev/null +++ b/docs/contributing/code-notes.md @@ -0,0 +1,80 @@ +# Contributing to ESPHome -- important notes + +## 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 +[`PollingComponent`](https://esphome.io/api/classesphome_1_1_polling_component). + +For any [`Component`](https://esphome.io/api/classesphome_1_1_component) (which is nearly everything), the well-known +`set_timeout` method is also available; this can be a handy alternative to implementing a state machine. + +## Custom components + +*"I read that support for custom components was removed...so now what do I do???"* + +ESPHome's "custom component" mechanism was a holdover from Home Assistant's feature by the same name. It existed before +[external components](https://esphome.io/components/external_components) and offered a way to "hack in" support for +devices which were not officially supported by ESPHome. + +### Why were custom components removed? + +There are several reasons for this change. + +- Custom components are very fragile: + - There is no validation. You can easily configure a custom component incorrectly and there will be no warning. + - Types are not checked. You might incorrectly pass a variable of an incorrect type or unit to a custom component + resulting in compiler errors, unexpected behavior and/or crashes. + - Custom components are difficult to use. You have to manually copy all of the custom component's files into *just + the right location* on your system or else you will receive compiler errors and the component won't work. + - Custom components lack flexibility and almost always require C++ code changes when you want them to work even + *slightly* differently than the original author intended/designed. For example, a simple change of input units + (`cm` to `m`, for example) could require significant changes to the C++ code, depending on how the original + author designed the custom component. + +- [External components](https://esphome.io/components/external_components) initially require a bit more effort by the + developer but are ultimately more robust and are easier to use and share: + - Just like any other ESPHome component/platform: + - They are configured entirely in YAML. + - Their YAML configuration is validated. + - They do not require the user to: + - Manually copy files onto their system. + - Touch/edit any C++ code. + - They tend to be more flexible since they are configured in YAML (as opposed to editing C++ code to make changes). + +### What's the difference? + +Custom components are typically (more or less) just the [runtime](/architecture/overview/#runtime) part of an ESPHome component/platform. On +the other hand, [external components](https://esphome.io/components/external_components) are just like any other +ESPHome component -- the only difference is that they are *external* in the sense that they are not "officially" a part +of ESPHome. + +In terms of implementation, custom components just lack the Python part of an ESPHome component, specifically: + +- [Config Validation](/architecture/overview/#config-validation) +- [Code Generation](/architecture/overview/#code-generation) + +As such, most custom components can be made into +[external components](https://esphome.io/components/external_components) simply by adding the required Python parts to +make the custom component into a proper, complete ESPHome component. + +### What's next? + +We encourage all custom component developers to extend their custom component(s) into proper +[external components](https://esphome.io/components/external_components); doing so will make your custom component +easier to share and use. As you do so, be sure to have a look at the the sections above as it walks through ESPHome +(component) architecture. In addition, it's often helpful to take a look at other, similar +[components](https://github.com/esphome/esphome/tree/dev/esphome/components) and adapt them to fit the needs of your +custom component. For common hardware devices such as [sensors](https://esphome.io/components/sensor/index), this is +often a reasonably trivial exercise and [we are happy to help you along!](https://discord.gg/KhAMKrd) diff --git a/docs/contributing/code.md b/docs/contributing/code.md new file mode 100644 index 0000000..2c51435 --- /dev/null +++ b/docs/contributing/code.md @@ -0,0 +1,79 @@ +# Contributing to ESPHome + +![logo](/images/logo-text.svg) + +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](https://google.github.io/styleguide/cppguide.html) 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` 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. + - 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 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. + +- Components should dump their configuration using `ESP_LOGCONFIG` at startup in `dump_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](submitting-your-work.md#automated-checks) + 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](code-notes.md#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](https://esphome.io/components/external_components). + +Please be sure your work is consistent with the standards outlined above before +[submitting your work for integration into ESPHome](submitting-your-work.md). + +## Running CI checks locally + +You can run the lint and GitHub Actions checks via a docker image: + +```bash +# 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` + +```bash +# 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 +``` diff --git a/docs/contributing/development-environment.md b/docs/contributing/development-environment.md new file mode 100644 index 0000000..4d9e506 --- /dev/null +++ b/docs/contributing/development-environment.md @@ -0,0 +1,100 @@ +# Setting up a development environment + +In order to develop ESPHome itself, or new ESPHome components, you will need to set up a development environment. This +guide will walk you through these steps. + +## Developing with a Python virtual environment + +*Note: These instructions apply for Linux and macOS. Windows users can still develop ESPHome and its components, +but the process is slightly different and not covered (yet) in this guide.* + +### Requirements + +- Python 3.12 or newer +- `pip` (Python package manager) + +### Using `git` and GitHub + +All of ESPHome's code and documentation is hosted on [GitHub](https://github.com); we use this to collaborate on all +changes. As a deep-dive into how `git` and [GitHub](https://github.com) works is beyond the scope of this +documentation, we'll assume that you're already familiar with these tools and just walk through the basic steps +required to get started. + +If you're not familiar with `git` and/or [GitHub](https://github.com) or if you'd just like more detail on any of the +steps that follow, you should read through the +[GitHub documentation](https://docs.github.com/en). While there's a lot there (and it's consequently probably a bit +daunting), if you just want to submit your own work to ESPHome, we might suggest you start +[here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo). + +### Set up the local repository + +First you will need a "Fork" of the ESPHome repository. You can do this by visiting the +[ESPHome repository](https://github.com/esphome/esphome), clicking the **Fork** button and following the instructions +on GitHub to complete the fork. + +Once the fork is created, you can clone the repository to your local machine: + +```bash +git clone https://github.com/YOUR_GITHUB_USERNAME/NAME_OF_FORK.git +cd NAME_OF_FORK +git remote add updtream https://github.com/esphome/esphome.git +``` + +Once the local clone is set up, you can now run the setup script. + +```bash +script/setup +``` + +This will create a Python virtual environment and install the requirements. + +### Activating the virtual environment + +To use the virtual environment, you need to activate it. This needs to be done for each new terminal session and is +done by running: + +```bash +source venv/bin/activate +``` + +Then ESPHome can be run directly from that terminal: + +```bash +esphome compile some-config-file.yaml +``` + +At this point, it is also good to create an empty directory named `config`. You should store all of your ESPHome +configurations in this directory. + +This folder is listed in the ESPHome `.gitignore` file, so it will not be added to git. + +### Creating your own working branch + +Always do your work in a new branch created from the latest ESPHome upstream `dev` branch; do not commit changes +directly to the `dev` branch. + +```bash +git checkout dev +git pull upstream dev + +git checkout -b my-new-feature +``` + +This branch should contain your work for this new feature. + +After you've run the above commands, you're ready to make (and test!) your changes. Once you're satisfied with your +changes, it's time to stage and commit them: + +```bash +git add . +git commit -m "Look mom, I'm contributing to ESPHome!" +``` + +After you've committed your changes, you can push your branch up to your fork in GitHub: + +```bash +git push -u origin my-new-feature +``` + +Once you've pushed your branch, if you wish, you can +[submit your work for integration into ESPHome](submitting-your-work.md). \ No newline at end of file diff --git a/docs/contributing/docs.md b/docs/contributing/docs.md new file mode 100644 index 0000000..2897d31 --- /dev/null +++ b/docs/contributing/docs.md @@ -0,0 +1,410 @@ +![logo-docs](/images/logo-docs.svg) + +# Contributing to `esphome-docs` + +Our documentation can always be improved. We rely on contributions from our users to do so. If you notice an issue (for +example, spelling/grammar mistakes) or if you want to share your awesome new setup, we encourage you to submit a pull +request (PR). + +The ESPHome documentation is built using [Sphinx](http://www.sphinx-doc.org) and uses +[reStructuredText](http://docutils.sourceforge.net/rst.html) for all source (`.rst`) files. + +If you're not familiar with rST, see [rST syntax](#rst-syntax) for a quick primer. + +## 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. + +At the bottom of each page in the docs, there is an "Edit this page on GitHub" link. Click this link and you'll see +something like this: + +![A screenshot of an rST file opened in GitHub, with the edit button circled](/images/ghedit_1.png) + +Click the edit button to start making changes. If you're unsure about syntax, see our [quick primer](#rst-syntax). + +Once you've made your changes, give them a useful name and click "Propose changes". At this point, you've made the +changes to your own personal copy of the docs in GitHub, but you still need to submit them to us. + +![The commit creation screen in GitHub, with the commit title and "Propose changes" button circled](/images/ghedit_2.png) + +To do that, you need to create a "Pull request" (PR): + +![The pull request prompt screen in GitHub with the "Create pull request" button circled](/images/ghedit_3.png) + +Complete the pull request form, replacing the `[ ]` with `[x]` to indicate that you have followed the instructions. +**Note that there must be no spaces around the `x`** when you populate it. + +![The pull request creation screen in GitHub with the "Create pull request" button circled](/images/ghedit_4.png) + +After a few minutes, you'll see either a green check ✅ or a red ❌ next to your commit in your pull request: + +![The pull request with a commit with a red x next to it](/images/ghedit_ci_failed.png) + +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). + +![Failed lint substep of build, with "details" link circled](/images/ghedit_ci_details.png) + +![Log messages showing reason for failed build](/images/ghedit_ci_logs.png) + +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 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 + +!!! note + + The easiest way is to use the [esphome-docs container image](https://ghcr.io/esphome/esphome-docs/): + + ```bash + 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, to see the preview, go to + `:8000` in your browser. + + This way, you don't have to install the dependencies to build the documentation. + +To check your documentation changes locally, you first need install Sphinx (with **Python 3**). + +```bash +# in ESPHome-Docs repo: +pip install -r requirements.txt +``` + +Then, use the provided Makefile to build the changes and start a live-updating web server: + +```bash +# Start web server on port 8000 +make live-html +``` + +## General notes/guidelines + +- Use the English language (duh...) +- An image is worth a thousand words, please use them wherever possible. Regardless, don't forget to optimize them so + pages load quickly; you might try using https://tinypng.com/ +- Try to use examples as often as possible. +- When using highly accurate, domain-specific terminology, be sure that it does not interfere with new users + understanding the content. +- Be sure to target the correct **base branch** of the `esphome-docs` repository: + + - **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 + +Here's a quick RST primer: + +Title hierarchy is based on order of occurrence, not on type of character used to underline it. For consistency, we +adhere to the following order: + +### Headers + +You can write titles like this: + +```rst +My Title +======== +``` + +and section headers like this: + +```rst +My Section +---------- +``` + +and sub-section headers like this: + +```rst +My Sub-section +************** +``` + +and sub-sub-section headers like this: + +```rst +My Sub-sub-section +^^^^^^^^^^^^^^^^^^ +``` + +!!! note + - The length of the bar below the text **must** match the title text length. + - Section titles should use Title Case. + +### Links + +Create a link to an external resource (for example: https://www.google.com) like this: + +```rst +`Google.com `__ +``` + +[Google.com](https://www.google.com) + +!!! note + Referral links are only permitted if they provide a direct benefit to the ESPHome project. + This policy applies to all official ESPHome documentation and websites. + +### References + +To reference another document, use the `:doc:` and `:ref:` roles (references are set up globally +and can be used between documents): + +```rst +.. _my-reference-label: + +Section to cross-reference +-------------------------- + +See :ref:`my-reference-label`, also see :doc:`/components/switch/gpio`. +:doc:`Using custom text `. +``` + +See [Supported Microcontrollers](https://esphome.io/components/#devices), also see +[GPIO Switch](https://esphome.io/components/switch/gpio). +[Using custom text](https://esphome.io/components/switch/gpio). + +### Inline code + +To have text appear `like this`, use double backticks: + +```rst +To have text appear ``like this``, use double backticks. +``` + +To have text appear `like this`, use double backticks. + +### Code blocks + +To show a sample configuration file, use the `code-block` directive: + +```rst +.. code-block:: yaml + + # Sample configuration entry + switch: + - platform: gpio + name: "Relay #42" + pin: GPIOXX +``` + +```yaml +# Sample configuration entry +switch: + - platform: gpio + name: "Relay #42" + pin: GPIOXX +``` + +!!! note + Note that a blank line is *required* after every `code-block` directive. + +### Collapsible section + +To add a collapsible section, use the `collapse` directive: + +```rst +.. collapse:: Details + + Something small enough to escape casual notice. +``` + +```rst +.. collapse:: A long code block + + .. code-block:: yaml + + # Sample configuration entry + switch: + - platform: gpio + name: "Relay #42" + pin: GPIOXX +``` + +The `:open:` flag can be used to have the section open by default. + +```rst +.. collapse:: Open + :open: + + This section is open by default. +``` + +!!! note + - The `:open:` flag must immediately follow the `collapse` directive without a blank line between them. + - A blank line is *required* after every `collapse` directive. + +### Tabs + +To group content into tabs, use the `tabs` directive. The tabs directive defines a tab set. +Basic tabs are added using the `tab` directive (without s), which takes the tab’s label as an argument: + +```rst +.. tabs:: + + .. tab:: Apples + + Apples are green, or sometimes red. + + .. tab:: Pears + + Pears are green. + + .. tab:: Oranges + + Oranges are orange. +``` + +Tabs can also be nested inside one another: + +```rst +.. tabs:: + + .. tab:: Stars + + .. tabs:: + + .. tab:: The Sun + + The closest star to us. + + .. tab:: Proxima Centauri + + The second closest star to us. + + .. tab:: Polaris + + The North Star. + + .. tab:: Moons + + .. tabs:: + + .. tab:: The Moon + + Orbits the Earth + + .. tab:: Titan + + Orbits Jupiter +``` + +!!! note + - A blank line is *required* after every `tabs` directive. + - The contents of each tab can be displayed by clicking on the tab that you wish to show. + Clicking again on the tab that is currently open will hide its content, leaving only the tab set labels visible. + - For advanced features like tab-groupings, refer to https://sphinx-tabs.readthedocs.io/en/latest/ + +### Images + +Use the `figure` directive to display an image: + +```rst +.. figure:: images/dashboard_states.png + :align: center + :width: 40.0% + + Optional figure caption. +``` + +!!! note + All images in the documentation need to be as small as possible to minimize page load times. Typically, the + maximum size should be roughly 1000x800 px or so. Additionally, please use online tools like + [tinypng](https://tinypng.com) or [tinyjpg](https://tinyjpg.com) to further compress images. + +### Notes and warnings + +You can create simple notes and warnings using the `note` and `warning` directives: + +```rst +.. note:: + + This is a note. + +.. warning:: + + This is a warning. +``` + +!!! note + This is a note. + +!!! warning + This is a warning. + +### Italic and boldface font families + +To *italicize* text, use one asterisk around the text. To put +**a strong emphasis** on a piece of text, put two asterisks around it. + +```rst +*This is italicized.* (A weird word...) + +**This is very important.** +``` + +*This is italicized.* (A weird word...) + +**This is very important.** + +### Ordered and unordered lists + +The syntax for lists in RST is more or less the same as in Markdown: + +```rst +- Unordered item + + - Unordered sub-item + +- Item with a very long text so that it does not fully fit in a single line and + must be split up into multiple lines. + +1. Ordered item #1 +2. Ordered item #2 +``` + +- Unordered item + + - Unordered sub-item + +- Item with a very long text so that it does not fully fit in a single line and + must be split up into multiple lines. + +1. Ordered item #1 +1. Ordered item #2 + +### imgtable + +ESPHome uses a custom RST directive to show the table on the main documentation page (see +[`components/index.rst`](https://github.com/esphome/esphome-docs/blob/current/components/index.rst)). New pages need to +be added to the `imgtable` list. The syntax is CSV with ``, `` (without RST), `` (in the +top-level `images/` directory), `` (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 +and JPGs should be no more than 300x300px. + +If you have imagemagick installed, you can use this command to convert the thumbnail: + +```bash +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; if you're looking for a more complete guide, please have a look at the +[Sphinx reStructuredText Primer](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html). diff --git a/docs/contributing/getting-started.md b/docs/contributing/getting-started.md new file mode 100644 index 0000000..81dd94e --- /dev/null +++ b/docs/contributing/getting-started.md @@ -0,0 +1,10 @@ +# Getting started with ESPHome development + +This page serves as an index for getting started with ESPHome development. + +- First, you'll need to [set up your development environment](development-environment.md). +- Next, you'll likely want to have a look at the [architectural overview](/architecture/overview). +- Beyond that, have a look at our contributing guides: + - [Code](code.md) and related [important notes](code-notes.md) + - [Documentation](docs.md) +- Finally, you can [submit your work for integration into ESPHome](submitting-your-work.md). diff --git a/docs/contributing/submitting-your-work.md b/docs/contributing/submitting-your-work.md new file mode 100644 index 0000000..88e3302 --- /dev/null +++ b/docs/contributing/submitting-your-work.md @@ -0,0 +1,223 @@ +# Submitting your work + +All contribution and collaboration is done through [GitHub](https://github.com). + +Once you have [set up your development environment](development-environment.md), committed to and pushed your branch, +it's time to create a [pull request (PR)](https://github.com/esphome/esphome/pulls). + +## Before you submit a PR... + +Please be sure you've read through our contributing guides: + +- [Code](code.md) +- [Documentation](docs.md) + +## Submitting a Pull Request + +After you have pushed your changes to GitHub, go to your repository fork and look for a "create pull request" button +near the top of the page (or, alternatively, go to branches and create it from there). As you create the PR: + +- Complete the Pull Request template: + - Include a brief (but complete) summary of your changes. + - PRs without a description/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 modify or 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. + +So now that you've created your PR...you're not quite done! Read on to the next section below so you know what to +expect next. + +## Review Process + +### Automated Checks + +At the bottom of each pull request you will see the "GitHub Actions" continuous integration (CI) checks which will +automatically analyze all code changed in your branch. These checks try to spot (and suggest corrections for) common +errors; they look like this: + +![Automated checks on PR by GitHub Actions](/images/gha_checks.jpg) + +You can click the "Details" link to the right of each check to see the logs for that check. If a red ❌ appears next to +any given check, *you'll need to view that check's logs and make the suggested changes so that the test will pass.* + +#### Implementing Feedback from Automated Checks + +Occasionally, an automated check may suggest a change that either isn't directly related to your PR or that may require +changes to other components/platforms. When this happens, please create a new/additional PR to implement this change. + +For example, the automated checks may suggest moving a constant from your (new) component/platform into `const.py`. +This is a simple change, but we require that it is done in a separate PR. + +Ultimately, **all automated checks must be passing** before maintainers will review and (eventually) merge your PR! + +### Review by Maintainers + +ESPHome's maintainers work hard to maintain a high standard for its code, so reviews by a human can take some time. + +**All automated checks must be passing** before maintainers will review and (eventually) merge your PR! See the +[automated checks](#automated-checks) section above. + +#### When will my PR be reviewed/merged? + +ESPHome is a big project; [we encourage everybody to test, review and comment on PRs.](#can-i-help-review-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: + +- **Keep PRs as small and as focused as possible.** Smaller PRs tend to be easier to understand and take less time to + review. Large PRs (many hundreds or thousands of lines) by their nature (of being large) tend to keep changing which + means reviewers have to revisit them over and over as they evolve. This isn't a tenable practice for project + maintainers. Break your work into multiple, smaller PRs and link these PRs together with comments in the description + so reviewers can follow the work more easily. +- The above bullet paraphrased: **we would rather review ten ten-line PRs than one 100-line PR.** +- **Be sure to follow all [codebase standards](code.md#codebase-standards).** 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. +- 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! + +## Can I Help Review PRs? + +**YES! PLEASE!!!** + +While only maintainers can *merge* PRs, we value feedback from the community and it *is considered* as we review them. +Put another way, when a PR has several "This worked for me!" comments on it, we know that the author's work is doing +what it's supposed to, even if some other, underlying aspects might still need some fine-tuning to be consistent with +the rest of the codebase. + +### Testing + +Often, the easiest way to help review PRs is by testing. Many (but not all) PRs can be used as +[external components](https://esphome.io/components/external_components) and can easily be added into your +configuration for testing, like this: + +```yaml +external_components: + - source: github://pr#2639 + components: [ rtttl ] +``` + +...you just need to update the PR number and component name(s) in the YAML accordingly. + +If you test a PR, please **share your results by leaving a comment on the PR!** If it doesn't work, be sure to include +any messages from the compiler and/or device logs so the author can troubleshoot the issue. +**Comments which state no more than "it doesn't work" are not helpful!** + +### Code Review + +Beyond basic functionality (*"does it work?"*), here are a few other items we check for when reviewing PRs: + +- Are file names & paths appropriate for/consistent with the codebase? +- Are namespace names consistent with the component/platform? +- Do all `#define` macro names match the namespace? +- Are all [codebase standards](code.md#codebase-standards) adhered to? +- Are there any calls to `delay()` with a duration longer than 10 milliseconds? +- Are any class methods doing work that they shouldn't be? For example, let's consider the `dump_config()` method: + - This method is intended to do **nothing** other than *print values* that were retrieved earlier (in `setup()`). + - If this method has (for example) a `this->read(...)` call in it, it does not pass review and needs to be changed. +- Is the component/platform doing *exactly what it's supposed to*? Consider the example of a new serial bus interface a + contributor has implemented: + - The author has implemented this component with an action called `superbus.send`. + - The author has concerns about too much traffic on the bus, so they have implemented a check in this action which + blocks duplicate message transmissions on the bus. The effect is that, if `superbus.send` is called repeatedly + with the same message, only the first call will actually send the message on the bus. + + This behavior is not consistent with what ESPHome users expect. If the action `superbus.send` is called, it should + *always* send the message, regardless of the content. If there are concerns about (in this example) bus + utilization, perhaps messages can be queued instead of dropped/ignored. + +## Why was my PR 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: + +![The ready for review button in the bottom of a PR in draft mode](/images/pr-draft-ready.png) + +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 + +There are a couple of ways you can update your (local) feature branch. + +- The easiest is by clicking the "Update branch" button in GitHub: + ![The "Update branch" button in GitHub](/images/update_branch.png) + ...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 run the following two commands: + + 1. Fetch the latest upstream changes: +```bash +git fetch upstream dev +``` + + 1. Merge in the changes we fetched above: +```bash +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: + +```bash +# Fetch the latest upstream changes +git fetch 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`. + +### Do not force-push! + +!!!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 as "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. diff --git a/docs/getting-started/development-environment.md b/docs/getting-started/development-environment.md deleted file mode 100644 index 5f1c7c3..0000000 --- a/docs/getting-started/development-environment.md +++ /dev/null @@ -1,74 +0,0 @@ -# Setting up a developement environment - -In order to develop ESPHome itself, or new ESPHome components, you will need to set up a -development environment. This guide will walk you through these steps. - -## Developing with a Python virtual environment - -*Note: These instructions apply for Linux and macOS. Windows users can still develop ESPHome and its components, -but the process is slightly different and not covered in this guide.* - -### Requirements - -- Python 3.12 or newer -- `pip` (Python package manager) - -### Set up the local repository - -First you will need a "Fork" of the ESPHome repository. You can do this by visiting -the [ESPHome repository](https://github.com/esphome/esphome), clicking the **Fork** button -and following the instructions on GitHub to complete the fork. - -Once the fork is created, you can clone the repository to your local machine: - -```bash -git clone https://github.com/YOUR_GITHUB_USERNAME/NAME_OF_FORK.git -cd NAME_OF_FORK -git remote add updtream https://github.com/esphome/esphome.git -``` - -Once the local clone is set up, you can now run the setup script. - -```bash -script/setup -``` - -This will create a Python virtual environment and install the requirements. - -### Activating the virtual environment - -To use the virtual environment, you need to activate it. This needs to be -done for each new terminal session and is done by running: - -```bash -source venv/bin/activate -``` - -Then ESPHome can be run directly from that terminal: - -```bash -esphome compile some-config-file.yaml -``` - -At this stage, it is also good to create an empty `config` directory that will be used for -your ESPHome configurations. - -This folder is listed in the ESPHome `.gitignore` file, so it will not be added to git. - -### Creating your own working branch - -Always do your work in a new branch, created from the latest ESPHome upstream `dev` branch. - -```bash -git checkout dev -git pull upstream dev - -git checkout -b my-new-feature -``` - -This branch should contain your work for this new feature. Once you are done and have committed -your changes, you can push your branch and create a pull request to the ESPHome repository. - -```bash -git push -u origin my-new-feature -``` diff --git a/docs/images/gha_checks.jpg b/docs/images/gha_checks.jpg new file mode 100644 index 0000000..6fb50a2 Binary files /dev/null and b/docs/images/gha_checks.jpg differ diff --git a/docs/images/ghedit_1.png b/docs/images/ghedit_1.png new file mode 100644 index 0000000..c80c6b9 Binary files /dev/null and b/docs/images/ghedit_1.png differ diff --git a/docs/images/ghedit_2.png b/docs/images/ghedit_2.png new file mode 100644 index 0000000..2698981 Binary files /dev/null and b/docs/images/ghedit_2.png differ diff --git a/docs/images/ghedit_3.png b/docs/images/ghedit_3.png new file mode 100644 index 0000000..5f26805 Binary files /dev/null and b/docs/images/ghedit_3.png differ diff --git a/docs/images/ghedit_4.png b/docs/images/ghedit_4.png new file mode 100644 index 0000000..f2680b6 Binary files /dev/null and b/docs/images/ghedit_4.png differ diff --git a/docs/images/ghedit_ci_details.png b/docs/images/ghedit_ci_details.png new file mode 100644 index 0000000..6123be1 Binary files /dev/null and b/docs/images/ghedit_ci_details.png differ diff --git a/docs/images/ghedit_ci_failed.png b/docs/images/ghedit_ci_failed.png new file mode 100644 index 0000000..45e206b Binary files /dev/null and b/docs/images/ghedit_ci_failed.png differ diff --git a/docs/images/ghedit_ci_logs.png b/docs/images/ghedit_ci_logs.png new file mode 100644 index 0000000..c5809a1 Binary files /dev/null and b/docs/images/ghedit_ci_logs.png differ diff --git a/docs/images/logo-core.svg b/docs/images/logo-core.svg new file mode 100644 index 0000000..07021bf --- /dev/null +++ b/docs/images/logo-core.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/logo-docs.svg b/docs/images/logo-docs.svg new file mode 100644 index 0000000..9c68e5c --- /dev/null +++ b/docs/images/logo-docs.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/images/logo-text.svg b/docs/images/logo-text.svg new file mode 100644 index 0000000..db4bcd5 --- /dev/null +++ b/docs/images/logo-text.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/images/logo.svg b/docs/images/logo.svg new file mode 100644 index 0000000..038b632 --- /dev/null +++ b/docs/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/pr-draft-ready.png b/docs/images/pr-draft-ready.png new file mode 100644 index 0000000..be8cc2f Binary files /dev/null and b/docs/images/pr-draft-ready.png differ diff --git a/docs/images/update_branch.png b/docs/images/update_branch.png new file mode 100644 index 0000000..01bb1e0 Binary files /dev/null and b/docs/images/update_branch.png differ diff --git a/docs/index.md b/docs/index.md index c0030d2..b146c4a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,15 +4,32 @@ hide: - toc --- -# ESPHome Developer Docs +# ESPHome Developer Documentation -All you need to start developing components for ESPHome +Start developing your own components for ESPHome! -[Not a developer? Go to the user documentation](https://esphome.io) +Not a developer? [Go to the user documentation.](https://esphome.io) -## Getting Started +--- + +# Start Here + +We welcome contributions to the ESPHome suite of code and documentation! + +All of the project code and documentation is hosted on [GitHub](https://github.com): + +- [ESPHome](https://github.com/esphome/esphome) +- [ESPHome-Docs](https://github.com/esphome/esphome-docs) + +## For New Developers + +Please see our [getting started guide](contributing/getting-started). + +## Contributing + +- [Code](contributing/code) and related [important notes](contributing/code-notes) +- [Documentation](contributing/docs) - - [Setting up your development environment](getting-started/development-environment) - - [ESPHome component structure](architecture/components) +## Going Further -## +- [ESPHome component architecture](architecture/components) diff --git a/mkdocs.yml b/mkdocs.yml index 8bfd547..6cf23a8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: ESPHome Developer Docs +site_name: ESPHome Developer Documentation site_author: ESPHome site_url: https://developers.esphome.io/ @@ -43,6 +43,11 @@ theme: icon: material/brightness-4 name: Switch to light mode +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + plugins: - blog - privacy @@ -53,11 +58,40 @@ hooks: nav: - Home: index.md - - Getting Started: - - "Setting up environment" + - Contributing: + - Getting started: contributing/getting-started.md + - Development environment: contributing/development-environment.md + - Submitting your work: contributing/submitting-your-work.md + - Contributing: + - Code: contributing/code.md + - Documentation: contributing/docs.md + - Important notes: contributing/code-notes.md - Architecture: + - Overview: architecture/overview.md - Core: architecture/core.md - Components: + - Overview: architecture/components/index.md + - Alarm control panel: architecture/components/alarm_control_panel.md + - Binary sensor: architecture/components/binary_sensor.md + - Button: architecture/components/button.md + - Climate: architecture/components/climate.md + - Cover: architecture/components/cover.md + - Display: architecture/components/display.md + - Event: architecture/components/event.md + - Fan: architecture/components/fan.md + - Light: architecture/components/light.md + - Lock: architecture/components/lock.md + - Media player: architecture/components/media_player.md + - Number: architecture/components/number.md + - Output: architecture/components/output.md + - Select: architecture/components/select.md - Sensor: architecture/components/sensor.md + - Speaker: architecture/components/speaker.md + - Switch: architecture/components/switch.md + - Text: architecture/components/text.md + - Text sensor: architecture/components/text_sensor.md + - Time: architecture/components/time.md + - Touchscreen: architecture/components/touchscreen.md + - Valve: architecture/components/valve.md - Blog: blog/index.md