Code block improvements (#382)

* Use f-strings instead of .format()

* Code block language marker fixes

* Make example code blocks syntactically valid Python

* Run all python code blocks through black

https://github.com/scop/misc/blob/master/black_markdown.py

* Add some missing code block language markers

* Use shell language consistently to mark shell code blocks

* Undo folding of some example dicts

* Remove outdated OrderedDict comments per Python 3.7, replace with plain dict
This commit is contained in:
Ville Skyttä 2020-01-13 21:55:41 +02:00 committed by GitHub
parent 732ee0523b
commit 770185004b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 382 additions and 368 deletions

View File

@ -34,7 +34,7 @@ class Auth:
async def request(self, method: str, path: str, **kwargs) -> ClientResponse:
"""Make a request."""
headers = kwargs.get('headers')
headers = kwargs.get("headers")
if headers is None:
headers = {}
@ -44,10 +44,7 @@ class Auth:
headers["authorization"] = self.access_token
return await self.websession.request(
method,
f"{self.host}/{path}",
**kwargs,
headers=headers,
method, f"{self.host}/{path}", **kwargs, headers=headers,
)
```
@ -65,10 +62,11 @@ async def main():
auth = Auth(session, "http://example.com/api", "secret_access_token")
# This will fetch data from http://example.com/api/lights
resp = await auth.request('get', 'lights')
resp = await auth.request("get", "lights")
print("HTTP response status code", resp.status)
print("HTTP response JSON content", await resp.json())
asyncio.run(main())
```
@ -88,7 +86,7 @@ class Auth:
async def request(self, method: str, path: str, **kwargs) -> requests.Response:
"""Make a request."""
headers = kwargs.get('headers')
headers = kwargs.get("headers")
if headers is None:
headers = {}
@ -98,10 +96,7 @@ class Auth:
headers["authorization"] = self.access_token
return requests.request(
method,
f"{self.host}/{path}",
**kwargs,
headers=headers,
method, f"{self.host}/{path}", **kwargs, headers=headers,
)
```
@ -114,7 +109,7 @@ from my_package import Auth
auth = Auth("http://example.com/api", "secret_access_token")
# This will fetch data from http://example.com/api/lights
resp = auth.request('get', 'lights')
resp = auth.request("get", "lights")
print("HTTP response status code", resp.status_code)
print("HTTP response JSON content", resp.json())
```
@ -147,7 +142,7 @@ class AbstractAuth(ABC):
async def request(self, method, url, **kwargs) -> ClientResponse:
"""Make a request."""
headers = kwargs.get('headers')
headers = kwargs.get("headers")
if headers is None:
headers = {}
@ -158,10 +153,7 @@ class AbstractAuth(ABC):
headers["authorization"] = f"Bearer {access_token}"
return await self.websession.request(
method,
f"{self.host}/{url}",
**kwargs,
headers=headers,
method, f"{self.host}/{url}", **kwargs, headers=headers,
)
```
@ -172,7 +164,6 @@ from my_package import AbstractAuth
class Auth(AbstractAuth):
def __init__(self, websession: ClientSession, host: str, token_manager):
"""Initialize the auth."""
super().__init__(websession, host)
@ -255,7 +246,6 @@ from my_package import AbstractAuth
class Auth(AbstractAuth):
def refresh_tokens(self) -> Dict[str, Union[str, int]]:
"""Refresh and return new tokens."""
self.token_manager.fetch_access_token()

View File

@ -58,29 +58,29 @@ class Light:
@property
def id(self) -> int:
"""Return the ID of the light."""
return self.raw_data['id']
return self.raw_data["id"]
@property
def name(self) -> str:
"""Return the name of the light."""
return self.raw_data['name']
return self.raw_data["name"]
@property
def is_on(self) -> bool:
"""Return if the light is on."""
return self.raw_data['id']
return self.raw_data["id"]
async def async_control(self, is_on: bool):
"""Control the light."""
resp = await self.auth.request('post', f'light/{self.id}', json={
'is_on': is_on
})
resp = await self.auth.request(
"post", f"light/{self.id}", json={"is_on": is_on}
)
resp.raise_for_status()
self.raw_data = await resp.json()
async def async_update(self):
"""Update the light data."""
resp = await self.auth.request('get', f'light/{self.id}')
resp = await self.auth.request("get", f"light/{self.id}")
resp.raise_for_status()
self.raw_data = await resp.json()
```
@ -103,16 +103,13 @@ class ExampleHubAPI:
async def async_get_lights(self) -> List[Light]:
"""Return the lights."""
resp = await self.auth.request('get', 'lights')
resp = await self.auth.request("get", "lights")
resp.raise_for_status()
return [
Light(light_data, self.auth)
for light_data in await resp.json()
]
return [Light(light_data, self.auth) for light_data in await resp.json()]
async def async_get_light(self, light_id) -> Light:
"""Return the lights."""
resp = await self.auth.request('get', f'light/{light_id}')
resp = await self.auth.request("get", f"light/{light_id}")
resp.raise_for_status()
return Light(await resp.json(), self.auth)
```

View File

@ -33,12 +33,12 @@ You will need to run an editable version of your library if you want to try your
Do so by going to your Home Assistant development environment, activating the virtual environment and typing:
```
```shell
pip3 install -e ../my_lib_folder
```
Now run Home Assistant without installing dependencies from PyPI to avoid overriding your package.
```
```shell
hass --skip-pip
```

View File

@ -18,7 +18,8 @@ To declare a function a coroutine, add `async` before the `def` of the function
async def async_look_my_coroutine(target):
result = await entity.async_turn_on()
if result:
print("hello {}".format(target))
print(f"hello {target}")
hass.loop.create_task(async_look_my_coroutine("world"))
```
@ -36,6 +37,7 @@ A common use case for a callback in Home Assistant is as a listener for an event
```python
from homeassistant.core import callback
@callback
def async_trigger_service_handler(service_call):
"""Handle automation trigger service calls."""

View File

@ -16,14 +16,16 @@ To make a component async, implement an async_setup.
```python
def setup(hass, config):
# Setup your component outside of the event loop.
"""Set up component."""
# Code for setting up your component outside of the event loop.
```
Will turn into:
```python
async def async_setup(hass, config):
# Setup your component inside of the event loop.
"""Set up component."""
# Code for setting up your component inside of the event loop.
```
## Implementing an async platform
@ -31,16 +33,17 @@ async def async_setup(hass, config):
For platforms we support async setup. Instead of setup_platform you need to have a coroutine async_setup_platform.
```python
setup_platform(hass, config, add_entities, discovery_info=None):
# Setup your platform outside of the event loop.
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up platform."""
# Code for setting up your platform outside of the event loop.
```
Will turn into:
```python
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
# Setup your platform inside of the event loop
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up platform."""
# Code for setting up your platform inside of the event loop.
```
The only difference with the original parameters is that the `add_entities` function has been replaced by the async friendly callback `async_add_entities`.
@ -76,12 +79,15 @@ In the following example, `say_hello` will schedule `async_say_hello` and block
```python
import asyncio
def say_hello(hass, target):
return asyncio.run_coroutine_threadsafe(
async_say_hello(hass, target), hass.loop).result()
async_say_hello(hass, target), hass.loop
).result()
async def async_say_hello(hass, target):
return "Hello {}!".format(target)
return f"Hello {target}!"
```
## Calling sync functions from async

View File

@ -176,7 +176,7 @@ For the websocket connection, pass the access token in the [authentication messa
For HTTP requests, pass the token type and access token as the authorization header:
```
```http
Authorization: Bearer ABCDEFGH
```
@ -195,9 +195,9 @@ import requests
url = "https://your.awesome.home/api/error/all"
headers = {
'Authorization': "Bearer ABCDEFGH",
"Authorization": "Bearer ABCDEFGH",
}
response = requests.request('GET', url, headers=headers)
response = requests.request("GET", url, headers=headers)
print(response.text)
```

View File

@ -36,7 +36,11 @@ Auth providers shall extend the following methods of `LoginFlow` class.
```python
async def async_step_init(self, user_input=None):
return self.async_show_form(step_id='init', data_schema='some schema to construct ui form') if user_input is None
return self.async_show_form(step_id='init', errors) if user_input is invalid
return await self.async_finish(username) if user_input is valid
if user_input is None:
return self.async_show_form(
step_id="init", data_schema="some schema to construct ui form"
)
if is_invalid(user_input):
return self.async_show_form(step_id="init", errors=errors)
return await self.async_finish(user_input)
```

View File

@ -14,7 +14,9 @@ Policies are dictionaries that at the root level consist of different categories
```python
{
"entities": …
"entities": {
# …
}
}
```
@ -22,10 +24,14 @@ Each category can further split into subcategories that describe parts of that c
```python
{
"entities": {
"domains": …,
"entity_ids": …
}
"entities": {
"domains": {
# …
},
"entity_ids": {
# …
},
}
}
```
@ -67,19 +73,19 @@ Let's look at an example:
```python
{
"entities": {
"entity_ids": {
"light.kitchen": True
"entities": {
"entity_ids": {
"light.kitchen": True
}
}
}
}
```
```python
{
"entities": {
"entity_ids": True
}
"entities": {
"entity_ids": True
}
}
```
@ -87,9 +93,9 @@ Once merged becomes
```python
{
"entities": {
"entity_ids": True
}
"entities": {
"entity_ids": True
}
}
```
@ -105,9 +111,7 @@ To check a permission, you will need to have access to the user object. Once you
```python
from homeassistant.exceptions import Unauthorized
from homeasistant.permissions.const import (
POLICY_READ, POLICY_CONTROL, POLICY_EDIT
)
from homeasistant.permissions.const import POLICY_READ, POLICY_CONTROL, POLICY_EDIT
# Raise error if user is not an admin
if not user.is_admin:
@ -129,9 +133,9 @@ It's crucial for permission checking that actions taken on behalf of the user ar
```python
from homeassistant.core import Context
await hass.services.async_call('homeassistant', 'stop', context=Context(
user_id=user.id
), blocking=True)
await hass.services.async_call(
"homeassistant", "stop", context=Context(user_id=user.id), blocking=True
)
```
### If a permission check fails
@ -167,9 +171,10 @@ Your service call handler will need to check the permissions for each entity tha
from homeassistant.exceptions import Unauthorized, UnknownUser
from homeassistant.auth.permissions.const import POLICY_CONTROL
async def handle_entity_service(call):
"""Handle a service call."""
entity_ids = call.data['entity_id']
entity_ids = call.data["entity_id"]
for entity_id in entity_ids:
if call.context.user_id:
@ -193,7 +198,7 @@ async def handle_entity_service(call):
async def async_setup(hass, config):
hass.services.async_register(DOMAIN, 'my_service', handle_entity_service)
hass.services.async_register(DOMAIN, "my_service", handle_entity_service)
return True
```
@ -211,7 +216,7 @@ async def handle_admin_service(call):
async def async_setup(hass, config):
hass.helpers.service.async_register_admin_service(
DOMAIN, 'my_service', handle_admin_service, vol.Schema({})
DOMAIN, "my_service", handle_admin_service, vol.Schema({})
)
return True
```
@ -227,20 +232,20 @@ from homeassistant.exceptions import Unauthorized
class MyView(HomeAssistantView):
"""View to handle Status requests."""
url = '/api/my-component/my-api'
name = 'api:my-component:my-api'
url = "/api/my-component/my-api"
name = "api:my-component:my-api"
async def post(self, request):
"""Notify that the API is running."""
hass = request.app['hass']
user = request['hass_user']
hass = request.app["hass"]
user = request["hass_user"]
if not user.is_admin:
raise Unauthorized()
hass.bus.async_fire('my-component-api-running', context=Context(
user_id=user.id
))
hass.bus.async_fire(
"my-component-api-running", context=Context(user_id=user.id)
)
return self.json_message("Done.")
```
@ -262,9 +267,9 @@ async def async_setup(hass, config):
@websocket_api.require_admin
@websocket_api.async_response
@websocket_api.websocket_command({
vol.Required('type'): 'my-component/my-action',
})
@websocket_api.websocket_command(
{vol.Required("type"): "my-component/my-action",}
)
async def websocket_create(hass, connection, msg):
"""Create a user."""
# Do action

View File

@ -21,7 +21,9 @@ Config entries uses the [data flow entry framework](data_entry_flow_index.md) to
from homeassistant import config_entries
from .const import DOMAIN
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Example config flow."""
```
Once you have updated your manifest and created the `config_flow.py`, you will need to run `python3 -m script.hassfest` (one time only) for Home Assistant to activate the config entry for your integration.
@ -32,16 +34,12 @@ Your config flow will need to define steps of your configuration flow. The docs
```python
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, info):
if info is not None:
# process info
pass # TODO: process info
return self.async_show_form(
step_id='user',
data_schema=vol.Schema({
vol.Required('password'): str
})
step_id="user", data_schema=vol.Schema({vol.Required("password"): str})
)
```

View File

@ -106,13 +106,14 @@ be done by calling the forward function on the config entry manager ([example](h
```python
# Use `hass.async_add_job` to avoid a circular dependency between the platform and the component
hass.async_add_job(hass.config_entries.async_forward_entry_setup(config_entry, 'light'))
hass.async_add_job(hass.config_entries.async_forward_entry_setup(config_entry, "light"))
```
For a platform to support config entries, it will need to add a setup entry method ([example](https://github.com/home-assistant/home-assistant/blob/0.68.0/homeassistant/components/light/hue.py#L60)):
```python
async def async_setup_entry(hass, config_entry, async_add_devices):
"""Set up entry."""
```
## Unloading entries
@ -122,7 +123,7 @@ Components can optionally support unloading a config entry. When unloading an en
For each platform that you forwarded the config entry to, you will need to forward the unloading too.
```python
await self.hass.config_entries.async_forward_entry_unload(self.config_entry, 'light')
await self.hass.config_entries.async_forward_entry_unload(self.config_entry, "light")
```
If you need to clean up resources used by an entity in a platform, have the entity implement the [`async_will_remove_from_hass`](entity_index.md#async_will_remove_from_hass) method.

View File

@ -24,7 +24,6 @@ The Flow handler works just like the config flow handler, except that the first
```python
class OptionsFlowHandler(config_entries.OptionsFlow):
async def async_step_init(self, user_input=None):
"""Manage the options."""
if user_input is not None:
@ -55,4 +54,5 @@ The Listener shall be an async function that takes the same input as async_setup
```python
async def update_listener(hass, entry):
"""Handle options update."""
```

View File

@ -47,9 +47,9 @@ will be passed to the component as
"platform": "example1"
},
{
"platform": "example2,
"platform": "example2",
"some_config": True
}
]
],
}
```

View File

@ -39,10 +39,10 @@ A checklist of things to do when you're adding a new component.
```python
# bad
status = requests.get(url('/status'))
status = requests.get(url("/status"))
# good
from phue import Bridge
bridge = Bridge(...)
status = bridge.status()
```

View File

@ -4,7 +4,7 @@ title: "Creating your first integration"
Alright, you learned about the [manifest](creating_integration_manifest.md), so it's time to write your first code for your integration. AWESOME. Don't worry, we've tried hard to keep it as easy as possible. From a Home Assistant development environment, type the following and follow the instructions:
```python
```shell
python3 -m script.scaffold integration
```
@ -15,10 +15,11 @@ This will set you up with everything that you need to build an integration that
The scaffold integration contains a bit more than just the bare minimum. The minimum is that you define a `DOMAIN` constant that contains the domain of the integration. The second part is that it needs to define a setup method that returns a boolean if the set up was successful.
```python
DOMAIN = 'hello_state'
DOMAIN = "hello_state"
def setup(hass, config):
hass.states.set('hello_state.world', 'Paulus')
hass.states.set("hello_state.world", "Paulus")
# Return boolean to indicate that initialization was successful.
return True
@ -27,10 +28,11 @@ def setup(hass, config):
And if you prefer an async component:
```python
DOMAIN = 'hello_state'
DOMAIN = "hello_state"
async def async_setup(hass, config):
hass.states.async_set('hello_state.world', 'Paulus')
hass.states.async_set("hello_state.world", "Paulus")
# Return boolean to indicate that initialization was successful.
return True

View File

@ -70,7 +70,7 @@ Requirements is an array of strings. Each entry is a `pip` compatible string. Fo
During the development of a component, it can be useful to test against different versions of a requirement. This can be done in two steps, using `pychromecast` as an example:
```bash
```shell
pip install pychromecast==3.2.0 --target ~/.homeassistant/deps
hass --skip-pip
```
@ -79,7 +79,7 @@ This will use the specified version, and prevent Home Assistant from trying to o
If you need to make changes to a requirement to support your component, it's also possible to install a development version of the requirement using `pip install -e`:
```bash
```shell
git clone https://github.com/balloob/pychromecast.git
pip install -e ./pychromecast
hass --skip-pip

View File

@ -35,15 +35,16 @@ from homeassistant.const import CONF_FILENAME, CONF_HOST
from homeassistant.components.light import PLATFORM_SCHEMA
import homeassistant.helpers.config_validation as cv
CONF_ALLOW_UNREACHABLE = 'allow_unreachable'
CONF_ALLOW_UNREACHABLE = "allow_unreachable"
DEFAULT_UNREACHABLE = False
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_ALLOW_UNREACHABLE,
default=DEFAULT_UNREACHABLE): cv.boolean,
vol.Optional(CONF_FILENAME): cv.string,
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_ALLOW_UNREACHABLE, default=DEFAULT_UNREACHABLE): cv.boolean,
vol.Optional(CONF_FILENAME): cv.string,
}
)
```
### 3. Setup Platform
@ -59,8 +60,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
```python
from homeassistant.components.light import Light
class HueLight(Light):
...
"""Hue light component."""
```
2. Avoid passing in `hass` as a parameter to the entity. When the entity has been added to Home Assistant, `hass` will be set on the entity when the entity is added to Home Assistant. This means you can access `hass` as `self.hass` inside the entity.
@ -75,10 +77,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
```python
# bad
status = requests.get(url('/status'))
status = requests.get(url("/status"))
# good
from phue import Bridge
bridge = Bridge(...)
status = bridge.status()
```

View File

@ -12,13 +12,15 @@ Data Entry Flow is used in Home Assistant to create config entries.
This is the class that manages the flows that are in progress. When instantiating one, you pass in two async callbacks:
```python
async def async_create_flow(handler, context=context, data=data)
async def async_create_flow(handler, context=context, data=data):
"""Create flow."""
```
The manager delegates instantiating of config flow handlers to this async callback. This allows the parent of the manager to define their own way of finding handlers and preparing a handler for instantiation. For example, in the case of the config entry manager, it will make sure that the dependencies and requirements are setup.
```python
async def async_finish_flow(flow, result)
async def async_finish_flow(flow, result):
"""Finish flow."""
```
This async callback is called when a flow is finished or aborted. i.e. `result['type'] in [RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_ABORT]`. The callback function can modify result and return it back, if the result type changed to `RESULT_TYPE_FORM`, the flow will continue running, display another form.
@ -27,19 +29,19 @@ If the result type is `RESULT_TYPE_FORM`, the result should look like:
```python
{
# The result type of the flow
'type': RESULT_TYPE_FORM,
"type": RESULT_TYPE_FORM,
# the id of the flow
'flow_id': 'abcdfgh1234,
"flow_id": "abcdfgh1234",
# handler name
'handler': 'hue',
"handler": "hue",
# name of the step, flow.async_step_[step_id] will be called when form submitted
'step_id': 'init',
"step_id": "init",
# a voluptuous schema to build and validate user input
'data_schema': vol.Schema(),
"data_schema": vol.Schema(),
# an errors dict, None if no errors
'errors': errors,
"errors": errors,
# a detail information about the step
'description_placeholders': description_placeholders,
"description_placeholders": description_placeholders,
}
```
@ -47,17 +49,17 @@ If the result type is `RESULT_TYPE_CREATE_ENTRY`, the result should look like:
```python
{
# Data schema version of the entry
'version': 2,
"version": 2,
# The result type of the flow
'type': RESULT_TYPE_CREATE_ENTRY,
"type": RESULT_TYPE_CREATE_ENTRY,
# the id of the flow
'flow_id': 'abcdfgh1234,
"flow_id": "abcdfgh1234",
# handler name
'handler': 'hue',
"handler": "hue",
# title and data as created by the handler
'title': 'Some title',
'result': {
'some': 'data'
"title": "Some title",
"result": {
"some": "data"
},
}
```
@ -66,13 +68,13 @@ If the result type is `RESULT_TYPE_ABORT`, the result should look like:
```python
{
# The result type of the flow
'type': RESULT_TYPE_ABORT,
"type": RESULT_TYPE_ABORT,
# the id of the flow
'flow_id': 'abcdfgh1234,
"flow_id": "abcdfgh1234",
# handler name
'handler': 'hue',
"handler": "hue",
# the abort reason
'reason': 'already_configured',
"reason": "already_configured",
}
```
@ -88,6 +90,7 @@ The bare minimum config flow:
```python
from homeassistant import data_entry_flow
@config_entries.HANDLERS.register(DOMAIN)
class ExampleConfigFlow(data_entry_flow.FlowHandler):
@ -97,7 +100,7 @@ class ExampleConfigFlow(data_entry_flow.FlowHandler):
VERSION = 1
async def async_step_user(self, user_input=None):
# Do something
"""Handle user step."""
```
### Show Form
@ -106,17 +109,14 @@ This result type will show a form to the user to fill in. You define the current
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
# Use OrderedDict to guarantee order of the form shown to the user
data_schema = OrderedDict()
data_schema[vol.Required('username')] = str
data_schema[vol.Required('password')] = str
# Specify items in the order they are to be displayed in the UI
data_schema = {
vol.Required("username"): str,
vol.Required("password"): str,
}
return self.async_show_form(
step_id='init',
data_schema=vol.Schema(data_schema)
)
return self.async_show_form(step_id="init", data_schema=vol.Schema(data_schema))
```
After the user has filled in the form, the step method will be called again and the user input is passed in. Your step will only be called if the user input passes your data schema. When the user passes in data, you will have to do extra validation of the data. For example, you can verify that the passed in username and password are valid.
@ -125,7 +125,6 @@ If something is wrong, you can return a dictionary with errors. Each key in the
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
errors = {}
if user_input is not None:
@ -135,17 +134,16 @@ class ExampleConfigFlow(data_entry_flow.FlowHandler):
# See next section on create entry usage
return self.create_entry(...)
errors['base'] = 'auth_error'
errors["base"] = "auth_error"
# Use OrderedDict to guarantee order of the form shown to the user
data_schema = OrderedDict()
data_schema[vol.Required('username')] = str
data_schema[vol.Required('password')] = str
# Specify items in the order they are to be displayed in the UI
data_schema = {
vol.Required("username"): str,
vol.Required("password"): str,
}
return self.async_show_form(
step_id='init',
data_schema=vol.Schema(data_schema),
errors=errors
step_id="init", data_schema=vol.Schema(data_schema), errors=errors
)
```
@ -155,7 +153,6 @@ If the user input passes validation, you can again return one of the three retur
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_init(self, user_input=None):
errors = {}
if user_input is not None:
@ -176,13 +173,12 @@ When the result is "Create Entry", an entry will be created and passed to the pa
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
return self.create_entry(
title='Title of the entry',
title="Title of the entry",
data={
'something_special': user_input['username']
}
"something_special": user_input["username"]
},
)
```
@ -192,11 +188,8 @@ When a flow cannot be finished, you need to abort it. This will finish the flow
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
return self.async_abort(
reason='not_supported'
)
return self.async_abort(reason="not_supported")
```
### External Step & External Step Done
@ -222,6 +215,7 @@ Example configuration flow that includes an external step.
```python
from homeassistant import config_entries
@config_entries.HANDLERS.register(DOMAIN)
class ExampleConfigFlow(data_entry_flow.FlowHandler):
VERSION = 1
@ -230,20 +224,15 @@ class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_user(self, user_input=None):
if not user_input:
return self.async_external_step(
step_id='user',
url='https://example.com/?config_flow_id={}'.format(
self.flow_id
),
step_id="user",
url=f"https://example.com/?config_flow_id={self.flow_id}",
)
self.data = user_input
return self.async_external_step_done(next_step_id='finish')
return self.async_external_step_done(next_step_id="finish")
async def async_step_finish(self, user_input=None):
return self.async_create_entry(
title=self.data['title'],
data=self.data
)
return self.async_create_entry(title=self.data["title"], data=self.data)
```
Avoid doing work based on the external step data before you return an `async_mark_external_step_done`. Instead, do the work in the step that you refer to as `next_step_id` when marking the external step done. This will give the user a better user experience by showing a spinner in the UI while the work is done.
@ -255,13 +244,14 @@ Example code to mark an external step as done:
```python
from homeassistant import data_entry_flow
async def handle_result(hass, flow_id, data):
result = await hass.config_entries.async_configure(flow_id, data)
if result['type'] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP_DONE:
return "success!"
else:
return "Invalid config flow specified"
async def handle_result(hass, flow_id, data):
result = await hass.config_entries.async_configure(flow_id, data)
if result["type"] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP_DONE:
return "success!"
else:
return "Invalid config flow specified"
```
## Translations
@ -273,14 +263,15 @@ Data entry flows depend on translations for showing the text in the forms. It de
You might want to initialize a config flow programmatically. For example, if we discover a device on the network that requires user interaction to finish setup. To do so, pass a source parameter and optional user input when initializing a flow:
```python
await flow_mgr.async_init('hue', context={'source': data_entry_flow.SOURCE_DISCOVERY}, data=discovery_info)
await flow_mgr.async_init(
"hue", context={"source": data_entry_flow.SOURCE_DISCOVERY}, data=discovery_info
)
```
The config flow handler will not start with the `init` step. Instead, it will be instantiated with a step name equal to the source. The step should follow the same return values as a normal step.
```python
class ExampleConfigFlow(data_entry_flow.FlowHandler):
async def async_step_discovery(self, info):
# Handle discovery info
"""Handle discovery info."""
```

View File

@ -15,15 +15,14 @@ To fire an event, you have to interact with the event bus. The event bus is avai
Example component that will fire an event when loaded. Note that custom event names are prefixed with the component name.
```python
DOMAIN = 'example_component'
DOMAIN = "example_component"
def setup(hass, config):
"""Set up is called when Home Assistant is loading our component."""
# Fire event example_component_my_cool_event with event data answer=42
hass.bus.fire('example_component_my_cool_event', {
'answer': 42
})
hass.bus.fire("example_component_my_cool_event", {"answer": 42})
# Return successful setup
return True
@ -34,7 +33,8 @@ def setup(hass, config):
Most of the times you'll not be firing events but instead listen to events. For example, the state change of an entity is broadcasted as an event.
```python
DOMAIN = 'example_component'
DOMAIN = "example_component"
def setup(hass, config):
"""Set up is called when Home Assistant is loading our component."""
@ -44,10 +44,10 @@ def setup(hass, config):
def handle_event(event):
nonlocal count
count += 1
print('Answer {0} is: {1}'.format(count, event.data.get('answer')))
print(f"Answer {count} is: {event.data.get('answer')}")
# Listen for when example_component_my_cool_event is fired
hass.bus.listen('example_component_my_cool_event', handle_event)
hass.bus.listen("example_component_my_cool_event", handle_event)
# Return successful setup
return True

View File

@ -21,7 +21,7 @@ DOMAIN = "hello_world"
def setup(hass, config):
"""Setup the hello_world component."""
# States are in the format DOMAIN.OBJECT_ID.
hass.states.set('hello_world.Hello_World', 'Works!')
hass.states.set("hello_world.Hello_World", "Works!")
# Return boolean to indicate that initialization was successfully.
return True

View File

@ -10,10 +10,10 @@ This is a simple "hello world" example to show the basics of registering a servi
Services can be called from automations and from the service "Developer tools" in the frontend.
```python
DOMAIN = 'hello_service'
DOMAIN = "hello_service"
ATTR_NAME = 'name'
DEFAULT_NAME = 'World'
ATTR_NAME = "name"
DEFAULT_NAME = "World"
def setup(hass, config):
@ -23,9 +23,9 @@ def setup(hass, config):
"""Handle the service call."""
name = call.data.get(ATTR_NAME, DEFAULT_NAME)
hass.states.set('hello_service.hello', name)
hass.states.set("hello_service.hello", name)
hass.services.register(DOMAIN, 'hello', handle_hello)
hass.services.register(DOMAIN, "hello", handle_hello)
# Return boolean to indicate that initialization was successfully.
return True

View File

@ -27,7 +27,8 @@ import logging
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'hello_state'
DOMAIN = "hello_state"
def setup(hass, config):
"""Setup the Hello State component. """
@ -58,7 +59,7 @@ hello_state:
After a start or a restart of Home Assistant the component will create an entry in the log.
```bash
```log
16-03-12 14:16:42 INFO (MainThread) [custom_components.hello_state] The 'hello state' component is ready!
```
@ -69,10 +70,11 @@ import logging
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'hello_state'
DOMAIN = "hello_state"
CONF_TEXT = "text"
DEFAULT_TEXT = "No text!"
CONF_TEXT = 'text'
DEFAULT_TEXT = 'No text!'
def setup(hass, config):
"""Set up the Hello State component. """
@ -80,7 +82,7 @@ def setup(hass, config):
text = config[DOMAIN].get(CONF_TEXT, DEFAULT_TEXT)
# States are in the format DOMAIN.OBJECT_ID
hass.states.set('hello_state.Hello_State', text)
hass.states.set("hello_state.Hello_State", text)
return True
```
@ -99,11 +101,9 @@ import voluptuous as vol
import homeassistant.helpers.config_validation as cv
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_TEXT): cv.string,
})
}, extra=vol.ALLOW_EXTRA)
CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Schema({vol.Required(CONF_TEXT): cv.string,})}, extra=vol.ALLOW_EXTRA
)
```
Now, when `text:` is missing from the config, Home Assistant will alert the user and not setup your component.
@ -116,7 +116,7 @@ After a start or a restart of Home Assistant the component will be visible in th
In order to expose attributes for a platform, you will need to define a property called `device_state_attributes` on the entity class, which will return a dictionary of attributes:
```
```python
@property
def device_state_attributes(self):
"""Return device specific state attributes."""

View File

@ -6,7 +6,7 @@ If it's taking a while to develop your feature, and you want to catch up with wh
> If you use the workflow below, it is important that you force push the update as described. Git might prompt you to do `git pull` first. Do **NOT** do that! It would mess up your commit history.
```bash
```shell
# Run this from your feature branch
$ git fetch upstream dev # to pull the latest changes into a local dev branch
$ git rebase upstream/dev # to put those changes into your feature branch before your changes
@ -21,14 +21,14 @@ If rebase detects conflicts, repeat this process until all changes have been res
After rebasing your branch, you will have rewritten history relative to your GitHub fork's branch. When you go to push you will see an error that your history has diverged from the original branch. In order to get your GitHub fork up-to-date with your local branch, you will need to force push, using the following command:
```bash
```shell
# Run this from your feature branch
$ git push origin --force
```
Other workflows are covered in detail in the [Github documentation](https://help.github.com/articles/fork-a-repo/). Add an additional `remote` after you clone your fork.
```bash
```shell
$ git remote add upstream https://github.com/home-assistant/home-assistant.git
```

View File

@ -10,13 +10,13 @@ You'll need to set up a development environment if you want to develop a new fea
Install the core dependencies.
```bash
```shell
$ sudo apt-get install python3-pip python3-dev python3-venv
```
In order to run `script/setup` below you will need some more dependencies.
```bash
```shell
$ sudo apt-get install autoconf libssl-dev libxml2-dev libxslt1-dev libjpeg-dev libffi-dev libudev-dev zlib1g-dev pkg-config
$ sudo apt-get install -y libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libavresample-dev libavfilter-dev
```
@ -31,7 +31,7 @@ Since Home Assistant is mainly designed and developed on Linux distributions, on
Open Powershell as an Administrator and run
```bash
```shell
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
```
@ -39,7 +39,7 @@ From Windows Store install Ubuntu.
When the Linux subsystem is set up, perform install as for Linux.
```bash
```shell
$ sudo apt-get update
$ sudo apt-get install python3-pip python3.7-dev python3.7-venv python-wheel-common
$ sudo apt-get install autoconf libssl-dev libxml2-dev libxslt1-dev libjpeg-dev libffi-dev libudev-dev zlib1g-dev
@ -50,7 +50,7 @@ Hint: Git is included in Linux subsytem.
When invoking your installation (see below), make sure to specify a folder for configuration which is accessible from Windows.
```bash
```shell
$ mkdir -p ../config
$ hass -c ../config
```
@ -59,13 +59,13 @@ $ hass -c ../config
Install [Homebrew](https://brew.sh/), then use that to install Python 3:
```bash
```shell
$ brew install python3 autoconf
```
Then install ffmpeg:
```bash
```shell
$ brew install ffmpeg
```
@ -105,7 +105,7 @@ Once forked, setup your local copy of the source using the commands:
_Windows users should be sure to clone to a path that inside the WSL (ex: ~/)._
```bash
```shell
$ git clone https://github.com/YOUR_GIT_USERNAME/home-assistant.git
$ cd home-assistant
$ git remote add upstream https://github.com/home-assistant/home-assistant.git
@ -115,20 +115,20 @@ $ git remote add upstream https://github.com/home-assistant/home-assistant.git
To isolate your environment from the rest of the system, set up a [`venv`](https://docs.python.org/3/library/venv.html). Within the `home-assistant` directory, create and activate your virtual environment.
```bash
```shell
$ python3.7 -m venv venv
$ source venv/bin/activate
```
Install the requirements with a provided script named `setup`.
```bash
```shell
$ script/setup
```
Invoke your installation, adjusting the [configuration](https://www.home-assistant.io/docs/configuration/) if required.
```bash
```shell
$ hass
```

View File

@ -34,7 +34,7 @@ There is no need to add the platform or component name to the log messages. This
_LOGGER.error("No route to device: %s", self._resource)
```
```bash
```log
2017-05-01 14:28:07 ERROR [homeassistant.components.sensor.arest] No route to device: 192.168.0.18
```
@ -45,7 +45,7 @@ Also note that `_LOGGER.info` is reserved for the core, use `_LOGGER.debug` for
Instead of order the imports manually, use [`isort`](https://github.com/timothycrosley/isort).
```bash
```shell
$ pip3 install isort
$ isort homeassistant/components/sensor/fixer.py
```
@ -58,8 +58,8 @@ Prefer [f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-s
# New
f"{some_value} {some_other_value}"
# Old, wrong
"{} {}".format('New', 'style')
"%s %s" % ('Old', 'style')
"{} {}".format("New", "style")
"%s %s" % ("Old", "style")
```
One exception is for logging which uses the percentage formatting. This is to avoid formatting the log message when it is suppressed.

View File

@ -9,7 +9,7 @@ As it states in the [Style guidelines section](development_guidelines.md) all co
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
```shell
$ tox
```
@ -34,7 +34,7 @@ If you are working on tests for an integeration and you need the dependencies av
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
```shell
# Stop after the first test fails
$ tox -e py38 -- tests/test_core.py -x
# Run test with specified name
@ -49,13 +49,13 @@ $ tox -e py38 -- tests/test_core.py --duration=10
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
```shell
$ 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
```shell
$ flake8 homeassistant/core.py
$ pylint homeassistant/core.py
$ pydocstyle homeassistant/core.py
@ -64,7 +64,7 @@ $ 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
```shell
$ script/lint
```
@ -72,7 +72,7 @@ $ script/lint
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
```shell
$ pip3 install flake8 flake8-docstrings
$ flake8 --install-hook=git
```

View File

@ -32,11 +32,14 @@ This section contains snippets for the validation we use.
It's common to set a default for a sensor if the user doesn't provide a name to use.
```python
DEFAULT_NAME = 'Sensor name'
DEFAULT_NAME = "Sensor name"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
...
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
# ...
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
```
#### Limit the values
@ -44,11 +47,14 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
You might want to limit the user's input to a couple of options.
```python
DEFAULT_METHOD = 'GET'
DEFAULT_METHOD = "GET"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
...
vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): vol.In(['POST', 'GET']),
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
# ...
vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): vol.In(["POST", "GET"]),
}
)
```
#### Port
@ -58,9 +64,12 @@ All port numbers are from a range of 1 to 65535.
```python
DEFAULT_PORT = 993
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
...
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
# ...
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
)
```
#### Lists
@ -69,12 +78,16 @@ If a sensor has a pre-defined list of available options, test to make sure the c
```python
SENSOR_TYPES = {
'article_cache': ('Article Cache', 'MB'),
'average_download_rate': ('Average Speed', 'MB/s'),
"article_cache": ("Article Cache", "MB"),
"average_download_rate": ("Average Speed", "MB/s"),
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
...
vol.Optional(CONF_MONITORED_VARIABLES, default=[]):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
# ...
vol.Optional(CONF_MONITORED_VARIABLES, default=[]): vol.All(
cv.ensure_list, [vol.In(SENSOR_TYPES)]
),
}
)
```

View File

@ -44,21 +44,19 @@ Each entity is able to define a device via the `device_info` property. This prop
```python
# Inside a platform
class HueLight(LightEntity):
@property
def device_info(self):
return {
'identifiers': {
"identifiers": {
# Serial numbers are unique identifiers within a specific domain
(hue.DOMAIN, self.unique_id)
},
'name': self.name,
'manufacturer': self.light.manufacturername,
'model': self.light.productname,
'sw_version': self.light.swversion,
'via_device': (hue.DOMAIN, self.api.bridgeid),
"name": self.name,
"manufacturer": self.light.manufacturername,
"model": self.light.productname,
"sw_version": self.light.swversion,
"via_device": (hue.DOMAIN, self.api.bridgeid),
}
```
Components are also able to register devices in the case that there are no entities representing them. An example is a hub that communicates with the lights.
@ -71,13 +69,9 @@ device_registry = await dr.async_get_registry(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={
(dr.CONNECTION_NETWORK_MAC, config.mac)
},
identifiers={
(DOMAIN, config.bridgeid)
},
manufacturer='Signify',
connections={(dr.CONNECTION_NETWORK_MAC, config.mac)},
identifiers={(DOMAIN, config.bridgeid)},
manufacturer="Signify",
name=config.name,
model=config.modelid,
sw_version=config.swversion,

View File

@ -33,7 +33,7 @@ Then you can work on the documentation:
The site generated by `bundle exec rake` is only available locally. If you are developing on a headless machine, use port forwarding:
```bash
```shell
$ ssh -L 4000:localhost:4000 user_on_headless_machine@ip_of_headless_machine
```
@ -41,12 +41,12 @@ $ ssh -L 4000:localhost:4000 user_on_headless_machine@ip_of_headless_machine
Every release we post long changelogs to the website. This slows down generation of the website a bit. We've include some tools to temporarily exclude integrations and blog posts that you're not working on out of the way.
```bash
```shell
bundle exec rake isolate[filename-of-blogpost-or-integration]
```
When you're done working on the site, run the following command to move the pages back again:
```bash
```shell
bundle exec rake integrate
```

View File

@ -108,7 +108,6 @@ class MyCover(CoverDevice):
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
```
### Stop cover

View File

@ -54,10 +54,10 @@ Only implement this method if the flag `SUPPORT_SET_SPEED` is set.
class FanEntity(ToggleEntity):
# Implement one of these methods.
def set_speed(self, speed: str) -> None:
def set_speed(self, speed: str) -> None:
"""Set the speed of the fan."""
async def async_set_speed(self, speed: str):
async def async_set_speed(self, speed: str):
"""Set the speed of the fan."""
```

View File

@ -10,15 +10,15 @@ Below is an example switch entity that keeps track of their state in memory.
```python
from homeassistant.components.switch import SwitchDevice
class MySwitch(SwitchDevice):
class MySwitch(SwitchDevice):
def __init__(self):
self._is_on = False
@property
def name(self):
"""Name of the device."""
return 'My Switch'
return "My Switch"
@property
def is_on(self):

View File

@ -39,23 +39,20 @@ A light entity is a device that controls the brightness, RGB value,color tempera
```python
class MyLightDevice(LightDevice):
def turn_on(self, **kwargs):
"""Turn the device on."""
async def async_turn_on(self, **kwargs):
"""Turn device on."""
def turn_on(self, **kwargs):
"""Turn the device on."""
async def async_turn_on(self, **kwargs):
"""Turn device on."""
```
# Turn Off Light Device
```python
class MyLightDevice(LightDevice):
def turn_off(self, **kwargs):
"""Turn the device off."""
def turn_off(self, **kwargs):
"""Turn the device off."""
async def async_turn_off(self, **kwargs):
"""Turn device off."""
async def async_turn_off(self, **kwargs):
"""Turn device off."""
```

View File

@ -29,7 +29,6 @@ class MySwitch(SwitchDevice):
async def async_turn_on(self, **kwargs):
"""Turn the entity on."""
```
### Turn Off

View File

@ -15,7 +15,7 @@ All API calls have to be accompanied by the header `Authorization: Bearer ABCDEF
There are multiple ways to consume the Home Assistant Rest API. One is with `curl`:
```bash
```shell
$ curl -X GET \
-H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
@ -27,10 +27,10 @@ Another option is to use Python and the [Requests](http://docs.python-requests.o
```python
from requests import get
url = 'http://localhost:8123/ENDPOINT'
url = "http://localhost:8123/ENDPOINT"
headers = {
'Authorization': 'Bearer ABCDEFGH',
'content-type': 'application/json',
"Authorization": "Bearer ABCDEFGH",
"content-type": "application/json",
}
response = get(url, headers=headers)
@ -71,7 +71,7 @@ Returns a message if the API is up and running.
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/
```
@ -123,7 +123,7 @@ Returns the current configuration as JSON.
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/config
```
@ -143,7 +143,7 @@ Returns basic information about the Home Assistant instance as JSON.
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/discovery_info
```
@ -167,7 +167,7 @@ Returns an array of event objects. Each event object contains event name and lis
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/events
```
@ -196,7 +196,7 @@ Returns an array of service objects. Each object contains the domain and which s
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/services
```
@ -241,19 +241,19 @@ You can pass the following optional GET parameters:
Sample `curl` commands:
```bash
```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
```
```bash
```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
```
```bash
```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
@ -282,7 +282,7 @@ Returns an array of state objects. Each state has the following attributes: enti
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" http://localhost:8123/api/states
```
@ -309,7 +309,7 @@ Returns a state object for specified entity_id. Returns 404 if not found.
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/states/sensor.kitchen_temperature
@ -327,7 +327,7 @@ Retrieve all errors logged during the current session of Home Assistant as a pla
Sample `curl` command:
```bash
```shell
$ curl -X GET -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
http://localhost:8123/api/error_log
@ -339,7 +339,7 @@ Returns the data (image) from the specified camera entity_id.
Sample `curl` command:
```bash
```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
@ -378,7 +378,7 @@ The return code is 200 if the entity existed, 201 if the state of a new entity w
Sample `curl` command:
```bash
```shell
$ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
-d '{"state": "25", "attributes": {"unit_of_measurement": "°C"}}' \
@ -440,7 +440,7 @@ Sample `curl` commands:
Turn the light on:
```bash
```shell
$ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
-d '{"entity_id": "switch.christmas_lights"}' \
@ -449,7 +449,7 @@ $ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
Send a MQTT message:
```bash
```shell
$ curl -X POST \
-H "Content-Type: application/json" \
-H "x-ha-access:YOUR_PASSWORD" \
@ -477,7 +477,7 @@ Paulus is at work!
Sample `curl` command:
```bash
```shell
$ curl -X POST -H "Authorization: Bearer ABCDEFGH" \
-H "Content-Type: application/json" \
-d '{"template": "It is {{ now() }}!"}' http://localhost:8123/api/template

View File

@ -10,7 +10,7 @@ A requirement on the client-side is existing support for the [EventSource](https
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`:
```bash
```shell
$ curl -X GET -H 'Authorization: Bearer ABCDEFGH' \
-H "Content-Type: application/json" http://localhost:8123/api/stream
```
@ -41,7 +41,7 @@ Visit [http://localhost:8123/local/sse.html](http://localhost:8123/local/sse.htm
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:
```bash
```shell
$ http --stream http://localhost:8123/api/stream 'Authorization:Bearer ABCDEFGH' content-type:application/json
```
@ -55,7 +55,7 @@ The [home-assistant-sse](https://github.com/fabaff/home-assistant-sse) repositor
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):
```bash
```shell
$ pip3 install sseclient
```
@ -64,8 +64,8 @@ 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)
auth = {"Authorization": "Bearer ABCDEFGH"}
messages = SSEClient("http://localhost:8123/api/stream", headers=auth)
for msg in messages:
print(msg)

View File

@ -14,7 +14,7 @@ Message types are made up the domain and the message type, separated by a forwar
```python
# The type of the message
WS_TYPE_MEDIA_PLAYER_THUMBNAIL = 'media_player/thumbnail'
WS_TYPE_MEDIA_PLAYER_THUMBNAIL = "media_player/thumbnail"
```
### Message Schema
@ -29,12 +29,13 @@ import homeassistant.helpers.config_validation as cv
# The schema for the message
SCHEMA_WEBSOCKET_GET_THUMBNAIL = \
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
'type': WS_TYPE_MEDIA_PLAYER_THUMBNAIL,
SCHEMA_WEBSOCKET_GET_THUMBNAIL = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
{
"type": WS_TYPE_MEDIA_PLAYER_THUMBNAIL,
# The entity that we want to retrieve the thumbnail for.
'entity_id': cv.entity_id
})
"entity_id": cv.entity_id,
}
)
```
### Defining a handler
@ -52,9 +53,11 @@ def websocket_handle_thumbnail(hass, connection, msg):
# We know the answer without having to fetch any information,
# so we send it directly.
connection.to_write.put_nowait(websocket_api.result_message(msg['id'], {
'thumbnail': 'http://via.placeholder.com/350x150'
}))
connection.to_write.put_nowait(
websocket_api.result_message(
msg["id"], {"thumbnail": "http://via.placeholder.com/350x150"}
)
)
```
#### Sending a delayed response
@ -66,12 +69,15 @@ If your command needs to interact with the network, a device or needs to compute
def websocket_handle_thumbnail(hass, connection, msg):
"""Handle get media player cover command."""
# Retrieve media player using passed in entity id.
player = hass.data[DOMAIN].get_entity(msg['entity_id'])
player = hass.data[DOMAIN].get_entity(msg["entity_id"])
# If the player does not exist, send an error message.
if player is None:
connection.to_write.put_nowait(websocket_api.error_message(
msg['id'], 'entity_not_found', 'Entity not found'))
connection.to_write.put_nowait(
websocket_api.error_message(
msg["id"], "entity_not_found", "Entity not found"
)
)
return
# Define a function to be enqueued.
@ -81,16 +87,22 @@ def websocket_handle_thumbnail(hass, connection, msg):
# No media player thumbnail available
if data is None:
connection.send_message_outside(websocket_api.error_message(
msg['id'], 'thumbnail_fetch_failed',
'Failed to fetch thumbnail'))
connection.send_message_outside(
websocket_api.error_message(
msg["id"], "thumbnail_fetch_failed", "Failed to fetch thumbnail"
)
)
return
connection.send_message_outside(websocket_api.result_message(
msg['id'], {
'content_type': content_type,
'content': base64.b64encode(data).decode('utf-8')
}))
connection.send_message_outside(
websocket_api.result_message(
msg["id"],
{
"content_type": content_type,
"content": base64.b64encode(data).decode("utf-8"),
},
)
)
# Player exist. Queue up a job to send the thumbnail.
hass.async_add_job(send_image())
@ -104,8 +116,10 @@ With all pieces defined, it's time to register the command. This is done inside
async def async_setup(hass, config):
"""Setup of your component."""
hass.components.websocket_api.async_register_command(
WS_TYPE_MEDIA_PLAYER_THUMBNAIL, websocket_handle_thumbnail,
SCHEMA_WEBSOCKET_GET_THUMBNAIL)
WS_TYPE_MEDIA_PLAYER_THUMBNAIL,
websocket_handle_thumbnail,
SCHEMA_WEBSOCKET_GET_THUMBNAIL,
)
```
## Calling the command from the frontend (JavaScript)

View File

@ -13,7 +13,7 @@ The Home Assistant frontend is built using web components. For more background a
First step is to fork the [home-assistant-polymer repository][hass-polymer] and add the upstream remote. You can place the forked repository anywhere on your system.
```bash
```shell
$ git clone git@github.com:YOUR_GIT_USERNAME/home-assistant-polymer.git
$ cd home-assistant-polymer
$ git remote add upstream https://github.com/home-assistant/home-assistant-polymer.git
@ -35,7 +35,7 @@ frontend:
Node.js is required to build the frontend. The preferred method of installing node.js is with [nvm](https://github.com/creationix/nvm). Install nvm using the instructions in the [README](https://github.com/creationix/nvm#install-script), and install the correct node.js by running the following command:
```bash
```shell
$ nvm install
```
@ -43,7 +43,7 @@ $ nvm install
Next, development dependencies need to be installed to bootstrap the frontend development environment. First activate the right Node version and then download all the dependencies:
```bash
```shell
$ nvm use
$ script/bootstrap
```
@ -52,7 +52,7 @@ $ script/bootstrap
During development, you will need to run the development script to maintain a development build of the frontend that auto updates when you change any of the source files. To run this server, run:
```bash
```shell
$ nvm use
$ script/develop
```
@ -77,7 +77,7 @@ Make sure you have cache disabled and correct settings to avoid stale content:
If you're planning on issuing a PR back to the Home Assistant codebase you need to fork the polymer project and add your fork as a remote to the Home Assistant Polymer repo.
```bash
```shell
$ git remote add fork <github URL to your fork>
```
@ -95,7 +95,7 @@ If you're making changes to the way the frontend is packaged, it might be necess
To test it out inside Home Assistant, run the following command from the main Home Assistant repository:
```bash
```shell
$ pip3 install -e /path/to/home-assistant-polymer/
$ hass --skip-pip
```

