mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-06-02 16:26:30 +00:00
Add version 78
This commit is contained in:
parent
b32ec893fa
commit
bec3533a0b
@ -0,0 +1,108 @@
|
||||
---
|
||||
title: Working with Async
|
||||
id: version-0.78.0-asyncio_working_with_async
|
||||
original_id: asyncio_working_with_async
|
||||
---
|
||||
|
||||
Although we have a backwards compatible API, using the async core directly will be a lot faster. Most core components have already been rewritten to leverage the async core. This includes the EntityComponent helper (foundation of light, switch, etc), scripts, groups and automation.
|
||||
|
||||
## Interacting with the core
|
||||
|
||||
[All methods in the Home Assistant core][dev-docs] are implemented in two flavors: an async version and a version to be called from other threads. The versions for other are merely wrappers that call the async version in a threadsafe manner.
|
||||
|
||||
So if you are making calls to the core (the hass object) from within a callback or coroutine, use the methods that start with async_. If you need to call an async_ function that is a coroutine, your task must also be a coroutine.
|
||||
|
||||
## Implementing an async component
|
||||
|
||||
To make a component async, implement an async_setup.
|
||||
|
||||
```python
|
||||
def setup(hass, config):
|
||||
# Setup your component outside of the event loop.
|
||||
```
|
||||
|
||||
Will turn into:
|
||||
|
||||
```python
|
||||
async def async_setup(hass, config):
|
||||
# Setup your component inside of the event loop.
|
||||
```
|
||||
|
||||
## Implementing an async platform
|
||||
|
||||
For platforms we support async setup. Instead of setup_platform you need to have a coroutine async_setup_platform.
|
||||
|
||||
```python
|
||||
setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
# Setup your platform outside of the event loop.
|
||||
```
|
||||
|
||||
Will turn into:
|
||||
|
||||
```python
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
# Setup your platform inside of the event loop
|
||||
```
|
||||
|
||||
The only difference with the original parameters is that the `add_entities` function has been replaced by the async friendly callback `async_add_entities`.
|
||||
|
||||
## Implementing an async entity
|
||||
|
||||
You can make your entity async friendly by converting your update method to be async. This requires the dependency of your entities to also be async friendly!
|
||||
|
||||
```python
|
||||
class MyEntity(Entity):
|
||||
def update(self):
|
||||
"""Retrieve latest state."""
|
||||
self._state = fetch_state()
|
||||
```
|
||||
|
||||
Will turn into:
|
||||
|
||||
```python
|
||||
class MyEntity(Entity):
|
||||
async def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
self._state = await async_fetch_state()
|
||||
```
|
||||
|
||||
Make sure that all properties defined on your entity do not result in I/O being done. All data has to be fetched inside the update method and cached on the entity. This is because these properties are read from within the event loop and thus doing I/O will result in the core of Home Assistant waiting until your I/O is done.
|
||||
|
||||
## Calling async functions from threads
|
||||
|
||||
Sometimes it will happen that you’re in a thread and you want to call a function that is only available as async. Home Assistant includes a few async helper utilities to help with this.
|
||||
|
||||
In the following example, `say_hello` will schedule `async_say_hello` and block till the function has run and get the result back.
|
||||
|
||||
```python
|
||||
from homeassistant.util.async_ import run_coroutine_threadsafe
|
||||
|
||||
def say_hello(hass, target):
|
||||
return run_coroutine_threadsafe(
|
||||
hass.loop, async_say_hello, target).result()
|
||||
|
||||
async def async_say_hello(hass, target):
|
||||
return "Hello {}!".format(target)
|
||||
```
|
||||
|
||||
## Calling sync functions from async
|
||||
|
||||
If you are running inside an async context, it might sometimes be necessary to call a sync function. Do this like this:
|
||||
|
||||
```python
|
||||
# hub.update() is a sync function.
|
||||
result = await hass.async_add_executor_job(hub.update)
|
||||
```
|
||||
|
||||
## Starting independent task from async
|
||||
|
||||
If you want to spawn a task that will not block the current async context, you can choose to create it as a task on the event loop. It will then be executed in parallel.
|
||||
|
||||
```python
|
||||
hass.async_create_task(async_say_hello(hass, target))
|
||||
```
|
||||
|
||||
|
||||
[dev-docs]: https://dev-docs.home-assistant.io/en/master/api/core.html
|
||||
[dev-docs-async]: https://dev-docs.home-assistant.io/en/dev/api/util.html#module-homeassistant.util.async
|
223
website/versioned_docs/version-0.78.0/auth_api.md
Normal file
223
website/versioned_docs/version-0.78.0/auth_api.md
Normal file
@ -0,0 +1,223 @@
|
||||
---
|
||||
title: Authentication API
|
||||
sidebar_label: API
|
||||
id: version-0.78.0-auth_api
|
||||
original_id: auth_api
|
||||
---
|
||||
|
||||
This page will describe the steps required for your application to authorize against and integrate with Home Assistant instances. [See a demo](https://hass-auth-demo.glitch.me) powered by our helper lib [home-assistant-js-websocket](https://github.com/home-assistant/home-assistant-js-websocket).
|
||||
|
||||
Each user has their own instance of Home Assistant which gives each user control over their own data. However, we also wanted to make it easy for third party developers to create applications that allow users to integrate with Home Assistant. To achieve this, we have adopted the [OAuth 2 specification][oauth2-spec] combined with the [OAuth 2 IndieAuth extension][indieauth-spec] for generating clients.
|
||||
|
||||
## Clients
|
||||
|
||||
Before you can ask the user to authorize their instance with your application, you will need a client. In traditional OAuth2, the server needs to generate a client before a user can authorize. However, as each server belongs to a user, we've adopted a slightly different approach from [IndieAuth][indieauth-clients].
|
||||
|
||||
The client ID you need to use is the website of your application. The redirect url has to be of the same host and port as the client ID. For example:
|
||||
|
||||
- client id: `https://www.my-application.io`
|
||||
- redirect uri: `https://www.my-application.io/hass/auth_callback`
|
||||
|
||||
If you require a different redirect url (ie, if building a native app), you can add an HTML tag to the content of the website of your application (the client ID) with an approved redirect url. For example, add this to your site to whitelist redirect uri `hass://auth`:
|
||||
|
||||
```html
|
||||
<link rel='redirect_uri' href='hass://auth'>
|
||||
```
|
||||
|
||||
Home Assistant will scan the first 10kB of a website for link tags.
|
||||
|
||||
## Authorize
|
||||
|
||||
[](https://www.websequencediagrams.com/?lz=dGl0bGUgQXV0aG9yaXphdGlvbiBGbG93CgpVc2VyIC0-IENsaWVudDogTG9nIGludG8gSG9tZSBBc3Npc3RhbnQKABoGIC0-IFVzZXI6AEMJZSB1cmwgAD4JACgOOiBHbyB0bwAeBWFuZCBhAC0ICgBQDgB1DACBFw5jb2RlAHELAE4RZXQgdG9rZW5zIGZvcgAoBgBBGlQAJQUK&s=qsd)
|
||||
|
||||
> All example URLs here are shown with extra spaces and new lines for display purposes only.
|
||||
|
||||
The authorize url should contain `client_id` and `redirect_uri` as query parameters.
|
||||
|
||||
```
|
||||
http://your-instance.com/auth/authorize?
|
||||
client_id=https%3A%2F%2Fhass-auth-demo.glitch.me&
|
||||
redirect_uri=https%3A%2F%2Fhass-auth-demo.glitch.me%2F%3Fauth_callback%3D1
|
||||
```
|
||||
|
||||
Optionally you can also include a `state` parameter, this will be added to the redirect uri. The state is perfect to store the instance url that you are authenticating with. Example:
|
||||
|
||||
```
|
||||
http://your-instance.com/auth/authorize?
|
||||
client_id=https%3A%2F%2Fhass-auth-demo.glitch.me&
|
||||
redirect_uri=https%3A%2F%2Fhass-auth-demo.glitch.me%2Fauth_callback&
|
||||
state=http%3A%2F%2Fhassio.local%3A8123
|
||||
```
|
||||
|
||||
The user will navigate to this link and be presented with instructions to log in and authorize your application. Once authorized, the user will be redirected back to the passed in redirect uri with the authorization code and state as part of the query parameters. Example:
|
||||
|
||||
```
|
||||
https://hass-auth-demo.glitch.me/auth_callback
|
||||
code=12345&
|
||||
state=http%3A%2F%2Fhassio.local%3A8123
|
||||
```
|
||||
|
||||
This authorization code can be exchanged for tokens by sending it to the token endpoint (see next section).
|
||||
|
||||
## Token
|
||||
|
||||
The token endpoint returns tokens given valid grants. This grant is either an authorization code retrieved from the authorize endpoint or a refresh token. In thee case of refresh token, the token endpoint is also capable of revoking a token.
|
||||
|
||||
All interactions with this endpoint need to be HTTP POST requests to `http://your-instance.com/auth/token` with the request body encoded in `application/x-www-form-urlencoded`.
|
||||
|
||||
### Authorization code
|
||||
|
||||
> All requests to the token endpoint need to contain the exact same client ID as was used to redirect the user to the authorize endpoint.
|
||||
|
||||
Use the grant type `authorization_code` to retrieve the tokens after a user has successfully finished the authorize step. The request body is:
|
||||
|
||||
```
|
||||
grant_type=authorization_code&
|
||||
code=12345&
|
||||
client_id=https%3A%2F%2Fhass-auth-demo.glitch.me
|
||||
```
|
||||
|
||||
The return response will be an access and refresh token:
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "ABCDEFGH",
|
||||
"expires_in": 1800,
|
||||
"refresh_token": "IJKLMNOPQRST",
|
||||
"token_type": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
The access token is a short lived token that can be used to access the API. The refresh token can be used to fetch new access tokens. The `expires_in` value is seconds that the access token is valid.
|
||||
|
||||
An HTTP status code of 400 will be returned if an invalid request has been issued. The HTTP status code will be 403 if a token is requested for an inactive user.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_request",
|
||||
"error_description": "Invalid client id",
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh token
|
||||
|
||||
Once you have retrieved a refresh token via the grant type `authorization_code`, you can use it to fetch new access tokens. The request body is:
|
||||
|
||||
```
|
||||
grant_type=refresh_token&
|
||||
refresh_token=IJKLMNOPQRST&
|
||||
client_id=https%3A%2F%2Fhass-auth-demo.glitch.me
|
||||
```
|
||||
|
||||
The return response will be an access token:
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "ABCDEFGH",
|
||||
"expires_in": 1800,
|
||||
"token_type": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
An HTTP status code of 400 will be returned if an invalid request has been issued.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_request",
|
||||
"error_description": "Invalid client id",
|
||||
}
|
||||
```
|
||||
|
||||
### Revoking a refresh token
|
||||
|
||||
> client_id is not need for revoke refresh token
|
||||
|
||||
The token endpoint is also capable of revoking a refresh token. Revoking a refresh token will immediately revoke the refresh token and all access tokens that it has ever granted. To revoke a refresh token, make the following request:
|
||||
|
||||
```
|
||||
token=IJKLMNOPQRST&
|
||||
action=revoke
|
||||
```
|
||||
|
||||
The request will always respond with an empty body and HTTP status 200, regardless if the request was successful.
|
||||
|
||||
## Long-lived access token
|
||||
|
||||
A long-lived access token is usually used for 3rd party API calls and webhook-ish integrations. To generate a long-lived access token, an active websocket connection has to be established.
|
||||
|
||||
Send websocket command `auth/long_lived_access_token` will create a long-lived access token for current user. Access token will not be saved in Home Assistant. User need to record the token in secure place.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 11,
|
||||
"type": "auth/long_lived_access_token",
|
||||
"client_name": "GPS Logger",
|
||||
"client_icon": null,
|
||||
"lifespan": 365
|
||||
}
|
||||
```
|
||||
|
||||
Result will be a long-lived access token:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 11,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": "ABCDEFGH"
|
||||
}
|
||||
```
|
||||
|
||||
## Making authenticated requests
|
||||
|
||||
Once you have an access token, you can make authenticated requests to the Home Assistant APIs.
|
||||
|
||||
For the websocket connection, pass the access token in the [authentication message](https://developers.home-assistant.io/docs/en/external_api_websocket.html#authentication-phase).
|
||||
|
||||
For HTTP requests, pass the token type and access token as the authorization header:
|
||||
|
||||
```
|
||||
Authorization: Bearer ABCDEFGH
|
||||
```
|
||||
|
||||
### Example: cURL
|
||||
|
||||
```shell
|
||||
curl -X GET \
|
||||
https://your.awesome.home/api/error/all \
|
||||
-H 'Authorization: Bearer ABCDEFGH'
|
||||
```
|
||||
|
||||
### Example: Python
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
url = "https://your.awesome.home/api/error/all"
|
||||
headers = {
|
||||
'Authorization': "Bearer ABCDEFGH",
|
||||
}
|
||||
response = requests.request('GET', url, headers=headers)
|
||||
|
||||
print(response.text)
|
||||
```
|
||||
|
||||
### Example: NodeJS
|
||||
```JavaScript
|
||||
fetch('https://your.awesome.home/api/error/all', {
|
||||
headers: { Authorization: 'Bearer ABCDEFGH' }
|
||||
}).then(function (response) {
|
||||
if (!response.ok) {
|
||||
return Promise.reject(response);
|
||||
}
|
||||
return response.text();
|
||||
}).then(function (body ) {
|
||||
console.log(body);
|
||||
});
|
||||
```
|
||||
|
||||
If the access token is no longer valid, you will get a response with HTTP status code 401 unauthorized. This means that you will need to refresh the token. If the refresh token doesn't work, the tokens are no longer valid and so the user is no longer logged in. You should clear the user's data and ask the user to authorize again.
|
||||
|
||||
[oauth2-spec]: https://tools.ietf.org/html/rfc6749
|
||||
[indieauth-spec]: https://indieauth.spec.indieweb.org/
|
||||
[indieauth-clients]: https://indieauth.spec.indieweb.org/#client-identifier
|
65
website/versioned_docs/version-0.78.0/auth_auth_module.md
Normal file
65
website/versioned_docs/version-0.78.0/auth_auth_module.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Multi-factor Authentication Modules
|
||||
id: version-0.78.0-auth_auth_module
|
||||
original_id: auth_auth_module
|
||||
---
|
||||
|
||||
Multi-factor Authentication Modules are used in conjunction with [Authentication Provider](auth_auth_provider.html) to provide a fully configurable authentication framework. Each MFA module may provide one multi-factor authentication function. User can enable multiple mfa modules, but can only select one module in login process.
|
||||
|
||||
## Defining an mfa auth module
|
||||
|
||||
> We currently only support built-in mfa auth modules. Support for custom auth modules might arrive in the future.
|
||||
|
||||
Multi-facor Auth modules are defined in `homeassistant/auth/mfa_modules/<name of module>.py`. The auth module will need to provide an implementation of the `MultiFactorAuthModule` class.
|
||||
|
||||
For an example of a fully implemented auth module, please see [insecure_example.py](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/auth/mfa_modules/insecure_example.py).
|
||||
|
||||
Multi-factor Auth modules shall extend the following methods of `MultiFactorAuthModule` class.
|
||||
|
||||
| method | Required | Description
|
||||
| ------ | -------- | -----------
|
||||
| `@property def input_schema(self)` | Yes | Return a schema defined the user input form.
|
||||
| `async def async_setup_flow(self, user_id)` | Yes | Return a SetupFlow to handle the setup workflow.
|
||||
| `async def async_setup_user(self, user_id, setup_data)` | Yes | Set up user for use this auth module.
|
||||
| `async def async_depose_user(self, user_id)` | Yes | Remove user information from this auth module.
|
||||
| `async def async_is_user_setup(self, user_id)` | Yes | Return whether user is set up.
|
||||
| `async def async_validate(self, user_id, user_input)` | Yes | Given a user_id and user input, return valiidation result.
|
||||
| `async def async_initialize_login_mfa_step(self, user_id)` | No | Will be called once before display the mfa step of login flow. This is not initialization for the MFA module but the mfa step in login flow.
|
||||
|
||||
## Setup Flow
|
||||
|
||||
Before user can use a multi-factor auth module, it has to be enabled or set up. All availaable modules will be listed in user profile page, user can enable the module he/she wants to use. A setup data entry flow will guide user finish the necessary steps.
|
||||
|
||||
Each MFA module need to implement a setup flow handler extends from `mfa_modules.SetupFlow` (if only one simple setup step need, `SetupFlow` can be used as well). For example for Google Authenticator (TOTP, Time-based One Time Password) module, the flow will need to be:
|
||||
- Generate a secret and store it on instance of setup flow
|
||||
- Return `async_show_form` with a QR code in the description (injected as base64 via `description_placeholders`)
|
||||
- User scans code and enters a code to verify it scanned correctly and clock in synced
|
||||
- TOTP module saved the secret along with user_id, module is enabled for user
|
||||
|
||||
## Workflow
|
||||
|
||||
> TODO: draw a diagram
|
||||
|
||||
User == select auth provider ==> LoginFlow.init == input/validate username/password ==> LoginFlow.finish ==> if user enabled mfa ==> LoginFlow.select_mfa_module ==> initialize(optional) ==> LoginFlow.mfa == input/validate MFA code ==> LoginFlow.finish ==> Done
|
||||
|
||||
## Configuration example
|
||||
|
||||
```yaml
|
||||
# configuration.xml
|
||||
homeassistant:
|
||||
auth_providers:
|
||||
- type: homeassistant
|
||||
- type: legacy_api_password
|
||||
auth_mfa_modules:
|
||||
- type: totp
|
||||
- type: insecure_example
|
||||
users: [{'user_id': 'a_32_bytes_length_user_id', 'pin': '123456'}]
|
||||
```
|
||||
|
||||
In this example, user will first select from `homeassistant` or `legacy_api_password` auth provider. For `homeassistant` auth provider, user will first input username/password, if that user enabled both `totp` and `insecure_example`, then user need select one auth module, then input Google Authenticator code or input pin code base on the selection.
|
||||
|
||||
> insecure_example is only for demo purpose, please do not use it in production.
|
||||
|
||||
## Validation session
|
||||
|
||||
Not like auth provider, auth module use session to manage the validation. After auth provider validated, mfa module will create a validation session, include an experiation time and user_id from auth provider validate result. Mutli-factor auth moudle will not only verify the user input, and also verify the session is not experied. The validatoin session data storges in login flow instance.
|
47
website/versioned_docs/version-0.78.0/auth_index.md
Normal file
47
website/versioned_docs/version-0.78.0/auth_index.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Authentication
|
||||
sidebar_label: Introduction
|
||||
id: version-0.78.0-auth_index
|
||||
original_id: auth_index
|
||||
---
|
||||
|
||||
Home Assistant has a built-in authentication system allowing different users to interact with Home Assistant. The authentication system consist of various parts.
|
||||
|
||||

