diff --git a/website/versioned_docs/version-0.102.0/app_integration_setup.md b/website/versioned_docs/version-0.102.0/app_integration_setup.md new file mode 100644 index 00000000..e00cffc8 --- /dev/null +++ b/website/versioned_docs/version-0.102.0/app_integration_setup.md @@ -0,0 +1,87 @@ +--- +title: Connecting to an instance +id: version-0.102.0-app_integration_setup +original_id: app_integration_setup +--- + +When a user first opens the app, they will need to connect to their local instance to authenticate and register the device. + +## Authenticating the user + +The local instance can be discovered if Home Assistant has the [zeroconf component] configured by searching for `_home-assistant._tcp.local.`. If not configured, the user will need to be asked for the local address of their instance. + +When the address of the instance is known, the app will ask the user to authenticate via [OAuth2 with Home Assistant]. Home Assistant uses IndieAuth, which means that to be able to redirect to a url that triggers your app, you need to take some extra steps. Make sure to read the last paragraph of the "Clients" section thoroughly. + +[zeroconf component]: https://www.home-assistant.io/components/zeroconf +[OAuth2 with Home Assistant]: auth_api.md + +## Registering the device + +_This requires Home Assistant 0.90 or later._ + +Home Assistant has a `mobile_app` component that allows applications to register themselves and interact with the instance. This is a generic component to handle most common mobile application tasks. This component is extendable with custom interactions if your app needs more types of interactions than are offered by this component. + +Once you have tokens to authenticate as a user, it's time to register the app with the mobile app component in Home Assistant. + +### Getting Ready + +First, you must ensure that the `mobile_app` component is loaded. There are two ways to do this: + +- You can publish a Zeroconf/Bonjour record `_hass-mobile-app._tcp.local.` to trigger the automatic load of the `mobile_app` component. You should wait at least 60 seconds after publishing the record before continuing. +- You can ask the user to add `mobile_app` to their configuration.yaml and restart Home Assistant. If the user already has `default_config` in their configuration, then `mobile_app` will have been already loaded. + +You can confirm the `mobile_app` component has been loaded by checking the `components` array of the [`/api/config` REST API call](external_api_rest.md#get-api-config). If you continue to device registration and receive a 404 status code, then it most likely hasn't been loaded yet. + +### Registering the device + +To register the device, make an authenticated POST request to `/api/mobile_app/registrations`. [More info on making authenticated requests.](auth_api.md#making-authenticated-requests) + +Example payload to send to the registration endpoint: + +```json +{ + "app_id": "awesome_home", + "app_name": "Awesome Home", + "app_version": "1.2.0", + "device_name": "Robbies iPhone", + "manufacturer": "Apple, Inc.", + "model": "iPhone X", + "os_name": "iOS", + "os_version": "iOS 10.12", + "supports_encryption": true, + "app_data": { + "push_notification_key": "abcdef", + } +} +``` + +| Key | Required | Type | Description | +| --- | -------- | ---- | ----------- | +| `app_id` | V | string | A unique identifier for this app. +| `app_name` | V | string | Name of the mobile app. +| `app_version` | V | string | Version of the mobile app. +| `device_name` | V | string | Name of the device running the app. +| `manufacturer` | V | string | The manufacturer of the device running the app. +| `model` | V | string | The model of the device running the app. +| `os_name` | V | string | The name of the OS running the app. +| `os_version` | V | string | The OS version of the device running the app. +| `supports_encryption` | V | bool | If the app supports encryption. See also the [encryption section](#encryption). +| `app_data` | | Dict | App data can be used if the app has a supporting component that extends `mobile_app` functionality. + +When you get a 200 response, the mobile app is registered with Home Assistant. The response is a JSON document and will contain the URLs on how to interact with the Home Assistant instance. You should permanently store this information. + +```json +{ + "cloudhook_url": "https://hooks.nabu.casa/randomlongstring123", + "remote_ui_url": "https://randomlongstring123.ui.nabu.casa", + "secret": "qwerty", + "webhook_id": "abcdefgh" +} +``` + +| Key | Type | Description +| --- | ---- | ----------- +| `cloudhook_url` | string | The cloudhook URL provided by Home Assistant Cloud. Only will be provided if user is actively subscribed to Nabu Casa. +| `remote_ui_url` | string | The remote UI URL provided by Home Assistant Cloud. Only will be provided if user is actively subscribed to Nabu Casa. +| `secret` | string | The secret to use for encrypted communication. Will only be included if encryption is supported by both the app and the Home Assistant instance. [More info](app_integration_sending_data.md#implementing-encryption). +| `webhook_id` | string | The webhook ID that can be used to send data back. diff --git a/website/versioned_docs/version-0.102.0/config_entries_config_flow_handler.md b/website/versioned_docs/version-0.102.0/config_entries_config_flow_handler.md new file mode 100644 index 00000000..e11d6351 --- /dev/null +++ b/website/versioned_docs/version-0.102.0/config_entries_config_flow_handler.md @@ -0,0 +1,120 @@ +--- +title: Integration Configuration +sidebar_label: Configuration +id: version-0.102.0-config_entries_config_flow_handler +original_id: config_entries_config_flow_handler +--- + +Integrations can be set up via the user interface by adding support for a config flow to create a config entry. Components that want to support config entries will need to define a Config Flow Handler. This handler will manage the creation of entries from user input, discovery or other sources (like Hass.io). + +Config Flow Handlers control the data that is stored in a config entry. This means that there is no need to validate that the config is correct when Home Assistant starts up. It will also prevent breaking changes, because we will be able to migrate configuration entries to new formats if the version changes. + +When instantiating the handler, Home Assistant will make sure to load all dependencies and install the requirements of the component. + +## Updating the manifest + +You need to update your integrations manifest to inform Home Assistant that your integration has a config flow. This is done by adding `config_flow: true` to your manifest ([docs](creating_integration_manifest.md#config-flow)). + +## Defining your config flow + +Config entries uses the [data flow entry framework](data_entry_flow_index.md) to define their config flows. The config flow needs to be defined in the file `config_flow.py` in your integration folder, extend `homeassistant.config_entries.ConfigFlow` and pass a `domain` key as part of inheriting `ConfigFlow`. + +```python +from homeassistant import config_entries +from .const import DOMAIN + +class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): +``` + +## Defining steps + +Your config flow will need to define steps of your configuration flow. The docs for [Data Entry Flow](data_entry_flow_index.md) describe the different return values of a step. Here is an example on how to define the `user` step. + +```python +class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + + async def async_step_user(self, info): + if info is not None: + # process info + + return self.async_show_form( + step_id='user', + data_schema=vol.Schema({ + vol.Required('password'): str + }) + ) +``` + +There are a few step names reserved for system use: + +| Step name | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `user` | Invoked when a user initiates a flow via the user interface. | +| `zeroconf` | Invoked if your integration has been discovered via Zeroconf/mDNS as specified [using `zeroconf` in the manifest](creating_integration_manifest.md#zeroconf). | +| `homekit` | Invoked if your integration has been discovered via HomeKit as specified [using `homekit` in the manifest](creating_integration_manifest.md#homekit). | +| `ssdp` | Invoked if your integration has been discovered via SSDP/uPnP as specified [using `ssdp` in the manifest](creating_integration_manifest.md#ssdp). | +| `discovery` | _DEPRECATED_ Invoked if your integration has been discovered by the discovery integration. | + +## Discovery steps + +When an integration is discovered, their respective discovery step is invoked with the discovery information. The step will have to check the following things: + +- Make sure there are no other instances of this config flow in progress of setting up the discovered device. This can happen if there are multiple ways of discovering that a device is on the network. +- Make sure that the device is not already set up. +- Invoking a discovery step should never result in a finished flow and a config entry. Always confirm with the user. + +## Discoverable integrations that require no authentication + +If your integration is discoverable without requiring any authentication, you'll be able to use the Discoverable Flow that is built-in. This flow offers the following features: + +- Detect if devices/services can be discovered on the network before finishing the config flow. +- Support all manifest-based discovery protocols. +- Limit to only 1 config entry. It is up to the config entry to discover all available devices. + +To get started, run `python3 -m script.scaffold config_flow_discovery` and follow the instructions. This will create all the boilerplate necessary to configure your integration using discovery. + +## Configuration via OAuth2 + +Home Assistant has built-in support for integrations that offer account linking using [the OAuth2 authorization framework](https://tools.ietf.org/html/rfc6749). To be able to leverage this, you will need to structure your Python API library in a way that allows Home Assistant to be responsible for refreshing tokens. See our [API library guide](api_lib_index.md) on how to do this. + +The built-in OAuth2 support works out of the box with locally configured client ID / secret and with the Home Assistant Cloud Account Linking service. This service allows users to link their account with a centrally managed client ID/secret. If you want your integration to be part of this service, reach out to us at [hello@home-assistant.io](mailto:hello@home-assistant.io). + +To get started, run `python3 -m script.scaffold config_flow_oauth2` and follow the instructions. This will create all the boilerplate necessary to configure your integration using OAuth2. + +## Translations + +Translations for the config flow handlers are defined under the `config` key in the component translation file `strings.json`. Example of the Hue component: + +```json +{ + "config": { + "title": "Philips Hue Bridge", + "step": { + "init": { + "title": "Pick Hue bridge", + "data": { + "host": "Host" + } + }, + "link": { + "title": "Link Hub", + "description": "Press the button on the bridge to register Philips Hue with Home Assistant.\n\n![Location of button on bridge](/static/images/config_philips_hue.jpg)" + } + }, + "error": { + "register_failed": "Failed to register, please try again", + "linking": "Unknown linking error occurred." + }, + "abort": { + "discover_timeout": "Unable to discover Hue bridges", + "no_bridges": "No Philips Hue bridges discovered", + "all_configured": "All Philips Hue bridges are already configured", + "unknown": "Unknown error occurred", + "cannot_connect": "Unable to connect to the bridge", + "already_configured": "Bridge is already configured" + } + } +} +``` + +When the translations are merged into Home Assistant, they will be automatically uploaded to [Lokalise](https://lokalise.co/) where the translation team will help to translate them in other languages. [More info on translating Home Assistant.](internationalization_translation.md) diff --git a/website/versioned_docs/version-0.102.0/development_testing.md b/website/versioned_docs/version-0.102.0/development_testing.md new file mode 100644 index 00000000..5a458dae --- /dev/null +++ b/website/versioned_docs/version-0.102.0/development_testing.md @@ -0,0 +1,86 @@ +--- +title: Testing your code +id: version-0.102.0-development_testing +original_id: development_testing +--- + +As it states in the [Style guidelines section](development_guidelines.md) all code is checked to verify the following: + +- All the unit tests pass +- All code passes the checks from the linting tools + +Local testing is done using [Tox](https://tox.readthedocs.io), which has been installed as part of running `script/setup` in the [virtual environment](development_environment.md). To start the tests, activate the virtual environment and simply run the command: + +```bash +$ tox +``` + +It might be required that you install additional packages depending on your distribution/operating system: + +- Fedora: `sudo dnf -y install systemd-devel gcc-c++` +- Ubuntu: `sudo spt-get install libudev-dev` + +**Important:** Run `tox` before you create your pull request to avoid annoying fixes. + +Running `tox` will run unit tests against the locally available Python releases, as well as validate the code and document style using `pycodestyle`, `pydocstyle` and `pylint`. You can run tests on only one `tox` target -- just use `-e` to select an environment. For example, `tox -e lint` runs the linters only, and `tox -e py38` runs unit tests only on Python 3.8. + +`tox` uses virtual environments under the hood to create isolated testing environments. The `tox` virtual environments will get out-of-date when requirements change, causing test errors. Run `tox -r` to tell `tox` to recreate the virtual environments. + +macOS users may see an `Error creating virtualenv` when runnng `tox`. If this occurs, install the [tox-venv](https://pypi.org/project/tox-venv/) package using the command `pip install tox-venv` and try again. + +### Adding new dependencies to test environment + +If you are working on tests for an integeration and you need the dependencies available inside the `tox` environment, update the list inside `script/gen_requirements_all.py`. Then run the script and then run `tox -r` to recreate the virtual environments. + +### Running single tests using `tox` + +You can pass arguments via `tox` to `py.test` to be able to run single test suites or test files. Replace `py38` with the Python version that you use. + +```bash +# Stop after the first test fails +$ tox -e py38 -- tests/test_core.py -x +# Run test with specified name +$ tox -e py38 -- tests/test_core.py -k test_split_entity_id +# Fail a test after it runs for 2 seconds +$ tox -e py38 -- tests/test_core.py --timeout 2 +# Show the 10 slowest tests +$ tox -e py38 -- tests/test_core.py --duration=10 +``` + +### Testing outside of Tox + +Running `tox` will invoke the full test suite. Even if you specify which tox target to run, you still run all tests inside that target. That's not very convenient to quickly iterate on your code! To be able to run the specific test suites without `tox`, you'll need to install the test dependencies into your Python environment: + +```bash +$ pip3 install -r requirements_test_all.txt -c homeassistant/package_constraints.txt +``` + +Now that you have all test dependencies installed, you can run tests on individual files: + +```bash +$ flake8 homeassistant/core.py +$ pylint homeassistant/core.py +$ pydocstyle homeassistant/core.py +$ py.test tests/test_core.py +``` + +You can also run linting tests against all changed files, as reported by `git diff upstream/dev... --diff-filter=d --name-only`, using the `lint` script: + +```bash +$ script/lint +``` + +### Preventing linter errors + +Save yourself the hassle of extra commits just to fix style errors by enabling the Flake8 git commit hook. Flake8 will check your code when you try to commit to the repository and block the commit if there are any style errors, which gives you a chance to fix them! + +```bash +$ pip3 install flake8 flake8-docstrings +$ flake8 --install-hook=git +``` + +The `flake8-docstrings` extension will check docstrings according to [PEP257](https://www.python.org/dev/peps/pep-0257/) when running Flake8. + +### Notes on PyLint and PEP8 validation + +If you can't avoid a PyLint warning, add a comment to disable the PyLint check for that line with `# pylint: disable=YOUR-ERROR-NAME`. Example of an unavoidable one is if PyLint incorrectly reports that a certain object doesn't have a certain member. diff --git a/website/versioned_docs/version-0.102.0/entity_index.md b/website/versioned_docs/version-0.102.0/entity_index.md new file mode 100644 index 00000000..97c84ad4 --- /dev/null +++ b/website/versioned_docs/version-0.102.0/entity_index.md @@ -0,0 +1,113 @@ +--- +title: Entity +sidebar_label: Introduction +id: version-0.102.0-entity_index +original_id: entity_index +--- + +Each device is represented in Home Assistant as an entity. An entity abstracts away the internal working of Home Assistant. As an integrator you don't have to worry about how services or the state machine work. Instead, you extend an entity class and implement the necessary properties and methods for the device type that you're integrating. + +Below is an example switch entity that keeps track of their state in memory. + +```python +from homeassistant.components.switch import SwitchDevice + +class MySwitch(SwitchDevice): + + def __init__(self): + self._is_on = False + + @property + def name(self): + """Name of the device.""" + return 'My Switch' + + @property + def is_on(self): + """If the switch is currently on or off.""" + return self._is_on + + def turn_on(self, **kwargs): + """Turn the switch on.""" + self._is_on = True + + def turn_off(self, **kwargs): + """Turn the switch off.""" + self._is_on = False +``` + +That's all there is to it to build a switch entity! Continue reading to learn more or check out the [video tutorial](https://youtu.be/Cfasc9EgbMU?t=737). + +## Updating the entity + +An entity represents a device. There are various strategies to keep your entity in sync with the state of the device, the most popular one being polling. + +### Polling + +With polling, Home Assistant will ask the entity from time to time (depending on the update interval of the component) to fetch the latest state. Home Assistant will poll an entity when the `should_poll` property returns `True` (the default value). You can either implement your update logic using `update()` or the async method `async_update()`. This method should fetch the latest state from the device and store it in an instance variable for the properties to return it. + +### Subscribing to updates + +When you subscribe to updates, your code is responsible for letting Home Assistant know that an update is available. Make sure you have the `should_poll` property return `False`. + +Whenever you receive new state from your subscription, you can tell Home Assistant that an update is available by calling `schedule_update_ha_state()` or async callback `async_schedule_update_ha_state()`. Pass in the boolean `True` to the method if you want Home Assistant to call your update method before writing the update to Home Assistant. + +## Generic properties + +The entity base class has a few properties that are common among all entities in Home Assistant. These can be added to any entity regardless of the type. All these properties are optional and don't need to be implemented. + +> Properties should always only return information from memory and not do I/O (like network requests). Implement `update()` or `async_update()` to fetch data. + +| Name | Type | Default | Description +| ---- | ---- | ------- | ----------- +| assumed_state | boolean | `False` | Return `True` if the state is based on our assumption instead of reading it from the device. +| available | boolean | `True` | Indicate if Home Assistant is able to read the state and control the underlying device. +| device_state_attributes | dict | `None` | Extra information to store in the state machine. It needs to be information that further explains the state, it should not be static information like firmware version. See [below](entity_index.md#standard-attributes) for details of standard attributes. +| entity_picture | URL | `None` | Url of a picture to show for the entity. +| name | string | `None` | Name of the entity +| should_poll | boolean | `True` | Should Home Assistant check with the entity for an updated state. If set to `False`, entity will need to notify Home Assistant of new updates by calling one of the [schedule update methods](#methods). +| unique_id | string | `None` | A unique identifier for this entity. Needs to be unique within a platform (ie `light.hue`). Should not be configurable by the user or be changeable. [Learn more.](entity_registry_index.md#unique-id-requirements) + +## Advanced properties + +The following properties are also available on entities. However, they are for advanced use only and should be used with caution. + +| Name | Type | Default | Description +| ---- | ---- | ------- | ----------- +| force_update | boolean | `False` | Write each update to the state machine, even if the data is the same. Example use: when you are directly reading the value from a connected sensor instead of a cache. Use with caution, will spam the state machine. +| hidden | boolean | `False` | Indicate if the entity should not be shown on the frontend. +| icon | icon | `None` | Icon to use in the frontend. Icons start with `mdi:` plus an [identifier](https://materialdesignicons.com/). You probably don't need this since Home Assistant already provides default icons for all devices. +| entity_registry_enabled_default | boolean | `True` | Indicate if the entity should be enabled or disabled when it is first added to the entity registry. + +## System properties + +The following properties are used and controlled by Home Assistant, and should not be overridden by integrations. + +| Name | Type | Default | Description +| ---- | ---- | ------- | ----------- +| enabled | boolean | `True` | Indicate if entity is enabled in the entity registry. It also returns `True` if the platform doesn't support the entity registry. Disabled entities will not be added to Home Assistant. + +## Standard attributes + +The following `device_state_attributes` are considered standard and should follow the convention below. The constant should be imported from `homeassistant/const.py`. + +| Name | Type | Unit | Constant | Description +| ---- | ---- | ---- | -------- | ----------- +| battery_charging | boolean | N/A | `ATTR_BATTERY_CHARGING` | Battery charging status of the entity, shown as a boolean `true` or `false`. If charging is not supported, then this attribute should not be created. +| battery_level | integer | % | `ATTR_BATTERY_LEVEL` | Battery level of the entity, shown as an integer percentage between 0-100. + +## Lifecycle hooks + +Use these lifecycle hooks to execute code when certain events happen to the entity. All lifecycle hooks are async methods. + +### `async_added_to_hass()` + +Called when an entity has their entity_id and hass object assigned, before it is written to the state machine for the first time. Example uses: restore the state, subscribe to updates or set callback/dispatch function/listener. + +### `async_will_remove_from_hass()` + +Called when an entity is about to be removed from Home Assistant. Example use: disconnect from the server or unsubscribe from updates. + +## Changing the entity model + +If you want to add a new feature to an entity or any of its subtypes (light, switch, etc), you will need to propose it first in our [architecture repo](https://github.com/home-assistant/architecture/issues). Only additions will be considered that are common features among various vendors. diff --git a/website/versioned_docs/version-0.102.0/frontend_external_auth.md b/website/versioned_docs/version-0.102.0/frontend_external_auth.md new file mode 100644 index 00000000..dade3ed6 --- /dev/null +++ b/website/versioned_docs/version-0.102.0/frontend_external_auth.md @@ -0,0 +1,68 @@ +--- +title: External Authentication +id: version-0.102.0-frontend_external_auth +original_id: frontend_external_auth +--- + +By default, the frontend will take care of its own authentication tokens. If none found, it will redirect the user to the login page and it will take care that the token is up to date. + +If you want to embed the Home Assistant frontend in an external app, you will want to store the authentication inside the app but make it available to the frontend. To support this, Home Assistant exposes an external authentication API. + +To activate this API, load the frontend with `?external_auth=1` appended to the URL. If this is passed in, Home Assistant will expect either `window.externalApp` (for Android) or `window.webkit.messageHandlers` (for iOS) to be defined containing the methods described below. + +## Get Access Token + +_This API has been introduced in Home Assistant 0.78._ + +When the frontend loads, it will request an access token from the external authentication. It does so by calling one of the following methods with an options object. The options object defines the callback method to be called with the response. + +```js +window.externalApp.getExternalAuth({ + callback: 'externalAuthSetToken' +}); +// or +window.webkit.messageHandlers.getExternalAuth.postMessage({ + callback: 'externalAuthSetToken' +}); +``` + +The response should contain a boolean if it was successful and an object containing an access token and the number of seconds that it will remain valid. Pass the response to the function defined in the options object. + +```js +// To be called by external app +window.externalAuthSetToken(true, { + "access_token": "qwere", + "expires_in": 1800 +}); + +// If unable to get new access token +window.externalAuthSetToken(false); +``` + +The frontend will call this method when the page first loads and whenever it needs a valid token but the previous received token has expired. + +## Revoke Token + +_This API has been introduced in Home Assistant 0.78._ + +When the user presses the logout button on the profile page, the external app will have to [revoke the refresh token](auth_api.md#revoking-a-refresh-token), and log the user out. + +```js +window.externalApp.revokeExternalAuth({ + callback: 'externalAuthRevokeToken' +}); +// or +window.webkit.messageHandlers.revokeExternalAuth.postMessage({ + callback: 'externalAuthRevokeToken' +}); +``` + +When done, the external app has to call the function defined in the options object. + +```js +// To be called by external app +window.externalAuthRevokeToken(true); + +// If unable to logout +window.externalAuthRevokeToken(false); +``` diff --git a/website/versioned_docs/version-0.102.0/hassio_addon_communication.md b/website/versioned_docs/version-0.102.0/hassio_addon_communication.md new file mode 100644 index 00000000..15a97cba --- /dev/null +++ b/website/versioned_docs/version-0.102.0/hassio_addon_communication.md @@ -0,0 +1,48 @@ +--- +title: Add-On Communication +id: version-0.102.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 [bearer token](https://developers.home-assistant.io/docs/en/auth_api.html#making-authenticated-requests) when making requests. + +For example `curl -X GET -H "Authorization: Bearer ${HASSIO_TOKEN}" -H "Content-Type: application/json" http://hassio/homeassistant/api/discovery_info` + +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 diff --git a/website/versioned_docs/version-0.102.0/lovelace_custom_card.md b/website/versioned_docs/version-0.102.0/lovelace_custom_card.md new file mode 100644 index 00000000..0ef4f4e5 --- /dev/null +++ b/website/versioned_docs/version-0.102.0/lovelace_custom_card.md @@ -0,0 +1,249 @@ +--- +title: Lovelace: Custom Cards +id: version-0.102.0-lovelace_custom_card +original_id: lovelace_custom_card +--- + +[Lovelace](https://www.home-assistant.io/lovelace/) is our new approach to defining your user interface for Home Assistant. We offer a lot of built-in cards, but you're not just limited to the ones that we decided to include in the Lovelace UI. You can build and use your own! + +## API + +You define your custom card as a [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements). It's up to you to decide how to render your DOM inside your element. You can use Polymer, Angular, Preact or any other popular framework (except for React – [more info on React here](https://custom-elements-everywhere.com/#react)). + +```js +const element = document.createElement('some-custom-card'); +``` + +Home Assistant will call `setConfig(config)` when the configuration changes (rare). If you throw an exception if the configuration is invalid, Lovelace will render an error card to notify the user. + +```js +try { + element.setConfig(config); +} catch (err) { + showErrorCard(err.message, config); +} +``` + +Home Assistant will set the `hass` property when the state of Home Assistant changes (frequent). Whenever the state changes, the component will have to update itself to represent the latest state. + +```js +element.hass = hass; +``` + +Your card can define a `getCardSize` method that returns the size of your card as a number. A height of 1 is equivalent to 50 pixels. This will help Home Assistant distribute the cards evenly over the columns. A card size of `1` will be assumed if the method is not defined. + +```js +if ('getCardSize' in element) { + return element.getCardSize(); +} else { + return 1; +} +``` + +## Defining your card + +Create a new file in your Home Assistant config dir as `/www/content-card-example.js` and put in the following contents: + +```js +class ContentCardExample extends HTMLElement { + set hass(hass) { + if (!this.content) { + const card = document.createElement('ha-card'); + card.header = 'Example card'; + this.content = document.createElement('div'); + this.content.style.padding = '0 16px 16px'; + card.appendChild(this.content); + this.appendChild(card); + } + + const entityId = this.config.entity; + const state = hass.states[entityId]; + const stateStr = state ? state.state : 'unavailable'; + + this.content.innerHTML = ` + The state of ${entityId} is ${stateStr}! +

