22import asyncio
33import logging
44
5+ import aiohttp
6+ from aiohttp import web
7+ from aiohttp .web_exceptions import HTTPBadGateway
8+ from aiohttp .hdrs import CONTENT_TYPE
9+ import async_timeout
510import voluptuous as vol
611
712from .util import api_process , api_process_raw , api_validate
813from ..const import (
914 ATTR_VERSION , ATTR_LAST_VERSION , ATTR_DEVICES , ATTR_IMAGE , ATTR_CUSTOM ,
10- ATTR_BOOT , CONTENT_TYPE_BINARY )
11- from ..validate import HASS_DEVICES
15+ ATTR_BOOT , ATTR_PORT , ATTR_PASSWORD , ATTR_SSL , ATTR_WATCHDOG ,
16+ CONTENT_TYPE_BINARY , HEADER_HA_ACCESS )
17+ from ..validate import HASS_DEVICES , NETWORK_PORT
1218
1319_LOGGER = logging .getLogger (__name__ )
1420
2026 vol .Inclusive (ATTR_IMAGE , 'custom_hass' ): vol .Any (None , vol .Coerce (str )),
2127 vol .Inclusive (ATTR_LAST_VERSION , 'custom_hass' ):
2228 vol .Any (None , vol .Coerce (str )),
29+ vol .Optional (ATTR_PORT ): NETWORK_PORT ,
30+ vol .Optional (ATTR_PASSWORD ): vol .Any (None , vol .Coerce (str )),
31+ vol .Optional (ATTR_SSL ): vol .Boolean (),
32+ vol .Optional (ATTR_WATCHDOG ): vol .Boolean (),
2333})
2434
2535SCHEMA_VERSION = vol .Schema ({
@@ -36,6 +46,45 @@ def __init__(self, config, loop, homeassistant):
3646 self .loop = loop
3747 self .homeassistant = homeassistant
3848
49+ async def homeassistant_proxy (self , path , request ):
50+ """Return a client request with proxy origin for Home-Assistant."""
51+ url = "{}/api/{}" .format (self .homeassistant .api_url , path )
52+
53+ try :
54+ data = None
55+ headers = {}
56+ method = getattr (
57+ self .homeassistant .websession , request .method .lower ())
58+
59+ # read data
60+ with async_timeout .timeout (10 , loop = self .loop ):
61+ data = await request .read ()
62+
63+ if data :
64+ headers .update ({CONTENT_TYPE : request .content_type })
65+
66+ # need api password?
67+ if self .homeassistant .api_password :
68+ headers = {HEADER_HA_ACCESS : self .homeassistant .api_password }
69+
70+ # reset headers
71+ if not headers :
72+ headers = None
73+
74+ client = await method (
75+ url , data = data , headers = headers , timeout = 300
76+ )
77+
78+ return client
79+
80+ except aiohttp .ClientError as err :
81+ _LOGGER .error ("Client error on api %s request %s." , path , err )
82+
83+ except asyncio .TimeoutError :
84+ _LOGGER .error ("Client timeout error on api request %s." , path )
85+
86+ raise HTTPBadGateway ()
87+
3988 @api_process
4089 async def info (self , request ):
4190 """Return host information."""
@@ -46,6 +95,9 @@ async def info(self, request):
4695 ATTR_DEVICES : self .homeassistant .devices ,
4796 ATTR_CUSTOM : self .homeassistant .is_custom_image ,
4897 ATTR_BOOT : self .homeassistant .boot ,
98+ ATTR_PORT : self .homeassistant .api_port ,
99+ ATTR_SSL : self .homeassistant .api_ssl ,
100+ ATTR_WATCHDOG : self .homeassistant .watchdog ,
49101 }
50102
51103 @api_process
@@ -63,6 +115,18 @@ async def options(self, request):
63115 if ATTR_BOOT in body :
64116 self .homeassistant .boot = body [ATTR_BOOT ]
65117
118+ if ATTR_PORT in body :
119+ self .homeassistant .api_port = body [ATTR_PORT ]
120+
121+ if ATTR_PASSWORD in body :
122+ self .homeassistant .api_password = body [ATTR_PASSWORD ]
123+
124+ if ATTR_SSL in body :
125+ self .homeassistant .api_ssl = body [ATTR_SSL ]
126+
127+ if ATTR_WATCHDOG in body :
128+ self .homeassistant .watchdog = body [ATTR_WATCHDOG ]
129+
66130 return True
67131
68132 @api_process
@@ -105,3 +169,14 @@ async def check(self, request):
105169 raise RuntimeError (message )
106170
107171 return True
172+
173+ async def api (self , request ):
174+ """Proxy API request to Home-Assistant."""
175+ path = request .match_info .get ('path' )
176+
177+ client = await self .homeassistant_proxy (path , request )
178+ return web .Response (
179+ body = await client .read (),
180+ status = client .status ,
181+ content_type = client .content_type
182+ )
0 commit comments