diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 85ed7391ec7..954cc8385f6 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -91,27 +91,36 @@ class CloudNotAvailable(HomeAssistantError): @bind_hass @callback -def async_is_logged_in(hass): +def async_is_logged_in(hass) -> bool: """Test if user is logged in.""" return DOMAIN in hass.data and hass.data[DOMAIN].is_logged_in @bind_hass -async def async_create_cloudhook(hass, webhook_id): +@callback +def async_active_subscription(hass) -> bool: + """Test if user has an active subscription.""" + return \ + async_is_logged_in(hass) and not hass.data[DOMAIN].subscription_expired + + +@bind_hass +async def async_create_cloudhook(hass, webhook_id: str) -> str: """Create a cloudhook.""" if not async_is_logged_in(hass): raise CloudNotAvailable - return await hass.data[DOMAIN].cloudhooks.async_create(webhook_id) + hook = await hass.data[DOMAIN].cloudhooks.async_create(webhook_id, True) + return hook['cloudhook_url'] @bind_hass -async def async_delete_cloudhook(hass, webhook_id): +async def async_delete_cloudhook(hass, webhook_id: str) -> None: """Delete a cloudhook.""" - if not async_is_logged_in(hass): + if DOMAIN not in hass.data: raise CloudNotAvailable - return await hass.data[DOMAIN].cloudhooks.async_delete(webhook_id) + await hass.data[DOMAIN].cloudhooks.async_delete(webhook_id) def is_cloudhook_request(request): diff --git a/homeassistant/components/mobile_app/http_api.py b/homeassistant/components/mobile_app/http_api.py index 4ae473876fc..8de1d954605 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -46,11 +46,9 @@ class RegistrationsView(HomeAssistantView): webhook_id = generate_secret() - if "cloud" in hass.config.components: - cloudhook = await async_create_cloudhook(hass, webhook_id) - - if cloudhook is not None: - data[CONF_CLOUDHOOK_URL] = cloudhook[CONF_CLOUDHOOK_URL] + if hass.components.cloud.async_active_subscription(): + data[CONF_CLOUDHOOK_URL] = \ + await async_create_cloudhook(hass, webhook_id) data[CONF_WEBHOOK_ID] = webhook_id diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index 2418e091740..0780826afd3 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -190,10 +190,9 @@ async def test_create_cloudhook_no_login(hass): assert len(mock_create.mock_calls) == 0 -async def test_delete_cloudhook_no_login(hass): +async def test_delete_cloudhook_no_setup(hass): """Test delete cloudhook when not logged in.""" - assert await async_setup_component(hass, 'cloud', {}) - coro = mock_coro({'yo': 'hey'}) + coro = mock_coro() with patch('homeassistant.components.cloud.cloudhooks.' 'Cloudhooks.async_delete', return_value=coro) as mock_delete, \ pytest.raises(cloud.CloudNotAvailable): @@ -205,28 +204,27 @@ async def test_delete_cloudhook_no_login(hass): async def test_create_cloudhook(hass): """Test create cloudhook.""" assert await async_setup_component(hass, 'cloud', {}) - coro = mock_coro({'yo': 'hey'}) + coro = mock_coro({'cloudhook_url': 'hello'}) with patch('homeassistant.components.cloud.cloudhooks.' 'Cloudhooks.async_create', return_value=coro) as mock_create, \ patch('homeassistant.components.cloud.async_is_logged_in', return_value=True): result = await hass.components.cloud.async_create_cloudhook('hello') - assert result == {'yo': 'hey'} + assert result == 'hello' assert len(mock_create.mock_calls) == 1 async def test_delete_cloudhook(hass): """Test delete cloudhook.""" assert await async_setup_component(hass, 'cloud', {}) - coro = mock_coro({'yo': 'hey'}) + coro = mock_coro() with patch('homeassistant.components.cloud.cloudhooks.' 'Cloudhooks.async_delete', return_value=coro) as mock_delete, \ patch('homeassistant.components.cloud.async_is_logged_in', return_value=True): - result = await hass.components.cloud.async_delete_cloudhook('hello') + await hass.components.cloud.async_delete_cloudhook('hello') - assert result == {'yo': 'hey'} assert len(mock_delete.mock_calls) == 1 @@ -244,3 +242,28 @@ async def test_async_logged_in(hass): # Cloud loaded, logged in assert hass.components.cloud.async_is_logged_in() is True + + +async def test_async_active_subscription(hass): + """Test if is_logged_in works.""" + # Cloud not loaded + assert hass.components.cloud.async_active_subscription() is False + + assert await async_setup_component(hass, 'cloud', {}) + + # Cloud loaded, not logged in + assert hass.components.cloud.async_active_subscription() is False + + hass.data['cloud'].id_token = "some token" + + # Cloud loaded, logged in, invalid sub + with patch('jose.jwt.get_unverified_claims', return_value={ + 'custom:sub-exp': '{}-12-31'.format(utcnow().year - 1) + }): + assert hass.components.cloud.async_active_subscription() is False + + # Cloud loaded, logged in, valid sub + with patch('jose.jwt.get_unverified_claims', return_value={ + 'custom:sub-exp': '{}-01-01'.format(utcnow().year + 1) + }): + assert hass.components.cloud.async_active_subscription() is True