mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-07-10 19:06:30 +00:00
103
This commit is contained in:
parent
7c5e46e743
commit
077db33bb4
@ -86,10 +86,6 @@
|
||||
"auth_permissions": {
|
||||
"title": "Permissions"
|
||||
},
|
||||
"config_entries_config_flow_handler_oauth2": {
|
||||
"title": "Integration Configuration with OAuth2",
|
||||
"sidebar_label": "Configuration with OAuth2"
|
||||
},
|
||||
"config_entries_config_flow_handler": {
|
||||
"title": "Integration Configuration",
|
||||
"sidebar_label": "Configuration"
|
||||
@ -103,8 +99,7 @@
|
||||
"sidebar_label": "Configuration Options"
|
||||
},
|
||||
"configuration_yaml_index": {
|
||||
"title": "Integration Configuration via YAML",
|
||||
"sidebar_label": "Configuration via YAML"
|
||||
"title": "Integration Configuration via YAML"
|
||||
},
|
||||
"creating_component_code_review": {
|
||||
"title": "Checklist for creating a component",
|
||||
@ -381,8 +376,7 @@
|
||||
"sidebar_label": "HASS Integration development"
|
||||
},
|
||||
"integration_quality_scale_index": {
|
||||
"title": "Integration Quality Scale",
|
||||
"sidebar_label": "Integration Quality Scale"
|
||||
"title": "Integration Quality Scale"
|
||||
},
|
||||
"intent_builtin": {
|
||||
"title": "Built-in intents"
|
||||
@ -448,6 +442,105 @@
|
||||
"title": "Hass.io <> Home Assistant integration development",
|
||||
"sidebar_label": "HASS Integration development"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-api_lib_auth": {
|
||||
"title": "Python Library: Authentication",
|
||||
"sidebar_label": "Authentication"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-api_lib_data_models": {
|
||||
"title": "Python Library: Modelling Data",
|
||||
"sidebar_label": "Modelling Data"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-api_lib_index": {
|
||||
"title": "Building a Python library for an API",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-config_entries_config_flow_handler": {
|
||||
"title": "Integration Configuration",
|
||||
"sidebar_label": "Configuration"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-config_entries_options_flow_handler": {
|
||||
"title": "Integration Configuration Options",
|
||||
"sidebar_label": "Configuration Options"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-data_entry_flow_index": {
|
||||
"title": "Data Entry Flow",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-documentation_create_page": {
|
||||
"title": "Create a new page"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-documentation_index": {
|
||||
"title": "Documentation"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-documentation_standards": {
|
||||
"title": "Standards"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-entity_climate": {
|
||||
"title": "Climate Entity",
|
||||
"sidebar_label": "Climate"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-hassio_addon_config": {
|
||||
"title": "Add-On Configuration"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-integration_quality_scale_index": {
|
||||
"title": "Integration Quality Scale"
|
||||
},
|
||||
"version-0.101.0/version-0.101.0-lovelace_custom_card": {
|
||||
"title": "Lovelace: Custom Cards"
|
||||
},
|
||||
"version-0.102.0/version-0.102.0-app_integration_setup": {
|
||||
"title": "Connecting to an instance"
|
||||
},
|
||||
"version-0.102.0/version-0.102.0-config_entries_config_flow_handler": {
|
||||
"title": "Integration Configuration",
|
||||
"sidebar_label": "Configuration"
|
||||
},
|
||||
"version-0.102.0/version-0.102.0-development_testing": {
|
||||
"title": "Testing your code"
|
||||
},
|
||||
"version-0.102.0/version-0.102.0-entity_index": {
|
||||
"title": "Entity",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"version-0.102.0/version-0.102.0-frontend_external_auth": {
|
||||
"title": "External Authentication"
|
||||
},
|
||||
"version-0.102.0/version-0.102.0-hassio_addon_communication": {
|
||||
"title": "Add-On Communication"
|
||||
},
|
||||
"version-0.102.0/version-0.102.0-lovelace_custom_card": {
|
||||
"title": "Lovelace: Custom Cards"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-api_lib_auth": {
|
||||
"title": "Python Library: Authentication",
|
||||
"sidebar_label": "Authentication"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-app_integration_sending_data": {
|
||||
"title": "Sending data home"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-config_entries_config_flow_handler": {
|
||||
"title": "Integration Configuration",
|
||||
"sidebar_label": "Configuration"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-creating_integration_manifest": {
|
||||
"title": "Integration Manifest",
|
||||
"sidebar_label": "Manifest"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-development_testing": {
|
||||
"title": "Testing your code"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-external_api_rest": {
|
||||
"title": "REST API"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-hassio_addon_config": {
|
||||
"title": "Add-On Configuration"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-internationalization_translation": {
|
||||
"title": "Translation"
|
||||
},
|
||||
"version-0.103.0/version-0.103.0-maintenance": {
|
||||
"title": "Maintenance"
|
||||
},
|
||||
"version-0.72/version-0.72-architecture_components": {
|
||||
"title": "Components Architecture",
|
||||
"sidebar_label": "Components"
|
||||
|
267
website/versioned_docs/version-0.103.0/api_lib_auth.md
Normal file
267
website/versioned_docs/version-0.103.0/api_lib_auth.md
Normal file
@ -0,0 +1,267 @@
|
||||
---
|
||||
title: Python Library: Authentication
|
||||
sidebar_label: Authentication
|
||||
id: version-0.103.0-api_lib_auth
|
||||
original_id: api_lib_auth
|
||||
---
|
||||
|
||||
The Authentication part of your library is responsible for acquiring authentication and for making authenticated requests. It should not be aware of what is in the requests.
|
||||
|
||||
Authentication comes in many forms, but it generally boils down to that each request is accompanied by an `authorization` header which contains an access token. The access token is generally a string of random numbers/letters.
|
||||
|
||||
Your library should be able to acquire the authentication tokens, update them if necessary and use the authentication to make requests. It should not offer features to store the authentication data.
|
||||
|
||||
Because authentication is going to be stored by the developer, it is important that you return the authentication to the developer in a format that can be JSON serializable. A `dict` with primitive types (`str`, `float`, `int`) is recommended.
|
||||
|
||||
If your API can be served from multiple locations, your authentication class should allow the developer to pass in the location of the API.
|
||||
|
||||
## Async example
|
||||
|
||||
Python allows developers to write code that is either synchronous or asynchronous (via `asyncio`). Home Assistant is written in async, but is able to work with synchronous libraries too. We prefer async libraries.
|
||||
|
||||
If you are writing a library in async, we recommend that you use `aiohttp`. It's a modern and mature HTTP library and is easy to use.
|
||||
|
||||
```python
|
||||
from aiohttp import ClientSession, ClientResponse
|
||||
|
||||
|
||||
class Auth:
|
||||
"""Class to make authenticated requests."""
|
||||
|
||||
def __init__(self, websession: ClientSession, host: str, access_token: str):
|
||||
"""Initialize the auth."""
|
||||
self.websession = websession
|
||||
self.host = host
|
||||
self.access_token = access_token
|
||||
|
||||
async def request(self, method: str, path: str, **kwargs) -> ClientResponse:
|
||||
"""Make a request."""
|
||||
headers = kwargs.get('headers')
|
||||
|
||||
if headers is None:
|
||||
headers = {}
|
||||
else:
|
||||
headers = dict(headers)
|
||||
|
||||
headers["authorization"] = self.access_token
|
||||
|
||||
return await self.websession.request(
|
||||
method,
|
||||
f"{self.host}/{path}",
|
||||
**kwargs,
|
||||
headers=headers,
|
||||
)
|
||||
```
|
||||
|
||||
To use this class, you will need to create an aiohttp ClientSession and pass it together with the API info to the constructor.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import aiohttp
|
||||
|
||||
from my_package import Auth
|
||||
|
||||
|
||||
async def main():
|
||||
async with aiohttp.ClientSession() as session:
|
||||
auth = Auth(session, "http://example.com/api", "secret_access_token")
|
||||
|
||||
# This will fetch data from http://example.com/api/lights
|
||||
resp = await auth.request('get', 'lights')
|
||||
print("HTTP response status code", resp.status)
|
||||
print("HTTP response JSON content", await resp.json())
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Sync example
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
|
||||
class Auth:
|
||||
"""Class to make authenticated requests."""
|
||||
|
||||
def __init__(self, host: str, access_token: str):
|
||||
"""Initialize the auth."""
|
||||
self.host = host
|
||||
self.access_token = access_token
|
||||
|
||||
async def request(self, method: str, path: str, **kwargs) -> requests.Response:
|
||||
"""Make a request."""
|
||||
headers = kwargs.get('headers')
|
||||
|
||||
if headers is None:
|
||||
headers = {}
|
||||
else:
|
||||
headers = dict(headers)
|
||||
|
||||
headers["authorization"] = self.access_token
|
||||
|
||||
return requests.request(
|
||||
method,
|
||||
f"{self.host}/{path}",
|
||||
**kwargs,
|
||||
headers=headers,
|
||||
)
|
||||
```
|
||||
|
||||
To use this class, construct the class with the API info.
|
||||
|
||||
```python
|
||||
from my_package import Auth
|
||||
|
||||
|
||||
auth = Auth("http://example.com/api", "secret_access_token")
|
||||
|
||||
# This will fetch data from http://example.com/api/lights
|
||||
resp = auth.request('get', 'lights')
|
||||
print("HTTP response status code", resp.status_code)
|
||||
print("HTTP response JSON content", resp.json())
|
||||
```
|
||||
|
||||
## OAuth2
|
||||
|
||||
OAuth2 is a [standardized version](https://tools.ietf.org/html/rfc6749) of an authentication schema leveraging refresh and access tokens. The access token expires within a short period of time after being issued. The refresh token can be used to acquire new access tokens.
|
||||
|
||||
Refreshing access tokens relies on a client ID and secret, which might be held by an external service. We need to structure the authentication class to be able to allow the developer to implement their own token refresh logic.
|
||||
|
||||
Home Assistant ships with the Home Assistant Cloud Account Linking service, a free cloud service to allow users to quickly connect accounts using OAuth2. Home Assistant has easy to use tools built-in to allow users to configure OAuth2-based integrations. For more info, [read here](config_entries_config_flow_handler.md#configuration-via-oauth2). These built-in tools work best if your library is implemented like the examples below.
|
||||
|
||||
### Async example
|
||||
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class AbstractAuth(ABC):
|
||||
"""Abstract class to make authenticated requests."""
|
||||
|
||||
def __init__(self, websession: ClientSession, host: str):
|
||||
"""Initialize the auth."""
|
||||
self.websession = websession
|
||||
self.host = host
|
||||
|
||||
@abstractmethod
|
||||
async def async_get_access_token(self) -> str:
|
||||
"""Return a valid access token."""
|
||||
|
||||
async def request(self, method, url, **kwargs) -> ClientResponse:
|
||||
"""Make a request."""
|
||||
headers = kwargs.get('headers')
|
||||
|
||||
if headers is None:
|
||||
headers = {}
|
||||
else:
|
||||
headers = dict(headers)
|
||||
|
||||
access_token = await self.async_get_access_token()
|
||||
headers["authorization"] = f"Bearer {access_token}"
|
||||
|
||||
return await self.websession.request(
|
||||
method,
|
||||
f"{self.host}/{url}",
|
||||
**kwargs,
|
||||
headers=headers,
|
||||
)
|
||||
```
|
||||
|
||||
Now the developer that is using your library will have to implement the abstract method for getting the access token. Let's assume that the developer has their own token manager class.
|
||||
|
||||
```python
|
||||
from my_package import AbstractAuth
|
||||
|
||||
|
||||
class Auth(AbstractAuth):
|
||||
|
||||
def __init__(self, websession: ClientSession, host: str, token_manager):
|
||||
"""Initialize the auth."""
|
||||
super().__init__(websession, host)
|
||||
self.token_manager = token_manager
|
||||
|
||||
async def async_get_access_token(self) -> str:
|
||||
"""Return a valid access token."""
|
||||
if self.token_manager.is_token_valid():
|
||||
return self.token_manager.access_token
|
||||
|
||||
await self.token_manager.fetch_access_token()
|
||||
await self.token_manager.save_access_token()
|
||||
|
||||
return self.token_manager.access_token
|
||||
```
|
||||
|
||||
### Sync example
|
||||
|
||||
If you are using `requests`, we recommend that you use the `requests_oauthlib` package. Below is an example that works with a local client ID and secret but also allows outsourcing token fetching to Home Assistant.
|
||||
|
||||
```python
|
||||
from typing import Optional, Union, Callable, Dict
|
||||
|
||||
from requests import Response
|
||||
from requests_oauthlib import OAuth2Session
|
||||
from oauthlib.oauth2 import TokenExpiredError
|
||||
|
||||
|
||||
class Auth:
|
||||
def __init__(
|
||||
self,
|
||||
host: str,
|
||||
token: Optional[Dict[str, str]] = None,
|
||||
client_id: str = None,
|
||||
client_secret: str = None,
|
||||
token_updater: Optional[Callable[[str], None]] = None,
|
||||
):
|
||||
self.host = host
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.token_updater = token_updater
|
||||
|
||||
extra = {"client_id": self.client_id, "client_secret": self.client_secret}
|
||||
|
||||
self._oauth = OAuth2Session(
|
||||
auto_refresh_kwargs=extra,
|
||||
client_id=client_id,
|
||||
token=token,
|
||||
token_updater=token_updater,
|
||||
)
|
||||
|
||||
def refresh_tokens(self) -> Dict[str, Union[str, int]]:
|
||||
"""Refresh and return new tokens."""
|
||||
token = self._oauth.refresh_token(f"{self.host}/auth/token")
|
||||
|
||||
if self.token_updater is not None:
|
||||
self.token_updater(token)
|
||||
|
||||
return token
|
||||
|
||||
def request(self, method: str, path: str, **kwargs) -> Response:
|
||||
"""Make a request.
|
||||
|
||||
We don't use the built-in token refresh mechanism of OAuth2 session because
|
||||
we want to allow overriding the token refresh logic.
|
||||
"""
|
||||
url = f"{self.host}/{path}"
|
||||
try:
|
||||
return getattr(self._oauth, method)(url, **kwargs)
|
||||
except TokenExpiredError:
|
||||
self._oauth.token = self.refresh_tokens()
|
||||
|
||||
return getattr(self._oauth, method)(url, **kwargs)
|
||||
```
|
||||
|
||||
A developer will now be able to override the refresh token function to route it via their own external service.
|
||||
|
||||
```python
|
||||
from my_package import AbstractAuth
|
||||
|
||||
|
||||
class Auth(AbstractAuth):
|
||||
|
||||
def refresh_tokens(self) -> Dict[str, Union[str, int]]:
|
||||
"""Refresh and return new tokens."""
|
||||
self.token_manager.fetch_access_token()
|
||||
self.token_manager.save_access_token()
|
||||
|
||||
return self.token_manager.access_token
|
||||
```
|
@ -0,0 +1,230 @@
|
||||
---
|
||||
title: Sending data home
|
||||
id: version-0.103.0-app_integration_sending_data
|
||||
original_id: app_integration_sending_data
|
||||
---
|
||||
|
||||
Once you have registered your app with the mobile app component, you can start interacting with Home Assistant via the provided webhook information.
|
||||
|
||||
The first step is to turn the returned webhook ID into a full URL: `<instance_url>/api/webhook/<webhook_id>`. This will be the only url that we will need for all our interactions. The webhook endpoint will not require authenticated requests.
|
||||
|
||||
If you were provided a Cloudhook URL during registration, you should use that by default and only fall back to a constructed URL as described above if that request fails.
|
||||
|
||||
If you were provided a remote UI URL during registration, you should use that as the `instance_url` when constructing a URL and only fallback to the user provided URL if the remote UI URL fails.
|
||||
|
||||
To summarize, here's how requests should be made:
|
||||
|
||||
1. If you have a Cloudhook URL, use that until a request fails. When a request fails, go to step 2.
|
||||
2. If you have a remote UI URL, use that to construct a webhook URL: `<remote_ui_url>/api/webhook/<webhook_id>`. When a request fails, go to step 3.
|
||||
3. Construct a webhook URL using the instance URL provided during setup: `<instance_url>/api/webhook/<webhook_id>`.
|
||||
|
||||
## Short note on instance URLs
|
||||
|
||||
Some users have configured Home Assistant to be available outside of their home network using a dynamic DNS service. There are some routers that don't support hairpinning / NAT loopback: a device sending data from inside the routers network, via the externally configured DNS service, to Home Asisstant, which also resides inside the local network.
|
||||
|
||||
To work around this, the app should record which WiFi SSID is the users home network, and use a direct connection when connected to the home WiFi network.
|
||||
|
||||
## Interaction basics
|
||||
|
||||
### Request
|
||||
|
||||
All interaction will be done by making HTTP POST requests to the webhook url. These requests do not need to contain authentication.
|
||||
|
||||
The payload format depends on the type of interaction, but it all shares a common base:
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "<type of message>",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
If you received a `secret` during registration, you **MUST** encrypt your message and put it in the payload like this:
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "encrypted",
|
||||
"encrypted": true,
|
||||
"encrypted_data": "<encrypted message>"
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
As a general rule, expect to receive a 200 response for all your requests. There are a few cases in which you will receive another code:
|
||||
|
||||
- You will receive a 400 status code if your JSON is invalid. However, you will not receive this error if the encrypted JSON is invalid.
|
||||
- You will receive a 201 when creating a sensor
|
||||
- If you receive a 404, the `mobile_app` component most likely isn't loaded.
|
||||
- Receiving a 410 means the integration has been deleted. You should notify the user and most likely register again.
|
||||
|
||||
## Implementing encryption
|
||||
|
||||
`mobile_app` supports two way encrypted communication via [Sodium](https://libsodium.gitbook.io/doc/).
|
||||
|
||||
> Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more.
|
||||
|
||||
### Choosing a library
|
||||
Libraries that wrap Sodium exist for most modern programming languages and platforms. Sodium itself is written in C.
|
||||
|
||||
Here are the libraries we suggest using, although you should feel free to use whatever works well for you.
|
||||
|
||||
- Swift/Objective-C: [swift-sodium](https://github.com/jedisct1/swift-sodium) (official library maintained by Sodium developers).
|
||||
|
||||
For other languages, please see the list of [Bindings for other languages](https://download.libsodium.org/doc/bindings_for_other_languages). If more than one choice is available, we recommend using the choice most recently updated.
|
||||
|
||||
### Configuration
|
||||
|
||||
We use the [secret-key cryptography](https://download.libsodium.org/doc/secret-key_cryptography) features of Sodium to encrypt and decrypt payloads. All payloads are JSON encoded in Base64. For Base64 type, use `sodium_base64_VARIANT_ORIGINAL` (that is, "original", no padding, not URL safe).
|
||||
|
||||
### Signaling encryption support
|
||||
|
||||
During registration, you must set `supports_encryption` to `true` to enable encryption. The Home Assistant instance must be able to install `libsodium` to enable encryption. Confirm that you should make all future webhook requests encrypted by the presence of the key `secret` in the initial registration response.
|
||||
You must store this secret forever. There is no way to recover it via the Home Assistant UI and you should **not** ask users to investigate hidden storage files to re-enter the encryption key. You should create a new registration if encryption ever fails and alert the user.
|
||||
|
||||
## Update device location
|
||||
|
||||
This message will inform Home Assistant of new location information.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "update_location",
|
||||
"data": {
|
||||
"gps": [12.34, 56.78],
|
||||
"gps_accuracy": 120,
|
||||
"battery": 45
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `location_name` | string | Name of the zone the device is in.
|
||||
| `gps` | latlong | Current location as latitude and longitude.
|
||||
| `gps_accuracy` | int | GPS accurracy in meters. Must be greater than 0.
|
||||
| `battery` | int | Percentage of battery the device has left. Must be greater than 0.
|
||||
| `speed` | int | Speed of the device in meters per second. Must be greater than 0.
|
||||
| `altitude` | int | Altitude of the device in meters. Must be greater than 0.
|
||||
| `course` | int | The direction in which the device is traveling, measured in degrees and relative to due north. Must be greater than 0.
|
||||
| `vertical_accuracy` | int | The accuracy of the altitude value, measured in meters. Must be greater than 0.
|
||||
|
||||
## Call a service
|
||||
|
||||
Call a service in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "call_service",
|
||||
"data": {
|
||||
"domain": "light",
|
||||
"service": "turn_on",
|
||||
"service_data": {
|
||||
"entity_id": "light.kitchen"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `domain` | string | The domain of the service
|
||||
| `service` | string | The service name
|
||||
| `service_data` | dict | The data to send to the service
|
||||
|
||||
## Fire an event
|
||||
|
||||
Fire an event in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "fire_event",
|
||||
"data": {
|
||||
"event_type": "my_custom_event",
|
||||
"event_data": {
|
||||
"something": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `event_type` | string | Type of the event to fire
|
||||
| `event_data` | string | Date of the event to fire
|
||||
|
||||
## Render templates
|
||||
|
||||
Renders one or more templates and returns the result(s).
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "render_template",
|
||||
"data": {
|
||||
"my_tpl": {
|
||||
"template": "Hello {{ name }}, you are {{ states('person.paulus') }}.",
|
||||
"variables": {
|
||||
"name": "Paulus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`data` must contain a map of `key`: `dictionary`. Results will be returned like `{"my_tpl": "Hello Paulus, you are home"}`. This allows for rendering multiple templates in a single call.
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `template` | string | The template to render
|
||||
| `variables` | Dict | The extra template variables to include.
|
||||
|
||||
## Update registration
|
||||
|
||||
Update your app registration. Use this if the app version changed or any of the other values.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "update_registration",
|
||||
"data": {
|
||||
"app_data": {
|
||||
"push_token": "abcd",
|
||||
"push_url": "https://push.mycool.app/push"
|
||||
},
|
||||
"app_version": "2.0.0",
|
||||
"device_name": "Robbies iPhone",
|
||||
"manufacturer": "Apple, Inc.",
|
||||
"model": "iPhone XR",
|
||||
"os_version": "23.02"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All keys are optional.
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | --- | --
|
||||
| `app_data` | Dict | App data can be used if the app has a supporting component that extends mobile_app functionality or wishes to enable the notification platform.
|
||||
| `app_version` | string | Version of the mobile app.
|
||||
| `device_name` | string | Name of the device running the app.
|
||||
| `manufacturer` | string | The manufacturer of the device running the app.
|
||||
| `model` | string | The model of the device running the app.
|
||||
| `os_version` | string | The OS version of the device running the app.
|
||||
|
||||
## Get zones
|
||||
|
||||
Get all enabled zones.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "get_zones"
|
||||
}
|
||||
```
|
||||
|
||||
## Get config
|
||||
|
||||
Returns a version of `/api/config` with values useful for configuring your app.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "get_config"
|
||||
}
|
||||
```
|
@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Integration Configuration
|
||||
sidebar_label: Configuration
|
||||
id: version-0.103.0-config_entries_config_flow_handler
|
||||
original_id: config_entries_config_flow_handler
|
||||
---
|
||||
|
||||
Integrations can be set up via the user interface by adding support for a config flow to create a config entry. Components that want to support config entries will need to define a Config Flow Handler. This handler will manage the creation of entries from user input, discovery or other sources (like Hass.io).
|
||||
|
||||
Config Flow Handlers control the data that is stored in a config entry. This means that there is no need to validate that the config is correct when Home Assistant starts up. It will also prevent breaking changes, because we will be able to migrate configuration entries to new formats if the version changes.
|
||||
|
||||
When instantiating the handler, Home Assistant will make sure to load all dependencies and install the requirements of the component.
|
||||
|
||||
## Updating the manifest
|
||||
|
||||
You need to update your integrations manifest to inform Home Assistant that your integration has a config flow. This is done by adding `config_flow: true` to your manifest ([docs](creating_integration_manifest.md#config-flow)).
|
||||
|
||||
## Defining your config flow
|
||||
|
||||
Config entries uses the [data flow entry framework](data_entry_flow_index.md) to define their config flows. The config flow needs to be defined in the file `config_flow.py` in your integration folder, extend `homeassistant.config_entries.ConfigFlow` and pass a `domain` key as part of inheriting `ConfigFlow`.
|
||||
|
||||
```python
|
||||
from homeassistant import config_entries
|
||||
from .const import DOMAIN
|
||||
|
||||
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
```
|
||||
|
||||
Once you have updated your manifest and created the `config_flow.py`, you will need to run `python3 -m script.hassfest` (one time only) for Home Assistant to activate the config entry for your integration.
|
||||
|
||||
## Defining steps
|
||||
|
||||
Your config flow will need to define steps of your configuration flow. The docs for [Data Entry Flow](data_entry_flow_index.md) describe the different return values of a step. Here is an example on how to define the `user` step.
|
||||
|
||||
```python
|
||||
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def async_step_user(self, info):
|
||||
if info is not None:
|
||||
# process info
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='user',
|
||||
data_schema=vol.Schema({
|
||||
vol.Required('password'): str
|
||||
})
|
||||
)
|
||||
```
|
||||
|
||||
There are a few step names reserved for system use:
|
||||
|
||||
| Step name | Description |
|
||||
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `user` | Invoked when a user initiates a flow via the user interface. |
|
||||
| `zeroconf` | Invoked if your integration has been discovered via Zeroconf/mDNS as specified [using `zeroconf` in the manifest](creating_integration_manifest.md#zeroconf). |
|
||||
| `homekit` | Invoked if your integration has been discovered via HomeKit as specified [using `homekit` in the manifest](creating_integration_manifest.md#homekit). |
|
||||
| `ssdp` | Invoked if your integration has been discovered via SSDP/uPnP as specified [using `ssdp` in the manifest](creating_integration_manifest.md#ssdp). |
|
||||
| `discovery` | _DEPRECATED_ Invoked if your integration has been discovered by the discovery integration. |
|
||||
|
||||
## Discovery steps
|
||||
|
||||
When an integration is discovered, their respective discovery step is invoked with the discovery information. The step will have to check the following things:
|
||||
|
||||
- Make sure there are no other instances of this config flow in progress of setting up the discovered device. This can happen if there are multiple ways of discovering that a device is on the network.
|
||||
- Make sure that the device is not already set up.
|
||||
- Invoking a discovery step should never result in a finished flow and a config entry. Always confirm with the user.
|
||||
|
||||
## Discoverable integrations that require no authentication
|
||||
|
||||
If your integration is discoverable without requiring any authentication, you'll be able to use the Discoverable Flow that is built-in. This flow offers the following features:
|
||||
|
||||
- Detect if devices/services can be discovered on the network before finishing the config flow.
|
||||
- Support all manifest-based discovery protocols.
|
||||
- Limit to only 1 config entry. It is up to the config entry to discover all available devices.
|
||||
|
||||
To get started, run `python3 -m script.scaffold config_flow_discovery` and follow the instructions. This will create all the boilerplate necessary to configure your integration using discovery.
|
||||
|
||||
## Configuration via OAuth2
|
||||
|
||||
Home Assistant has built-in support for integrations that offer account linking using [the OAuth2 authorization framework](https://tools.ietf.org/html/rfc6749). To be able to leverage this, you will need to structure your Python API library in a way that allows Home Assistant to be responsible for refreshing tokens. See our [API library guide](api_lib_index.md) on how to do this.
|
||||
|
||||
The built-in OAuth2 support works out of the box with locally configured client ID / secret and with the Home Assistant Cloud Account Linking service. This service allows users to link their account with a centrally managed client ID/secret. If you want your integration to be part of this service, reach out to us at [hello@home-assistant.io](mailto:hello@home-assistant.io).
|
||||
|
||||
To get started, run `python3 -m script.scaffold config_flow_oauth2` and follow the instructions. This will create all the boilerplate necessary to configure your integration using OAuth2.
|
||||
|
||||
## 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"
|
||||
}
|
||||
},
|
||||
"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. While developing locally, you will need to run `script/translations_develop` to see changes made to `strings.json` [More info on translating Home Assistant.](internationalization_translation.md)
|
@ -0,0 +1,135 @@
|
||||
---
|
||||
title: Integration Manifest
|
||||
sidebar_label: Manifest
|
||||
id: version-0.103.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"]`.
|
||||
|
||||
### 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, and all data in UPnP device description. The manifest value is a list of matcher dictionaries, your integration is discovered if all items of any of the specified matchers are found in the SSDP/UPnP data. It's up to your config flow to filter out duplicates.
|
||||
|
||||
The following example has one matcher consisting of three items, all of which must match for discovery to happen by this config.
|
||||
|
||||
```json5
|
||||
{
|
||||
"ssdp": [
|
||||
{
|
||||
"st": "roku:ecp",
|
||||
"manufacturer": "Roku",
|
||||
"deviceType": "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.
|
@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Testing your code
|
||||
id: version-0.103.0-development_testing
|
||||
original_id: development_testing
|
||||
---
|
||||
|
||||
As it states in the [Style guidelines section](development_guidelines.md) all code is checked to verify the following:
|
||||
|
||||
- All the unit tests pass
|
||||
- All code passes the checks from the linting tools
|
||||
|
||||
Local testing is done using [Tox](https://tox.readthedocs.io), which has been installed as part of running `script/setup` in the [virtual environment](development_environment.md). To start the tests, activate the virtual environment and simply run the command:
|
||||
|
||||
```bash
|
||||
$ tox
|
||||
```
|
||||
|
||||
It might be required that you install additional packages depending on your distribution/operating system:
|
||||
|
||||
- Fedora: `sudo dnf -y install systemd-devel gcc-c++`
|
||||
- Ubuntu: `sudo apt-get install libudev-dev`
|
||||
|
||||
**Important:** Run `tox` before you create your pull request to avoid annoying fixes.
|
||||
|
||||
Running `tox` will run unit tests against the locally available Python releases, as well as validate the code and document style using `pycodestyle`, `pydocstyle` and `pylint`. You can run tests on only one `tox` target -- just use `-e` to select an environment. For example, `tox -e lint` runs the linters only, and `tox -e py38` runs unit tests only on Python 3.8.
|
||||
|
||||
`tox` uses virtual environments under the hood to create isolated testing environments. The `tox` virtual environments will get out-of-date when requirements change, causing test errors. Run `tox -r` to tell `tox` to recreate the virtual environments.
|
||||
|
||||
macOS users may see an `Error creating virtualenv` when runnng `tox`. If this occurs, install the [tox-venv](https://pypi.org/project/tox-venv/) package using the command `pip install tox-venv` and try again.
|
||||
|
||||
### Adding new dependencies to test environment
|
||||
|
||||
If you are working on tests for an integeration and you need the dependencies available inside the `tox` environment, update the list inside `script/gen_requirements_all.py`. Then run the script and then run `tox -r` to recreate the virtual environments.
|
||||
|
||||
### Running single tests using `tox`
|
||||
|
||||
You can pass arguments via `tox` to `py.test` to be able to run single test suites or test files. Replace `py38` with the Python version that you use.
|
||||
|
||||
```bash
|
||||
# Stop after the first test fails
|
||||
$ tox -e py38 -- tests/test_core.py -x
|
||||
# Run test with specified name
|
||||
$ tox -e py38 -- tests/test_core.py -k test_split_entity_id
|
||||
# Fail a test after it runs for 2 seconds
|
||||
$ tox -e py38 -- tests/test_core.py --timeout 2
|
||||
# Show the 10 slowest tests
|
||||
$ tox -e py38 -- tests/test_core.py --duration=10
|
||||
```
|
||||
|
||||
### Testing outside of Tox
|
||||
|
||||
Running `tox` will invoke the full test suite. Even if you specify which tox target to run, you still run all tests inside that target. That's not very convenient to quickly iterate on your code! To be able to run the specific test suites without `tox`, you'll need to install the test dependencies into your Python environment:
|
||||
|
||||
```bash
|
||||
$ pip3 install -r requirements_test_all.txt -c homeassistant/package_constraints.txt
|
||||
```
|
||||
|
||||
Now that you have all test dependencies installed, you can run tests on individual files:
|
||||
|
||||
```bash
|
||||
$ flake8 homeassistant/core.py
|
||||
$ pylint homeassistant/core.py
|
||||
$ pydocstyle homeassistant/core.py
|
||||
$ py.test tests/test_core.py
|
||||
```
|
||||
|
||||
You can also run linting tests against all changed files, as reported by `git diff upstream/dev... --diff-filter=d --name-only`, using the `lint` script:
|
||||
|
||||
```bash
|
||||
$ script/lint
|
||||
```
|
||||
|
||||
### Preventing linter errors
|
||||
|
||||
Save yourself the hassle of extra commits just to fix style errors by enabling the Flake8 git commit hook. Flake8 will check your code when you try to commit to the repository and block the commit if there are any style errors, which gives you a chance to fix them!
|
||||
|
||||
```bash
|
||||
$ pip3 install flake8 flake8-docstrings
|
||||
$ flake8 --install-hook=git
|
||||
```
|
||||
|
||||
The `flake8-docstrings` extension will check docstrings according to [PEP257](https://www.python.org/dev/peps/pep-0257/) when running Flake8.
|
||||
|
||||
### Notes on PyLint and PEP8 validation
|
||||
|
||||
If you can't avoid a PyLint warning, add a comment to disable the PyLint check for that line with `# pylint: disable=YOUR-ERROR-NAME`. Example of an unavoidable one is if PyLint incorrectly reports that a certain object doesn't have a certain member.
|
552
website/versioned_docs/version-0.103.0/external_api_rest.md
Normal file
552
website/versioned_docs/version-0.103.0/external_api_rest.md
Normal file
@ -0,0 +1,552 @@
|
||||
---
|
||||
title: REST API
|
||||
id: version-0.103.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/<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/<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.<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/<entity_id>
|
||||
|
||||
Updates or creates a state. You can create any state that you want, it does not have to be backed by an entity in Home Assistant.
|
||||
|
||||
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/<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/<domain>/<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."
|
||||
}
|
||||
```
|
232
website/versioned_docs/version-0.103.0/hassio_addon_config.md
Normal file
232
website/versioned_docs/version-0.103.0/hassio_addon_config.md
Normal file
@ -0,0 +1,232 @@
|
||||
---
|
||||
title: Add-On Configuration
|
||||
id: version-0.103.0-hassio_addon_config
|
||||
original_id: hassio_addon_config
|
||||
---
|
||||
|
||||
Each add-on is stored in a folder. The file structure looks like this:
|
||||
|
||||
```text
|
||||
addon_name/
|
||||
apparmor.txt
|
||||
build.json
|
||||
CHANGELOG.md
|
||||
config.json
|
||||
Dockerfile
|
||||
icon.png
|
||||
logo.png
|
||||
README.md
|
||||
run.sh
|
||||
```
|
||||
|
||||
## Add-on script
|
||||
|
||||
As with every Docker container, you will need a script to run when the container is started. A user might run many add-ons, so it is encouraged to try to stick to Bash scripts if you're doing simple things.
|
||||
|
||||
All our Images have also [bashio][bashio] installed. It contains a set of commonly used operations and can be used to be included in add-ons to reduce code duplication across add-ons and therefore making it easier to develop and maintain add-ons.
|
||||
|
||||
When developing your script:
|
||||
|
||||
- `/data` is a volume for persistent storage.
|
||||
- `/data/options.json` contains the user configuration. You can use bashio or `jq` inside your shell script to parse this data.
|
||||
|
||||
```bash
|
||||
CONFIG_PATH=/data/options.json
|
||||
|
||||
TARGET="$(jq --raw-output '.target' $CONFIG_PATH)"
|
||||
```
|
||||
|
||||
So if your `options` contain
|
||||
```json
|
||||
{ "target": "beer" }
|
||||
```
|
||||
then there will be a variable `TARGET` containing `beer` in the environment of your bash file afterwards.
|
||||
|
||||
[bashio]: https://github.com/hassio-addons/bashio
|
||||
|
||||
## Add-on Docker file
|
||||
|
||||
All add-ons are based on latest Alpine Linux. Hass.io will automatically substitute the right base image based on the machine architecture. Add `tzdata` if you need run in a different timezone. `tzdata` Is is already added to our base images.
|
||||
|
||||
```
|
||||
ARG BUILD_FROM
|
||||
FROM $BUILD_FROM
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
|
||||
# Install requirements for add-on
|
||||
RUN apk add --no-cache jq
|
||||
|
||||
# Copy data for add-on
|
||||
COPY run.sh /
|
||||
RUN chmod a+x /run.sh
|
||||
|
||||
CMD [ "/run.sh" ]
|
||||
```
|
||||
|
||||
If you don't use local build on device or our build script, make sure that the Dockerfile have also a set of labels include:
|
||||
```
|
||||
LABEL io.hass.version="VERSION" io.hass.type="addon" io.hass.arch="armhf|aarch64|i386|amd64"
|
||||
```
|
||||
|
||||
It is possible to use own base image with `build.json` or if you do not need support for automatic multi-arch building you can also use a simple docker `FROM`.
|
||||
|
||||
### Build Args
|
||||
|
||||
We support the following build arguments by default:
|
||||
|
||||
| ARG | Description |
|
||||
|-----|-------------|
|
||||
| BUILD_FROM | Hold image for dynamic builds or buildings over our systems.
|
||||
| BUILD_VERSION | Add-on version (read from `config.json`).
|
||||
| BUILD_ARCH | Hold current build arch inside.
|
||||
|
||||
## Add-on config
|
||||
|
||||
The config for an add-on is stored in `config.json`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "xy",
|
||||
"version": "1.2",
|
||||
"slug": "folder",
|
||||
"description": "long description",
|
||||
"arch": ["amd64"],
|
||||
"url": "website with more information about add-on (ie a forum thread for support)",
|
||||
"startup": "application",
|
||||
"boot": "auto",
|
||||
"ports": {
|
||||
"123/tcp": 123
|
||||
},
|
||||
"map": ["config:rw", "ssl"],
|
||||
"options": {},
|
||||
"schema": {},
|
||||
"image": "repo/{arch}-my-custom-addon"
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Required | Description |
|
||||
| --- | ---- | -------- | ----------- |
|
||||
| name | string | yes | Name of the add-on
|
||||
| version | string | yes | Version of the add-on
|
||||
| slug | string | yes | Slug of the add-on
|
||||
| description | string | yes | Description of the add-on
|
||||
| arch | list | yes | List of supported arch: `armhf`, `armv7`, `aarch64`, `amd64`, `i386`.
|
||||
| machine | list | no | Default it support any machine type. You can select that this add-on run only on specific machines.
|
||||
| url | url | no | Homepage of the addon. Here you can explain the add-ons and options.
|
||||
| startup | string | 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`. If host-port is `null`, the mapping is disabled.
|
||||
| ports_description | dict | no | Network ports description mapping. Format is `"container-port/type": "description of this port"`.
|
||||
| host_network | bool | no | If that is True, the add-on run on host network.
|
||||
| host_ipc | bool | no | Default False. Allow to share the IPC namespace with others.
|
||||
| host_dbus | bool | no | Default False. Map Host dbus service into add-on.
|
||||
| host_pid | bool | no | Default False. Allow to run container on host PID namespace. Work only for not protected add-ons.
|
||||
| devices | list | no | Device list to map into the add-on. Format is: `<path_on_host>:<path_in_container>:<cgroup_permissions>`. i.e. `/dev/ttyAMA0:/dev/ttyAMA0:rwm`
|
||||
| udev | bool | no | Default False. Set this True, if your container run a own udev process.
|
||||
| auto_uart | bool | no | Default False. Auto mapping all UART/Serial device from host into add-on.
|
||||
| homeassistant | string | no | Pin a minimum required Home Assistant version for such Add-on. Value is a version string like `0.91.2`.
|
||||
| hassio_role | str | no | Default `default`. Role based access to Hass.io API. Available: `default`, `homeassistant`, `backup`, `manager`, `admin`.
|
||||
| hassio_api | bool | no | This add-on can access to Hass.io REST API. It set the host alias `hassio`.
|
||||
| homeassistant_api | bool | no | This add-on can access to Hass.io Home-Assistant REST API proxy. Use `http://hassio/homeassistant/api`.
|
||||
| docker_api | bool | no | Allow read-oly access to docker API for add-on. Work only for not protected add-ons.
|
||||
| privileged | list | no | Privilege for access to hardware/system. Available access: `NET_ADMIN`, `SYS_ADMIN`, `SYS_RAWIO`, `SYS_TIME`, `SYS_NICE`, `SYS_RESOURCE`, `SYS_PTRACE`, `SYS_MODULE`, `DAC_READ_SEARCH`.
|
||||
| full_access | bool | no | Give full access to hardware like the privileged mode in docker. Work only for not protected add-ons.
|
||||
| apparmor | bool/string | no | Enable or disable AppArmor support. If it is enable, you can also use custom profiles with the name of the profile.
|
||||
| map | list | no | List of maps for additional Hass.io folders. Possible values: `config`, `ssl`, `addons`, `backup`, `share`. Defaults to `ro`, which you can change by adding `:rw` to the end of the name.
|
||||
| environment | dict | no | A dict of environment variable to run add-on.
|
||||
| audio | bool | no | Boolean. Mark this add-on to use internal an audio system. The ALSA configuration for this add-on will be mount automatic.
|
||||
| gpio | bool | no | Boolean. If this is set to True, `/sys/class/gpio` will map into add-on for access to GPIO interface from kernel. Some library need also `/dev/mem` and `SYS_RAWIO` for read/write access to this device. On system with AppArmor enabled, you need disable AppArmor or better for security, provide you own profile for the add-on.
|
||||
| devicetree | bool | no | Boolean. If this is set to True, `/device-tree` will map into add-on.
|
||||
| kernel_modules | bool | no | Map host kernel modules and config into add-on (readonly).
|
||||
| stdin | bool | no | Boolean. If that is enable, you can use the STDIN with Hass.io API.
|
||||
| legacy | bool | no | Boolean. If the docker image have no hass.io labels, you can enable the legacy mode to use the config data.
|
||||
| options | dict | yes | Default options value of the add-on
|
||||
| schema | dict | yes | Schema for options value of the add-on. It can be `False` to disable schema validation and use custom options.
|
||||
| image | string | no | For use with Docker Hub and other container registries.
|
||||
| timeout | integer | no | Default 10 (second). The timeout to wait until the docker is done or will be killed.
|
||||
| tmpfs | string | no | Mount a tmpfs file system in `/tmpfs`. Valide format for this option is : `size=XXXu,uid=N,rw`. Size is mandatory, valid units (`u`) are `k`, `m` and `g` and `XXX` has to be replaced by a number. `uid=N` (with `N` the uid number) and `rw` are optional.
|
||||
| discovery | list | no | A list of services they this Add-on allow to provide for Home Assistant. Currently supported: `mqtt`
|
||||
| services | list | no | A list of services they will be provided or consumed with this Add-on. Format is `service`:`function` and functions are: `provide` (this add-on can provide this service), `want` (this add-on can use this service) or `need` (this add-on need this service to work correctly).
|
||||
| auth_api | bool | no | Allow access to Home Assistent user backend.
|
||||
| ingress | bool | no | Enable the ingress feature for the Add-on
|
||||
| ingress_port | integer | no | Default `8099`. For Add-ons they run on host network, you can use `0` and read the port later on API.
|
||||
| ingress_entry | string | no | Modify the URL entry point from `/`.
|
||||
| panel_icon | string | no | Default: mdi:puzzle. MDI icon for the menu panel integration.
|
||||
| panel_title | string | no | Default add-on name, but can Modify with this options.
|
||||
| panel_admin | bool | no | Default True. Make menu entry only available with admin privileged.
|
||||
| snapshot_exlude | list | no | List of file/path with glob support they are excluded from snapshots.
|
||||
|
||||
|
||||
### 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 / str(min,) / str(,max) / str(min,max)
|
||||
- bool
|
||||
- int / int(min,) / int(,max) / int(min,max)
|
||||
- float / float(min,) / float(,max) / float(min,max)
|
||||
- email
|
||||
- url
|
||||
- port
|
||||
- match(REGEX)
|
||||
- list(val1|val2|...)
|
||||
|
||||
## Add-on extended build
|
||||
|
||||
Additional build options for an add-on is stored in `build.json`. This file will be read from our build systems.
|
||||
You need this only, if you not use the default images or need additionals things.
|
||||
|
||||
```json
|
||||
{
|
||||
"build_from": {
|
||||
"armhf": "mycustom/base-image:latest"
|
||||
},
|
||||
"squash": false,
|
||||
"args": {
|
||||
"my_build_arg": "xy"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Required | Description |
|
||||
| --- | -------- | ----------- |
|
||||
| build_from | no | A dictionary with the hardware architecture as the key and the base Docker image as value.
|
||||
| squash | no | Default `False`. Be carfully with this option, you can not use the image for caching stuff after that!
|
||||
| args | no | Allow to set additional Docker build arguments as a dictionary.
|
||||
|
||||
We provide a set of [Base-Images][hassio-base] which should cover a lot of needs. If you don't want use the Alpine based version or need a specific Image tag, feel free to pin this requirements for you build with `build_from` option.
|
||||
|
||||
[hassio-base]: https://github.com/home-assistant/hassio-base
|
@ -0,0 +1,43 @@
|
||||
---
|
||||
title: Translation
|
||||
id: version-0.103.0-internationalization_translation
|
||||
original_id: internationalization_translation
|
||||
---
|
||||
|
||||
Translations for Home Assistant are managed through [Lokalise](https://lokalise.co/), an online translation management tool. Our translations are split between two projects, a backend project for platform-specific translations, and a frontend project for UI translations. Click the links below to join both projects! Even if your language is completely translated, extra proofreading is a big help! Please feel free to review the existing translations, and vote for alternatives that might be more appropriate.
|
||||
|
||||
- [Join the frontend translation team](https://lokalise.co/signup/3420425759f6d6d241f598.13594006/all/)
|
||||
- [Join the backend translation team](https://lokalise.co/signup/130246255a974bd3b5e8a1.51616605/all/)
|
||||
- [Join the iOS translation team](https://lokalise.co/signup/834452985a05254348aee2.46389241/all/)
|
||||
- [Join the Android translation team](https://lokalise.com/public/145814835dd655bc5ab0d0.36753359/)
|
||||
|
||||
For more information about the translation workflow, please see the [Lokalise translation workflow documents](https://docs.lokalise.co/category/iOzEuQPS53-for-team-leads-and-translators).
|
||||
|
||||
> The translation of the Home Assistant frontend is still a work in progress. More phrases will be available for translation soon.
|
||||
|
||||
## Translation placeholders
|
||||
|
||||
Some translation strings will contain special placeholders that will be replaced later. Placeholders shown in square brackets `[]` are [Lokalise key references](https://docs.lokalise.co/article/KO5SZWLLsy-key-referencing). These are primarily used to link translation strings that will be duplicated. Different languages may not have the same duplicates as English, and are welcome to link duplicate translations that are not linked in English. Placeholders shown in curly brackets `{}` are [translation arguments](https://formatjs.io/guides/message-syntax/) that will be replaced with a live value when Home Assistant is running. Any translation argument placeholders present in the original string must be included in the translated string. These may include special syntax for defining plurals or other replacement rules. The linked format.js guide explains the syntax for adding plural definitions and other rules.
|
||||
|
||||
## Rules
|
||||
1. Only native speakers should submit translations.
|
||||
2. Stick to [Material Design guidelines](https://material.io/guidelines/style/writing.html).
|
||||
3. Don't translate or change proper nouns like `Home Assistant`, `Hass.io` or `Hue`.
|
||||
4. For a region specific translation, keys that will be the same as the base translation should be filled with `[VOID]`. These will be replaced during our translation build process.
|
||||
5. Translations under the `state_badge` keys will be used for the notification badge display. These translations should be short enough to fit in the badge label without overflowing. This can be tested in the Home Assistant UI either by editing the label text with your browsers development tools, or by using the States <img src='/img/dev-tools/states-icon.png' alt='states dev tool icon' class="inline" width="38" /> developer tool in the Home Assistant UI. In the UI, enter a new entity ID (`device_tracker.test`), and enter the text you want to test in state.
|
||||
6. If text will be duplicated across different translation keys, make use of the Lokalise key reference feature where possible. The base translation provides examples of this underneath the `states` translations. Please see the [Lokalise key referencing](https://docs.lokalise.co/article/KO5SZWLLsy-key-referencing) documentation for more details.
|
||||
|
||||
## Adding a new language
|
||||
If your language is not listed you can request it at [GitHub](https://github.com/home-assistant/home-assistant-polymer/issues/new). Please provide both the English name and the native name for your language. For example:
|
||||
```
|
||||
English Name: German
|
||||
Native Name: Deutsch
|
||||
```
|
||||
|
||||
> Region specific translations (`en-US`, `fr-CA`) will only be included if translations for that region need to differ from the base language translation.
|
||||
|
||||
### Maintainer steps to add a new language
|
||||
1. Language tags have to follow [BCP 47](https://tools.ietf.org/html/bcp47). A list of most language tags can be found here: [IANA sutbtag registry](http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). Examples: `fr`, `fr-CA`, `zh-Hans`. Only include the country code if country specific overrides are being included, and the base language is already translated.
|
||||
2. Add the language tag and native name in `src/translations/translationMetadata.json`. Examples: "Français", "Français (CA)"
|
||||
3. Add the new language in Lokalize.
|
||||
Note: Sometimes you have to change the tag in Lokalise (Language -> Language settings -> custom ISO code).
|
47
website/versioned_docs/version-0.103.0/maintenance.md
Normal file
47
website/versioned_docs/version-0.103.0/maintenance.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Maintenance
|
||||
id: version-0.103.0-maintenance
|
||||
original_id: maintenance
|
||||
---
|
||||
|
||||
This page documents a couple of points for maintaining the Home Assistant code. Most of the tasks don't need to be performed on a regular base thus the steps, used tools, or details are preserved here.
|
||||
|
||||
## Source code
|
||||
|
||||
### Line separator
|
||||
|
||||
People are using various operating systems to develop components and platforms for Home Assistant. This could lead to different line endings on file. We prefer `LN`. Especially Microsoft Windows tools tend to use `CRLF`.
|
||||
|
||||
```bash
|
||||
$ find homeassistant -name "*.py" -exec file {} \; | grep BOM
|
||||
$ find homeassistant -name "*.py" -exec file {} \; | grep CRLF
|
||||
```
|
||||
|
||||
To fix the line separator, use `dos2unix` or `sed`.
|
||||
|
||||
```bash
|
||||
$ dos2unix homeassistant/components/notify/kodi.py
|
||||
```
|
||||
|
||||
### File permissions
|
||||
|
||||
Most files don't need to the be executable. `0644` is fine.
|
||||
|
||||
### Dependencies
|
||||
|
||||
A lot of components and platforms depends on third-party Python modules. The dependencies which are stored in the `requirements_all.txt` files can be tracked with [`pur`](https://pypi.org/project/pur/) or [`pip-upgrader`](https://github.com/simion/pip-upgrader).
|
||||
|
||||
If you update the requirements of a component/platform by updating `manifest.json`, run the provided script to update the `requirements_*.txt` file(s).
|
||||
|
||||
```bash
|
||||
$ script/gen_requirements_all.py
|
||||
```
|
||||
|
||||
Start a test run of Home Assistant. If that was successful, include all files in a Pull Request. Add a short summary of the changes, a sample configuration entry, details about the tests you performed to ensure the update works, and other useful information to the description.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
- Merge `current` into `next` on a regular base.
|
||||
- Optimize the images.
|
||||
|
@ -1,4 +1,5 @@
|
||||
[
|
||||
"0.103.0",
|
||||
"0.102.0",
|
||||
"0.101.0",
|
||||
"0.100.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user