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>
This commit is contained in:
parent
260d9f8e16
commit
b9a525a9a7
5 changed files with 62 additions and 6 deletions
|
@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
class RequestDataValidator:
|
class RequestDataValidator:
|
||||||
"""Decorator that will validate the incoming data.
|
"""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.
|
keyword argument to the function call.
|
||||||
|
|
||||||
Will return a 400 if no JSON provided or doesn't match schema.
|
Will return a 400 if no JSON provided or doesn't match schema.
|
||||||
|
|
|
@ -14,6 +14,7 @@ from homeassistant.helpers import device_registry
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
|
||||||
|
from .api import async_register_api
|
||||||
from .const import (
|
from .const import (
|
||||||
DATA_CLIENT,
|
DATA_CLIENT,
|
||||||
DATA_UNSUBSCRIBE,
|
DATA_UNSUBSCRIBE,
|
||||||
|
@ -22,7 +23,6 @@ from .const import (
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
)
|
)
|
||||||
from .discovery import async_discover_values
|
from .discovery import async_discover_values
|
||||||
from .websocket_api import async_register_api
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
CONNECT_TIMEOUT = 10
|
CONNECT_TIMEOUT = 10
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
"""Websocket API for Z-Wave JS."""
|
"""Websocket API for Z-Wave JS."""
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp import hdrs, web, web_exceptions
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
from zwave_js_server import dump
|
||||||
from zwave_js_server.client import Client as ZwaveClient
|
from zwave_js_server.client import Client as ZwaveClient
|
||||||
from zwave_js_server.model.node import Node as ZwaveNode
|
from zwave_js_server.model.node import Node as ZwaveNode
|
||||||
|
|
||||||
from homeassistant.components import websocket_api
|
from homeassistant.components import websocket_api
|
||||||
|
from homeassistant.components.http.view import HomeAssistantView
|
||||||
from homeassistant.components.websocket_api.connection import ActiveConnection
|
from homeassistant.components.websocket_api.connection import ActiveConnection
|
||||||
|
from homeassistant.const import CONF_URL
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import device_registry
|
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.device_registry import DeviceEntry
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
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_stop_inclusion)
|
||||||
websocket_api.async_register_command(hass, websocket_remove_node)
|
websocket_api.async_register_command(hass, websocket_remove_node)
|
||||||
websocket_api.async_register_command(hass, websocket_stop_exclusion)
|
websocket_api.async_register_command(hass, websocket_stop_exclusion)
|
||||||
|
hass.http.register_view(DumpView) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.require_admin
|
@websocket_api.require_admin
|
||||||
|
@ -54,7 +60,7 @@ def websocket_network_status(
|
||||||
},
|
},
|
||||||
"controller": {
|
"controller": {
|
||||||
"home_id": client.driver.controller.data["homeId"],
|
"home_id": client.driver.controller.data["homeId"],
|
||||||
"node_count": len(client.driver.controller.nodes),
|
"nodes": list(client.driver.controller.nodes),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
connection.send_result(
|
connection.send_result(
|
||||||
|
@ -278,3 +284,31 @@ async def remove_from_device_registry(
|
||||||
return
|
return
|
||||||
|
|
||||||
registry.async_remove_device(device.id)
|
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"',
|
||||||
|
},
|
||||||
|
)
|
|
@ -4,5 +4,6 @@
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
||||||
"requirements": ["zwave-js-server-python==0.13.0"],
|
"requirements": ["zwave-js-server-python==0.13.0"],
|
||||||
"codeowners": ["@home-assistant/z-wave"]
|
"codeowners": ["@home-assistant/z-wave"],
|
||||||
|
"dependencies": ["http", "websocket_api"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
"""Test the Z-Wave JS Websocket API."""
|
"""Test the Z-Wave JS Websocket API."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from zwave_js_server.event import Event
|
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.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
|
from homeassistant.helpers.device_registry import async_get_registry
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,3 +153,22 @@ async def test_remove_node(
|
||||||
identifiers={(DOMAIN, "3245146787-67")},
|
identifiers={(DOMAIN, "3245146787-67")},
|
||||||
)
|
)
|
||||||
assert device is None
|
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
|
Loading…
Add table
Add a link
Reference in a new issue