home-assistant.github.io/developers/asyncio_working_with_async/index.html
2017-02-15 07:47:12 +00:00

370 lines
No EOL
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Working with Async - Home Assistant</title>
<meta name="author" content="Home Assistant">
<meta name="description" content="A breakdown of all the different ways to work with the asynchronous core of Home Assistant.">
<meta name="viewport" content="width=device-width">
<link rel="canonical" href="https://home-assistant.io/developers/asyncio_working_with_async/">
<meta property="fb:app_id" content="338291289691179">
<meta property="og:title" content="Working with Async">
<meta property="og:site_name" content="Home Assistant">
<meta property="og:url" content="https://home-assistant.io/developers/asyncio_working_with_async/">
<meta property="og:type" content="website">
<meta property="og:description" content="A breakdown of all the different ways to work with the asynchronous core of Home Assistant.">
<meta property="og:image" content="https://home-assistant.io/images/default-social.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@home_assistant">
<meta name="twitter:title" content="Working with Async">
<meta name="twitter:description" content="A breakdown of all the different ways to work with the asynchronous core of Home Assistant.">
<meta name="twitter:image" content="https://home-assistant.io/images/default-social.png">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet">
<link href="/atom.xml" rel="alternate" title="Home Assistant" type="application/atom+xml">
<link rel='shortcut icon' href='/images/favicon.ico' />
<link rel='icon' type='image/png' href='/images/favicon-192x192.png' sizes='192x192' />
</head>
<body >
<header>
<div class="grid-wrapper">
<div class="grid">
<div class="grid__item three-tenths lap-two-sixths palm-one-whole ha-title">
<a href="/" class="site-title">
<img width='40' src='/demo/favicon-192x192.png'>
<span>Home Assistant</span>
</a>
</div>
<div class="grid__item seven-tenths lap-four-sixths palm-one-whole">
<nav>
<input type="checkbox" id="toggle">
<label for="toggle" class="toggle" data-open="Main Menu" data-close="Close Menu"></label>
<ul class="menu pull-right">
<li><a href="/getting-started/">Getting started</a></li>
<li><a href="/components/">Components</a></li>
<li><a href="/ecosystem/">Ecosystem</a></li>
<li><a href="/cookbook/">Examples</a></li>
<li><a href="/developers/">Developers</a></li>
<li><a href="/blog/">Blog</a></li>
<li><a href="/help/">Need help?</a></li>
</ul>
</nav>
</div>
</div>
</div>
</header>
<div class="grid-wrapper">
<div class="grid grid-center">
<div class="grid__item two-thirds lap-one-whole palm-one-whole">
<article class="page">
<header>
<h1 class="title indent">
Working With Async
</h1>
</header>
<hr class="divider">
<p>Although we have a backwards compatible API, using the async core directly will be a lot faster. Most core components have already been rewritten to leverage the async core. This includes the EntityComponent helper (foundation of light, switch, etc), scripts, groups and automation.</p>
<h2><a class="title-link" name="interacting-with-the-core" href="#interacting-with-the-core"></a> Interacting with the core</h2>
<p><a href="https://dev-docs.home-assistant.io/en/master/api/core.html">All methods in the Home Assistant core</a> are implemented in two flavors: an async version and a version to be called from other threads. The versions for other are merely wrappers that call the async version in a threadsafe manner using <a href="https://dev-docs.home-assistant.io/en/dev/api/util.html#module-homeassistant.util.async">the available async utilities</a>.</p>
<p>So if you are making calls to the core (the hass object) from within a callback or coroutine, use the methods that start with async_. If you need to call an async_ function that is a coroutine, your task must also be a coroutine.</p>
<h2><a class="title-link" name="implementing-an-async-component" href="#implementing-an-async-component"></a> Implementing an async component</h2>
<p>To make a component async, implement an async_setup.</p>
<div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="n">hass</span><span class="p">,</span> <span class="n">config</span><span class="p">):</span>
<span class="c"># Setup your component outside of the event loop.</span>
</code></pre>
</div>
<p>Will turn into:</p>
<div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="nd">@asyncio.coroutine</span>
<span class="k">def</span> <span class="nf">async_setup</span><span class="p">(</span><span class="n">hass</span><span class="p">,</span> <span class="n">config</span><span class="p">):</span>
<span class="c"># Setup your component inside of the event loop.</span>
</code></pre>
</div>
<h2><a class="title-link" name="implementing-an-async-platform" href="#implementing-an-async-platform"></a> Implementing an async platform</h2>
<p>For platforms we support async setup. Instead of setup_platform you need to have a coroutine async_setup_platform.</p>
<div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="n">setup_platform</span><span class="p">(</span><span class="n">hass</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="n">add_entities</span><span class="p">,</span> <span class="n">discovery_info</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="c"># Setup your platform outside of the event loop.</span>
</code></pre>
</div>
<p>Will turn into:</p>
<div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="nd">@asyncio.coroutine</span>
<span class="k">def</span> <span class="nf">async_setup_platform</span><span class="p">(</span><span class="n">hass</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="n">async_add_entities</span><span class="p">,</span>
<span class="n">discovery_info</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="c"># Setup your platform inside of the event loop</span>
</code></pre>
</div>
<p>The only difference with the original parameters is that the add_entities function has been replaced by the coroutine <code class="highlighter-rouge">async_add_entities</code>.</p>
<h2><a class="title-link" name="implementing-an-async-entity" href="#implementing-an-async-entity"></a> Implementing an async entity</h2>
<p>You can make your entity async friendly by converting your update method to be async. This requires the dependency of your entities to also be async friendly!</p>
<div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MyEntity</span><span class="p">(</span><span class="n">Entity</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s">"""Retrieve latest state."""</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_state</span> <span class="o">=</span> <span class="n">fetch_state</span><span class="p">()</span>
</code></pre>
</div>
<p>Will turn into:</p>
<div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="k">class</span> <span class="nc">MyEntity</span><span class="p">(</span><span class="n">Entity</span><span class="p">):</span>
<span class="nd">@asyncio.coroutine</span>
<span class="k">def</span> <span class="nf">async_update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s">"""Retrieve latest state."""</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_state</span> <span class="o">=</span> <span class="k">yield</span> <span class="k">from</span> <span class="n">async_fetch_state</span><span class="p">()</span>
</code></pre>
</div>
<p>Make sure that all properties defined on your entity do not result in I/O being done. All data has to be fetched inside the update method and cached on the entity. This is because these properties are read from within the event loop and thus doing I/O will result in the core of Home Assistant waiting until your I/O is done.</p>
<h2><a class="title-link" name="calling-async-functions-from-threads" href="#calling-async-functions-from-threads"></a> Calling async functions from threads</h2>
<p>Sometimes it will happen that youre in a thread and you want to call a function that is only available as async. Home Assistant includes a few async helper utilities to help with this.</p>
<p>In the following example, <code class="highlighter-rouge">say_hello</code> will schedule <code class="highlighter-rouge">async_say_hello</code> and block till the function has run and get the result back.</p>
<div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">homeassistant.util.async</span> <span class="kn">import</span> <span class="n">run_callback_threadsafe</span>
<span class="k">def</span> <span class="nf">say_hello</span><span class="p">(</span><span class="n">hass</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="k">return</span> <span class="n">run_callback_threadsafe</span><span class="p">(</span>
<span class="n">hass</span><span class="o">.</span><span class="n">loop</span><span class="p">,</span> <span class="n">async_say_hello</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span><span class="o">.</span><span class="n">result</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">async_say_hello</span><span class="p">(</span><span class="n">hass</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="k">return</span> <span class="s">"Hello {}!"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
</code></pre>
</div>
<h2><a class="title-link" name="dealing-with-passed-in-functions" href="#dealing-with-passed-in-functions"></a> Dealing with passed in functions</h2>
<p>If your code takes in functions from other code, you will not know which category the function belongs to and how they should be invoked. This usually only occurs if your code supplies an event helper like <code class="highlighter-rouge">mqtt.async_subscribe</code> or <code class="highlighter-rouge">track_state_change_listener</code>.</p>
<p>To help with this, there are two helper methods on the hass object that you can call from inside the event loop:</p>
<h4><a class="title-link" name="hassasync_run_job" href="#hassasync_run_job"></a> hass.async_run_job</h4>
<p>Use this method if the function should be called as soon as possible. This will call callbacks immediately, schedule coroutines for execution on the event loop and schedule other functions to be run inside the thread pool.</p>
<table>
<tbody>
<tr>
<td>Callback</td>
<td>Call immediately.</td>
</tr>
<tr>
<td>Coroutine</td>
<td>Schedule for execution on the event loop.</td>
</tr>
<tr>
<td>Other functions</td>
<td>Schedule for execution in the thread pool.</td>
</tr>
</tbody>
</table>
<h4><a class="title-link" name="hassasync_add_job" href="#hassasync_add_job"></a> hass.async_add_job</h4>
<p>Use this method if the function should be called but not get priority over already scheduled calls.</p>
<table>
<tbody>
<tr>
<td>Callback</td>
<td>Schedule for execution on the event loop.</td>
</tr>
<tr>
<td>Coroutine</td>
<td>Schedule for execution on the event loop.</td>
</tr>
<tr>
<td>Other functions</td>
<td>Schedule for execution in the thread pool.</td>
</tr>
</tbody>
</table>
<h3><a href="/developers/asyncio_misc/">Next step: Miscellaneous »</a></h3>
</article>
</div>
<aside id="sidebar" class="grid__item one-third lap-one-whole palm-one-whole">
<div class="grid">
<section class="aside-module grid__item one-whole lap-one-half">
<div class='edit-github'><a href='https://github.com/home-assistant/home-assistant.github.io/tree/current/source/developers/asyncio_working_with_async.markdown'>Edit this page on GitHub</a></div>
<div class='section'>
<h1 class="title delta">Development Guide</h1>
<ul class='divided sidebar-menu'>
<li>
<a href='/developers/'>Introduction </a>
<ul>
<li><a href='/developers/architecture/'>Architecture </a></li>
<li><a href='/developers/architecture_components/'>Components </a></li>
</ul>
</li>
<li>
<a href='/developers/development/'>Starting with Development </a>
<ul>
<li><a href='/developers/development_environment/'>Setting up Environment </a></li>
<li><a href='/developers/development_submitting/'>Submit your Work </a></li>
<li><a href='/developers/development_checklist/'>Checklist </a></li>
<li><a href='/developers/development_testing/'>Testing </a></li>
<li><a href='/developers/development_catching_up/'>Catching up with Reality </a></li>
<li><a href='/developers/development_validation/'>Validation </a></li>
</ul>
</li>
<li>
<a href='/developers/add_new_platform/'>Support a new device (as a platform) </a>
<ul>
<li><a href='/developers/code_review_platform/'>Checklist creating a platform </a></li>
<li><a href='/developers/platform_example_sensor/'>Example sensor platform </a></li>
<li><a href='/developers/platform_example_light/'>Example light platform </a></li>
</ul>
</li>
<li>
<a href='/developers/creating_components/'>Adding a new component </a>
<ul>
<li><a href='/developers/code_review_component/'>Checklist creating a component </a></li>
<li><a href='/developers/component_loading/'>Loading components </a></li>
<li><a href='/developers/component_deps_and_reqs/'>Requirements & Dependencies </a></li>
<li><a href='/developers/component_initialization/'>Initialization </a></li>
<li><a href='/developers/component_events/'>Handling events </a></li>
<li><a href='/developers/component_states/'>States </a></li>
<li><a href='/developers/component_visibility/'>Visibility </a></li>
<li><a href='/developers/component_generic_discovery/'>Loading Platforms </a></li>
<li><a href='/developers/component_discovery/'>Component Discovery </a></li>
</ul>
</li>
<li>
<a href='/developers/asyncio/'>Asynchronous Programming </a>
<ul>
<li><a href='/developers/asyncio_categorizing_functions/'>Categorizing Functions </a></li>
<li><a class='active' href='/developers/asyncio_working_with_async/'>Working with Async </a></li>
<li><a href='/developers/asyncio_misc/'>Miscellaneous </a></li>
</ul>
</li>
<li>
<a href='/developers/frontend/'>Frontend Development </a>
<ul>
<li><a href='/developers/frontend_add_card/'>Add State Card </a></li>
<li><a href='/developers/frontend_add_more_info/'>Add More Info Dialog </a></li>
<li><a href='/developers/frontend_creating_custom_panels/'>Add Custom Panels </a></li>
<li><a href='/developers/frontend_creating_custom_ui/'>Add Custom UI </a></li>
</ul>
</li>
<li>
<a href='/developers/api/'>API </a>
<ul>
<li><a href='https://dev-docs.home-assistant.io/en/dev/'>Home Assistant API </a></li>
<li><a href='/developers/websocket_api/'>Websocket API </a></li>
<li><a href='/developers/rest_api/'>RESTful API </a></li>
<li><a href='/developers/python_api/'>Python API </a></li>
<li><a href='/developers/server_sent_events/'>Server-sent events </a></li>
</ul>
</li>
<li><a href='/developers/helpers/'>Online helpers </a></li>
<li><a href='/developers/multiple_instances/'>Multiple Instances </a></li>
<li><a href='/developers/website/'>Home-Assistant.io </a></li>
<li><a href='/developers/releasing/'>Releasing </a></li>
<li>
Governance
<ul>
<li><a href='/developers/cla/'>Contributor License Agreement </a></li>
<li><a href='/developers/code_of_conduct/'>Code of Conduct </a></li>
<li><a href='/developers/credits/'>Credits </a></li>
<li><a href='/developers/license/'>License </a></li>
</ul>
</li>
</ul>
</div>
</section>
</div>
</aside>
</div>
</div>
<footer>
<div class="grid-wrapper">
<div class="grid">
<div class="grid__item">
<div class="copyright">
<a rel="me" href='https://twitter.com/home_assistant'><i class="icon-twitter"></i></a>
<a rel="me" href='https://facebook.com/homeassistantio'><i class="icon-facebook"></i></a>
<a rel="me" href='https://plus.google.com/110560654828510104551'><i class="icon-google-plus"></i></a>
<a rel="me" href='https://github.com/home-assistant/home-assistant'><i class="icon-github"></i></a>
<div class="credit">
Contact us at <a href='mailto:hello@home-assistant.io'>hello@home-assistant.io</a>.<br>
Website powered by <a href='http://jekyllrb.com/'>Jekyll</a> and the <a href='https://github.com/coogie/oscailte'>Oscalite theme</a>.<br />
Hosted by <a href='https://pages.github.com/'>GitHub</a> and served by <a href='https://cloudflare.com'>CloudFlare</a>.
</div>
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">home-assistant.io</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.
</div>
</div>
</div>
</div>
</footer>
<script>
var _gaq=[['_setAccount','UA-57927901-1'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</body>
</html>