+ + `; + } + + setConfig(config) { + if (!config.entity) { + throw new Error('You need to define an entity'); + } + this.config = config; + } + + // The height of your card. Home Assistant uses this to automatically + // distribute all cards over the available columns. + getCardSize() { + return 3; + } +} + +customElements.define('content-card-example', ContentCardExample); +``` + +## Referencing your new card + +In our example card we defined a card with the tag `content-card-example` (see last line), so our card type will be `custom:content-card-example`. And because you created the file in your `/www` directory, it will be accessible in your browser via the url `/local/` (if you have recently added the www folder you will need to re-start home assistant for files to be picked up). + +```yaml +# Example Lovelace configuration +resources: + - url: /local/content-card-example.js + type: js +views: +- name: Example + cards: + - type: "custom:content-card-example" + entity: input_boolean.switch_tv +``` + +## Advanced example + +Resources to load in Lovelace can be imported as a JS script, an HTML import or as a JS module import. Below is an example of a custom card using JS modules that does all the fancy things. + +![Screenshot of the wired card](/img/en/frontend/lovelace-ui-custom-card-screenshot.png) + +Create a new file in your Home Assistant config dir as `/www/wired-cards.js` and put in the following contents: + +```js +import "https://unpkg.com/wired-card@0.8.1/wired-card.js?module"; +import "https://unpkg.com/wired-toggle@0.8.0/wired-toggle.js?module"; +import { + LitElement, + html, + css +} from "https://unpkg.com/lit-element@2.0.1/lit-element.js?module"; + +function loadCSS(url) { + const link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = url; + document.head.appendChild(link); +} + +loadCSS("https://fonts.googleapis.com/css?family=Gloria+Hallelujah"); + +class WiredToggleCard extends LitElement { + static get properties() { + return { + hass: {}, + config: {} + }; + } + + render() { + return html` + + ${this.config.entities.map(ent => { + const stateObj = this.hass.states[ent]; + return stateObj + ? html` +
+ ${stateObj.attributes.friendly_name} + +
+ ` + : html` +
Entity ${ent} not found.
+ `; + })} +
+ `; + } + + setConfig(config) { + if (!config.entities) { + throw new Error("You need to define entities"); + } + this.config = config; + } + + // The height of your card. Home Assistant uses this to automatically + // distribute all cards over the available columns. + getCardSize() { + return this.config.entities.length + 1; + } + + _toggle(state) { + this.hass.callService("homeassistant", "toggle", { + entity_id: state.entity_id + }); + } + + static get styles() { + return css` + :host { + font-family: "Gloria Hallelujah", cursive; + } + wired-card { + background-color: white; + padding: 16px; + display: block; + font-size: 18px; + } + .state { + display: flex; + justify-content: space-between; + padding: 8px; + align-items: center; + } + .not-found { + background-color: yellow; + font-family: sans-serif; + font-size: 14px; + padding: 8px; + } + wired-toggle { + margin-left: 8px; + } + `; + } +} +customElements.define("wired-toggle-card", WiredToggleCard); +``` + +And for your configuration: + +```yaml +# Example Lovelace configuration +resources: + - url: /local/wired-cards.js + type: module +views: +- name: Example + cards: + - type: "custom:wired-toggle-card" + entities: + - input_boolean.switch_ac_kitchen + - input_boolean.switch_ac_livingroom + - input_boolean.switch_tv +``` + +## Recommended Design Elements + +We are currently migrating from using Paper Elements to MWC (Material Web Component) Elements. + +If an element exists in the below repository for MWC. We recommended using it. + +- [MWC (Material Web Components)](https://material-components.github.io/material-components-web-components/demos/index.html) + +If an element does not exist in MWC, we default to using Paper Elements. + +- [Paper Elements](https://www.webcomponents.org/collection/PolymerElements/paper-elements) + +## Advanced Resources + +Community Maintained Boilerplate Card - Advanced Template (Typescript, Rollup, Linting, etc.) + +- [Boilerplate Card](https://github.com/custom-cards/boilerplate-card) + +Developer Documentation for [HACS](https://hacs.xyz/) (Home Assistant Community Store). + +- [HACS Plugin Docs](https://hacs.xyz/docs/publish/plugin) diff --git a/website/versions.json b/website/versions.json index 828328bd..c37e0891 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1,4 +1,5 @@ [ + "0.102.0", "0.101.0", "0.100.0", "0.99.3",