mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-11-10 11:29:53 +00:00
Restructure docs APIs (#504)
This commit is contained in:
11
docs/api/native-app-integration.md
Normal file
11
docs/api/native-app-integration.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: "Native App Integration"
|
||||
sidebar_label: "Introduction"
|
||||
---
|
||||
|
||||
This guide describes how to build a native Home Assistant app that communicates with Home Assistant and offers a seamless integration. Below is a list of the things that we will discuss in this guide.
|
||||
|
||||
- Allow the user to establish a connection and authenticate with their own Home Assistant instance.
|
||||
- Send location and device info back to Home Assistant.
|
||||
- Call services, fire events and render templates.
|
||||
- A view to control the house via an authenticated webview.
|
||||
237
docs/api/native-app-integration/notifications.md
Normal file
237
docs/api/native-app-integration/notifications.md
Normal file
@@ -0,0 +1,237 @@
|
||||
---
|
||||
title: "Push Notifications"
|
||||
---
|
||||
|
||||
The `mobile_app` component has a notify platform built in that allows for a generic way to send push notifications to your users without requiring installation of an external custom component.
|
||||
|
||||
## Enabling push notifications
|
||||
|
||||
To enable the notify platform for your application, you must set two keys in the `app_data` object during the initial registration or later update of an existing registration.
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `push_token` | string | A push notification token unique to your users device. For example, this could be a APNS token or a FCM Instance ID/token.
|
||||
| `push_url` | string | The URL on your server that push notifications will be HTTP POSTed to.
|
||||
|
||||
You should advise the user to restart Home Assistant after you set these keys in order for them to see the notify target. It will have the format `notify.mobile_app_<safed_device_name>`.
|
||||
|
||||
## Deploying a server component
|
||||
|
||||
The notify platform doesn't concern itself with how to notify your users. It simply forwards a notification to your external server where you should actually handle the request.
|
||||
This approach allows you to maintain full control over your push notification infrastructure.
|
||||
|
||||
See the next section of this document for an example server implementation of a push notification forwarder that uses Firebase Cloud Functions and Firebase Cloud Messaging.
|
||||
|
||||
Your server should accept a HTTP POST payload like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello World",
|
||||
"title": "Test message sent via mobile_app.notify",
|
||||
"push_token": "my-secure-token",
|
||||
"registration_info": {
|
||||
"app_id": "io.home-assistant.iOS",
|
||||
"app_version": "1.0.0",
|
||||
"os_version": "12.2"
|
||||
},
|
||||
"data": {
|
||||
"key": "value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It should respond with a 201 status code assuming the notification was queued for delivery successfully.
|
||||
|
||||
### Errors
|
||||
|
||||
If an error occurs you should return a description of what went wrong with a status code _other than_ 201 or 429. An error response must be a JSON object and can contain one of the following keys:
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `errorMessage` | string | If provided, it will be appended to a preset error message. For example, if `errorMessage` is "Could not communicate with Apple" it will be output in the log like "Internal server error, please try again later: Could not communicate with Apple"
|
||||
| `message` | string | If provided, it will be output directly to the logs at the warning log level.
|
||||
|
||||
No matter what key you use, you should try to be as descriptive as possible about what went wrong and, if possible, how the user can fix it.
|
||||
|
||||
### Rate limits
|
||||
|
||||
The notify platform also supports exposing rate limits to users. Home Assistant suggests you implement a conservative rate limit to keep your costs low and also so that users don't overload themselves with too many notifications.
|
||||
For reference, Home Assistant Companion has a maximum sendable notifications per 24 hours of 150 notifications. The rate limit resets for all users at midnight, UTC. You of course are free to use whatever configuration for your own rate limiting.
|
||||
|
||||
If you choose to implement rate limiting, your successful server response should look like the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"rateLimits": {
|
||||
"successful": 1,
|
||||
"errors": 5,
|
||||
"maximum": 150,
|
||||
"resetsAt": "2019-04-08T00:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `successful` | integer | The number of successful push notifications the user has sent during the rate limit period.
|
||||
| `errors` | integer | The number of failed push notifications the user has sent during the rate limit period.
|
||||
| `maximum` | integer | The maximum number of push notifications the user can send during the users rate limit period.
|
||||
| `resetsAt` | ISO8601 timestamp | The timestamp that the users rate limit period expires at. Must be provided in the UTC timezone.
|
||||
|
||||
The rate limits will be output to the log at the warning log level after every notification is successfully sent. Home Assistant will also output the exact time remaining until the rate limit period resets.
|
||||
|
||||
Once the user hits their maximum amount of notifications sent in the rate limit period, you should start responding with a 429 status code until the rate limit period expires. The response object can optionally contain a key, `message` which will be output to the Home Assistant log instead of the standard error message.
|
||||
|
||||
The notify platform does not itself implement any kind of rate limit protections. Users will be able to keep sending you notifications, so you should reject them with a 429 status code as early in your logic as possible.
|
||||
|
||||
## Example server implementation
|
||||
The below code is a Firebase Cloud Function that forwards notifications to Firebase Cloud Messaging. To deploy this, you should create a new Firestore database named `rateLimits`. Then, you can deploy the following code.
|
||||
Also, ensure that you have properly configured your project with the correct authentication keys for APNS and FCM.
|
||||
|
||||
```javascript
|
||||
'use strict';
|
||||
|
||||
const functions = require('firebase-functions');
|
||||
const admin = require('firebase-admin');
|
||||
admin.initializeApp();
|
||||
|
||||
var db = admin.firestore();
|
||||
|
||||
const MAX_NOTIFICATIONS_PER_DAY = 150;
|
||||
|
||||
exports.sendPushNotification = functions.https.onRequest(async (req, res) => {
|
||||
console.log('Received payload', req.body);
|
||||
var today = getToday();
|
||||
var token = req.body.push_token;
|
||||
var ref = db.collection('rateLimits').doc(today).collection('tokens').doc(token);
|
||||
|
||||
var payload = {
|
||||
notification: {
|
||||
body: req.body.message,
|
||||
},
|
||||
token: token,
|
||||
};
|
||||
|
||||
if(req.body.title) {
|
||||
payload.notification.title = req.body.title;
|
||||
}
|
||||
|
||||
if(req.body.data) {
|
||||
if(req.body.data.android) {
|
||||
payload.android = req.body.data.android;
|
||||
}
|
||||
if(req.body.data.apns) {
|
||||
payload.apns = req.body.data.apns;
|
||||
}
|
||||
if(req.body.data.data) {
|
||||
payload.data = req.body.data.data;
|
||||
}
|
||||
if(req.body.data.webpush) {
|
||||
payload.webpush = req.body.data.webpush;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Notification payload', JSON.stringify(payload));
|
||||
|
||||
var docExists = false;
|
||||
var docData = {
|
||||
deliveredCount: 0,
|
||||
errorCount: 0,
|
||||
totalCount: 0,
|
||||
};
|
||||
|
||||
try {
|
||||
let currentDoc = await ref.get();
|
||||
docExists = currentDoc.exists;
|
||||
if(currentDoc.exists) {
|
||||
docData = currentDoc.data();
|
||||
}
|
||||
} catch(err) {
|
||||
console.error('Error getting document!', err);
|
||||
return handleError(res, 'getDoc', err);
|
||||
}
|
||||
|
||||
if(docData.deliveredCount > MAX_NOTIFICATIONS_PER_DAY) {
|
||||
return res.status(429).send({
|
||||
errorType: 'RateLimited',
|
||||
message: 'The given target has reached the maximum number of notifications allowed per day. Please try again later.',
|
||||
target: token,
|
||||
rateLimits: getRateLimitsObject(docData),
|
||||
});
|
||||
}
|
||||
|
||||
docData.totalCount = docData.totalCount + 1;
|
||||
|
||||
var messageId;
|
||||
try {
|
||||
messageId = await admin.messaging().send(payload);
|
||||
docData.deliveredCount = docData.deliveredCount + 1;
|
||||
} catch(err) {
|
||||
docData.errorCount = docData.errorCount + 1;
|
||||
await setRateLimitDoc(ref, docExists, docData, res);
|
||||
return handleError(res, 'sendNotification', err);
|
||||
}
|
||||
|
||||
console.log('Successfully sent message:', messageId);
|
||||
|
||||
await setRateLimitDoc(ref, docExists, docData, res);
|
||||
|
||||
return res.status(201).send({
|
||||
messageId: messageId,
|
||||
sentPayload: payload,
|
||||
target: token,
|
||||
rateLimits: getRateLimitsObject(docData),
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
async function setRateLimitDoc(ref, docExists, docData, res) {
|
||||
try {
|
||||
if(docExists) {
|
||||
console.log('Updating existing doc!');
|
||||
await ref.update(docData);
|
||||
} else {
|
||||
console.log('Creating new doc!');
|
||||
await ref.set(docData);
|
||||
}
|
||||
} catch(err) {
|
||||
if(docExists) {
|
||||
console.error('Error updating document!', err);
|
||||
} else {
|
||||
console.error('Error creating document!', err);
|
||||
}
|
||||
return handleError(res, 'setDocument', err);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleError(res, step, incomingError) {
|
||||
if (!incomingError) return null;
|
||||
console.error('InternalError during', step, incomingError);
|
||||
return res.status(500).send({
|
||||
errorType: 'InternalError',
|
||||
errorStep: step,
|
||||
message: incomingError.message,
|
||||
});
|
||||
}
|
||||
|
||||
function getToday() {
|
||||
var today = new Date();
|
||||
var dd = String(today.getDate()).padStart(2, '0');
|
||||
var mm = String(today.getMonth() + 1).padStart(2, '0');
|
||||
var yyyy = today.getFullYear();
|
||||
return yyyy + mm + dd;
|
||||
}
|
||||
|
||||
function getRateLimitsObject(doc) {
|
||||
var d = new Date();
|
||||
return {
|
||||
successful: (doc.deliveredCount || 0),
|
||||
errors: (doc.errorCount || 0),
|
||||
total: (doc.totalCount || 0),
|
||||
maximum: MAX_NOTIFICATIONS_PER_DAY,
|
||||
remaining: (MAX_NOTIFICATIONS_PER_DAY - doc.deliveredCount),
|
||||
resetsAt: new Date(d.getFullYear(), d.getMonth(), d.getDate()+1)
|
||||
};
|
||||
}
|
||||
```
|
||||
255
docs/api/native-app-integration/sending-data.md
Normal file
255
docs/api/native-app-integration/sending-data.md
Normal file
@@ -0,0 +1,255 @@
|
||||
---
|
||||
title: "Sending data home"
|
||||
---
|
||||
|
||||
Once you have registered your app with the mobile app component, you can start interacting with Home Assistant via the provided webhook information.
|
||||
|
||||
The first step is to turn the returned webhook ID into a full URL: `<instance_url>/api/webhook/<webhook_id>`. This will be the only url that we will need for all our interactions. The webhook endpoint will not require authenticated requests.
|
||||
|
||||
If you were provided a Cloudhook URL during registration, you should use that by default and only fall back to a constructed URL as described above if that request fails.
|
||||
|
||||
If you were provided a remote UI URL during registration, you should use that as the `instance_url` when constructing a URL and only fallback to the user provided URL if the remote UI URL fails.
|
||||
|
||||
To summarize, here's how requests should be made:
|
||||
|
||||
1. If you have a Cloudhook URL, use that until a request fails. When a request fails, go to step 2.
|
||||
2. If you have a remote UI URL, use that to construct a webhook URL: `<remote_ui_url>/api/webhook/<webhook_id>`. When a request fails, go to step 3.
|
||||
3. Construct a webhook URL using the instance URL provided during setup: `<instance_url>/api/webhook/<webhook_id>`.
|
||||
|
||||
## Short note on instance URLs
|
||||
|
||||
Some users have configured Home Assistant to be available outside of their home network using a dynamic DNS service. There are some routers that don't support hairpinning / NAT loopback: a device sending data from inside the routers network, via the externally configured DNS service, to Home Asisstant, which also resides inside the local network.
|
||||
|
||||
To work around this, the app should record which WiFi SSID is the users home network, and use a direct connection when connected to the home WiFi network.
|
||||
|
||||
## Interaction basics
|
||||
|
||||
### Request
|
||||
|
||||
All interaction will be done by making HTTP POST requests to the webhook url. These requests do not need to contain authentication.
|
||||
|
||||
The payload format depends on the type of interaction, but it all shares a common base:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "<type of message>",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
If you received a `secret` during registration, you **MUST** encrypt your message and put it in the payload like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "encrypted",
|
||||
"encrypted": true,
|
||||
"encrypted_data": "<encrypted message>"
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
As a general rule, expect to receive a 200 response for all your requests. There are a few cases in which you will receive another code:
|
||||
|
||||
- You will receive a 400 status code if your JSON is invalid. However, you will not receive this error if the encrypted JSON is invalid.
|
||||
- You will receive a 201 when creating a sensor
|
||||
- If you receive a 404, the `mobile_app` component most likely isn't loaded.
|
||||
- Receiving a 410 means the integration has been deleted. You should notify the user and most likely register again.
|
||||
|
||||
## Implementing encryption
|
||||
|
||||
`mobile_app` supports two way encrypted communication via [Sodium](https://libsodium.gitbook.io/doc/).
|
||||
|
||||
:::info
|
||||
Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more.
|
||||
:::
|
||||
|
||||
### Choosing a library
|
||||
Libraries that wrap Sodium exist for most modern programming languages and platforms. Sodium itself is written in C.
|
||||
|
||||
Here are the libraries we suggest using, although you should feel free to use whatever works well for you.
|
||||
|
||||
- Swift/Objective-C: [swift-sodium](https://github.com/jedisct1/swift-sodium) (official library maintained by Sodium developers).
|
||||
|
||||
For other languages, please see the list of [Bindings for other languages](https://download.libsodium.org/doc/bindings_for_other_languages). If more than one choice is available, we recommend using the choice most recently updated as well as most peer reviewed (a easy way to check this is seeing how many GitHub stars a project has).
|
||||
|
||||
### Configuration
|
||||
|
||||
We use the [secret-key cryptography](https://download.libsodium.org/doc/secret-key_cryptography) features of Sodium to encrypt and decrypt payloads. All payloads are JSON encoded in Base64. For Base64 type, use `sodium_base64_VARIANT_ORIGINAL` (that is, "original", no padding, not URL safe).
|
||||
|
||||
### Signaling encryption support
|
||||
|
||||
There are two ways to enable encryption support:
|
||||
|
||||
- **During initial registration** you set `supports_encryption` to `true`.
|
||||
- **After initial registration** you call the `enable_encryption` webhook action.
|
||||
|
||||
The Home Assistant instance must be able to install `libsodium` to enable encryption. Confirm that you should make all future webhook requests encrypted by the presence of the key `secret` in the initial registration or enable encryption response.
|
||||
|
||||
You must store this secret forever. There is no way to recover it via the Home Assistant UI and you should **not** ask users to investigate hidden storage files to re-enter the encryption key. You should create a new registration if encryption ever fails and alert the user.
|
||||
|
||||
A registration may not initially support encryption due to a lack of Sodium/NaCL on the Home Assistant Core side. You should always strive to encrypt communications if possible. Therefore, we politely request that from time to time you attempt to enable encryption automatically or allow the user to manually enable encryption via a button in your app. That way, they can attempt to first fix whatever error is causing Sodium/NaCL to be uninstallable and then have a encrypted registration later. Home Assistant Core will log exact details if Sodium/NaCL is uninstallable.
|
||||
|
||||
## Update device location
|
||||
|
||||
This message will inform Home Assistant of new location information.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "update_location",
|
||||
"data": {
|
||||
"gps": [12.34, 56.78],
|
||||
"gps_accuracy": 120,
|
||||
"battery": 45
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `location_name` | string | Name of the zone the device is in.
|
||||
| `gps` | latlong | Current location as latitude and longitude.
|
||||
| `gps_accuracy` | int | GPS accurracy in meters. Must be greater than 0.
|
||||
| `battery` | int | Percentage of battery the device has left. Must be greater than 0.
|
||||
| `speed` | int | Speed of the device in meters per second. Must be greater than 0.
|
||||
| `altitude` | int | Altitude of the device in meters. Must be greater than 0.
|
||||
| `course` | int | The direction in which the device is traveling, measured in degrees and relative to due north. Must be greater than 0.
|
||||
| `vertical_accuracy` | int | The accuracy of the altitude value, measured in meters. Must be greater than 0.
|
||||
|
||||
## Call a service
|
||||
|
||||
Call a service in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "call_service",
|
||||
"data": {
|
||||
"domain": "light",
|
||||
"service": "turn_on",
|
||||
"service_data": {
|
||||
"entity_id": "light.kitchen"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `domain` | string | The domain of the service
|
||||
| `service` | string | The service name
|
||||
| `service_data` | dict | The data to send to the service
|
||||
|
||||
## Fire an event
|
||||
|
||||
Fire an event in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "fire_event",
|
||||
"data": {
|
||||
"event_type": "my_custom_event",
|
||||
"event_data": {
|
||||
"something": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `event_type` | string | Type of the event to fire
|
||||
| `event_data` | string | Date of the event to fire
|
||||
|
||||
## Render templates
|
||||
|
||||
Renders one or more templates and returns the result(s).
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "render_template",
|
||||
"data": {
|
||||
"my_tpl": {
|
||||
"template": "Hello {{ name }}, you are {{ states('person.paulus') }}.",
|
||||
"variables": {
|
||||
"name": "Paulus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`data` must contain a map of `key`: `dictionary`. Results will be returned like `{"my_tpl": "Hello Paulus, you are home"}`. This allows for rendering multiple templates in a single call.
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | ---- | -----------
|
||||
| `template` | string | The template to render
|
||||
| `variables` | Dict | The extra template variables to include.
|
||||
|
||||
## Update registration
|
||||
|
||||
Update your app registration. Use this if the app version changed or any of the other values.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "update_registration",
|
||||
"data": {
|
||||
"app_data": {
|
||||
"push_token": "abcd",
|
||||
"push_url": "https://push.mycool.app/push"
|
||||
},
|
||||
"app_version": "2.0.0",
|
||||
"device_name": "Robbies iPhone",
|
||||
"manufacturer": "Apple, Inc.",
|
||||
"model": "iPhone XR",
|
||||
"os_version": "23.02"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All keys are optional.
|
||||
|
||||
| Key | Type | Description
|
||||
| --- | --- | --
|
||||
| `app_data` | Dict | App data can be used if the app has a supporting component that extends mobile_app functionality or wishes to enable the notification platform.
|
||||
| `app_version` | string | Version of the mobile app.
|
||||
| `device_name` | string | Name of the device running the app.
|
||||
| `manufacturer` | string | The manufacturer of the device running the app.
|
||||
| `model` | string | The model of the device running the app.
|
||||
| `os_version` | string | The OS version of the device running the app.
|
||||
|
||||
## Get zones
|
||||
|
||||
Get all enabled zones.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "get_zones"
|
||||
}
|
||||
```
|
||||
|
||||
## Get config
|
||||
|
||||
Returns a version of `/api/config` with values useful for configuring your app.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "get_config"
|
||||
}
|
||||
```
|
||||
|
||||
## Enable encryption
|
||||
|
||||
_This requires Home Assistant 0.106 or later._
|
||||
|
||||
Enables encryption support for an existing registration
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "enable_encryption"
|
||||
}
|
||||
```
|
||||
|
||||
There are two errors you may receive:
|
||||
|
||||
- `encryption_already_enabled` - Encryption is already enabled for this registration
|
||||
- `encryption_not_available` - Sodium/NaCL is unable to be installed. Cease all future attempts to enable encryption.
|
||||
78
docs/api/native-app-integration/sensors.md
Normal file
78
docs/api/native-app-integration/sensors.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: "Sensors"
|
||||
---
|
||||
|
||||
The `mobile_app` component supports exposing custom sensors that can be managed entirely via your app.
|
||||
|
||||
## Registering a sensor
|
||||
|
||||
All sensors must be registered before they can get updated. You can only register one sensor at a time, unlike updating sensors.
|
||||
|
||||
To register a sensor, make a request to the webhook like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"device_class": "battery",
|
||||
"icon": "mdi:battery",
|
||||
"name": "Battery State",
|
||||
"state": "12345",
|
||||
"type": "sensor",
|
||||
"unique_id": "battery_state",
|
||||
"unit_of_measurement": "%"
|
||||
},
|
||||
"type": "register_sensor"
|
||||
}
|
||||
```
|
||||
|
||||
The valid keys are:
|
||||
|
||||
| Key | Type | Required | Description |
|
||||
|---------------------|-------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| attributes | object | No | Attributes to attach to the sensor |
|
||||
| device_class | string | No | One of the valid device classes. [Binary Sensor Classes](https://www.home-assistant.io/components/binary_sensor/#device-class), [Sensor Classes](https://www.home-assistant.io/components/sensor/#device-class) |
|
||||
| icon | Material Design Icon (string) | No | Must be prefixed `mdi:`. If not provided, default value is `mdi:cellphone` |
|
||||
| name | string | Yes | The name of the sensor |
|
||||
| state | bool, float, int, string | Yes | The state of the sensor |
|
||||
| type | string | Yes | The type of the sensor. Must be one of `binary_sensor` or `sensor` |
|
||||
| unique_id | string | Yes | An identifier unique to this installation of your app. You'll need this later. Usually best when its a safe version of the sensor name |
|
||||
| unit_of_measurement | string | No | The unit of measurement for the sensor |
|
||||
|
||||
Sensors will appear as soon as they are registered.
|
||||
|
||||
## Updating a sensor
|
||||
|
||||
Once a sensor has been registered, you need to update it. This is very similar to registering it, but you can update all your sensors at the same time.
|
||||
|
||||
For example, to update the sensor we registered above, you would send this:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"attributes": {
|
||||
"hello": "world"
|
||||
},
|
||||
"icon": "mdi:battery",
|
||||
"state": 123,
|
||||
"type": "sensor",
|
||||
"unique_id": "battery_state"
|
||||
}
|
||||
],
|
||||
"type": "update_sensor_states"
|
||||
}
|
||||
```
|
||||
|
||||
Only some of the keys are allowed during updates:
|
||||
|
||||
| Key | Type | Required | Description |
|
||||
|---------------------|-------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| attributes | object | No | Attributes to attach to the sensor |
|
||||
| icon | Material Design Icon (string) | No | Must be prefixed `mdi:` |
|
||||
| state | bool, float, int, string | Yes | The state of the sensor |
|
||||
| type | string | Yes | The type of the sensor. Must be one of `binary_sensor` or `sensor` |
|
||||
| unique_id | string | Yes | An identifier unique to this installation of your app. You'll need this later. Usually best when its a safe version of the sensor name |
|
||||
|
||||
87
docs/api/native-app-integration/setup.md
Normal file
87
docs/api/native-app-integration/setup.md
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: "Connecting to an instance"
|
||||
---
|
||||
|
||||
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
|
||||
{
|
||||
"device_id": "ABCDEFGH",
|
||||
"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 |
|
||||
| --------------------- | -------- | ------ | --------------------------------------------------------------------------------------------------- |
|
||||
| `device_id` | V | string | A unique identifier for this device. New in Home Assistant 0.104 |
|
||||
| `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](app_integration_sending_data.md#implementing-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. |
|
||||
9
docs/api/native-app-integration/webview.md
Normal file
9
docs/api/native-app-integration/webview.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: "Authenticated Webview"
|
||||
---
|
||||
|
||||
Your application already asked the user to authenticate. This means that your app should not ask the user to authenticate again when they open the Home Assistant UI.
|
||||
|
||||
To make this possible, the Home Assistant UI supports [external authentication](frontend_external_auth). This allows your app to provide hooks so that the frontend will ask your app for access tokens.
|
||||
|
||||
Note that this feature requires a direct connection to the instance.
|
||||
556
docs/api/rest.md
Normal file
556
docs/api/rest.md
Normal file
@@ -0,0 +1,556 @@
|
||||
---
|
||||
title: "REST API"
|
||||
---
|
||||
|
||||
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`:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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
|
||||
```
|
||||
|
||||
```shell
|
||||
$ 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
|
||||
```
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
|
||||
-H "Content-Type: application/json" \
|
||||
http://localhost:8123/api/camera_proxy/camera.my_sample_camera?time=1462653861261 -o image.jpg
|
||||
```
|
||||
|
||||
#### POST /api/states/<entity_id>
|
||||
|
||||
Updates or creates a state. You can create any state that you want, it does not have to be backed by an entity in Home Assistant.
|
||||
|
||||
:::info
|
||||
This endpoint sets the representation of a device within Home Asistant and will not communicate with the actual device. To communicate with the device, use the [POST /api/services/<domain>/<service>](#post-apiservicesltdomainltservice) endpoint.
|
||||
:::
|
||||
|
||||
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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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:
|
||||
|
||||
```shell
|
||||
$ 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
|
||||
```
|
||||
|
||||
:::tip
|
||||
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:
|
||||
|
||||
```shell
|
||||
$ 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. Needs config integration enabled.
|
||||
|
||||
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."
|
||||
}
|
||||
```
|
||||
76
docs/api/server-sent-events.md
Normal file
76
docs/api/server-sent-events.md
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: "Server-sent events"
|
||||
---
|
||||
|
||||
The [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) feature is a one-way channel from your Home Assistant server to a client which is acting as a consumer. For a bi-directional streaming API, check out the [WebSocket API](external_api_websocket.md).
|
||||
|
||||
The URI that is generating the data is `/api/stream`.
|
||||
|
||||
A requirement on the client-side is existing support for the [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) interface.
|
||||
|
||||
There are various ways to access the stream. If you have not set an `api_password` in the [`http`](https://www.home-assistant.io/components/http/) section of your `configuration.yaml` file then you use your modern browser to read the messages. A command-line option is `curl`:
|
||||
|
||||
```shell
|
||||
$ curl -X GET -H 'Authorization: Bearer ABCDEFGH' \
|
||||
-H "Content-Type: application/json" http://localhost:8123/api/stream
|
||||
```
|
||||
|
||||
:::caution
|
||||
Will no longer work with the new Authentication system.
|
||||
:::
|
||||
|
||||
You can create a convenient view for this by creating an HTML file (`sse.html`) in the `www` folder of your Home Assistant configuration directory (`.homeassistant`). Paste this snippet into the file:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Getting Home Assistant server events</h1>
|
||||
<div id="events"></div>
|
||||
<script type="text/javascript">
|
||||
var source = new EventSource("/api/stream?api_password=YOUR_PASSWORD");
|
||||
source.onmessage = function(event) {
|
||||
document.getElementById("events").innerHTML += event.data + "<br>";
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Visit [http://localhost:8123/local/sse.html](http://localhost:8123/local/sse.html) to see the stream of events.
|
||||
|
||||
## Examples
|
||||
|
||||
A simple way to consume server-sent events is to use a command-line http client like [httpie](https://httpie.org/). Installation info is on the site (if you use Homebrew, it's `brew install httpie`). Once installed, run this snippet from your terminal:
|
||||
|
||||
```shell
|
||||
$ http --stream http://localhost:8123/api/stream 'Authorization:Bearer ABCDEFGH' content-type:application/json
|
||||
```
|
||||
|
||||
### Website
|
||||
|
||||
:::caution
|
||||
Will no longer work with the new Authentication system.
|
||||
:::
|
||||
|
||||
The [home-assistant-sse](https://github.com/fabaff/home-assistant-sse) repository contains a more advanced example.
|
||||
|
||||
### Python
|
||||
|
||||
If you want to test the server-sent events without creating a website, the Python module [`sseclient` ](https://pypi.python.org/pypi/sseclient/) can help. To install (assuming Python and pip3 are already installed):
|
||||
|
||||
```shell
|
||||
$ pip3 install sseclient
|
||||
```
|
||||
|
||||
A simple script to consume SSE in Python looks like this:
|
||||
|
||||
```python
|
||||
from sseclient import SSEClient
|
||||
|
||||
auth = {"Authorization": "Bearer ABCDEFGH"}
|
||||
messages = SSEClient("http://localhost:8123/api/stream", headers=auth)
|
||||
|
||||
for msg in messages:
|
||||
print(msg)
|
||||
```
|
||||
435
docs/api/websocket.md
Normal file
435
docs/api/websocket.md
Normal file
@@ -0,0 +1,435 @@
|
||||
---
|
||||
title: "WebSocket API"
|
||||
---
|
||||
|
||||
Home Assistant contains a WebSocket API. This API can be used to stream information from a Home Assistant instance to any client that implements WebSockets. Implementations in different languages:
|
||||
|
||||
- [JavaScript](https://github.com/home-assistant/home-assistant-js-websocket) - powers the frontend
|
||||
- [Python](https://raw.githubusercontent.com/home-assistant/home-assistant-dev-helper/master/ha-websocket-client.py) - CLI client using [`asyncws`](https://async-websockets.readthedocs.io/en/latest/)
|
||||
- [JavaScript/HTML](https://raw.githubusercontent.com/home-assistant/home-assistant-dev-helper/master/ha-websocket.html) - WebSocket connection in your browser
|
||||
|
||||
Connect your websocket implementation to `ws://localhost:8123/api/websocket`. You will need a valid access token.
|
||||
|
||||
If you are not using the [`frontend`](https://www.home-assistant.io/components/frontend/) in your setup then you need to add the [`websocket_api` component](https://www.home-assistant.io/components/websocket_api/) to your `configuration.yaml` file to use the WebSocket API.
|
||||
|
||||
## Server states
|
||||
|
||||
1. Client connects.
|
||||
1. Authentication phase starts.
|
||||
- Server sends `auth_required` message.
|
||||
- Client sends `auth` message.
|
||||
- If `auth` message correct: go to 3.
|
||||
- Server sends `auth_invalid`. Go to 6.
|
||||
1. Send `auth_ok` message
|
||||
1. Authentication phase ends.
|
||||
1. Command phase starts.
|
||||
1. Client can send commands.
|
||||
1. Server can send results of previous commands.
|
||||
1. Client or server disconnects session.
|
||||
|
||||
During the command phase, the client attaches a unique identifier to each message. The server will add this identifier to each message so that the client can link each message to its origin.
|
||||
|
||||
## Message format
|
||||
|
||||
Each API message is a JSON serialized object containing a `type` key. After the authentication phase messages also must contain an `id`, an integer that contains the number of interactions.
|
||||
|
||||
Example of an auth message:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth",
|
||||
"access_token": "ABCDEFGHIJKLMNOPQ"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 5,
|
||||
"type":"event",
|
||||
"event":{
|
||||
"data":{},
|
||||
"event_type":"test_event",
|
||||
"time_fired":"2016-11-26T01:37:24.265429+00:00",
|
||||
"origin":"LOCAL"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication phase
|
||||
|
||||
When a client connects to the server, the server will test if the client is authenticated. Authentication will not be necessary if no api_password is set or if the user fulfills one of the other criteria for authentication (trusted network, password in url/header).
|
||||
|
||||
If no authentication is needed, the authentication phase will complete and the server will send an `auth_ok` message.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_ok"
|
||||
}
|
||||
```
|
||||
|
||||
If authentication is necessary, the server sends out `auth_required`.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_required"
|
||||
}
|
||||
```
|
||||
|
||||
This means that the next message from the client should be an auth message. You can authorize with an access token.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth",
|
||||
"access_token": "ABCDEFGH"
|
||||
}
|
||||
```
|
||||
|
||||
For now, we also support authentication with an API password (legacy auth).
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth",
|
||||
"api_password": "supersecret"
|
||||
}
|
||||
```
|
||||
|
||||
If the client supplies valid authentication, the authentication phase will complete by the server sending the `auth_ok` message:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_ok"
|
||||
}
|
||||
```
|
||||
|
||||
If the data is incorrect, the server will reply with `auth_invalid` message and disconnect the session.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_invalid",
|
||||
"message": "Invalid password"
|
||||
}
|
||||
```
|
||||
|
||||
## Command phase
|
||||
|
||||
During this phase the client can give commands to the server. The server will respond to each command with a `result` message indicating when the command is done and if it was successful.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 6,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
// Can contain extra result info
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
## Subscribe to events
|
||||
|
||||
The command `subscribe_events` will subscribe your client to the event bus. You can either listen to all events or to a specific event type. If you want to listen to multiple event types, you will have to send multiple `subscribe_events` commands.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 18,
|
||||
"type": "subscribe_events",
|
||||
// Optional
|
||||
"event_type": "state_changed"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message to indicate that the subscription is active.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 18,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
For each event that matches, the server will send a message of type `event`. The `id` in the message will point at the original `id` of the `listen_event` command.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 18,
|
||||
"type":"event",
|
||||
"event":{
|
||||
"data":{
|
||||
"entity_id":"light.bed_light",
|
||||
"new_state":{
|
||||
"entity_id":"light.bed_light",
|
||||
"last_changed":"2016-11-26T01:37:24.265390+00:00",
|
||||
"state":"on",
|
||||
"attributes":{
|
||||
"rgb_color":[
|
||||
254,
|
||||
208,
|
||||
0
|
||||
],
|
||||
"color_temp":380,
|
||||
"supported_features":147,
|
||||
"xy_color":[
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"brightness":180,
|
||||
"white_value":200,
|
||||
"friendly_name":"Bed Light"
|
||||
},
|
||||
"last_updated":"2016-11-26T01:37:24.265390+00:00"
|
||||
},
|
||||
"old_state":{
|
||||
"entity_id":"light.bed_light",
|
||||
"last_changed":"2016-11-26T01:37:10.466994+00:00",
|
||||
"state":"off",
|
||||
"attributes":{
|
||||
"supported_features":147,
|
||||
"friendly_name":"Bed Light"
|
||||
},
|
||||
"last_updated":"2016-11-26T01:37:10.466994+00:00"
|
||||
}
|
||||
},
|
||||
"event_type":"state_changed",
|
||||
"time_fired":"2016-11-26T01:37:24.265429+00:00",
|
||||
"origin":"LOCAL"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Unsubscribing from events
|
||||
|
||||
You can unsubscribe from previously created subscription events. Pass the id of the original subscription command as value to the subscription field.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "unsubscribe_events",
|
||||
"subscription": 18
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message to indicate that unsubscribing was successful.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
## Calling a service
|
||||
|
||||
This will call a service in Home Assistant. Right now there is no return value. The client can listen to `state_changed` events if it is interested in changed entities as a result of a service call.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 24,
|
||||
"type": "call_service",
|
||||
"domain": "light",
|
||||
"service": "turn_on",
|
||||
// Optional
|
||||
"service_data": {
|
||||
"entity_id": "light.kitchen"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The server will indicate with a message indicating that the service is done executing.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 24,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
## Fetching states
|
||||
|
||||
This will get a dump of all the current states in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_states"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the states.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
## Fetching config
|
||||
|
||||
This will get a dump of the current config in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_config"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the config.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## Fetching services
|
||||
|
||||
This will get a dump of the current services in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_services"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the services.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## Fetching panels
|
||||
|
||||
This will get a dump of the current registered panels in Home Assistant.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "get_panels"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the current registered panels.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
## Fetching camera thumbnails
|
||||
|
||||
_Introduced in Home Assistant 0.69._
|
||||
|
||||
Return a b64 encoded thumbnail of a camera entity.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "camera_thumbnail",
|
||||
"entity_id": "camera.driveway"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with a result message containing the thumbnail.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": {
|
||||
"content_type": "image/jpeg",
|
||||
"content": "<base64 encoded image>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Fetching media player thumbnails
|
||||
|
||||
_Introduced in Home Assistant 0.69._
|
||||
|
||||
Fetch a base64 encoded thumbnail picture for a media player.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "media_player_thumbnail",
|
||||
"entity_id": "media_player.living_room"
|
||||
}
|
||||
```
|
||||
|
||||
The server will respond with the image encoded via base64.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "result",
|
||||
"success": true,
|
||||
"result": {
|
||||
"content_type": "image/jpeg",
|
||||
"content": "<base64 encoded image>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Pings and Pongs
|
||||
|
||||
The API supports receiving a ping from the client and returning a pong. This serves as a heartbeat to ensure the connection is still alive:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "ping"
|
||||
}
|
||||
```
|
||||
|
||||
The server must send a pong back as quickly as possible, if the connection is still active:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 19,
|
||||
"type": "pong"
|
||||
}
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
If an error occurs, the `success` key in the `result` message will be set to `false`. It will contain an `error` key containing an object with two keys: `code` and `message`.
|
||||
|
||||
| Code | Description |
|
||||
| ----- | ------------ |
|
||||
| 1 | A non-increasing identifier has been supplied.
|
||||
| 2 | Received message is not in expected format (voluptuous validation error).
|
||||
| 3 | Requested item cannot be found
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 12,
|
||||
"type":"result",
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": 2,
|
||||
"message": "Message incorrectly formatted: expected str for dictionary value @ data['event_type']. Got 100"
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user