release 94

This commit is contained in:
Paulus Schoutsen 2019-06-12 11:49:14 -07:00
parent f1465e617e
commit 7ee774329d
17 changed files with 2262 additions and 0 deletions

View File

@ -1471,6 +1471,56 @@
}, },
"version-0.93.0/version-0.93.0-hassio_debugging": { "version-0.93.0/version-0.93.0-hassio_debugging": {
"title": "Debugging Hass.io" "title": "Debugging Hass.io"
},
"version-0.94.0/version-0.94.0-config_entries_config_flow_handler": {
"title": "Integration Configuration",
"sidebar_label": "Configuration"
},
"version-0.94.0/version-0.94.0-creating_component_code_review": {
"title": "Checklist for creating a component",
"sidebar_label": "Component Checklist"
},
"version-0.94.0/version-0.94.0-creating_component_index": {
"title": "Creating a Minimal Component",
"sidebar_label": "Minimal Component"
},
"version-0.94.0/version-0.94.0-creating_integration_manifest": {
"title": "Integration Manifest",
"sidebar_label": "Manifest"
},
"version-0.94.0/version-0.94.0-creating_platform_code_review": {
"title": "Checklist for creating a platform",
"sidebar_label": "Platform Checklist"
},
"version-0.94.0/version-0.94.0-data_entry_flow_index": {
"title": "Data Entry Flow",
"sidebar_label": "Introduction"
},
"version-0.94.0/version-0.94.0-device_registry_index": {
"title": "Device Registry",
"sidebar_label": "Introduction"
},
"version-0.94.0/version-0.94.0-entity_cover": {
"title": "Cover Entity",
"sidebar_label": "Cover"
},
"version-0.94.0/version-0.94.0-external_api_rest": {
"title": "REST API"
},
"version-0.94.0/version-0.94.0-frontend_creating_custom_panels": {
"title": "Creating custom panels"
},
"version-0.94.0/version-0.94.0-frontend_creating_custom_ui": {
"title": "Creating custom UI"
},
"version-0.94.0/version-0.94.0-frontend_external_bus": {
"title": "External Bus"
},
"version-0.94.0/version-0.94.0-hassio_addon_communication": {
"title": "Add-On Communication"
},
"version-0.94.0/version-0.94.0-hassio_debugging": {
"title": "Debugging Hass.io"
} }
}, },
"links": { "links": {

View File

@ -0,0 +1,142 @@
---
title: Integration Configuration
sidebar_label: Configuration
id: version-0.94.0-config_entries_config_flow_handler
original_id: config_entries_config_flow_handler
---
> This option is currently only available for built-in components.
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.
To define it, extend the ConfigFlow base class from the config entries module, and decorate it with the `HANDLERS.register` decorator:
```python
from homeassistant import config_entries
@config_entries.HANDLERS.register(DOMAIN)
class ExampleConfigFlow(config_entries.ConfigFlow):
```
## 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
@config_entries.HANDLERS.register(DOMAIN)
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, info):
if info is not None:
# process info
return self.async_show_form(
step_id='init',
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
from .const import DOMAIN
async def _async_has_devices(hass):
"""Return if there are devices that can be discovered."""
import aiolifx
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,69 @@
---
title: Checklist for creating a component
sidebar_label: Component Checklist
id: version-0.94.0-creating_component_code_review
original_id: creating_component_code_review
---
A checklist of things to do when you're adding a new component.
> Not all existing code follow the requirements in this checklist. This cannot be used as a reason to not follow them!
### 0. Common
1. Follow our [Style guidelines](development_guidelines.md)
2. Use existing constants from [`const.py`](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/const.py)
* Only add new constants to `const.py` if they are widely used. Otherwise keep them on components level
### 1. Requirements
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.
### 2. Configuration
1. Voluptuous schema present for [configuration validation](development_validation.md)
2. Default parameters specified in voluptuous schema, not in `setup(…)`
3. Schema using as many generic config keys as possible from `homeassistant.const`
4. If your component has platforms, define a `PLATFORM_SCHEMA` instead of a `CONFIG_SCHEMA`.
5. If using a `PLATFORM_SCHEMA` to be used with `EntityComponent`, import base from `homeassistant.helpers.config_validation`
6. Never depend on users adding things to `customize` to configure behavior inside your component.
### 3. Component/platform communication
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'))
# 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
- 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 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.

View File

@ -0,0 +1,40 @@
---
title: Creating a Minimal Component
sidebar_label: Minimal Component
id: version-0.94.0-creating_component_index
original_id: creating_component_index
---
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.
More extensive examples of integrations are available from [our example repository](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/).
## 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_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 one of the two codeblocks above to test it locally.

View File

@ -0,0 +1,139 @@
---
title: Integration Manifest
sidebar_label: Manifest
id: version-0.94.0-creating_integration_manifest
original_id: creating_integration_manifest
---
Since 0.92.0, every 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, except 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 Integration",
"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.
## Config Flow
Specify the `config_flow` key if your integration has a config flow to create a config entry. When specified, the file `config_flow.py` needs to exist in your integration.
```json5
{
"config_flow": true
}
```
## 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 are 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 an array of strings. Each entry is a `pip` compatible string. For example, the media player Cast platform depends on the Python package PyChromecast v3.2.0: `["pychromecast==3.2.0"]`.
> 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==3.2.0 --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 -e ./pychromecast
hass --skip-pip
```
## Zeroconf
If your integration supports discovery via [Zeroconf](https://en.wikipedia.org/wiki/Zero-configuration_networking), you can add the type to your manifest. If the user has the `zeroconf` integration loaded, it will load the `zeroconf` step of your integration's config flow when it is discovered.
```json5
{
"zeroconf": ["_googlecast._tcp.local."]
}
```
## SSDP
If your integration supports discovery via [SSDP](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol), you can add the type to your manifest. If the user has the `ssdp` integration loaded, it will load the `ssdp` step of your integration's config flow when it is discovered. We support SSDP discovery by ST, manufacturer and device type. Your integration is discovered if any of the specified info is found. It's up to your config flow to filter out duplicates.
```json5
{
"ssdp": {
"st": [
"roku:ecp"
],
"manufacturer": [
"Roku"
],
"device_type": [
"urn:roku-com:device:player:1-0"
]
}
}
```
## HomeKit
If your integration supports discovery via HomeKit, you can add the supported model names to your manifest. If the user has the `zeroconf` integration loaded, it will load the `homekit` step of your integration's config flow when it is discovered.
HomeKit discovery works by testing if the discovered modelname starts with any of the model names specified in the manifest.json.
```json5
{
"homekit": {
"models": [
"LIFX"
]
}
}
```
Discovery via HomeKit does not mean that you have to talk the HomeKit protocol to communicate with your device. You can communicate with the device however you see fit.
When a discovery info is routed to your integration because of this entry in your manifest, the discovery info is no longer routed to integrations that listen to the HomeKit zeroconf type.

View File

@ -0,0 +1,88 @@
---
title: Checklist for creating a platform
sidebar_label: Platform Checklist
id: version-0.94.0-creating_platform_code_review
original_id: creating_platform_code_review
---
A checklist of things to do when you're adding a new platform.
> Not all existing platforms follow the requirements in this checklist. This cannot be used as a reason to not follow them!
### 0. Common
1. Follow our [Style guidelines](development_guidelines.md)
2. Use existing constants from [`const.py`](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/const.py)
* Only add new constants to `const.py` if they are widely used. Otherwise keep them on platform level
* Use `CONF_MONITORED_CONDITIONS` instead of `CONF_MONITORED_VARIABLES`
### 1. Requirements
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.
### 2. Configuration
1. If the platform can be set up directly, add a voluptuous schema for [configuration validation](development_validation.md)
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`
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,
})
```
### 3. Setup Platform
1. Verify that the passed in configuration (user/pass/host etc.) works.
2. Group your calls to `add_devices` if possible.
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 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. 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.
### 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'))
# 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/)

View File

@ -0,0 +1,284 @@
---
title: Data Entry Flow
sidebar_label: Introduction
id: version-0.94.0-data_entry_flow_index
original_id: data_entry_flow_index
---
Data Entry Flow is a data entry framework that is part of Home Assistant. Data entry is done via data entry flows. A flow can represent a simple login form or a multi-step setup wizard for a component. A Flow Manager is managing all flows that are in progress and handles creation of new flows.
Data Entry Flow is being used in Home Assistant to create config entries.
## Flow Manager
This is the class that manages the flows that are in progress. When instantiating one, you pass in two async callbacks:
```python
async def async_create_flow(handler, context=context, data=data)
```
The manager delegates instantiating of config flow handlers to this async callback. This allows the parent of the manager to define their own way of finding handlers and preparing a handler for instantiation. For example, in the case of the config entry manager, it will make sure that the dependencies and requirements are setup.
```python
async def async_finish_flow(flow, result)
```
This async callback is called when a flow is finished or aborted. i.e. `result['type'] in [RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_ABORT]`. The callback function can modify result and return it back, if the result type changed to `RESULT_TYPE_FORM`, the flow will continue running, display another form.
If the result type is `RESULT_TYPE_FORM`, the result should like:
```python
{
# The result type of the flow
'type': RESULT_TYPE_FORM,
# the id of the flow
'flow_id': 'abcdfgh1234,
# handler name
'handler': 'hue',
# name of the step, flow.async_step_[step_id] will be called when form submitted
'step_id': 'init',
# a voluptuous schema to build and validate user input
'data_schema': vol.Schema(),
# an errors dict, None if no errors
'errors': errors,
# a detail information about the step
'description_placeholders': description_placeholders,
}
```
If the result type is `RESULT_TYPE_CREATE_ENTRY`, the result should like:
```python
{
# Data schema version of the entry
'version': 2,
# The result type of the flow
'type': RESULT_TYPE_CREATE_ENTRY,
# the id of the flow
'flow_id': 'abcdfgh1234,
# handler name
'handler': 'hue',
# title and data as created by the handler
'title': 'Some title',
'result': {
'some': 'data'
},
}
```
If the result type is `RESULT_TYPE_ABORT`, the result should like:
```python
{
# The result type of the flow
'type': RESULT_TYPE_ABORT,
# the id of the flow
'flow_id': 'abcdfgh1234,
# handler name
'handler': 'hue',
# the abort reason
'reason': 'already_configured',
}
```
## Flow Handler
Flow handlers will handle a single flow. A flow contains one or more steps. When a flow is instantiated, the `FlowHandler.init_step` step will be called. Each step has three different possible results: "Show Form", "Abort" and "Create Entry".
At a minimum, each flow handler will have to define a version number and a step. This doens't have to be `init`, as `async_create_flow` can assign `init_step` depends on diffreent workflow, for example in configuration, `context.source` will be use as `init_step`.
The bare minimum config flow:
```python
from homeassistant import data_entry_flow
@config_entries.HANDLERS.register(DOMAIN)
class ExampleConfigFlow(data_entry_flow.FlowHandler):
# The schema version of the entries that it creates
# Home Assistant will call your migrate method if the version changes
# (this is not implemented yet)
VERSION = 1
async def async_step_user(self, user_input=None):
# Do something
```
### Show Form
This result type will show a form to the user to fill in. You define the current step, the schema of the data (using voluptuous) and optionally a dictionary of errors. Title and description of the step will be provided via the translation file. Where this is defined depends on the context of the data entry flow.
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
# Use OrderedDict to guarantee order of the form shown to the user
data_schema = OrderedDict()
data_schema[vol.Required('username')] = str
data_schema[vol.Required('password')] = str
return self.async_show_form(
step_id='init',
data_schema=vol.Schema(data_schema)
)
```
After the user has filled in the form, the step method will be called again and the user input is passed in. Your step will only be called if the user input passes your data schema. When the user passes in data, you will have to do extra validation of the data. For example, you can verify that the passed in username and password are valid.
If something is wrong, you can return a dictionary with errors. Each key in the error dictionary refers to a field name that contains the error. Use the key `base` if you want to show an error unrelated to a specific field. The specified errors need to refer to a key in a translation file.
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
errors = {}
if user_input is not None:
# Validate user input
valid = await is_valid(user_input)
if valid:
# See next section on create entry usage
return self.create_entry(...)
errors['base'] = 'auth_error'
# Use OrderedDict to guarantee order of the form shown to the user
data_schema = OrderedDict()
data_schema[vol.Required('username')] = str
data_schema[vol.Required('password')] = str
return self.async_show_form(
step_id='init',
data_schema=vol.Schema(data_schema),
errors=errors
)
```
#### Multi-step flows
If the user input passes validation, you can again return one of the three return values. If you want to navigate the user to the next step, return the return value of that step:
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_init(self, user_input=None):
errors = {}
if user_input is not None:
# Validate user input
valid = await is_valid(user_input)
if valid:
# Store info to use in next step
self.init_info = user_input
# Return the form of the next step
return await self.async_step_account()
...
```
### Create Entry
When the result is "Create Entry", an entry will be created and passed to the parent of the flow manager. A success message is shown to the user and the flow is finished. You create an entry by passing a title and data. The title can be used in the UI to indicate to the user which entry it is. Data can be any data type, as long as it is JSON serializable.
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
return self.create_entry(
title='Title of the entry',
data={
'something_special': user_input['username']
}
)
```
### Abort
When a flow cannot be finished, you need to abort it. This will finish the flow and inform the user that the flow has finished. Reasons for a flow to not be able to finish can be that a device is already configured or not compatible with Home Assistant.
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
return self.async_abort(
reason='not_supported'
)
```
### External Step & External Step Done
It is possible that a user needs to finish a config flow by doing actions on an external website. For example, setting up an integration by being redirected to an external webpage. This is commonly used by integrations that use OAuth2 to authorize a user.
_The example is about config entries, but works with other parts that use data entry flows too._
The flow works as follows:
1. User starts config flow in Home Assistant
2. Config flow prompts user to finish the flow on an external website
3. User opens the external website
4. Upon completion of the external step, the user's browser will be redirected to a Home Assistant endpoint to deliver the response.
5. The endpoint validates the response, and upon validation, marks the external step as done and returns JavaScript code to close the window: `<script>window.close()</script>`.
To be able to route the result of the external step to the Home Assistant endpoint, you will need to make sure the config flow ID is included. If your external step is an OAuth2 flow, you can leverage the oauth2 state for this. This is a variable that is not interpreted by the authorization page but is passed as-is to the Home Assistant endpoint.
6. The window closes and the Home Assistant user interface with the config flow will be visible to the user again.
7. The config flow has automatically advanced to the next step when the external step was marked as done. The user is prompted with the next step.
Example configuration flow that includes an external step.
```python
from homeassistant import config_entries
@config_entries.HANDLERS.register(DOMAIN)
class ExampleConfigFlow(data_entry_flow.FlowHandler):
VERSION = 1
data = None
async def async_step_user(self, user_input=None):
if not user_input:
return self.async_external_step(
step_id='user',
url='https://example.com/?config_flow_id={}'.format(
self.flow_id
),
)
self.data = user_input
return self.async_external_step_done(next_step_id='finish')
async def async_step_finish(self, user_input=None):
return self.async_create_entry(
title=self.data['title'],
data=self.data
)
```
Example code to mark an external step as done:
```python
from homeassistant import data_entry_flow
async def handle_result(hass, flow_id, data):
result = await hass.config_entries.async_configure(flow_id, data)
if result['type'] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP_DONE:
return "success!"
else:
return "Invalid config flow specified"
```
## Translations
Data entry flows depend on translations for showing the text in the forms. It depends on the parent of a data entry flow manager where this is stored.
## Initializing a config flow from an external source
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 a flow:
```python
await flow_mgr.async_init('hue', context={'source': data_entry_flow.SOURCE_DISCOVERY}, data=discovery_info)
```
The config flow handler will not start with the `init` step. Instead, it will be instantiated with a step name equal to the source. The step should follow the same return values as a normal step.
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_discovery(self, info):
# Handle discovery info
```

View File

@ -0,0 +1,87 @@
---
title: Device Registry
sidebar_label: Introduction
id: version-0.94.0-device_registry_index
original_id: device_registry_index
---
The device registry is a registry where Home Assistant keeps track of devices. A device is represented in Home Assistant via one or more entities. For example, a battery-powered temperature and a humidity sensor might expose entities for temperature, humidity and battery level.
<img
src='/img/en/device_registry/overview.png'
alt='Device registry overview'
/>
## What is a device?
A device in Home Assistant represents a physical device that has its own control unit. The control unit itself does not have to be smart, but it should be in control of what happens. For example, an Ecobee thermostat with 4 room sensors equals 5 devices in Home Assistant, one for the thermostat including all sensors inside it, and one for each sensor. Each device exists in a specific geographical area, and may have more than one input or output within that area.
If you connect a sensor to another device to read some of its data, it should still be represented as two different devices. The reason for this is that the sensor could be moved to read the data of another device.
A device that offers multiple endpoints, where parts of the device sense or output in different areas, should be split into separate devices and refer back to parent device with the `via_device` attribute. This allows the separate endpoints to be assigned to different areas in the building.
> Although not currently available, we could consider offering an option to users to merge devices.
## Device properties
| Attribute | Description |
| --------- | ----------- |
| id | Unique ID of device (generated by Home Assistant)
| name | Name of this device
| connections | A set of tuples of `(connection_type, connection identifier)`. Connection types are defined in the device registry module.
| identifiers | Set of identifiers. They identify the device in the outside world. An example is a serial number.
| manufacturer | The manufacturer of the device.
| model | The model of the device.
| config_entries | Config entries that are linked to this device.
| sw_version | The firmware version of the device.
| via_device | Identifier of a device that routes messages between this device and Home Assistant. Examples of such devices are hubs, or parent devices of a sub-device. This is used to show device topology in Home Assistant.
| area_id | The Area which the device is placed in.
## Defining devices
> Entity device info is only read if the entity is loaded via a [config entry](config_entries_index.md).
Each entity is able to define a device via the `device_info` property. This property is read when an entity is added to Home Assistant via a config entry. A device will be matched up with an existing device via supplied identifiers and connections, like serial numbers or MAC addresses.
```python
# Inside a platform
class HueLight(LightEntity):
@property
def device_info(self):
return {
'identifiers': {
# Serial numbers are unique identifiers within a specific domain
(hue.DOMAIN, self.unique_id)
},
'name': self.name,
'manufacturer': self.light.manufacturername,
'model': self.light.productname,
'sw_version': self.light.swversion,
'via_device': (hue.DOMAIN, self.api.bridgeid),
}
```
Components are also able to register devices in the case that there are no entities representing them. An example is a hub that communicates with the lights.
```python
# Inside a component
from homeassistant.helpers import device_registry as dr
device_registry = await dr.async_get_registry(hass)
device_registry.async_get_or_create(
config_entry=entry.entry_id,
connections={
(dr.CONNECTION_NETWORK_MAC, config.mac)
},
identifiers={
(DOMAIN, config.bridgeid)
},
manufacturer='Signify',
name=config.name,
model=config.modelid,
sw_version=config.swversion,
)
```

View File

@ -0,0 +1,189 @@
---
title: Cover Entity
sidebar_label: Cover
id: version-0.94.0-entity_cover
original_id: entity_cover
---
A cover entity is a device that controls an opening or cover, such as a garage door and window shade. Derive entity platforms from [`homeassistant.components.cover.CoverDevice`](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/cover/__init__.py).
## Properties
> Properties should always only return information from memory and not do I/O (like network requests). Implement `update()` or `async_update()` to fetch data.
### Platform Properties (to be implemented by deriving platform classes)
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| current_cover_position | int | None | The current position of cover where 0 means closed and 100 is fully open. Required with `SUPPORT_SET_POSITION`.
| current_cover_tilt_position | int | None | The current tilt position of the cover where 0 means closed/no tilt and 100 means open/maximum tilt. Required with `SUPPORT_SET_TILT_POSITION`
| is_opening | bool | None | If the cover is opening or not. Used to determine `state`.
| is_closing | bool | None | If the cover is closing or not. Used to determine `state`.
| is_closed | bool | `NotImplementedError()` | If the cover is closed or not. if the state is unknown, return `None`. Used to determine `state`.
### Entity Properties (base class properties which may be overriden)
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| device_class | string | None | Describes the type/class of the cover. Must be `None` or one of the valid values from the table below.
| supported_features | int (bitwise) | Value determined from `current_cover_position` and `current_cover_tilt_position` | Describes the supported features. See the related table below for details.
### Device Classes
| Constant | Description
|----------|-----------------------|
| `DEVICE_CLASS_AWNING` | Control of an awning, such as an exterior retractible window, door, or patio cover.
| `DEVICE_CLASS_BLIND` | Control of blinds, which are linked slats that expand or collapse to cover an opening or may be tilted to partially cover an opening, such as window blinds.
| `DEVICE_CLASS_CURTAIN` | Control of curtains or drapes, which is often fabric hung above a window or door that can be drawn open.
| `DEVICE_CLASS_DAMPER` | Control of a mechanical damper that reduces air flow, sound, or light.
| `DEVICE_CLASS_DOOR` | Control of a door or gate that provides access to an area.
| `DEVICE_CLASS_GARAGE` | Control of a garage door that provides access to a garage.
| `DEVICE_CLASS_SHADE` | Control of shades, which are a continous plane of material or connected cells that expanded or collapsed over an opening, such as window shades.
| `DEVICE_CLASS_SHUTTER` | Control of shutters, which are linked slats that swing out/in to cover an opening or may be tilted to partially cover an opening, such as indoor or exterior window shutters.
| `DEVICE_CLASS_WINDOW` | Control of a physical window that opens and closes or may tilt.
### States
| Constant | Description
|----------|------------------------|
| `STATE_OPENING` | The cover is in the process of opening to reach a set position.
| `STATE_OPEN` | The cover has reached the open position.
| `STATE_CLOSING` | The cover is in the process of closing to reach a set position.
| `STATE_CLOSED` | The cover has reach the closed position.
### Supported Features
Supported features constants are combined using the bitwise or (`|`) operator.
| Constant | Description |
|----------|--------------------------------------|
| `SUPPORT_OPEN` | The cover supports being opened.
| `SUPPORT_CLOSE` | The cover supports being closed.
| `SUPPORT_SET_POSITION` | The cover supports moving to a specific position between opened and closed.
| `SUPPORT_STOP` | The cover supports stopping the current action (open, close, set position)
| `SUPPORT_OPEN_TILT` | The cover supports being tilting open.
| `SUPPORT_CLOSE_TILT` | The cover supports being tilting closed.
| `SUPPORT_SET_TILT_POSITION` | The cover supports moving to a specific tilt position between opened and closed.
| `SUPPORT_STOP_TILT` | The cover supports stopping the current tilt action (open, close, set position)
## Methods
### Open cover
Only implement this method if the flag `SUPPORT_OPEN` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def open_cover(self, **kwargs):
"""Open the cover."""
async def async_open_cover(self, **kwargs):
"""Open the cover."""
```
### Close cover
Only implement this method if the flag `SUPPORT_CLOSE` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def close_cover(self, **kwargs):
"""Close cover."""
async def async_close_cover(self, **kwargs):
"""Close cover."""
```
### Set cover position
Only implement this method if the flag `SUPPORT_SET_POSITION` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
```
### Stop cover
Only implement this metohd if the flag `SUPPORT_STOP` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def stop_cover(self, **kwargs):
"""Stop the cover."""
async def async_stop_cover(self, **kwargs):
"""Stop the cover."""
```
### Open cover tilt
Only implement this method if the flag `SUPPORT_OPEN_TILT` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def open_cover_tilt(self, **kwargs):
"""Open the cover tilt."""
async def async_open_cover_tilt(self, **kwargs):
"""Open the cover tilt."""
```
### Close cover tilt
Only implement this method if the flag `SUPPORT_CLOSE_TILT` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def close_cover_tilt(self, **kwargs):
"""Close the cover tilt."""
async def async_close_cover_tilt(self, **kwargs):
"""Close the cover tilt."""
```
### Set cover tilt position
Only implement this method if the flag `SUPPORT_SET_TILT_POSITION` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
async def async_set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position."""
```
### Stop cover tilt
Only implement this method if the flag `SUPPORT_STOP_TILT` is set.
```python
class MyCover(CoverDevice):
# Implement one of these methods.
def stop_cover_tilt(self, **kwargs):
"""Stop the cover."""
async def async_stop_cover_tilt(self, **kwargs):
"""Stop the cover."""
```

