Compare commits

...

8 Commits

Author SHA1 Message Date
Paulus Schoutsen 3ea984ca25 Version bump to 0.35.2 2016-12-18 15:00:37 -08:00
Paulus Schoutsen 1258c4c680 Base url: Fix external port different from internal port (#4990)
* Base url: Fix external port different from internal port

* Add base_url example to new config
2016-12-18 15:00:16 -08:00
Paulus Schoutsen 75dd391118 Merge pull request #4988 from home-assistant/release-0-35-1
0.35.1
2016-12-18 14:11:10 -08:00
Paulus Schoutsen 76a9eba744 Version bump to 0.35.1 2016-12-18 13:00:35 -08:00
Paulus Schoutsen 31fe1d28e8 Gracefully exit with async logger (#4965)
* Gracefully exit with async logger

* Lint
2016-12-18 13:00:21 -08:00
Pascal Vizeli a4a38c8a00 Bugfix TTS clear cache (#4974)
* Bugfix TTS base url with certificate

* fix lint

* remove base_url stuff fix only clear_cache stuff

* cleanup
2016-12-18 13:00:21 -08:00
Paulus Schoutsen 3b74cc606e Allow setting base url (#4985) 2016-12-18 13:00:21 -08:00
Pascal Vizeli b750319de4 Bugfix wait in automation (#4984) 2016-12-18 13:00:21 -08:00
10 changed files with 146 additions and 44 deletions
@@ -166,7 +166,9 @@ def async_setup(hass, config):
for entity in component.async_extract_from_service(service_call):
tasks.append(entity.async_trigger(
service_call.data.get(ATTR_VARIABLES), True))
yield from asyncio.wait(tasks, loop=hass.loop)
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def turn_onoff_service_handler(service_call):
@@ -175,7 +177,9 @@ def async_setup(hass, config):
method = 'async_{}'.format(service_call.service)
for entity in component.async_extract_from_service(service_call):
tasks.append(getattr(entity, method)())
yield from asyncio.wait(tasks, loop=hass.loop)
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def toggle_service_handler(service_call):
@@ -186,7 +190,9 @@ def async_setup(hass, config):
tasks.append(entity.async_turn_off())
else:
tasks.append(entity.async_turn_on())
yield from asyncio.wait(tasks, loop=hass.loop)
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def reload_service_handler(service_call):
+16 -4
View File
@@ -18,7 +18,7 @@ from aiohttp.web_exceptions import HTTPUnauthorized, HTTPMovedPermanently
import homeassistant.helpers.config_validation as cv
import homeassistant.remote as rem
from homeassistant.util import get_local_ip
import homeassistant.util as hass_util
from homeassistant.components import persistent_notification
from homeassistant.const import (
SERVER_PORT, CONTENT_TYPE_JSON, ALLOWED_CORS_HEADERS,
@@ -41,6 +41,7 @@ REQUIREMENTS = ('aiohttp_cors==0.5.0',)
CONF_API_PASSWORD = 'api_password'
CONF_SERVER_HOST = 'server_host'
CONF_SERVER_PORT = 'server_port'
CONF_BASE_URL = 'base_url'
CONF_DEVELOPMENT = 'development'
CONF_SSL_CERTIFICATE = 'ssl_certificate'
CONF_SSL_KEY = 'ssl_key'
@@ -84,6 +85,7 @@ HTTP_SCHEMA = vol.Schema({
vol.Optional(CONF_SERVER_HOST, default=DEFAULT_SERVER_HOST): cv.string,
vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT):
vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)),
vol.Optional(CONF_BASE_URL): cv.string,
vol.Optional(CONF_DEVELOPMENT, default=DEFAULT_DEVELOPMENT): cv.string,
vol.Optional(CONF_SSL_CERTIFICATE, default=None): cv.isfile,
vol.Optional(CONF_SSL_KEY, default=None): cv.isfile,
@@ -155,9 +157,19 @@ def async_setup(hass, config):
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_server)
hass.http = server
hass.config.api = rem.API(server_host if server_host != '0.0.0.0'
else get_local_ip(),
api_password, server_port,
host = conf.get(CONF_BASE_URL)
if host:
port = None
elif server_host != DEFAULT_SERVER_HOST:
host = server_host
port = server_port
else:
host = hass_util.get_local_ip()
port = server_port
hass.config.api = rem.API(host, api_password, port,
ssl_certificate is not None)
return True
+6 -5
View File
@@ -157,7 +157,8 @@ def async_setup(hass, config):
hass.services.async_register(
DOMAIN, SERVICE_CLEAR_CACHE, async_clear_cache_handle,
descriptions.get(SERVICE_CLEAR_CACHE), schema=SERVICE_CLEAR_CACHE)
descriptions.get(SERVICE_CLEAR_CACHE),
schema=SCHEMA_SERVICE_CLEAR_CACHE)
return True
@@ -170,9 +171,9 @@ class SpeechManager(object):
self.hass = hass
self.providers = {}
self.use_cache = True
self.cache_dir = None
self.time_memory = None
self.use_cache = DEFAULT_CACHE
self.cache_dir = DEFAULT_CACHE_DIR
self.time_memory = DEFAULT_TIME_MEMORY
self.file_cache = {}
self.mem_cache = {}
@@ -229,7 +230,7 @@ class SpeechManager(object):
"""Remove files from filesystem."""
for _, filename in self.file_cache.items():
try:
os.remove(os.path.join(self.cache_dir), filename)
os.remove(os.path.join(self.cache_dir, filename))
except OSError:
pass
+7
View File
@@ -54,6 +54,8 @@ frontend:
http:
# Uncomment this to add a password (recommended!)
# api_password: PASSWORD
# Uncomment this if you are using SSL or running in Docker etc
# base_url: example.duckdns.org:8123
# Checks for available updates
updater:
@@ -76,6 +78,11 @@ sun:
# Weather Prediction
sensor:
platform: yr
# Text to speech
tts:
platform: google
"""
+1 -1
View File
@@ -2,7 +2,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 35
PATCH_VERSION = '0'
PATCH_VERSION = '2'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 4, 2)
+2 -3
View File
@@ -298,9 +298,8 @@ class HomeAssistant(object):
# cleanup async layer from python logging
if self.data.get(DATA_ASYNCHANDLER):
handler = self.data.pop(DATA_ASYNCHANDLER)
logger = logging.getLogger('')
handler.close()
logger.removeHandler(handler)
logging.getLogger('').removeHandler(handler)
yield from handler.async_close(blocking=True)
self.loop.stop()
+11 -6
View File
@@ -55,15 +55,20 @@ class API(object):
"""Object to pass around Home Assistant API location and credentials."""
def __init__(self, host: str, api_password: Optional[str]=None,
port: Optional[int]=None, use_ssl: bool=False) -> None:
port: Optional[int]=SERVER_PORT, use_ssl: bool=False) -> None:
"""Initalize the API."""
self.host = host
self.port = port or SERVER_PORT
self.port = port
self.api_password = api_password
if use_ssl:
self.base_url = "https://{}:{}".format(host, self.port)
self.base_url = "https://{}".format(host)
else:
self.base_url = "http://{}:{}".format(host, self.port)
self.base_url = "http://{}".format(host)
if port is not None:
self.base_url += ':{}'.format(port)
self.status = None
self._headers = {
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON,
@@ -106,8 +111,8 @@ class API(object):
def __repr__(self) -> str:
"""Return the representation of the API."""
return "API({}, {}, {})".format(
self.host, self.api_password, self.port)
return "<API({}, password: {})>".format(
self.base_url, 'yes' if self.api_password is not None else 'no')
class HomeAssistant(ha.HomeAssistant):
+25
View File
@@ -49,6 +49,23 @@ class AsyncHandler(object):
"""Wrap close to handler."""
self.emit(None)
@asyncio.coroutine
def async_close(self, blocking=False):
"""Close the handler.
When blocking=True, will wait till closed.
"""
self.close()
if blocking:
# Python 3.4.4+
# pylint: disable=no-member
if hasattr(self._queue, 'join'):
yield from self._queue.join()
else:
while not self._queue.empty():
yield from asyncio.sleep(0, loop=self.loop)
def emit(self, record):
"""Process a record."""
ident = self.loop.__dict__.get("_thread_ident")
@@ -66,15 +83,23 @@ class AsyncHandler(object):
def _process(self):
"""Process log in a thread."""
support_join = hasattr(self._queue, 'task_done')
while True:
record = run_coroutine_threadsafe(
self._queue.get(), self.loop).result()
# pylint: disable=no-member
if record is None:
self.handler.close()
if support_join:
self.loop.call_soon_threadsafe(self._queue.task_done)
return
self.handler.emit(record)
if support_join:
self.loop.call_soon_threadsafe(self._queue.task_done)
def createLock(self):
"""Ignore lock stuff."""
+47
View File
@@ -1,6 +1,7 @@
"""The tests for the Home Assistant HTTP component."""
import asyncio
import requests
from unittest.mock import MagicMock
from homeassistant import bootstrap, const
import homeassistant.components.http as http
@@ -154,3 +155,49 @@ def test_registering_view_while_running(hass, test_client):
text = yield from resp.text()
assert text == 'hello'
def test_api_base_url(loop):
"""Test setting api url."""
hass = MagicMock()
hass.loop = loop
assert loop.run_until_complete(
bootstrap.async_setup_component(hass, 'http', {
'http': {
'base_url': 'example.com'
}
})
)
assert hass.config.api.base_url == 'http://example.com'
assert loop.run_until_complete(
bootstrap.async_setup_component(hass, 'http', {
'http': {
'server_host': '1.1.1.1'
}
})
)
assert hass.config.api.base_url == 'http://1.1.1.1:8123'
assert loop.run_until_complete(
bootstrap.async_setup_component(hass, 'http', {
'http': {
'server_host': '1.1.1.1'
}
})
)
assert hass.config.api.base_url == 'http://1.1.1.1:8123'
assert loop.run_until_complete(
bootstrap.async_setup_component(hass, 'http', {
'http': {
}
})
)
assert hass.config.api.base_url == 'http://127.0.0.1:8123'
+22 -22
View File
@@ -88,35 +88,35 @@ class TestTTS(object):
self.default_tts_cache,
"265944c108cbb00b2a621be5930513e03a0bb2cd_demo.mp3"))
def test_setup_component_and_test_service_clear_cache(self):
"""Setup the demo platform and call service clear cache."""
calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
def test_setup_component_and_test_service_clear_cache(self):
"""Setup the demo platform and call service clear cache."""
calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
config = {
tts.DOMAIN: {
'platform': 'demo',
}
config = {
tts.DOMAIN: {
'platform': 'demo',
}
}
with assert_setup_component(1, tts.DOMAIN):
setup_component(self.hass, tts.DOMAIN, config)
with assert_setup_component(1, tts.DOMAIN):
setup_component(self.hass, tts.DOMAIN, config)
self.hass.services.call(tts.DOMAIN, 'demo_say', {
tts.ATTR_MESSAGE: "I person is on front of your door.",
})
self.hass.block_till_done()
self.hass.services.call(tts.DOMAIN, 'demo_say', {
tts.ATTR_MESSAGE: "I person is on front of your door.",
})
self.hass.block_till_done()
assert len(calls) == 1
assert os.path.isfile(os.path.join(
self.default_tts_cache,
"265944c108cbb00b2a621be5930513e03a0bb2cd_demo.mp3"))
assert len(calls) == 1
assert os.path.isfile(os.path.join(
self.default_tts_cache,
"265944c108cbb00b2a621be5930513e03a0bb2cd_demo.mp3"))
self.hass.services.call(tts.DOMAIN, tts.SERVICE_CLEAR_CACHE, {})
self.hass.block_till_done()
self.hass.services.call(tts.DOMAIN, tts.SERVICE_CLEAR_CACHE, {})
self.hass.block_till_done()
assert not os.path.isfile(os.path.join(
self.default_tts_cache,
"265944c108cbb00b2a621be5930513e03a0bb2cd_demo.mp3"))
assert not os.path.isfile(os.path.join(
self.default_tts_cache,
"265944c108cbb00b2a621be5930513e03a0bb2cd_demo.mp3"))
def test_setup_component_and_test_service_with_receive_voice(self):
"""Setup the demo platform and call service and receive voice."""