Updated the developer documentation

This commit is contained in:
Paulus Schoutsen 2014-12-23 23:36:52 -08:00
parent aa12c7c401
commit f19400796e
7 changed files with 190 additions and 70 deletions

View file

@ -0,0 +1,25 @@
---
layout: page
title: "Adding support for a new platform"
date: 2014-12-21 13:27
sidebar: false
comments: true
sharing: true
footer: true
---
Components that interact with devices are structured in core- and platform logic. This allows the same logic to be used for different platforms.
For example, the built-in `switch` component consists of the following files in [`homeassistant/components/switch/`](https://github.com/balloob/home-assistant/tree/master/homeassistant/components/switch):
| File | Description |
| ---- | ----------- |
| \_\_init\_\_.py | Contains the Switch core logic.|
| wemo.py | WeMo platform logic. Included if in config `platform=wemo`. |
| tellstick.py | Tellstick platform logic. Included if in config `platform=tellstick`. |
If you are planning to add support for a new type of device to an existing component, you can get away with only writing platform logic. Have a look at how the component works with other platforms and create a similar file for the platform that you would like to add.
<div class='note'><p class='title'>Note</p><p class='content'>
Platform logic should not interface directly with the devices but use a third-party Python 3 library that speaks the actual API.
</p></div>

View file

@ -0,0 +1,244 @@
---
layout: page
title: "Rest API"
date: 2014-12-21 13:27
sidebar: false
comments: true
sharing: true
footer: true
---
Home Assistent runs a webserver accessible on port 8123.
* http://127.0.0.1:8123/ is an interface to control Home Assistant.
* http://localhost:8123/api/ is a Rest API.
In the package [`homeassistant.remote`](https://github.com/balloob/home-assistant/blob/master/homeassistant/remote.py) a Python API on top of the HTTP API can be found.
The API accepts and returns only JSON encoded objects. All API calls have to be accompanied by the header `X-HA-Access: YOUR_PASSWORD` (as specified in your `home-assistant.conf`).
<div class='note'><p class='title'>Note</p><p class='content'>
You can append <code>?api_password=YOUR_PASSWORD</code> to any url to log in automatically.
</p></div>
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)
The api supports the following actions:
#### GET /api
Returns message if API is up and running.
```json
{
"message": "API running."
}
```
#### GET /api/events
Returns an array of event objects. Each event object contain event name and listener count.
```json
[
{
"event": "state_changed",
"listener_count": 5
},
{
"event": "time_changed",
"listener_count": 2
}
]
```
#### 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"
]
}
]
```
#### GET /api/states
Returns an array of state objects. Each state has the following attributes: entity_id, state, last_changed and attributes.
```json
[
{
"attributes": {
"next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
},
"entity_id": "sun.sun",
"last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
},
{
"attributes": {},
"entity_id": "process.Dropbox",
"last_changed": "23:24:33 28-10-2013",
"state": "on"
}
]
```
#### GET /api/states/&lt;entity_id>
Returns a state object for specified entity_id. Returns 404 if not found.
```json
{
"attributes": {
"next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
},
"entity_id": "sun.sun",
"last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
}
```
#### POST /api/states/&lt;entity_id>
Updates or creates the current state of an entity.
Expects a JSON object that has atleast a state attribute:
```json
{
"state": "below_horizon",
"next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
}
```
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": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
},
"entity_id": "weather.sun",
"last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
}
```
#### POST /api/events/&lt;event_type>
Fires an event with event_type
You can pass an optional JSON object to be used as `event_data`.
```json
{
"next_rising": "18:00:31 29-10-2013"
}
```
Returns a message if successful.
```json
{
"message": "Event download_file fired."
}
```
#### POST /api/services/&lt;domain>/&lt;service>
Calls a service within a specific domain. Will return when the service has been executed or 10 seconds has past, 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": {
"next_rising": "07:04:15 29-10-2013",
"next_setting": "18:00:31 29-10-2013"
},
"entity_id": "sun.sun",
"last_changed": "23:24:33 28-10-2013",
"state": "below_horizon"
},
{
"attributes": {},
"entity_id": "process.Dropbox",
"last_changed": "23:24:33 28-10-2013",
"state": "on"
}
]
```
<div class='note'><p class='title'>Note</p><p class='content'>
The result will include any changed states that changed while the service was being executed, even if their change was the result of something else happening in the system.
</p></div>
#### POST /api/event_forwarding
Setup event forwarding to another Home Assistant instance.
Requires a JSON object that represents the API to forward to.
```json
{
"host": "machine",
"api_password": "my_super_secret_password",
"port": 8880 // optional
}
```
It will return a message if event forwarding was setup successful.
```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.
```json
{
"host": "machine",
"api_password": "my_super_secret_password",
"port": 8880 // optional
}
```
It will return a message if event forwarding was cancelled successful.
```json
{
"message": "Event forwarding cancelled."
}
```
<div class='note'><p class='title'>Note</p><p class='content'>
If your client does not support <code>DELETE</code> HTTP requests you can add an optional attribute <code>_METHOD</code> and set its value to <code>DELETE</code>.
</p></div>

View file

@ -0,0 +1,73 @@
---
layout: page
title: "Architecture"
date: 2014-12-18 21:49
sidebar: false
comments: true
sharing: true
footer: true
---
<a href='{{ root_url }}/images/ha_architecture.png'>
<img src='{{ root_url }}/images/ha_architecture.png' class='no-shadow' />
</a>
The core of Home Assistant exists of the following parts.
The **Event Bus** facilitates the firing and listening of events. This is the beating heart of Home Assistant.
The **State Machine** keeps track of the states of things. Fires a state_changed event when a state has been changed.
The **Service Registry** listens on the event bus for call_service events and allows other code to register services.
The **Timer** will send every 10 seconds a time_changed event on the event bus.
Take for example the device_tracker component. This component is responsible for keeping track which devices are home. It checks which devices are home every time a time_changed event is fired on the event bus. It will then update the state machine with the information for each device.
This setup allows us to create simple yet powerful logic for controlling your home:
In the event that device 'Paulus Nexus 5' changes to the 'Home' state:
If the sun has set and the lights are not on:
Turn on the lights
<!-- comment to seperate markdown blockquotes -->
In the event that the combined state of all tracked devices changes to 'Not Home':
If the lights are on:
Turn off the lights
<!-- comment to seperate markdown blockquotes -->
In the event of the sun setting:
If the lights are off and the combined state of all tracked device equals 'Home':
Turn on the lights
By using the Bus as a central communication hub between components it is easy to replace components or add functionality. If you would want to change the way devices are detected you only have to write a component that updates the device states in the State Machine.
## Multiple connected instances
Home Assistant supports running multiple synchronzied instances using a master-slave model. Slaves forward all local events fired and states set to the master instance which will then replicate it to each slave.
<a href='{{ root_url }}/images/architecture-remote.png'>
<img src='{{ root_url }}/images/architecture-remote.png' class='no-shadow' />
</a>
A slave instance can be started with the following code and has the same support for components as a master-instance.
```python
import homeassistant.remote as remote
import homeassistant.components.http as http
remote_api = remote.API("remote_host_or_ip", "remote_api_password")
hass = remote.HomeAssistant(remote_api)
http.setup(hass, "my_local_api_password")
hass.start()
hass.block_till_stopped()
```
<div class='note'><p class='title'>Note</p><p class='content'>
Because each slave maintains its own ServiceRegistry it is possible to have multiple slaves respond to one service call.
</p></div>

View file

@ -0,0 +1,81 @@
---
layout: page
title: "Creating components"
date: 2014-12-21 13:32
sidebar: false
comments: true
sharing: true
footer: true
---
Home Assistant offers [built-in components]({{site_root}}/components/) but it
is easy to built your own.
Each component is responsible for a specific domain within Home Assistant.
Components can listen for- or trigger events, offer services and maintain
states. Components are written in Python and can do all the goodness that
Python has to offer.
We can differentiate between two different types of
components within Home Assistant.
#### Components that interact with devices
These components are keeping track of devices within a specific domain. It will also provide services to control those devices.
For example, one of the built-in components is the `switch` component. This component is responsible for interaction with different types of switches.
If you are planning on adding support for a new platform, do not forget to check out the [add new platform section]({{root_url}}/developers/add_new_platform.html).
#### Components that respond to events that happen within Home Assistant
These components can provide automation logic or services that do common tasks within your house.
For example the `device_sun_light_trigger` component tracks the state of
devices and the sun to make sure that the lights are turned on when it gets
dark and there are people home.
An example of such a component can be found in [`/config/custom_components/example.py`](https://github.com/balloob/home-assistant/blob/master/config/custom_components/example.py).
## Loading components
A component will be loaded on start if a section (ie. `[light]`) for it exists in the config file. A component can also be loaded if another component is loaded that depends on it. When loading a component Home Assistant will check the following paths:
* <code>&lt;config directory>/custom_components/&lt;component name></code>
* <code>homeassistant/components/&lt;component name></code> (built-in components)
Once loaded, a component will only be setup if all dependencies can be loaded and are able to setup. Keep an eye on the logs to see if your component could be loaded and initialized.
<div class='note warning'><p class='title'>Warning</p><p class='content'>
You can override a built-in component by having a component with the same name in your <code>config/custom_components</code> folder. This is not recommended and will probably break things!
</p></div>
<div class='note'><p class='title'>Note</p><p class='content'>
Home Assistant will use the directory that contains your config file as the directory that holds your customizations. By default this is the <code>config</code> folder in your current work directory. You can use a different folder by running Home Assistant with the --config argument: <code>python3 homeassistant --config /YOUR/CONFIG/PATH/</code>.
</p></div>
## Initializing components
After loading, the bootstrapper will call `setup(hass, config)` method on the component to initialize it. The following parameters are passed in:
| Parameter | Description |
| --------- | ----------- |
| <code>hass</code> | The Home Assistant object. Call its methods to track time, register services, listen for events or track states: [Overview of available methods.](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L38) |
| <code>config</code> | A dict containing the configuration. The keys of the config-dict are component names and the value is another dict with the component configuration. |
### Guidance on using the Home Assistant object
The Home Assistant object contains three objects to help you interact with the system.
| Object | Description |
| ------ | ----------- |
| <code>hass.states</code> | This is the StateMachine. It allows you to set states and trach when they are changed. [See available methods](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L473). |
| <code>hass.events</code> | This is the EventBus. It allows you to trigger and listen for events.<br>[See available methods](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L308). |
| <code>hass.services</code> | This is the ServiceRegistry. It allows you to register services.<br>[See available methods](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L589). |
### Example on using the configuration parameter
If your configuration file containes the following lines:
```
[example]
host=paulusschoutsen.nl
```
Then in the setup-method of your component you will be able to refer to `config['example']['host']` to get the value `paulusschoutsen.nl`.

View file

@ -0,0 +1,55 @@
---
layout: page
title: "Frontend development"
date: 2014-12-21 13:32
sidebar: false
comments: true
sharing: true
footer: true
---
Home Assistant uses [Polymer](https://www.polymer-project.org/) for the frontend. Polymer allows building encapsulated and interoperable custom elements that extend HTML itself.
# Turning on development mode
Home Assistant will by default serve the compiled version of the frontend. To change it so that the components are served separately, update your `home-assistant.conf` to have these lines:
```
[http]
api_password=YOUR_PASSWORD
development=1
```
After turning on development mode, you will have to install the webcomponents that the frontend depends on. You can do this by running the `build_frontend` script.
<div class='note warning'><p class='title'>Warning</p><p class='content'>
Do not use development mode in production. Home Assistant uses aggresive caching to improve the mobile experience. This is disabled during development so that you do not have to restart the server in between changes.
</p></div>
# Building the frontend
To build the frontend you need to install node and the npm packages bower and vulcanize.
```bash
npm install -g bower vulcanize
```
After that you can run [`./build_frontend`](https://github.com/balloob/home-assistant/blob/master/build_frontend) from the project directory. This will take all the used webcomponents and 'vulcanize' it into a single file to be served by Home Assistant. The script also updates [`homeassistant/components/http/frontend.py`](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/http/frontend.py) with an MD5 hash of the frontend.
# Adding new state cards
The main interface of Home Assistant is a list of current states in the State Machine. It will filter out the group states and offers options to filter by groups on the top.
Currently there are two supported card types:
* Display: shows the state in the card
* Toggle: allows the user to toggle a device on/off from its state
To add your own card type, follow the following steps:
1. Adjust the cardType property of the State class in [home-assistant-api.html](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/http/www_static/polymer/home-assistant-api.html) to return your new card type when appropriate.
2. Create a new component following the naming convention state-card-CARDTYPE.html.
3. Import your new component and add a template for it in [states-cards.html](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/http/www_static/polymer/states-cards.html).

View file

@ -8,71 +8,19 @@ sharing: true
footer: true
---
Home Assistant can be extended by components. Each component is responsible for a specific domain within Home Assistant. An example is the switch component, which is responsible for interaction with different types of switches. Components can listen for- or trigger events, offer services and maintain states. Components are written in Python and can do all the goodness that Python has to offer.
Home Assistant is build from the ground-up to be easily extensible by other developers using components. It uses [Python 3](https://www.python.org/) for the backend and [Polymer (Webcomponents)](https://www.polymer-project.org/) for the frontend.
Home Assistant offers [built-in components]({{site_root}}/components/) but it is easy to built your own. An example component can be found in [`/config/custom_components/example.py`](https://github.com/balloob/home-assistant/blob/master/config/custom_components/example.py).
## Component structure
Components that interact with devices are structured in core- and platform logic. This allows the same logic to be used for different platforms. Components that add automation usually consist of, but are not limited to, one file.
For example, the built-in switch component consists of the following files in [`homeassistant/components/switch/`](https://github.com/balloob/home-assistant/tree/master/homeassistant/components/switch):
| File | Description |
| ---- | ----------- |
| \_\_init\_\_.py | Contains the Switch core logic.|
| wemo.py | WeMo platform logic. Included if in config `platform=wemo`. |
| tellstick.py | Tellstick platform logic. Included if in config `platform=tellstick`. |
## Deciding what to built
If you are planning to add support for a new type of device to an existing component, you can get away with only writing a platform. Have a look at how the component works with other platforms and create a similar file for the platform that you would like to add.
<div class='note'><p class='title'>Note</p><p class='content'>
Platform logic should not interface directly with the devices but use a third-party Python 3 library that speaks the actual API.
</p></div>
## Loading components
A component will be loaded on start if a section (ie. `[light]`) for it exists in the config file. A component can also be loaded if another component is loaded that depends on it. When loading a component Home Assistant will check the following paths:
* <code>&lt;config directory>/custom_components/&lt;component name></code>
* <code>homeassistant/components/&lt;component name></code> (built-in components)
Once loaded, a component will only be setup if all dependencies can be loaded and are able to setup. Keep an eye on the logs to see if your component could be loaded and initialized.
<div class='note warning'><p class='title'>Warning</p><p class='content'>
You can override a built-in component by having a component with the same name in your <code>config/custom_components</code> folder. This is not recommended and will probably break things!
</p></div>
<div class='note'><p class='title'>Note</p><p class='content'>
Home Assistant will use the directory that contains your config file as the directory that holds your customizations. By default this is the <code>config</code> folder in your current work directory. You can use a different folder by running Home Assistant with the --config argument: <code>python3 homeassistant --config /YOUR/CONFIG/PATH/</code>.
</p></div>
## Initializing components
After loading, the bootstrapper will call `setup(hass, config)` method on the component to initialize it. The following parameters are passed in:
| Parameter | Description |
| --------- | ----------- |
| <code>hass</code> | The Home Assistant object. Call its methods to track time, register services, listen for events or track states: [Overview of available methods.](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L38) |
| <code>config</code> | A dict containing the configuration. The keys of the config-dict are component names and the value is another dict with the component configuration. |
### Guidance on using the Home Assistant object
The Home Assistant object contains three objects to help you interact with the system.
| Object | Description |
| ------ | ----------- |
| <code>hass.states</code> | This is the StateMachine. It allows you to set states and trach when they are changed. [See available methods](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L473). |
| <code>hass.events</code> | This is the EventBus. It allows you to trigger and listen for events.<br>[See available methods](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L308). |
| <code>hass.services</code> | This is the ServiceRegistry. It allows you to register services.<br>[See available methods](https://github.com/balloob/home-assistant/blob/master/homeassistant/__init__.py#L589). |
### Example on using the configuration parameter
If your configuration file containes the following lines:
```
[example]
host=paulusschoutsen.nl
```
Then in the setup-method of your component you will be able to refer to `config['example']['host']` to get the value `paulusschoutsen.nl`.
For further reading, see:
<ul>
<li><a href="{{ root_url }}/developers/architecture.html">
Home Assistant Architecture
</a></li>
<li><a href="{{ root_url}}/developers/frontend.html">Frontend development</a></li>
<li><a href="{{ root_url}}/developers/creating_components.html">
Creating a custom component
</a></li>
<li><a href="{{ root_url}}/developers/add_new_platform.html">
Adding support for a new platform
</a></li>
<li><a href="{{ root_url }}/developers/api.html">Rest API</a></li>
</ul>