Skip to content
Merged
49 changes: 49 additions & 0 deletions homeassistant/components/config/zwave.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def async_setup(hass):
hass.http.register_view(ZWaveUserCodeView)
hass.http.register_view(ZWaveLogView)
hass.http.register_view(ZWaveConfigWriteView)
hass.http.register_view(ZWaveProtectionView)

return True

Expand Down Expand Up @@ -196,3 +197,51 @@ def get(self, request, node_id):
'label': value.label,
'length': len(value.data)}
return self.json(usercodes)


class ZWaveProtectionView(HomeAssistantView):
"""View for the protection commandclass of a node."""

url = r"/api/zwave/protection/{node_id:\d+}"
name = "api:zwave:protection"

@ha.callback
def get(self, request, node_id):
"""Retrieve the protection commandclass options of node."""
nodeid = int(node_id)
hass = request.app['hass']
network = hass.data.get(const.DATA_NETWORK)
node = network.nodes.get(nodeid)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is doing I/O ?

if node is None:
return self.json_message('Node not found', HTTP_NOT_FOUND)
protection_options = {}
if not node.has_command_class(const.COMMAND_CLASS_PROTECTION):
return self.json(protection_options)
protections = node.get_protections()
protection_options = {
'value_id': '{0:d}'.format(list(protections.keys())[0]),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need the .keys(). Passing a dict into a list copy will automatically make a list out of the keys.

'selected': node.get_protection_item(list(protections.keys())[0]),
'options': node.get_protection_items(list(protections.keys())[0])}
return self.json(protection_options)

async def post(self, request, node_id):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many blank lines (2)

"""Change the selected option in protection commandclass."""
nodeid = int(node_id)
hass = request.app['hass']
network = hass.data.get(const.DATA_NETWORK)
node = network.nodes.get(nodeid)
protection_data = await request.json()

selection = protection_data["selection"]
value_id = int(protection_data[const.ATTR_VALUE_ID])
if node is None:
return self.json_message('Node not found', HTTP_NOT_FOUND)
if not node.has_command_class(const.COMMAND_CLASS_PROTECTION):
return self.json_message('No protection commandclass on this node',
HTTP_NOT_FOUND)
state = node.set_protection(value_id, selection)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is doing I/O ?

if not state:
return self.json_message(
'Protection setting did not complete', 202)
return self.json_message(
'Protection setting succsessfully set', HTTP_OK)
196 changes: 196 additions & 0 deletions tests/components/config/test_zwave.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,199 @@ def test_save_config(hass, client):
result = yield from resp.json()
assert network.write_config.called
assert result == {'message': 'Z-Wave configuration saved to file.'}


@asyncio.coroutine

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the Python 3.5 async syntax instead. async def.

def test_get_protection_values(hass, client):
"""Test getting protection values on node."""
network = hass.data[DATA_NETWORK] = MagicMock()
node = MockNode(node_id=18,
command_classes=[const.COMMAND_CLASS_PROTECTION])
value = MockValue(
value_id=123456,
index=0,
instance=1,
command_class=const.COMMAND_CLASS_PROTECTION)
value.label = 'Protection Test'
value.data_items = ['Unprotected', 'Protection by Sequence',
'No Operation Possible']
value.data = 'Unprotected'
network.nodes = {18: node}
node.value = value

node.get_protection_item.return_value = "Unprotected"
node.get_protection_items.return_value = value.data_items
node.get_protections.return_value = {value.value_id: 'Object'}

resp = yield from client.get('/api/zwave/protection/18')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many blank lines (2)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use await instead of yield from.


assert resp.status == 200
result = yield from resp.json()
assert node.get_protections.called
assert node.get_protection_item.called
assert node.get_protection_items.called
assert result == {
'value_id': '123456',
'selected': 'Unprotected',
'options': ['Unprotected', 'Protection by Sequence',
'No Operation Possible']
}


@asyncio.coroutine
def test_get_protection_values_nonexisting_node(hass, client):
"""Test getting protection values on node with wrong nodeid."""
network = hass.data[DATA_NETWORK] = MagicMock()
node = MockNode(node_id=18,
command_classes=[const.COMMAND_CLASS_PROTECTION])
value = MockValue(
value_id=123456,
index=0,
instance=1,
command_class=const.COMMAND_CLASS_PROTECTION)
value.label = 'Protection Test'
value.data_items = ['Unprotected', 'Protection by Sequence',
'No Operation Possible']
value.data = 'Unprotected'
network.nodes = {17: node}
node.value = value

