Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 26 additions & 30 deletions homeassistant/components/cloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@

from homeassistant.const import (
EVENT_HOMEASSISTANT_START, CONF_REGION, CONF_MODE, CONF_NAME, CONF_TYPE)
from homeassistant.helpers import entityfilter
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import entityfilter, config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util import dt as dt_util
from homeassistant.components.alexa import smart_home as alexa_sh
Expand Down Expand Up @@ -105,12 +104,7 @@ def async_setup(hass, config):
)

cloud = hass.data[DOMAIN] = Cloud(hass, **kwargs)

success = yield from cloud.initialize()

if not success:
return False

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, cloud.async_start)
yield from http_api.async_setup(hass)
return True

Expand Down Expand Up @@ -192,19 +186,6 @@ def should_expose(entity):

return self._gactions_config

@asyncio.coroutine
def initialize(self):
"""Initialize and load cloud info."""
jwt_success = yield from self._fetch_jwt_keyset()

if not jwt_success:
return False

self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, self._start_cloud)

return True

def path(self, *parts):
"""Get config path inside cloud dir.

Expand Down Expand Up @@ -234,19 +215,34 @@ def write_user_info(self):
'refresh_token': self.refresh_token,
}, indent=4))

def _start_cloud(self, event):
@asyncio.coroutine
def async_start(self, _):
"""Start the cloud component."""
# Ensure config dir exists
path = self.hass.config.path(CONFIG_DIR)
if not os.path.isdir(path):
os.mkdir(path)
success = yield from self._fetch_jwt_keyset()

user_info = self.user_info_path
if not os.path.isfile(user_info):
# Fetching keyset can fail if internet is not up yet.
if not success:
self.hass.helpers.async_call_later(5, self.async_start)
return

with open(user_info, 'rt') as file:
info = json.loads(file.read())
def load_config():
"""Load config."""
# Ensure config dir exists
path = self.hass.config.path(CONFIG_DIR)
if not os.path.isdir(path):
os.mkdir(path)

user_info = self.user_info_path
if not os.path.isfile(user_info):
return None

with open(user_info, 'rt') as file:
return json.loads(file.read())

info = yield from self.hass.async_add_job(load_config)

if info is None:
return

# Validate tokens
try:
Expand Down
9 changes: 9 additions & 0 deletions homeassistant/helpers/event.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Helpers for listening to events."""
from datetime import timedelta
import functools as ft

from homeassistant.loader import bind_hass
Expand Down Expand Up @@ -219,6 +220,14 @@ def point_in_time_listener(event):
async_track_point_in_utc_time)


@callback
@bind_hass
def async_call_later(hass, delay, action):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this

"""Add a listener that is called in <delay>."""
return async_track_point_in_utc_time(
hass, action, dt_util.utcnow() + timedelta(seconds=delay))


@callback
@bind_hass
def async_track_time_interval(hass, action, interval):
Expand Down
4 changes: 2 additions & 2 deletions tests/components/cloud/test_http_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
@pytest.fixture
def cloud_client(hass, test_client):
"""Fixture that can fetch from the cloud client."""
with patch('homeassistant.components.cloud.Cloud.initialize',
return_value=mock_coro(True)):
with patch('homeassistant.components.cloud.Cloud.async_start',
return_value=mock_coro()):
hass.loop.run_until_complete(async_setup_component(hass, 'cloud', {
'cloud': {
'mode': 'development',
Expand Down
2 changes: 1 addition & 1 deletion tests/components/cloud/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def test_initialize_loads_info(mock_os, hass):

with patch('homeassistant.components.cloud.open', mopen, create=True), \
patch('homeassistant.components.cloud.Cloud._decode_claims'):
cl._start_cloud(None)
yield from cl.async_start(None)

assert cl.id_token == 'test-id-token'
assert cl.access_token == 'test-access-token'
Expand Down
8 changes: 4 additions & 4 deletions tests/components/cloud/test_iot.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ def test_handler_alexa(hass):
hass.states.async_set(
'switch.test2', 'on', {'friendly_name': "Test switch 2"})

with patch('homeassistant.components.cloud.Cloud.initialize',
return_value=mock_coro(True)):
with patch('homeassistant.components.cloud.Cloud.async_start',
return_value=mock_coro()):
setup = yield from async_setup_component(hass, 'cloud', {
'cloud': {
'alexa': {
Expand Down Expand Up @@ -309,8 +309,8 @@ def test_handler_google_actions(hass):
hass.states.async_set(
'switch.test2', 'on', {'friendly_name': "Test switch 2"})

with patch('homeassistant.components.cloud.Cloud.initialize',
return_value=mock_coro(True)):
with patch('homeassistant.components.cloud.Cloud.async_start',
return_value=mock_coro()):
setup = yield from async_setup_component(hass, 'cloud', {
'cloud': {
'google_actions': {
Expand Down
27 changes: 24 additions & 3 deletions tests/helpers/test_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
from astral import Astral
import pytest

from homeassistant.core import callback
from homeassistant.setup import setup_component
import homeassistant.core as ha
from homeassistant.const import MATCH_ALL
from homeassistant.helpers.event import (
async_call_later,
track_point_in_utc_time,
track_point_in_time,
track_utc_time_change,
Expand Down Expand Up @@ -52,7 +54,7 @@ def test_track_point_in_time(self):
runs = []

track_point_in_utc_time(
self.hass, lambda x: runs.append(1), birthday_paulus)
self.hass, callback(lambda x: runs.append(1)), birthday_paulus)

self._send_time_changed(before_birthday)
self.hass.block_till_done()
Expand All @@ -68,14 +70,14 @@ def test_track_point_in_time(self):
self.assertEqual(1, len(runs))

track_point_in_time(
self.hass, lambda x: runs.append(1), birthday_paulus)
self.hass, callback(lambda x: runs.append(1)), birthday_paulus)

self._send_time_changed(after_birthday)
self.hass.block_till_done()
self.assertEqual(2, len(runs))

unsub = track_point_in_time(
self.hass, lambda x: runs.append(1), birthday_paulus)
self.hass, callback(lambda x: runs.append(1)), birthday_paulus)
unsub()

self._send_time_changed(after_birthday)
Expand Down Expand Up @@ -642,3 +644,22 @@ def test_periodic_task_wrong_input(self):
self._send_time_changed(datetime(2014, 5, 2, 0, 0, 0))
self.hass.block_till_done()
self.assertEqual(0, len(specific_runs))


@asyncio.coroutine
def test_async_call_later(hass):
"""Test calling an action later."""
def action(): pass
now = datetime(2017, 12, 19, 15, 40, 0, tzinfo=dt_util.UTC)

with patch('homeassistant.helpers.event'
'.async_track_point_in_utc_time') as mock, \
patch('homeassistant.util.dt.utcnow', return_value=now):
remove = async_call_later(hass, 3, action)

assert len(mock.mock_calls) == 1
p_hass, p_action, p_point = mock.mock_calls[0][1]
assert hass is hass
assert p_action is action
assert p_point == now + timedelta(seconds=3)
assert remove is mock()