This commit is contained in:
Paulus Schoutsen 2019-08-30 06:25:31 -07:00
parent 14f0a96a86
commit 5020580993
12 changed files with 1211 additions and 0 deletions

View File

@ -1581,6 +1581,38 @@
},
"version-0.97.0/version-0.97.0-external_api_websocket": {
"title": "WebSocket API"
},
"version-0.98.0/version-0.98.0-config_entries_config_flow_handler": {
"title": "Integration Configuration",
"sidebar_label": "Configuration"
},
"version-0.98.0/version-0.98.0-config_entries_options_flow_handler": {
"title": "Integration Configuration Options",
"sidebar_label": "Configuration Options"
},
"version-0.98.0/version-0.98.0-creating_component_generic_discovery": {
"title": "Integration with Multiple Platforms",
"sidebar_label": "Multiple platforms"
},
"version-0.98.0/version-0.98.0-development_environment": {
"title": "Set up Development Environment"
},
"version-0.98.0/version-0.98.0-development_guidelines": {
"title": "Style guidelines"
},
"version-0.98.0/version-0.98.0-entity_index": {
"title": "Entity",
"sidebar_label": "Introduction"
},
"version-0.98.0/version-0.98.0-entity_registry_disabled_by": {
"title": "Entity Registry and disabling entities",
"sidebar_label": "Disabling entities"
},
"version-0.98.0/version-0.98.0-hassio_addon_config": {
"title": "Add-On Configuration"
},
"version-0.98.0/version-0.98.0-hassio_addon_tutorial": {
"title": "Tutorial: Making your first add-on"
}
},
"links": {

View File

@ -0,0 +1,138 @@
---
title: Integration Configuration
sidebar_label: Configuration
id: version-0.98.0-config_entries_config_flow_handler
original_id: config_entries_config_flow_handler
---
Integrations can be set up via the user interface by adding support for a config config to create a config entry. 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.
When instantiating the handler, Home Assistant will make sure to load all dependencies and install the requirements of the component.
## Updating the manifest
You need to update your integrations manifest to inform Home Assistant that your integration has a config flow. This is done by adding `config_flow: true` to your manifest ([docs](creating_integration_manifest.md#config-flow)).
## Defining your config flow
Config entries uses the [data flow entry framework](data_entry_flow_index.md) to define their config flows. The config flow needs to be defined in the file `config_flow.py` in your integration folder, extend `homeassistant.config_entries.ConfigFlow` and pass a `domain` key as part of inheriting `ConfigFlow`.
```python
from homeassistant import config_entries
from .const import DOMAIN
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
```
## Defining steps
Your config flow will need to define steps of your configuration flow. The docs for [Data Entry Flow](data_entry_flow_index.md) describe the different return values of a step. Here is an example on how to define the `user` step.
```python
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, info):
if info is not None:
# process info
return self.async_show_form(
step_id='user',
data_schema=vol.Schema({
vol.Required('password'): str
})
)
```
There are a few step names reserved for system use:
| Step name | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user` | Invoked when a user initiates a flow via the user interface. |
| `zeroconf` | Invoked if your integration has been discovered via Zeroconf/mDNS as specified [using `zeroconf` in the manifest](creating_integration_manifest.md#zeroconf). |
| `homekit` | Invoked if your integration has been discovered via HomeKit as specified [using `homekit` in the manifest](creating_integration_manifest.md#homekit). |
| `ssdp` | Invoked if your integration has been discovered via SSDP/uPnP as specified [using `ssdp` in the manifest](creating_integration_manifest.md#ssdp). |
| `discovery` | _DEPRECATED_ Invoked if your integration has been discovered by the discovery integration. |
## Discovery steps
When an integration is discovered, their respective discovery step is invoked with the discovery information. The step will have to check the following things:
- Make sure there are no other instances of this config flow in progress of setting up the discovered device. This can happen if there are multiple ways of discovering that a device is on the network.
- Make sure that the device is not already set up.
- Invoking a discovery step should never result in a finished flow and a config entry. Always confirm with the user.
## Discoverable integrations that require no authentication
If your integration is discoverable without requiring any authentication, you'll be able to use the Discoverable Flow that is built-in. This flow offers the following features:
- Detect if devices/services can be discovered on the network before finishing the config flow.
- Support all manifest-based discovery protocols.
- Limit to only 1 config entry. It is up to the config entry to discover all available devices.
```python
"""Config flow for LIFX."""
from homeassistant.helpers import config_entry_flow
from homeassistant import config_entries
import aiolifx
from .const import DOMAIN
async def _async_has_devices(hass):
"""Return if there are devices that can be discovered."""
lifx_ip_addresses = await aiolifx.LifxScan(hass.loop).scan()
return len(lifx_ip_addresses) > 0
config_entry_flow.register_discovery_flow(
# Domain of your integration
DOMAIN,
# Title of the created config entry
'LIFX',
# async method that returns a boolean if devices/services are found
_async_has_devices,
# Connection class of the integration
config_entries.CONN_CLASS_LOCAL_POLL
)
```
## 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:
```json
{
"config": {
"title": "Philips Hue Bridge",
"step": {
"init": {
"title": "Pick Hue bridge",
"data": {
"host": "Host"
}
},
"link": {
"title": "Link Hub",
"description": "Press the button on the bridge to register Philips Hue with Home Assistant.\n\n![Location of button on bridge](/static/images/config_philips_hue.jpg)"
}
},
"error": {
"register_failed": "Failed to register, please try again",
"linking": "Unknown linking error occurred."
},
"abort": {
"discover_timeout": "Unable to discover Hue bridges",
"no_bridges": "No Philips Hue bridges discovered",
"all_configured": "All Philips Hue bridges are already configured",
"unknown": "Unknown error occurred",
"cannot_connect": "Unable to connect to the bridge",
"already_configured": "Bridge is already configured"
}
}
}
```
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)

View File

@ -0,0 +1,60 @@
---
title: Integration Configuration Options
sidebar_label: Configuration Options
id: version-0.98.0-config_entries_options_flow_handler
original_id: config_entries_options_flow_handler
---
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 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
@callback
def async_get_options_flow(config_entry):
return OptionsFlowHandler()
```
## Flow handler
The Flow handler works just like the config flow handler, except that the first step in the flow will always be `async_step_init`.
```python
class OptionsFlowHandler(config_entries.OptionsFlow):
async def async_step_init(self, user_input=None):
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Required(
"show_things",
default=self.config_entry.options.get("show_things"),
): bool
}
),
)
```
## Signal updates
If the component should act on updated options, you can register an update listener to the config entry that will be called when the entry is updated.
```python
entry.add_update_listener(update_listener)
```
The Listener shall be an async function that takes the same input as async_setup_entry. Options can then be accessed from `entry.options`.
```python
async def update_listener(hass, entry):
```

View File

@ -0,0 +1,23 @@
---
title: Integration with Multiple Platforms
sidebar_label: Multiple platforms
id: version-0.98.0-creating_component_generic_discovery
original_id: creating_component_generic_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`).
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.
## Loading platforms when configured via a config entry
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).
## Loading platforms when configured via configuration.yaml
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.
To do this, you will need to use the `load_platform` and `async_load_platform` methods from the discovery helper.
- See also a [full example that implements this logic](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_load_platform/)

View File

@ -0,0 +1,147 @@
---
title: Set up Development Environment
id: version-0.98.0-development_environment
original_id: development_environment
---
You'll need to set up a development environment if you want to develop a new feature or component for Home Assistant. Read on to learn how to set up.
## Preparing your environment
### Developing on Linux
Install the core dependencies.
```bash
$ sudo apt-get install python3-pip python3-dev python3-venv
```
In order to run `script/setup` below you will need some more dependencies.
```bash
$ sudo apt-get install autoconf libssl-dev libxml2-dev libxslt1-dev libjpeg-dev libffi-dev libudev-dev zlib1g-dev pkg-config
$ sudo apt-get install -y libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libavresample-dev libavfilter-dev
```
> Different distributions have different package installation mechanisms and sometimes packages names as well. For example CentOS would use: `sudo yum install epel-release && sudo yum install python36 python36-devel mysql-devel gcc`
Additional dependencies exist if you plan to perform Frontend Development, please read the [Frontend](frontend_index.md) section to learn more.
### Developing on Windows
Due to Home Assistant is mainly designed and developed on Linux distributions, on Windows 10 you can setup a [Linux subsystem](https://docs.microsoft.com/windows/wsl/install-win10).
Open Powershell as an Administrator and run
```bash
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
```
From Windows Store install Ubuntu.
When the Linux subsystem is set up, perform install as for Linux.
```bash
$ sudo apt-get update
$ sudo apt-get install python3-pip python3-dev python3-venv
$ sudo apt-get install autoconf libssl-dev libxml2-dev libxslt1-dev libjpeg-dev libffi-dev libudev-dev zlib1g-dev
$ sudo apt-get install -y libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libavresample-dev libavfilter-dev
```
Hint: Git is included in Linux subsytem.
When invoking your installation (see below), make sure to specify a folder for configuration which is accessible from Windows.
```bash
$ mkdir -p ../config
$ hass -c ../config
```
### Developing on macOS
Install [Homebrew](https://brew.sh/), then use that to install Python 3:
```bash
$ brew install python3 autoconf
```
Then install ffmpeg:
```bash
$ brew install ffmpeg
```
### Developing with devcontainer
The devcontainer is a preconfigured development environment with all the tools you need.
**Prerequisites**
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [Docker](https://docs.docker.com/install/)
- [Visual Studio code](https://code.visualstudio.com/)
- [Remote - Containers (VSC Extension)](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
[More info about requirements and devcontainer in general](https://code.visualstudio.com/docs/remote/containers#_getting-started)
**Getting started:**
1. Fork the repository.
1. Clone the repository to your computer.
1. Open the repository using Visual Studio code.
When you open this repository with Visual Studio code you are asked to "Reopen in Container", this will start the build of the container.
_If you don't see this notification, open the command pallet and select `Remote-Containers: Reopen Folder in Container`._
The devcontainter comes with some useful tasks to help you with development, you can start these tasks by opening the command pallet and select `Tasks: Run Task` then select the task you want to run.
Running tasks like `Preview` can be restarted by opening the command pallet and selecting `Tasks: Restart Running Task`, then select the task you want to restart.
## Setup Local Repository
Visit the [Home Assistant repository](https://github.com/home-assistant/home-assistant) and click **Fork**.
Once forked, setup your local copy of the source using the commands:
```bash
$ git clone https://github.com/YOUR_GIT_USERNAME/home-assistant.git
$ cd home-assistant
$ git remote add upstream https://github.com/home-assistant/home-assistant.git
```
## Setting up virtual environment
To isolate your environment from the rest of the system, set up a [`venv`](https://docs.python.org/3/library/venv.html). Within the `home-assistant` directory, create and activate your virtual environment.
```bash
$ python3 -m venv venv
$ source venv/bin/activate
```
Install the requirements with a provided script named `setup`.
```bash
$ script/setup
```
Invoke your installation, adjusting the [configuration](https://www.home-assistant.io/docs/configuration/) if required.
```bash
$ hass
```
## Logging
By default logging in Home Assistant is tuned for operating in production (set to INFO by default, with some modules set to even less verbose logging levels).
You can use the [logger](https://www.home-assistant.io/components/logger/) component to adjust logging to DEBUG to see even more details about what is going on:
```yaml
logger:
default: info
logs:
homeassistant.core: debug
nest.nest: debug
asyncio: debug
homeassistant.components.cloud.iot: debug
```

View File

@ -0,0 +1,71 @@
---
title: Style guidelines
id: version-0.98.0-development_guidelines
original_id: development_guidelines
---
Home Assistant enforces quite 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 use [Black](https://github.com/psf/black) for uncompromised code formatting. Every pull request is automatically checked as part of the linting process and we never merge submissions that diverge.
Summary of the most relevant points:
- Comments should be full sentences and end with a period.
- [Imports](https://www.python.org/dev/peps/pep-0008/#imports) should be ordered.
- Constants and the content of lists and dictionaries should be in alphabetical order.
It is advisable to adjust IDE or editor settings to match those requirements.
## Our recommendations
For some cases [PEPs](https://www.python.org/dev/peps/) don't make a statement. This section covers our recommendations about the code style. Those points were collected from the existing code and based on what contributors and developers were using the most. This is basically a majority decision, thus you may not agree with it. But we would like to encourage you follow those recommendations to keep the code consistent.
### File headers
The docstring in the file header should describe what the file is about.
```python
"""Support for MQTT lights."""
```
### 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. A widely used style is shown below but you are free to compose the messages as you like.
```python
_LOGGER.error("No route to device: %s", self._resource)
```
```bash
2017-05-01 14:28:07 ERROR [homeassistant.components.sensor.arest] No route to device: 192.168.0.18
```
Do not print out API keys, tokens, usernames or passwords (even if they are wrong).
Also note that `_LOGGER.info` is reserved for the core, use `_LOGGER.debug` for anything else.
### Ordering of imports
Instead of order the imports manually, use [`isort`](https://github.com/timothycrosley/isort).
```bash
$ pip3 install isort
$ isort homeassistant/components/sensor/fixer.py
```
### Use new style string formatting
Prefer [f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings) over `%` or `str.format`.
```python
# New
f"{some_value} {some_other_value}"
# Old, wrong
"{} {}".format('New', 'style')
"%s %s" % ('Old', 'style')
```
One exception is for logging which uses the percentage formatting. This is to avoid formatting the log message when it is suppressed.
```python
_LOGGER.info("Can't connect to the webservice %s at %s", string1, string2)
```

View File

@ -0,0 +1,113 @@
---
title: Entity
sidebar_label: Introduction
id: version-0.98.0-entity_index
original_id: entity_index
---
Each device is represented in Home Assistant as an entity. An entity abstracts away the internal working of Home Assistant. As an integrator you don't have to worry about how services or the state machine work. Instead, you extend an entity class and implement the necessary properties and methods for the device type that you're integrating.
Below is an example switch entity that keeps track of their state in memory.
```python
from homeassistant.components.switch import SwitchDevice
class MySwitch(SwitchDevice):
def __init__(self):
self._is_on = False
@property
def name(self):
"""Name of the device."""
return 'My Switch'
@property
def is_on(self):
"""If the switch is currently on or off."""
return self._is_on
def turn_on(self, **kwargs):
"""Turn the switch on."""
self._is_on = True
def turn_off(self, **kwargs):
"""Turn the switch off."""
self._is_on = False
```
That's all there is to it to build a switch entity! Continue reading to learn more or check out the [video tutorial](https://youtu.be/Cfasc9EgbMU?t=737).
## Updating the entity
An entity represents a device. There are various strategies to keep your entity in sync with the state of the device, the most popular one being polling.
### Polling
With polling, Home Assistant will ask the entity from time to time (depending on the update interval of the component) to fetch the latest state. Home Assistant will poll an entity when the `should_poll` property returns `True` (the default value). You can either implement your update logic using `update()` or the async method `async_update()`. This method should fetch the latest state from the device and store it in an instance variable for the properties to return it.
### Subscribing to updates
When you subscribe to updates, your code is responsible for letting Home Assistant know that an update is available. Make sure you have the `should_poll` property return `False`.
Whenever you receive new state from your subscription, you can tell Home Assistant that an update is available by calling `schedule_update_ha_state()` or async callback `async_schedule_update_ha_state()`. Pass in the boolean `True` to the method if you want Home Assistant to call your update method before writing the update to Home Assistant.
## Generic properties
The entity base class has a few properties that are common among all entities in Home Assistant. These can be added to any entity regardless of the type. All these properties are optional and don't need to be implemented.
> Properties should always only return information from memory and not do I/O (like network requests). Implement `update()` or `async_update()` to fetch data.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| assumed_state | boolean | `False` | Return `True` if the state is based on our assumption instead of reading it from the device.
| available | boolean | `True` | Indicate if Home Assistant is able to read the state and control the underlying device.
| device_state_attributes | dict | `None` | Extra information to store in the state machine. It needs to be information that further explains the state, it should not be static information like firmware version. See [below](entity_index.md#standard_attributes) for details of standard attributes.
| entity_picture | URL | `None` | Url of a picture to show for the entity.
| name | string | `None` | Name of the entity
| should_poll | boolean | `True` | Should Home Assistant check with the entity for an updated state. If set to `False`, entity will need to notify Home Assistant of new updates by calling one of the [schedule update methods](#methods).
| unique_id | string | `None` | A unique identifier for this entity. Needs to be unique within a platform (ie `light.hue`). Should not be configurable by the user or be changeable. [Learn more.](entity_registry_index.md#unique-id-requirements)
## Advanced properties
The following properties are also available on entities. However, they are for advanced use only and should be used with caution.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| force_update | boolean | `False` | Write each update to the state machine, even if the data is the same. Example use: when you are directly reading the value from a connected sensor instead of a cache. Use with caution, will spam the state machine.
| hidden | boolean | `False` | Indicate if the entity should not be shown on the frontend.
| icon | icon | `None` | Icon to use in the frontend. Icons start with `mdi:` plus an [identifier](https://materialdesignicons.com/). You probably don't need this since Home Assistant already provides default icons for all devices.
| entity_registry_enabled_default | boolean | `True` | Indicate if the entity should be enabled or disabled when it is first added to the entity registry.
## System properties
The following properties are used and controlled by Home Assistant, and should not be overridden by integrations.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| enabled | boolean | `True` | Indicate if entity is enabled in the entity registry. It also returns `True` if the platform doesn't support the entity registry. Disabled entities will not be added to Home Assistant.
## Standard attributes
The following `device_state_attributes` are considered standard and should follow the convention below. The constant should be imported from `homeassistant/const.py`.
| Name | Type | Unit | Constant | Description
| ---- | ---- | ---- | -------- | -----------
| battery_charging | boolean | N/A | `ATTR_BATTERY_CHARGING` | Battery charging status of the entity, shown as a boolean `true` or `false`. If charging is not supported, then this attribute should not be created.
| battery_level | integer | % | `ATTR_BATTERY_LEVEL` | Battery level of the entity, shown as an integer percentage between 0-100.
## Lifecycle hooks
Use these lifecycle hooks to execute code when certain events happen to the entity. All lifecycle hooks are async methods.
### `async_added_to_hass()`
Called when an entity has their entity_id and hass object assigned, before it is written to the state machine for the first time. Example uses: restore the state, subscribe to updates or set callback/dispatch function/listener.
### `async_will_remove_from_hass()`
Called when an entity is about to be removed from Home Assistant. Example use: disconnect from the server or unsubscribe from updates.
## Changing the entity model
If you want to add a new feature to an entity or any of its subtypes (light, switch, etc), you will need to propose it first in our [architecture repo](https://github.com/home-assistant/architecture/issues). Only additions will be considered that are common features among various vendors.

View File

@ -0,0 +1,45 @@
---
title: Entity Registry and disabling entities
sidebar_label: Disabling entities
id: version-0.98.0-entity_registry_disabled_by
original_id: entity_registry_disabled_by
---
The entity registry tracks all entities with unique IDs. For each entity, the registry keeps track of options that impact how the entity interacts with the core. One of these options is `disabled_by`.
When `disabled_by` is set to a string value, the entity will not be added to Home Assistant when the integration passes it to `async_add_entities`.
## Integration Architecture
Integrations will need to make sure that they work correctly when their entities get disabled. If your integration is keeping references to the created entity objects, it should register those references only inside the entity's lifecycle method `async_added_to_hass`. This lifecycle method is only called if the entity is actually added to Home Assistant (and so it's not disabled).
Entity disabling works with entities provided via a config entry or via an entry in configuration.yaml. If your integration is set up via a config entry and supports [unloading](config_entries_index.md#unloading-entries), Home Assistant will be able to reload your integration after entities have been enabled/disabled to apply the changes without a restart.
## Users editing the entity registry
One way an entity can be disabled is by the user editing the entity registry via the UI. In this case, the `disabled_by` value will be set to `user`. This will only work with entities that are already registered.
## Integrations setting default value of disabled_by for new entity registry entries
As an integration you can control if your entity is enabled when it is first registered. This is controlled by the `entity_registry_enabled_default` property. It defaults to `True`, which means the entity will be enabled.
If the property returns `False`, the `disabled_by` value of the newly registered entity will be set to `integration`.
## Config entry system options setting default value of disabled_by for new entity registry entries
The user can also control how new entities that are related to a config entry are received by setting the system option `disable_new_entities` of a config entry to `True`. This can be done via the UI.
If an entity is getting registered and this system option is set to `True`, the `disabled_by` property will be initialized as `config_entry`.
If `disable_new_entities` is set to `True` and `entity_registry_enabled_default` returns `False`, the `disabled_by` value will be set to `integration`.
## Integrations offering options to control disabled_by
Some integrations will want to offer options to the user to control which entities are being added to Home Assistant. For example, the Unifi integration might want to offer to only include wireless clients but exclude wired clients. Another example is that the Hue integration offers an option to make the groups defined inside Hue available in Home Assistant.
Integrations can offer options to users either via [configuration.yaml](configuration_yaml_index) or using an [Options Flow](config_entries_options_flow_handler.md).
It is important that if an integration offers an option to change which entities are exposed that:
- The option is only applied to existing integrations the first time it is set. It should not be applied on every reboot. This allows the user to enable individual entities.
- The option should impact the value of the entity property `entity_registry_enabled_default` so that new entities are disabled properly.

View File

@ -0,0 +1,229 @@
---
title: Add-On Configuration
id: version-0.98.0-hassio_addon_config
original_id: hassio_addon_config
---
Each add-on is stored in a folder. The file structure looks like this:
```text
addon_name/
apparmor.txt
build.json
CHANGELOG.md
config.json
Dockerfile
icon.png
logo.png
README.md
run.sh
```
## Add-on script
As with every Docker container, you will need a script to run when the container is started. A user might run many add-ons, so it is encouraged to try to stick to Bash scripts if you're doing simple things.
All our Images have also [bashio][bashio] installed. It contains a set of commonly used operations and can be used to be included in add-ons to reduce code duplication across add-ons and therefore making it easier to develop and maintain add-ons.
When developing your script:
- `/data` is a volume for persistent storage.
- `/data/options.json` contains the user configuration. You can use bashio or `jq` inside your shell script to parse this data.
```bash
CONFIG_PATH=/data/options.json
TARGET="$(jq --raw-output '.target' $CONFIG_PATH)"
```
So if your `options` contain
```json
{ "target": "beer" }
```
then there will be a variable `TARGET` containing `beer` in the environment of your bash file afterwards.
[bashio]: https://github.com/hassio-addons/bashio
## Add-on Docker file
All add-ons are based on latest Alpine Linux. Hass.io will automatically substitute the right base image based on the machine architecture. Add `tzdata` if you need run in a different timezone. `tzdata` Is is already added to our base images.
```
ARG BUILD_FROM
FROM $BUILD_FROM
ENV LANG C.UTF-8
# Install requirements for add-on
RUN apk add --no-cache jq
# Copy data for add-on
COPY run.sh /
RUN chmod a+x /run.sh
CMD [ "/run.sh" ]
```
If you don't use local build on device or our build script, make sure that the Dockerfile have also a set of labels include:
```
LABEL io.hass.version="VERSION" io.hass.type="addon" io.hass.arch="armhf|aarch64|i386|amd64"
```
It is possible to use own base image with `build.json` or if you do not need support for automatic multi-arch building you can also use a simple docker `FROM`.
### Build Args
We support the following build arguments by default:
| ARG | Description |
|-----|-------------|
| BUILD_FROM | Hold image for dynamic builds or buildings over our systems.
| BUILD_VERSION | Add-on version (read from `config.json`).
| BUILD_ARCH | Hold current build arch inside.
## Add-on config
The config for an add-on is stored in `config.json`.
```json
{
"name": "xy",
"version": "1.2",
"slug": "folder",
"description": "long description",
"arch": ["amd64"],
"url": "website with more information about add-on (ie a forum thread for support)",
"startup": "application",
"boot": "auto",
"ports": {
"123/tcp": 123
},
"map": ["config:rw", "ssl"],
"options": {},
"schema": {},
"image": "repo/{arch}-my-custom-addon"
}
```
| Key | Type | Required | Description |
| --- | ---- | -------- | ----------- |
| name | string | yes | Name of the add-on
| version | string | yes | Version of the add-on
| slug | string | yes | Slug of the add-on
| description | string | yes | Description of the add-on
| arch | list | yes | List of supported arch: `armhf`, `armv7`, `aarch64`, `amd64`, `i386`.
| machine | list | no | Default it support any machine type. You can select that this add-on run only on specific machines.
| url | url | no | Homepage of the addon. Here you can explain the add-ons and options.
| startup | bool | yes | `initialize` will start addon on setup of Hass.io. `system` is for things like databases and not dependent on other things. `services` will start before Home Assistant, while `application` is started afterwards. Finally `once` is for applications that don't run as a daemon.
| webui | string | no | A URL for web interface of this add-on. Like `http://[HOST]:[PORT:2839]/dashboard`, the port needs the internal port, which will be replaced with the effective port. It is also possible to bind the proto part to a config options with: `[PROTO:option_name]://[HOST]:[PORT:2839]/dashboard` and he lookup if they is True and going to `https`.
| boot | string | yes | `auto` by system and manual or only `manual`
| ports | dict | no | Network ports to expose from the container. Format is `"container-port/type": host-port`.
| host_network | bool | no | If that is True, the add-on run on host network.
| host_ipc | bool | no | Default False. Allow to share the IPC namespace with others.
| host_dbus | bool | no | Default False. Map Host dbus service into add-on.
| host_pid | bool | no | Default False. Allow to run container on host PID namespace. Work only for not protected add-ons.
| devices | list | no | Device list to map into the add-on. Format is: `<path_on_host>:<path_in_container>:<cgroup_permissions>`. i.e. `/dev/ttyAMA0:/dev/ttyAMA0:rwm`
| udev | bool | no | Default False. Set this True, if your container run a own udev process.
| auto_uart | bool | no | Default False. Auto mapping all UART/Serial device from host into add-on.
| homeassistant | string | no | Pin a minimum required Home Assistant version for such Add-on. Value is a version string like `0.91.2`.
| hassio_role | str | no | Default `default`. Role based access to Hass.io API. Available: `default`, `homeassistant`, `backup`, `manager`, `admin`.
| hassio_api | bool | no | This add-on can access to Hass.io REST API. It set the host alias `hassio`.
| homeassistant_api | bool | no | This add-on can access to Hass.io Home-Assistant REST API proxy. Use `http://hassio/homeassistant/api`.
| docker_api | bool | no | Allow read-oly access to docker API for add-on. Work only for not protected add-ons.
| privileged | list | no | Privilege for access to hardware/system. Available access: `NET_ADMIN`, `SYS_ADMIN`, `SYS_RAWIO`, `SYS_TIME`, `SYS_NICE`, `SYS_RESOURCE`, `SYS_PTRACE`, `SYS_MODULE`, `DAC_READ_SEARCH`.
| full_access | bool | no | Give full access to hardware like the privileged mode in docker. Work only for not protected add-ons.
| apparmor | bool/string | no | Enable or disable AppArmor support. If it is enable, you can also use custom profiles with the name of the profile.
| map | list | no | List of maps for additional Hass.io folders. Possible values: `config`, `ssl`, `addons`, `backup`, `share`. Defaults to `ro`, which you can change by adding `:rw` to the end of the name.
| environment | dict | no | A dict of environment variable to run add-on.
| audio | bool | no | Boolean. Mark this add-on to use internal an audio system. The ALSA configuration for this add-on will be mount automatic.
| gpio | bool | no | Boolean. If this is set to True, `/sys/class/gpio` will map into add-on for access to GPIO interface from kernel. Some library need also `/dev/mem` and `SYS_RAWIO` for read/write access to this device. On system with AppArmor enabled, you need disable AppArmor or better for security, provide you own profile for the add-on.
| devicetree | bool | no | Boolean. If this is set to True, `/device-tree` will map into add-on.
| kernel_modules | bool | no | Map host kernel modules and config into add-on (readonly).
| stdin | bool | no | Boolean. If that is enable, you can use the STDIN with Hass.io API.
| legacy | bool | no | Boolean. If the docker image have no hass.io labels, you can enable the legacy mode to use the config data.
| options | dict | yes | Default options value of the add-on
| schema | dict | yes | Schema for options value of the add-on. It can be `False` to disable schema validation and use custom options.
| image | string | no | For use with Docker Hub and other container registries.
| timeout | integer | no | Default 10 (second). The timeout to wait until the docker is done or will be killed.
| tmpfs | string | no | Mount a tmpfs file system in `/tmpfs`. Valide format for this option is : `size=XXXu,uid=N,rw`. Size is mandatory, valid units (`u`) are `k`, `m` and `g` and `XXX` has to be replaced by a number. `uid=N` (with `N` the uid number) and `rw` are optional.
| discovery | list | no | A list of services they this Add-on allow to provide for Home Assistant. Currently supported: `mqtt`
| services | list | no | A list of services they will be provided or consumed with this Add-on. Format is `service`:`function` and functions are: `provide` (this add-on can provide this service), `want` (this add-on can use this service) or `need` (this add-on need this service to work correctly).
| auth_api | bool | no | Allow access to Home Assistent user backend.
| ingress | bool | no | Enable the ingress feature for the Add-on
| ingress_port | integer | no | Default `8099`. For Add-ons they run on host network, you can use `0` and read the port later on API.
| ingress_entry | string | no | Modify the URL entry point from `/`.
| panel_icon | string | no | Default: mdi:puzzle. MDI icon for the menu panel integration.
| panel_title | string | no | Default add-on name, but can Modify with this options.
| panel_admin | bool | no | Default True. Make menu entry only available with admin privileged.
### Options / Schema
The `options` dictionary contains all available options and their default value. Set the default value to `null` if the value is required to be given by the user before the add-on can start, and it show it inside default values. Only nested arrays and dictionaries are supported with a deep of two size. If you want make an option optional, put `?` to the end of data type, otherwise it will be a required value.
```json
{
"message": "custom things",
"logins": [
{ "username": "beer", "password": "123456" },
{ "username": "cheep", "password": "654321" }
],
"random": ["haha", "hihi", "huhu", "hghg"],
"link": "http://example.com/",
"size": 15,
"count": 1.2
}
```
The `schema` looks like `options` but describes how we should validate the user input. For example:
```json
{
"message": "str",
"logins": [
{ "username": "str", "password": "str" }
],
"random": ["match(^\w*$)"],
"link": "url",
"size": "int(5,20)",
"count": "float",
"not_need": "str?"
}
```
We support:
- str
- bool
- int / int(min,) / int(,max) / int(min,max)
- float / float(min,) / float(,max) / float(min,max)
- email
- url
- port
- match(REGEX)
## Add-on extended build
Additional build options for an add-on is stored in `build.json`. This file will be read from our build systems.
You need this only, if you not use the default images or need additionals things.
```json
{
"build_from": {
"armhf": "mycustom/base-image:latest"
},
"squash": false,
"args": {
"my_build_arg": "xy"
}
}
```
| Key | Required | Description |
| --- | -------- | ----------- |
| build_from | no | A dictionary with the hardware architecture as the key and the base Docker image as value.
| squash | no | Default `False`. Be carfully with this option, you can not use the image for caching stuff after that!
| args | no | Allow to set additional Docker build arguments as a dictionary.
We provide a set of [Base-Images][hassio-base] which should cover a lot of needs. If you don't want use the Alpine based version or need a specific Image tag, feel free to pin this requirements for you build with `build_from` option.
[hassio-base]: https://github.com/home-assistant/hassio-base

View File

@ -0,0 +1,182 @@
---
title: Tutorial: Making your first add-on
id: version-0.98.0-hassio_addon_tutorial
original_id: hassio_addon_tutorial
---
So you've got Home Assistant going and you've been enjoying the built-in add-ons but you're missing this one application. Time to make your own add-on! In Hass.io 0.24 we introduced the option to have local add-ons be build on your device. This is great for developing new add-ons locally.
To get started with developing add-ons, we first need access to where Hass.io looks for local add-ons. For this you can use the Samba add-on or the SSH add-on.
For Samba, once you have enabled and started it, your Hass.io instance will show up in your local network tab and share a folder called "addons". This is the folder to store your custom add-ons.
If you are on macOS and the folder is not showing up automatically, go to Finder and press CMD+K then enter 'smb://hassio.local'
![Screenshot of Windows Explorer showing a folder on the Hass.io server](/img/en/hass.io/tutorial/samba.png)
For SSH, you will have to install it. Before you can start it, you will have to have a private/public key pair and store your public key in the add-on config ([see docs for more info][ssh]). Once started, you can SSH to Hass.io and store your custom add-ons in "/addons".
![Screenshot of Putty connected to Hass.io](/img/en/hass.io/tutorial/ssh.png)
Once you have located your add-on directory, it's time to get started!
[ssh]: https://www.home-assistant.io/addons/ssh/
## Step 1: The basics
- Create a new directory called `hello_world`
- Inside that directory create three files.
`Dockerfile`:
```
ARG BUILD_FROM
FROM $BUILD_FROM
ENV LANG C.UTF-8
# Copy data for add-on
COPY run.sh /
RUN chmod a+x /run.sh
CMD [ "/run.sh" ]
```
`config.json`:
```json
{
"name": "Hello world",
"version": "1",
"slug": "hello_world",
"description": "My first real add-on!",
"arch": ["armhf", "armv7", "aarch64", "amd64", "i386"],
"startup": "before",
"boot": "auto",
"options": {},
"schema": {}
}
```
`run.sh`:
```bash
echo Hello world!
```
Make sure your editor is using UNIX-like line breaks (LF), not Dos/Windows (CRLF).
## Step 2: Installing and testing your add-on
Now comes the fun part, time to open the Hass.io UI and install and run your add-on.
- Open the Home Assistant frontend
- Go to the Hass.io panel
- On the top right click the shopping basket to go to the add-on store.
![Screenshot of the Hass.io main panel](/img/en/hass.io/screenshots/main_panel_addon_store.png)
- On the top right click the refresh button
- You should now see a new card called "Local" that lists your add-on!
![Screenshot of the local repository card](/img/en/hass.io/screenshots/local_repository.png)
- Click on your add-on to go to the add-on details page.
- Install your add-on
- Start your add-on
- Refresh the logs of your add-on, you should now see "Hello world!" in your logs.
![Screenshot of the add-on logs](/img/en/hass.io/tutorial/addon_hello_world_logs.png)
### I don't see my add-on?!
Oops! You clicked refresh in the store and your add-on didn't show up. Or maybe you just updated an option, clicked refresh and saw your add-on disappear.
When this happens, it means that your `config.json` is invalid. It's either invalid JSON or one of the specified options is incorrect. To see what went wrong, go to the Hass.io panel and in the supervisor card click on "View logs". This should bring you to a page with the logs of the supervisor. Scroll to the bottom and you should be able to find the validation error.
Once you fixed the error, go to the add-on store and click refresh again.
## Step 3: Hosting a server
Until now we've been able to do some basic stuff, but it's not very useful yet. So let's take it one step further and host a server that we expose on a port. For this we're going to use the built-in HTTP server that comes with Python 3.
To do this, we will need to update our files as follows:
- `Dockerfile`: Install Python 3
- `config.json`: Make the port from the container available on the host
- `run.sh`: Run the Python 3 command to start the HTTP server
Add to your `Dockerfile` before `RUN`:
```
# Install requirements for add-on
RUN apk add --no-cache python3
# Python 3 HTTP Server serves the current working dir
# So let's set it to our add-on persistent data directory.
WORKDIR /data
```
Add "ports" to `config.json`. This will make TCP on port 8000 inside the container available on the host on port 8000.
```json
{
"name": "Hello world",
"version": "0.2",
"slug": "hello_world",
"description": "My first real add-on!",
"arch": ["armhf", "armv7", "aarch64", "amd64", "i386"],
"startup": "before",
"boot": "auto",
"options": {},
"schema": {},
"ports": {
"8000/tcp": 8000
}
}
```
Update `run.sh` to start the Python 3 server:
```
python3 -m http.server 8000
```
## Step 4: Installing the update
Since we updated the version number in our `config.json`, Home Assistant will show an update button when looking at the add-on details. You might have to refresh your browser or click the refresh button in the add-on store for it to show up. If you did not update the version number, you can also uninstall and install the add-on again. After installing the add-on again, make sure you start it.
Now navigate to [http://hassio.local:8000](http://hassio.local:8000) to see our server in action!
![Screenshot of the file index served by the add-on](/img/en/hass.io/tutorial/python3-http-server.png)
## Bonus: Working with add-on options
In the screenshot you've probably seen that our server only served up 1 file: `options.json`. This file contains the user configuration for this add-on. Because we specified an empty "config" and "schema" in our `config.json`, the file is currently empty.
Let's see if we can get some data into that file!
To do this, we need to specify the default options and a schema for the user to change the options.
Change the options and schema entries in your `config.json` with the following:
```json
{
"options": {
"beer": true,
"wine": true,
"liquor": false,
"name": "world",
"year": 2017
},
"schema": {
"beer": "bool",
"wine": "bool",
"liquor": "bool",
"name": "str",
"year": "int"
},
}
```
Refresh the add-on store and re-install your add-on. You will now see the options available in the add-on config screen. When you now go back to our Python 3 server and download `options.json`, you'll see the options you set. [Example of how options.json can be used inside `run.sh`](https://github.com/home-assistant/hassio-addons/blob/master/mosquitto/data/run.sh#L4-L5)

View File

@ -0,0 +1,170 @@
{
"version-0.98.0-Architecture": {
"Architecture": [
"version-0.98.0-architecture_index",
"version-0.98.0-architecture_components",
"version-0.98.0-architecture_entities",
"version-0.98.0-architecture_hassio"
],
"Entities": [
"version-0.98.0-entity_index",
"version-0.98.0-entity_air_quality",
"version-0.98.0-entity_alarm_control_panel",
"version-0.98.0-entity_binary_sensor",
"version-0.98.0-entity_climate",
"version-0.98.0-entity_cover",
"version-0.98.0-entity_fan",
"version-0.98.0-entity_light",
"version-0.98.0-entity_lock",
"version-0.98.0-entity_media_player",
"version-0.98.0-entity_remote",
"version-0.98.0-entity_sensor",
"version-0.98.0-entity_switch",
"version-0.98.0-entity_vacuum",
"version-0.98.0-entity_water_heater",
"version-0.98.0-entity_weather"
],
"Authentication": [
"version-0.98.0-auth_index",
"version-0.98.0-auth_permissions",
"version-0.98.0-auth_api",
"version-0.98.0-auth_auth_provider",
"version-0.98.0-auth_auth_module"
],
"Config Entries": [
"version-0.98.0-config_entries_index"
],
"Data Entry Flow": [
"version-0.98.0-data_entry_flow_index"
],
"Entity Registry": [
"version-0.98.0-entity_registry_index",
"version-0.98.0-entity_registry_disabled_by"
],
"Device Registry": [
"version-0.98.0-device_registry_index"
],
"Area Registry": [
"version-0.98.0-area_registry_index"
]
},
"version-0.98.0-Extending Frontend": {
"Frontend": [
"version-0.98.0-frontend_index",
"version-0.98.0-frontend_architecture",
"version-0.98.0-frontend_development",
"version-0.98.0-frontend_data",
"version-0.98.0-frontend_external_auth",
"version-0.98.0-frontend_external_bus"
],
"Extending the frontend": [
"version-0.98.0-frontend_add_card",
"version-0.98.0-frontend_add_more_info",
"version-0.98.0-frontend_add_websocket_api"
],
"Custom UI": [
"version-0.98.0-lovelace_custom_card",
"version-0.98.0-frontend_creating_custom_panels"
]
},
"version-0.98.0-Extending HASS": {
"Development Workflow": [
"version-0.98.0-development_index",
"version-0.98.0-development_environment",
"version-0.98.0-development_submitting",
"version-0.98.0-development_guidelines",
"version-0.98.0-development_testing",
"version-0.98.0-development_catching_up"
],
"Building Integrations": [
"version-0.98.0-creating_integration_file_structure",
"version-0.98.0-creating_integration_manifest",
"version-0.98.0-creating_component_index",
"version-0.98.0-config_entries_config_flow_handler",
"version-0.98.0-config_entries_options_flow_handler",
"version-0.98.0-configuration_yaml_index",
"version-0.98.0-dev_101_services",
"version-0.98.0-creating_platform_index",
"version-0.98.0-creating_component_generic_discovery"
],
"Development Checklist": [
"version-0.98.0-development_checklist",
"version-0.98.0-creating_component_code_review",
"version-0.98.0-creating_platform_code_review",
"version-0.98.0-integration_quality_scale_index"
],
"Home Assistant Core 101": [
"version-0.98.0-dev_101_index",
"version-0.98.0-dev_101_hass",
"version-0.98.0-dev_101_events",
"version-0.98.0-dev_101_states",
"version-0.98.0-dev_101_config"
],
"Misc": [
"version-0.98.0-development_validation",
"version-0.98.0-development_typing"
]
},
"version-0.98.0-Misc": {
"Introduction": [
"version-0.98.0-misc"
],
"External API": [
"version-0.98.0-external_api_rest",
"version-0.98.0-external_api_rest_python",
"version-0.98.0-external_api_websocket",
"version-0.98.0-external_api_server_sent_events"
],
"Internationalization": [
"version-0.98.0-internationalization_index",
"version-0.98.0-internationalization_backend_localization",
"version-0.98.0-internationalization_custom_component_localization",
"version-0.98.0-internationalization_translation"
],
"Documentation": [
"version-0.98.0-documentation_index",
"version-0.98.0-documentation_standards",
"version-0.98.0-documentation_create_page"
],
"Intents": [
"version-0.98.0-intent_index",
"version-0.98.0-intent_firing",
"version-0.98.0-intent_handling",
"version-0.98.0-intent_conversation",
"version-0.98.0-intent_builtin"
],
"Native App Integration": [
"version-0.98.0-app_integration_index",
"version-0.98.0-app_integration_setup",
"version-0.98.0-app_integration_sending_data",
"version-0.98.0-app_integration_sensors",
"version-0.98.0-app_integration_notifications",
"version-0.98.0-app_integration_webview"
],
"asyncio": [
"version-0.98.0-asyncio_index",
"version-0.98.0-asyncio_101",
"version-0.98.0-asyncio_categorizing_functions",
"version-0.98.0-asyncio_working_with_async"
],
"Hass.io": [
"version-0.98.0-hassio_debugging",
"version-0.98.0-hassio_hass"
],
"Hass.io Add-Ons": [
"version-0.98.0-hassio_addon_index",
"version-0.98.0-hassio_addon_tutorial",
"version-0.98.0-hassio_addon_config",
"version-0.98.0-hassio_addon_communication",
"version-0.98.0-hassio_addon_testing",
"version-0.98.0-hassio_addon_publishing",
"version-0.98.0-hassio_addon_presentation",
"version-0.98.0-hassio_addon_repository",
"version-0.98.0-hassio_addon_security"
],
"Maintainer docs": [
"version-0.98.0-maintenance",
"version-0.98.0-releasing"
]
}
}

View File

@ -1,4 +1,5 @@
[
"0.98.0",
"0.97.0",
"0.96.0",
"0.95.0",