Add new mobile_app webhook command: get_zones (#22604)

## Description:

Adds a new `mobile_app` webhook command, `get_zones`, which just returns all zones.

## Checklist:
  - [x] The code change is tested and works locally.
  - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**
  - [x] There is no commented out code in this PR.
This commit is contained in:
Robbie Trencheny 2019-03-31 21:30:45 -07:00 committed by GitHub
parent 734a67ede0
commit 7bd8c0d39a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 15 deletions

View File

@ -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]

View File

@ -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")

View File

@ -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)

View File

@ -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))

View File

@ -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