View File

@ -0,0 +1,552 @@
---
title: REST API
id: version-0.94.0-external_api_rest
original_id: external_api_rest
---
Home Assistant provides a RESTful API on the same port as the web frontend. (default port is port 8123).
If you are not using the [`frontend`](https://www.home-assistant.io/components/frontend/) in your setup then you need to add the [`api` component](https://www.home-assistant.io/components/api/) to your `configuration.yaml` file.
* http://IP_ADDRESS:8123/ is an interface to control Home Assistant.
* http://IP_ADDRESS:8123/api/ is a RESTful API.
The API accepts and returns only JSON encoded objects.
All API calls have to be accompanied by the header `Authorization: Bearer ABCDEFGH`, where `ABCDEFGH` is replaced by your token. You can obtain a token ("Long-Lived Access Token") by logging into the frontend using a web browser, and going to [your profile](https://www.home-assistant.io/docs/authentication/#your-account-profile) `http://IP_ADDRESS:8123/profile`.
There are multiple ways to consume the Home Assistant Rest API. One is with `curl`:
```bash
$ curl -X GET \
-H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://IP_ADDRESS:8123/ENDPOINT
```
Another option is to use Python and the [Requests](http://docs.python-requests.org/en/latest/) module.
```python
from requests import get
url = 'http://localhost:8123/ENDPOINT'
headers = {
'Authorization': 'Bearer ABCDEFGH',
'content-type': 'application/json',
}
response = get(url, headers=headers)
print(response.text)
```
Another option is to use the Restful Command component https://www.home-assistant.io/components/rest_command/ in a Home Assistant automation or script.
```yaml
turn_light_on:
url: http://localhost:8123/api/states/light.study_light
method: POST
headers:
authorization: 'Bearer ABCDEFGH'
content-type: 'application/json'
payload: '{"state":"on"}'
```
Successful calls will return status code 200 or 201. Other status codes that can return are:
- 400 (Bad Request)
- 401 (Unauthorized)
- 404 (Not Found)
- 405 (Method not allowed)
### Actions
The API supports the following actions:
#### GET /api/
Returns a message if the API is up and running.
```json
{
"message": "API running."
}
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/
```
#### GET /api/config
Returns the current configuration as JSON.
```json
{
"components":[
"sensor.cpuspeed",
"frontend",
"config.core",
"http",
"map",
"api",
"sun",
"config",
"discovery",
"conversation",
"recorder",
"group",
"sensor",
"websocket_api",
"automation",
"config.automation",
"config.customize"
],
"config_dir":"/home/ha/.homeassistant",
"elevation":510,
"latitude":45.8781529,
"location_name":"Home",
"longitude":8.458853651,
"time_zone":"Europe/Zurich",
"unit_system":{
"length":"km",
"mass":"g",
"temperature":"\u00b0C",
"volume":"L"
},
"version":"0.56.2",
"whitelist_external_dirs":[
"/home/ha/.homeassistant/www",
"/home/ha/.homeassistant/"
]
}
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/config
```
#### GET /api/discovery_info
Returns basic information about the Home Assistant instance as JSON.
```json
{
"base_url": "http://192.168.0.2:8123",
"location_name": "Home",
"requires_api_password": true,
"version": "0.56.2"
}
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/discovery_info
```
#### GET /api/events
Returns an array of event objects. Each event object contains event name and listener count.
```json
[
{
"event": "state_changed",
"listener_count": 5
},
{
"event": "time_changed",
"listener_count": 2
}
]
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/events
```
#### GET /api/services
Returns an array of service objects. Each object contains the domain and which services it contains.
```json
[
{
"domain": "browser",
"services": [
"browse_url"
]
},
{
"domain": "keyboard",
"services": [
"volume_up",
"volume_down"
]
}
]
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/services
```
#### GET /api/history/period/&lt;timestamp>
Returns an array of state changes in the past. Each object contains further details for the entities.
The `<timestamp>` (`YYYY-MM-DDThh:mm:ssTZD`) is optional and defaults to 1 day before the time of the request. It determines the beginning of the period.
You can pass the following optional GET parameters:
- `filter_entity_id=<entity_ids>` to filter on one or more entities - comma separated.
- `end_time=<timestamp>` to choose the end of the period in URL encoded format (defaults to 1 day).
```json
[
[
{
"attributes": {
"friendly_name": "Weather Temperature",
"unit_of_measurement": "\u00b0C"
},
"entity_id": "sensor.weather_temperature",
"last_changed": "2016-02-06T22:15:00+00:00",
"last_updated": "2016-02-06T22:15:00+00:00",
"state": "-3.9"
},
{
"attributes": {
"friendly_name": "Weather Temperature",
"unit_of_measurement": "\u00b0C"
},
"entity_id": "sensor.weather_temperature",
"last_changed": "2016-02-06T22:15:00+00:00",
"last_updated": "2016-02-06T22:15:00+00:00",
"state": "-1.9"
},
]
]
```
Sample `curl` commands:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/history/period/2016-12-29T00:00:00+02:00
```
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/history/period/2016-12-29T00:00:00+02:00?filter_entity_id=sensor.temperature
```
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/history/period/2016-12-29T00:00:00+02:00?end_time=2016-12-31T00%3A00%3A00%2B02%3A00
```
#### GET /api/states
Returns an array of state objects. Each state has the following attributes: entity_id, state, last_changed and attributes.
```json
[
{
"attributes": {},
"entity_id": "sun.sun",
"last_changed": "2016-05-30T21:43:32.418320+00:00",
"state": "below_horizon"
},
{
"attributes": {},
"entity_id": "process.Dropbox",
"last_changed": "22016-05-30T21:43:32.418320+00:00",
"state": "on"
}
]
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/states
```
#### GET /api/states/&lt;entity_id>
Returns a state object for specified entity_id. Returns 404 if not found.
```json
{
"attributes":{
"azimuth":336.34,
"elevation":-17.67,
"friendly_name":"Sun",
"next_rising":"2016-05-31T03:39:14+00:00",
"next_setting":"2016-05-31T19:16:42+00:00"
},
"entity_id":"sun.sun",
"last_changed":"2016-05-30T21:43:29.204838+00:00",
"last_updated":"2016-05-30T21:50:30.529465+00:00",
"state":"below_horizon"
}
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/states/sensor.kitchen_temperature
```
#### GET /api/error_log
Retrieve all errors logged during the current session of Home Assistant as a plaintext response.
```text
15-12-20 11:02:50 homeassistant.components.recorder: Found unfinished sessions
15-12-20 11:03:03 netdisco.ssdp: Error fetching description at http://192.168.1.1:8200/rootDesc.xml
15-12-20 11:04:36 homeassistant.components.alexa: Received unknown intent HelpIntent
```
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/error_log
```
#### GET /api/camera_proxy/camera.&lt;entity_id>
Returns the data (image) from the specified camera entity_id.
Sample `curl` command:
```bash
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/camera_proxy/camera.my_sample_camera?time=1462653861261 -o image.jpg
```
#### POST /api/states/&lt;entity_id>
Updates or creates the current state of an entity.
Expects a JSON object that has at least a state attribute:
```json
{
"state": "below_horizon",
"attributes": {
"next_rising":"2016-05-31T03:39:14+00:00",
"next_setting":"2016-05-31T19:16:42+00:00"
}
}
```
The return code is 200 if the entity existed, 201 if the state of a new entity was set. A location header will be returned with the URL of the new resource. The response body will contain a JSON encoded State object.
```json
{
"attributes": {
"next_rising":"2016-05-31T03:39:14+00:00",
"next_setting":"2016-05-31T19:16:42+00:00"
},
"entity_id": "sun.sun",
"last_changed": "2016-05-30T21:43:29.204838+00:00",
"last_updated": "2016-05-30T21:47:30.533530+00:00",
"state": "below_horizon"
}
```
Sample `curl` command:
```bash
$ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
-d '{"state": "25", "attributes": {"unit_of_measurement": "°C"}}' \
http://localhost:8123/api/states/sensor.kitchen_temperature
```
#### POST /api/events/&lt;event_type>
Fires an event with event_type
You can pass an optional JSON object to be used as `event_data`.
```json
{
"next_rising":"2016-05-31T03:39:14+00:00",
}
```
Returns a message if successful.
```json
{
"message": "Event download_file fired."
}
```
#### POST /api/services/&lt;domain>/&lt;service>
Calls a service within a specific domain. Will return when the service has been executed or after 10 seconds, whichever comes first.
You can pass an optional JSON object to be used as `service_data`.
```json
{
"entity_id": "light.Ceiling"
}
```
Returns a list of states that have changed while the service was being executed.
```json
[
{
"attributes": {},
"entity_id": "sun.sun",
"last_changed": "2016-05-30T21:43:32.418320+00:00",
"state": "below_horizon"
},
{
"attributes": {},
"entity_id": "process.Dropbox",
"last_changed": "22016-05-30T21:43:32.418320+00:00",
"state": "on"
}
]
```
Sample `curl` commands:
Turn the light on:
```bash
$ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
-d '{"entity_id": "switch.christmas_lights"}' \
http://localhost:8123/api/services/switch/turn_on
```
Send a MQTT message:
```bash
$ curl -X POST \
-H "Content-Type: application/json" \
-H "x-ha-access:YOUR_PASSWORD" \
-d '{"payload": "OFF", "topic": "home/fridge", "retain": "True"}' \
http://localhost:8123/api/services/mqtt/publish
```
> The result will include any states that changed while the service was being executed, even if their change was the result of something else happening in the system.
#### POST /api/template
Render a Home Assistant template. [See template docs for more information.](https://www.home-assistant.io/topics/templating/)
```json
{
"template": "Paulus is at {{ states('device_tracker.paulus') }}!"
}
```
Returns the rendered template in plain text.
```text
Paulus is at work!
```
Sample `curl` command:
```bash
$ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
-d '{"template": "It is {{ now }}!"}' http://localhost:8123/api/template
```
#### POST /api/config/core/check_config
Trigger a check of `configuration.yaml`. No additional data needs to be passed in with this request.
If the check is successful, the following will be returned:
```javascript
{
"errors": null,
"result": "valid"
}
```
If the check fails, the errors attribute in the object will list what caused the check to fail. For example:
```javascript
{
"errors": "Integration not found: frontend:",
"result": "invalid"
}
```
#### POST /api/event_forwarding
Set up event forwarding to another Home Assistant instance.
Requires a JSON object that represents the API to forward to.
```javascript
{
"host": "machine",
"api_password": "my_super_secret_password",
"port": 8880 // optional
}
```
It will return a message if event forwarding was set up successfully.
```json
{
"message": "Event forwarding setup."
}
```
#### DELETE /api/event_forwarding
Cancel event forwarding to another Home Assistant instance.<br>
Requires a JSON object that represents the API to cancel forwarding to.
```javascript
{
"host": "machine",
"api_password": "my_super_secret_password",
"port": 8880 // optional
}
```
It will return a message if event forwarding was canceled successfully.
```json
{
"message": "Event forwarding cancelled."
}
```

View File

@ -0,0 +1,35 @@
---
title: Creating custom panels
id: version-0.94.0-frontend_creating_custom_panels
original_id: frontend_creating_custom_panels
---
Panels are pages that show information within Home Assistant and can allow controlling it. Panels are linked from the sidebar and rendered full screen. They have real-time access to the Home Assistant object via JavaScript. Examples of panels in the app are Map, Logbook and History.
Besides components registering panels, users can also register panels using the `panel_custom` component. This allows users to quickly build their own custom interfaces for Home Assistant.
## Introduction
Panels are defined as custom elements. You can use any framework that you want, as long as you wrap it up as a custom element. To quickly get started with a panel, we've created a [React custom panel starter kit](https://github.com/home-assistant/custom-panel-starter-kit-react).
## API reference
The Home Assistant frontend will pass information to your panel by setting properties on your custom element. The following properties are set:
| Property | Type | Description
| -------- | ---- | -----------
| hass | object | Current state of Home Assistant
| narrow | boolean | if the panel should render in narrow mode
| panel | object | Panel information. Config is available as `panel.config`.
## JavaScript versions
The Home Assistant user interface is currently served to browsers in modern JavaScript and older JavaScript (ES5). The older version has a wider browser support but that comes at a cost of size and performance.
If you do need to run with ES5 support, you will need to load the ES5 custom elements adapter before defining your element:
```js
window.loadES5Adapter().then(function() {
customElements.define('my-panel', MyCustomPanel)
});
```

View File

@ -0,0 +1,122 @@
---
title: Creating custom UI
id: version-0.94.0-frontend_creating_custom_ui
original_id: frontend_creating_custom_ui
---
> This feature has been derepcated and is no longer supported. To add custom UI to Home Assistant, build a [custom Lovelace card](lovelace_custom_card.md) instead.
### State card
If you would like to use your own [State card](frontend_add_card.md) without merging your code into [home-assistant-polymer](https://github.com/home-assistant/home-assistant-polymer/) you can create your own implementation.
Put the element source file and its dependencies in `www/custom_ui/` directory under your Home Assistant [configuration](https://www.home-assistant.io/docs/configuration/) directory.
For example if creating a state card for the `light` domain named `state-card-my-custom-light` put `state-card-my-custom-light.html` in `www/custom_ui/`.
That file should implement `<state-card-my-custom-light>` tag with Polymer.
In `state-card-my-custom-light.html` you should use `<link rel="import">` to import all the dependencies **not** used by Home Assistant's UI.
Do not import any dependencies used by the Home Assistant UI.
Importing those will work in `development: 1` mode, but will fail in production mode.
1. In the `customize:` section of the `configuration.yaml` file put `custom_ui_state_card: state-card-my-custom-light`.
2. In the `frontend` section use `extra_html_url` to specify the URL to load.
Example:
`configuration.yaml`:
```yaml
homeassistant:
customize:
light.bedroom:
custom_ui_state_card: state-card-my-custom-light
frontend:
extra_html_url:
- /local/custom_ui/state-card-my-custom-light.html
```
`www/custom_ui/state-card-my-custom-light.html`:
```html
<script>
{
// show the version of your custom UI in the HA dev info panel (HA 0.66.0+):
const _NAME = 'My custom light';
const _URL = 'https://home-assistant.io/developers/frontend_creating_custom_ui/';
const _VERSION = '20180312';
if (!window.CUSTOM_UI_LIST) window.CUSTOM_UI_LIST = [];
window.CUSTOM_UI_LIST.push({
name: _NAME,
url: _URL,
version: _VERSION
});
}
</script>
<dom-module id='state-card-my-custom-light'>
<template>
<style>
</style>
<textarea>[[_toStr(stateObj)]]</textarea>
</template>
</dom-module>
<script>
class StateCardMyCustomLight extends Polymer.Element {
static get is() { return 'state-card-my-custom-light'; }
static get properties() {
return {
// Home Assistant object
hass: Object,
// inDialog is true if shown as more-info-card
inDialog: {
type: Boolean,
value: false,
},
// includes state, config and more information of the entity
stateObj: Object,
};
}
_toStr(obj) {
return JSON.stringify(obj, null, 2);
}
}
customElements.define(StateCardMyCustomLight.is, StateCardMyCustomLight);
</script>
```
> Some browsers don't support latest ECMAScript standards, these require a separate ES5 compatible file (`extra_html_url_es5`).
For more possibilities, see the [Custom UI section](https://www.home-assistant.io/cookbook/#user-interface) on our Examples page.
### More info dialog
_Introduced in Home Assistant 0.69._
Similar to the custom State card, if you would like to use your own [More info dialog](frontend_add_more_info.md) you can create your own implementation.
Following a similar example, if creating a more info dialog a light named `more-info-my-custom-light` put `more-info-my-custom-light.html` in `www/custom_ui/`.
1. In the `customize:` section of the `configuration.yaml` file put `custom_ui_more_info: more-info-my-custom-light`.
2. In the `frontend` section use `extra_html_url` to specify the URL to load.
Example:
`configuration.yaml`:
```yaml
homeassistant:
customize:
light.bedroom:
custom_ui_more_info: more-info-my-custom-light
frontend:
extra_html_url:
- /local/custom_ui/more-info-my-custom-light.html
```

View File

@ -0,0 +1,159 @@
---
title: External Bus
id: version-0.94.0-frontend_external_bus
original_id: frontend_external_bus
---
The frontend is able to set up a message bus with an external app that is embedding the Home Assistant frontend. This system is a generalization of the [external auth](frontend_external_auth.md), making it easier to add more commands in the future without extensive plumbing on either the app or frontend side.
## Message exchange
Just like external auth, message exchange is achieved by the external app making a JavaScript method available.
Messages are passed to the external app as serialized JSON objects. The function that will be called takes a single parameter: a string. The external app will have to process the message and deal with it accordingly (or ignore it).
On Android, your app needs to define the following method:
```ts
window.externalApp.externalBus(message: string)
```
On iOS, your app needs to define the following method:
```ts
window.webkit.messageHandlers.externalBus.postMessage(message: string);
```
To send messages to the frontend, serialize your message to JSON and call the following function from the external app:
```ts
window.externalBus(message: string)
```
## Message format
The message describes an action or a piece of information that the sender wants the receiver to do or know about. If it's an action, the sender will expect a response with the result of that action. A response to a command can either be successful or failed.
### Action and Info Message format
The format of a message that contains or provides information is the same. It contains an identifier, a type and an optional payload (depending on the type).
A result message will re-use the identifier in the response, to indicate to which action the response is related.
The basic format of a message is the following:
```ts
{
id: number;
type: string;
payload?: unknown;
}
```
An example message:
```json
{
"id": 5,
"type": "config/get"
}
```
### Result Message Format
If the message was an action, the sender will expect a response with the result. The response is either success or failure.
The type of result depends on the type of the message that it is responding to. For example, if it is responding to `config/get`, the result should be an object describing the configuration.
Message formats:
```ts
interface SuccessResult {
id: number;
type: "result";
success: true;
result: unknown;
}
interface ErrorResult {
id: number;
type: "result";
success: false;
error: {
code: string;
message: string;
};
}
```
## Supported messages
### Get External Config
Available in: Home Assistant 0.92
Type: `config/get`
Direction: frontend to external app.
Expects answer: yes
Query the external app for the external configuration. The external configuration is used to customize the experience in the frontend.
Expected response payload:
```ts
{
hasSettingsScreen: boolean;
}
```
- `hasSettingsScreen` set to true if the external app will show a configuration screen when it receives the command `config_screen/show`. If so, a new option will be added to the sidebar to trigger the configuration screen.
### Show Config Screen `config_screen/show`
Available in: Home Assistant 0.92
Type: `config_screen/show`
Direction: frontend to external app.
Expect answer: no
Show the configuration screen of the external app.
### Connection Status update `connection-status`
Available in: Home Assistant 0.92
Type: `connection-status`
Direction: frontend to external app.
Expect answer: no
Notify the external app if the frontend is connected to Home Assistant.
Payload structure:
```ts
{
event: "connected" | "auth-invalid" | "disconnected";
}
```
### Trigger Haptic `haptic`
Available in: Home Assistant 0.92
Type: `haptic`
Direction: frontend to external app.
Expect answer: no
Notify the external app to trigger haptic feedback.
Payload structure:
```ts
{
hapticType:
| "success"
| "warning"
| "failure"
| "light"
| "medium"
| "heavy"
| "selection";
}
```

View File

@ -0,0 +1,46 @@
---
title: Add-On Communication
id: version-0.94.0-hassio_addon_communication
original_id: hassio_addon_communication
---
There are different ways for communication between add-ons inside Hass.io.
## Network
We use an internal network that's allowed to communicate with every add-on, including to/from Home Assistant, by using its name or alias. Only add-ons that run on the host network are limited in that they can talk with all internal add-ons by their name, but all other add-ons can't address these add-ons by name. However, using an alias works for both!
Names/aliases are used for communication inside Hass.io.
The name is generated using the following format: `{REPO}_{SLUG}`, e.g., `local_xy` or `3283fh_myaddon`. In this example, `{SLUG}` is defined in an add-on's `config.json` file. You can use this name as the DNS name also, but you need replace any `_` with `-` to have a valid hostname. If an add-on is installed locally, `{REPO}` will be `local`. If the add-on is installed from a Github repository, `{REPO}` is a hashed identifier generated from the GitHub repository's URL (ex: https://github.com/xy/my_hassio_addons). See [here](https://github.com/home-assistant/hassio/blob/587047f9d648b8491dc8eef17dc6777f81938bfd/hassio/addons/utils.py#L17) to understand how this identifier is generated. Note that this identifier is required in certain service calls that use the [Hass.io add-on API][hassio-addon-api]. You can view the repository identifiers for all currently-installed add-ons via a GET request to the hassio API `addons` endpoint.
Use `hassio` for communication with the internal API.
## Home Assistant
An add-on can talk to the [Home Assistant API][hass-api] using the internal proxy. This makes it very easy to communicate with the API without knowing the password, port or any other information about the Home Assistant instance. Using this URL: `http://hassio/homeassistant/api` ensures that internal communication is redirected to the right place. The next step is to add `homeassistant_api: true` to the `config.json` file and read the environment variable `HASSIO_TOKEN`. Use this as the Home-Assistant password.
There is also a proxy for the [Home Assistant Websocket API][hass-websocket] that works like the API proxy above and requires `HASSIO_TOKEN` as the password. Use this URL: `http://hassio/homeassistant/websocket`.
It is also possible to talk directly to the Home Assistant instance, which is named `homeassistant`, over the internal network. However, you'll need to know the configuration that is used by the running instance.
We have several services for Hass.io inside Home Assistant to run tasks. Send data over STDIN to an add-on to use the `hassio.addon_stdin` service.
## Hass.io API
To enable calls to the [Hass.io API][hassio-api], add `hassio_api: true` to the `config.json` file and read the environment variable `HASSIO_TOKEN`. Now you can use the API over the URL: `http://hassio/`. Use the `HASSIO_TOKEN` with header `X-HASSIO-KEY`. You may also need to change the Hass.io API role to `hassio_role: default`.
Add-ons can call some API commands without needing to set `hassio_api: true`:
- `/homeassistant/api`
- `/homeassistant/api/stream`
- `/homeassistant/websocket`
- `/addons/self/*`
- `/services*`
- `/discovery*`
- `/info`
***Note:*** For Home Assistant API access requirements, see above.
[hass-api]: https://www.home-assistant.io/developers/rest_api/
[hass-websocket]: https://www.home-assistant.io/developers/websocket_api/
[hassio-api]: https://github.com/home-assistant/hassio/blob/master/API.md
[hassio-addon-api]: https://github.com/home-assistant/hassio/blob/dev/API.md#restful-for-api-addons

View File

@ -0,0 +1,90 @@
---
title: Debugging Hass.io
id: version-0.94.0-hassio_debugging
original_id: hassio_debugging
---
> This section is not for end users. End users should use the [SSH add-on] to SSH into Hass.io. This is for <b>developers</b> of Hass.io. Do not ask for support if you are using these options.
[SSH add-on]: https://www.home-assistant.io/addons/ssh/
The following debug tips and tricks are for developers who are running the Hass.io image and are working on the base image. If you use the generic Linux installer script, you should be able to access your host and logs as per your host.
## Debug Supervisor
Visual Studio Code config:
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Hass.io remote debug",
"type": "python",
"request": "attach",
"port": 33333,
"host": "IP",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/usr/src/hassio"
}
]
}
]
}
```
You need set the dev mode on supervisor and enable debug with options. You need also install the Remote debug Add-on from Developer Repository to expose the debug port to Host.
## SSH access to the host
### resinOS based Hass.io (deprecated)
Create an `authorized_keys` file containing your public key, and place it in the root of the boot partition of your SD card. See [Generating SSH Keys](#generating-ssh-keys) section below if you need help generating keys. Once the device is booted, you can access your device as root over SSH on port 22222.
### HassOS based Hass.io
Use a USB drive formatted with FAT, ext4, or NTFS and name it CONFIG (case sensitive). Create an `authorized_keys` file (no extension) containing your public key, and place it in the root of the USB drive. File needs to be ANSI encoded (not UTF-8) and must have Unix line ends (LF), not Windows (CR LF). See [Generating SSH Keys](#generating-ssh-keys) section below if you need help generating keys. From the UI, navigate to the hass.io system page and choose "Import from USB". You can now access your device as root over SSH on port 22222. Alternatively, the file will be imported from the USB when the hass.io device is rebooted.
> Make sure when you are copying the public key to the root of the USB drive that you rename the file correctly to `authorized_keys` with no `.pub` file extension.
You should then be able to SSH into your Hass.io device. On Mac/Linux, use:
```
ssh root@hassio.local -p 22222
```
You will initially be logged in to Hass.io CLI for HassOS where you can perform normal [CLI functions]. If you need access to the host system use the 'login' command. [Hass.io OS] is a hypervisor for Docker. See the [Hass.io Architecture] documentation for information regarding the Hass.io supervisor. The supervisor offers an API to manage the host and running the Docker containers. Home Assistant itself and all installed addon's run in separate Docker containers.
[CLI functions]: https://www.home-assistant.io/hassio/commandline/
[Hass.io OS]: https://github.com/home-assistant/hassos
[Hass.io Architecture]: https://developers.home-assistant.io/docs/en/architecture_hassio.html
## Checking the logs
```bash
# Logs from the supervisor service on the Host OS
journalctl -f -u hassos-supervisor.service
# Hass.io supervisor logs
docker logs hassos_supervisor
# Home Assistant logs
docker logs homeassistant
```
## Accessing the container bash
```bash
docker exec -it homeassistant /bin/bash
```
[windows-keys]: https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-putty-on-digitalocean-droplets-windows-users
### Generating SSH Keys
Windows instructions for how to generate and use private/public keys with Putty are [here][windows-keys]. Instead of the droplet instructions, add the public key as per above instructions.
Alternative instructions, for Mac, Windows and Linux can be found [here](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/#platform-mac).
Follow steps 1-4 under 'Generating a new SSH key' (The other sections are not applicable to Hass.io and can be ignored.)
Step 3 in the link above, shows the path to the private key file `id_rsa` for your chosen operating system. Your public key, `id_rsa.pub`, is saved in the same folder. Next, select all text from text box "Public key for pasting into the authorized_keys file" and save it to the root of your USB drive as `authorized_keys`.

View File

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

View File

@ -1,4 +1,5 @@
[ [
"0.94.0",
"0.93.0", "0.93.0",
"0.92.1", "0.92.1",
"0.92.0", "0.92.0",