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
5 changes: 5 additions & 0 deletions homeassistant/components/zha/core/channels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ async def async_configure(self):

async def async_initialize(self, from_cache):
"""Initialize channel."""
_LOGGER.debug(
'initializing channel: %s from_cache: %s',
self._channel_name,
from_cache
)
self._status = ChannelStatus.INITIALIZED

@callback
Expand Down
20 changes: 19 additions & 1 deletion homeassistant/components/zha/core/channels/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import logging
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_call_later
from . import ZigbeeChannel, parse_and_log_command, MAINS_POWERED
from ..helpers import get_attr_id_by_name
from ..const import (
Expand Down Expand Up @@ -40,11 +41,28 @@ def cluster_command(self, tsn, command_id, args):

if cmd in ('off', 'off_with_effect'):
self.attribute_updated(self.ON_OFF, False)
elif cmd in ('on', 'on_with_recall_global_scene', 'on_with_timed_off'):
elif cmd in ('on', 'on_with_recall_global_scene'):
self.attribute_updated(self.ON_OFF, True)
elif cmd == 'on_with_timed_off':
should_accept = args[0]
on_time = args[1]
# 0 is always accept 1 is only accept when already on
if should_accept == 0 or (should_accept == 1 and self._state):
self.attribute_updated(self.ON_OFF, True)
if on_time > 0:
async_call_later(
self.device.hass,
(on_time / 10), # value is in 10ths of a second
self.set_to_off
)
elif cmd == 'toggle':
self.attribute_updated(self.ON_OFF, not bool(self._state))

@callback
def set_to_off(self, *_):
"""Set the state to off."""
self.attribute_updated(self.ON_OFF, False)

@callback
def attribute_updated(self, attrid, value):
"""Handle attribute updates on this cluster."""
Expand Down
54 changes: 48 additions & 6 deletions homeassistant/components/zha/core/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,18 +200,59 @@ def add_cluster_channel(self, cluster_channel):
self.cluster_channels[cluster_channel.name] = cluster_channel
self._all_channels.append(cluster_channel)

def get_channels_to_configure(self):
"""Get a deduped list of channels for configuration.

This goes through all channels and gets a unique list of channels to
configure. It first assembles a unique list of channels that are part
of entities while stashing relay channels off to the side. It then
takse the stashed relay channels and adds them to the list of channels
that will be returned if there isn't a channel in the list for that
cluster already. This is done to ensure each cluster is only configured
once.
"""
channel_keys = []
channels = []
relay_channels = self._relay_channels.values()

def get_key(channel):
channel_key = "ZDO"
if hasattr(channel.cluster, 'cluster_id'):
channel_key = "{}_{}".format(
channel.cluster.endpoint.endpoint_id,
channel.cluster.cluster_id
)
return channel_key

# first we get all unique non event channels
for channel in self.all_channels:
c_key = get_key(channel)
if c_key not in channel_keys and channel not in relay_channels:
channel_keys.append(c_key)
channels.append(channel)

# now we get event channels that still need their cluster configured
for channel in relay_channels:
channel_key = get_key(channel)
if channel_key not in channel_keys:
channel_keys.append(channel_key)
channels.append(channel)
return channels

async def async_configure(self):
"""Configure the device."""
_LOGGER.debug('%s: started configuration', self.name)
await self._execute_channel_tasks('async_configure')
await self._execute_channel_tasks(
self.get_channels_to_configure(), 'async_configure')
_LOGGER.debug('%s: completed configuration', self.name)
entry = self.gateway.zha_storage.async_create_or_update(self)
_LOGGER.debug('%s: stored in registry: %s', self.name, entry)

async def async_initialize(self, from_cache=False):
"""Initialize channels."""
_LOGGER.debug('%s: started initialization', self.name)
await self._execute_channel_tasks('async_initialize', from_cache)
await self._execute_channel_tasks(
self.all_channels, 'async_initialize', from_cache)
_LOGGER.debug(
'%s: power source: %s',
self.name,
Expand All @@ -220,16 +261,17 @@ async def async_initialize(self, from_cache=False):
self.status = DeviceStatus.INITIALIZED
_LOGGER.debug('%s: completed initialization', self.name)

async def _execute_channel_tasks(self, task_name, *args):
async def _execute_channel_tasks(self, channels, task_name, *args):
"""Gather and execute a set of CHANNEL tasks."""
channel_tasks = []
semaphore = asyncio.Semaphore(3)
zdo_task = None
for channel in self.all_channels:
for channel in channels:
if channel.name == ZDO_CHANNEL:
# pylint: disable=E1111
zdo_task = self._async_create_task(
semaphore, channel, task_name, *args)
if zdo_task is None: # We only want to do this once
zdo_task = self._async_create_task(
semaphore, channel, task_name, *args)
else:
channel_tasks.append(
self._async_create_task(
Expand Down
45 changes: 27 additions & 18 deletions homeassistant/components/zha/core/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,17 +259,25 @@ async def async_device_initialized(self, device, is_new_join):
"""Handle device joined and basic information discovered (async)."""
zha_device = self._async_get_or_create_device(device, is_new_join)

discovery_infos = []
for endpoint_id, endpoint in device.endpoints.items():
async_process_endpoint(
self._hass, self._config, endpoint_id, endpoint,
discovery_infos, device, zha_device, is_new_join
is_rejoin = False
if zha_device.status is not DeviceStatus.INITIALIZED:
discovery_infos = []
for endpoint_id, endpoint in device.endpoints.items():
async_process_endpoint(
self._hass, self._config, endpoint_id, endpoint,
discovery_infos, device, zha_device, is_new_join
)
if endpoint_id != 0:
for cluster in endpoint.in_clusters.values():
cluster.bind_only = False
for cluster in endpoint.out_clusters.values():
cluster.bind_only = True
else:
is_rejoin = is_new_join is True
_LOGGER.debug(
'skipping discovery for previously discovered device: %s',
"{} - is rejoin: {}".format(zha_device.ieee, is_rejoin)
)
if endpoint_id != 0:
for cluster in endpoint.in_clusters.values():
cluster.bind_only = False
for cluster in endpoint.out_clusters.values():
cluster.bind_only = True

if is_new_join:
# configure the device
Expand All @@ -290,15 +298,16 @@ async def async_device_initialized(self, device, is_new_join):
else:
await zha_device.async_initialize(from_cache=True)

for discovery_info in discovery_infos:
async_dispatch_discovery_info(
self._hass,
is_new_join,
discovery_info
)
if not is_rejoin:
for discovery_info in discovery_infos:
async_dispatch_discovery_info(
self._hass,
is_new_join,
discovery_info
)

device_entity = async_create_device_entity(zha_device)
await self._component.async_add_entities([device_entity])
device_entity = async_create_device_entity(zha_device)
await self._component.async_add_entities([device_entity])

if is_new_join:
device_info = async_get_device_info(self._hass, zha_device)
Expand Down