336 lines
9.5 KiB
Python
336 lines
9.5 KiB
Python
"""Tests for WebSocket API commands."""
|
|
from async_timeout import timeout
|
|
|
|
from homeassistant.core import callback
|
|
from homeassistant.components.websocket_api.const import URL
|
|
from homeassistant.components.websocket_api.auth import (
|
|
TYPE_AUTH, TYPE_AUTH_OK, TYPE_AUTH_REQUIRED
|
|
)
|
|
from homeassistant.components.websocket_api import const
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from tests.common import async_mock_service
|
|
|
|
from . import API_PASSWORD
|
|
|
|
|
|
async def test_call_service(hass, websocket_client):
|
|
"""Test call service command."""
|
|
calls = []
|
|
|
|
@callback
|
|
def service_call(call):
|
|
calls.append(call)
|
|
|
|
hass.services.async_register('domain_test', 'test_service', service_call)
|
|
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'call_service',
|
|
'domain': 'domain_test',
|
|
'service': 'test_service',
|
|
'service_data': {
|
|
'hello': 'world'
|
|
}
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success']
|
|
|
|
assert len(calls) == 1
|
|
call = calls[0]
|
|
|
|
assert call.domain == 'domain_test'
|
|
assert call.service == 'test_service'
|
|
assert call.data == {'hello': 'world'}
|
|
|
|
|
|
async def test_call_service_not_found(hass, websocket_client):
|
|
"""Test call service command."""
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'call_service',
|
|
'domain': 'domain_test',
|
|
'service': 'test_service',
|
|
'service_data': {
|
|
'hello': 'world'
|
|
}
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert not msg['success']
|
|
assert msg['error']['code'] == const.ERR_NOT_FOUND
|
|
|
|
|
|
async def test_call_service_error(hass, websocket_client):
|
|
"""Test call service command with error."""
|
|
@callback
|
|
def ha_error_call(_):
|
|
raise HomeAssistantError('error_message')
|
|
|
|
hass.services.async_register('domain_test', 'ha_error', ha_error_call)
|
|
|
|
async def unknown_error_call(_):
|
|
raise ValueError('value_error')
|
|
|
|
hass.services.async_register(
|
|
'domain_test', 'unknown_error', unknown_error_call)
|
|
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'call_service',
|
|
'domain': 'domain_test',
|
|
'service': 'ha_error',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
print(msg)
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success'] is False
|
|
assert msg['error']['code'] == 'home_assistant_error'
|
|
assert msg['error']['message'] == 'error_message'
|
|
|
|
await websocket_client.send_json({
|
|
'id': 6,
|
|
'type': 'call_service',
|
|
'domain': 'domain_test',
|
|
'service': 'unknown_error',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
print(msg)
|
|
assert msg['id'] == 6
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success'] is False
|
|
assert msg['error']['code'] == 'unknown_error'
|
|
assert msg['error']['message'] == 'value_error'
|
|
|
|
|
|
async def test_subscribe_unsubscribe_events(hass, websocket_client):
|
|
"""Test subscribe/unsubscribe events command."""
|
|
init_count = sum(hass.bus.async_listeners().values())
|
|
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'subscribe_events',
|
|
'event_type': 'test_event'
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success']
|
|
|
|
# Verify we have a new listener
|
|
assert sum(hass.bus.async_listeners().values()) == init_count + 1
|
|
|
|
hass.bus.async_fire('ignore_event')
|
|
hass.bus.async_fire('test_event', {'hello': 'world'})
|
|
hass.bus.async_fire('ignore_event')
|
|
|
|
with timeout(3, loop=hass.loop):
|
|
msg = await websocket_client.receive_json()
|
|
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == 'event'
|
|
event = msg['event']
|
|
|
|
assert event['event_type'] == 'test_event'
|
|
assert event['data'] == {'hello': 'world'}
|
|
assert event['origin'] == 'LOCAL'
|
|
|
|
await websocket_client.send_json({
|
|
'id': 6,
|
|
'type': 'unsubscribe_events',
|
|
'subscription': 5
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 6
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success']
|
|
|
|
# Check our listener got unsubscribed
|
|
assert sum(hass.bus.async_listeners().values()) == init_count
|
|
|
|
|
|
async def test_get_states(hass, websocket_client):
|
|
"""Test get_states command."""
|
|
hass.states.async_set('greeting.hello', 'world')
|
|
hass.states.async_set('greeting.bye', 'universe')
|
|
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'get_states',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success']
|
|
|
|
states = []
|
|
for state in hass.states.async_all():
|
|
state = state.as_dict()
|
|
state['last_changed'] = state['last_changed'].isoformat()
|
|
state['last_updated'] = state['last_updated'].isoformat()
|
|
states.append(state)
|
|
|
|
assert msg['result'] == states
|
|
|
|
|
|
async def test_get_services(hass, websocket_client):
|
|
"""Test get_services command."""
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'get_services',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success']
|
|
assert msg['result'] == hass.services.async_services()
|
|
|
|
|
|
async def test_get_config(hass, websocket_client):
|
|
"""Test get_config command."""
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'get_config',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success']
|
|
|
|
if 'components' in msg['result']:
|
|
msg['result']['components'] = set(msg['result']['components'])
|
|
if 'whitelist_external_dirs' in msg['result']:
|
|
msg['result']['whitelist_external_dirs'] = \
|
|
set(msg['result']['whitelist_external_dirs'])
|
|
|
|
assert msg['result'] == hass.config.as_dict()
|
|
|
|
|
|
async def test_ping(websocket_client):
|
|
"""Test get_panels command."""
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'ping',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == 'pong'
|
|
|
|
|
|
async def test_call_service_context_with_user(hass, aiohttp_client,
|
|
hass_access_token):
|
|
"""Test that the user is set in the service call context."""
|
|
assert await async_setup_component(hass, 'websocket_api', {
|
|
'http': {
|
|
'api_password': API_PASSWORD
|
|
}
|
|
})
|
|
|
|
calls = async_mock_service(hass, 'domain_test', 'test_service')
|
|
client = await aiohttp_client(hass.http.app)
|
|
|
|
async with client.ws_connect(URL) as ws:
|
|
auth_msg = await ws.receive_json()
|
|
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
|
|
|
await ws.send_json({
|
|
'type': TYPE_AUTH,
|
|
'access_token': hass_access_token
|
|
})
|
|
|
|
auth_msg = await ws.receive_json()
|
|
assert auth_msg['type'] == TYPE_AUTH_OK
|
|
|
|
await ws.send_json({
|
|
'id': 5,
|
|
'type': 'call_service',
|
|
'domain': 'domain_test',
|
|
'service': 'test_service',
|
|
'service_data': {
|
|
'hello': 'world'
|
|
}
|
|
})
|
|
|
|
msg = await ws.receive_json()
|
|
assert msg['success']
|
|
|
|
refresh_token = await hass.auth.async_validate_access_token(
|
|
hass_access_token)
|
|
|
|
assert len(calls) == 1
|
|
call = calls[0]
|
|
assert call.domain == 'domain_test'
|
|
assert call.service == 'test_service'
|
|
assert call.data == {'hello': 'world'}
|
|
assert call.context.user_id == refresh_token.user.id
|
|
|
|
|
|
async def test_subscribe_requires_admin(websocket_client, hass_admin_user):
|
|
"""Test subscribing events without being admin."""
|
|
hass_admin_user.groups = []
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'subscribe_events',
|
|
'event_type': 'test_event'
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert not msg['success']
|
|
assert msg['error']['code'] == const.ERR_UNAUTHORIZED
|
|
|
|
|
|
async def test_states_filters_visible(hass, hass_admin_user, websocket_client):
|
|
"""Test we only get entities that we're allowed to see."""
|
|
hass_admin_user.mock_policy({
|
|
'entities': {
|
|
'entity_ids': {
|
|
'test.entity': True
|
|
}
|
|
}
|
|
})
|
|
hass.states.async_set('test.entity', 'hello')
|
|
hass.states.async_set('test.not_visible_entity', 'invisible')
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'get_states',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert msg['id'] == 5
|
|
assert msg['type'] == const.TYPE_RESULT
|
|
assert msg['success']
|
|
|
|
assert len(msg['result']) == 1
|
|
assert msg['result'][0]['entity_id'] == 'test.entity'
|
|
|
|
|
|
async def test_get_states_not_allows_nan(hass, websocket_client):
|
|
"""Test get_states command not allows NaN floats."""
|
|
hass.states.async_set('greeting.hello', 'world', {
|
|
'hello': float("NaN")
|
|
})
|
|
|
|
await websocket_client.send_json({
|
|
'id': 5,
|
|
'type': 'get_states',
|
|
})
|
|
|
|
msg = await websocket_client.receive_json()
|
|
assert not msg['success']
|
|
assert msg['error']['code'] == const.ERR_UNKNOWN_ERROR
|