View File

@ -28,7 +28,7 @@ When developing your script:
- `/data` is a volume for persistent storage.
- `/data/options.json` contains the user configuration. You can use bashio or `jq` inside your shell script to parse this data.
```bash
```shell
CONFIG_PATH=/data/options.json
TARGET="$(jq --raw-output '.target' $CONFIG_PATH)"
@ -46,7 +46,7 @@ then there will be a variable `TARGET` containing `beer` in the environment of y
All add-ons are based on latest Alpine Linux. Hass.io will automatically substitute the right base image based on the machine architecture. Add `tzdata` if you need run in a different timezone. `tzdata` Is is already added to our base images.
```
```dockerfile
ARG BUILD_FROM
FROM $BUILD_FROM

View File

@ -38,7 +38,7 @@ You need a Docker Hub account to make your own add-ons. You can build your Docke
For a git repository:
```bash
```shell
$ docker run --rm --privileged -v \
~/.docker:/root/.docker homeassistant/amd64-builder \
--all -t addon-folder -r https://github.com/xy/addons \
@ -47,7 +47,7 @@ $ docker run --rm --privileged -v \
For a local repository:
```bash
```shell
$ docker run --rm --privileged -v \
~/.docker:/root/.docker -v /my_addon:/data homeassistant/amd64-builder \
--all -t /data

