2019-02-13 20:21:14 +00:00
|
|
|
"""Support for deCONZ devices."""
|
2018-01-01 16:08:13 +00:00
|
|
|
import voluptuous as vol
|
|
|
|
|
2018-08-09 11:24:14 +00:00
|
|
|
from homeassistant import config_entries
|
2018-01-01 16:08:13 +00:00
|
|
|
from homeassistant.const import (
|
2018-10-31 21:38:04 +00:00
|
|
|
CONF_API_KEY, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP)
|
|
|
|
from homeassistant.helpers import config_validation as cv
|
2018-08-24 17:37:22 +00:00
|
|
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
2018-01-01 16:08:13 +00:00
|
|
|
|
2018-04-18 14:27:44 +00:00
|
|
|
# Loading the config flow file will register the flow
|
|
|
|
from .config_flow import configured_hosts
|
2018-11-06 09:34:24 +00:00
|
|
|
from .const import DEFAULT_PORT, DOMAIN, _LOGGER
|
2018-10-31 21:38:04 +00:00
|
|
|
from .gateway import DeconzGateway
|
2018-01-01 16:08:13 +00:00
|
|
|
|
2019-03-26 06:43:58 +00:00
|
|
|
REQUIREMENTS = ['pydeconz==54']
|
2018-09-21 17:59:20 +00:00
|
|
|
|
2018-01-01 16:08:13 +00:00
|
|
|
CONFIG_SCHEMA = vol.Schema({
|
|
|
|
DOMAIN: vol.Schema({
|
|
|
|
vol.Optional(CONF_API_KEY): cv.string,
|
2018-01-19 06:36:29 +00:00
|
|
|
vol.Optional(CONF_HOST): cv.string,
|
2018-11-06 09:34:24 +00:00
|
|
|
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
2018-01-01 16:08:13 +00:00
|
|
|
})
|
|
|
|
}, extra=vol.ALLOW_EXTRA)
|
|
|
|
|
2018-04-29 14:16:20 +00:00
|
|
|
SERVICE_DECONZ = 'configure'
|
|
|
|
|
2018-01-01 16:08:13 +00:00
|
|
|
SERVICE_FIELD = 'field'
|
2018-02-14 00:23:04 +00:00
|
|
|
SERVICE_ENTITY = 'entity'
|
2018-01-01 16:08:13 +00:00
|
|
|
SERVICE_DATA = 'data'
|
|
|
|
|
2018-10-26 07:15:26 +00:00
|
|
|
SERVICE_SCHEMA = vol.All(vol.Schema({
|
|
|
|
vol.Optional(SERVICE_ENTITY): cv.entity_id,
|
|
|
|
vol.Optional(SERVICE_FIELD): cv.matches_regex('/.*'),
|
2018-01-30 22:42:24 +00:00
|
|
|
vol.Required(SERVICE_DATA): dict,
|
2018-10-26 07:15:26 +00:00
|
|
|
}), cv.has_at_least_one_key(SERVICE_ENTITY, SERVICE_FIELD))
|
2018-01-01 16:08:13 +00:00
|
|
|
|
2018-09-04 07:24:42 +00:00
|
|
|
SERVICE_DEVICE_REFRESH = 'device_refresh'
|
|
|
|
|
2018-02-14 00:23:04 +00:00
|
|
|
|
2018-03-13 07:47:45 +00:00
|
|
|
async def async_setup(hass, config):
|
2018-04-18 14:27:44 +00:00
|
|
|
"""Load configuration for deCONZ component.
|
2018-01-01 16:08:13 +00:00
|
|
|
|
2018-04-18 14:27:44 +00:00
|
|
|
Discovery has loaded the component if DOMAIN is not present in config.
|
|
|
|
"""
|
|
|
|
if DOMAIN in config:
|
|
|
|
deconz_config = None
|
2018-11-06 09:34:24 +00:00
|
|
|
if CONF_HOST in config[DOMAIN]:
|
2018-04-18 14:27:44 +00:00
|
|
|
deconz_config = config[DOMAIN]
|
|
|
|
if deconz_config and not configured_hosts(hass):
|
|
|
|
hass.async_add_job(hass.config_entries.flow.async_init(
|
2018-08-09 11:24:14 +00:00
|
|
|
DOMAIN,
|
|
|
|
context={'source': config_entries.SOURCE_IMPORT},
|
|
|
|
data=deconz_config
|
2018-04-18 14:27:44 +00:00
|
|
|
))
|
|
|
|
return True
|
2018-01-01 16:08:13 +00:00
|
|
|
|
|
|
|
|
2018-04-23 16:00:16 +00:00
|
|
|
async def async_setup_entry(hass, config_entry):
|
|
|
|
"""Set up a deCONZ bridge for a config entry.
|
2018-01-01 16:08:13 +00:00
|
|
|
|
|
|
|
Load config, group, light and sensor data for server information.
|
|
|
|
Start websocket for push notification of state changes from deCONZ.
|
|
|
|
"""
|
2018-04-23 16:00:16 +00:00
|
|
|
if DOMAIN in hass.data:
|
|
|
|
_LOGGER.error(
|
|
|
|
"Config entry failed since one deCONZ instance already exists")
|
|
|
|
return False
|
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway = DeconzGateway(hass, config_entry)
|
2018-05-05 14:11:00 +00:00
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
if not await gateway.async_setup():
|
2018-01-01 16:08:13 +00:00
|
|
|
return False
|
|
|
|
|
2019-02-27 20:04:55 +00:00
|
|
|
hass.data[DOMAIN] = gateway
|
|
|
|
|
2018-08-24 17:37:22 +00:00
|
|
|
device_registry = await \
|
|
|
|
hass.helpers.device_registry.async_get_registry()
|
|
|
|
device_registry.async_get_or_create(
|
2018-09-17 11:39:30 +00:00
|
|
|
config_entry_id=config_entry.entry_id,
|
2018-10-31 21:38:04 +00:00
|
|
|
connections={(CONNECTION_NETWORK_MAC, gateway.api.config.mac)},
|
|
|
|
identifiers={(DOMAIN, gateway.api.config.bridgeid)},
|
|
|
|
manufacturer='Dresden Elektronik', model=gateway.api.config.modelid,
|
|
|
|
name=gateway.api.config.name, sw_version=gateway.api.config.swversion)
|
2018-08-24 17:37:22 +00:00
|
|
|
|
2018-03-13 07:47:45 +00:00
|
|
|
async def async_configure(call):
|
2018-01-01 16:08:13 +00:00
|
|
|
"""Set attribute of device in deCONZ.
|
|
|
|
|
2018-10-26 07:15:26 +00:00
|
|
|
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.
|
2018-01-01 16:08:13 +00:00
|
|
|
Data is a json object with what data you want to alter
|
|
|
|
e.g. data={'on': true}.
|
|
|
|
{
|
|
|
|
"field": "/lights/1/state",
|
|
|
|
"data": {"on": true}
|
|
|
|
}
|
|
|
|
See Dresden Elektroniks REST API documentation for details:
|
|
|
|
http://dresden-elektronik.github.io/deconz-rest-doc/rest/
|
|
|
|
"""
|
2018-10-26 07:15:26 +00:00
|
|
|
field = call.data.get(SERVICE_FIELD, '')
|
2018-02-14 00:23:04 +00:00
|
|
|
entity_id = call.data.get(SERVICE_ENTITY)
|
2018-01-01 16:08:13 +00:00
|
|
|
data = call.data.get(SERVICE_DATA)
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway = hass.data[DOMAIN]
|
|
|
|
|
2018-02-14 00:23:04 +00:00
|
|
|
if entity_id:
|
2018-10-26 07:15:26 +00:00
|
|
|
try:
|
2018-10-31 21:38:04 +00:00
|
|
|
field = gateway.deconz_ids[entity_id] + field
|
2018-10-26 07:15:26 +00:00
|
|
|
except KeyError:
|
2018-02-14 00:23:04 +00:00
|
|
|
_LOGGER.error('Could not find the entity %s', entity_id)
|
|
|
|
return
|
2018-09-04 07:24:42 +00:00
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
await gateway.api.async_put_state(field, data)
|
2018-09-04 07:24:42 +00:00
|
|
|
|
2018-01-01 16:08:13 +00:00
|
|
|
hass.services.async_register(
|
2018-04-29 14:16:20 +00:00
|
|
|
DOMAIN, SERVICE_DECONZ, async_configure, schema=SERVICE_SCHEMA)
|
2018-01-01 16:08:13 +00:00
|
|
|
|
2018-09-04 07:24:42 +00:00
|
|
|
async def async_refresh_devices(call):
|
|
|
|
"""Refresh available devices from deCONZ."""
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway = hass.data[DOMAIN]
|
2018-09-04 07:24:42 +00:00
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
groups = set(gateway.api.groups.keys())
|
|
|
|
lights = set(gateway.api.lights.keys())
|
|
|
|
scenes = set(gateway.api.scenes.keys())
|
|
|
|
sensors = set(gateway.api.sensors.keys())
|
2018-09-04 07:24:42 +00:00
|
|
|
|
2019-03-24 18:27:32 +00:00
|
|
|
await gateway.api.async_load_parameters()
|
2018-09-04 07:24:42 +00:00
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway.async_add_device_callback(
|
2018-09-04 07:24:42 +00:00
|
|
|
'group', [group
|
2018-10-31 21:38:04 +00:00
|
|
|
for group_id, group in gateway.api.groups.items()
|
2018-09-04 07:24:42 +00:00
|
|
|
if group_id not in groups]
|
|
|
|
)
|
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway.async_add_device_callback(
|
2018-09-04 07:24:42 +00:00
|
|
|
'light', [light
|
2018-10-31 21:38:04 +00:00
|
|
|
for light_id, light in gateway.api.lights.items()
|
2018-09-04 07:24:42 +00:00
|
|
|
if light_id not in lights]
|
|
|
|
)
|
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway.async_add_device_callback(
|
2018-09-04 07:24:42 +00:00
|
|
|
'scene', [scene
|
2018-10-31 21:38:04 +00:00
|
|
|
for scene_id, scene in gateway.api.scenes.items()
|
2018-09-04 07:24:42 +00:00
|
|
|
if scene_id not in scenes]
|
|
|
|
)
|
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway.async_add_device_callback(
|
2018-09-04 07:24:42 +00:00
|
|
|
'sensor', [sensor
|
2018-10-31 21:38:04 +00:00
|
|
|
for sensor_id, sensor in gateway.api.sensors.items()
|
2018-09-04 07:24:42 +00:00
|
|
|
if sensor_id not in sensors]
|
|
|
|
)
|
|
|
|
|
|
|
|
hass.services.async_register(
|
|
|
|
DOMAIN, SERVICE_DEVICE_REFRESH, async_refresh_devices)
|
|
|
|
|
2018-10-31 21:38:04 +00:00
|
|
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gateway.shutdown)
|
2018-01-01 16:08:13 +00:00
|
|
|
return True
|
2018-04-29 14:16:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def async_unload_entry(hass, config_entry):
|
|
|
|
"""Unload deCONZ config entry."""
|
2018-10-31 21:38:04 +00:00
|
|
|
gateway = hass.data.pop(DOMAIN)
|
2018-04-29 14:16:20 +00:00
|
|
|
hass.services.async_remove(DOMAIN, SERVICE_DECONZ)
|
2018-10-31 21:38:04 +00:00
|
|
|
hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH)
|
|
|
|
return await gateway.async_reset()
|