Merge pull request #4988 from home-assistant/release-0-35-1

0.35.1
This commit is contained in:
Paulus Schoutsen 2016-12-18 14:11:10 -08:00 committed by GitHub
commit 75dd391118
8 changed files with 126 additions and 38 deletions

View File

@ -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):

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,17 @@ 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:
pass
elif server_host != DEFAULT_SERVER_HOST:
host = server_host
else:
host = hass_util.get_local_ip()
hass.config.api = rem.API(host, api_password, server_port,
ssl_certificate is not None)
return True

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

View File

@ -2,7 +2,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 35
PATCH_VERSION = '0'
PATCH_VERSION = '1'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 4, 2)

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()

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."""

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: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': {
'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'

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."""