Catch device not found in device automations (#31401)

pull/31418/head
Paulus Schoutsen 2020-02-02 07:13:07 -08:00 committed by GitHub
parent 55aa341dab
commit 7127310f10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 1 deletions

View File

@ -1,5 +1,6 @@
"""Helpers for device automations.""" """Helpers for device automations."""
import asyncio import asyncio
from functools import wraps
import logging import logging
from types import ModuleType from types import ModuleType
from typing import Any, List, MutableMapping from typing import Any, List, MutableMapping
@ -14,7 +15,7 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_registry import async_entries_for_device from homeassistant.helpers.entity_registry import async_entries_for_device
from homeassistant.loader import IntegrationNotFound, async_get_integration from homeassistant.loader import IntegrationNotFound, async_get_integration
from .exceptions import InvalidDeviceAutomationConfig from .exceptions import DeviceNotFound, InvalidDeviceAutomationConfig
# mypy: allow-untyped-calls, allow-untyped-defs # mypy: allow-untyped-calls, allow-untyped-defs
@ -117,6 +118,10 @@ async def _async_get_device_automations(hass, automation_type, device_id):
domains = set() domains = set()
automations: List[MutableMapping[str, Any]] = [] automations: List[MutableMapping[str, Any]] = []
device = device_registry.async_get(device_id) device = device_registry.async_get(device_id)
if device is None:
raise DeviceNotFound
for entry_id in device.config_entries: for entry_id in device.config_entries:
config_entry = hass.config_entries.async_get_entry(entry_id) config_entry = hass.config_entries.async_get_entry(entry_id)
domains.add(config_entry.domain) domains.add(config_entry.domain)
@ -173,6 +178,21 @@ async def _async_get_device_automation_capabilities(hass, automation_type, autom
return capabilities return capabilities
def handle_device_errors(func):
"""Handle device automation errors."""
@wraps(func)
async def with_error_handling(hass, connection, msg):
try:
await func(hass, connection, msg)
except DeviceNotFound:
connection.send_error(
msg["id"], websocket_api.const.ERR_NOT_FOUND, "Device not found"
)
return with_error_handling
@websocket_api.websocket_command( @websocket_api.websocket_command(
{ {
vol.Required("type"): "device_automation/action/list", vol.Required("type"): "device_automation/action/list",
@ -180,6 +200,7 @@ async def _async_get_device_automation_capabilities(hass, automation_type, autom
} }
) )
@websocket_api.async_response @websocket_api.async_response
@handle_device_errors
async def websocket_device_automation_list_actions(hass, connection, msg): async def websocket_device_automation_list_actions(hass, connection, msg):
"""Handle request for device actions.""" """Handle request for device actions."""
device_id = msg["device_id"] device_id = msg["device_id"]
@ -194,6 +215,7 @@ async def websocket_device_automation_list_actions(hass, connection, msg):
} }
) )
@websocket_api.async_response @websocket_api.async_response
@handle_device_errors
async def websocket_device_automation_list_conditions(hass, connection, msg): async def websocket_device_automation_list_conditions(hass, connection, msg):
"""Handle request for device conditions.""" """Handle request for device conditions."""
device_id = msg["device_id"] device_id = msg["device_id"]
@ -208,6 +230,7 @@ async def websocket_device_automation_list_conditions(hass, connection, msg):
} }
) )
@websocket_api.async_response @websocket_api.async_response
@handle_device_errors
async def websocket_device_automation_list_triggers(hass, connection, msg): async def websocket_device_automation_list_triggers(hass, connection, msg):
"""Handle request for device triggers.""" """Handle request for device triggers."""
device_id = msg["device_id"] device_id = msg["device_id"]
@ -222,6 +245,7 @@ async def websocket_device_automation_list_triggers(hass, connection, msg):
} }
) )
@websocket_api.async_response @websocket_api.async_response
@handle_device_errors
async def websocket_device_automation_get_action_capabilities(hass, connection, msg): async def websocket_device_automation_get_action_capabilities(hass, connection, msg):
"""Handle request for device action capabilities.""" """Handle request for device action capabilities."""
action = msg["action"] action = msg["action"]
@ -238,6 +262,7 @@ async def websocket_device_automation_get_action_capabilities(hass, connection,
} }
) )
@websocket_api.async_response @websocket_api.async_response
@handle_device_errors
async def websocket_device_automation_get_condition_capabilities(hass, connection, msg): async def websocket_device_automation_get_condition_capabilities(hass, connection, msg):
"""Handle request for device condition capabilities.""" """Handle request for device condition capabilities."""
condition = msg["condition"] condition = msg["condition"]
@ -254,6 +279,7 @@ async def websocket_device_automation_get_condition_capabilities(hass, connectio
} }
) )
@websocket_api.async_response @websocket_api.async_response
@handle_device_errors
async def websocket_device_automation_get_trigger_capabilities(hass, connection, msg): async def websocket_device_automation_get_trigger_capabilities(hass, connection, msg):
"""Handle request for device trigger capabilities.""" """Handle request for device trigger capabilities."""
trigger = msg["trigger"] trigger = msg["trigger"]

View File

@ -4,3 +4,7 @@ from homeassistant.exceptions import HomeAssistantError
class InvalidDeviceAutomationConfig(HomeAssistantError): class InvalidDeviceAutomationConfig(HomeAssistantError):
"""When device automation config is invalid.""" """When device automation config is invalid."""
class DeviceNotFound(HomeAssistantError):
"""When referenced device not found."""

View File

@ -761,3 +761,17 @@ async def test_automation_with_bad_trigger(hass, caplog):
) )
assert "required key not provided" in caplog.text assert "required key not provided" in caplog.text
async def test_websocket_device_not_found(hass, hass_ws_client):
"""Test caling command with unknown device."""
await async_setup_component(hass, "device_automation", {})
client = await hass_ws_client(hass)
await client.send_json(
{"id": 1, "type": "device_automation/action/list", "device_id": "non-existing"}
)
msg = await client.receive_json()
assert msg["id"] == 1
assert not msg["success"]
assert msg["error"] == {"code": "not_found", "message": "Device not found"}