View File

@ -26,7 +26,7 @@ Once you have located your add-on directory, it's time to get started!
- Inside that directory create three files.
`Dockerfile`:
```
```dockerfile
ARG BUILD_FROM
FROM $BUILD_FROM
@ -55,7 +55,7 @@ CMD [ "/run.sh" ]
```
`run.sh`:
```bash
```shell
echo Hello world!
```
Make sure your editor is using UNIX-like line breaks (LF), not Dos/Windows (CRLF).
@ -102,7 +102,7 @@ To do this, we will need to update our files as follows:
Add to your `Dockerfile` before `RUN`:
```
```dockerfile
# Install requirements for add-on
RUN apk add --no-cache python3
@ -132,7 +132,7 @@ Add "ports" to `config.json`. This will make TCP on port 8000 inside the contain
Update `run.sh` to start the Python 3 server:
```
```shell
python3 -m http.server 8000
```

View File

@ -47,7 +47,7 @@ Use a USB drive formatted with FAT, ext4, or NTFS and name it CONFIG (case sensi
You should then be able to SSH into your Hass.io device. On Mac/Linux, use:
```
```shell
ssh root@hassio.local -p 22222
```
@ -59,7 +59,7 @@ You will initially be logged in to Hass.io CLI for HassOS where you can perform
## Checking the logs
```bash
```shell
# Logs from the supervisor service on the Host OS
journalctl -f -u hassos-supervisor.service
@ -72,7 +72,7 @@ docker logs homeassistant
## Accessing the container bash
```bash
```shell
docker exec -it homeassistant /bin/bash
```

View File

@ -15,7 +15,7 @@ To develop for the frontend, we're going to need API access to the supervisor.
- Install the Add-on "Remote API proxy"
For some API commands you need explicit the Home Assistant API token, but 99% of the functionality work with `Remote API proxy`. This token change sometimes but you can read the current legal token on host system with:
```sh
```shell
$ docker inspect homeassistant | grep HASSIO_TOKEN
```
@ -25,7 +25,7 @@ $ docker inspect homeassistant | grep HASSIO_TOKEN
First, make sure Home Assistant will load the Hass.io component by adding `hassio:` to your `configuration.yaml` file. Next, we will need to tell the local Home Assistant instance how to connect to the remote Hass.io instance. We do this by setting the `HASSIO` and `HASSIO_TOKEN` environment variables when starting Home Assistant. Note that the `HASSIO` value is not the same as the one that we saw above and the `HASSIO_TOKEN` is available inside log output of "Remote API Add-on" (This changes every restart of the add-on!).
```bash
```shell
HASSIO=<IP OF HASS.IO>:80 HASSIO_TOKEN=<VALUE OF HASSIO_TOKEN> hass
```
@ -47,7 +47,7 @@ hassio:
To build a local version of the Hass.io panel, go to the frontend repository and run:
```bash
```shell
cd hassio
script/develop
```