resp = yield from client.get('/api/zwave/protection/18')

assert resp.status == 404
result = yield from resp.json()
assert not node.get_protections.called
assert not node.get_protection_item.called
assert not node.get_protection_items.called
assert result == {'message': 'Node not found'}


@asyncio.coroutine
def test_get_protection_values_without_protectionclass(hass, client):
"""Test getting protection values on node without protectionclass."""
network = hass.data[DATA_NETWORK] = MagicMock()
node = MockNode(node_id=18)
value = MockValue(
value_id=123456,
index=0,
instance=1)
network.nodes = {18: node}
node.value = value

resp = yield from client.get('/api/zwave/protection/18')

assert resp.status == 200
result = yield from resp.json()
assert not node.get_protections.called
assert not node.get_protection_item.called
assert not node.get_protection_items.called
assert result == {}


@asyncio.coroutine
def test_set_protection_value(hass, client):
"""Test setting protection value on node."""
network = hass.data[DATA_NETWORK] = MagicMock()
node = MockNode(node_id=18,
command_classes=[const.COMMAND_CLASS_PROTECTION])
value = MockValue(
value_id=123456,
index=0,
instance=1,
command_class=const.COMMAND_CLASS_PROTECTION)
value.label = 'Protection Test'
value.data_items = ['Unprotected', 'Protection by Sequence',
'No Operation Possible']
value.data = 'Unprotected'
network.nodes = {18: node}
node.value = value

resp = yield from client.post(
'/api/zwave/protection/18', data=json.dumps({
'value_id': '123456', 'selection': 'Protection by Sequence'}))

assert resp.status == 200
result = yield from resp.json()
assert node.set_protection.called
assert result == {'message': 'Protection setting succsessfully set'}


@asyncio.coroutine
def test_set_protection_value_failed(hass, client):
"""Test setting protection value failed on node."""
network = hass.data[DATA_NETWORK] = MagicMock()
node = MockNode(node_id=18,
command_classes=[const.COMMAND_CLASS_PROTECTION])
value = MockValue(
value_id=123456,
index=0,
instance=1,
command_class=const.COMMAND_CLASS_PROTECTION)
value.label = 'Protection Test'
value.data_items = ['Unprotected', 'Protection by Sequence',
'No Operation Possible']
value.data = 'Unprotected'
network.nodes = {18: node}
node.value = value
node.set_protection.return_value = False

resp = yield from client.post(
'/api/zwave/protection/18', data=json.dumps({
'value_id': '123456', 'selection': 'Protecton by Seuence'}))

assert resp.status == 202
result = yield from resp.json()
assert node.set_protection.called
assert result == {'message': 'Protection setting did not complete'}


@asyncio.coroutine
def test_set_protection_value_nonexisting_node(hass, client):
"""Test setting protection value on nonexisting node."""
network = hass.data[DATA_NETWORK] = MagicMock()
node = MockNode(node_id=17,
command_classes=[const.COMMAND_CLASS_PROTECTION])
value = MockValue(
value_id=123456,
index=0,
instance=1,
command_class=const.COMMAND_CLASS_PROTECTION)
value.label = 'Protection Test'
value.data_items = ['Unprotected', 'Protection by Sequence',
'No Operation Possible']
value.data = 'Unprotected'
network.nodes = {17: node}
node.value = value
node.set_protection.return_value = False

resp = yield from client.post(
'/api/zwave/protection/18', data=json.dumps({
'value_id': '123456', 'selection': 'Protecton by Seuence'}))

assert resp.status == 404
result = yield from resp.json()
assert not node.set_protection.called
assert result == {'message': 'Node not found'}


@asyncio.coroutine
def test_set_protection_value_missing_class(hass, client):
"""Test setting protection value on node without protectionclass."""
network = hass.data[DATA_NETWORK] = MagicMock()
node = MockNode(node_id=17)
value = MockValue(
value_id=123456,
index=0,
instance=1)
network.nodes = {17: node}
node.value = value
node.set_protection.return_value = False

resp = yield from client.post(
'/api/zwave/protection/17', data=json.dumps({
'value_id': '123456', 'selection': 'Protecton by Seuence'}))

assert resp.status == 404
result = yield from resp.json()
assert not node.set_protection.called
assert result == {'message': 'No protection commandclass on this node'}