Merge pull request #6227 from home-assistant/release-0-39

Release 0 39
This commit is contained in:
Paulus Schoutsen 2017-02-25 15:04:54 -08:00 committed by GitHub
commit 58f9455604
9 changed files with 31 additions and 22 deletions

View File

@ -3,7 +3,7 @@
FINGERPRINTS = { FINGERPRINTS = {
"compatibility.js": "83d9c77748dafa9db49ae77d7f3d8fb0", "compatibility.js": "83d9c77748dafa9db49ae77d7f3d8fb0",
"core.js": "1f7f88d8f5dada08bce1d935cfa5f33e", "core.js": "1f7f88d8f5dada08bce1d935cfa5f33e",
"frontend.html": "be258a53166b82f4ebd5232037e1cbd5", "frontend.html": "ca9efa7e4506aa6b1a668703c8d0f800",
"mdi.html": "c1dde43ccf5667f687c418fc8daf9668", "mdi.html": "c1dde43ccf5667f687c418fc8daf9668",
"micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a",
"panels/ha-panel-config.html": "412b3e24515ffa1ee8074ce974cf4057", "panels/ha-panel-config.html": "412b3e24515ffa1ee8074ce974cf4057",

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 5d223c8da4b4380bf79d8f0285ce7824063e89ef Subproject commit e509ed07a08d35152b9eea6e263411dfc027867b

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ to query this database.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/recorder/ https://home-assistant.io/components/recorder/
""" """
import asyncio
import logging import logging
import queue import queue
import threading import threading
@ -20,7 +21,7 @@ import voluptuous as vol
from homeassistant.core import HomeAssistant, callback, split_entity_id from homeassistant.core import HomeAssistant, callback, split_entity_id
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, CONF_ENTITIES, CONF_EXCLUDE, CONF_DOMAINS, ATTR_ENTITY_ID, CONF_ENTITIES, CONF_EXCLUDE, CONF_DOMAINS,
CONF_INCLUDE, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_INCLUDE, EVENT_HOMEASSISTANT_STOP,
EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL) EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL)
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -83,7 +84,18 @@ def session_scope():
session.close() session.close()
def get_instance() -> None: @asyncio.coroutine
def async_get_instance():
"""Throw error if recorder not initialized."""
if _INSTANCE is None:
raise RuntimeError("Recorder not initialized.")
yield from _INSTANCE.async_db_ready.wait()
return _INSTANCE
def get_instance():
"""Throw error if recorder not initialized.""" """Throw error if recorder not initialized."""
if _INSTANCE is None: if _INSTANCE is None:
raise RuntimeError("Recorder not initialized.") raise RuntimeError("Recorder not initialized.")
@ -200,7 +212,7 @@ class Recorder(threading.Thread):
self.recording_start = dt_util.utcnow() self.recording_start = dt_util.utcnow()
self.db_url = uri self.db_url = uri
self.db_ready = threading.Event() self.db_ready = threading.Event()
self.start_recording = threading.Event() self.async_db_ready = asyncio.Event(loop=hass.loop)
self.engine = None # type: Any self.engine = None # type: Any
self._run = None # type: Any self._run = None # type: Any
@ -209,11 +221,6 @@ class Recorder(threading.Thread):
self.exclude = exclude.get(CONF_ENTITIES, []) + \ self.exclude = exclude.get(CONF_ENTITIES, []) + \
exclude.get(CONF_DOMAINS, []) exclude.get(CONF_DOMAINS, [])
def start_recording(event):
"""Start recording."""
self.start_recording.set()
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_recording)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, self.shutdown) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, self.shutdown)
hass.bus.listen(MATCH_ALL, self.event_listener) hass.bus.listen(MATCH_ALL, self.event_listener)
@ -229,6 +236,7 @@ class Recorder(threading.Thread):
self._setup_connection() self._setup_connection()
self._setup_run() self._setup_run()
self.db_ready.set() self.db_ready.set()
self.async_db_ready.set()
break break
except SQLAlchemyError as err: except SQLAlchemyError as err:
_LOGGER.error("Error during connection setup: %s (retrying " _LOGGER.error("Error during connection setup: %s (retrying "
@ -239,8 +247,6 @@ class Recorder(threading.Thread):
async_track_time_interval( async_track_time_interval(
self.hass, self._purge_old_data, timedelta(days=2)) self.hass, self._purge_old_data, timedelta(days=2))
_wait(self.start_recording, "Waiting to start recording")
while True: while True:
event = self.queue.get() event = self.queue.get()
@ -297,9 +303,6 @@ class Recorder(threading.Thread):
"""Tell the recorder to shut down.""" """Tell the recorder to shut down."""
global _INSTANCE # pylint: disable=global-statement global _INSTANCE # pylint: disable=global-statement
self.queue.put(None) self.queue.put(None)
if not self.start_recording.is_set():
_LOGGER.warning("Recorder never started correctly")
self.start_recording.set()
self.join() self.join()
_INSTANCE = None _INSTANCE = None
@ -505,7 +508,7 @@ def _wait(event, message):
event.wait(10) event.wait(10)
if event.is_set(): if event.is_set():
return return
msg = message + " ({} seconds)".format(retry) msg = "{} ({} seconds)".format(message, retry)
_LOGGER.warning(msg) _LOGGER.warning(msg)
if not event.is_set(): if not event.is_set():
raise HomeAssistantError(msg) raise HomeAssistantError(msg)

View File

@ -6,7 +6,8 @@ from datetime import timedelta
from homeassistant.core import HomeAssistant, CoreState, callback from homeassistant.core import HomeAssistant, CoreState, callback
from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.components.history import get_states, last_recorder_run from homeassistant.components.history import get_states, last_recorder_run
from homeassistant.components.recorder import DOMAIN as _RECORDER from homeassistant.components.recorder import (
async_get_instance, DOMAIN as _RECORDER)
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -57,6 +58,8 @@ def async_get_last_state(hass, entity_id: str):
hass.state) hass.state)
return None return None
yield from async_get_instance() # Ensure recorder ready
if _LOCK not in hass.data: if _LOCK not in hass.data:
hass.data[_LOCK] = asyncio.Lock(loop=hass.loop) hass.data[_LOCK] = asyncio.Lock(loop=hass.loop)

View File

@ -11,7 +11,8 @@ from homeassistant.components import input_boolean, recorder
from homeassistant.helpers.restore_state import ( from homeassistant.helpers.restore_state import (
async_get_last_state, DATA_RESTORE_CACHE) async_get_last_state, DATA_RESTORE_CACHE)
from tests.common import get_test_home_assistant, init_recorder_component from tests.common import (
get_test_home_assistant, mock_coro, init_recorder_component)
@asyncio.coroutine @asyncio.coroutine
@ -29,7 +30,9 @@ def test_caching_data(hass):
with patch('homeassistant.helpers.restore_state.last_recorder_run', with patch('homeassistant.helpers.restore_state.last_recorder_run',
return_value=MagicMock(end=dt_util.utcnow())), \ return_value=MagicMock(end=dt_util.utcnow())), \
patch('homeassistant.helpers.restore_state.get_states', patch('homeassistant.helpers.restore_state.get_states',
return_value=states): return_value=states), \
patch('homeassistant.helpers.restore_state.async_get_instance',
return_value=mock_coro()):
state = yield from async_get_last_state(hass, 'input_boolean.b1') state = yield from async_get_last_state(hass, 'input_boolean.b1')
assert DATA_RESTORE_CACHE in hass.data assert DATA_RESTORE_CACHE in hass.data