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:
parent
734a67ede0
commit
7bd8c0d39a
5 changed files with 55 additions and 15 deletions
|
@ -65,6 +65,7 @@ ERR_SENSOR_DUPLICATE_UNIQUE_ID = 'duplicate_unique_id'
|
||||||
|
|
||||||
WEBHOOK_TYPE_CALL_SERVICE = 'call_service'
|
WEBHOOK_TYPE_CALL_SERVICE = 'call_service'
|
||||||
WEBHOOK_TYPE_FIRE_EVENT = 'fire_event'
|
WEBHOOK_TYPE_FIRE_EVENT = 'fire_event'
|
||||||
|
WEBHOOK_TYPE_GET_ZONES = 'get_zones'
|
||||||
WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor'
|
WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor'
|
||||||
WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template'
|
WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template'
|
||||||
WEBHOOK_TYPE_UPDATE_LOCATION = 'update_location'
|
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_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states'
|
||||||
|
|
||||||
WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT,
|
WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT,
|
||||||
WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE,
|
WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR,
|
||||||
WEBHOOK_TYPE_UPDATE_LOCATION,
|
WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION,
|
||||||
WEBHOOK_TYPE_UPDATE_REGISTRATION,
|
WEBHOOK_TYPE_UPDATE_REGISTRATION,
|
||||||
WEBHOOK_TYPE_UPDATE_SENSOR_STATES]
|
WEBHOOK_TYPE_UPDATE_SENSOR_STATES]
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from typing import Callable, Dict, Tuple
|
||||||
from aiohttp.web import json_response, Response
|
from aiohttp.web import json_response, Response
|
||||||
|
|
||||||
from homeassistant.core import Context
|
from homeassistant.core import Context
|
||||||
|
from homeassistant.helpers.json import JSONEncoder
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_NAME,
|
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,
|
def webhook_response(data, *, registration: Dict, status: int = 200,
|
||||||
headers: Dict = None) -> Response:
|
headers: Dict = None) -> Response:
|
||||||
"""Return a encrypted response if registration supports it."""
|
"""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()
|
keylen, encrypt = setup_encrypt()
|
||||||
|
|
||||||
key = registration[CONF_SECRET].encode("utf-8")
|
key = registration[CONF_SECRET].encode("utf-8")
|
||||||
|
|
|
@ -9,6 +9,8 @@ from homeassistant.components.device_tracker import (ATTR_ATTRIBUTES,
|
||||||
DOMAIN as DT_DOMAIN,
|
DOMAIN as DT_DOMAIN,
|
||||||
SERVICE_SEE as DT_SEE)
|
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,
|
from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA,
|
||||||
CONF_WEBHOOK_ID, HTTP_BAD_REQUEST,
|
CONF_WEBHOOK_ID, HTTP_BAD_REQUEST,
|
||||||
HTTP_CREATED)
|
HTTP_CREATED)
|
||||||
|
@ -33,9 +35,10 @@ from .const import (ATTR_ALTITUDE, ATTR_BATTERY, ATTR_COURSE, ATTR_DEVICE_ID,
|
||||||
DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED,
|
DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED,
|
||||||
ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED,
|
ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED,
|
||||||
SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA,
|
SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA,
|
||||||
WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE,
|
WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE,
|
||||||
WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_REGISTER_SENSOR,
|
WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_ZONES,
|
||||||
WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION,
|
WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE,
|
||||||
|
WEBHOOK_TYPE_UPDATE_LOCATION,
|
||||||
WEBHOOK_TYPE_UPDATE_REGISTRATION,
|
WEBHOOK_TYPE_UPDATE_REGISTRATION,
|
||||||
WEBHOOK_TYPE_UPDATE_SENSOR_STATES)
|
WEBHOOK_TYPE_UPDATE_SENSOR_STATES)
|
||||||
|
|
||||||
|
@ -87,10 +90,13 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str,
|
||||||
enc_data = req_data[ATTR_WEBHOOK_ENCRYPTED_DATA]
|
enc_data = req_data[ATTR_WEBHOOK_ENCRYPTED_DATA]
|
||||||
webhook_payload = _decrypt_payload(registration[CONF_SECRET], enc_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)
|
_LOGGER.error('Received invalid webhook type: %s', webhook_type)
|
||||||
return empty_okay_response()
|
return empty_okay_response()
|
||||||
|
|
||||||
|
data = webhook_payload
|
||||||
|
|
||||||
|
if webhook_type in WEBHOOK_SCHEMAS:
|
||||||
try:
|
try:
|
||||||
data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload)
|
data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload)
|
||||||
except vol.Invalid as ex:
|
except vol.Invalid as ex:
|
||||||
|
@ -261,3 +267,9 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str,
|
||||||
|
|
||||||
return webhook_response(resp, registration=registration,
|
return webhook_response(resp, registration=registration,
|
||||||
headers=headers)
|
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)
|
||||||
|
|
|
@ -14,7 +14,7 @@ from .const import CONF_PASSIVE, DOMAIN, HOME_ZONE
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def configured_zones(hass):
|
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
|
return set((slugify(entry.data[CONF_NAME])) for
|
||||||
entry in hass.config_entries.async_entries(DOMAIN))
|
entry in hass.config_entries.async_entries(DOMAIN))
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@ import logging
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.mobile_app.const import CONF_SECRET
|
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.const import CONF_WEBHOOK_ID
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import async_mock_service
|
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
|
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
|
async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501
|
||||||
create_registrations, # noqa: F401, F811, E501
|
create_registrations, # noqa: F401, F811, E501
|
||||||
caplog): # noqa: E501 F811
|
caplog): # noqa: E501 F811
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue