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
27 changes: 17 additions & 10 deletions homeassistant/components/binary_sensor/deconz.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.deconz.const import (
ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DATA_DECONZ,
DECONZ_DOMAIN)
ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DECONZ_REACHABLE,
DOMAIN as DECONZ_DOMAIN)
from homeassistant.const import ATTR_BATTERY_LEVEL
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
Expand All @@ -24,6 +24,8 @@ async def async_setup_platform(hass, config, async_add_entities,

async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ binary sensor."""
gateway = hass.data[DECONZ_DOMAIN]

@callback
def async_add_sensor(sensors):
"""Add binary sensor from deCONZ."""
Expand All @@ -33,30 +35,35 @@ def async_add_sensor(sensors):
for sensor in sensors:
if sensor.type in DECONZ_BINARY_SENSOR and \
not (not allow_clip_sensor and sensor.type.startswith('CLIP')):
entities.append(DeconzBinarySensor(sensor))
entities.append(DeconzBinarySensor(sensor, gateway))
async_add_entities(entities, True)

hass.data[DATA_DECONZ].listeners.append(
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor))

async_add_sensor(hass.data[DATA_DECONZ].api.sensors.values())
async_add_sensor(gateway.api.sensors.values())


class DeconzBinarySensor(BinarySensorDevice):
"""Representation of a binary sensor."""

def __init__(self, sensor):
def __init__(self, sensor, gateway):
"""Set up sensor and add update callback to get data from websocket."""
self._sensor = sensor
self.gateway = gateway
self.unsub_dispatcher = None

async def async_added_to_hass(self):
"""Subscribe sensors events."""
self._sensor.register_async_callback(self.async_update_callback)
self.hass.data[DATA_DECONZ].deconz_ids[self.entity_id] = \
self._sensor.deconz_id
self.gateway.deconz_ids[self.entity_id] = self._sensor.deconz_id
self.unsub_dispatcher = async_dispatcher_connect(
self.hass, DECONZ_REACHABLE, self.async_update_callback)

async def async_will_remove_from_hass(self) -> None:
"""Disconnect sensor object when removed."""
if self.unsub_dispatcher is not None:
self.unsub_dispatcher()
self._sensor.remove_callback(self.async_update_callback)
self._sensor = None

Expand Down Expand Up @@ -101,7 +108,7 @@ def icon(self):
@property
def available(self):
"""Return True if sensor is available."""
return self._sensor.reachable
return self.gateway.available and self._sensor.reachable

@property
def should_poll(self):
Expand All @@ -128,7 +135,7 @@ def device_info(self):
self._sensor.uniqueid.count(':') != 7):
return None
serial = self._sensor.uniqueid.split('-', 1)[0]
bridgeid = self.hass.data[DATA_DECONZ].api.config.bridgeid
Comment thread
Kane610 marked this conversation as resolved.
bridgeid = self.gateway.api.config.bridgeid
return {
'connections': {(CONNECTION_ZIGBEE, serial)},
'identifiers': {(DECONZ_DOMAIN, serial)},
Expand Down
29 changes: 19 additions & 10 deletions homeassistant/components/cover/deconz.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
https://home-assistant.io/components/cover.deconz/
"""
from homeassistant.components.deconz.const import (
COVER_TYPES, DAMPERS, DOMAIN as DATA_DECONZ, DECONZ_DOMAIN, WINDOW_COVERS)
COVER_TYPES, DAMPERS, DECONZ_REACHABLE, DOMAIN as DECONZ_DOMAIN,
WINDOW_COVERS)
from homeassistant.components.cover import (
ATTR_POSITION, CoverDevice, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP,
SUPPORT_SET_POSITION)
Expand All @@ -29,30 +30,35 @@ async def async_setup_entry(hass, config_entry, async_add_entities):