View File

@ -10,10 +10,10 @@ Example code:
```python
async def async_setup(hass, config):
hass.components.conversation.async_register('MyCoolIntent', [
'I think that {object} is [very] cool',
'Nothing is cooler than {object}'
])
hass.components.conversation.async_register(
"MyCoolIntent",
["I think that {object} is [very] cool", "Nothing is cooler than {object}"],
)
```
If a sentence like "I think that beer is cool" comes in, the conversation component will generate an intent of type `MyCoolIntent` and with 1 slot, named `object` and value `beer`.

View File

@ -9,24 +9,22 @@ Example code to handle an intent in Home Assistant.
```python
from homeassistant.helpers import intent
intent_type = 'TurnLightOn'
slots = {
'entity': { 'value': 'Kitchen' }
}
intent_type = "TurnLightOn"
slots = {"entity": {"value": "Kitchen"}}
try:
intent_response = yield from intent.async_handle(
hass, 'example_component', intent_type, slots
hass, "example_component", intent_type, slots
)
except intent.UnknownIntent as err:
_LOGGER.warning('Received unknown intent %s', intent_type)
_LOGGER.warning("Received unknown intent %s", intent_type)
except intent.InvalidSlotInfo as err:
_LOGGER.error('Received invalid slot data: %s', err)
_LOGGER.error("Received invalid slot data: %s", err)
except intent.IntentError:
_LOGGER.exception('Error handling request for %s', intent_type)
_LOGGER.exception("Error handling request for %s", intent_type)
```
The intent response is an instance of `homeassistant.helpers.intent.IntentResponse`.

