Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions homeassistant/components/zwave/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from . import api
from . import const
from .const import DOMAIN, DATA_DEVICES, DATA_NETWORK
from .const import DOMAIN, DATA_DEVICES, DATA_NETWORK, DATA_ENTITY_VALUES
from .node_entity import ZWaveBaseEntity, ZWaveNodeEntity
from . import workaround
from .discovery_schemas import DISCOVERY_SCHEMAS
Expand Down Expand Up @@ -74,12 +74,20 @@
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
vol.Required(const.ATTR_NAME): cv.string,
})

RENAME_VALUE_SCHEMA = vol.Schema({
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
vol.Required(const.ATTR_VALUE_ID): vol.Coerce(int),
vol.Required(const.ATTR_NAME): cv.string,
})

SET_CONFIG_PARAMETER_SCHEMA = vol.Schema({
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
vol.Required(const.ATTR_CONFIG_PARAMETER): vol.Coerce(int),
vol.Required(const.ATTR_CONFIG_VALUE): vol.Any(vol.Coerce(int), cv.string),
vol.Optional(const.ATTR_CONFIG_SIZE, default=2): vol.Coerce(int)
})

PRINT_CONFIG_PARAMETER_SCHEMA = vol.Schema({
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
vol.Required(const.ATTR_CONFIG_PARAMETER): vol.Coerce(int),
Expand Down Expand Up @@ -258,6 +266,7 @@ def setup(hass, config):

network = hass.data[DATA_NETWORK] = ZWaveNetwork(options, autostart=False)
hass.data[DATA_DEVICES] = {}
hass.data[DATA_ENTITY_VALUES] = []

if use_debug: # pragma: no cover
def log_all(signal, value=None):
Expand All @@ -276,12 +285,10 @@ def log_all(signal, value=None):

dispatcher.connect(log_all, weak=False)

discovered_values = []

def value_added(node, value):
"""Handle new added value to a node on the network."""
# Check if this value should be tracked by an existing entity
for values in discovered_values:
for values in hass.data[DATA_ENTITY_VALUES]:
values.check_value(value)

for schema in DISCOVERY_SCHEMAS:
Expand All @@ -294,7 +301,11 @@ def value_added(node, value):

values = ZWaveDeviceEntityValues(
hass, schema, value, config, device_config)
discovered_values.append(values)

# We create a new list and update the reference here so that
# the list can be safely iterated over in the main thread
new_values = hass.data[DATA_ENTITY_VALUES] + [values]
hass.data[DATA_ENTITY_VALUES] = new_values

component = EntityComponent(_LOGGER, DOMAIN, hass)

Expand Down Expand Up @@ -401,6 +412,18 @@ def rename_node(service):
_LOGGER.info(
"Renamed Z-Wave node %d to %s", node_id, name)

def rename_value(service):
"""Rename a node value."""
node_id = service.data.get(const.ATTR_NODE_ID)
value_id = service.data.get(const.ATTR_VALUE_ID)
node = network.nodes[node_id]
value = node.values[value_id]
name = service.data.get(const.ATTR_NAME)
value.label = name
_LOGGER.info(
"Renamed Z-Wave value (Node %d Value %d) to %s",
node_id, value_id, name)

def remove_failed_node(service):
"""Remove failed node."""
node_id = service.data.get(const.ATTR_NODE_ID)
Expand Down Expand Up @@ -585,6 +608,10 @@ def start_zwave(_service_or_event):
hass.services.register(DOMAIN, const.SERVICE_RENAME_NODE, rename_node,
descriptions[const.SERVICE_RENAME_NODE],
schema=RENAME_NODE_SCHEMA)
hass.services.register(DOMAIN, const.SERVICE_RENAME_VALUE,
rename_value,
descriptions[const.SERVICE_RENAME_VALUE],
schema=RENAME_VALUE_SCHEMA)
hass.services.register(DOMAIN, const.SERVICE_SET_CONFIG_PARAMETER,
set_config_parameter,
descriptions[
Expand Down Expand Up @@ -644,6 +671,7 @@ def start_zwave(_service_or_event):

if 'frontend' in hass.config.components:
register_built_in_panel(hass, 'zwave', 'Z-Wave', 'mdi:nfc')
hass.http.register_view(api.ZWaveNodeValueView)
hass.http.register_view(api.ZWaveNodeGroupView)
hass.http.register_view(api.ZWaveNodeConfigView)
hass.http.register_view(api.ZWaveUserCodeView)
Expand Down
26 changes: 26 additions & 0 deletions homeassistant/components/zwave/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@
_LOGGER = logging.getLogger(__name__)


class ZWaveNodeValueView(HomeAssistantView):
"""View to return the node values."""

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

@ha.callback
def get(self, request, node_id):
"""Retrieve groups of node."""
nodeid = int(node_id)
hass = request.app['hass']
values_list = hass.data[const.DATA_ENTITY_VALUES]

values_data = {}
# Return a list of values for this node that are used as a
# primary value for an entity
for entity_values in values_list:
if entity_values.primary.node.node_id != nodeid:
continue

values_data[entity_values.primary.value_id] = {
'label': entity_values.primary.label,
}
return self.json(values_data)


class ZWaveNodeGroupView(HomeAssistantView):
"""View to return the nodes group configuration."""

Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/zwave/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

DATA_DEVICES = 'zwave_devices'
DATA_NETWORK = 'zwave_network'
DATA_ENTITY_VALUES = 'zwave_entity_values'

SERVICE_CHANGE_ASSOCIATION = "change_association"
SERVICE_ADD_NODE = "add_node"
Expand All @@ -38,6 +39,7 @@
SERVICE_STOP_NETWORK = "stop_network"
SERVICE_START_NETWORK = "start_network"
SERVICE_RENAME_NODE = "rename_node"
SERVICE_RENAME_VALUE = "rename_value"
SERVICE_REFRESH_ENTITY = "refresh_entity"
SERVICE_REFRESH_NODE = "refresh_node"
SERVICE_RESET_NODE_METERS = "reset_node_meters"
Expand Down
13 changes: 13 additions & 0 deletions homeassistant/components/zwave/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ rename_node:
description: New Name
example: 'kitchen'

rename_value:
description: Set the name of a node value. Value IDs can be queried from /api/zwave/values/{node_id}
fields:
node_id:
description: ID of the node to rename.
example: 10
value_id:
description: ID of the value to rename.
example: 72037594255792737
name:
description: New Name
example: 'Luminosity'

reset_node_meters:
description: Resets the meter counters of a node.
fields:
Expand Down
33 changes: 31 additions & 2 deletions tests/components/zwave/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,38 @@
from unittest.mock import MagicMock
from homeassistant.components.zwave import DATA_NETWORK, const
from homeassistant.components.zwave.api import (
ZWaveNodeGroupView, ZWaveNodeConfigView, ZWaveUserCodeView)
ZWaveNodeValueView, ZWaveNodeGroupView, ZWaveNodeConfigView,
ZWaveUserCodeView)
from tests.common import mock_http_component_app
from tests.mock.zwave import MockNode, MockValue
from tests.mock.zwave import MockNode, MockValue, MockEntityValues


@asyncio.coroutine
def test_get_values(hass, test_client):
"""Test getting values on node."""
app = mock_http_component_app(hass)
ZWaveNodeValueView().register(app.router)

node = MockNode(node_id=1)
value = MockValue(value_id=123456, node=node, label='Test Label')
values = MockEntityValues(primary=value)
node2 = MockNode(node_id=2)
value2 = MockValue(value_id=234567, node=node2, label='Test Label 2')
values2 = MockEntityValues(primary=value2)
hass.data[const.DATA_ENTITY_VALUES] = [values, values2]

client = yield from test_client(app)

resp = yield from client.get('/api/zwave/values/1')

assert resp.status == 200
result = yield from resp.json()

assert result == {
'123456': {
'label': 'Test Label',
}
}


@asyncio.coroutine
Expand Down
17 changes: 17 additions & 0 deletions tests/components/zwave/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,23 @@ def test_rename_node(self):

assert self.zwave_network.nodes[11].name == 'test_name'

def test_rename_value(self):
"""Test zwave rename_value service."""
node = MockNode(node_id=14)
value = MockValue(index=12, value_id=123456, label="Old Label")
node.values = {123456: value}
self.zwave_network.nodes = {11: node}

assert value.label == "Old Label"
self.hass.services.call('zwave', 'rename_value', {
const.ATTR_NODE_ID: 11,
const.ATTR_VALUE_ID: 123456,
const.ATTR_NAME: "New Label",
})
self.hass.block_till_done()

assert value.label == "New Label"

def test_remove_failed_node(self):
"""Test zwave remove_failed_node service."""
self.hass.services.call('zwave', 'remove_failed_node', {
Expand Down