mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-07-27 19:26:29 +00:00
release 94
This commit is contained in:
parent
f1465e617e
commit
7ee774329d
@ -1471,6 +1471,56 @@
|
||||
},
|
||||
"version-0.93.0/version-0.93.0-hassio_debugging": {
|
||||
"title": "Debugging Hass.io"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-config_entries_config_flow_handler": {
|
||||
"title": "Integration Configuration",
|
||||
"sidebar_label": "Configuration"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-creating_component_code_review": {
|
||||
"title": "Checklist for creating a component",
|
||||
"sidebar_label": "Component Checklist"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-creating_component_index": {
|
||||
"title": "Creating a Minimal Component",
|
||||
"sidebar_label": "Minimal Component"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-creating_integration_manifest": {
|
||||
"title": "Integration Manifest",
|
||||
"sidebar_label": "Manifest"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-creating_platform_code_review": {
|
||||
"title": "Checklist for creating a platform",
|
||||
"sidebar_label": "Platform Checklist"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-data_entry_flow_index": {
|
||||
"title": "Data Entry Flow",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-device_registry_index": {
|
||||
"title": "Device Registry",
|
||||
"sidebar_label": "Introduction"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-entity_cover": {
|
||||
"title": "Cover Entity",
|
||||
"sidebar_label": "Cover"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-external_api_rest": {
|
||||
"title": "REST API"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-frontend_creating_custom_panels": {
|
||||
"title": "Creating custom panels"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-frontend_creating_custom_ui": {
|
||||
"title": "Creating custom UI"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-frontend_external_bus": {
|
||||
"title": "External Bus"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-hassio_addon_communication": {
|
||||
"title": "Add-On Communication"
|
||||
},
|
||||
"version-0.94.0/version-0.94.0-hassio_debugging": {
|
||||
"title": "Debugging Hass.io"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
|
@ -0,0 +1,142 @@
|
||||
---
|
||||
title: Integration Configuration
|
||||
sidebar_label: Configuration
|
||||
id: version-0.94.0-config_entries_config_flow_handler
|
||||
original_id: config_entries_config_flow_handler
|
||||
---
|
||||
|
||||
> This option is currently only available for built-in components.
|
||||
|
||||
Integrations can be set up via the user interface by adding support for a config config to create a config entry. Components that want to support config entries will need to define a Config Flow Handler. This handler will manage the creation of entries from user input, discovery or other sources (like Hass.io).
|
||||
|
||||
Config Flow Handlers control the data that is stored in a config entry. This means that there is no need to validate that the config is correct when Home Assistant starts up. It will also prevent breaking changes, because we will be able to migrate configuration entries to new formats if the version changes.
|
||||
|
||||
When instantiating the handler, Home Assistant will make sure to load all dependencies and install the requirements of the component.
|
||||
|
||||
## Updating the manifest
|
||||
|
||||
You need to update your integrations manifest to inform Home Assistant that your integration has a config flow. This is done by adding `config_flow: true` to your manifest ([docs](creating_integration_manifest.md#config-flow)).
|
||||
|
||||
## Defining your config flow
|
||||
|
||||
Config entries uses the [data flow entry framework](data_entry_flow_index.md) to define their config flows. The config flow needs to be defined in the file `config_flow.py` in your integration folder.
|
||||
|
||||
To define it, extend the ConfigFlow base class from the config entries module, and decorate it with the `HANDLERS.register` decorator:
|
||||
|
||||
```python
|
||||
from homeassistant import config_entries
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class ExampleConfigFlow(config_entries.ConfigFlow):
|
||||
```
|
||||
|
||||
## Defining steps
|
||||
|
||||
Your config flow will need to define steps of your configuration flow. The docs for [Data Entry Flow](data_entry_flow_index.md) describe the different return values of a step. Here is an example on how to define the `user` step.
|
||||
|
||||
```python
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_user(self, info):
|
||||
if info is not None:
|
||||
# process info
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='init',
|
||||
data_schema=vol.Schema({
|
||||
vol.Required('password'): str
|
||||
})
|
||||
)
|
||||
```
|
||||
|
||||
There are a few step names reserved for system use:
|
||||
|
||||
| Step name | Description |
|
||||
| --------- | ----------- |
|
||||
| `user` | Invoked when a user initiates a flow via the user interface.
|
||||
| `zeroconf` | Invoked if your integration has been discovered via Zeroconf/mDNS as specified [using `zeroconf` in the manifest](creating_integration_manifest.md#zeroconf).
|
||||
| `homekit` | Invoked if your integration has been discovered via HomeKit as specified [using `homekit` in the manifest](creating_integration_manifest.md#homekit).
|
||||
| `ssdp` | Invoked if your integration has been discovered via SSDP/uPnP as specified [using `ssdp` in the manifest](creating_integration_manifest.md#ssdp).
|
||||
| `discovery` | _DEPRECATED_ Invoked if your integration has been discovered by the discovery integration.
|
||||
|
||||
## Discovery steps
|
||||
|
||||
When an integration is discovered, their respective discovery step is invoked with the discovery information. The step will have to check the following things:
|
||||
|
||||
- Make sure there are no other instances of this config flow in progress of setting up the discovered device. This can happen if there are multiple ways of discovering that a device is on the network.
|
||||
- Make sure that the device is not already set up.
|
||||
- Invoking a discovery step should never result in a finished flow and a config entry. Always confirm with the user.
|
||||
|
||||
## Discoverable integrations that require no authentication
|
||||
|
||||
If your integration is discoverable without requiring any authentication, you'll be able to use the Discoverable Flow that is built-in. This flow offers the following features:
|
||||
|
||||
- Detect if devices/services can be discovered on the network before finishing the config flow.
|
||||
- Support all manifest-based discovery protocols.
|
||||
- Limit to only 1 config entry. It is up to the config entry to discover all available devices.
|
||||
|
||||
```python
|
||||
"""Config flow for LIFX."""
|
||||
from homeassistant.helpers import config_entry_flow
|
||||
from homeassistant import config_entries
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def _async_has_devices(hass):
|
||||
"""Return if there are devices that can be discovered."""
|
||||
import aiolifx
|
||||
|
||||
lifx_ip_addresses = await aiolifx.LifxScan(hass.loop).scan()
|
||||
return len(lifx_ip_addresses) > 0
|
||||
|
||||
|
||||
config_entry_flow.register_discovery_flow(
|
||||
# Domain of your integration
|
||||
DOMAIN,
|
||||
# Title of the created config entry
|
||||
'LIFX',
|
||||
# async method that returns a boolean if devices/services are found
|
||||
_async_has_devices,
|
||||
# Connection class of the integration
|
||||
config_entries.CONN_CLASS_LOCAL_POLL
|
||||
)
|
||||
```
|
||||
|
||||
## Translations
|
||||
|
||||
Translations for the config flow handlers are defined under the `config` key in the component translation file `strings.json`. Example of the Hue component:
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"title": "Philips Hue Bridge",
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Pick Hue bridge",
|
||||
"data": {
|
||||
"host": "Host"
|
||||
}
|
||||
},
|
||||
"link": {
|
||||
"title": "Link Hub",
|
||||
"description": "Press the button on the bridge to register Philips Hue with Home Assistant.\n\n"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"register_failed": "Failed to register, please try again",
|
||||
"linking": "Unknown linking error occurred."
|
||||
},
|
||||
"abort": {
|
||||
"discover_timeout": "Unable to discover Hue bridges",
|
||||
"no_bridges": "No Philips Hue bridges discovered",
|
||||
"all_configured": "All Philips Hue bridges are already configured",
|
||||
"unknown": "Unknown error occurred",
|
||||
"cannot_connect": "Unable to connect to the bridge",
|
||||
"already_configured": "Bridge is already configured"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the translations are merged into Home Assistant, they will be automatically uploaded to [Lokalise](https://lokalise.co/) where the translation team will help to translate them in other languages. [More info on translating Home Assistant.](internationalization_translation.md)
|
@ -0,0 +1,69 @@
|
||||
---
|
||||
title: Checklist for creating a component
|
||||
sidebar_label: Component Checklist
|
||||
id: version-0.94.0-creating_component_code_review
|
||||
original_id: creating_component_code_review
|
||||
---
|
||||
|
||||
A checklist of things to do when you're adding a new component.
|
||||
|
||||
> Not all existing code follow the requirements in this checklist. This cannot be used as a reason to not follow them!
|
||||
|
||||
### 0. Common
|
||||
|
||||
1. Follow our [Style guidelines](development_guidelines.md)
|
||||
2. Use existing constants from [`const.py`](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/const.py)
|
||||
* Only add new constants to `const.py` if they are widely used. Otherwise keep them on components level
|
||||
|
||||
### 1. Requirements
|
||||
|
||||
1. Requirements have been added to [`manifest.json`](creating_integration_manifest.md). The `REQUIREMENTS` constant is deprecated.
|
||||
2. Requirement version should be pinned: `"requirements": ['phue==0.8.1']`
|
||||
3. We no longer want requirements hosted on GitHub. Please upload to PyPi.
|
||||
|
||||
### 2. Configuration
|
||||
|
||||
1. Voluptuous schema present for [configuration validation](development_validation.md)
|
||||
2. Default parameters specified in voluptuous schema, not in `setup(…)`
|
||||
3. Schema using as many generic config keys as possible from `homeassistant.const`
|
||||
4. If your component has platforms, define a `PLATFORM_SCHEMA` instead of a `CONFIG_SCHEMA`.
|
||||
5. If using a `PLATFORM_SCHEMA` to be used with `EntityComponent`, import base from `homeassistant.helpers.config_validation`
|
||||
6. Never depend on users adding things to `customize` to configure behavior inside your component.
|
||||
|
||||
### 3. Component/platform communication
|
||||
|
||||
1. You can share data with your platforms by leveraging `hass.data[DOMAIN]`.
|
||||
2. If the component fetches data that causes its related platform entities to update, you can notify them using the dispatcher code in `homeassistant.helpers.dispatcher`.
|
||||
|
||||
### 4. Communication with devices/services
|
||||
|
||||
1. All API specific code has to be part of a third party library hosted on PyPi. Home Assistant should only interact with objects and not make direct calls to the API.
|
||||
|
||||
```python
|
||||
# bad
|
||||
status = requests.get(url('/status'))
|
||||
|
||||
# good
|
||||
from phue import Bridge
|
||||
bridge = Bridge(...)
|
||||
status = bridge.status()
|
||||
```
|
||||
|
||||
[Tutorial on publishing your own PyPI package](https://jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/)
|
||||
|
||||
### 5. Make your pull request as small as possible
|
||||
|
||||
Keep a new integration to the minimum functionality needed for someone to get value out of the integration. This allows reviewers to sign off on smaller chunks of code one at a time, and lets us get your new integration/features in sooner. **Pull requests containing large code dumps will not be a priority for review and may be closed.**
|
||||
|
||||
- Limit to a single platform
|
||||
- Do not add features not needed to directly support the single platform (such as custom services)
|
||||
- Do not mix clean-ups and new features in a single pull request.
|
||||
- Do not solve several issues in a single pull request.
|
||||
- Do not submit pull requests that depend on other work which is still unmerged.
|
||||
|
||||
### 6. Event names
|
||||
Prefix component event names with the domain name. For example, use `netatmo_person` instead of `person` for the `netatmo` component.
|
||||
|
||||
### 7. Tests
|
||||
|
||||
Strongly consider adding tests for your component to minimize future regressions.
|
@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Creating a Minimal Component
|
||||
sidebar_label: Minimal Component
|
||||
id: version-0.94.0-creating_component_index
|
||||
original_id: creating_component_index
|
||||
---
|
||||
|
||||
Alright, you learned about the [manifest](creating_integration_manifest.md), so it's time to write your first code for your integration. AWESOME. Don't worry, we've tried hard to keep it as easy as possible.
|
||||
|
||||
More extensive examples of integrations are available from [our example repository](https://github.com/home-assistant/example-custom-config/tree/master/custom_components/).
|
||||
|
||||
## The code
|
||||
|
||||
Each component needs to have 2 basic parts: it needs to define a `DOMAIN` constant that contains the domain of the integration. The second part is that it needs to define a setup method that returns a boolean if the set up was successful. So let's take a look at how this looks:
|
||||
|
||||
```python
|
||||
DOMAIN = 'hello_state'
|
||||
|
||||
def setup(hass, config):
|
||||
hass.states.set('hello_state.world', 'Paulus')
|
||||
|
||||
# Return boolean to indicate that initialization was successful.
|
||||
return True
|
||||
```
|
||||
|
||||
And if you prefer an async component:
|
||||
|
||||
```python
|
||||
DOMAIN = 'hello_state'
|
||||
|
||||
async def async_setup(hass, config):
|
||||
hass.states.async_set('hello_state.world', 'Paulus')
|
||||
|
||||
# Return boolean to indicate that initialization was successful.
|
||||
return True
|
||||
```
|
||||
|
||||
That's it! If you load this, you will see a new state in the state machine.
|
||||
|
||||
To load this, add `hello_state:` to your `configuration.yaml` file and create a file `<config_dir>/custom_components/hello_state/__init__.py` with one of the two codeblocks above to test it locally.
|
@ -0,0 +1,139 @@
|
||||
---
|
||||
title: Integration Manifest
|
||||
sidebar_label: Manifest
|
||||
id: version-0.94.0-creating_integration_manifest
|
||||
original_id: creating_integration_manifest
|
||||
---
|
||||
|
||||
Since 0.92.0, every integration has a manifest file to specify basic information about an integration. This file is stored as `manifest.json` in your integration directory. It is required to add such a file, except for custom components.
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": "hue",
|
||||
"name": "Philips Hue",
|
||||
"documentation": "https://www.home-assistant.io/components/hue",
|
||||
"dependencies": ["mqtt"],
|
||||
"codeowners": ["@balloob"],
|
||||
"requirements": ["aiohue==1.9.1"]
|
||||
}
|
||||
```
|
||||
|
||||
Or a minimal example that you can copy into your project:
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": "your_domain_name",
|
||||
"name": "Your Integration",
|
||||
"documentation": "https://www.example.com",
|
||||
"dependencies": [],
|
||||
"codeowners": [],
|
||||
"requirements": []
|
||||
}
|
||||
```
|
||||
|
||||
## Domain
|
||||
|
||||
The domain is a short name consisting of characters and underscores. This domain has to be unique and cannot be changed. Example of the domain for the mobile app integration: `mobile_app`.
|
||||
|
||||
## Name
|
||||
|
||||
The name of the integration.
|
||||
|
||||
## Documentation
|
||||
|
||||
The website containing documentation on how to use your integration. If this integration is being submitted for inclusion in Home Assistant, it should be `https://www.home-assistant.io/components/<domain>`
|
||||
|
||||
## Dependencies
|
||||
|
||||
Dependencies are other Home Assistant integrations that you want Home Assistant to set up successfully prior to the integration being loaded. This can be necessary in case you want to offer functionality from that other integration, like using webhooks or an MQTT connection.
|
||||
|
||||
## Code Owners
|
||||
|
||||
GitHub usernames or team names of people that are responsible for this integration. You should add at least your GitHub username here, as well as anyone who helped you to write code that is being included.
|
||||
|
||||
## Config Flow
|
||||
|
||||
Specify the `config_flow` key if your integration has a config flow to create a config entry. When specified, the file `config_flow.py` needs to exist in your integration.
|
||||
|
||||
```json5
|
||||
{
|
||||
"config_flow": true
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
Requirements are Python libraries or modules that you would normally install using `pip` for your component. Home Assistant will try to install the requirements into the `deps` subdirectory of the Home Assistant [configuration directory](https://www.home-assistant.io/docs/configuration/) if you are not using a `venv` or in something like `path/to/venv/lib/python3.6/site-packages` if you are running in a virtual environment. This will make sure that all requirements are present at startup. If steps fail, like missing packages for the compilation of a module or other install errors, the component will fail to load.
|
||||
|
||||
Requirements is an array of strings. Each entry is a `pip` compatible string. For example, the media player Cast platform depends on the Python package PyChromecast v3.2.0: `["pychromecast==3.2.0"]`.
|
||||
|
||||
> Because of how Home Assistant installs requirements on demand, actual Python imports of your requirements should be done inside functions instead of at the root level of your Python files.
|
||||
|
||||
### Custom requirements during development & testing
|
||||
|
||||
During the development of a component, it can be useful to test against different versions of a requirement. This can be done in two steps, using `pychromecast` as an example:
|
||||
|
||||
```bash
|
||||
pip install pychromecast==3.2.0 --target ~/.homeassistant/deps
|
||||
hass --skip-pip
|
||||
```
|
||||
|
||||
This will use the specified version, and prevent Home Assistant from trying to override it with what is specified in `requirements`.
|
||||
|
||||
If you need to make changes to a requirement to support your component, it's also possible to install a development version of the requirement using `pip install -e`:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/balloob/pychromecast.git
|
||||
pip install -e ./pychromecast
|
||||
hass --skip-pip
|
||||
```
|
||||
|
||||
## Zeroconf
|
||||
|
||||
If your integration supports discovery via [Zeroconf](https://en.wikipedia.org/wiki/Zero-configuration_networking), you can add the type to your manifest. If the user has the `zeroconf` integration loaded, it will load the `zeroconf` step of your integration's config flow when it is discovered.
|
||||
|
||||
```json5
|
||||
{
|
||||
"zeroconf": ["_googlecast._tcp.local."]
|
||||
}
|
||||
```
|
||||
|
||||
## SSDP
|
||||
|
||||
If your integration supports discovery via [SSDP](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol), you can add the type to your manifest. If the user has the `ssdp` integration loaded, it will load the `ssdp` step of your integration's config flow when it is discovered. We support SSDP discovery by ST, manufacturer and device type. Your integration is discovered if any of the specified info is found. It's up to your config flow to filter out duplicates.
|
||||
|
||||
```json5
|
||||
{
|
||||
"ssdp": {
|
||||
"st": [
|
||||
"roku:ecp"
|
||||
],
|
||||
"manufacturer": [
|
||||
"Roku"
|
||||
],
|
||||
"device_type": [
|
||||
"urn:roku-com:device:player:1-0"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## HomeKit
|
||||
|
||||
If your integration supports discovery via HomeKit, you can add the supported model names to your manifest. If the user has the `zeroconf` integration loaded, it will load the `homekit` step of your integration's config flow when it is discovered.
|
||||
|
||||
HomeKit discovery works by testing if the discovered modelname starts with any of the model names specified in the manifest.json.
|
||||
|
||||
```json5
|
||||
{
|
||||
"homekit": {
|
||||
"models": [
|
||||
"LIFX"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Discovery via HomeKit does not mean that you have to talk the HomeKit protocol to communicate with your device. You can communicate with the device however you see fit.
|
||||
|
||||
When a discovery info is routed to your integration because of this entry in your manifest, the discovery info is no longer routed to integrations that listen to the HomeKit zeroconf type.
|
@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Checklist for creating a platform
|
||||
sidebar_label: Platform Checklist
|
||||
id: version-0.94.0-creating_platform_code_review
|
||||
original_id: creating_platform_code_review
|
||||
---
|
||||
|
||||
A checklist of things to do when you're adding a new platform.
|
||||
|
||||
> Not all existing platforms follow the requirements in this checklist. This cannot be used as a reason to not follow them!
|
||||
|
||||
### 0. Common
|
||||
|
||||
1. Follow our [Style guidelines](development_guidelines.md)
|
||||
2. Use existing constants from [`const.py`](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/const.py)
|
||||
* Only add new constants to `const.py` if they are widely used. Otherwise keep them on platform level
|
||||
* Use `CONF_MONITORED_CONDITIONS` instead of `CONF_MONITORED_VARIABLES`
|
||||
|
||||
### 1. Requirements
|
||||
|
||||
1. Requirements have been added to [`manifest.json`](creating_integration_manifest.md). The `REQUIREMENTS` constant is deprecated.
|
||||
2. Requirement version should be pinned: `"requirements": ['phue==0.8.1']`
|
||||
3. We no longer want requirements hosted on GitHub. Please upload to PyPi.
|
||||
|
||||
### 2. Configuration
|
||||
|
||||
1. If the platform can be set up directly, add a voluptuous schema for [configuration validation](development_validation.md)
|
||||
2. Voluptuous schema extends schema from component<br>(e.g., `hue.light.PLATFORM_SCHEMA` extends `light.PLATFORM_SCHEMA`)
|
||||
3. Default parameters specified in voluptuous schema, not in `setup_platform(...)`
|
||||
4. Your `PLATFORM_SCHEMA` should use as many generic config keys as possible from `homeassistant.const`
|
||||
5. Never depend on users adding things to `customize` to configure behavior inside your platform.
|
||||
|
||||
```python
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_FILENAME, CONF_HOST
|
||||
from homeassistant.components.light import PLATFORM_SCHEMA
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
CONF_ALLOW_UNREACHABLE = 'allow_unreachable'
|
||||
DEFAULT_UNREACHABLE = False
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_ALLOW_UNREACHABLE,
|
||||
default=DEFAULT_UNREACHABLE): cv.boolean,
|
||||
vol.Optional(CONF_FILENAME): cv.string,
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Setup Platform
|
||||
|
||||
1. Verify that the passed in configuration (user/pass/host etc.) works.
|
||||
2. Group your calls to `add_devices` if possible.
|
||||
3. If the platform adds extra services, the format should be `<domain of your integration>.<service name>`. So if your integration's domain is "awesome_sauce" and you are making a light platform, you would register services under the `awesome_sauce` domain. Make sure that your services [verify permissions](auth_permissions.md#checking-permissions).
|
||||
|
||||
### 5. Entity
|
||||
|
||||
1. Extend the entity from the integration you're building a platform for.
|
||||
|
||||
```python
|
||||
from homeassistant.components.light import Light
|
||||
|
||||
class HueLight(Light):
|
||||
...
|
||||
```
|
||||
|
||||
2. Avoid passing in `hass` as a parameter to the entity. When the entity has been added to Home Assistant, `hass` will be set on the entity when the entity is added to Home Assistant. This means you can access `hass` as `self.hass` inside the entity.
|
||||
3. Do not call `update()` in constructor, use `add_entities(devices, True)` instead.
|
||||
4. Do not do any I/O inside properties. Cache values inside `update()` instead.
|
||||
5. When dealing with time, state and/or attributes should not contain relative time since something happened. Instead, it should store UTC timestamps.
|
||||
6. Leverage the [entity lifecycle callbacks](entity_index.md#lifecycle-hooks) to attach event listeners or clean up connections.
|
||||
|
||||
### 4. Communication with devices/services
|
||||
|
||||
1. All API specific code has to be part of a third party library hosted on PyPi. Home Assistant should only interact with objects and not make direct calls to the API.
|
||||
|
||||
```python
|
||||
# bad
|
||||
status = requests.get(url('/status'))
|
||||
|
||||
# good
|
||||
from phue import Bridge
|
||||
bridge = Bridge(...)
|
||||
status = bridge.status()
|
||||
```
|
||||
|
||||
[Tutorial on publishing your own PyPI package](https://jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/)
|
284
website/versioned_docs/version-0.94.0/data_entry_flow_index.md
Normal file
284
website/versioned_docs/version-0.94.0/data_entry_flow_index.md
Normal file
@ -0,0 +1,284 @@
|
||||
---
|
||||
title: Data Entry Flow
|
||||
sidebar_label: Introduction
|
||||
id: version-0.94.0-data_entry_flow_index
|
||||
original_id: data_entry_flow_index
|
||||
---
|
||||
|
||||
Data Entry Flow is a data entry framework that is part of Home Assistant. Data entry is done via data entry flows. A flow can represent a simple login form or a multi-step setup wizard for a component. A Flow Manager is managing all flows that are in progress and handles creation of new flows.
|
||||
|
||||
Data Entry Flow is being used in Home Assistant to create config entries.
|
||||
|
||||
## Flow Manager
|
||||
|
||||
This is the class that manages the flows that are in progress. When instantiating one, you pass in two async callbacks:
|
||||
|
||||
```python
|
||||
async def async_create_flow(handler, context=context, data=data)
|
||||
```
|
||||
|
||||
The manager delegates instantiating of config flow handlers to this async callback. This allows the parent of the manager to define their own way of finding handlers and preparing a handler for instantiation. For example, in the case of the config entry manager, it will make sure that the dependencies and requirements are setup.
|
||||
|
||||
```python
|
||||
async def async_finish_flow(flow, result)
|
||||
```
|
||||
|
||||
This async callback is called when a flow is finished or aborted. i.e. `result['type'] in [RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_ABORT]`. The callback function can modify result and return it back, if the result type changed to `RESULT_TYPE_FORM`, the flow will continue running, display another form.
|
||||
|
||||
If the result type is `RESULT_TYPE_FORM`, the result should like:
|
||||
```python
|
||||
{
|
||||
# The result type of the flow
|
||||
'type': RESULT_TYPE_FORM,
|
||||
# the id of the flow
|
||||
'flow_id': 'abcdfgh1234,
|
||||
# handler name
|
||||
'handler': 'hue',
|
||||
# name of the step, flow.async_step_[step_id] will be called when form submitted
|
||||
'step_id': 'init',
|
||||
# a voluptuous schema to build and validate user input
|
||||
'data_schema': vol.Schema(),
|
||||
# an errors dict, None if no errors
|
||||
'errors': errors,
|
||||
# a detail information about the step
|
||||
'description_placeholders': description_placeholders,
|
||||
}
|
||||
```
|
||||
|
||||
If the result type is `RESULT_TYPE_CREATE_ENTRY`, the result should like:
|
||||
```python
|
||||
{
|
||||
# Data schema version of the entry
|
||||
'version': 2,
|
||||
# The result type of the flow
|
||||
'type': RESULT_TYPE_CREATE_ENTRY,
|
||||
# the id of the flow
|
||||
'flow_id': 'abcdfgh1234,
|
||||
# handler name
|
||||
'handler': 'hue',
|
||||
# title and data as created by the handler
|
||||
'title': 'Some title',
|
||||
'result': {
|
||||
'some': 'data'
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If the result type is `RESULT_TYPE_ABORT`, the result should like:
|
||||
```python
|
||||
{
|
||||
# The result type of the flow
|
||||
'type': RESULT_TYPE_ABORT,
|
||||
# the id of the flow
|
||||
'flow_id': 'abcdfgh1234,
|
||||
# handler name
|
||||
'handler': 'hue',
|
||||
# the abort reason
|
||||
'reason': 'already_configured',
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Flow Handler
|
||||
|
||||
Flow handlers will handle a single flow. A flow contains one or more steps. When a flow is instantiated, the `FlowHandler.init_step` step will be called. Each step has three different possible results: "Show Form", "Abort" and "Create Entry".
|
||||
|
||||
At a minimum, each flow handler will have to define a version number and a step. This doens't have to be `init`, as `async_create_flow` can assign `init_step` depends on diffreent workflow, for example in configuration, `context.source` will be use as `init_step`.
|
||||
|
||||
The bare minimum config flow:
|
||||
|
||||
```python
|
||||
from homeassistant import data_entry_flow
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
# The schema version of the entries that it creates
|
||||
# Home Assistant will call your migrate method if the version changes
|
||||
# (this is not implemented yet)
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
# Do something
|
||||
```
|
||||
|
||||
### Show Form
|
||||
|
||||
This result type will show a form to the user to fill in. You define the current step, the schema of the data (using voluptuous) and optionally a dictionary of errors. Title and description of the step will be provided via the translation file. Where this is defined depends on the context of the data entry flow.
|
||||
|
||||
```python
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
# Use OrderedDict to guarantee order of the form shown to the user
|
||||
data_schema = OrderedDict()
|
||||
data_schema[vol.Required('username')] = str
|
||||
data_schema[vol.Required('password')] = str
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='init',
|
||||
data_schema=vol.Schema(data_schema)
|
||||
)
|
||||
```
|
||||
|
||||
After the user has filled in the form, the step method will be called again and the user input is passed in. Your step will only be called if the user input passes your data schema. When the user passes in data, you will have to do extra validation of the data. For example, you can verify that the passed in username and password are valid.
|
||||
|
||||
If something is wrong, you can return a dictionary with errors. Each key in the error dictionary refers to a field name that contains the error. Use the key `base` if you want to show an error unrelated to a specific field. The specified errors need to refer to a key in a translation file.
|
||||
|
||||
```python
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
# Validate user input
|
||||
valid = await is_valid(user_input)
|
||||
if valid:
|
||||
# See next section on create entry usage
|
||||
return self.create_entry(...)
|
||||
|
||||
errors['base'] = 'auth_error'
|
||||
|
||||
# Use OrderedDict to guarantee order of the form shown to the user
|
||||
data_schema = OrderedDict()
|
||||
data_schema[vol.Required('username')] = str
|
||||
data_schema[vol.Required('password')] = str
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='init',
|
||||
data_schema=vol.Schema(data_schema),
|
||||
errors=errors
|
||||
)
|
||||
```
|
||||
|
||||
#### Multi-step flows
|
||||
|
||||
If the user input passes validation, you can again return one of the three return values. If you want to navigate the user to the next step, return the return value of that step:
|
||||
|
||||
```python
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
# Validate user input
|
||||
valid = await is_valid(user_input)
|
||||
if valid:
|
||||
# Store info to use in next step
|
||||
self.init_info = user_input
|
||||
# Return the form of the next step
|
||||
return await self.async_step_account()
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
### Create Entry
|
||||
|
||||
When the result is "Create Entry", an entry will be created and passed to the parent of the flow manager. A success message is shown to the user and the flow is finished. You create an entry by passing a title and data. The title can be used in the UI to indicate to the user which entry it is. Data can be any data type, as long as it is JSON serializable.
|
||||
|
||||
```python
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
return self.create_entry(
|
||||
title='Title of the entry',
|
||||
data={
|
||||
'something_special': user_input['username']
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Abort
|
||||
|
||||
When a flow cannot be finished, you need to abort it. This will finish the flow and inform the user that the flow has finished. Reasons for a flow to not be able to finish can be that a device is already configured or not compatible with Home Assistant.
|
||||
|
||||
```python
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
return self.async_abort(
|
||||
reason='not_supported'
|
||||
)
|
||||
```
|
||||
|
||||
### External Step & External Step Done
|
||||
|
||||
It is possible that a user needs to finish a config flow by doing actions on an external website. For example, setting up an integration by being redirected to an external webpage. This is commonly used by integrations that use OAuth2 to authorize a user.
|
||||
|
||||
_The example is about config entries, but works with other parts that use data entry flows too._
|
||||
|
||||
The flow works as follows:
|
||||
|
||||
1. User starts config flow in Home Assistant
|
||||
2. Config flow prompts user to finish the flow on an external website
|
||||
3. User opens the external website
|
||||
4. Upon completion of the external step, the user's browser will be redirected to a Home Assistant endpoint to deliver the response.
|
||||
5. The endpoint validates the response, and upon validation, marks the external step as done and returns JavaScript code to close the window: `<script>window.close()</script>`.
|
||||
|
||||
To be able to route the result of the external step to the Home Assistant endpoint, you will need to make sure the config flow ID is included. If your external step is an OAuth2 flow, you can leverage the oauth2 state for this. This is a variable that is not interpreted by the authorization page but is passed as-is to the Home Assistant endpoint.
|
||||
6. The window closes and the Home Assistant user interface with the config flow will be visible to the user again.
|
||||
7. The config flow has automatically advanced to the next step when the external step was marked as done. The user is prompted with the next step.
|
||||
|
||||
Example configuration flow that includes an external step.
|
||||
|
||||
```python
|
||||
from homeassistant import config_entries
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
VERSION = 1
|
||||
data = None
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
if not user_input:
|
||||
return self.async_external_step(
|
||||
step_id='user',
|
||||
url='https://example.com/?config_flow_id={}'.format(
|
||||
self.flow_id
|
||||
),
|
||||
)
|
||||
|
||||
self.data = user_input
|
||||
return self.async_external_step_done(next_step_id='finish')
|
||||
|
||||
async def async_step_finish(self, user_input=None):
|
||||
return self.async_create_entry(
|
||||
title=self.data['title'],
|
||||
data=self.data
|
||||
)
|
||||
```
|
||||
|
||||
Example code to mark an external step as done:
|
||||
|
||||
```python
|
||||
from homeassistant import data_entry_flow
|
||||
|
||||
async def handle_result(hass, flow_id, data):
|
||||
result = await hass.config_entries.async_configure(flow_id, data)
|
||||
|
||||
if result['type'] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP_DONE:
|
||||
return "success!"
|
||||
else:
|
||||
return "Invalid config flow specified"
|
||||
```
|
||||
|
||||
## Translations
|
||||
|
||||
Data entry flows depend on translations for showing the text in the forms. It depends on the parent of a data entry flow manager where this is stored.
|
||||
|
||||
## Initializing a config flow from an external source
|
||||
|
||||
You might want to initialize a config flow programmatically. For example, if we discover a device on the network that requires user interaction to finish setup. To do so, pass a source parameter and optional user input when initializing a flow:
|
||||
|
||||
```python
|
||||
await flow_mgr.async_init('hue', context={'source': data_entry_flow.SOURCE_DISCOVERY}, data=discovery_info)
|
||||
```
|
||||
|
||||
The config flow handler will not start with the `init` step. Instead, it will be instantiated with a step name equal to the source. The step should follow the same return values as a normal step.
|
||||
|
||||
```python
|
||||
class ExampleConfigFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_discovery(self, info):
|
||||
# Handle discovery info
|
||||
```
|
@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Device Registry
|
||||
sidebar_label: Introduction
|
||||
id: version-0.94.0-device_registry_index
|
||||
original_id: device_registry_index
|
||||
---
|
||||
|
||||
The device registry is a registry where Home Assistant keeps track of devices. A device is represented in Home Assistant via one or more entities. For example, a battery-powered temperature and a humidity sensor might expose entities for temperature, humidity and battery level.
|
||||
|
||||
<img
|
||||
src='/img/en/device_registry/overview.png'
|
||||
alt='Device registry overview'
|
||||
/>
|
||||
|
||||
## What is a device?
|
||||
|
||||
A device in Home Assistant represents a physical device that has its own control unit. The control unit itself does not have to be smart, but it should be in control of what happens. For example, an Ecobee thermostat with 4 room sensors equals 5 devices in Home Assistant, one for the thermostat including all sensors inside it, and one for each sensor. Each device exists in a specific geographical area, and may have more than one input or output within that area.
|
||||
|
||||
If you connect a sensor to another device to read some of its data, it should still be represented as two different devices. The reason for this is that the sensor could be moved to read the data of another device.
|
||||
|
||||
A device that offers multiple endpoints, where parts of the device sense or output in different areas, should be split into separate devices and refer back to parent device with the `via_device` attribute. This allows the separate endpoints to be assigned to different areas in the building.
|
||||
|
||||
> Although not currently available, we could consider offering an option to users to merge devices.
|
||||
|
||||
## Device properties
|
||||
|
||||
| Attribute | Description |
|
||||
| --------- | ----------- |
|
||||
| id | Unique ID of device (generated by Home Assistant)
|
||||
| name | Name of this device
|
||||
| connections | A set of tuples of `(connection_type, connection identifier)`. Connection types are defined in the device registry module.
|
||||
| identifiers | Set of identifiers. They identify the device in the outside world. An example is a serial number.
|
||||
| manufacturer | The manufacturer of the device.
|
||||
| model | The model of the device.
|
||||
| config_entries | Config entries that are linked to this device.
|
||||
| sw_version | The firmware version of the device.
|
||||
| via_device | Identifier of a device that routes messages between this device and Home Assistant. Examples of such devices are hubs, or parent devices of a sub-device. This is used to show device topology in Home Assistant.
|
||||
| area_id | The Area which the device is placed in.
|
||||
|
||||
## Defining devices
|
||||
|
||||
> Entity device info is only read if the entity is loaded via a [config entry](config_entries_index.md).
|
||||
|
||||
Each entity is able to define a device via the `device_info` property. This property is read when an entity is added to Home Assistant via a config entry. A device will be matched up with an existing device via supplied identifiers and connections, like serial numbers or MAC addresses.
|
||||
|
||||
```python
|
||||
# Inside a platform
|
||||
class HueLight(LightEntity):
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
return {
|
||||
'identifiers': {
|
||||
# Serial numbers are unique identifiers within a specific domain
|
||||
(hue.DOMAIN, self.unique_id)
|
||||
},
|
||||
'name': self.name,
|
||||
'manufacturer': self.light.manufacturername,
|
||||
'model': self.light.productname,
|
||||
'sw_version': self.light.swversion,
|
||||
'via_device': (hue.DOMAIN, self.api.bridgeid),
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Components are also able to register devices in the case that there are no entities representing them. An example is a hub that communicates with the lights.
|
||||
|
||||
```python
|
||||
# Inside a component
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
device_registry = await dr.async_get_registry(hass)
|
||||
|
||||
device_registry.async_get_or_create(
|
||||
config_entry=entry.entry_id,
|
||||
connections={
|
||||
(dr.CONNECTION_NETWORK_MAC, config.mac)
|
||||
},
|
||||
identifiers={
|
||||
(DOMAIN, config.bridgeid)
|
||||
},
|
||||
manufacturer='Signify',
|
||||
name=config.name,
|
||||
model=config.modelid,
|
||||
sw_version=config.swversion,
|
||||
)
|
||||
```
|
189
website/versioned_docs/version-0.94.0/entity_cover.md
Normal file
189
website/versioned_docs/version-0.94.0/entity_cover.md
Normal file
@ -0,0 +1,189 @@
|
||||
---
|
||||
title: Cover Entity
|
||||
sidebar_label: Cover
|
||||
id: version-0.94.0-entity_cover
|
||||
original_id: entity_cover
|
||||
---
|
||||
|
||||
A cover entity is a device that controls an opening or cover, such as a garage door and window shade. Derive entity platforms from [`homeassistant.components.cover.CoverDevice`](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/cover/__init__.py).
|
||||
|
||||
## Properties
|
||||
|
||||
> Properties should always only return information from memory and not do I/O (like network requests). Implement `update()` or `async_update()` to fetch data.
|
||||
|
||||
### Platform Properties (to be implemented by deriving platform classes)
|
||||
|
||||
| Name | Type | Default | Description
|
||||
| ---- | ---- | ------- | -----------
|
||||
| current_cover_position | int | None | The current position of cover where 0 means closed and 100 is fully open. Required with `SUPPORT_SET_POSITION`.
|
||||
| current_cover_tilt_position | int | None | The current tilt position of the cover where 0 means closed/no tilt and 100 means open/maximum tilt. Required with `SUPPORT_SET_TILT_POSITION`
|
||||
| is_opening | bool | None | If the cover is opening or not. Used to determine `state`.
|
||||
| is_closing | bool | None | If the cover is closing or not. Used to determine `state`.
|
||||
| is_closed | bool | `NotImplementedError()` | If the cover is closed or not. if the state is unknown, return `None`. Used to determine `state`.
|
||||
|
||||
### Entity Properties (base class properties which may be overriden)
|
||||
|
||||
| Name | Type | Default | Description
|
||||
| ---- | ---- | ------- | -----------
|
||||
| device_class | string | None | Describes the type/class of the cover. Must be `None` or one of the valid values from the table below.
|
||||
| supported_features | int (bitwise) | Value determined from `current_cover_position` and `current_cover_tilt_position` | Describes the supported features. See the related table below for details.
|
||||
|
||||
### Device Classes
|
||||
| Constant | Description
|
||||
|----------|-----------------------|
|
||||
| `DEVICE_CLASS_AWNING` | Control of an awning, such as an exterior retractible window, door, or patio cover.
|
||||
| `DEVICE_CLASS_BLIND` | Control of blinds, which are linked slats that expand or collapse to cover an opening or may be tilted to partially cover an opening, such as window blinds.
|
||||
| `DEVICE_CLASS_CURTAIN` | Control of curtains or drapes, which is often fabric hung above a window or door that can be drawn open.
|
||||
| `DEVICE_CLASS_DAMPER` | Control of a mechanical damper that reduces air flow, sound, or light.
|
||||
| `DEVICE_CLASS_DOOR` | Control of a door or gate that provides access to an area.
|
||||
| `DEVICE_CLASS_GARAGE` | Control of a garage door that provides access to a garage.
|
||||
| `DEVICE_CLASS_SHADE` | Control of shades, which are a continous plane of material or connected cells that expanded or collapsed over an opening, such as window shades.
|
||||
| `DEVICE_CLASS_SHUTTER` | Control of shutters, which are linked slats that swing out/in to cover an opening or may be tilted to partially cover an opening, such as indoor or exterior window shutters.
|
||||
| `DEVICE_CLASS_WINDOW` | Control of a physical window that opens and closes or may tilt.
|
||||
|
||||
### States
|
||||
| Constant | Description
|
||||
|----------|------------------------|
|
||||
| `STATE_OPENING` | The cover is in the process of opening to reach a set position.
|
||||
| `STATE_OPEN` | The cover has reached the open position.
|
||||
| `STATE_CLOSING` | The cover is in the process of closing to reach a set position.
|
||||
| `STATE_CLOSED` | The cover has reach the closed position.
|
||||
|
||||
|
||||
### Supported Features
|
||||
|
||||
Supported features constants are combined using the bitwise or (`|`) operator.
|
||||
|
||||
| Constant | Description |
|
||||
|----------|--------------------------------------|
|
||||
| `SUPPORT_OPEN` | The cover supports being opened.
|
||||
| `SUPPORT_CLOSE` | The cover supports being closed.
|
||||
| `SUPPORT_SET_POSITION` | The cover supports moving to a specific position between opened and closed.
|
||||
| `SUPPORT_STOP` | The cover supports stopping the current action (open, close, set position)
|
||||
| `SUPPORT_OPEN_TILT` | The cover supports being tilting open.
|
||||
| `SUPPORT_CLOSE_TILT` | The cover supports being tilting closed.
|
||||
| `SUPPORT_SET_TILT_POSITION` | The cover supports moving to a specific tilt position between opened and closed.
|
||||
| `SUPPORT_STOP_TILT` | The cover supports stopping the current tilt action (open, close, set position)
|
||||
|
||||
## Methods
|
||||
|
||||
### Open cover
|
||||
|
||||
Only implement this method if the flag `SUPPORT_OPEN` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
```
|
||||
|
||||
### Close cover
|
||||
|
||||
Only implement this method if the flag `SUPPORT_CLOSE` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def close_cover(self, **kwargs):
|
||||
"""Close cover."""
|
||||
|
||||
async def async_close_cover(self, **kwargs):
|
||||
"""Close cover."""
|
||||
```
|
||||
|
||||
### Set cover position
|
||||
|
||||
Only implement this method if the flag `SUPPORT_SET_POSITION` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def set_cover_position(self, **kwargs):
|
||||
"""Move the cover to a specific position."""
|
||||
|
||||
async def async_set_cover_position(self, **kwargs):
|
||||
"""Move the cover to a specific position."""
|
||||
|
||||
```
|
||||
|
||||
### Stop cover
|
||||
|
||||
Only implement this metohd if the flag `SUPPORT_STOP` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def stop_cover(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
|
||||
async def async_stop_cover(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
```
|
||||
|
||||
### Open cover tilt
|
||||
|
||||
Only implement this method if the flag `SUPPORT_OPEN_TILT` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def open_cover_tilt(self, **kwargs):
|
||||
"""Open the cover tilt."""
|
||||
|
||||
async def async_open_cover_tilt(self, **kwargs):
|
||||
"""Open the cover tilt."""
|
||||
```
|
||||
|
||||
### Close cover tilt
|
||||
|
||||
Only implement this method if the flag `SUPPORT_CLOSE_TILT` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def close_cover_tilt(self, **kwargs):
|
||||
"""Close the cover tilt."""
|
||||
|
||||
async def async_close_cover_tilt(self, **kwargs):
|
||||
"""Close the cover tilt."""
|
||||
```
|
||||
|
||||
### Set cover tilt position
|
||||
|
||||
Only implement this method if the flag `SUPPORT_SET_TILT_POSITION` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def set_cover_tilt_position(self, **kwargs):
|
||||
"""Move the cover tilt to a specific position."""
|
||||
|
||||
async def async_set_cover_tilt_position(self, **kwargs):
|
||||
"""Move the cover tilt to a specific position."""
|
||||
```
|
||||
|
||||
### Stop cover tilt
|
||||
|
||||
Only implement this method if the flag `SUPPORT_STOP_TILT` is set.
|
||||
|
||||
```python
|
||||
class MyCover(CoverDevice):
|
||||
# Implement one of these methods.
|
||||
|
||||
def stop_cover_tilt(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
|
||||
async def async_stop_cover_tilt(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
```
|
552
website/versioned_docs/version-0.94.0/external_api_rest.md
Normal file
552
website/versioned_docs/version-0.94.0/external_api_rest.md
Normal file
@ -0,0 +1,552 @@
|
||||
---
|
||||
title: REST API
|
||||
id: version-0.94.0-external_api_rest
|
||||
original_id: external_api_rest
|
||||
---
|
||||
|
||||
Home Assistant provides a RESTful API on the same port as the web frontend. (default port is port 8123).
|
||||
|
||||
If you are not using the [`frontend`](https://www.home-assistant.io/components/frontend/) in your setup then you need to add the [`api` component](https://www.home-assistant.io/components/api/) to your `configuration.yaml` file.
|
||||
|
||||
* http://IP_ADDRESS:8123/ is an interface to control Home Assistant.
|
||||
* http://IP_ADDRESS:8123/api/ is a RESTful API.
|
||||
|
||||
The API accepts and returns only JSON encoded objects.
|
||||
|
||||
All API calls have to be accompanied by the header `Authorization: Bearer ABCDEFGH`, where `ABCDEFGH` is replaced by your token. You can obtain a token ("Long-Lived Access Token") by logging into the frontend using a web browser, and going to [your profile](https://www.home-assistant.io/docs/authentication/#your-account-profile) `http://IP_ADDRESS:8123/profile`.
|
||||
|
||||
There are multiple ways to consume the Home Assistant Rest API. One is with `curl`:
|
||||
|
||||
```bash
|
||||
$ curl -X GET \
|
||||
-H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" \
|
||||
http://IP_ADDRESS:8123/ENDPOINT
|
||||
```
|
||||
|
||||
Another option is to use Python and the [Requests](http://docs.python-requests.org/en/latest/) module.
|
||||
|
||||
```python
|
||||
from requests import get
|
||||
|
||||
url = 'http://localhost:8123/ENDPOINT'
|
||||
headers = {
|
||||
'Authorization': 'Bearer ABCDEFGH',
|
||||
'content-type': 'application/json',
|
||||
}
|
||||
|
||||
response = get(url, headers=headers)
|
||||
print(response.text)
|
||||
```
|
||||
Another option is to use the Restful Command component https://www.home-assistant.io/components/rest_command/ in a Home Assistant automation or script.
|
||||
|
||||
```yaml
|
||||
turn_light_on:
|
||||
url: http://localhost:8123/api/states/light.study_light
|
||||
method: POST
|
||||
headers:
|
||||
authorization: 'Bearer ABCDEFGH'
|
||||
content-type: 'application/json'
|
||||
payload: '{"state":"on"}'
|
||||
```
|
||||
|
||||
Successful calls will return status code 200 or 201. Other status codes that can return are:
|
||||
|
||||
- 400 (Bad Request)
|
||||
- 401 (Unauthorized)
|
||||
- 404 (Not Found)
|
||||
- 405 (Method not allowed)
|
||||
|
||||
### Actions
|
||||
|
||||
The API supports the following actions:
|
||||
|
||||
#### GET /api/
|
||||
|
||||
Returns a message if the API is up and running.
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "API running."
|
||||
}
|
||||
```
|
||||
|
||||
Sample `curl` command:
|
||||
|
||||
```bash
|
||||
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" http://localhost:8123/api/
|
||||
```
|
||||
|
||||
#### GET /api/config
|
||||
|
||||
Returns the current configuration as JSON.
|
||||
|
||||
```json
|
||||
{
|
||||
"components":[
|
||||
"sensor.cpuspeed",
|
||||
"frontend",
|
||||
"config.core",
|
||||
"http",
|
||||
"map",
|
||||
"api",
|
||||
"sun",
|
||||
"config",
|
||||
"discovery",
|
||||
"conversation",
|
||||
"recorder",
|
||||
"group",
|
||||
"sensor",
|
||||
"websocket_api",
|
||||
"automation",
|
||||
"config.automation",
|
||||
"config.customize"
|
||||
],
|
||||
"config_dir":"/home/ha/.homeassistant",
|
||||
"elevation":510,
|
||||
"latitude":45.8781529,
|
||||
"location_name":"Home",
|
||||
"longitude":8.458853651,
|
||||
"time_zone":"Europe/Zurich",
|
||||
"unit_system":{
|
||||
"length":"km",
|
||||
"mass":"g",
|
||||
"temperature":"\u00b0C",
|
||||
"volume":"L"
|
||||
},
|
||||
"version":"0.56.2",
|
||||
"whitelist_external_dirs":[
|
||||
"/home/ha/.homeassistant/www",
|
||||
"/home/ha/.homeassistant/"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Sample `curl` command:
|
||||
|
||||
```bash
|
||||
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" http://localhost:8123/api/config
|
||||
```
|
||||
|
||||
#### GET /api/discovery_info
|
||||
|
||||
Returns basic information about the Home Assistant instance as JSON.
|
||||
|
||||
```json
|
||||
{
|
||||
"base_url": "http://192.168.0.2:8123",
|
||||
"location_name": "Home",
|
||||
"requires_api_password": true,
|
||||
"version": "0.56.2"
|
||||
}
|
||||
```
|
||||
|
||||
Sample `curl` command:
|
||||
|
||||
```bash
|
||||
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" http://localhost:8123/api/discovery_info
|
||||
```
|
||||
|
||||
#### GET /api/events
|
||||
|
||||
Returns an array of event objects. Each event object contains event name and listener count.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"event": "state_changed",
|
||||
"listener_count": 5
|
||||
},
|
||||
{
|
||||
"event": "time_changed",
|
||||
"listener_count": 2
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Sample `curl` command:
|
||||
|
||||
```bash
|
||||
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" http://localhost:8123/api/events
|
||||
```
|
||||
|
||||
#### GET /api/services
|
||||
|
||||
Returns an array of service objects. Each object contains the domain and which services it contains.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"domain": "browser",
|
||||
"services": [
|
||||
"browse_url"
|
||||
]
|
||||
},
|
||||
{
|
||||
"domain": "keyboard",
|
||||
"services": [
|
||||
"volume_up",
|
||||
"volume_down"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Sample `curl` command:
|
||||
|
||||
```bash
|
||||
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" http://localhost:8123/api/services
|
||||
```
|
||||
|
||||
#### GET /api/history/period/<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 the current state of an entity.
|
||||
|
||||
Expects a JSON object that has at least a state attribute:
|
||||
|
||||
```json
|
||||
{
|
||||
"state": "below_horizon",
|
||||
"attributes": {
|
||||
"next_rising":"2016-05-31T03:39:14+00:00",
|
||||
"next_setting":"2016-05-31T19:16:42+00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The return code is 200 if the entity existed, 201 if the state of a new entity was set. A location header will be returned with the URL of the new resource. The response body will contain a JSON encoded State object.
|
||||
|
||||
```json
|
||||
{
|
||||
"attributes": {
|
||||
"next_rising":"2016-05-31T03:39:14+00:00",
|
||||
"next_setting":"2016-05-31T19:16:42+00:00"
|
||||
},
|
||||
"entity_id": "sun.sun",
|
||||
"last_changed": "2016-05-30T21:43:29.204838+00:00",
|
||||
"last_updated": "2016-05-30T21:47:30.533530+00:00",
|
||||
"state": "below_horizon"
|
||||
}
|
||||
```
|
||||
|
||||
Sample `curl` command:
|
||||
|
||||
```bash
|
||||
$ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"state": "25", "attributes": {"unit_of_measurement": "°C"}}' \
|
||||
http://localhost:8123/api/states/sensor.kitchen_temperature
|
||||
```
|
||||
|
||||
#### POST /api/events/<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."
|
||||
}
|
||||
```
|
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Creating custom panels
|
||||
id: version-0.94.0-frontend_creating_custom_panels
|
||||
original_id: frontend_creating_custom_panels
|
||||
---
|
||||
|
||||
Panels are pages that show information within Home Assistant and can allow controlling it. Panels are linked from the sidebar and rendered full screen. They have real-time access to the Home Assistant object via JavaScript. Examples of panels in the app are Map, Logbook and History.
|
||||
|
||||
Besides components registering panels, users can also register panels using the `panel_custom` component. This allows users to quickly build their own custom interfaces for Home Assistant.
|
||||
|
||||
## Introduction
|
||||
|
||||
Panels are defined as custom elements. You can use any framework that you want, as long as you wrap it up as a custom element. To quickly get started with a panel, we've created a [React custom panel starter kit](https://github.com/home-assistant/custom-panel-starter-kit-react).
|
||||
|
||||
## API reference
|
||||
|
||||
The Home Assistant frontend will pass information to your panel by setting properties on your custom element. The following properties are set:
|
||||
|
||||
| Property | Type | Description
|
||||
| -------- | ---- | -----------
|
||||
| hass | object | Current state of Home Assistant
|
||||
| narrow | boolean | if the panel should render in narrow mode
|
||||
| panel | object | Panel information. Config is available as `panel.config`.
|
||||
|
||||
## JavaScript versions
|
||||
|
||||
The Home Assistant user interface is currently served to browsers in modern JavaScript and older JavaScript (ES5). The older version has a wider browser support but that comes at a cost of size and performance.
|
||||
|
||||
If you do need to run with ES5 support, you will need to load the ES5 custom elements adapter before defining your element:
|
||||
|
||||
```js
|
||||
window.loadES5Adapter().then(function() {
|
||||
customElements.define('my-panel', MyCustomPanel)
|
||||
});
|
||||
```
|
@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Creating custom UI
|
||||
id: version-0.94.0-frontend_creating_custom_ui
|
||||
original_id: frontend_creating_custom_ui
|
||||
---
|
||||
|
||||
> This feature has been derepcated and is no longer supported. To add custom UI to Home Assistant, build a [custom Lovelace card](lovelace_custom_card.md) instead.
|
||||
|
||||
### State card
|
||||
|
||||
If you would like to use your own [State card](frontend_add_card.md) without merging your code into [home-assistant-polymer](https://github.com/home-assistant/home-assistant-polymer/) you can create your own implementation.
|
||||
|
||||
Put the element source file and its dependencies in `www/custom_ui/` directory under your Home Assistant [configuration](https://www.home-assistant.io/docs/configuration/) directory.
|
||||
|
||||
For example if creating a state card for the `light` domain named `state-card-my-custom-light` put `state-card-my-custom-light.html` in `www/custom_ui/`.
|
||||
|
||||
That file should implement `<state-card-my-custom-light>` tag with Polymer.
|
||||
|
||||
In `state-card-my-custom-light.html` you should use `<link rel="import">` to import all the dependencies **not** used by Home Assistant's UI.
|
||||
Do not import any dependencies used by the Home Assistant UI.
|
||||
Importing those will work in `development: 1` mode, but will fail in production mode.
|
||||
|
||||
1. In the `customize:` section of the `configuration.yaml` file put `custom_ui_state_card: state-card-my-custom-light`.
|
||||
2. In the `frontend` section use `extra_html_url` to specify the URL to load.
|
||||
|
||||
Example:
|
||||
|
||||
`configuration.yaml`:
|
||||
|
||||
```yaml
|
||||
homeassistant:
|
||||
customize:
|
||||
light.bedroom:
|
||||
custom_ui_state_card: state-card-my-custom-light
|
||||
|
||||
frontend:
|
||||
extra_html_url:
|
||||
- /local/custom_ui/state-card-my-custom-light.html
|
||||
```
|
||||
|
||||
`www/custom_ui/state-card-my-custom-light.html`:
|
||||
|
||||
```html
|
||||
<script>
|
||||
{
|
||||
// show the version of your custom UI in the HA dev info panel (HA 0.66.0+):
|
||||
const _NAME = 'My custom light';
|
||||
const _URL = 'https://home-assistant.io/developers/frontend_creating_custom_ui/';
|
||||
const _VERSION = '20180312';
|
||||
|
||||
if (!window.CUSTOM_UI_LIST) window.CUSTOM_UI_LIST = [];
|
||||
window.CUSTOM_UI_LIST.push({
|
||||
name: _NAME,
|
||||
url: _URL,
|
||||
version: _VERSION
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<dom-module id='state-card-my-custom-light'>
|
||||
<template>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<textarea>[[_toStr(stateObj)]]</textarea>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
class StateCardMyCustomLight extends Polymer.Element {
|
||||
static get is() { return 'state-card-my-custom-light'; }
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
// Home Assistant object
|
||||
hass: Object,
|
||||
// inDialog is true if shown as more-info-card
|
||||
inDialog: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
// includes state, config and more information of the entity
|
||||
stateObj: Object,
|
||||
};
|
||||
}
|
||||
|
||||
_toStr(obj) {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
}
|
||||
}
|
||||
customElements.define(StateCardMyCustomLight.is, StateCardMyCustomLight);
|
||||
</script>
|
||||
```
|
||||
|
||||
> Some browsers don't support latest ECMAScript standards, these require a separate ES5 compatible file (`extra_html_url_es5`).
|
||||
|
||||
For more possibilities, see the [Custom UI section](https://www.home-assistant.io/cookbook/#user-interface) on our Examples page.
|
||||
|
||||
### More info dialog
|
||||
|
||||
_Introduced in Home Assistant 0.69._
|
||||
|
||||
Similar to the custom State card, if you would like to use your own [More info dialog](frontend_add_more_info.md) you can create your own implementation.
|
||||
|
||||
Following a similar example, if creating a more info dialog a light named `more-info-my-custom-light` put `more-info-my-custom-light.html` in `www/custom_ui/`.
|
||||
|
||||
1. In the `customize:` section of the `configuration.yaml` file put `custom_ui_more_info: more-info-my-custom-light`.
|
||||
2. In the `frontend` section use `extra_html_url` to specify the URL to load.
|
||||
|
||||
Example:
|
||||
|
||||
`configuration.yaml`:
|
||||
|
||||
```yaml
|
||||
homeassistant:
|
||||
customize:
|
||||
light.bedroom:
|
||||
custom_ui_more_info: more-info-my-custom-light
|
||||
|
||||
frontend:
|
||||
extra_html_url:
|
||||
- /local/custom_ui/more-info-my-custom-light.html
|
||||
```
|
159
website/versioned_docs/version-0.94.0/frontend_external_bus.md
Normal file
159
website/versioned_docs/version-0.94.0/frontend_external_bus.md
Normal file
@ -0,0 +1,159 @@
|
||||
---
|
||||
title: External Bus
|
||||
id: version-0.94.0-frontend_external_bus
|
||||
original_id: frontend_external_bus
|
||||
---
|
||||
|
||||
The frontend is able to set up a message bus with an external app that is embedding the Home Assistant frontend. This system is a generalization of the [external auth](frontend_external_auth.md), making it easier to add more commands in the future without extensive plumbing on either the app or frontend side.
|
||||
|
||||
## Message exchange
|
||||
|
||||
Just like external auth, message exchange is achieved by the external app making a JavaScript method available.
|
||||
|
||||
Messages are passed to the external app as serialized JSON objects. The function that will be called takes a single parameter: a string. The external app will have to process the message and deal with it accordingly (or ignore it).
|
||||
|
||||
On Android, your app needs to define the following method:
|
||||
|
||||
```ts
|
||||
window.externalApp.externalBus(message: string)
|
||||
```
|
||||
|
||||
On iOS, your app needs to define the following method:
|
||||
|
||||
```ts
|
||||
window.webkit.messageHandlers.externalBus.postMessage(message: string);
|
||||
```
|
||||
|
||||
To send messages to the frontend, serialize your message to JSON and call the following function from the external app:
|
||||
|
||||
```ts
|
||||
window.externalBus(message: string)
|
||||
```
|
||||
|
||||
## Message format
|
||||
|
||||
The message describes an action or a piece of information that the sender wants the receiver to do or know about. If it's an action, the sender will expect a response with the result of that action. A response to a command can either be successful or failed.
|
||||
|
||||
### Action and Info Message format
|
||||
|
||||
The format of a message that contains or provides information is the same. It contains an identifier, a type and an optional payload (depending on the type).
|
||||
|
||||
A result message will re-use the identifier in the response, to indicate to which action the response is related.
|
||||
|
||||
The basic format of a message is the following:
|
||||
|
||||
```ts
|
||||
{
|
||||
id: number;
|
||||
type: string;
|
||||
payload?: unknown;
|
||||
}
|
||||
```
|
||||
|
||||
An example message:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 5,
|
||||
"type": "config/get"
|
||||
}
|
||||
```
|
||||
|
||||
### Result Message Format
|
||||
|
||||
If the message was an action, the sender will expect a response with the result. The response is either success or failure.
|
||||
|
||||
The type of result depends on the type of the message that it is responding to. For example, if it is responding to `config/get`, the result should be an object describing the configuration.
|
||||
|
||||
Message formats:
|
||||
|
||||
```ts
|
||||
interface SuccessResult {
|
||||
id: number;
|
||||
type: "result";
|
||||
success: true;
|
||||
result: unknown;
|
||||
}
|
||||
|
||||
interface ErrorResult {
|
||||
id: number;
|
||||
type: "result";
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Supported messages
|
||||
|
||||
### Get External Config
|
||||
|
||||
Available in: Home Assistant 0.92
|
||||
Type: `config/get`
|
||||
Direction: frontend to external app.
|
||||
Expects answer: yes
|
||||
|
||||
Query the external app for the external configuration. The external configuration is used to customize the experience in the frontend.
|
||||
|
||||
Expected response payload:
|
||||
|
||||
```ts
|
||||
{
|
||||
hasSettingsScreen: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
- `hasSettingsScreen` set to true if the external app will show a configuration screen when it receives the command `config_screen/show`. If so, a new option will be added to the sidebar to trigger the configuration screen.
|
||||
|
||||
### Show Config Screen `config_screen/show`
|
||||
|
||||
Available in: Home Assistant 0.92
|
||||
Type: `config_screen/show`
|
||||
Direction: frontend to external app.
|
||||
Expect answer: no
|
||||
|
||||
Show the configuration screen of the external app.
|
||||
|
||||
### Connection Status update `connection-status`
|
||||
|
||||
Available in: Home Assistant 0.92
|
||||
Type: `connection-status`
|
||||
Direction: frontend to external app.
|
||||
Expect answer: no
|
||||
|
||||
Notify the external app if the frontend is connected to Home Assistant.
|
||||
|
||||
Payload structure:
|
||||
|
||||
```ts
|
||||
{
|
||||
event: "connected" | "auth-invalid" | "disconnected";
|
||||
}
|
||||
```
|
||||
|
||||
### Trigger Haptic `haptic`
|
||||
|
||||
Available in: Home Assistant 0.92
|
||||
Type: `haptic`
|
||||
Direction: frontend to external app.
|
||||
Expect answer: no
|
||||
|
||||
Notify the external app to trigger haptic feedback.
|
||||
|
||||
Payload structure:
|
||||
|
||||
```ts
|
||||
{
|
||||
hapticType:
|
||||
| "success"
|
||||
| "warning"
|
||||
| "failure"
|
||||
| "light"
|
||||
| "medium"
|
||||
| "heavy"
|
||||
| "selection";
|
||||
|
||||
}
|
||||
```
|
@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Add-On Communication
|
||||
id: version-0.94.0-hassio_addon_communication
|
||||
original_id: hassio_addon_communication
|
||||
---
|
||||
|
||||
There are different ways for communication between add-ons inside Hass.io.
|
||||
|
||||
## Network
|
||||
|
||||
We use an internal network that's allowed to communicate with every add-on, including to/from Home Assistant, by using its name or alias. Only add-ons that run on the host network are limited in that they can talk with all internal add-ons by their name, but all other add-ons can't address these add-ons by name. However, using an alias works for both!
|
||||
|
||||
Names/aliases are used for communication inside Hass.io.
|
||||
The name is generated using the following format: `{REPO}_{SLUG}`, e.g., `local_xy` or `3283fh_myaddon`. In this example, `{SLUG}` is defined in an add-on's `config.json` file. You can use this name as the DNS name also, but you need replace any `_` with `-` to have a valid hostname. If an add-on is installed locally, `{REPO}` will be `local`. If the add-on is installed from a Github repository, `{REPO}` is a hashed identifier generated from the GitHub repository's URL (ex: https://github.com/xy/my_hassio_addons). See [here](https://github.com/home-assistant/hassio/blob/587047f9d648b8491dc8eef17dc6777f81938bfd/hassio/addons/utils.py#L17) to understand how this identifier is generated. Note that this identifier is required in certain service calls that use the [Hass.io add-on API][hassio-addon-api]. You can view the repository identifiers for all currently-installed add-ons via a GET request to the hassio API `addons` endpoint.
|
||||
|
||||
Use `hassio` for communication with the internal API.
|
||||
|
||||
## Home Assistant
|
||||
|
||||
An add-on can talk to the [Home Assistant API][hass-api] using the internal proxy. This makes it very easy to communicate with the API without knowing the password, port or any other information about the Home Assistant instance. Using this URL: `http://hassio/homeassistant/api` ensures that internal communication is redirected to the right place. The next step is to add `homeassistant_api: true` to the `config.json` file and read the environment variable `HASSIO_TOKEN`. Use this as the Home-Assistant password.
|
||||
|
||||
There is also a proxy for the [Home Assistant Websocket API][hass-websocket] that works like the API proxy above and requires `HASSIO_TOKEN` as the password. Use this URL: `http://hassio/homeassistant/websocket`.
|
||||
|
||||
It is also possible to talk directly to the Home Assistant instance, which is named `homeassistant`, over the internal network. However, you'll need to know the configuration that is used by the running instance.
|
||||
|
||||
We have several services for Hass.io inside Home Assistant to run tasks. Send data over STDIN to an add-on to use the `hassio.addon_stdin` service.
|
||||
|
||||
## Hass.io API
|
||||
|
||||
To enable calls to the [Hass.io API][hassio-api], add `hassio_api: true` to the `config.json` file and read the environment variable `HASSIO_TOKEN`. Now you can use the API over the URL: `http://hassio/`. Use the `HASSIO_TOKEN` with header `X-HASSIO-KEY`. You may also need to change the Hass.io API role to `hassio_role: default`.
|
||||
|
||||
Add-ons can call some API commands without needing to set `hassio_api: true`:
|
||||
- `/homeassistant/api`
|
||||
- `/homeassistant/api/stream`
|
||||
- `/homeassistant/websocket`
|
||||
- `/addons/self/*`
|
||||
- `/services*`
|
||||
- `/discovery*`
|
||||
- `/info`
|
||||
|
||||
***Note:*** For Home Assistant API access requirements, see above.
|
||||
|
||||
[hass-api]: https://www.home-assistant.io/developers/rest_api/
|
||||
[hass-websocket]: https://www.home-assistant.io/developers/websocket_api/
|
||||
[hassio-api]: https://github.com/home-assistant/hassio/blob/master/API.md
|
||||
[hassio-addon-api]: https://github.com/home-assistant/hassio/blob/dev/API.md#restful-for-api-addons
|
90
website/versioned_docs/version-0.94.0/hassio_debugging.md
Normal file
90
website/versioned_docs/version-0.94.0/hassio_debugging.md
Normal file
@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Debugging Hass.io
|
||||
id: version-0.94.0-hassio_debugging
|
||||
original_id: hassio_debugging
|
||||
---
|
||||
|
||||
> This section is not for end users. End users should use the [SSH add-on] to SSH into Hass.io. This is for <b>developers</b> of Hass.io. Do not ask for support if you are using these options.
|
||||
|
||||
[SSH add-on]: https://www.home-assistant.io/addons/ssh/
|
||||
|
||||
The following debug tips and tricks are for developers who are running the Hass.io image and are working on the base image. If you use the generic Linux installer script, you should be able to access your host and logs as per your host.
|
||||
|
||||
## Debug Supervisor
|
||||
|
||||
Visual Studio Code config:
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Hass.io remote debug",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"port": 33333,
|
||||
"host": "IP",
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "/usr/src/hassio"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You need set the dev mode on supervisor and enable debug with options. You need also install the Remote debug Add-on from Developer Repository to expose the debug port to Host.
|
||||
|
||||
## SSH access to the host
|
||||
|
||||
### resinOS based Hass.io (deprecated)
|
||||
Create an `authorized_keys` file containing your public key, and place it in the root of the boot partition of your SD card. See [Generating SSH Keys](#generating-ssh-keys) section below if you need help generating keys. Once the device is booted, you can access your device as root over SSH on port 22222.
|
||||
|
||||
### HassOS based Hass.io
|
||||
Use a USB drive formatted with FAT, ext4, or NTFS and name it CONFIG (case sensitive). Create an `authorized_keys` file (no extension) containing your public key, and place it in the root of the USB drive. File needs to be ANSI encoded (not UTF-8) and must have Unix line ends (LF), not Windows (CR LF). See [Generating SSH Keys](#generating-ssh-keys) section below if you need help generating keys. From the UI, navigate to the hass.io system page and choose "Import from USB". You can now access your device as root over SSH on port 22222. Alternatively, the file will be imported from the USB when the hass.io device is rebooted.
|
||||
|
||||
> Make sure when you are copying the public key to the root of the USB drive that you rename the file correctly to `authorized_keys` with no `.pub` file extension.
|
||||
|
||||
You should then be able to SSH into your Hass.io device. On Mac/Linux, use:
|
||||
|
||||
```
|
||||
ssh root@hassio.local -p 22222
|
||||
```
|
||||
|
||||
You will initially be logged in to Hass.io CLI for HassOS where you can perform normal [CLI functions]. If you need access to the host system use the 'login' command. [Hass.io OS] is a hypervisor for Docker. See the [Hass.io Architecture] documentation for information regarding the Hass.io supervisor. The supervisor offers an API to manage the host and running the Docker containers. Home Assistant itself and all installed addon's run in separate Docker containers.
|
||||
|
||||
[CLI functions]: https://www.home-assistant.io/hassio/commandline/
|
||||
[Hass.io OS]: https://github.com/home-assistant/hassos
|
||||
[Hass.io Architecture]: https://developers.home-assistant.io/docs/en/architecture_hassio.html
|
||||
|
||||
## Checking the logs
|
||||
|
||||
```bash
|
||||
# Logs from the supervisor service on the Host OS
|
||||
journalctl -f -u hassos-supervisor.service
|
||||
|
||||
# Hass.io supervisor logs
|
||||
docker logs hassos_supervisor
|
||||
|
||||
# Home Assistant logs
|
||||
docker logs homeassistant
|
||||
```
|
||||
|
||||
## Accessing the container bash
|
||||
|
||||
```bash
|
||||
docker exec -it homeassistant /bin/bash
|
||||
```
|
||||
|
||||
[windows-keys]: https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-putty-on-digitalocean-droplets-windows-users
|
||||
|
||||
### Generating SSH Keys
|
||||
|
||||
Windows instructions for how to generate and use private/public keys with Putty are [here][windows-keys]. Instead of the droplet instructions, add the public key as per above instructions.
|
||||
|
||||
Alternative instructions, for Mac, Windows and Linux can be found [here](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/#platform-mac).
|
||||
|
||||
Follow steps 1-4 under 'Generating a new SSH key' (The other sections are not applicable to Hass.io and can be ignored.)
|
||||
|
||||
Step 3 in the link above, shows the path to the private key file `id_rsa` for your chosen operating system. Your public key, `id_rsa.pub`, is saved in the same folder. Next, select all text from text box "Public key for pasting into the authorized_keys file" and save it to the root of your USB drive as `authorized_keys`.
|
169
website/versioned_sidebars/version-0.94.0-sidebars.json
Normal file
169
website/versioned_sidebars/version-0.94.0-sidebars.json
Normal file
@ -0,0 +1,169 @@
|
||||
{
|
||||
"version-0.94.0-Architecture": {
|
||||
"Architecture": [
|
||||
"version-0.94.0-architecture_index",
|
||||
"version-0.94.0-architecture_components",
|
||||
"version-0.94.0-architecture_entities",
|
||||
"version-0.94.0-architecture_hassio"
|
||||
],
|
||||
"Entities": [
|
||||
"version-0.94.0-entity_index",
|
||||
"version-0.94.0-entity_air_quality",
|
||||
"version-0.94.0-entity_alarm_control_panel",
|
||||
"version-0.94.0-entity_binary_sensor",
|
||||
"version-0.94.0-entity_climate",
|
||||
"version-0.94.0-entity_cover",
|
||||
"version-0.94.0-entity_fan",
|
||||
"version-0.94.0-entity_light",
|
||||
"version-0.94.0-entity_lock",
|
||||
"version-0.94.0-entity_media_player",
|
||||
"version-0.94.0-entity_remote",
|
||||
"version-0.94.0-entity_sensor",
|
||||
"version-0.94.0-entity_switch",
|
||||
"version-0.94.0-entity_vacuum",
|
||||
"version-0.94.0-entity_water_heater",
|
||||
"version-0.94.0-entity_weather"
|
||||
],
|
||||
"Authentication": [
|
||||
"version-0.94.0-auth_index",
|
||||
"version-0.94.0-auth_permissions",
|
||||
"version-0.94.0-auth_api",
|
||||
"version-0.94.0-auth_auth_provider",
|
||||
"version-0.94.0-auth_auth_module"
|
||||
],
|
||||
"Config Entries": [
|
||||
"version-0.94.0-config_entries_index"
|
||||
],
|
||||
"Data Entry Flow": [
|
||||
"version-0.94.0-data_entry_flow_index"
|
||||
],
|
||||
"Entity Registry": [
|
||||
"version-0.94.0-entity_registry_index"
|
||||
],
|
||||
"Device Registry": [
|
||||
"version-0.94.0-device_registry_index"
|
||||
],
|
||||
"Area Registry": [
|
||||
"version-0.94.0-area_registry_index"
|
||||
]
|
||||
},
|
||||
"version-0.94.0-Extending Frontend": {
|
||||
"Frontend": [
|
||||
"version-0.94.0-frontend_index",
|
||||
"version-0.94.0-frontend_architecture",
|
||||
"version-0.94.0-frontend_development",
|
||||
"version-0.94.0-frontend_data",
|
||||
"version-0.94.0-frontend_external_auth",
|
||||
"version-0.94.0-frontend_external_bus"
|
||||
],
|
||||
"Extending the frontend": [
|
||||
"version-0.94.0-frontend_add_card",
|
||||
"version-0.94.0-frontend_add_more_info",
|
||||
"version-0.94.0-frontend_add_websocket_api"
|
||||
],
|
||||
"Custom UI": [
|
||||
"version-0.94.0-lovelace_custom_card",
|
||||
"version-0.94.0-frontend_creating_custom_panels"
|
||||
]
|
||||
},
|
||||
"version-0.94.0-Extending HASS": {
|
||||
"Development Workflow": [
|
||||
"version-0.94.0-development_index",
|
||||
"version-0.94.0-development_environment",
|
||||
"version-0.94.0-development_submitting",
|
||||
"version-0.94.0-development_guidelines",
|
||||
"version-0.94.0-development_testing",
|
||||
"version-0.94.0-development_catching_up"
|
||||
],
|
||||
"Building Integrations": [
|
||||
"version-0.94.0-creating_integration_file_structure",
|
||||
"version-0.94.0-creating_integration_manifest",
|
||||
"version-0.94.0-creating_component_index",
|
||||
"version-0.94.0-config_entries_config_flow_handler",
|
||||
"version-0.94.0-config_entries_options_flow_handler",
|
||||
"version-0.94.0-configuration_yaml_index",
|
||||
"version-0.94.0-dev_101_services",
|
||||
"version-0.94.0-creating_platform_index",
|
||||
"version-0.94.0-creating_component_generic_discovery"
|
||||
],
|
||||
"Development Checklist": [
|
||||
"version-0.94.0-development_checklist",
|
||||
"version-0.94.0-creating_component_code_review",
|
||||
"version-0.94.0-creating_platform_code_review",
|
||||
"version-0.94.0-integration_quality_scale_index"
|
||||
],
|
||||
"Home Assistant Core 101": [
|
||||
"version-0.94.0-dev_101_index",
|
||||
"version-0.94.0-dev_101_hass",
|
||||
"version-0.94.0-dev_101_events",
|
||||
"version-0.94.0-dev_101_states",
|
||||
"version-0.94.0-dev_101_config"
|
||||
],
|
||||
"Misc": [
|
||||
"version-0.94.0-development_validation",
|
||||
"version-0.94.0-development_typing"
|
||||
]
|
||||
},
|
||||
"version-0.94.0-Misc": {
|
||||
"Introduction": [
|
||||
"version-0.94.0-misc"
|
||||
],
|
||||
"External API": [
|
||||
"version-0.94.0-external_api_rest",
|
||||
"version-0.94.0-external_api_rest_python",
|
||||
"version-0.94.0-external_api_websocket",
|
||||
"version-0.94.0-external_api_server_sent_events"
|
||||
],
|
||||
"Internationalization": [
|
||||
"version-0.94.0-internationalization_index",
|
||||
"version-0.94.0-internationalization_backend_localization",
|
||||
"version-0.94.0-internationalization_custom_component_localization",
|
||||
"version-0.94.0-internationalization_translation"
|
||||
],
|
||||
"Documentation": [
|
||||
"version-0.94.0-documentation_index",
|
||||
"version-0.94.0-documentation_standards",
|
||||
"version-0.94.0-documentation_create_page"
|
||||
],
|
||||
"Intents": [
|
||||
"version-0.94.0-intent_index",
|
||||
"version-0.94.0-intent_firing",
|
||||
"version-0.94.0-intent_handling",
|
||||
"version-0.94.0-intent_conversation",
|
||||
"version-0.94.0-intent_builtin"
|
||||
],
|
||||
"Native App Integration": [
|
||||
"version-0.94.0-app_integration_index",
|
||||
"version-0.94.0-app_integration_setup",
|
||||
"version-0.94.0-app_integration_sending_data",
|
||||
"version-0.94.0-app_integration_sensors",
|
||||
"version-0.94.0-app_integration_notifications",
|
||||
"version-0.94.0-app_integration_webview"
|
||||
],
|
||||
"asyncio": [
|
||||
"version-0.94.0-asyncio_index",
|
||||
"version-0.94.0-asyncio_101",
|
||||
"version-0.94.0-asyncio_categorizing_functions",
|
||||
"version-0.94.0-asyncio_working_with_async"
|
||||
],
|
||||
"Hass.io": [
|
||||
"version-0.94.0-hassio_debugging",
|
||||
"version-0.94.0-hassio_hass"
|
||||
],
|
||||
"Hass.io Add-Ons": [
|
||||
"version-0.94.0-hassio_addon_index",
|
||||
"version-0.94.0-hassio_addon_tutorial",
|
||||
"version-0.94.0-hassio_addon_config",
|
||||
"version-0.94.0-hassio_addon_communication",
|
||||
"version-0.94.0-hassio_addon_testing",
|
||||
"version-0.94.0-hassio_addon_publishing",
|
||||
"version-0.94.0-hassio_addon_presentation",
|
||||
"version-0.94.0-hassio_addon_repository",
|
||||
"version-0.94.0-hassio_addon_security"
|
||||
],
|
||||
"Maintainer docs": [
|
||||
"version-0.94.0-maintenance",
|
||||
"version-0.94.0-releasing"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
[
|
||||
"0.94.0",
|
||||
"0.93.0",
|
||||
"0.92.1",
|
||||
"0.92.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user