Add basic websocket api for OZW (#38265)

pull/38372/head
Charles Garwood 2020-07-29 15:35:26 -05:00 committed by GitHub
parent c5ca484eaa
commit 13e8e28778
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 178 additions and 1 deletions

View File

@ -35,6 +35,7 @@ from .entity import (
create_value_id,
)
from .services import ZWaveServices
from .websocket_api import ZWaveWebsocketApi
_LOGGER = logging.getLogger(__name__)
@ -206,6 +207,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
services = ZWaveServices(hass, manager)
services.async_register()
# Register WebSocket API
ws_api = ZWaveWebsocketApi(hass, manager)
ws_api.async_register_api()
@callback
def async_receive_message(msg):
manager.receive_message(msg.topic, msg.payload)

View File

@ -0,0 +1,114 @@
"""Web socket API for OpenZWave."""
import logging
import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import callback
_LOGGER = logging.getLogger(__name__)
TYPE = "type"
ID = "id"
OZW_INSTANCE = "ozw_instance"
NODE_ID = "node_id"
class ZWaveWebsocketApi:
"""Class that holds our websocket api commands."""
def __init__(self, hass, manager):
"""Initialize with both hass and ozwmanager objects."""
self._hass = hass
self._manager = manager
@callback
def async_register_api(self):
"""Register all of our api endpoints."""
websocket_api.async_register_command(self._hass, self.websocket_network_status)
websocket_api.async_register_command(self._hass, self.websocket_node_status)
websocket_api.async_register_command(self._hass, self.websocket_node_statistics)
@websocket_api.websocket_command(
{
vol.Required(TYPE): "ozw/network_status",
vol.Optional(OZW_INSTANCE, default=1): vol.Coerce(int),
}
)
def websocket_network_status(self, hass, connection, msg):
"""Get Z-Wave network status."""
connection.send_result(
msg[ID],
{
"state": self._manager.get_instance(msg[OZW_INSTANCE])
.get_status()
.status,
OZW_INSTANCE: msg[OZW_INSTANCE],
},
)
@websocket_api.websocket_command(
{
vol.Required(TYPE): "ozw/node_status",
vol.Required(NODE_ID): vol.Coerce(int),
vol.Optional(OZW_INSTANCE, default=1): vol.Coerce(int),
}
)
def websocket_node_status(self, hass, connection, msg):
"""Get the status for a Z-Wave node."""
node = self._manager.get_instance(msg[OZW_INSTANCE]).get_node(msg[NODE_ID])
connection.send_result(
msg[ID],
{
"node_query_stage": node.node_query_stage,
"node_id": node.node_id,
"is_zwave_plus": node.is_zwave_plus,
"is_awake": node.is_awake,
"is_failed": node.is_failed,
"node_baud_rate": node.node_baud_rate,
"is_beaming": node.is_beaming,
"is_flirs": node.is_flirs,
"is_routing": node.is_routing,
"is_securityv1": node.is_securityv1,
"node_basic_string": node.node_basic_string,
"node_generic_string": node.node_generic_string,
"node_specific_string": node.node_specific_string,
OZW_INSTANCE: msg[OZW_INSTANCE],
},
)
@websocket_api.websocket_command(
{
vol.Required(TYPE): "ozw/node_statistics",
vol.Required(NODE_ID): vol.Coerce(int),
vol.Optional(OZW_INSTANCE, default=1): vol.Coerce(int),
}
)
def websocket_node_statistics(self, hass, connection, msg):
"""Get the statistics for a Z-Wave node."""
stats = (
self._manager.get_instance(msg[OZW_INSTANCE])
.get_node(msg[NODE_ID])
.get_statistics()
)
connection.send_result(
msg[ID],
{
"node_id": msg[NODE_ID],
"send_count": stats.send_count,
"sent_failed": stats.sent_failed,
"retries": stats.retries,
"last_request_rtt": stats.last_request_rtt,
"last_response_rtt": stats.last_response_rtt,
"average_request_rtt": stats.average_request_rtt,
"average_response_rtt": stats.average_response_rtt,
"received_packets": stats.received_packets,
"received_dup_packets": stats.received_dup_packets,
"received_unsolicited": stats.received_unsolicited,
OZW_INSTANCE: msg[OZW_INSTANCE],
},
)

View File

@ -0,0 +1,57 @@
"""Test OpenZWave Websocket API."""
from homeassistant.components.ozw.websocket_api import ID, NODE_ID, OZW_INSTANCE, TYPE
from .common import setup_ozw
async def test_websocket_api(hass, generic_data, hass_ws_client):
"""Test the ozw websocket api."""
await setup_ozw(hass, fixture=generic_data)
client = await hass_ws_client(hass)
# Test network status
await client.send_json({ID: 5, TYPE: "ozw/network_status"})
msg = await client.receive_json()
result = msg["result"]
assert result["state"] == "driverAllNodesQueried"
assert result[OZW_INSTANCE] == 1
# Test node status
await client.send_json({ID: 6, TYPE: "ozw/node_status", NODE_ID: 32})
msg = await client.receive_json()
result = msg["result"]
assert result[OZW_INSTANCE] == 1
assert result[NODE_ID] == 32
assert result["node_query_stage"] == "Complete"
assert result["is_zwave_plus"]
assert result["is_awake"]
assert not result["is_failed"]
assert result["node_baud_rate"] == 100000
assert result["is_beaming"]
assert not result["is_flirs"]
assert result["is_routing"]
assert not result["is_securityv1"]
assert result["node_basic_string"] == "Routing Slave"
assert result["node_generic_string"] == "Binary Switch"
assert result["node_specific_string"] == "Binary Power Switch"
# Test node statistics
await client.send_json({ID: 7, TYPE: "ozw/node_statistics", NODE_ID: 39})
msg = await client.receive_json()
result = msg["result"]
assert result[OZW_INSTANCE] == 1
assert result[NODE_ID] == 39
assert result["send_count"] == 57
assert result["sent_failed"] == 0
assert result["retries"] == 1
assert result["last_request_rtt"] == 26
assert result["last_response_rtt"] == 38
assert result["average_request_rtt"] == 29
assert result["average_response_rtt"] == 37
assert result["received_packets"] == 3594
assert result["received_dup_packets"] == 12
assert result["received_unsolicited"] == 3546

View File

@ -279,4 +279,5 @@ OpenZWave/1/node/39/association/1/,{ "Name": "Lifeline", "Help": "", "M
OpenZWave/1/node/39/instance/1/commandclass/43/,{ "Instance": 1, "CommandClassId": 43, "CommandClass": "COMMAND_CLASS_SCENE_ACTIVATION", "TimeStamp": 1579566891}
OpenZWave/1/node/39/instance/1/commandclass/43/value/562950622511127/,{ "Label": "Scene", "Value": 0, "Units": "", "Min": -2147483648, "Max": 2147483647, "Type": "Int", "Instance": 1, "CommandClass": "COMMAND_CLASS_SCENE_ACTIVATION", "Index": 0, "Node": 7, "Genre": "User", "Help": "", "ValueIDKey": 122339347, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueChanged", "TimeStamp": 1579630367}
OpenZWave/1/node/39/instance/1/commandclass/91/,{ "Instance": 1, "CommandClassId": 91, "CommandClass": "COMMAND_CLASS_CENTRAL_SCENE", "TimeStamp": 1579630630}
OpenZWave/1/node/39/instance/1/commandclass/91/value/281476005806100/,{ "Label": "Scene 1", "Value": { "List": [ { "Value": 0, "Label": "Inactive" }, { "Value": 1, "Label": "Pressed 1 Time" }, { "Value": 2, "Label": "Key Released" }, { "Value": 3, "Label": "Key Held down" } ], "Selected": "Inactive", "Selected_id": 0 }, "Units": "", "Min": 0, "Max": 0, "Type": "List", "Instance": 1, "CommandClass": "COMMAND_CLASS_CENTRAL_SCENE", "Index": 1, "Node": 61, "Genre": "User", "Help": "", "ValueIDKey": 281476005806100, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueChanged", "TimeStamp": 1579640710}
OpenZWave/1/node/39/instance/1/commandclass/91/value/281476005806100/,{ "Label": "Scene 1", "Value": { "List": [ { "Value": 0, "Label": "Inactive" }, { "Value": 1, "Label": "Pressed 1 Time" }, { "Value": 2, "Label": "Key Released" }, { "Value": 3, "Label": "Key Held down" } ], "Selected": "Inactive", "Selected_id": 0 }, "Units": "", "Min": 0, "Max": 0, "Type": "List", "Instance": 1, "CommandClass": "COMMAND_CLASS_CENTRAL_SCENE", "Index": 1, "Node": 61, "Genre": "User", "Help": "", "ValueIDKey": 281476005806100, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueChanged", "TimeStamp": 1579640710}
OpenZWave/1/node/39/statistics/,{ "sendCount": 57, "sentFailed": 0, "retries": 1, "receivedPackets": 3594, "receivedDupPackets": 12, "receivedUnsolicited": 3546, "lastSentTimeStamp": 1595764791, "lastReceivedTimeStamp": 1595802261, "lastRequestRTT": 26, "averageRequestRTT": 29, "lastResponseRTT": 38, "averageResponseRTT": 37, "quality": 0, "extendedTXSupported": false, "txTime": 0, "hops": 0, "rssi_1": "", "rssi_2": "", "rssi_3": "", "rssi_4": "", "rssi_5": "", "route_1": 0, "route_2": 0, "route_3": 0, "route_4": 0, "ackChannel": 0, "lastTXChannel": 0, "routeScheme": "Idle", "routeUsed": "", "routeSpeed": "Auto", "routeTries": 0, "lastFailedLinkFrom": 0, "lastFailedLinkTo": 0}
Can't render this file because it contains an unexpected character in line 1 and column 26.