Covers are based on same device class as lights in deCONZ.
"""
gateway = hass.data[DECONZ_DOMAIN]

@callback
def async_add_cover(lights):
"""Add cover from deCONZ."""
entities = []
for light in lights:
if light.type in COVER_TYPES:
if light.modelid in ZIGBEE_SPEC:
entities.append(DeconzCoverZigbeeSpec(light))
entities.append(DeconzCoverZigbeeSpec(light, gateway))
else:
entities.append(DeconzCover(light))
entities.append(DeconzCover(light, gateway))
async_add_entities(entities, True)

hass.data[DATA_DECONZ].listeners.append(
gateway.listeners.append(
Comment thread
Kane610 marked this conversation as resolved.
async_dispatcher_connect(hass, 'deconz_new_light', async_add_cover))

async_add_cover(hass.data[DATA_DECONZ].api.lights.values())
async_add_cover(gateway.api.lights.values())


class DeconzCover(CoverDevice):
"""Representation of a deCONZ cover."""

def __init__(self, cover):
def __init__(self, cover, gateway):
"""Set up cover and add update callback to get data from websocket."""
self._cover = cover
self.gateway = gateway
self.unsub_dispatcher = None

self._features = SUPPORT_OPEN
self._features |= SUPPORT_CLOSE
self._features |= SUPPORT_STOP
Expand All @@ -61,11 +67,14 @@ def __init__(self, cover):
async def async_added_to_hass(self):
"""Subscribe to covers events."""
self._cover.register_async_callback(self.async_update_callback)
self.hass.data[DATA_DECONZ].deconz_ids[self.entity_id] = \
self._cover.deconz_id
self.gateway.deconz_ids[self.entity_id] = self._cover.deconz_id
self.unsub_dispatcher = async_dispatcher_connect(
self.hass, DECONZ_REACHABLE, self.async_update_callback)

async def async_will_remove_from_hass(self) -> None:
"""Disconnect cover object when removed."""
if self.unsub_dispatcher is not None:
self.unsub_dispatcher()
self._cover.remove_callback(self.async_update_callback)
Comment thread
Kane610 marked this conversation as resolved.
self._cover = None

Expand Down Expand Up @@ -112,7 +121,7 @@ def supported_features(self):
@property
def available(self):
"""Return True if light is available."""
return self._cover.reachable
return self.gateway.available and self._cover.reachable

@property
def should_poll(self):
Expand Down Expand Up @@ -150,7 +159,7 @@ def device_info(self):
self._cover.uniqueid.count(':') != 7):
return None
serial = self._cover.uniqueid.split('-', 1)[0]
bridgeid = self.hass.data[DATA_DECONZ].api.config.bridgeid
bridgeid = self.gateway.api.config.bridgeid
return {
'connections': {(CONNECTION_ZIGBEE, serial)},
'identifiers': {(DECONZ_DOMAIN, serial)},
Expand Down
5 changes: 2 additions & 3 deletions homeassistant/components/deconz/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@

DOMAIN = 'deconz'
CONFIG_FILE = 'deconz.conf'
DATA_DECONZ_EVENT = 'deconz_events'
DATA_DECONZ_ID = 'deconz_entities'
DATA_DECONZ_UNSUB = 'deconz_dispatchers'
DECONZ_DOMAIN = 'deconz'

CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor'
Expand All @@ -16,6 +13,8 @@
SUPPORTED_PLATFORMS = ['binary_sensor', 'cover',
'light', 'scene', 'sensor', 'switch']

DECONZ_REACHABLE = 'deconz_reachable'
Comment thread
Kane610 marked this conversation as resolved.

ATTR_DARK = 'dark'
ATTR_ON = 'on'

Expand Down
19 changes: 15 additions & 4 deletions homeassistant/components/deconz/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from homeassistant.util import slugify

from .const import (
_LOGGER, CONF_ALLOW_CLIP_SENSOR, SUPPORTED_PLATFORMS)
_LOGGER, DECONZ_REACHABLE, CONF_ALLOW_CLIP_SENSOR, SUPPORTED_PLATFORMS)


class DeconzGateway:
Expand All @@ -18,6 +18,7 @@ def __init__(self, hass, config_entry):
"""Initialize the system."""
self.hass = hass
self.config_entry = config_entry
self.available = True
self.api = None
self._cancel_retry_setup = None

Expand All @@ -30,7 +31,8 @@ async def async_setup(self, tries=0):
hass = self.hass

self.api = await get_gateway(
hass, self.config_entry.data, self.async_add_device_callback
hass, self.config_entry.data, self.async_add_device_callback,
self.async_connection_status_callback
)

if self.api is False:
Expand Down Expand Up @@ -65,6 +67,13 @@ async def retry_setup(_now):

return True

@callback
def async_connection_status_callback(self, available):
"""Handle signals of gateway connection status."""
self.available = available
async_dispatcher_send(
self.hass, DECONZ_REACHABLE, {'state': True, 'attr': 'reachable'})

@callback
def async_add_device_callback(self, device_type, device):
"""Handle event of new device creation in deCONZ."""
Expand Down Expand Up @@ -122,13 +131,15 @@ async def async_reset(self):
return True


async def get_gateway(hass, config, async_add_device_callback):
async def get_gateway(hass, config, async_add_device_callback,
async_connection_status_callback):
"""Create a gateway object and verify configuration."""
from pydeconz import DeconzSession

session = aiohttp_client.async_get_clientsession(hass)
deconz = DeconzSession(hass.loop, session, **config,
async_add_device=async_add_device_callback)
async_add_device=async_add_device_callback,
connection_status=async_connection_status_callback)
result = await deconz.async_load_parameters()

if result:
Expand Down
31 changes: 19 additions & 12 deletions homeassistant/components/light/deconz.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
https://home-assistant.io/components/light.deconz/
"""
from homeassistant.components.deconz.const import (
CONF_ALLOW_DECONZ_GROUPS, DOMAIN as DATA_DECONZ, DECONZ_DOMAIN,
CONF_ALLOW_DECONZ_GROUPS, DECONZ_REACHABLE, DOMAIN as DECONZ_DOMAIN,
COVER_TYPES, SWITCH_TYPES)
from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_HS_COLOR,
Expand All @@ -28,16 +28,18 @@ async def async_setup_platform(hass, config, async_add_entities,

async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ lights and groups from a config entry."""
gateway = hass.data[DECONZ_DOMAIN]

@callback
def async_add_light(lights):
"""Add light from deCONZ."""
entities = []
for light in lights:
if light.type not in COVER_TYPES + SWITCH_TYPES:
entities.append(DeconzLight(light))
entities.append(DeconzLight(light, gateway))
async_add_entities(entities, True)

hass.data[DATA_DECONZ].listeners.append(
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_light', async_add_light))

@callback
Expand All @@ -47,22 +49,24 @@ def async_add_group(groups):
allow_group = config_entry.data.get(CONF_ALLOW_DECONZ_GROUPS, True)
for group in groups:
if group.lights and allow_group:
entities.append(DeconzLight(group))
entities.append(DeconzLight(group, gateway))
async_add_entities(entities, True)

hass.data[DATA_DECONZ].listeners.append(
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_group', async_add_group))

async_add_light(hass.data[DATA_DECONZ].api.lights.values())
async_add_group(hass.data[DATA_DECONZ].api.groups.values())
async_add_light(gateway.api.lights.values())
async_add_group(gateway.api.groups.values())


class DeconzLight(Light):
"""Representation of a deCONZ light."""

def __init__(self, light):
def __init__(self, light, gateway):
"""Set up light and add update callback to get data from websocket."""
self._light = light
self.gateway = gateway
self.unsub_dispatcher = None

self._features = SUPPORT_BRIGHTNESS
self._features |= SUPPORT_FLASH
Expand All @@ -80,11 +84,14 @@ def __init__(self, light):
async def async_added_to_hass(self):
"""Subscribe to lights events."""
self._light.register_async_callback(self.async_update_callback)
self.hass.data[DATA_DECONZ].deconz_ids[self.entity_id] = \
self._light.deconz_id
self.gateway.deconz_ids[self.entity_id] = self._light.deconz_id
self.unsub_dispatcher = async_dispatcher_connect(
self.hass, DECONZ_REACHABLE, self.async_update_callback)

async def async_will_remove_from_hass(self) -> None:
"""Disconnect light object when removed."""
if self.unsub_dispatcher is not None:
self.unsub_dispatcher()
self._light.remove_callback(self.async_update_callback)
self._light = None

Expand Down Expand Up @@ -141,7 +148,7 @@ def supported_features(self):
@property
def available(self):
"""Return True if light is available."""
return self._light.reachable
return self.gateway.available and self._light.reachable

@property
def should_poll(self):
Expand Down Expand Up @@ -214,7 +221,7 @@ def device_info(self):
self._light.uniqueid.count(':') != 7):
return None
serial = self._light.uniqueid.split('-', 1)[0]
bridgeid = self.hass.data[DATA_DECONZ].api.config.bridgeid
bridgeid = self.gateway.api.config.bridgeid
return {
'connections': {(CONNECTION_ZIGBEE, serial)},
'identifiers': {(DECONZ_DOMAIN, serial)},
Expand Down
16 changes: 9 additions & 7 deletions homeassistant/components/scene/deconz.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/scene.deconz/
"""
from homeassistant.components.deconz import DOMAIN as DATA_DECONZ
from homeassistant.components.deconz import DOMAIN as DECONZ_DOMAIN
from homeassistant.components.scene import Scene
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
Expand All @@ -20,30 +20,32 @@ async def async_setup_platform(hass, config, async_add_entities,

async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up scenes for deCONZ component."""
gateway = hass.data[DECONZ_DOMAIN]

@callback
def async_add_scene(scenes):
"""Add scene from deCONZ."""
entities = []
for scene in scenes:
entities.append(DeconzScene(scene))
entities.append(DeconzScene(scene, gateway))
async_add_entities(entities)
hass.data[DATA_DECONZ].listeners.append(
gateway.listeners.append(
async_dispatcher_connect(hass, 'deconz_new_scene', async_add_scene))

async_add_scene(hass.data[DATA_DECONZ].api.scenes.values())
async_add_scene(gateway.api.scenes.values())


class DeconzScene(Scene):
"""Representation of a deCONZ scene."""

def __init__(self, scene):
def __init__(self, scene, gateway):
"""Set up a scene."""
self._scene = scene
self.gateway = gateway

async def async_added_to_hass(self):
"""Subscribe to sensors events."""
self.hass.data[DATA_DECONZ].deconz_ids[self.entity_id] = \
self._scene.deconz_id
self.gateway.deconz_ids[self.entity_id] = self._scene.deconz_id

async def async_will_remove_from_hass(self) -> None:
"""Disconnect scene object when removed."""
Expand Down
Loading