mirror of
https://github.com/home-assistant/core.git
synced 2025-04-22 16:27:56 +00:00
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:
parent
5d820ec188
commit
1eaec8f406
@ -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
|
||||
|
||||
|
95
homeassistant/components/zwave/api.py
Normal file
95
homeassistant/components/zwave/api.py
Normal 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)
|
260
tests/components/zwave/test_api.py
Normal file
260
tests/components/zwave/test_api.py
Normal 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 == {}
|
Loading…
x
Reference in New Issue
Block a user