Add an HTTP view to dump the Z-Wave JS state (#45452)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
pull/45560/head
Paulus Schoutsen 2021-01-26 10:27:20 +01:00 committed by GitHub
parent 260d9f8e16
commit b9a525a9a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 6 deletions

View File

@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__)
class RequestDataValidator:
"""Decorator that will validate the incoming data.
Takes in a voluptuous schema and adds 'post_data' as
Takes in a voluptuous schema and adds 'data' as
keyword argument to the function call.
Will return a 400 if no JSON provided or doesn't match schema.

View File

@ -14,6 +14,7 @@ from homeassistant.helpers import device_registry
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send
from .api import async_register_api
from .const import (
DATA_CLIENT,
DATA_UNSUBSCRIBE,
@ -22,7 +23,6 @@ from .const import (
PLATFORMS,
)
from .discovery import async_discover_values
from .websocket_api import async_register_api
LOGGER = logging.getLogger(__name__)
CONNECT_TIMEOUT = 10

View File

@ -1,15 +1,20 @@
"""Websocket API for Z-Wave JS."""
import json
import logging
from aiohttp import hdrs, web, web_exceptions
import voluptuous as vol
from zwave_js_server import dump
from zwave_js_server.client import Client as ZwaveClient
from zwave_js_server.model.node import Node as ZwaveNode
from homeassistant.components import websocket_api
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.components.websocket_api.connection import ActiveConnection
from homeassistant.const import CONF_URL
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -32,6 +37,7 @@ def async_register_api(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, websocket_stop_inclusion)
websocket_api.async_register_command(hass, websocket_remove_node)
websocket_api.async_register_command(hass, websocket_stop_exclusion)
hass.http.register_view(DumpView) # type: ignore
@websocket_api.require_admin
@ -54,7 +60,7 @@ def websocket_network_status(
},
"controller": {
"home_id": client.driver.controller.data["homeId"],
"node_count": len(client.driver.controller.nodes),
"nodes": list(client.driver.controller.nodes),
},
}
connection.send_result(
@ -278,3 +284,31 @@ async def remove_from_device_registry(
return
registry.async_remove_device(device.id)
class DumpView(HomeAssistantView):
"""View to dump the state of the Z-Wave JS server."""
url = "/api/zwave_js/dump/{config_entry_id}"
name = "api:zwave_js:dump"
async def get(self, request: web.Request, config_entry_id: str) -> web.Response:
"""Dump the state of Z-Wave."""
hass = request.app["hass"]
if config_entry_id not in hass.data[DOMAIN]:
raise web_exceptions.HTTPBadRequest
entry = hass.config_entries.async_get_entry(config_entry_id)
msgs = await dump.dump_msgs(
entry.data[CONF_URL], async_get_clientsession(hass), wait_nodes_ready=False
)
return web.Response(
body="\n".join(json.dumps(msg) for msg in msgs) + "\n",
headers={
hdrs.CONTENT_TYPE: "application/jsonl",
hdrs.CONTENT_DISPOSITION: 'attachment; filename="zwave_js_dump.jsonl"',
},
)

View File

@ -4,5 +4,6 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
"requirements": ["zwave-js-server-python==0.13.0"],
"codeowners": ["@home-assistant/z-wave"]
"codeowners": ["@home-assistant/z-wave"],
"dependencies": ["http", "websocket_api"]
}

View File

@ -1,8 +1,10 @@
"""Test the Z-Wave JS Websocket API."""
from unittest.mock import patch
from zwave_js_server.event import Event
from homeassistant.components.zwave_js.api import ENTRY_ID, ID, NODE_ID, TYPE
from homeassistant.components.zwave_js.const import DOMAIN
from homeassistant.components.zwave_js.websocket_api import ENTRY_ID, ID, NODE_ID, TYPE
from homeassistant.helpers.device_registry import async_get_registry
@ -151,3 +153,22 @@ async def test_remove_node(
identifiers={(DOMAIN, "3245146787-67")},
)
assert device is None
async def test_dump_view(integration, hass_client):
"""Test the HTTP dump view."""
client = await hass_client()
with patch(
"zwave_js_server.dump.dump_msgs",
return_value=[{"hello": "world"}, {"second": "msg"}],
):
resp = await client.get(f"/api/zwave_js/dump/{integration.entry_id}")
assert resp.status == 200
assert await resp.text() == '{"hello": "world"}\n{"second": "msg"}\n'
async def test_dump_view_invalid_entry_id(integration, hass_client):
"""Test an invalid config entry id parameter."""
client = await hass_client()
resp = await client.get("/api/zwave_js/dump/INVALID")
assert resp.status == 400