Skip to content

Commit

Permalink
Merge 47cd586 into 77bf10e
Browse files Browse the repository at this point in the history
  • Loading branch information
lbschenkel committed Oct 25, 2018
2 parents 77bf10e + 47cd586 commit 436c336
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 20 deletions.
27 changes: 12 additions & 15 deletions homeassistant/components/deconz/__init__.py
Expand Up @@ -43,11 +43,11 @@
SERVICE_ENTITY = 'entity'
SERVICE_DATA = 'data'

SERVICE_SCHEMA = vol.Schema({
vol.Exclusive(SERVICE_FIELD, 'deconz_id'): cv.string,
vol.Exclusive(SERVICE_ENTITY, 'deconz_id'): cv.entity_id,
SERVICE_SCHEMA = vol.All(vol.Schema({
vol.Optional(SERVICE_ENTITY): cv.entity_id,
vol.Optional(SERVICE_FIELD): cv.matches_regex('/.*'),
vol.Required(SERVICE_DATA): dict,
})
}), cv.has_at_least_one_key(SERVICE_ENTITY, SERVICE_FIELD))

SERVICE_DEVICE_REFRESH = 'device_refresh'

Expand Down Expand Up @@ -139,9 +139,10 @@ def async_add_remote(sensors):
async def async_configure(call):
"""Set attribute of device in deCONZ.
Field is a string representing a specific device in deCONZ
e.g. field='/lights/1/state'.
Entity_id can be used to retrieve the proper field.
Entity is used to resolve to a device path (e.g. '/lights/1').
Field is a string representing either a full path
(e.g. '/lights/1/state') when entity is not specified, or a
subpath (e.g. '/state') when used together with entity.
Data is a json object with what data you want to alter
e.g. data={'on': true}.
{
Expand All @@ -151,18 +152,14 @@ async def async_configure(call):
See Dresden Elektroniks REST API documentation for details:
http://dresden-elektronik.github.io/deconz-rest-doc/rest/
"""
field = call.data.get(SERVICE_FIELD)
field = call.data.get(SERVICE_FIELD, '')
entity_id = call.data.get(SERVICE_ENTITY)
data = call.data.get(SERVICE_DATA)
deconz = hass.data[DOMAIN]
if entity_id:

entities = hass.data.get(DATA_DECONZ_ID)

if entities:
field = entities.get(entity_id)

if field is None:
try:
field = hass.data[DATA_DECONZ_ID][entity_id] + field
except KeyError:
_LOGGER.error('Could not find the entity %s', entity_id)
return

Expand Down
9 changes: 6 additions & 3 deletions homeassistant/components/deconz/services.yaml
@@ -1,12 +1,15 @@
configure:
description: Set attribute of device in deCONZ. See https://home-assistant.io/components/deconz/#device-services for details.
fields:
field:
description: Field is a string representing a specific device in deCONZ.
example: '/lights/1/state'
entity:
description: Entity id representing a specific device in deCONZ.
example: 'light.rgb_light'
field:
description: >-
Field is a string representing a full path to deCONZ endpoint (when
entity is not specified) or a subpath of the device path for the
entity (when entity is specified).
example: '"/lights/1/state" or "/state"'
data:
description: Data is a json object with what data you want to alter.
example: '{"on": true}'
Expand Down
55 changes: 53 additions & 2 deletions tests/components/deconz/test_init.py
@@ -1,10 +1,10 @@
"""Test deCONZ component setup process."""
from unittest.mock import Mock, patch

from homeassistant.components import deconz
from homeassistant.components.deconz import DATA_DECONZ_ID
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.setup import async_setup_component
from homeassistant.components import deconz

from tests.common import mock_coro

CONFIG = {
Expand Down Expand Up @@ -218,3 +218,54 @@ async def test_do_not_allow_clip_sensor(hass):
async_dispatcher_send(hass, 'deconz_new_sensor', [remote])
await hass.async_block_till_done()
assert len(hass.data[deconz.DATA_DECONZ_EVENT]) == 0


async def test_service_configure(hass):
"""Test that service invokes pydeconz with the correct path and data."""
entry = Mock()
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
with patch('pydeconz.DeconzSession.async_get_state',
return_value=mock_coro(CONFIG)), \
patch('pydeconz.DeconzSession.start', return_value=True), \
patch('homeassistant.helpers.device_registry.async_get_registry',
return_value=mock_coro(Mock())):
assert await deconz.async_setup_entry(hass, entry) is True

hass.data[DATA_DECONZ_ID] = {
'light.test': '/light/1'
}
data = {'on': True, 'attr1': 10, 'attr2': 20}

# only field
with patch('pydeconz.DeconzSession.async_put_state') as async_put_state:
await hass.services.async_call('deconz', 'configure', service_data={
'field': '/light/42', 'data': data
})
await hass.async_block_till_done()
async_put_state.assert_called_with('/light/42', data)
# only entity
with patch('pydeconz.DeconzSession.async_put_state') as async_put_state:
await hass.services.async_call('deconz', 'configure', service_data={
'entity': 'light.test', 'data': data
})
await hass.async_block_till_done()
async_put_state.assert_called_with('/light/1', data)
# entity + field
with patch('pydeconz.DeconzSession.async_put_state') as async_put_state:
await hass.services.async_call('deconz', 'configure', service_data={
'entity': 'light.test', 'field': '/state', 'data': data})
await hass.async_block_till_done()
async_put_state.assert_called_with('/light/1/state', data)

# non-existing entity (or not from deCONZ)
with patch('pydeconz.DeconzSession.async_put_state') as async_put_state:
await hass.services.async_call('deconz', 'configure', service_data={
'entity': 'light.nonexisting', 'field': '/state', 'data': data})
await hass.async_block_till_done()
async_put_state.assert_not_called()
# field does not start with /
with patch('pydeconz.DeconzSession.async_put_state') as async_put_state:
await hass.services.async_call('deconz', 'configure', service_data={
'entity': 'light.test', 'field': 'state', 'data': data})
await hass.async_block_till_done()
async_put_state.assert_not_called()

0 comments on commit 436c336

Please sign in to comment.