Skip to content

Commit

Permalink
Cloud unauth (#12840)
Browse files Browse the repository at this point in the history
* Handle expired refresh token better

* Retry less aggressive

* Newline
  • Loading branch information
balloob committed Mar 2, 2018
1 parent 7a979e9 commit 228b030
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 7 deletions.
6 changes: 6 additions & 0 deletions homeassistant/components/cloud/const.py
Expand Up @@ -16,3 +16,9 @@
It looks like your Home Assistant Cloud subscription has expired. Please check
your [account page](/config/cloud/account) to continue using the service.
"""

MESSAGE_AUTH_FAIL = """
You have been logged out of Home Assistant Cloud because we have been unable
to verify your credentials. Please [log in](/config/cloud) again to continue
using the service.
"""
20 changes: 15 additions & 5 deletions homeassistant/components/cloud/iot.py
Expand Up @@ -10,7 +10,7 @@
from homeassistant.util.decorator import Registry
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from . import auth_api
from .const import MESSAGE_EXPIRATION
from .const import MESSAGE_EXPIRATION, MESSAGE_AUTH_FAIL

HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -77,9 +77,9 @@ def _handle_hass_stop(event):
self.tries += 1

try:
# Sleep 0, 5, 10, 15 ... 30 seconds between retries
# Sleep 2^tries seconds between retries
self.retry_task = hass.async_add_job(asyncio.sleep(
min(30, (self.tries - 1) * 5), loop=hass.loop))
2**min(9, self.tries), loop=hass.loop))
yield from self.retry_task
self.retry_task = None
except asyncio.CancelledError:
Expand All @@ -97,13 +97,23 @@ def _handle_connection(self):

try:
yield from hass.async_add_job(auth_api.check_token, self.cloud)
except auth_api.Unauthenticated as err:
_LOGGER.error('Unable to refresh token: %s', err)

hass.components.persistent_notification.async_create(
MESSAGE_AUTH_FAIL, 'Home Assistant Cloud',
'cloud_subscription_expired')

# Don't await it because it will cancel this task
hass.async_add_job(self.cloud.logout())
return
except auth_api.CloudError as err:
_LOGGER.warning("Unable to connect: %s", err)
_LOGGER.warning("Unable to refresh token: %s", err)
return

if self.cloud.subscription_expired:
hass.components.persistent_notification.async_create(
MESSAGE_EXPIRATION, 'Subscription expired',
MESSAGE_EXPIRATION, 'Home Assistant Cloud',
'cloud_subscription_expired')
self.close_requested = True
return
Expand Down
18 changes: 16 additions & 2 deletions tests/components/cloud/test_iot.py
Expand Up @@ -6,7 +6,7 @@
import pytest

from homeassistant.setup import async_setup_component
from homeassistant.components.cloud import iot, auth_api
from homeassistant.components.cloud import Cloud, iot, auth_api, MODE_DEV
from tests.components.alexa import test_smart_home as test_alexa
from tests.common import mock_coro

Expand Down Expand Up @@ -202,7 +202,7 @@ def test_cloud_check_token_raising(mock_client, caplog, mock_cloud):

yield from conn.connect()

assert 'Unable to connect: BLA' in caplog.text
assert 'Unable to refresh token: BLA' in caplog.text


@asyncio.coroutine
Expand Down Expand Up @@ -348,3 +348,17 @@ def test_handler_google_actions(hass):
assert device['name']['name'] == 'Config name'
assert device['name']['nicknames'] == ['Config alias']
assert device['type'] == 'action.devices.types.LIGHT'


async def test_refresh_token_expired(hass):
"""Test handling Unauthenticated error raised if refresh token expired."""
cloud = Cloud(hass, MODE_DEV, None, None)

with patch('homeassistant.components.cloud.auth_api.check_token',
side_effect=auth_api.Unauthenticated) as mock_check_token, \
patch.object(hass.components.persistent_notification,
'async_create') as mock_create:
await cloud.iot.connect()

assert len(mock_check_token.mock_calls) == 1
assert len(mock_create.mock_calls) == 1

0 comments on commit 228b030

Please sign in to comment.