Zwave panel api (#7456)

* # This is a combination of 3 commits.
# The first commit's message is:
Add seperate zwave panel

# The 2nd commit message will be skipped:

#	unused import

# The 3rd commit message will be skipped:

#	Use get for config

* Add seperate zwave panel

* more info

* Add usercodeview

* Improve api

* Improve api

* Separate api into own file.

* disable missing import

* review changes

* Tests 1

* Verify that we fetch data from groups

* Tests groups

* config 1

* usercode 1

* Api mods

* Tweak API

* docstrings

* 100% api testing
This commit is contained in:
John Arild Berentsen 2017-05-10 03:56:41 +02:00 committed by Paulus Schoutsen
parent 5d820ec188
commit 1eaec8f406
3 changed files with 365 additions and 2 deletions

View File

@ -28,6 +28,7 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, async_dispatcher_send)
from homeassistant.components.frontend import register_built_in_panel
from . import api
from . import const
from .const import DOMAIN
from .node_entity import ZWaveBaseEntity, ZWaveNodeEntity
@ -66,6 +67,8 @@ DEFAULT_CONF_REFRESH_VALUE = False
DEFAULT_CONF_REFRESH_DELAY = 5
DATA_ZWAVE_DICT = 'zwave_devices'
OZW_LOG_FILENAME = 'OZW_Log.txt'
URL_API_OZW_LOG = '/api/zwave/ozwlog'
ZWAVE_NETWORK = 'zwave_network'
RENAME_NODE_SCHEMA = vol.Schema({
@ -383,7 +386,7 @@ def setup(hass, config):
def rename_node(service):
"""Rename a node."""
node_id = service.data.get(const.ATTR_NODE_ID)
node = hass.data[ZWAVE_NETWORK].nodes[node_id]
node = network.nodes[node_id]
name = service.data.get(const.ATTR_NAME)
node.name = name
_LOGGER.info(
@ -501,7 +504,7 @@ def setup(hass, config):
# to be ready.
for i in range(const.NETWORK_READY_WAIT_SECS):
_LOGGER.debug(
"network state: %d %s", hass.data[ZWAVE_NETWORK].state,
"network state: %d %s", network.state,
network.state_str)
if network.state >= network.STATE_AWAKED:
_LOGGER.info("Z-Wave ready after %d seconds", i)
@ -607,6 +610,11 @@ def setup(hass, config):
if 'frontend' in hass.config.components:
register_built_in_panel(hass, 'zwave', 'Z-Wave', 'mdi:nfc')
hass.http.register_view(api.ZWaveNodeGroupView)
hass.http.register_view(api.ZWaveNodeConfigView)
hass.http.register_view(api.ZWaveUserCodeView)
hass.http.register_static_path(
URL_API_OZW_LOG, hass.config.path(OZW_LOG_FILENAME), False)
return True

View File

@ -0,0 +1,95 @@
"""API class to give info to the Z-Wave panel."""
import logging
import homeassistant.core as ha
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import HTTP_NOT_FOUND
from . import const
_LOGGER = logging.getLogger(__name__)
ZWAVE_NETWORK = 'zwave_network'
class ZWaveNodeGroupView(HomeAssistantView):
"""View to return the nodes group configuration."""
url = r"/api/zwave/groups/{node_id:\d+}"
name = "api:zwave:groups"
@ha.callback
def get(self, request, node_id):
"""Retrieve groups of node."""
nodeid = int(node_id)
hass = request.app['hass']
network = hass.data.get(ZWAVE_NETWORK)
node = network.nodes.get(nodeid)
if node is None:
return self.json_message('Node not found', HTTP_NOT_FOUND)
groupdata = node.groups
groups = {}
for key, value in groupdata.items():
groups[key] = {'associations': value.associations,
'association_instances':
value.associations_instances,
'label': value.label,
'max_associations': value.max_associations}
return self.json(groups)
class ZWaveNodeConfigView(HomeAssistantView):
"""View to return the nodes configuration options."""
url = r"/api/zwave/config/{node_id:\d+}"
name = "api:zwave:config"
@ha.callback
def get(self, request, node_id):
"""Retrieve configurations of node."""
nodeid = int(node_id)
hass = request.app['hass']
network = hass.data.get(ZWAVE_NETWORK)
node = network.nodes.get(nodeid)
if node is None:
return self.json_message('Node not found', HTTP_NOT_FOUND)
config = {}
for value in (
node.get_values(class_id=const.COMMAND_CLASS_CONFIGURATION)
.values()):
config[value.index] = {'label': value.label,
'type': value.type,
'help': value.help,
'data_items': value.data_items,
'data': value.data,
'max': value.max,
'min': value.min}
return self.json(config)
class ZWaveUserCodeView(HomeAssistantView):
"""View to return the nodes usercode configuration."""
url = r"/api/zwave/usercodes/{node_id:\d+}"
name = "api:zwave:usercodes"
@ha.callback
def get(self, request, node_id):
"""Retrieve usercodes of node."""
nodeid = int(node_id)
hass = request.app['hass']
network = hass.data.get(ZWAVE_NETWORK)
node = network.nodes.get(nodeid)
if node is None:
return self.json_message('Node not found', HTTP_NOT_FOUND)
usercodes = {}
if not node.has_command_class(const.COMMAND_CLASS_USER_CODE):
return self.json(usercodes)
for value in (
node.get_values(class_id=const.COMMAND_CLASS_USER_CODE)
.values()):
if value.genre != const.GENRE_USER:
continue
usercodes[value.index] = {'code': value.data,
'label': value.label,
'length': len(value.data)}
return self.json(usercodes)

View File

@ -0,0 +1,260 @@
"""Test Z-Wave config panel."""
import asyncio
from unittest.mock import MagicMock
from homeassistant.components.zwave import ZWAVE_NETWORK, const
from homeassistant.components.zwave.api import (
ZWaveNodeGroupView, ZWaveNodeConfigView, ZWaveUserCodeView)
from tests.common import mock_http_component_app
from tests.mock.zwave import MockNode, MockValue
@asyncio.coroutine
def test_get_groups(hass, test_client):
"""Test getting groupdata on node."""
app = mock_http_component_app(hass)
ZWaveNodeGroupView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
node = MockNode(node_id=2)
node.groups.associations = 'assoc'
node.groups.associations_instances = 'inst'
node.groups.label = 'the label'
node.groups.max_associations = 'max'
node.groups = {1: node.groups}
network.nodes = {2: node}
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/groups/2')
assert resp.status == 200
result = yield from resp.json()
assert result == {
'1': {
'association_instances': 'inst',
'associations': 'assoc',
'label': 'the label',
'max_associations': 'max'
}
}
@asyncio.coroutine
def test_get_groups_nogroups(hass, test_client):
"""Test getting groupdata on node with no groups."""
app = mock_http_component_app(hass)
ZWaveNodeGroupView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
node = MockNode(node_id=2)
network.nodes = {2: node}
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/groups/2')
assert resp.status == 200
result = yield from resp.json()
assert result == {}
@asyncio.coroutine
def test_get_groups_nonode(hass, test_client):
"""Test getting groupdata on nonexisting node."""
app = mock_http_component_app(hass)
ZWaveNodeGroupView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
network.nodes = {1: 1, 5: 5}
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/groups/2')
assert resp.status == 404
result = yield from resp.json()
assert result == {'message': 'Node not found'}
@asyncio.coroutine
def test_get_config(hass, test_client):
"""Test getting config on node."""
app = mock_http_component_app(hass)
ZWaveNodeConfigView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
node = MockNode(node_id=2)
value = MockValue(
index=12,
command_class=const.COMMAND_CLASS_CONFIGURATION)
value.label = 'label'
value.help = 'help'
value.type = 'type'
value.data = 'data'
value.data_items = ['item1', 'item2']
value.max = 'max'
value.min = 'min'
node.values = {12: value}
network.nodes = {2: node}
node.get_values.return_value = node.values
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/config/2')
assert resp.status == 200
result = yield from resp.json()
assert result == {'12': {'data': 'data',
'data_items': ['item1', 'item2'],
'help': 'help',
'label': 'label',
'max': 'max',
'min': 'min',
'type': 'type'}}
@asyncio.coroutine
def test_get_config_noconfig_node(hass, test_client):
"""Test getting config on node without config."""
app = mock_http_component_app(hass)
ZWaveNodeConfigView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
node = MockNode(node_id=2)
network.nodes = {2: node}
node.get_values.return_value = node.values
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/config/2')
assert resp.status == 200
result = yield from resp.json()
assert result == {}
@asyncio.coroutine
def test_get_config_nonode(hass, test_client):
"""Test getting config on nonexisting node."""
app = mock_http_component_app(hass)
ZWaveNodeConfigView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
network.nodes = {1: 1, 5: 5}
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/config/2')
assert resp.status == 404
result = yield from resp.json()
assert result == {'message': 'Node not found'}
@asyncio.coroutine
def test_get_usercodes_nonode(hass, test_client):
"""Test getting usercodes on nonexisting node."""
app = mock_http_component_app(hass)
ZWaveUserCodeView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
network.nodes = {1: 1, 5: 5}
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/usercodes/2')
assert resp.status == 404
result = yield from resp.json()
assert result == {'message': 'Node not found'}
@asyncio.coroutine
def test_get_usercodes(hass, test_client):
"""Test getting usercodes on node."""
app = mock_http_component_app(hass)
ZWaveUserCodeView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
node = MockNode(node_id=18,
command_classes=[const.COMMAND_CLASS_USER_CODE])
value = MockValue(
index=0,
command_class=const.COMMAND_CLASS_USER_CODE)
value.genre = const.GENRE_USER
value.label = 'label'
value.data = '1234'
node.values = {0: value}
network.nodes = {18: node}
node.get_values.return_value = node.values
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/usercodes/18')
assert resp.status == 200
result = yield from resp.json()
assert result == {'0': {'code': '1234',
'label': 'label',
'length': 4}}
@asyncio.coroutine
def test_get_usercode_nousercode_node(hass, test_client):
"""Test getting usercodes on node without usercodes."""
app = mock_http_component_app(hass)
ZWaveUserCodeView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
node = MockNode(node_id=18)
network.nodes = {18: node}
node.get_values.return_value = node.values
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/usercodes/18')
assert resp.status == 200
result = yield from resp.json()
assert result == {}
@asyncio.coroutine
def test_get_usercodes_no_genreuser(hass, test_client):
"""Test getting usercodes on node missing genre user."""
app = mock_http_component_app(hass)
ZWaveUserCodeView().register(app.router)
network = hass.data[ZWAVE_NETWORK] = MagicMock()
node = MockNode(node_id=18,
command_classes=[const.COMMAND_CLASS_USER_CODE])
value = MockValue(
index=0,
command_class=const.COMMAND_CLASS_USER_CODE)
value.genre = const.GENRE_SYSTEM
value.label = 'label'
value.data = '1234'
node.values = {0: value}
network.nodes = {18: node}
node.get_values.return_value = node.values
client = yield from test_client(app)
resp = yield from client.get('/api/zwave/usercodes/18')
assert resp.status == 200
result = yield from resp.json()
assert result == {}