View File

@ -10,7 +10,8 @@ A component has to register an intent handler for each type that it wants to han
import asyncio
from homeassistant.helpers import intent
DATA_KEY = 'example_key'
DATA_KEY = "example_key"
@asyncio.coroutine
def async_setup(hass, config):
@ -22,7 +23,7 @@ class CountInvocationIntent(intent.IntentHandler):
"""Handle CountInvocationIntent intents."""
# Type of intent to handle
intent_type = 'CountInvocationIntent'
intent_type = "CountInvocationIntent"
# Optional. A validation schema for slots
# slot_schema = {
@ -36,7 +37,7 @@ class CountInvocationIntent(intent.IntentHandler):
response = intent_obj.create_response()
response.async_set_speech(
"This intent has been invoked {} times".format(
intent_obj.hass.data[DATA_KEY]))
f"This intent has been invoked {intent_obj.hass.data[DATA_KEY]} times"
)
return response
```

View File

@ -7,7 +7,7 @@ Platform translation strings are stored as JSON in the [home-assistant](https://
In order to test changes to translation files, the translation strings must be compiled into Home Assistants translation directories by running the following script:
```bash
```shell
$ script/translations_develop
```

View File

@ -10,14 +10,14 @@ This page documents a couple of points for maintaining the Home Assistant code.
People are using various operating systems to develop components and platforms for Home Assistant. This could lead to different line endings on file. We prefer `LN`. Especially Microsoft Windows tools tend to use `CRLF`.
```bash
```shell
$ find homeassistant -name "*.py" -exec file {} \; | grep BOM
$ find homeassistant -name "*.py" -exec file {} \; | grep CRLF
```
To fix the line separator, use `dos2unix` or `sed`.
```bash
```shell
$ dos2unix homeassistant/components/notify/kodi.py
```
@ -31,7 +31,7 @@ A lot of components and platforms depends on third-party Python modules. The dep
If you update the requirements of a component/platform by updating `manifest.json`, run the provided script to update the `requirements_*.txt` file(s).
```bash
```shell
$ script/gen_requirements_all.py
```

View File

@ -18,6 +18,7 @@ from typing import Iterable, Optional
from homeassistant.core import Context, State
from homeassistant.helpers.typing import HomeAssistantType
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
) -> None: