diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 3aa4626da29..38897056c11 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -65,6 +65,7 @@ ERR_SENSOR_DUPLICATE_UNIQUE_ID = 'duplicate_unique_id' WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_FIRE_EVENT = 'fire_event' +WEBHOOK_TYPE_GET_ZONES = 'get_zones' WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor' WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template' WEBHOOK_TYPE_UPDATE_LOCATION = 'update_location' @@ -72,8 +73,8 @@ WEBHOOK_TYPE_UPDATE_REGISTRATION = 'update_registration' WEBHOOK_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states' WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, - WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, + WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES] diff --git a/homeassistant/components/mobile_app/helpers.py b/homeassistant/components/mobile_app/helpers.py index 60bd8b4e1d6..ee593588ef8 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -6,6 +6,7 @@ from typing import Callable, Dict, Tuple from aiohttp.web import json_response, Response from homeassistant.core import Context +from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.typing import HomeAssistantType from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_NAME, @@ -133,9 +134,9 @@ def savable_state(hass: HomeAssistantType) -> Dict: def webhook_response(data, *, registration: Dict, status: int = 200, headers: Dict = None) -> Response: """Return a encrypted response if registration supports it.""" - data = json.dumps(data) + data = json.dumps(data, cls=JSONEncoder) - if CONF_SECRET in registration: + if registration[ATTR_SUPPORTS_ENCRYPTION]: keylen, encrypt = setup_encrypt() key = registration[CONF_SECRET].encode("utf-8") diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index aafa6046d11..71c6d0d6673 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -9,6 +9,8 @@ from homeassistant.components.device_tracker import (ATTR_ATTRIBUTES, DOMAIN as DT_DOMAIN, SERVICE_SEE as DT_SEE) +from homeassistant.components.zone.const import DOMAIN as ZONE_DOMAIN + from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, CONF_WEBHOOK_ID, HTTP_BAD_REQUEST, HTTP_CREATED) @@ -33,9 +35,10 @@ from .const import (ATTR_ALTITUDE, ATTR_BATTERY, ATTR_COURSE, ATTR_DEVICE_ID, DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED, ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, - WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_REGISTER_SENSOR, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE, + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_ZONES, + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES) @@ -87,16 +90,19 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, enc_data = req_data[ATTR_WEBHOOK_ENCRYPTED_DATA] webhook_payload = _decrypt_payload(registration[CONF_SECRET], enc_data) - if webhook_type not in WEBHOOK_SCHEMAS: + if webhook_type not in WEBHOOK_TYPES: _LOGGER.error('Received invalid webhook type: %s', webhook_type) return empty_okay_response() - try: - data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) - except vol.Invalid as ex: - err = vol.humanize.humanize_error(webhook_payload, ex) - _LOGGER.error('Received invalid webhook payload: %s', err) - return empty_okay_response(headers=headers) + data = webhook_payload + + if webhook_type in WEBHOOK_SCHEMAS: + try: + data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) + except vol.Invalid as ex: + err = vol.humanize.humanize_error(webhook_payload, ex) + _LOGGER.error('Received invalid webhook payload: %s', err) + return empty_okay_response(headers=headers) context = registration_context(registration) @@ -261,3 +267,9 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, return webhook_response(resp, registration=registration, headers=headers) + + if webhook_type == WEBHOOK_TYPE_GET_ZONES: + zones = (hass.states.get(entity_id) for entity_id + in sorted(hass.states.async_entity_ids(ZONE_DOMAIN))) + return webhook_response(list(zones), registration=registration, + headers=headers) diff --git a/homeassistant/components/zone/config_flow.py b/homeassistant/components/zone/config_flow.py index bf221a828ad..a7b968676d6 100644 --- a/homeassistant/components/zone/config_flow.py +++ b/homeassistant/components/zone/config_flow.py @@ -14,7 +14,7 @@ from .const import CONF_PASSIVE, DOMAIN, HOME_ZONE @callback def configured_zones(hass): - """Return a set of the configured hosts.""" + """Return a set of the configured zones.""" return set((slugify(entry.data[CONF_NAME])) for entry in hass.config_entries.async_entries(DOMAIN)) diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index a70e8ba1275..ad19a70a806 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -4,8 +4,10 @@ import logging import pytest from homeassistant.components.mobile_app.const import CONF_SECRET +from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.core import callback +from homeassistant.setup import async_setup_component from tests.common import async_mock_service @@ -100,6 +102,30 @@ async def test_webhook_update_registration(webhook_client, hass_client): # noqa assert CONF_SECRET not in update_json +async def test_webhook_handle_get_zones(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F811 + """Test that we can get zones properly.""" + await async_setup_component(hass, ZONE_DOMAIN, { + ZONE_DOMAIN: { + 'name': 'test', + 'latitude': 32.880837, + 'longitude': -117.237561, + 'radius': 250, + } + }) + + resp = await webhook_client.post( + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), + json={'type': 'get_zones'} + ) + + assert resp.status == 200 + + json = await resp.json() + assert len(json) == 1 + assert json[0]['entity_id'] == 'zone.home' + + async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501 create_registrations, # noqa: F401, F811, E501 caplog): # noqa: E501 F811