|
||||
|
||||
## Authentication providers
|
||||
|
||||
An authentication provider is used for users to authenticate themselves. It's up to the authentication provider to choose the method of authentication and the backend to use. By default we enable the built-in Home Assistant authentication provider which stores the users securely inside your configuration directory.
|
||||
|
||||
The authentication providers that Home Assistant will use are specified inside `configuration.yaml`. It is possible to have multiple instances of the same authentication provider active. In that case, each will be identified by a unique identifier. Authentication providers of the same type will not share credentials.
|
||||
|
||||
## Credentials
|
||||
|
||||
Credentials store the authentication of a user with a specific authentication provider. It is produced when a user successfully authenticates. It will allow the system to find the user in our system. If the user does not exist, a new user will be created. This user will not be activated but will require approval by the owner.
|
||||
|
||||
It is possible for a user to have multiple credentials linked to it. However, it can only have a single credential per specific authentication provider.
|
||||
|
||||
## Users
|
||||
|
||||
Each person is a user in the system. To log in as a specific user, authenticate with any of the authentication providers that are linked to this user. When a user logs in, it will get a refresh and an access token to make requests to Home Assistant.
|
||||
|
||||
### Owner
|
||||
|
||||
The first user to log in to Home Assistant will be marked as the owner. This user is able to manage users.
|
||||
|
||||
## Clients
|
||||
|
||||
Clients are applications that users use to access the Home Assistant API. Each client has a client identifier and a redirect uri. The redirect uri is used to redirect the user after it has successfully authorized.
|
||||
|
||||
## Access and refresh tokens
|
||||
|
||||
The client will be provided with an authorization code when a user successfully authorizes with Home Assistant. This code can be used to retrieve an access and a refresh token. The access token will have a limited lifetime while refresh tokens will remain valid until a user deletes it.
|
||||
|
||||
The access token is used to access the Home Assistant APIs. The refresh token is used to retrieve a new valid access token.
|
||||
|
||||
### Refresh token types
|
||||
|
||||
Refresh token has 3 different types:
|
||||
- *Normal*: is generated by a success log in request, and will be sent to user and possessed by user.
|
||||
- *System*: can only be generated by system user.
|
||||
- *Long-lived Access Token*: such refresh token is generated by user, but will not delivery to user, however the access token generated by this refresh token will send to user.
|
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Checklist for creating a component
|
||||
id: version-0.78.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 platforms follow the requirements in this checklist. This cannot be used as a reason to not follow them!
|
||||
|
||||
### 1. Requirements
|
||||
|
||||
1. Requirement version pinned: `REQUIREMENTS = ['phue==0.8.1']`
|
||||
2. We no longer want requirements hosted on GitHub. Please upload to PyPi.
|
||||
3. Requirements should only be imported inside functions. This is necessary because requirements are installed on the fly.
|
||||
|
||||
### 2. Configuration
|
||||
|
||||
1. Voluptuous schema present for config validation
|
||||
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. If you need to share global data with platforms, use the dictionary `hass.data`. `hass.data[DATA_XY]` while `XY` is the component is preferred over `hass.data[DOMAIN]`.
|
||||
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()
|
||||
```
|
@ -0,0 +1,94 @@
|
||||
---
|
||||
title: Style guidelines
|
||||
id: version-0.78.0-development_guidelines
|
||||
original_id: development_guidelines
|
||||
---
|
||||
|
||||
Home Assistant enforces strict [PEP8 style](https://www.python.org/dev/peps/pep-0008/) and [PEP 257 (Docstring Conventions)](https://www.python.org/dev/peps/pep-0257/) compliance on all code submitted. We automatically test every pull request as part of the linting process with [Coveralls](https://coveralls.io/github/home-assistant/home-assistant) and [Travis CI](https://travis-ci.org/home-assistant/home-assistant).
|
||||
|
||||
Summary of the most relevant points:
|
||||
|
||||
- Line length is limited to 79 characters (see below).
|
||||
- Use 4 spaces per indentation level. We don't use tabs.
|
||||
- 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.
|
||||
- Avoid trailing whitespace but surround binary operators with a single space.
|
||||
- Line separator should be set to `LF`.
|
||||
|
||||
The maximum line length comes directly from the [PEP8 style guide](https://www.python.org/dev/peps/pep-0008/#maximum-line-length), and is also used by the Python standard library. All code must pass these linting checks, and no exceptions will be made. There have already been numerous requests to increase the maximum line length, but after evaluating the options, the Home Assistant maintainers have decided to stay at 79 characters. This decision is final.
|
||||
|
||||
Those points may require that you adjust your IDE or editor settings.
|
||||
|
||||
## 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 unified.
|
||||
|
||||
### Quotes
|
||||
|
||||
Use single quotes `'` for single word and `"` for multiple words or sentences.
|
||||
|
||||
```python
|
||||
ATTR_WATERLEVEL = 'level'
|
||||
CONF_ATTRIBUTION = "Data provided by the WUnderground weather service"
|
||||
SENSOR_TYPES = {
|
||||
'alerts': ['Alerts', None],
|
||||
}
|
||||
```
|
||||
|
||||
### File headers
|
||||
|
||||
The docstring in the file header should contain a link to the documentation to make it easy to find further information, especially about the configuration or details which are not mentioned in the code.
|
||||
|
||||
```python
|
||||
"""
|
||||
Support for MQTT lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.mqtt/
|
||||
"""
|
||||
```
|
||||
|
||||
### Requirements
|
||||
|
||||
Please place [Platform requirements](creating_platform_code_review.md#1-requirements) right after the imports.
|
||||
|
||||
```python
|
||||
[...]
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['xmltodict==0.11.0']
|
||||
```
|
||||
|
||||
### Log messages
|
||||
|
||||
There is no need to add the platform or component name to the log messages. This will be added automatically. Like `syslog` messages there shouldn't be any period at the end. Try to avoid brackets and additional quotes around the output to make it easier for users to parse the log. A widely style is shown below but you are free to compose the messages as you like.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Don't print out wrong API keys, tokens, usernames, or passwords.
|
||||
Also note that `_LOGGER.info` is reserved for the core, use `_LOGGER.debug` in 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 [new style string formatting](https://www.python.org/dev/peps/pep-3101/) over old.
|
||||
|
||||
```python
|
||||
"{} {}".format('New', 'style')
|
||||
"%s %s" % ('Old', 'style')
|
||||
```
|
@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Device Registry
|
||||
sidebar_label: Introduction
|
||||
id: version-0.78.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'
|
||||
/>
|
||||
|
||||
| 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_hub | Identifier of a device that routes messages between this device and Home Assistant. Examples of such devices are hubs. This is used to show device topology in Home Assistant.
|
||||
|
||||
## 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 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_hub': (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,
|
||||
)
|
||||
```
|
126
website/versioned_docs/version-0.78.0/documentation_standards.md
Normal file
126
website/versioned_docs/version-0.78.0/documentation_standards.md
Normal file
@ -0,0 +1,126 @@
|
||||
---
|
||||
title: Standards
|
||||
id: version-0.78.0-documentation_standards
|
||||
original_id: documentation_standards
|
||||
---
|
||||
|
||||
To ensure that the documentation for Home Assistant is consistent and easy to follow for both novice and expert users, we ask that you follow a very strict set of standards for developing the documentation.
|
||||
|
||||
## General Documentation
|
||||
|
||||
* The language of the documentation should be American-English.
|
||||
* Don't put two spaces after a period and avoid the "Oxford comma".
|
||||
* There is no limit for the line length. You are allowed to write in a flowing text style. This will make it easier to use the GitHub online editor in the future.
|
||||
* Be objective and not gender favoring, polarizing, race related or religion inconsiderate.
|
||||
* The case of brand names, services, protocols, components, and platforms must match its respective counterpart. e.g., "Z-Wave" **not** "Zwave", "Z-wave", "Z Wave" or "ZWave". Also, "Input Select" **not** "input select" or "Input select".
|
||||
* All headings should use the `{% linkable_title %}` tag.
|
||||
* Do not use ALL CAPITALS for emphasis - use italics instead
|
||||
|
||||
## Component and Platform Pages
|
||||
|
||||
* The **Configuration Variables** section must use the `{% configuration %}` tag.
|
||||
* Configuration variables must document the requirement status (`false` or `true`)
|
||||
* Configuration variables must document the default value, if any.
|
||||
* Configuration variables must document the accepted value types (`string`, `boolean`, `integer`, `list`)
|
||||
* For configuration variables that accept multiple types, separate the types with a comma (i.e. `string, int`).
|
||||
* Use YAML sequence syntax in the sample code if it is supported.
|
||||
* All examples should be formatted to be included in `configuration.yaml` unless explicitly stated.
|
||||
* Use capital letters and `_` to indicate that the value needs to be replaced. E.g., `api_key: YOUR_API_KEY` or `api_key: REPLACE_ME`.
|
||||
* If you know that the API key or value contains [control characters](https://en.wikipedia.org/wiki/YAML#Syntax), e.g., `#`, `[`, `?`, etc., wrap it in quotes and add a note.
|
||||
* Component and platform names should be a link to their respective documentation pages.
|
||||
|
||||
Example configuration block
|
||||
|
||||
```yaml
|
||||
{% configuration %}
|
||||
some_key:
|
||||
description: This is a description of what this key is for.
|
||||
required: false
|
||||
type: string, list
|
||||
default: Optional default value - leave out if there isn't one
|
||||
{% endconfiguration %}
|
||||
```
|
||||
|
||||
## Templates
|
||||
|
||||
* All examples containing Jinja2 templates should be wrapped **outside** of the code markdown with the `{% raw %}` tag.
|
||||
* Do not use `states.switch.source.state` in templates. Instead use `states()` and `is_state()`.
|
||||
* Use double quotes (`"`) for ([more information](#single-vs-double-quotation-marks)):
|
||||
* `friendly_name`
|
||||
* Single-line templates:
|
||||
* `value_template`
|
||||
* `level_template`
|
||||
* `icon_template`
|
||||
* Children of `data_template`
|
||||
* Use single quotes (`'`) for ([more information](#single-vs-double-quotation-marks):
|
||||
* Strings inside of templates:
|
||||
* States
|
||||
* Entity IDs
|
||||
* `unit_of_measurement`
|
||||
* No whitespace around pipe character (`|`) for Jinja2 filters.
|
||||
* Single whitespace after Jinja2 opening delimiters ({% raw %}`{{`{% endraw %}).
|
||||
* Single whitespace before Jinja2 closing delimiters ({% raw %}`}}`{% endraw %}).
|
||||
* Do not quote values for:
|
||||
* `device_class`
|
||||
* `platform`
|
||||
* `condition`
|
||||
* `service`
|
||||
|
||||
## Renaming Pages
|
||||
|
||||
It can happen that a component or platform is renamed, in this case the documentation needs to be updated as well. If you rename a page, add `redirect_from:` to the file header and let it point to the old location/name of the page. Please consider to add details, like release number or old component/platform name, to the page in a [note](/developers/documentation/create_page/#html).
|
||||
|
||||
```text
|
||||
---
|
||||
...
|
||||
redirect_from: /getting-started/android/
|
||||
---
|
||||
```
|
||||
|
||||
Adding a redirect also applies if you move content around in the [documentation](/docs/).
|
||||
|
||||
## Single vs. Double Quotation Marks
|
||||
|
||||
Use single quotes (`'`) for strings inside of a template. It is more obvious to escape a single quote when necessary (i.e. `name` is a possessive noun), because the single quotes that wrap the string are closer in position to the apostrophe inside the string. Use double quotes (`"`) outside of a template (unless it is a multi-line template, in which case outside quotes are not required).
|
||||
|
||||
### Examples
|
||||
|
||||
#### {% linkable_title Double Quotes Outside, Single Quotes Inside (Valid) %}
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
...
|
||||
action:
|
||||
- service: notify.notify
|
||||
data_template:
|
||||
message: "{% if trigger.to_state.name == 'Dale\'s Bedroom' %}Someone's in your base, killing your noobs!{% else %}It's just another door.{% endif %}"
|
||||
```
|
||||
|
||||
#### Single Quotes Outside, Double Quotes Inside (Invalid)
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
...
|
||||
action:
|
||||
- service: notify.notify
|
||||
data_template:
|
||||
message: '{% if trigger.to_state.name == "Dale's Bedroom" %}Someone's in your base, killing your noobs!{% else %}It's just another door.{% endif %}'
|
||||
```
|
||||
|
||||
#### Multi-Line Template (Valid)
|
||||
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
...
|
||||
action:
|
||||
- service: notify.notify
|
||||
data_template:
|
||||
message: >-
|
||||
{% if trigger.to_state.name == 'Dale\'s Bedroom' %}
|
||||
Someone's in your base, killing your noobs!
|
||||
{% else %}
|
||||
It's just another door.
|
||||
{% endif %}
|
||||
```
|
||||
|
416
website/versioned_docs/version-0.78.0/external_api_websocket.md
Normal file
416
website/versioned_docs/version-0.78.0/external_api_websocket.md
Normal file
@ -0,0 +1,416 @@
|
||||
---
|
||||
title: WebSocket API
|
||||
id: version-0.78.0-external_api_websocket
|
||||
original_id: external_api_websocket
|
||||
---
|
||||
|
||||
Home Assistant contains a WebSocket API. This API can be used to stream information from a Home Assistant instance to any client that implements WebSockets. Implementations in different languages:
|
||||
|
||||
- [JavaScript](https://github.com/home-assistant/home-assistant-js-websocket) - powers the frontend
|
||||
- [Python](https://raw.githubusercontent.com/home-assistant/home-assistant-dev-helper/master/ha-websocket-client.py) - CLI client using [`asyncws`](https://async-websockets.readthedocs.io/en/latest/)
|
||||
- [JavaScript/HTML](https://raw.githubusercontent.com/home-assistant/home-assistant-dev-helper/master/ha-websocket.html) - WebSocket connection in your browser
|
||||
|
||||
Connect your websocket implementation to `ws://localhost:8123/api/websocket`. You will need a valid access token.
|
||||
|
||||
If you are not using the [`frontend`](https://www.home-assistant.io/components/frontend/) in your setup then you need to add the [`websocket_api` component](https://www.home-assistant.io/components/websocket_api/) to your `configuration.yaml` file to use the WebSocket API.
|
||||
|
||||
## Server states
|
||||
|
||||
1. Client connects.
|
||||
1. Authentication phase starts.
|
||||
- Server sends `auth_required` message.
|
||||
- Client sends `auth` message.
|
||||
- If `auth` message correct: go to 3.
|
||||
- Server sends `auth_invalid`. Go to 6.
|
||||
1. Send `auth_ok` message
|
||||
1. Authentication phase ends.
|
||||
1. Command phase starts.
|
||||
1. Client can send commands.
|
||||
1. Server can send results of previous commands.
|
||||
1. Client or server disconnects session.
|
||||
|
||||
During the command phase, the client attaches a unique identifier to each message. The server will add this identifier to each message so that the client can link each message to its origin.
|
||||
|
||||
## Message format
|
||||
|
||||
Each API message is a JSON serialized object containing a `type` key. After the authentication phase messages also must contain an `id`, an integer that contains the number of interactions.
|
||||
|
||||
Example of an auth message:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth",
|
||||
"access_token": "ABCDEFGHIJKLMNOPQ"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 5,
|
||||
"type":"event",
|
||||
"event":{
|
||||
"data":{},
|
||||
"event_type":"test_event",
|
||||
"time_fired":"2016-11-26T01:37:24.265429+00:00",
|
||||
"origin":"LOCAL"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication phase
|
||||
|
||||
When a client connects to the server, the server will test if the client is authenticated. Authentication will not be necessary if no api_password is set or if the user fulfills one of the other criteria for authentication (trusted network, password in url/header).
|
||||
|
||||
If no authentication is needed, the authentication phase will complete and the server will send an `auth_ok` message.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_ok"
|
||||
}
|
||||
```
|
||||
|
||||
If authentication is necessary, the server sends out `auth_required`.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_required"
|
||||
}
|
||||
```
|
||||
|
||||
This means that the next message from the client should be an auth message. You can authorize with an access token.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth",
|
||||
"access_token": "ABCDEFGH"
|
||||
}
|
||||
```
|
||||
|
||||
For now, we also support authentication with an API password (legacy auth).
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth",
|
||||
"api_password": "supersecret"
|
||||
}
|
||||
```
|
||||
|
||||
If the client supplies valid authentication, the authentication phase will complete by the server sending the `auth_ok` message:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_ok"
|
||||
}
|
||||
```
|
||||
|
||||
If the data is incorrect, the server will reply with `auth_invalid` message and disconnect the session.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_invalid",
|
||||
"message": "Invalid password"
|
||||
}
|
||||
```
|
||||
|
||||
## Command phase
|
||||
|
||||
During this phase the client can give commands to the server. The server will respond to each command with a `result` message indicating when the command is done and if it was successful.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 6.
|
||||
"type": "result",
|
||||
"success": true,
|
||||
// Can contain extra result info
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
## Subscribe to events
|
||||
|
||||
The command `subscribe_events` will subscribe your client to the event bus. You can either listen to all events or to a specific event type. If you want to listen to multiple event types, you will have to send multiple `subscribe_events` commands.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 18,
|
||||
"type": "subscribe_events",
|
||||
// Optional
|
||||
"event_type": "state_changed"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message to indicate that the subscription is active.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 18,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
For each event that matches, the server will send a message of type `event`. The `id` in the message will point at the original `id` of the `listen_event` command.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 18,
|
||||
"type":"event",
|
||||
"event":{
|
||||
"data":{
|
||||
"entity_id":"light.bed_light",
|
||||
"new_state":{
|
||||
"entity_id":"light.bed_light",
|
||||
"last_changed":"2016-11-26T01:37:24.265390+00:00",
|
||||
"state":"on",
|
||||
"attributes":{
|
||||
"rgb_color":[
|
||||
254,
|
||||
208,
|
||||
0
|
||||
],
|
||||
"color_temp":380,
|
||||
"supported_features":147,
|
||||
"xy_color":[
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"brightness":180,
|
||||
"white_value":200,
|
||||
"friendly_name":"Bed Light"
|
||||
},
|
||||
"last_updated":"2016-11-26T01:37:24.265390+00:00"
|
||||
},
|
||||
"old_state":{
|
||||
"entity_id":"light.bed_light",
|
||||
"last_changed":"2016-11-26T01:37:10.466994+00:00",
|
||||
"state":"off",
|
||||
"attributes":{
|
||||
"supported_features":147,
|
||||
"friendly_name":"Bed Light"
|
||||
},
|
||||
"last_updated":"2016-11-26T01:37:10.466994+00:00"
|
||||
}
|
||||
},
|
||||
"event_type":"state_changed",
|
||||
"time_fired":"2016-11-26T01:37:24.265429+00:00",
|
||||
"origin":"LOCAL"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Unsubscribing from events
|
||||
|
||||
You can unsubscribe from previously created subscription events. Pass the id of the original subscription command as value to the subscription field.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "unsubscribe_events",
|
||||
"subscription": 18
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message to indicate that unsubscribing was successful.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
### Calling a service
|
||||
|
||||
This will call a service in Home Assistant. Right now there is no return value. The client can listen to `state_changed` events if it is interested in changed entities as a result of a service call.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 24,
|
||||
"type": "call_service",
|
||||
"domain": "light",
|
||||
"service": "turn_on",
|
||||
// Optional
|
||||
"service_data": {
|
||||
"entity_id": "light.kitchen"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The server will indicate with a message indicating that the service is done executing.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 24,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching states
|
||||
|
||||
This will get a dump of all the current states in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_states"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the states.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching config
|
||||
|
||||
This will get a dump of the current config in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_config"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the config.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching services
|
||||
|
||||
This will get a dump of the current services in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_services"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the services.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching panels
|
||||
|
||||
This will get a dump of the current registered panels in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_panels"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the current registered panels.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching camera thumbnails
|
||||
|
||||
_Introduced in Home Assistant 0.69._
|
||||
|
||||
Return a b64 encoded thumbnail of a camera entity.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "camera_thumbnail"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the thumbnail.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": {
|
||||
"content_type": "image/jpeg",
|
||||
"content": "<base64 encoded image>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching media player thumbnails
|
||||
|
||||
_Introduced in Home Assistant 0.69._
|
||||
|
||||
Fetch a base64 encoded thumbnail picture for a media player.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "media_player_thumbnail",
|
||||
"entity_id": "media_player.living_room"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with the image encoded via base64.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": {
|
||||
"content_type": "image/jpeg",
|
||||
"content": "<base64 encoded image>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
If an error occurs, the `success` key in the `result` message will be set to `false`. It will contain an `error` key containing an object with two keys: `code` and `message`.
|
||||
|
||||
| Code | Description |
|
||||
| ----- | ------------ |
|
||||
| 1 | A non-increasing identifier has been supplied.
|
||||
| 2 | Received message is not in expected format (voluptuous validation error).
|
||||
| 3 | Requested item cannot be found
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 12,
|
||||
"type":"result",
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": 2,
|
||||
"message": "Message incorrectly formatted: expected str for dictionary value @ data['event_type']. Got 100"
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Adding more info dialogs
|
||||
id: version-0.78.0-frontend_add_more_info
|
||||
original_id: frontend_add_more_info
|
||||
---
|
||||
|
||||
Whenever the user taps or clicks on one of the cards, a more info dialog will show. The header of this dialog will be the state card, followed by the history of this entity for the last 24 hours. Below this the more info component is rendered for that entity. The more info component can show more information or allow more ways of control.
|
||||
|
||||
<img
|
||||
src='/img/en/frontend/frontend-more-info-light.png'
|
||||
alt='The more info dialog for a light allows the user to control the color and the brightness.'
|
||||
/>
|
||||
|
||||
The instructions to add a more info dialog are very similar to adding a new card type. This example will add a new more info component for the domain `camera`:
|
||||
|
||||
1. Add `'camera'` to the array `DOMAINS_WITH_MORE_INFO` in the file [/common/const.js](https://github.com/home-assistant/home-assistant-polymer/blob/master/src/common/const.js).
|
||||
2. Create the files `more-info-camera.js` in the folder [/dialogs/more-info/controls](https://github.com/home-assistant/home-assistant-polymer/tree/master/src/dialogs/more-info/controls).
|
||||
3. Add `import './more-info-camera.js';` to [/dialogs/more-info/controls/more-info-content.js](https://github.com/home-assistant/home-assistant-polymer/blob/master/src/dialogs/more-info/controls/more-info-content.js)
|
@ -0,0 +1,68 @@
|
||||
---
|
||||
title: External Authentication
|
||||
id: version-0.78.0-frontend_external_auth
|
||||
original_id: frontend_external_auth
|
||||
---
|
||||
|
||||
By default, the frontend will take care of its own authentication tokens. If none found, it will redirect the user to the login page and it will take care that the token is up to date.
|
||||
|
||||
If you want to embed the Home Assistant frontend in an external app, you will want to store the authentication inside the app but make it available to the frontend. To support this, Home Assistant exposes an external authentication API.
|
||||
|
||||
To activate this API, load the frontend with `?external_auth=1` appended to the URL. If this is passed in, Home Assistant will expect either `window.externalApp` (for Android) or `window.webkit.messageHandlers` (for iOS) to be defined containing the methods described below.
|
||||
|
||||
## Get Access Token
|
||||
|
||||
_This API has been introduced in Home Assistant 0.78._
|
||||
|
||||
When the frontend loads, it will request an access token from the external authentication. It does so by calling one of the following methods with an options object. The options object defines the callback method to be called with the response.
|
||||
|
||||
```js
|
||||
window.externalApp.getExternalAuth({
|
||||
callback: 'externalAuthSetToken'
|
||||
});
|
||||
// or
|
||||
window.webkit.messageHandlers.getExternalAuth.postMessage({
|
||||
callback: 'externalAuthSetToken'
|
||||
});
|
||||
```
|
||||
|
||||
The response should contain a boolean if it was successful and an object containing an access token and the number of seconds that it will remain valid. Pass the response to the function defined in the options object.
|
||||
|
||||
```js
|
||||
// To be called by external app
|
||||
window.externalAuthSetToken(true, {
|
||||
"access_token": "qwere",
|
||||
"expires_in": 1800
|
||||
});
|
||||
|
||||
// If unable to get new access token
|
||||
window.externalAuthSetToken(false);
|
||||
```
|
||||
|
||||
The frontend will call this method when the page first loads and whenever it needs a valid token but the previous received token has expired.
|
||||
|
||||
## Revoke Token
|
||||
|
||||
_This API has been introduced in Home Assistant 0.78._
|
||||
|
||||
When the user presses the logout button on the profile page, the external app will have to [revoke the refresh token](auth_api.md#revoking-a-refresh-token), and log the user out.
|
||||
|
||||
```js
|
||||
window.externalApp.revokeExternalAuth({
|
||||
callback: 'externalAuthSetToken'
|
||||
});
|
||||
// or
|
||||
window.webkit.messageHandlers.revokeExternalAuth.postMessage({
|
||||
callback: 'externalAuthSetToken'
|
||||
});
|
||||
```
|
||||
|
||||
When done, the external app has to call the function defined in the options object.
|
||||
|
||||
```js
|
||||
// To be called by external app
|
||||
window.externalAuthRevokeToken(true);
|
||||
|
||||
// If unable to logout
|
||||
window.externalAuthRevokeToken(false);
|
||||
```
|
@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Add-On Communication
|
||||
id: version-0.78.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 allows to communicate with every add-on, even to/from Home Assistant, by using its name or alias. Only the add-ons which run on the host network are a bit limited. These can talk with all internal add-ons by their name but all other add-on can't address these add-on by name - using an alias works for both!
|
||||
|
||||
Name/alias 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-ons `config.json`. You can use this name also as DNS name but you need replace the `_` 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. That makes it very easy to communicate with the API without knowing the password, port or any other information of the Home Assistant instance. Use this URL: `http://hassio/homeassistant/api` and internal communication is redirected to the right place. The next stept is to add `homeassistant_api: true` to `config.json` and read the environment variable `HASSIO_TOKEN` and use this as Home-Assistant password.
|
||||
|
||||
There is also a proxy for the [Home Assistant Websocket API][hass-websocket]. It works like the API proxy above and requires `HASSIO_TOKEN` as password. Use this URL: `http://hassio/homeassistant/websocket`.
|
||||
|
||||
It is also possible to talk direct to the Home Assistant instance which is named `homeassistant` over the internal network. But you need to know the configuration that is used by the running instance.
|
||||
|
||||
We have severals services for Hass.io inside Home Assistant to run tasks. To send data over STDIN to an add-on use the `hassio.addon_stdin` service.
|
||||
|
||||
## Hass.io API
|
||||
|
||||
To enables calls to the [Hass.io API][hassio-api], add `hassio_api: true` to `config.json` 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`. It could be that you need also change the Hass.io API role like `hassio_role: default`.
|
||||
|
||||
Add-ons can call some API commands without need set `hassio_api: true`:
|
||||
- `/homeassistant/info`
|
||||
- `/supervisor/info`
|
||||
- `/addons`
|
||||
- `/addons/self/...`
|
||||
- `/services*`
|
||||
|
||||
[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
|
206
website/versioned_docs/version-0.78.0/hassio_addon_config.md
Normal file
206
website/versioned_docs/version-0.78.0/hassio_addon_config.md
Normal file
@ -0,0 +1,206 @@
|
||||
---
|
||||
title: Add-On Configuration
|
||||
id: version-0.78.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/
|
||||
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.
|
||||
|
||||
When developing your script:
|
||||
|
||||
- `/data` is a volume for persistent storage.
|
||||
- `/data/options.json` contains the user configuration. You can use `jq` inside your shell script to parse this data. However, you might have to install `jq` as a separate package in your container (see `Dockerfile` below).
|
||||
|
||||
```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.
|
||||
|
||||
## Add-on Docker file
|
||||
|
||||
All add-ons are based on Alpine Linux 3.6. 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 | no | List of supported arch: `armhf`, `aarch64`, `amd64`, `i386`. Default all.
|
||||
| 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`
|
||||
| auto_uart | bool | no | Default False. Auto mapping all UART/Serial device from host into add-on.
|
||||
| hassio_role | str | no | Default `default`. Role based access to Hass.io API. Available: `default`, `homeassistant`, `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`.
|
||||
| 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.
|
||||
| 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.
|
||||
|
||||
### 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.
|
@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Add-on security
|
||||
id: version-0.78.0-hassio_addon_security
|
||||
original_id: hassio_addon_security
|
||||
---
|
||||
|
||||
Hass.io rates every add-on based on the wanted rights. An add-on with a rating of 6 is very secure. If an add-on has a rating of 1, you shouldn't run this add-on unless you are 100% sure that you can trust the source.
|
||||
|
||||
## API Role
|
||||
|
||||
For access to Hass.io API you need define a role or you run in default mode. This is only required for Hass.io API not Home Assistant proxy. Any of the role have also the default API calls inheret for that are no settings are required.
|
||||
|
||||
### Available Roles
|
||||
|
||||
| Role | Description |
|
||||
|------|-------------|
|
||||
| default | Have access to all `info` calls |
|
||||
| homeassistant | Can access to all Home Assistant API endpoints |
|
||||
| manager | Is for Add-ons they run CLIs and need extended rights |
|
||||
| admin | Have access to every API call. That is the only one they can disable/enable the Add-on protection mode |
|
||||
|
||||
## Protection
|
||||
|
||||
Default, all add-ons run in protection enabled mode. This mode prevents the add-on from getting any rights on the system. If an add-on requires more rights, you can disable this protection via the API add-on options for that add-on. But be carful, an add-on with disabled protection can destroy your system!
|
||||
|
||||
## Making a secure add-on
|
||||
|
||||
As a developer, follow the following best practices to make your add-on secure:
|
||||
|
||||
- Don't run on host network
|
||||
- Create an AppArmor profile
|
||||
- Map folders read only if you don't need write access
|
||||
- If you need any API access, make sure you that you not grant to highest permission if you don't need it
|
157
website/versioned_sidebars/version-0.78.0-sidebars.json
Normal file
157
website/versioned_sidebars/version-0.78.0-sidebars.json
Normal file
@ -0,0 +1,157 @@
|
||||
{
|
||||
"version-0.78.0-Architecture": {
|
||||
"Architecture": [
|
||||
"version-0.78.0-architecture_index",
|
||||
"version-0.78.0-architecture_components",
|
||||
"version-0.78.0-architecture_entities",
|
||||
"version-0.78.0-architecture_hassio"
|
||||
],
|
||||
"Entities": [
|
||||
"version-0.78.0-entity_index",
|
||||
"version-0.78.0-entity_alarm_control_panel",
|
||||
"version-0.78.0-entity_binary_sensor",
|
||||
"version-0.78.0-entity_climate",
|
||||
"version-0.78.0-entity_cover",
|
||||
"version-0.78.0-entity_fan",
|
||||
"version-0.78.0-entity_light",
|
||||
"version-0.78.0-entity_lock",
|
||||
"version-0.78.0-entity_media_player",
|
||||
"version-0.78.0-entity_remote",
|
||||
"version-0.78.0-entity_sensor",
|
||||
"version-0.78.0-entity_switch",
|
||||
"version-0.78.0-entity_vacuum",
|
||||
"version-0.78.0-entity_weather"
|
||||
],
|
||||
"Authentication": [
|
||||
"version-0.78.0-auth_index",
|
||||
"version-0.78.0-auth_api",
|
||||
"version-0.78.0-auth_auth_provider",
|
||||
"version-0.78.0-auth_auth_module"
|
||||
],
|
||||
"Configuration.yaml": [
|
||||
"version-0.78.0-configuration_yaml_index"
|
||||
],
|
||||
"Config Entries": [
|
||||
"version-0.78.0-config_entries_index",
|
||||
"version-0.78.0-config_entries_config_flow_handler"
|
||||
],
|
||||
"Data Entry Flow": [
|
||||
"version-0.78.0-data_entry_flow_index"
|
||||
],
|
||||
"Entity Registry": [
|
||||
"version-0.78.0-entity_registry_index"
|
||||
],
|
||||
"Device Registry": [
|
||||
"version-0.78.0-device_registry_index"
|
||||
]
|
||||
},
|
||||
"version-0.78.0-Extending Frontend": {
|
||||
"Frontend": [
|
||||
"version-0.78.0-frontend_index",
|
||||
"version-0.78.0-frontend_architecture",
|
||||
"version-0.78.0-frontend_development",
|
||||
"version-0.78.0-frontend_data",
|
||||
"version-0.78.0-frontend_external_auth"
|
||||
],
|
||||
"Extending the frontend": [
|
||||
"version-0.78.0-frontend_add_card",
|
||||
"version-0.78.0-frontend_add_more_info",
|
||||
"version-0.78.0-frontend_add_websocket_api"
|
||||
],
|
||||
"Custom UI": [
|
||||
"version-0.78.0-lovelace_custom_card",
|
||||
"version-0.78.0-frontend_creating_custom_ui",
|
||||
"version-0.78.0-frontend_creating_custom_panels"
|
||||
]
|
||||
},
|
||||
"version-0.78.0-Extending HASS": {
|
||||
"Developing a feature": [
|
||||
"version-0.78.0-development_index",
|
||||
"version-0.78.0-development_environment",
|
||||
"version-0.78.0-development_submitting",
|
||||
"version-0.78.0-development_checklist",
|
||||
"version-0.78.0-development_guidelines",
|
||||
"version-0.78.0-development_testing",
|
||||
"version-0.78.0-development_catching_up",
|
||||
"version-0.78.0-development_validation",
|
||||
"version-0.78.0-development_typing"
|
||||
],
|
||||
"Development 101": [
|
||||
"version-0.78.0-dev_101_index",
|
||||
"version-0.78.0-dev_101_hass",
|
||||
"version-0.78.0-dev_101_events",
|
||||
"version-0.78.0-dev_101_states",
|
||||
"version-0.78.0-dev_101_services",
|
||||
"version-0.78.0-dev_101_config"
|
||||
],
|
||||
"Creating Platforms": [
|
||||
"version-0.78.0-creating_platform_index",
|
||||
"version-0.78.0-creating_platform_code_review",
|
||||
"version-0.78.0-creating_platform_example_light",
|
||||
"version-0.78.0-creating_platform_example_sensor"
|
||||
],
|
||||
"Creating Components": [
|
||||
"version-0.78.0-creating_component_index",
|
||||
"version-0.78.0-creating_component_code_review",
|
||||
"version-0.78.0-creating_component_deps_and_reqs",
|
||||
"version-0.78.0-creating_component_events",
|
||||
"version-0.78.0-creating_component_states",
|
||||
"version-0.78.0-creating_component_discovery",
|
||||
"version-0.78.0-creating_component_loading",
|
||||
"version-0.78.0-creating_component_generic_discovery"
|
||||
]
|
||||
},
|
||||
"version-0.78.0-Misc": {
|
||||
"Introduction": [
|
||||
"version-0.78.0-misc"
|
||||
],
|
||||
"External API": [
|
||||
"version-0.78.0-external_api_rest",
|
||||
"version-0.78.0-external_api_websocket",
|
||||
"version-0.78.0-external_api_server_sent_events"
|
||||
],
|
||||
"Internationalization": [
|
||||
"version-0.78.0-internationalization_index",
|
||||
"version-0.78.0-internationalization_backend_localization",
|
||||
"version-0.78.0-internationalization_custom_component_localization",
|
||||
"version-0.78.0-internationalization_translation"
|
||||
],
|
||||
"Documentation": [
|
||||
"version-0.78.0-documentation_index",
|
||||
"version-0.78.0-documentation_standards",
|
||||
"version-0.78.0-documentation_create_page"
|
||||
],
|
||||
"Intents": [
|
||||
"version-0.78.0-intent_index",
|
||||
"version-0.78.0-intent_firing",
|
||||
"version-0.78.0-intent_handling",
|
||||
"version-0.78.0-intent_conversation",
|
||||
"version-0.78.0-intent_builtin"
|
||||
],
|
||||
"asyncio": [
|
||||
"version-0.78.0-asyncio_index",
|
||||
"version-0.78.0-asyncio_101",
|
||||
"version-0.78.0-asyncio_categorizing_functions",
|
||||
"version-0.78.0-asyncio_working_with_async"
|
||||
],
|
||||
"Hass.io": [
|
||||
"version-0.78.0-hassio_debugging",
|
||||
"version-0.78.0-hassio_hass"
|
||||
],
|
||||
"Hass.io Add-Ons": [
|
||||
"version-0.78.0-hassio_addon_index",
|
||||
"version-0.78.0-hassio_addon_tutorial",
|
||||
"version-0.78.0-hassio_addon_config",
|
||||
"version-0.78.0-hassio_addon_communication",
|
||||
"version-0.78.0-hassio_addon_testing",
|
||||
"version-0.78.0-hassio_addon_publishing",
|
||||
"version-0.78.0-hassio_addon_presentation",
|
||||
"version-0.78.0-hassio_addon_repository",
|
||||
"version-0.78.0-hassio_addon_security"
|
||||
],
|
||||
"Maintainer docs": [
|
||||
"version-0.78.0-maintenance",
|
||||
"version-0.78.0-releasing"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
[
|
||||
"0.78.0",
|
||||
"0.77.0",
|
||||
"0.76.0",
|
||||
"0.75.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user