mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-07-12 11:56:29 +00:00
Docs to talk about intregrations (#215)
* Add manifest docs * Talk about integrations * Apply suggestions from code review Co-Authored-By: balloob <paulus@home-assistant.io> * Address comments * Apply suggestions from code review Co-Authored-By: balloob <paulus@home-assistant.io> * Address comments * Final comment * Mention codeowners script
This commit is contained in:
parent
950fabe2bd
commit
ba924f7330
@ -1,8 +1,11 @@
|
||||
---
|
||||
title: Config Flow Handlers
|
||||
title: Integration Configuration
|
||||
sidebar_label: Configuration
|
||||
---
|
||||
|
||||
Config Entries uses the [Data Flow Entry framework](data_entry_flow_index.md) to allow users to create entries. Components that want to support config entries will need to define a Config Flow Handler. This handler will manage the creation of entries from user input, discovery or other sources (like hassio).
|
||||
> This option is currently only available for built-in components.
|
||||
|
||||
Integrations can be set up via the user interface by adding support for config entries. Config entries uses the [data flow entry framework](data_entry_flow_index.md) to allow users to create entries. Components that want to support config entries will need to define a Config Flow Handler. This handler will manage the creation of entries from user input, discovery or other sources (like Hass.io).
|
||||
|
||||
Config Flow Handlers control the data that is stored in a config entry. This means that there is no need to validate that the config is correct when Home Assistant starts up. It will also prevent breaking changes, because we will be able to migrate configuration entries to new formats if the version changes.
|
||||
|
||||
@ -11,25 +14,25 @@ When instantiating the handler, Home Assistant will make sure to load all depend
|
||||
To register your config flow handler with Home Assistant, register it with the config entries `HANDLERS` registry:
|
||||
|
||||
```python
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant import config_entries
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
class ExampleConfigFlow(config_entries.ConfigFlow):
|
||||
```
|
||||
|
||||
> Temporarily, all config flow handlers will also need to add their component name to the `FLOWS` constant in `homeassistant/config_entries.py`. We are working on automating discovery.
|
||||
All config flow handlers will also need to add their domain name to the `FLOWS` constant in `homeassistant/config_entries.py`.
|
||||
|
||||
## Initializing a config flow from an external source
|
||||
## Discovering your config flow
|
||||
|
||||
You might want to initialize a config flow programmatically. For example, if we discover a device on the network that requires user interaction to finish setup. To do so, pass a source parameter and optional user input when initializing the config entry:
|
||||
Home Assistant has a discovery integration that scans the network for available devices and services and will trigger the config flow of the appropriate integration. Discovery is limited to UPnP and zeroconf/mDNS.
|
||||
|
||||
```python
|
||||
await hass.config_entries.flow.async_init(
|
||||
'hue', data=discovery_info,
|
||||
context={'source': config_entries.SOURCE_DISCOVERY})
|
||||
```
|
||||
To have your integration be discovered, you will have to extend the [NetDisco library](https://github.com/home-assistant/netdisco) to be able to find your device. This is done by adding a new discoverable. [See the repository for examples of existing discoverable.](https://github.com/home-assistant/netdisco/tree/master/netdisco/discoverables)
|
||||
|
||||
The config flow handler will need to add a step to support the given source. The step should follow the same return values as a normal step.
|
||||
Once done, you will have to update the discovery integration to make it aware which discovery maps to which integration, by updating [this list](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/discovery/__init__.py#L55).
|
||||
|
||||
Finally, you will have to add support to your config flow to be triggered from discovery. This is done by adding a new discovery step. Make sure that your discovery step does not automatically create an entry. All discovered config flows are required to have a confirmation from the user.
|
||||
|
||||
Once discovered, the user will be notified that they can continue the flow from the config panel.
|
||||
|
||||
```python
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
@ -39,8 +42,6 @@ class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
# Handle discovery info
|
||||
```
|
||||
|
||||
If the result of the step is to show a form, the user will be able to continue the flow from the config panel.
|
||||
|
||||
## Translations
|
||||
|
||||
Translations for the config flow handlers are defined under the `config` key in the component translation file `strings.json`. Example of the Hue component:
|
||||
@ -76,3 +77,15 @@ Translations for the config flow handlers are defined under the `config` key in
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the translations are merged into Home Assistant, they will be automatically uploaded to [Lokalise](https://lokalise.co/) where the translation team will help to translate them in other languages. [More info on translating Home Assistant.](internationalization_translation.md)
|
||||
|
||||
## Triggering other config flows
|
||||
|
||||
If you are writing an integration that discovers other integrations, you will want to trigger their config flows so the user can set them up. Do this by passing a source parameter and optional user input when initializing the config entry:
|
||||
|
||||
```python
|
||||
await hass.config_entries.flow.async_init(
|
||||
'hue', data=discovery_info,
|
||||
context={'source': config_entries.SOURCE_DISCOVERY})
|
||||
```
|
||||
|
@ -117,12 +117,15 @@ async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||
|
||||
## Unloading entries
|
||||
|
||||
Components can optionally support unloading a config entry. When unloading an entry, the component needs
|
||||
to clean up all entities, unsubscribe any event listener and close all connections. To implement this,
|
||||
add `async_unload_entry(hass, entry)` to your component ([example](https://github.com/home-assistant/home-assistant/blob/0.68.0/homeassistant/components/hue/__init__.py#L136)).
|
||||
Components can optionally support unloading a config entry. When unloading an entry, the component needs to clean up all entities, unsubscribe any event listener and close all connections. To implement this, add `async_unload_entry(hass, entry)` to your component ([example](https://github.com/home-assistant/home-assistant/blob/0.68.0/homeassistant/components/hue/__init__.py#L136)).
|
||||
|
||||
Platforms will not need to add any logic for unloading a config entry. The entity component will take care of this.
|
||||
If you need to clean up resources used for an entity, implement the `async_will_remove_from_hass` method on the Entity ([example](https://github.com/home-assistant/home-assistant/blob/0.68.0/homeassistant/components/media_player/cast.py#L313)).
|
||||
For each platform that you forwarded the config entry to, you will need to forward the unloading too.
|
||||
|
||||
```python
|
||||
await self.hass.config_entries.async_forward_entry_unload(self.config_entry, 'light')
|
||||
```
|
||||
|
||||
If you need to clean up resources used by an entity in a platform, have the entity implement the [`async_will_remove_from_hass`](entity_index.md#async_will_remove_from_hass) method.
|
||||
|
||||
## Removal of entries
|
||||
|
||||
|
@ -1,12 +1,17 @@
|
||||
---
|
||||
title: Options Flow Handlers
|
||||
title: Integration Configuration Options
|
||||
sidebar_label: Configuration Options
|
||||
---
|
||||
|
||||
> This option is currently only available for built-in components.
|
||||
|
||||
An integration that is configured via a config entry can expose options to the user to allow tweaking behavior of the integration, like which devices or locations should be integrated.
|
||||
|
||||
Config Entry Options uses the [Data Flow Entry framework](data_entry_flow_index.md) to allow users to update a config entries options. Components that want to support config entry options will need to define a Options Flow Handler.
|
||||
|
||||
## Options support
|
||||
|
||||
For a component to support options it needs to have an `async_get_options_flow` method in its config flow handler. Calling it will return an instance of the components options flow handler.
|
||||
For an integration to support options it needs to have an `async_get_options_flow` method in its config flow handler. Calling it will return an instance of the components options flow handler.
|
||||
|
||||
```python
|
||||
@staticmethod
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Configuration.yaml"
|
||||
sidebar_label: Introduction
|
||||
title: "Integration Configuration via YAML"
|
||||
sidebar_label: Configuration via YAML
|
||||
---
|
||||
|
||||
`configuration.yaml` is a configuration file defined by the user. It is automatically created by Home Assistant on first launch. It defines which components to load.
|
||||
|
@ -1,5 +1,6 @@
|
||||
---
|
||||
title: "Checklist for creating a component"
|
||||
sidebar_label: Component Checklist
|
||||
---
|
||||
|
||||
A checklist of things to do when you're adding a new component.
|
||||
@ -14,9 +15,10 @@ A checklist of things to do when you're adding a new component.
|
||||
|
||||
### 1. Requirements
|
||||
|
||||
1. Requirement version pinned: `REQUIREMENTS = ['phue==0.8.1']`
|
||||
2. We no longer want requirements hosted on GitHub. Please upload to PyPi.
|
||||
3. Requirements should [only be imported inside functions](creating_component_deps_and_reqs.md). This is necessary because requirements are installed on the fly.
|
||||
1. Requirements have been added to [`manifest.json`](creating_integration_manifest.md). The `REQUIREMENTS` constant is deprecated.
|
||||
2. Requirement version should be pinned: `"requirements": ['phue==0.8.1']`
|
||||
3. We no longer want requirements hosted on GitHub. Please upload to PyPi.
|
||||
4. Requirements should only be imported inside functions. This is necessary because requirements are installed on the fly.
|
||||
|
||||
### 2. Configuration
|
||||
|
||||
@ -29,34 +31,38 @@ A checklist of things to do when you're adding a new component.
|
||||
|
||||
### 3. Component/platform communication
|
||||
|
||||
1. If you need to share global data with platforms, use the dictionary `hass.data`. `hass.data[DATA_XY]` while `XY` is the component is preferred over `hass.data[DOMAIN]`.
|
||||
1. You can share data with your platforms by leveraging `hass.data[DOMAIN]`.
|
||||
2. If the component fetches data that causes its related platform entities to update, you can notify them using the dispatcher code in `homeassistant.helpers.dispatcher`.
|
||||
|
||||
|
||||
### 4. Communication with devices/services
|
||||
|
||||
1. All API specific code has to be part of a third party library hosted on PyPi. Home Assistant should only interact with objects and not make direct calls to the API.
|
||||
|
||||
```python
|
||||
# bad
|
||||
status = requests.get(url('/status'))
|
||||
```python
|
||||
# bad
|
||||
status = requests.get(url('/status'))
|
||||
|
||||
# good
|
||||
from phue import Bridge
|
||||
bridge = Bridge(...)
|
||||
status = bridge.status()
|
||||
```
|
||||
# good
|
||||
from phue import Bridge
|
||||
bridge = Bridge(...)
|
||||
status = bridge.status()
|
||||
```
|
||||
|
||||
[Tutorial on publishing your own PyPI package](https://jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/)
|
||||
|
||||
### 5. Make your pull request as small as possible
|
||||
|
||||
Keep a new integration to the minimum functionality needed for someone to get value out of the integration. This allows reviewers to sign off on smaller chunks of code one at a time, and lets us get your new integration/features in sooner. **Pull requests containing large code dumps will not be a priority for review and may be closed.**
|
||||
- Limit to a single platform
|
||||
|
||||
- Limit to a single platform
|
||||
- Do not add features not needed to directly support the single platform (such as custom services)
|
||||
- Do not mix clean-ups and new features in a single pull request.
|
||||
- Do not solve several issues in a single pull request.
|
||||
- Do not submit pull requests that depend on other work which is still unmerged.
|
||||
|
||||
### 6. Event names
|
||||
Prefix component event names with the component name itself. For example, use `netatmo_person` instead of `person` for the `netatmo` component.
|
||||
Prefix component event names with the domain name. For example, use `netatmo_person` instead of `person` for the `netatmo` component.
|
||||
|
||||
### 7. Tests
|
||||
|
||||
Strongly consider adding tests for your component to minimize future regressions.
|
||||
|
@ -1,66 +1,7 @@
|
||||
---
|
||||
title: "Requirements & Dependencies"
|
||||
title: ""
|
||||
---
|
||||
|
||||
Home Assistant allows components and platforms to specify their dependencies and requirements using the variables `DEPENDENCIES` and `REQUIREMENTS`. Both are lists that contain strings.
|
||||
[This page has moved.](creating_integration_manifest.md)
|
||||
|
||||
## Dependencies
|
||||
|
||||
Dependencies are other Home Assistant components that should be setup before the platform is loaded. An example is the MQTT sensor component, which requires an active connection to an MQTT broker. If Home Assistant is unable to load and setup the MQTT component, it will not setup the MQTT sensor component.
|
||||
|
||||
```python
|
||||
DEPENDENCIES = ['mqtt']
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
Requirements are Python libraries or modules that you would normally install using `pip` for your component. Home Assistant will try to install the requirements into the `deps` subdirectory of the Home Assistant [configuration directory](https://www.home-assistant.io/docs/configuration/) if you are not using a `venv` or in something like `path/to/venv/lib/python3.6/site-packages` if you running in a virtual environment. This will make sure that all requirements are present at startup. If steps fails like missing packages for the compilation of a module or other install errors, the component will fail to load.
|
||||
|
||||
Requirements is a list of strings. Each entry is a `pip` compatible string. For example, the media player Cast platform depends on the Python package PyChromecast v0.6.12:
|
||||
|
||||
```python
|
||||
REQUIREMENTS = ['pychromecast==0.6.12']
|
||||
```
|
||||
|
||||
### Note
|
||||
|
||||
Be aware that actual python imports of these dependencies should be done inside functions that use them. This is because Home Assistant installs requirements on demand and so the requirement won't be loaded the first time your component is loaded.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
REQUIREMENTS = ['pychromecast==0.6.12']
|
||||
|
||||
def setup(hass, config):
|
||||
import pychromecast
|
||||
|
||||
<your code goes here>
|
||||
```
|
||||
|
||||
### Custom requirements during development & testing
|
||||
|
||||
During development of a component, it can be useful to test against different versions of a requirement. This can be done in two steps, using pychromecast as an example:
|
||||
|
||||
* `pip install pychromecast==0.6.13 --target ~/.homeassistant/deps`
|
||||
* `hass --skip-pip`
|
||||
|
||||
This will use the specified version, and prevent Home Assistant from trying to override it with what is currently in `REQUIREMENTS`.
|
||||
|
||||
If you need to make changes to a requirement to support your component, it's also possible to `pip install` from a checkout of the requirement.
|
||||
|
||||
* `git clone https://github.com/balloob/pychromecast.git`
|
||||
* `pip install ./pychromecast`
|
||||
* `hass --skip-pip`
|
||||
|
||||
For testing and development purposes you can also to point to a hosted package in the form of an archive (zip/tar.gz) file as a requirement. GitHub provides archive files for a specific branch, release or even a specific commit. To do that the string in `REQUIREMENTS` needs to be composed of two parts:
|
||||
|
||||
* an URL pointing to the archive file (e.g. `https://github.com/my/repo/archive/branch-x.zip`)
|
||||
* a hashtag and `pip` string (as described above) to declare what package and version that archive file contains
|
||||
|
||||
Note: Components and platforms included in Home Assistant should point to published PyPI packages. This ensures that the unit tests will not be slowed down as they can be cached.
|
||||
|
||||
For example, the Neato robot vacuum components specifies the v.0.0.5 release on GitHub as a requirement that gets installed as pybotvac version 0.0.5 (`pybotvac==0.0.5`).
|
||||
|
||||
```python
|
||||
REQUIREMENTS = ['https://github.com/jabesq/pybotvac/archive/v0.0.5.zip#pybotvac==0.0.5']
|
||||
```
|
||||
<script>document.location = 'creating_integration_manifest.html';</script>
|
||||
|
@ -1,51 +0,0 @@
|
||||
---
|
||||
title: "Component Discovery"
|
||||
---
|
||||
|
||||
> This option is only available for built-in components.
|
||||
|
||||
Home Assistant has a discovery service running in the background to discover new devices. Whenever a new device is discovered, a `SERVICE_DISCOVERED` event will be fired with the found service and the information. The `discovery` component has some knowledge about which components handle which type of services and will ensure those are loaded and listening before firing the `SERVICE_DISCOVERED` event.
|
||||
|
||||
### Add discovery instructions
|
||||
|
||||
Device discovery for Home Assistant has been extracted into an external library called [NetDisco](https://github.com/home-assistant/netdisco). This library is integrated using [the `discovery` component](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/discovery.py) and scans the network in intervals for uPnP and zeroconf/mDNS services.
|
||||
|
||||
To have your device be discovered, you will have to extend the NetDisco library to be able to find your device. This is done by adding a new discoverable. [See the repository for examples of existing discoverable.](https://github.com/home-assistant/netdisco/tree/master/netdisco/discoverables)
|
||||
|
||||
### Listening to `SERVICE_DISCOVERED` events
|
||||
|
||||
From your component, you will have to set up the listening for specific services. Given below is an example how one would listen for a discovered AwesomeDevice:
|
||||
|
||||
```python
|
||||
from homeassistant.components.discovery import SERVICE_AWESOMEDEVICE
|
||||
from homeassistant.helpers import discovery
|
||||
|
||||
DOMAIN = 'awesomedevice'
|
||||
|
||||
DEPENDENCIES = ['http']
|
||||
|
||||
def setup(hass, config):
|
||||
cfg = config.get(DOMAIN)
|
||||
|
||||
def device_discovered(service, info):
|
||||
""" Called when a Awesome device has been discovered. """
|
||||
print("Discovered a new Awesome device: {}".format(info))
|
||||
|
||||
discovery.listen(
|
||||
hass, SERVICE_AWESOMEDEVICE, device_discovered)
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
### Auto-loading your component upon discovery
|
||||
|
||||
The `discovery` component is capable of setting up your components before firing the `EVENT_PLATFORM_DISCOVERED` event. To do this you will have to update the [`SERVICE_HANDLERS`](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/discovery.py#L40) constant in [the `discovery` component](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/discovery.py):
|
||||
|
||||
```python
|
||||
SERVICE_AWESOMEDEVICE = 'awesomedevice'
|
||||
|
||||
SERVICE_HANDLERS = {
|
||||
...
|
||||
SERVICE_AWESOMEDEVICE: ('awesomedevice', None),
|
||||
}
|
||||
```
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
title: "Handling events"
|
||||
---
|
||||
|
||||
Home Assistant has different ways of responding to events that occur in Home Assistant. These have been organized in [helper methods](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/helpers/event.py). Examples are `track_state_change`, `track_point_in_time`, `track_time_change`.
|
@ -1,70 +1,21 @@
|
||||
---
|
||||
title: "Generic Platform Discovery"
|
||||
title: "Integration with Multiple Platforms"
|
||||
sidebar_label: Multiple platforms
|
||||
---
|
||||
|
||||
New controller or hub components often need to add platforms in sub-components (i.e. Lights & Switches) without additional configuration.
|
||||
This can be achieved using the `load_platform` or `async_load_platform` methods from `homeassistant.helpers.discovery`:
|
||||
Most integrations consist of a single platform. And in that case, it's fine to just define that one platform. However, if you are going to add a second platform, you will want to centralize your connection logic. This is done inside the component (`__init__.py`).
|
||||
|
||||
```python
|
||||
def load_platform(hass, component, platform, discovered, hass_config)
|
||||
```
|
||||
If your integration is configurable via `configuration.yaml`, it will cause the entry point of your configuration to change, as now users will need to set up your integration directly, and it is up to your integration to set up the platforms.
|
||||
|
||||
From more info on how this works, refer to the [load_platform](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/helpers/discovery.py#L117) method.
|
||||
## Loading platforms when configured via a config entry
|
||||
|
||||
### Example
|
||||
If your integration is set up via a config entry, you will need to forward the config entry to the appropriate integration to set up your platform. For more info, see the [config entry documentation](config_entries_index.md#for-platforms).
|
||||
|
||||
Say you need to implement your new MyFlashyHub that controls both Switches & Lights, you can follow these steps:
|
||||
## Loading platforms when configured via configuration.yaml
|
||||
|
||||
Configuration required for your new hub component:
|
||||
If your integration is not using config entries, it will have to use our discovery helpers to set up its platforms. Note, this approach does not support unloading.
|
||||
|
||||
```yaml
|
||||
myflashyhub:
|
||||
example: setting
|
||||
```
|
||||
|
||||
The source for your component can be located in your configuration directory for now:
|
||||
|
||||
```bash
|
||||
~/.homeassistant/custom_components/myflashyhub.py
|
||||
~/.homeassistant/custom_components/light/myflashyhub.py
|
||||
~/.homeassistant/custom_components/switch/myflashyhub.py
|
||||
```
|
||||
|
||||
In the hub component `myflashyhub.py` you can call your light and switch components. To pass any non-serializable information to the platforms in the sub-component, you should use `hass.data`.
|
||||
|
||||
```python
|
||||
from homeassistant.helpers.discovery import load_platform
|
||||
|
||||
DOMAIN = 'myflashyhub'
|
||||
DATA_MFH = 'MFH'
|
||||
|
||||
def setup(hass, hass_config):
|
||||
"""Your controller/hub specific code."""
|
||||
hass.data[DATA_MFH] = SomeObjectToInitialise()
|
||||
|
||||
#--- snip ---
|
||||
load_platform(hass, 'light', DOMAIN, None, hass_config)
|
||||
load_platform(hass, 'switch', DOMAIN, {'optional': 'arguments'}, hass_config)
|
||||
```
|
||||
|
||||
Add your custom device specific code to the `setup_platform` method in `light/myflashyhub.py` and `switch/myflashyhub.py`.
|
||||
|
||||
```python
|
||||
import custom_components.myflashyhub as myflashyhub
|
||||
|
||||
# 'switch' will receive discovery_info={'optional': 'arguments'}
|
||||
# as passed in above. 'light' will receive discovery_info=None
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Your switch/light specific code."""
|
||||
# You can now use hass.data[myflashyhub.DATA_MFH]
|
||||
```
|
||||
To do this, you will need to use the `load_platform` and `async_load_platform` methods from the discovery helper.
|
||||
|
||||
|
||||
The `load_platform` method allows the platforms to be loaded without the need for any additional platform entries in your `configuration.yaml` file, which normally would have been:
|
||||
|
||||
```yaml
|
||||
#light:
|
||||
# platform: myflashyhub
|
||||
#switch:
|
||||
# platform: myflashyhub
|
||||
```
|
||||
- See also a [full example that implementing this logic](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_load_platform/)
|
||||
|
@ -1,19 +1,38 @@
|
||||
---
|
||||
title: "Creating components"
|
||||
sidebar_label: "Introduction"
|
||||
title: "Creating a Minimal Component"
|
||||
sidebar_label: "Minimal Component"
|
||||
---
|
||||
|
||||
Alright, you're ready to make your first component. AWESOME. Don't worry, we've tried hard to keep it as easy as possible.
|
||||
Alright, you learned about the [manifest](creating_integration_manifest.md), so it's time to write your first code for your integration. AWESOME. Don't worry, we've tried hard to keep it as easy as possible.
|
||||
|
||||
### Example component
|
||||
More extensive examples of integrations are available from [our example repository](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/).
|
||||
|
||||
Add `hello_state:` to your `configuration.yaml` file and create a file `<config_dir>/custom_components/hello_state.py` with the below code to test it locally.
|
||||
## The code
|
||||
|
||||
Each component needs to have 2 basic parts: it needs to define a `DOMAIN` constant that contains the domain of the integration. The second part is that it needs to define a setup method that returns a boolean if the set up was successful. So let's take a look at how this looks:
|
||||
|
||||
```python
|
||||
DOMAIN = 'hello_state'
|
||||
|
||||
def setup(hass, config):
|
||||
hass.states.set('hello.world', 'Paulus')
|
||||
hass.states.set('hello_state.world', 'Paulus')
|
||||
|
||||
# Return boolean to indicate that initialization was successful.
|
||||
return True
|
||||
```
|
||||
|
||||
And if you prefer an async component:
|
||||
|
||||
```python
|
||||
DOMAIN = 'hello_state'
|
||||
|
||||
async def async_setup(hass, config):
|
||||
hass.states.async_set('hello_state.world', 'Paulus')
|
||||
|
||||
# Return boolean to indicate that initialization was successful.
|
||||
return True
|
||||
```
|
||||
|
||||
That's it! If you load this, you will see a new state in the state machine.
|
||||
|
||||
To load this, add `hello_state:` to your `configuration.yaml` file and create a file `<config_dir>/custom_components/hello_state/__init__.py` with the below code to test it locally.
|
||||
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "Loading your components"
|
||||
---
|
||||
|
||||
A component will be loaded on start if a section (ie. `light:`) for it exists in the config file. A component can also be loaded if another component is loaded that depends on it. When loading a component Home Assistant will check the following paths:
|
||||
|
||||
* `<config directory>/custom_components/<component name>`
|
||||
* `homeassistant/components/<component name>` (built-in components)
|
||||
|
||||
Once loaded, a component will only be setup if all dependencies can be loaded and are able to setup. Keep an eye on the logs to see if your component could be loaded and initialized.
|
||||
|
||||
> You can override a built-in component by having a component with the same name in your <code>config/custom_components</code> folder. If the built-in component is inside a subfolder, take care to place your customization in a folder with the same name in <code>config/custom_components/*folder*</code>. Note that overriding built-in components is not recommended and will probably break things!
|
||||
|
||||
> Home Assistant will use the directory that contains your config file as the directory that holds your customizations. By default this is the <code>config</code> folder in your current work directory. You can use a different folder by running Home Assistant with the --config argument: <code>python3 homeassistant --config /YOUR/CONFIG/PATH/</code>.
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "Handling states"
|
||||
---
|
||||
|
||||
It is the responsibility of the component to maintain the states of the devices in your domain. Each device should be a single state and, if possible, a group should be provided that tracks the combined state of the devices.
|
||||
|
||||
A state can have several attributes that will help the frontend in displaying your state:
|
||||
|
||||
- `friendly_name`: this name will be used as the name of the device
|
||||
- `entity_picture`: this picture will be shown instead of the domain icon
|
||||
- `unit_of_measurement`: this will be appended to the state in the interface
|
||||
- `hidden`: This is a suggestion to the frontend on if the state should be hidden
|
||||
|
||||
These attributes are defined in [homeassistant.helpers.entity](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/helpers/entity.py#L180).
|
||||
|
31
docs/creating_integration_file_structure.md
Normal file
31
docs/creating_integration_file_structure.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
title: "Integration File Structure"
|
||||
sidebar_label: "File Structure"
|
||||
---
|
||||
|
||||
Each integration is stored inside a directory named after the integration domain. The domain is a short name consisting of characters and underscores. This domain has to be unique and cannot be changed. Example of the domain for the mobile app integration: `mobile_app`. So all files for this integration are in the folder `mobile_app/`.
|
||||
|
||||
The bare minimum content of this folder looks like this:
|
||||
|
||||
- `manifest.json`: The manifest file describes the integration and its dependencies. [More info](creating_integration_manifest.md)
|
||||
- `__init__.py`: The component file. If the integration only offers a platform, you can keep this file limited to a docstring introducing the integration `"""The Mobile App integration."""`.
|
||||
|
||||
## Integrating devices - `light.py`, `switch.py` etc
|
||||
|
||||
If your integration is going to integrate one or more devices, you will need to do this by creating a platform that interacts with an entity integration. For example, if you want to represent a light device inside Home Assistant, you will create `light.py`, which will contain a light platform for the light integration.
|
||||
|
||||
- More info on [available entity integrations](entity_index.md).
|
||||
- More info on [creating platforms](creating_platform_index.md).
|
||||
|
||||
## Integrating services - `services.yaml`
|
||||
|
||||
If your integration is going to register services, it will need to provide a description of the available services. The description is stored in `services.yaml`. [More information about `services.yaml`.](dev_101_services.md)
|
||||
|
||||
## Where Home Assistant looks for integrations
|
||||
|
||||
Home Assistant will look for an integration when it sees the domain referenced in the config file (i.e. `mobile_app:`) or if it is a dependency of another integration. Home Assistant will look at the following locations:
|
||||
|
||||
* `<config directory>/custom_components/<domain>`
|
||||
* `homeassistant/components/<domain>` (built-in integrations)
|
||||
|
||||
You can override a built-in integration by having an integration with the same domain in your `config/custom_components` folder. Note that overriding built-in components is not recommended as you will no longer get updates. It is recommended to pick a unique name.
|
77
docs/creating_integration_manifest.md
Normal file
77
docs/creating_integration_manifest.md
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
title: "Integration Manifest"
|
||||
sidebar_label: "Manifest"
|
||||
---
|
||||
|
||||
Each integration has a manifest file to specify basic information about an integration. This file is stored as `manifest.json` in your integration directory. It is required to add such a file, including for custom components.
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": "hue",
|
||||
"name": "Philips Hue",
|
||||
"documentation": "https://www.home-assistant.io/components/hue",
|
||||
"dependencies": ["mqtt"],
|
||||
"codeowners": ["@balloob"],
|
||||
"requirements": ["aiohue==1.9.1"]
|
||||
}
|
||||
```
|
||||
|
||||
Or a minimal example that you can copy into your project:
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": "your_domain_name",
|
||||
"name": "Your Intgration",
|
||||
"documentation": "https://www.example.com",
|
||||
"dependencies": [],
|
||||
"codeowners": [],
|
||||
"requirements": []
|
||||
}
|
||||
```
|
||||
|
||||
## Domain
|
||||
|
||||
The domain is a short name consisting of characters and underscores. This domain has to be unique and cannot be changed. Example of the domain for the mobile app integration: `mobile_app`.
|
||||
|
||||
## Name
|
||||
|
||||
The name of the integration.
|
||||
|
||||
## Documentation
|
||||
|
||||
The website containing documentation on how to use your integration. If this integration is being submitted for inclusion in Home Assistant, it should be `https://www.home-assistant.io/components/<domain>`
|
||||
|
||||
## Dependencies
|
||||
|
||||
Dependencies are other Home Assistant integrations that you want Home Assistant to set up successfully prior to the integration being loaded. This can be necessary in case you want to offer functionality from that other integration, like using webhooks or an MQTT connection.
|
||||
|
||||
## Code Owners
|
||||
|
||||
GitHub usernames or team names of people that are responsible for this integration. You should add at least your GitHub username here, as well as anyone who helped you to write code that is being included.
|
||||
|
||||
## Requirements
|
||||
|
||||
Requirements are Python libraries or modules that you would normally install using `pip` for your component. Home Assistant will try to install the requirements into the `deps` subdirectory of the Home Assistant [configuration directory](https://www.home-assistant.io/docs/configuration/) if you are not using a `venv` or in something like `path/to/venv/lib/python3.6/site-packages` if you running in a virtual environment. This will make sure that all requirements are present at startup. If steps fail, like missing packages for the compilation of a module or other install errors, the component will fail to load.
|
||||
|
||||
Requirements is a list of strings. Each entry is a `pip` compatible string. For example, the media player Cast platform depends on the Python package PyChromecast v0.6.12: `['pychromecast==0.6.12']`.
|
||||
|
||||
> Because of how Home Assistant installs requirements on demand, actual Python imports of your requirements should be done inside functions instead of at the root level of your Python files.
|
||||
|
||||
### Custom requirements during development & testing
|
||||
|
||||
During the development of a component, it can be useful to test against different versions of a requirement. This can be done in two steps, using `pychromecast` as an example:
|
||||
|
||||
```bash
|
||||
pip install pychromecast==0.6.13 --target ~/.homeassistant/deps
|
||||
hass --skip-pip
|
||||
```
|
||||
|
||||
This will use the specified version, and prevent Home Assistant from trying to override it with what is specified in `requirements`.
|
||||
|
||||
If you need to make changes to a requirement to support your component, it's also possible to install a development version of the requirement using `pip install -e`:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/balloob/pychromecast.git
|
||||
pip install ./pychromecast
|
||||
hass --skip-pip
|
||||
```
|
@ -1,5 +1,6 @@
|
||||
---
|
||||
title: "Checklist for creating a platform"
|
||||
sidebar_label: Platform Checklist
|
||||
---
|
||||
|
||||
A checklist of things to do when you're adding a new platform.
|
||||
@ -15,63 +16,76 @@ A checklist of things to do when you're adding a new platform.
|
||||
|
||||
### 1. Requirements
|
||||
|
||||
1. Requirement version should be pinned: `REQUIREMENTS = ['phue==0.8.1']`
|
||||
2. We no longer want requirements hosted on GitHub. Please upload to PyPi.
|
||||
3. Requirements should [only be imported inside functions](creating_component_deps_and_reqs.md). This is necessary because requirements are installed on the fly.
|
||||
1. Requirements have been added to [`manifest.json`](creating_integration_manifest.md). The `REQUIREMENTS` constant is deprecated.
|
||||
2. Requirement version should be pinned: `"requirements": ['phue==0.8.1']`
|
||||
3. We no longer want requirements hosted on GitHub. Please upload to PyPi.
|
||||
4. Requirements should only be imported inside functions. This is necessary because requirements are installed on the fly.
|
||||
|
||||
### 2. Dependencies
|
||||
|
||||
1. If you depend on a component for the connection, add it to your dependencies: `DEPENDENCIES = ['nest']`
|
||||
1. If you depend on a component for the connection, add it to your dependencies in [`manifest.json`](creating_integration_manifest.md): `"dependencies": ['nest']`. The `DEPENDENCIES` constant is deprecated.
|
||||
|
||||
### 3. Configuration
|
||||
|
||||
1. Voluptuous schema present for [configuration validation](development_validation.md)
|
||||
2. Voluptuous schema extends schema from component<br>(e.g., `light.hue.PLATFORM_SCHEMA` extends `light.PLATFORM_SCHEMA`)
|
||||
2. Voluptuous schema extends schema from component<br>(e.g., `hue.light.PLATFORM_SCHEMA` extends `light.PLATFORM_SCHEMA`)
|
||||
3. Default parameters specified in voluptuous schema, not in `setup_platform(...)`
|
||||
4. Your `PLATFORM_SCHEMA` should use as many generic config keys as possible from `homeassistant.const`
|
||||
```python
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_FILENAME, CONF_HOST
|
||||
from homeassistant.components.light import PLATFORM_SCHEMA
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
CONF_ALLOW_UNREACHABLE = 'allow_unreachable'
|
||||
DEFAULT_UNREACHABLE = False
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_ALLOW_UNREACHABLE,
|
||||
default=DEFAULT_UNREACHABLE): cv.boolean,
|
||||
vol.Optional(CONF_FILENAME): cv.string,
|
||||
})
|
||||
```
|
||||
5. Never depend on users adding things to `customize` to configure behavior inside your platform.
|
||||
|
||||
```python
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_FILENAME, CONF_HOST
|
||||
from homeassistant.components.light import PLATFORM_SCHEMA
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
CONF_ALLOW_UNREACHABLE = 'allow_unreachable'
|
||||
DEFAULT_UNREACHABLE = False
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_ALLOW_UNREACHABLE,
|
||||
default=DEFAULT_UNREACHABLE): cv.boolean,
|
||||
vol.Optional(CONF_FILENAME): cv.string,
|
||||
})
|
||||
```
|
||||
|
||||
### 4. Setup Platform
|
||||
|
||||
1. Test if passed in info (user/pass/host etc.) works.
|
||||
1. Verify that the passed in configuration (user/pass/host etc.) works.
|
||||
2. Group your calls to `add_devices` if possible.
|
||||
3. If platform adds extra services, format should be `<component>.<platform>_<service name>`.
|
||||
3. If the platform adds extra services, the format should be `<domain of your integration>.<service name>`. So if your integration's domain is "awesome_sauce" and you are making a light platform, you would register services under the `awesome_sauce` domain. Make sure that your services [verify permissions](auth_permissions.md#checking-permissions).
|
||||
|
||||
### 5. Entity
|
||||
|
||||
1. Extend entity from component, e.g., `class HueLight(Light)`.
|
||||
2. Avoid passing in `hass` as a parameter to the entity. When the entity has been added to Home Assistant, `hass` will be set on the entity by the helper in entity_platform.py. This means you can access `hass` as `self.hass` inside the entity.
|
||||
1. Extend the entity from the integration you're building a platform for.
|
||||
|
||||
```python
|
||||
from homeassistant.components.light import Light
|
||||
|
||||
class HueLight(Light):
|
||||
...
|
||||
```
|
||||
|
||||
2. Avoid passing in `hass` as a parameter to the entity. When the entity has been added to Home Assistant, `hass` will be set on the entity when the entity is added to Home Assistant. This means you can access `hass` as `self.hass` inside the entity.
|
||||
3. Do not call `update()` in constructor, use `add_entities(devices, True)` instead.
|
||||
4. Do not do any I/O inside properties. Cache values inside `update()` instead.
|
||||
5. The state and/or attributes should not contain relative time since something happened. Instead it should store UTC timestamps.
|
||||
5. When dealing with time, state and/or attributes should not contain relative time since something happened. Instead, it should store UTC timestamps.
|
||||
6. Leverage the [entity lifecycle callbacks](entity_index.md#lifecycle-hooks) to attach event listeners or clean up connections.
|
||||
|
||||
### 6. Communication with devices/services
|
||||
|
||||
1. All API specific code has to be part of a third party library hosted on PyPi. Home Assistant should only interact with objects and not make direct calls to the API.
|
||||
|
||||
```python
|
||||
# bad
|
||||
status = requests.get(url('/status'))
|
||||
```python
|
||||
# bad
|
||||
status = requests.get(url('/status'))
|
||||
|
||||
# good
|
||||
from phue import Bridge
|
||||
bridge = Bridge(...)
|
||||
status = bridge.status()
|
||||
```
|
||||
# good
|
||||
from phue import Bridge
|
||||
bridge = Bridge(...)
|
||||
status = bridge.status()
|
||||
```
|
||||
|
||||
[Tutorial on publishing your own PyPI package](https://jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/)
|
||||
|
@ -1,117 +1,7 @@
|
||||
---
|
||||
title: "Example light platform"
|
||||
title: ""
|
||||
---
|
||||
|
||||
This example is for adding support for the imaginary Awesome Lights. It shows the different best practices for developing a platform.
|
||||
[This page has moved.](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_light)
|
||||
|
||||
Similar to Example Sensor Platform, copy the code below, and create it as a file in `<config_dir>/custom_components/awesomelights/light.py`.
|
||||
|
||||
Add the following to your configuration.yaml:
|
||||
|
||||
```yaml
|
||||
light:
|
||||
- platform: awesomelights
|
||||
host: HOST_HERE
|
||||
username: USERNAME_HERE
|
||||
password: PASSWORD_HERE_OR_secrets.yaml
|
||||
```
|
||||
|
||||
Note the `platform` name matches the directory name that contains the source code.
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
# Import the device class from the component that you want to support
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, Light, PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
# Home Assistant depends on 3rd party packages for API specific code.
|
||||
REQUIREMENTS = ['awesome_lights==1.2.3']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Validation of the user's configuration
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_USERNAME, default='admin'): cv.string,
|
||||
vol.Optional(CONF_PASSWORD): cv.string,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Awesome Light platform."""
|
||||
import awesomelights
|
||||
|
||||
# Assign configuration variables. The configuration check takes care they are
|
||||
# present.
|
||||
host = config.get(CONF_HOST)
|
||||
username = config.get(CONF_USERNAME)
|
||||
password = config.get(CONF_PASSWORD)
|
||||
|
||||
# Setup connection with devices/cloud
|
||||
hub = awesomelights.Hub(host, username, password)
|
||||
|
||||
# Verify that passed in configuration works
|
||||
if not hub.is_valid_login():
|
||||
_LOGGER.error("Could not connect to AwesomeLight hub")
|
||||
return
|
||||
|
||||
# Add devices
|
||||
add_devices(AwesomeLight(light) for light in hub.lights())
|
||||
|
||||
|
||||
|
||||
class AwesomeLight(Light):
|
||||
"""Representation of an Awesome Light."""
|
||||
|
||||
def __init__(self, light):
|
||||
"""Initialize an AwesomeLight."""
|
||||
self._light = light
|
||||
self._name = light.name
|
||||
self._state = None
|
||||
self._brightness = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the display name of this light."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness of the light.
|
||||
|
||||
This method is optional. Removing it indicates to Home Assistant
|
||||
that brightness is not supported for this light.
|
||||
"""
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if light is on."""
|
||||
return self._state
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Instruct the light to turn on.
|
||||
|
||||
You can skip the brightness part if your light does not support
|
||||
brightness control.
|
||||
"""
|
||||
self._light.brightness = kwargs.get(ATTR_BRIGHTNESS, 255)
|
||||
self._light.turn_on()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Instruct the light to turn off."""
|
||||
self._light.turn_off()
|
||||
|
||||
def update(self):
|
||||
"""Fetch new state data for this light.
|
||||
|
||||
This is the only method that should fetch new data for Home Assistant.
|
||||
"""
|
||||
self._light.update()
|
||||
self._state = self._light.is_on()
|
||||
self._brightness = self._light.brightness
|
||||
```
|
||||
<script>document.location = 'https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_light';</script>
|
||||
|
@ -1,59 +1,7 @@
|
||||
---
|
||||
title: "Example sensor platform"
|
||||
title: ""
|
||||
---
|
||||
|
||||
This is a minimum implementation of a platform for the sensor component.
|
||||
[This page has moved.](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_sensor)
|
||||
|
||||
### Installation
|
||||
|
||||
Copy the code below and create it as a file in `<config_dir>/custom_components/example/sensor.py`.
|
||||
|
||||
Add the following to your `configuration.yaml` file:
|
||||
|
||||
```yaml
|
||||
# Example configuration.yaml entry
|
||||
sensor:
|
||||
platform: example
|
||||
```
|
||||
|
||||
### Code
|
||||
|
||||
```python
|
||||
from homeassistant.const import TEMP_CELSIUS
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the sensor platform."""
|
||||
add_devices([ExampleSensor()])
|
||||
|
||||
|
||||
class ExampleSensor(Entity):
|
||||
"""Representation of a Sensor."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the sensor."""
|
||||
self._state = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return 'Example Temperature'
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement."""
|
||||
return TEMP_CELSIUS
|
||||
|
||||
def update(self):
|
||||
"""Fetch new state data for the sensor.
|
||||
|
||||
This is the only method that should fetch new data for Home Assistant.
|
||||
"""
|
||||
self._state = 23
|
||||
```
|
||||
<script>document.location = 'https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_sensor';</script>
|
||||
|
@ -1,29 +1,21 @@
|
||||
---
|
||||
title: "Adding support for a new platform"
|
||||
sidebar_label: "Introduction"
|
||||
title: "Integration Platforms"
|
||||
sidebar_label: "Platforms"
|
||||
---
|
||||
|
||||
Components that interact with devices are called "[Entity Components](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/helpers/entity_component.py)." They are structured in core and platform logic, which means different brands can use the same logic to handle a light.
|
||||
Home Assistant has various built-in integrations that abstract device types. There are [lights](entity_light.md), [switches](entity_switch.md), [covers](entity_cover.md), [climate devices](entity_climate.md), and [many more](entity_index.md). Your integration can hook into these integrations by creating a platform. You will need a platform for each integration that you are integrating with.
|
||||
|
||||
For example, the built-in `switch` component consists of various platforms in [`homeassistant/components/switch/`](https://github.com/home-assistant/home-assistant/tree/master/homeassistant/components/switch). The file `__init__.py` contains the core logic of all platforms and the `vendor_name.py` files contain only the relevant platform code.
|
||||
To create a platform, you will need to create a file with the domain name of the integration that you are building a platform for. So if you are building a light, you will add a new file `light.py` to your integration folder.
|
||||
|
||||
If you're planning to add support for a new type of device to an existing component, you can get away with only writing platform logic. Have a look at how the component works with other platforms and create a similar file for the platform that you want to add:
|
||||
We have created two example integrations that should give you a look at how this works:
|
||||
|
||||
- [Example sensor platform](creating_platform_example_sensor.md): hello world of platforms.
|
||||
- [Example light platform](creating_platform_example_light.md): showing best practices.
|
||||
- [Example sensor platform](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_sensor/): hello world of platforms.
|
||||
- [Example light platform](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_light/): showing best practices.
|
||||
|
||||
### Interfacing with devices
|
||||
|
||||
One Home Assistant rule is that platform logic should never interface directly with devices. Instead, use a third-party Python 3 library. This way, Home Assistant can share code with the Python community and keep the project maintainable.
|
||||
One Home Assistant rule is that the integration should never interface directly with devices. Instead, it should interact with a third-party Python 3 library. This way, Home Assistant can share code with the Python community and keep the project maintainable.
|
||||
|
||||
To integrate the third-party library, create an [Entity class](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/helpers/entity.py) for your device. Entities are Home Assistant's representations of lights, switches, sensors, etc. and are derived from the [Entity Abstract Class](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/helpers/entity.py). This abstract class contains logic for integrating most standard features into your entities, such as visibility, entity IDs, updates, and much more.
|
||||
|
||||
### Requirements and dependencies
|
||||
|
||||
Platforms can specify dependencies and requirements [the same way as components](creating_component_deps_and_reqs.md):
|
||||
|
||||
```python
|
||||
REQUIREMENTS = ['some-package==2.0.0', 'some-other-package==2.5.0']
|
||||
DEPENDENCIES = ['mqtt']
|
||||
```
|
||||
Once you have your Python library ready and published to PyPi, add it to the [manifest](creating_integration_manifest.md). It will now be time to implement the Entity base class that is provided by the integration that you are creating a platform for.
|
||||
|
||||
Find your integration at the [entity index](entity_index.md) to see what methods and properties are available to implement.
|
||||
|
@ -1,13 +1,15 @@
|
||||
---
|
||||
title: "Using Services"
|
||||
title: "Integration Services"
|
||||
sidebar_label: "Custom Services"
|
||||
---
|
||||
|
||||
This is a simple "hello world" example to show the basics of registering a service. To use this example, create the file `<config dir>/custom_components/hello_service.py` and copy the below example code.
|
||||
Home Assistant provides ready-made services for a lot of things, but it doesn't always cover everything. Instead of trying to change Home Assistant, it is preferred to add it as a service under your own integration first. Once we see a pattern in these services, we can talk about generalizing them.
|
||||
|
||||
Services can be called from automation and from the service "Developer tools" in the frontend.
|
||||
This is a simple "hello world" example to show the basics of registering a service. To use this example, create the file `<config dir>/custom_components/hello_service/__init__.py` and copy the below example code.
|
||||
|
||||
Services can be called from automations and from the service "Developer tools" in the frontend.
|
||||
|
||||
```python
|
||||
# The domain of your component. Should be equal to the name of your component.
|
||||
DOMAIN = 'hello_service'
|
||||
|
||||
ATTR_NAME = 'name'
|
||||
@ -18,6 +20,7 @@ def setup(hass, config):
|
||||
"""Set up is called when Home Assistant is loading our component."""
|
||||
|
||||
def handle_hello(call):
|
||||
"""Handle the service call."""
|
||||
name = call.data.get(ATTR_NAME, DEFAULT_NAME)
|
||||
|
||||
hass.states.set('hello_service.hello', name)
|
||||
@ -28,7 +31,7 @@ def setup(hass, config):
|
||||
return True
|
||||
```
|
||||
|
||||
Load the component by adding the following to your `configuration.yaml`. When your component is loaded, a new service should be available to call.
|
||||
Load the integration by adding the following to your `configuration.yaml`. When your component is loaded, a new service should be available to call.
|
||||
|
||||
```yaml
|
||||
# configuration.yaml entry
|
||||
@ -46,3 +49,28 @@ Pressing "Call Service" will now call your service without any parameters. This
|
||||
```
|
||||
|
||||
The service will now overwrite the previous state with "Planet".
|
||||
|
||||
## Service descriptions
|
||||
|
||||
Adding services is only useful if users know about them. In Home Assistant we use a `services.yaml` as part of your integration to describe the services.
|
||||
|
||||
Services are published under the domain name of your integration, so in `services.yaml` we only use the service name as the base key.
|
||||
|
||||
```yaml
|
||||
# Example services.yaml entry
|
||||
|
||||
set_speed:
|
||||
# Description of the service
|
||||
description: Sets fan speed.
|
||||
# Different fields that your service accepts
|
||||
fields:
|
||||
# Key of the field
|
||||
entity_id:
|
||||
# Description of the field
|
||||
description: Name(s) of the entities to set
|
||||
# Example value that can be passed for this field
|
||||
example: 'fan.living_room'
|
||||
speed:
|
||||
description: Speed setting
|
||||
example: 'low'
|
||||
```
|
||||
|
@ -28,7 +28,6 @@ import logging
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'hello_state'
|
||||
DEPENDENCIES = []
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup the Hello State component. """
|
||||
@ -40,8 +39,7 @@ def setup(hass, config):
|
||||
1. In the file header we decided to add some details: A short description and the link to the documentation.
|
||||
2. We want to do some logging. This means that we import the Python logging module and create an alias.
|
||||
3. The component name is equal to the domain name.
|
||||
4. At the moment this component has no dependencies. For detail check [dependencies](creating_component_deps_and_reqs.md#dependencies) section.
|
||||
5. The `setup` function will take care of the initialization of our component.
|
||||
4. The `setup` function will take care of the initialization of our component.
|
||||
The component will only write a log message. Keep in mind for later that you have several options for the severity:
|
||||
|
||||
- `_LOGGER.info(msg)`
|
||||
@ -50,7 +48,7 @@ def setup(hass, config):
|
||||
- `_LOGGER.critical(msg)`
|
||||
- `_LOGGER.exception(msg)`
|
||||
|
||||
7. We return `True` if everything is ok.
|
||||
5. We return `True` if everything is ok.
|
||||
|
||||
Add the component to your `configuration.yaml` file.
|
||||
|
||||
@ -72,7 +70,6 @@ import logging
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'hello_state'
|
||||
DEPENDENCIES = []
|
||||
|
||||
CONF_TEXT = 'text'
|
||||
DEFAULT_TEXT = 'No text!'
|
||||
|
@ -1,5 +1,6 @@
|
||||
---
|
||||
title: "Development Checklist"
|
||||
sidebar_label: Introduction
|
||||
---
|
||||
|
||||
|
||||
@ -7,44 +8,8 @@ Before you commit any changes, check your work against these requirements:
|
||||
|
||||
- All communication to external devices or services must be wrapped in an external Python library hosted on [pypi](https://pypi.python.org/pypi).
|
||||
- New dependencies are added to `requirements_all.txt` (if applicable), using `script/gen_requirements_all.py`
|
||||
- New codeowners are added to `CODEOWNERS` (if applicable), using `script/manifest/codeowners.py`
|
||||
- The `.coveragerc` file is updated to exclude your platform if there are no tests available or your new code uses a third-party library for communication with the device, service, or sensor
|
||||
- Documentation is developed for [home-assistant.io](https://home-assistant.io/)
|
||||
* Visit the [website documentation](https://www.home-assistant.io/developers/documentation/) for more information about contributing to [home-assistant.io](https://github.com/home-assistant/home-assistant.github.io).
|
||||
- All dependencies are [only imported inside functions](creating_component_deps_and_reqs.md) that use them.
|
||||
- Add _the manifest file_. What is _the manifest file_? Keep reading to find out!
|
||||
|
||||
## _The Manifest File_
|
||||
|
||||
As of Home Assistant 0.91, we have begun a migration to a new concept we call _the manifest file_. What is _the manifest file_? _The manifest file_ contains information about a Home Assistant integration. It looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": "mobile_app",
|
||||
"name": "Home Assistant Mobile App Support",
|
||||
"documentation": "https://www.home-assistant.io/components/mobile_app",
|
||||
"requirements": [
|
||||
"PyNaCl==1.3.0"
|
||||
],
|
||||
"dependencies": [
|
||||
"device_tracker",
|
||||
"http",
|
||||
"webhook"
|
||||
],
|
||||
"codeowners": [
|
||||
"@robbiet480"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Here are the fields contained in _the manifest file_.
|
||||
|
||||
| Key | Type | Description |
|
||||
|---------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| codeowners | array of strings | Contains a array of GitHub usernames or team names responsible for the contents of this file that will be notified whenever someone submits a issue or PR relating to it. You, the author, should put your username here. |
|
||||
| dependencies | array of strings | Other integrations/components this integration requires to be loaded before it can start. |
|
||||
| documentation | url | A URL pointing to documentation for the integration. If this integration is being submitted for inclusion to Home Assistant, it should probably point to documentation under home-assistant.io. |
|
||||
| domain | string | The domain of the integration. For example, if your integration is in a folder named `mobile_app`, this should be `mobile_app` |
|
||||
| name | string | The human readable name of your integration |
|
||||
| requirements | array of strings | Dependencies that should be installed from PyPi. |
|
||||
|
||||
_The manifest file_ should be stored in the integration folder and be named `manifest.json`.
|
||||
- All dependencies are only imported inside functions that use them.
|
||||
|
@ -2,7 +2,7 @@
|
||||
title: "Style guidelines"
|
||||
---
|
||||
|
||||
Home Assistant enforces strict [PEP8 style](https://www.python.org/dev/peps/pep-0008/) and [PEP 257 (Docstring Conventions)](https://www.python.org/dev/peps/pep-0257/) compliance on all code submitted. We automatically test every pull request as part of the linting process with [Coveralls](https://coveralls.io/github/home-assistant/home-assistant) and [Travis CI](https://travis-ci.org/home-assistant/home-assistant).
|
||||
Home Assistant enforces strict [PEP8 style](https://www.python.org/dev/peps/pep-0008/) and [PEP 257 (Docstring Conventions)](https://www.python.org/dev/peps/pep-0257/) compliance on all code submitted. We automatically test every pull request as part of the linting process.
|
||||
|
||||
Summary of the most relevant points:
|
||||
|
||||
@ -42,17 +42,6 @@ The docstring in the file header should describe what the file is about.
|
||||
"""Support for MQTT lights."""
|
||||
```
|
||||
|
||||
### Requirements
|
||||
|
||||
Please place [Platform requirements](creating_platform_code_review.md#1-requirements) right after the imports.
|
||||
|
||||
```python
|
||||
[...]
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['xmltodict==0.11.0']
|
||||
```
|
||||
|
||||
### Log messages
|
||||
|
||||
There is no need to add the platform or component name to the log messages. This will be added automatically. Like `syslog` messages there shouldn't be any period at the end. Try to avoid brackets and additional quotes around the output to make it easier for users to parse the log. A widely style is shown below but you are free to compose the messages as you like.
|
||||
@ -74,7 +63,7 @@ Instead of order the imports manually, use [`isort`](https://github.com/timothyc
|
||||
|
||||
```bash
|
||||
$ pip3 install isort
|
||||
$ isort homeassistant/components/sensor/fixer.py
|
||||
$ isort homeassistant/components/sensor/fixer.py
|
||||
```
|
||||
|
||||
### Use new style string formatting
|
||||
|
@ -3,25 +3,9 @@ title: "Starting with Development"
|
||||
sidebar_label: Introduction
|
||||
---
|
||||
|
||||
Home Assistant is built from the ground up to be easily extensible using components. Home Assistant uses [Python 3](https://www.python.org/) for the backend and [Polymer (Web components)](https://www.polymer-project.org/) for the frontend.
|
||||
Home Assistant is built from the ground up to be easily extensible using integrations. In this section, we're focusing on how to develop integrations.
|
||||
|
||||
Home Assistant is open-source and licensed under [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0). Here are links to the source:
|
||||
Before you start, make sure that you have read up on the [Home Assistant architecture](architecture_index.md) so that you are familiar with the concepts that make up Home Assistant.
|
||||
|
||||
- [home-assistant](https://github.com/home-assistant/home-assistant): Python server backend.
|
||||
- [home-assistant-polymer](https://github.com/home-assistant/home-assistant-polymer): Polymer UI.
|
||||
If you run into trouble following this documentation, don't hesitate to join our #devs_backend channel on [Discord](https://www.home-assistant.io/join-chat/).
|
||||
|
||||
For those new to contributing to open source software, make sure you are familiar with all of the tools and concepts used in Home Assistant before you start.
|
||||
|
||||
When contributing Home Assistant code:
|
||||
- [Github](https://guides.github.com/activities/hello-world/)
|
||||
- [Pip and Virtual Environments](https://www.dabapps.com/blog/introduction-to-pip-and-virtualenv-python/)
|
||||
- [Python 3](https://www.python.org/)
|
||||
- [Pylint](https://www.pylint.org)
|
||||
- [Flake8](http://flake8.pycqa.org/en/latest/)
|
||||
- [Tox](http://tox.readthedocs.org/en/latest/)
|
||||
- [TravisCl](https://travis-ci.org/)
|
||||
|
||||
Home Assistant follows [Debian Stable](https://packages.debian.org/stable/python/python3) for the minimum Python version, which is currently [Python 3.5.3](https://www.python.org/downloads/release/python-353/).
|
||||
|
||||
When contributing 3rd Party code to be used by Home Assistant:
|
||||
- [Publishing your own PyPI package](https://jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/)
|
||||
|
@ -19,9 +19,9 @@ To validate platforms using [MQTT](https://www.home-assistant.io/components/mqtt
|
||||
Some things to keep in mind:
|
||||
|
||||
- Use the constants defined in `const.py`
|
||||
- Import `PLATFORM_SCHEMA` from the parent component and extend it
|
||||
- Import `PLATFORM_SCHEMA` from the integration you are integrating with and extend it.
|
||||
- Preferred order is `required` first and `optional` second
|
||||
- Starting with Home Assistant 0.64 `voluptuous` requires default values for optional configuration keys to be valid values. Don't use a default which is `None` like `vol.Optional(CONF_SOMETHING, default=None): cv.string`, set the default to `default=''` if required.
|
||||
- Default values for optional configuration keys need to be valid values. Don't use a default which is `None` like `vol.Optional(CONF_SOMETHING, default=None): cv.string`, set the default to `default=''` if required.
|
||||
|
||||
### Snippets
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Integration Quality Scale"
|
||||
sidebar_label: "Introduction"
|
||||
sidebar_label: "Integration Quality Scale"
|
||||
---
|
||||
|
||||
The Integration Quality Scale scores each integration based on the code quality and user experience. Each level of the quality scale consists of a list of requirements. If an integration matches all requirements, it's considered to have reached that level.
|
||||
|
@ -2,8 +2,7 @@
|
||||
title: "Translation"
|
||||
---
|
||||
|
||||
## How to start
|
||||
Translations for Home Assistant are managed through Lokalise, an online translation management tool. Our translations are split between two projects, a backend project for platform-specific translations, and a frontend project for UI translations. Click the links below to join both projects! Even if your language is completely translated, extra proofreading is a big help! Please feel free to review the existing translations, and vote for alternatives that might be more appropriate.
|
||||
Translations for Home Assistant are managed through [Lokalise](https://lokalise.co/), an online translation management tool. Our translations are split between two projects, a backend project for platform-specific translations, and a frontend project for UI translations. Click the links below to join both projects! Even if your language is completely translated, extra proofreading is a big help! Please feel free to review the existing translations, and vote for alternatives that might be more appropriate.
|
||||
|
||||
- [Join the frontend translation team](https://lokalise.co/signup/3420425759f6d6d241f598.13594006/all/)
|
||||
- [Join the backend translation team](https://lokalise.co/signup/130246255a974bd3b5e8a1.51616605/all/)
|
||||
|
@ -29,7 +29,7 @@ Most files don't need to the be executable. `0644` is fine.
|
||||
|
||||
A lot of components and platforms depends on third-party Python modules. The dependencies which are stored in the `requirements_*.txt` files are tracked by [Requires.io](https://requires.io/github/home-assistant/home-assistant/requirements/?branch=dev).
|
||||
|
||||
If you update the requirements of a component/platform through the `REQUIREMENTS = ['modules-xyz==0.3']` entry, run the provided script to update the `requirements_*.txt` file(s).
|
||||
If you update the requirements of a component/platform by updating `manifest.json`, run the provided script to update the `requirements_*.txt` file(s).
|
||||
|
||||
```bash
|
||||
$ script/gen_requirements_all.py
|
||||
|
@ -69,56 +69,57 @@
|
||||
"title": "Permissions"
|
||||
},
|
||||
"config_entries_config_flow_handler": {
|
||||
"title": "Config Flow Handlers"
|
||||
"title": "Integration Configuration",
|
||||
"sidebar_label": "Configuration"
|
||||
},
|
||||
"config_entries_index": {
|
||||
"title": "Config Entries",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"config_entries_options_flow_handler": {
|
||||
"title": "Options Flow Handlers"
|
||||
"title": "Integration Configuration Options",
|
||||
"sidebar_label": "Configuration Options"
|
||||
},
|
||||
"configuration_yaml_index": {
|
||||
"title": "Configuration.yaml",
|
||||
"sidebar_label": "Introduction"
|
||||
"title": "Integration Configuration via YAML",
|
||||
"sidebar_label": "Configuration via YAML"
|
||||
},
|
||||
"creating_component_code_review": {
|
||||
"title": "Checklist for creating a component"
|
||||
"title": "Checklist for creating a component",
|
||||
"sidebar_label": "Component Checklist"
|
||||
},
|
||||
"creating_component_deps_and_reqs": {
|
||||
"title": "Requirements & Dependencies"
|
||||
},
|
||||
"creating_component_discovery": {
|
||||
"title": "Component Discovery"
|
||||
},
|
||||
"creating_component_events": {
|
||||
"title": "Handling events"
|
||||
"title": "creating_component_deps_and_reqs"
|
||||
},
|
||||
"creating_component_generic_discovery": {
|
||||
"title": "Generic Platform Discovery"
|
||||
"title": "Integration with Multiple Platforms",
|
||||
"sidebar_label": "Multiple platforms"
|
||||
},
|
||||
"creating_component_index": {
|
||||
"title": "Creating components",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"creating_component_loading": {
|
||||
"title": "Loading your components"
|
||||
"creating_integration_file_structure": {
|
||||
"title": "Integration File Structure",
|
||||
"sidebar_label": "File Structure"
|
||||
},
|
||||
"creating_component_states": {
|
||||
"title": "Handling states"
|
||||
"creating_integration_manifest": {
|
||||
"title": "Integration Manifest",
|
||||
"sidebar_label": "Manifest"
|
||||
},
|
||||
"creating_platform_code_review": {
|
||||
"title": "Checklist for creating a platform"
|
||||
"title": "Checklist for creating a platform",
|
||||
"sidebar_label": "Platform Checklist"
|
||||
},
|
||||
"creating_platform_example_light": {
|
||||
"title": "Example light platform"
|
||||
"title": "creating_platform_example_light"
|
||||
},
|
||||
"creating_platform_example_sensor": {
|
||||
"title": "Example sensor platform"
|
||||
"title": "creating_platform_example_sensor"
|
||||
},
|
||||
"creating_platform_index": {
|
||||
"title": "Adding support for a new platform",
|
||||
"sidebar_label": "Introduction"
|
||||
"title": "Integration Platforms",
|
||||
"sidebar_label": "Platforms"
|
||||
},
|
||||
"data_entry_flow_index": {
|
||||
"title": "Data Entry Flow",
|
||||
@ -138,7 +139,8 @@
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"dev_101_services": {
|
||||
"title": "Using Services"
|
||||
"title": "Integration Services",
|
||||
"sidebar_label": "Custom Services"
|
||||
},
|
||||
"dev_101_states": {
|
||||
"title": "Using States"
|
||||
@ -147,7 +149,8 @@
|
||||
"title": "Catching up with Reality"
|
||||
},
|
||||
"development_checklist": {
|
||||
"title": "Development Checklist"
|
||||
"title": "Development Checklist",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"development_environment": {
|
||||
"title": "Set up Development Environment"
|
||||
@ -332,7 +335,7 @@
|
||||
},
|
||||
"integration_quality_scale_index": {
|
||||
"title": "Integration Quality Scale",
|
||||
"sidebar_label": "Introduction"
|
||||
"sidebar_label": "Integration Quality Scale"
|
||||
},
|
||||
"intent_builtin": {
|
||||
"title": "Built-in intents"
|
||||
@ -1243,7 +1246,6 @@
|
||||
"Architecture": "Architecture",
|
||||
"Entities": "Entities",
|
||||
"Authentication": "Authentication",
|
||||
"Configuration.yaml": "Configuration.yaml",
|
||||
"Config Entries": "Config Entries",
|
||||
"Data Entry Flow": "Data Entry Flow",
|
||||
"Entity Registry": "Entity Registry",
|
||||
@ -1252,11 +1254,11 @@
|
||||
"Frontend": "Frontend",
|
||||
"Extending the frontend": "Extending the frontend",
|
||||
"Custom UI": "Custom UI",
|
||||
"Developing a feature": "Developing a feature",
|
||||
"Development 101": "Development 101",
|
||||
"Integration Quality Scale": "Integration Quality Scale",
|
||||
"Creating Platforms": "Creating Platforms",
|
||||
"Creating Components": "Creating Components",
|
||||
"Development Workflow": "Development Workflow",
|
||||
"Building Integrations": "Building Integrations",
|
||||
"Development Checklist": "Development Checklist",
|
||||
"Home Assistant Core 101": "Home Assistant Core 101",
|
||||
"Misc": "Misc",
|
||||
"Introduction": "Introduction",
|
||||
"External API": "External API",
|
||||
"Internationalization": "Internationalization",
|
||||
@ -1267,6 +1269,12 @@
|
||||
"Hass.io": "Hass.io",
|
||||
"Hass.io Add-Ons": "Hass.io Add-Ons",
|
||||
"Maintainer docs": "Maintainer docs",
|
||||
"Configuration.yaml": "Configuration.yaml",
|
||||
"Developing a feature": "Developing a feature",
|
||||
"Development 101": "Development 101",
|
||||
"Integration Quality Scale": "Integration Quality Scale",
|
||||
"Creating Platforms": "Creating Platforms",
|
||||
"Creating Components": "Creating Components",
|
||||
"Lovelace UI": "Lovelace UI"
|
||||
}
|
||||
},
|
||||
|
@ -31,13 +31,8 @@
|
||||
"auth_auth_provider",
|
||||
"auth_auth_module"
|
||||
],
|
||||
"Configuration.yaml": [
|
||||
"configuration_yaml_index"
|
||||
],
|
||||
"Config Entries": [
|
||||
"config_entries_index",
|
||||
"config_entries_config_flow_handler",
|
||||
"config_entries_options_flow_handler"
|
||||
"config_entries_index"
|
||||
],
|
||||
"Data Entry Flow": [
|
||||
"data_entry_flow_index"
|
||||
@ -72,43 +67,41 @@
|
||||
]
|
||||
},
|
||||
"Extending HASS": {
|
||||
"Developing a feature": [
|
||||
"Development Workflow": [
|
||||
"development_index",
|
||||
"development_environment",
|
||||
"development_submitting",
|
||||
"development_checklist",
|
||||
"development_guidelines",
|
||||
"development_testing",
|
||||
"development_catching_up",
|
||||
"development_validation",
|
||||
"development_typing"
|
||||
"development_catching_up"
|
||||
],
|
||||
"Development 101": [
|
||||
"Building Integrations": [
|
||||
"creating_integration_file_structure",
|
||||
"creating_integration_manifest",
|
||||
"creating_component_index",
|
||||
"config_entries_config_flow_handler",
|
||||
"config_entries_options_flow_handler",
|
||||
"configuration_yaml_index",
|
||||
"dev_101_services",
|
||||
"creating_platform_index",
|
||||
"creating_component_generic_discovery"
|
||||
],
|
||||
"Development Checklist": [
|
||||
"development_checklist",
|
||||
"creating_component_code_review",
|
||||
"creating_platform_code_review",
|
||||
"integration_quality_scale_index"
|
||||
],
|
||||
"Home Assistant Core 101": [
|
||||
"dev_101_index",
|
||||
"dev_101_hass",
|
||||
"dev_101_events",
|
||||
"dev_101_states",
|
||||
"dev_101_services",
|
||||
"dev_101_config"
|
||||
],
|
||||
"Integration Quality Scale": [
|
||||
"integration_quality_scale_index"
|
||||
],
|
||||
"Creating Platforms": [
|
||||
"creating_platform_index",
|
||||
"creating_platform_code_review",
|
||||
"creating_platform_example_light",
|
||||
"creating_platform_example_sensor"
|
||||
],
|
||||
"Creating Components": [
|
||||
"creating_component_index",
|
||||
"creating_component_code_review",
|
||||
"creating_component_deps_and_reqs",
|
||||
"creating_component_events",
|
||||
"creating_component_states",
|
||||
"creating_component_discovery",
|
||||
"creating_component_loading",
|
||||
"creating_component_generic_discovery"
|
||||
"Misc": [
|
||||
"development_validation",
|
||||
"development_typing"
|
||||
]
|
||||
},
|
||||
"Misc": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user