diff options
Diffstat (limited to 'unmaintained/home-assistant')
6 files changed, 580 insertions, 0 deletions
diff --git a/unmaintained/home-assistant/95b439fbd5ad96966dfe1176697c6ee4731a6207.patch b/unmaintained/home-assistant/95b439fbd5ad96966dfe1176697c6ee4731a6207.patch new file mode 100644 index 0000000000..f606d6f349 --- /dev/null +++ b/unmaintained/home-assistant/95b439fbd5ad96966dfe1176697c6ee4731a6207.patch @@ -0,0 +1,469 @@ +From 95b439fbd5ad96966dfe1176697c6ee4731a6207 Mon Sep 17 00:00:00 2001 +From: Lewis Juggins <ldjuggins@gmail.com> +Date: Fri, 25 Nov 2016 05:37:56 +0000 +Subject: [PATCH] Upgrade aiohttp to 1.1.5 (#4213) + +--- + homeassistant/components/frontend/__init__.py | 70 +++++++++++++++++---------- + homeassistant/components/http.py | 58 +++++++++++----------- + requirements_all.txt | 6 +-- + requirements_test.txt | 2 +- + setup.py | 4 +- + tests/components/media_player/test_demo.py | 10 ++-- + tests/components/test_google.py | 36 +++++++------- + tests/components/test_panel_iframe.py | 10 ++-- + 8 files changed, 106 insertions(+), 90 deletions(-) + +diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py +index 195d79e..6fde1ae 100644 +--- a/homeassistant/components/frontend/__init__.py ++++ b/homeassistant/components/frontend/__init__.py +@@ -8,7 +8,7 @@ + from aiohttp import web + + from homeassistant.core import callback +-from homeassistant.const import EVENT_HOMEASSISTANT_START, HTTP_NOT_FOUND ++from homeassistant.const import HTTP_NOT_FOUND + from homeassistant.components import api, group + from homeassistant.components.http import HomeAssistantView + from .version import FINGERPRINTS +@@ -18,7 +18,6 @@ + URL_PANEL_COMPONENT = '/frontend/panels/{}.html' + URL_PANEL_COMPONENT_FP = '/frontend/panels/{}-{}.html' + STATIC_PATH = os.path.join(os.path.dirname(__file__), 'www_static') +-PANELS = {} + MANIFEST_JSON = { + "background_color": "#FFFFFF", + "description": "Open-source home automation platform running on Python 3.", +@@ -32,6 +31,16 @@ + "theme_color": "#03A9F4" + } + ++for size in (192, 384, 512, 1024): ++ MANIFEST_JSON['icons'].append({ ++ "src": "/static/icons/favicon-{}x{}.png".format(size, size), ++ "sizes": "{}x{}".format(size, size), ++ "type": "image/png" ++ }) ++ ++DATA_PANELS = 'frontend_panels' ++DATA_INDEX_VIEW = 'frontend_index_view' ++ + # To keep track we don't register a component twice (gives a warning) + _REGISTERED_COMPONENTS = set() + _LOGGER = logging.getLogger(__name__) +@@ -68,10 +77,14 @@ def register_panel(hass, component_name, path, md5=None, sidebar_title=None, + + Warning: this API will probably change. Use at own risk. + """ ++ panels = hass.data.get(DATA_PANELS) ++ if panels is None: ++ panels = hass.data[DATA_PANELS] = {} ++ + if url_path is None: + url_path = component_name + +- if url_path in PANELS: ++ if url_path in panels: + _LOGGER.warning('Overwriting component %s', url_path) + if not os.path.isfile(path): + _LOGGER.error('Panel %s component does not exist: %s', +@@ -106,7 +119,15 @@ def register_panel(hass, component_name, path, md5=None, sidebar_title=None, + fprinted_url = URL_PANEL_COMPONENT_FP.format(component_name, md5) + data['url'] = fprinted_url + +- PANELS[url_path] = data ++ panels[url_path] = data ++ ++ # Register index view for this route if IndexView already loaded ++ # Otherwise it will be done during setup. ++ index_view = hass.data.get(DATA_INDEX_VIEW) ++ ++ if index_view: ++ hass.http.app.router.add_route('get', '/{}'.format(url_path), ++ index_view.get) + + + def add_manifest_json_key(key, val): +@@ -134,29 +155,24 @@ def setup(hass, config): + if os.path.isdir(local): + hass.http.register_static_path("/local", local) + ++ index_view = hass.data[DATA_INDEX_VIEW] = IndexView(hass) ++ hass.http.register_view(index_view) ++ ++ # Components have registered panels before frontend got setup. ++ # Now register their urls. ++ if DATA_PANELS in hass.data: ++ for url_path in hass.data[DATA_PANELS]: ++ hass.http.app.router.add_route('get', '/{}'.format(url_path), ++ index_view.get) ++ else: ++ hass.data[DATA_PANELS] = {} ++ + register_built_in_panel(hass, 'map', 'Map', 'mdi:account-location') + + for panel in ('dev-event', 'dev-info', 'dev-service', 'dev-state', + 'dev-template'): + register_built_in_panel(hass, panel) + +- def register_frontend_index(event): +- """Register the frontend index urls. +- +- Done when Home Assistant is started so that all panels are known. +- """ +- hass.http.register_view(IndexView( +- hass, ['/{}'.format(name) for name in PANELS])) +- +- hass.bus.listen_once(EVENT_HOMEASSISTANT_START, register_frontend_index) +- +- for size in (192, 384, 512, 1024): +- MANIFEST_JSON['icons'].append({ +- "src": "/static/icons/favicon-{}x{}.png".format(size, size), +- "sizes": "{}x{}".format(size, size), +- "type": "image/png" +- }) +- + return True + + +@@ -174,7 +190,7 @@ def get(self, request): + 'states': self.hass.states.async_all(), + 'events': api.async_events_json(self.hass), + 'services': api.async_services_json(self.hass), +- 'panels': PANELS, ++ 'panels': self.hass.data[DATA_PANELS], + }) + + +@@ -186,13 +202,12 @@ class IndexView(HomeAssistantView): + requires_auth = False + extra_urls = ['/states', '/states/{entity_id}'] + +- def __init__(self, hass, extra_urls): ++ def __init__(self, hass): + """Initialize the frontend view.""" + super().__init__(hass) + + from jinja2 import FileSystemLoader, Environment + +- self.extra_urls = self.extra_urls + extra_urls + self.templates = Environment( + loader=FileSystemLoader( + os.path.join(os.path.dirname(__file__), 'templates/') +@@ -223,7 +238,10 @@ def get(self, request, entity_id=None): + else: + panel = request.path.split('/')[1] + +- panel_url = PANELS[panel]['url'] if panel != 'states' else '' ++ if panel == 'states': ++ panel_url = '' ++ else: ++ panel_url = self.hass.data[DATA_PANELS][panel]['url'] + + no_auth = 'true' + if self.hass.config.api.api_password: +@@ -244,7 +262,7 @@ def get(self, request, entity_id=None): + resp = template.render( + core_url=core_url, ui_url=ui_url, no_auth=no_auth, + icons_url=icons_url, icons=FINGERPRINTS['mdi.html'], +- panel_url=panel_url, panels=PANELS) ++ panel_url=panel_url, panels=self.hass.data[DATA_PANELS]) + + return web.Response(text=resp, content_type='text/html') + +diff --git a/homeassistant/components/http.py b/homeassistant/components/http.py +index a008a3f..a6293d0 100644 +--- a/homeassistant/components/http.py ++++ b/homeassistant/components/http.py +@@ -20,7 +20,7 @@ + from aiohttp.file_sender import FileSender + from aiohttp.web_exceptions import ( + HTTPUnauthorized, HTTPMovedPermanently, HTTPNotModified) +-from aiohttp.web_urldispatcher import StaticRoute ++from aiohttp.web_urldispatcher import StaticResource + + from homeassistant.core import is_callback + import homeassistant.remote as rem +@@ -33,7 +33,7 @@ + from homeassistant.components import persistent_notification + + DOMAIN = 'http' +-REQUIREMENTS = ('aiohttp_cors==0.4.0',) ++REQUIREMENTS = ('aiohttp_cors==0.5.0',) + + CONF_API_PASSWORD = 'api_password' + CONF_SERVER_HOST = 'server_host' +@@ -212,13 +212,8 @@ def send(self, request, filepath): + file_size = st.st_size + + resp.content_length = file_size +- resp.set_tcp_cork(True) +- try: +- with filepath.open('rb') as f: +- yield from self._sendfile(request, resp, f, file_size) +- +- finally: +- resp.set_tcp_nodelay(True) ++ with filepath.open('rb') as f: ++ yield from self._sendfile(request, resp, f, file_size) + + return resp + +@@ -226,26 +221,32 @@ def send(self, request, filepath): + _GZIP_FILE_SENDER = GzipFileSender() + + +-class HAStaticRoute(StaticRoute): +- """StaticRoute with support for fingerprinting.""" ++@asyncio.coroutine ++def staticresource_enhancer(app, handler): ++ """Enhance StaticResourceHandler. ++ ++ Adds gzip encoding and fingerprinting matching. ++ """ ++ inst = getattr(handler, '__self__', None) ++ if not isinstance(inst, StaticResource): ++ return handler + +- def __init__(self, prefix, path): +- """Initialize a static route with gzip and cache busting support.""" +- super().__init__(None, prefix, path) +- self._file_sender = _GZIP_FILE_SENDER ++ # pylint: disable=protected-access ++ inst._file_sender = _GZIP_FILE_SENDER + +- def match(self, path): +- """Match path to filename.""" +- if not path.startswith(self._prefix): +- return None ++ @asyncio.coroutine ++ def middleware_handler(request): ++ """Strip out fingerprints from resource names.""" ++ fingerprinted = _FINGERPRINT.match(request.match_info['filename']) + +- # Extra sauce to remove fingerprinted resource names +- filename = path[self._prefix_len:] +- fingerprinted = _FINGERPRINT.match(filename) + if fingerprinted: +- filename = '{}.{}'.format(*fingerprinted.groups()) ++ request.match_info['filename'] = \ ++ '{}.{}'.format(*fingerprinted.groups()) ++ ++ resp = yield from handler(request) ++ return resp + +- return {'filename': filename} ++ return middleware_handler + + + class HomeAssistantWSGI(object): +@@ -257,7 +258,8 @@ def __init__(self, hass, development, api_password, ssl_certificate, + """Initialize the WSGI Home Assistant server.""" + import aiohttp_cors + +- self.app = web.Application(loop=hass.loop) ++ self.app = web.Application(middlewares=[staticresource_enhancer], ++ loop=hass.loop) + self.hass = hass + self.development = development + self.api_password = api_password +@@ -318,11 +320,7 @@ def register_static_path(self, url_root, path, cache_length=31): + Specify optional cache length of asset in days. + """ + if os.path.isdir(path): +- assert url_root.startswith('/') +- if not url_root.endswith('/'): +- url_root += '/' +- route = HAStaticRoute(url_root, path) +- self.app.router.register_route(route) ++ self.app.router.add_static(url_root, path) + return + + filepath = Path(path) +diff --git a/requirements_all.txt b/requirements_all.txt +index af47a1d..f9ff30d 100644 +--- a/requirements_all.txt ++++ b/requirements_all.txt +@@ -6,8 +6,8 @@ pip>=7.0.0 + jinja2>=2.8 + voluptuous==0.9.2 + typing>=3,<4 +-aiohttp==1.0.5 +-async_timeout==1.0.0 ++aiohttp==1.1.5 ++async_timeout==1.1.0 + + # homeassistant.components.nuimo_controller + --only-binary=all git+https://github.com/getSenic/nuimo-linux-python#nuimo==1.0.0 +@@ -31,7 +31,7 @@ SoCo==0.12 + TwitterAPI==2.4.2 + + # homeassistant.components.http +-aiohttp_cors==0.4.0 ++aiohttp_cors==0.5.0 + + # homeassistant.components.apcupsd + apcaccess==0.0.4 +diff --git a/requirements_test.txt b/requirements_test.txt +index 7846318..838e4c9 100644 +--- a/requirements_test.txt ++++ b/requirements_test.txt +@@ -10,7 +10,7 @@ pytest>=2.9.2 + pytest-aiohttp>=0.1.3 + pytest-asyncio>=0.5.0 + pytest-cov>=2.3.1 +-pytest-timeout>=1.0.0 ++pytest-timeout>=1.2.0 + pytest-catchlog>=1.2.2 + requests_mock>=1.0 + mock-open>=1.3.1 +diff --git a/setup.py b/setup.py +index 145b027..7060550 100755 +--- a/setup.py ++++ b/setup.py +@@ -21,8 +21,8 @@ + 'jinja2>=2.8', + 'voluptuous==0.9.2', + 'typing>=3,<4', +- 'aiohttp==1.0.5', +- 'async_timeout==1.0.0', ++ 'aiohttp==1.1.5', ++ 'async_timeout==1.1.0', + ] + + setup( +diff --git a/tests/components/media_player/test_demo.py b/tests/components/media_player/test_demo.py +index 8bcda32..3539c73 100644 +--- a/tests/components/media_player/test_demo.py ++++ b/tests/components/media_player/test_demo.py +@@ -245,13 +245,17 @@ def setUp(self): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() + +- setup_component(self.hass, http.DOMAIN, { ++ assert setup_component(self.hass, http.DOMAIN, { + http.DOMAIN: { + http.CONF_SERVER_PORT: SERVER_PORT, + http.CONF_API_PASSWORD: API_PASSWORD, + }, + }) + ++ assert setup_component( ++ self.hass, mp.DOMAIN, ++ {'media_player': {'platform': 'demo'}}) ++ + self.hass.start() + + def tearDown(self): +@@ -287,10 +291,6 @@ def close(self): + + self.hass._websession = MockWebsession() + +- self.hass.block_till_done() +- assert setup_component( +- self.hass, mp.DOMAIN, +- {'media_player': {'platform': 'demo'}}) + assert self.hass.states.is_state(entity_id, 'playing') + state = self.hass.states.get(entity_id) + req = requests.get(HTTP_BASE_URL + +diff --git a/tests/components/test_google.py b/tests/components/test_google.py +index 10db913..fbaddb1 100644 +--- a/tests/components/test_google.py ++++ b/tests/components/test_google.py +@@ -34,6 +34,7 @@ def test_setup_component(self, mock_do_auth): + self.assertTrue(setup_component(self.hass, 'google', config)) + + def test_get_calendar_info(self): ++ """Test getting the calendar info.""" + calendar = { + 'id': 'qwertyuiopasdfghjklzxcvbnm@import.calendar.google.com', + 'etag': '"3584134138943410"', +@@ -61,21 +62,22 @@ def test_get_calendar_info(self): + }) + + def test_found_calendar(self): +- calendar = { +- 'id': 'qwertyuiopasdfghjklzxcvbnm@import.calendar.google.com', +- 'etag': '"3584134138943410"', +- 'timeZone': 'UTC', +- 'accessRole': 'reader', +- 'foregroundColor': '#000000', +- 'selected': True, +- 'kind': 'calendar#calendarListEntry', +- 'backgroundColor': '#16a765', +- 'description': 'Test Calendar', +- 'summary': 'We are, we are, a... Test Calendar', +- 'colorId': '8', +- 'defaultReminders': [], +- 'track': True +- } ++ """Test when a calendar is found.""" ++ # calendar = { ++ # 'id': 'qwertyuiopasdfghjklzxcvbnm@import.calendar.google.com', ++ # 'etag': '"3584134138943410"', ++ # 'timeZone': 'UTC', ++ # 'accessRole': 'reader', ++ # 'foregroundColor': '#000000', ++ # 'selected': True, ++ # 'kind': 'calendar#calendarListEntry', ++ # 'backgroundColor': '#16a765', ++ # 'description': 'Test Calendar', ++ # 'summary': 'We are, we are, a... Test Calendar', ++ # 'colorId': '8', ++ # 'defaultReminders': [], ++ # 'track': True ++ # } + + # self.assertIsInstance(self.hass.data[google.DATA_INDEX], dict) + # self.assertEquals(self.hass.data[google.DATA_INDEX], {}) +@@ -84,8 +86,8 @@ def test_found_calendar(self): + self.hass.config.path(google.TOKEN_FILE)) + self.assertTrue(google.setup_services(self.hass, True, + calendar_service)) +- self.hass.services.call('google', 'found_calendar', calendar, +- blocking=True) ++ # self.hass.services.call('google', 'found_calendar', calendar, ++ # blocking=True) + + # TODO: Fix this + # self.assertTrue(self.hass.data[google.DATA_INDEX] +diff --git a/tests/components/test_panel_iframe.py b/tests/components/test_panel_iframe.py +index ac479de..cf2fdc2 100644 +--- a/tests/components/test_panel_iframe.py ++++ b/tests/components/test_panel_iframe.py +@@ -14,12 +14,10 @@ class TestPanelIframe(unittest.TestCase): + def setup_method(self, method): + """Setup things to be run when tests are started.""" + self.hass = get_test_home_assistant() +- frontend.PANELS = {} + + def teardown_method(self, method): + """Stop everything that was started.""" + self.hass.stop() +- frontend.PANELS = {} + + def test_wrong_config(self): + """Test setup with wrong configuration.""" +@@ -55,9 +53,9 @@ def test_correct_config(self): + }, + }) + +- # 5 dev tools + map are automatically loaded +- assert len(frontend.PANELS) == 8 +- assert frontend.PANELS['router'] == { ++ # 5 dev tools + map are automatically loaded + 2 iframe panels ++ assert len(self.hass.data[frontend.DATA_PANELS]) == 8 ++ assert self.hass.data[frontend.DATA_PANELS]['router'] == { + 'component_name': 'iframe', + 'config': {'url': 'http://192.168.1.1'}, + 'icon': 'mdi:network-wireless', +@@ -66,7 +64,7 @@ def test_correct_config(self): + 'url_path': 'router' + } + +- assert frontend.PANELS['weather'] == { ++ assert self.hass.data[frontend.DATA_PANELS]['weather'] == { + 'component_name': 'iframe', + 'config': {'url': 'https://www.wunderground.com/us/ca/san-diego'}, + 'icon': 'mdi:weather', diff --git a/unmaintained/home-assistant/APKBUILD b/unmaintained/home-assistant/APKBUILD new file mode 100644 index 0000000000..01e18d79b2 --- /dev/null +++ b/unmaintained/home-assistant/APKBUILD @@ -0,0 +1,64 @@ +# Maintainer: Fabian Affolter <fabian@affolter-engineering.ch> +pkgname=home-assistant +_pkgname=homeassistant +pkgver=0.33.4 +pkgrel=1 +pkgdesc="A Home automation platform" +url="https://home-assistant.io" +arch="all" +license="MIT" +depends=" + py3-requests + py3-yaml + py3-tz + py3-jinja2 + py3-voluptuous + py3-aiohttp + py-netdisco + " +makedepends="python3-dev" +subpackages="" +pkgusers="hass" +pkggroups="hass" +install="$pkgname.pre-install" +source="$pkgname-$pkgver.tar.gz::https://github.com/home-assistant/home-assistant/archive/$pkgver.tar.gz + 95b439fbd5ad96966dfe1176697c6ee4731a6207.patch + update-deps.patch + + $pkgname.initd + $pkgname.confd" + +builddir="$srcdir"/$pkgname-$pkgver + +build() { + cd "$builddir" + python3 setup.py build || return 1 + cd "$_builddir_aiohttp" + python3 setup.py build || return 1 +} + +package() { + cd "$builddir" + python3 setup.py install \ + --root="$pkgdir" --prefix=/usr --optimize=1 || return 1 + install -Dm 755 "$srcdir/$pkgname".initd \ + "$pkgdir"/etc/init.d/hass || return 1 + install -Dm 644 "$srcdir/$pkgname".confd \ + "$pkgdir"/etc/conf.d/hass || return 1 +} + +md5sums="6e48b65ed8cdfc330d7b41b9625a53dc home-assistant-0.33.4.tar.gz +0b10f28946288be517d237fff8bd6a4d 95b439fbd5ad96966dfe1176697c6ee4731a6207.patch +e9594298e7aef8e682bf09bb6801081c update-deps.patch +f98b4840a76b5cee70b2f825cfb26103 home-assistant.initd +6cc698d864cfe3970913202ccf4ccbab home-assistant.confd" +sha256sums="4946fbba5c9951d4b587f4a5048a45b8636d3d5da9e9858899bb81aa30225bfc home-assistant-0.33.4.tar.gz +3e2d0e4c8aca753d8dd39fbe3422cccf141d2d261175dcaa136ff430a0201d57 95b439fbd5ad96966dfe1176697c6ee4731a6207.patch +da580edf8ee2ad1d3855092c9c19421dee4348f78adb12ddb54f9c4ab042f48e update-deps.patch +3518ebc275eee3041ff2821a83b7bb6151990b37c4512b0497874a9eb6b28cc8 home-assistant.initd +ed25ddb01fc0362bb5442ef8ca1ae9a1382334f0cadc54dec53a5d5b5c46c8c3 home-assistant.confd" +sha512sums="9880294ec8e6cf86ce3f7d076dfd4a89857cbf9ac587a40a9550fe981fd5dacdcbb29dd9ebd4906dfd6948249903e359eb6d3fab83a7dd00c95c1cc96e06088d home-assistant-0.33.4.tar.gz +3f4c6f8f2b6ebfc8a57834bfda4e955d6ecefe959ce55599dca94fe78d69ec80f104414c367c9a280c587f8cb88f0ac0a72cb458d2268b094c1b61cdf47568c1 95b439fbd5ad96966dfe1176697c6ee4731a6207.patch +86daecf24c5a595bf7c8ee39c4c2bc7abc829f7e9f4579651c0ab24dc57ea29f50a1c94cae765d7f1ed16c0138b3561587b8675a18d0dd1b117f3527b4cbd82f update-deps.patch +34b8387836490228d94f6836bd0222812285d0a1161225b47b03c1398526f235c2ef2180ebaf0a81fe59165230467b5ad75eb06b3349175ef655da9bd00acd8e home-assistant.initd +12c24a79245ec70f001e63f60737017974f61ac5b508d29f998cd60ad5c0e97dfd0c5efb4f3d8357be1d05b7585de9b8d945f8f73b47e94e3667c9b02138f822 home-assistant.confd" diff --git a/unmaintained/home-assistant/home-assistant.confd b/unmaintained/home-assistant/home-assistant.confd new file mode 100644 index 0000000000..591809b147 --- /dev/null +++ b/unmaintained/home-assistant/home-assistant.confd @@ -0,0 +1,5 @@ +# hass settings +HASS_CONF="/var/lib/hass" +HASS_PID="/run/hass/hass.pid" +HASS_USER="hass" +HASS_GROUP="hass" diff --git a/unmaintained/home-assistant/home-assistant.initd b/unmaintained/home-assistant/home-assistant.initd new file mode 100755 index 0000000000..b5dc6b0748 --- /dev/null +++ b/unmaintained/home-assistant/home-assistant.initd @@ -0,0 +1,18 @@ +#!/sbin/openrc-run + +name="Home Assistant" +command="/usr/bin/hass" +command_args="--config $HASS_CONF --pid-file $HASS_PID --daemon" +start_stop_daemon_args="--user $HASS_USER --group $HASS_GROUP" +pidfile="$HASS_PID" +retry="TERM/30/KILL/5" + +depend() { + need net localmount + after firewall +} + +start_pre() { + checkpath --directory --owner $HASS_USER:$HASS_GROUP --mode 0775 \ + /run/hass +} diff --git a/unmaintained/home-assistant/home-assistant.pre-install b/unmaintained/home-assistant/home-assistant.pre-install new file mode 100644 index 0000000000..b94ac9b418 --- /dev/null +++ b/unmaintained/home-assistant/home-assistant.pre-install @@ -0,0 +1,6 @@ +#!/bin/sh + +addgroup -S hass 2>/dev/null +adduser -S -D -h /var/lib/hass -s /sbin/nologin -G hass -g "Home Assistent" hass 2>/dev/null + +exit 0 diff --git a/unmaintained/home-assistant/update-deps.patch b/unmaintained/home-assistant/update-deps.patch new file mode 100644 index 0000000000..cb7f595766 --- /dev/null +++ b/unmaintained/home-assistant/update-deps.patch @@ -0,0 +1,18 @@ +--- ./setup.py.orig ++++ ./setup.py +@@ -17,12 +17,10 @@ + 'requests>=2,<3', + 'pyyaml>=3.11,<4', + 'pytz>=2016.7', +- 'pip>=7.0.0', + 'jinja2>=2.8', +- 'voluptuous==0.9.2', +- 'typing>=3,<4', +- 'aiohttp==1.1.5', +- 'async_timeout==1.1.0', ++ 'voluptuous>=0.9.2', ++ 'aiohttp>=1.1.5', ++ 'async_timeout>=1.1.0', + ] + + setup( |