Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 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: 38 additions & 0 deletions homeassistant/components/zha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ async def remove(service):
hass.services.async_register(DOMAIN, SERVICE_REMOVE, remove,
schema=SERVICE_SCHEMAS[SERVICE_REMOVE])

import homeassistant.components.zha.const as zha_const
hass.data[zha_const.DATA_ZHA_EVENT] = []

return True


Expand Down Expand Up @@ -177,6 +180,7 @@ def device_removed(self, device):
async def async_device_initialized(self, device, join):
"""Handle device joined and basic information discovered (async)."""
import zigpy.profiles
import homeassistant.components.zha.event as event
import homeassistant.components.zha.const as zha_const

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a design issue w/ the modules that causes circular imports. I have it corrected in another branch and can fix this in a subsequent PR if that is ok.

zha_const.populate_data()

Expand All @@ -197,6 +201,40 @@ async def async_device_initialized(self, device, join):
node_config = self._config[DOMAIN][CONF_DEVICE_CONFIG].get(
device_key, {})

_LOGGER.debug(
"Manufacturer: %s model: %s",
endpoint.manufacturer,
endpoint.model
)
supported_remote_models = zha_const.REMOTE_DEVICE_TYPES.get(
endpoint.profile_id, {}).get(endpoint.manufacturer, [])
_LOGGER.debug(
"Supported remote models: %s",
supported_remote_models
)
if endpoint.profile_id in zigpy.profiles.PROFILES and \
endpoint.model in supported_remote_models:
profile = zigpy.profiles.PROFILES[endpoint.profile_id]
profile_clusters = profile.CLUSTERS[endpoint.device_type]
in_clusters = [endpoint.in_clusters[c]
for c in profile_clusters[0]
if c in endpoint.in_clusters]
out_clusters = [endpoint.out_clusters[c]
for c in profile_clusters[1]
if c in endpoint.out_clusters]
discovery_info = {
'application_listener': self,
'endpoint': endpoint,
'in_clusters': in_clusters,
'out_clusters': out_clusters,
'manufacturer': endpoint.manufacturer,
'model': endpoint.model,
'new_join': join,
'unique_id': device_key,
}
self._hass.data[DISCOVERY_KEY][device_key] = discovery_info
await event._async_setup_event(self._hass, discovery_info)

if endpoint.profile_id in zigpy.profiles.PROFILES:
profile = zigpy.profiles.PROFILES[endpoint.profile_id]
if zha_const.DEVICE_CLASS.get(endpoint.profile_id,
Expand Down
14 changes: 13 additions & 1 deletion homeassistant/components/zha/const.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"""All constants related to the ZHA component."""

DATA_ZHA_EVENT = 'zha_events'

DEVICE_CLASS = {}
SINGLE_INPUT_CLUSTER_DEVICE_CLASS = {}
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {}
CUSTOM_CLUSTER_MAPPINGS = {}
COMPONENT_CLUSTERS = {}
REMOTE_DEVICE_TYPES = {}


def populate_data():
Expand All @@ -30,6 +33,11 @@ def populate_data():
zha.DeviceType.DIMMER_SWITCH: 'binary_sensor',
zha.DeviceType.COLOR_DIMMER_SWITCH: 'binary_sensor',
}
REMOTE_DEVICE_TYPES[zha.PROFILE_ID] = {
'CentraLite': ['3130'],
'LUMI': ['lumi.sensor_switch.aq2'],
'OSRAM': ['LIGHTIFY Dimming Switch'],
}
DEVICE_CLASS[zll.PROFILE_ID] = {
zll.DeviceType.ON_OFF_LIGHT: 'light',
zll.DeviceType.ON_OFF_PLUGIN_UNIT: 'switch',
Expand All @@ -44,7 +52,8 @@ def populate_data():
zll.DeviceType.SCENE_CONTROLLER: 'binary_sensor',
zll.DeviceType.ON_OFF_SENSOR: 'binary_sensor',
}

REMOTE_DEVICE_TYPES[zll.PROFILE_ID] = [
]
SINGLE_INPUT_CLUSTER_DEVICE_CLASS.update({
zcl.clusters.general.OnOff: 'switch',
zcl.clusters.general.LevelControl: 'light',
Expand All @@ -67,6 +76,9 @@ def populate_data():
('sensor', sensor_zha.RelativeHumiditySensor)
})

PROFILES[zha.PROFILE_ID].CLUSTERS[0x5F01] = ([0x0000, 0x0006, 0xFFFF],
[0x0000, 0x0004, 0xFFFF])

# A map of hass components to all Zigbee clusters it could use
for profile_id, classes in DEVICE_CLASS.items():
profile = PROFILES[profile_id]
Expand Down
99 changes: 99 additions & 0 deletions homeassistant/components/zha/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import logging
from homeassistant.util import slugify
from homeassistant.core import EventOrigin, callback


_LOGGER = logging.getLogger(__name__)


async def _async_setup_event(hass, discovery_info):
Comment thread
dmulcahey marked this conversation as resolved.
Outdated
from homeassistant.components.zha import const as zha_const
Comment thread
dmulcahey marked this conversation as resolved.
Outdated
from homeassistant.components.zha import configure_reporting
out_clusters = discovery_info['out_clusters']
in_clusters = discovery_info['in_clusters']
for in_cluster in in_clusters:
event = ZHAEvent(hass, in_cluster, discovery_info)
if discovery_info['new_join']:
await configure_reporting(event.event_id, in_cluster, 0,
False, 0, 600, 1)
hass.data[zha_const.DATA_ZHA_EVENT].append(
Comment thread
dmulcahey marked this conversation as resolved.
Outdated
event
)
for out_cluster in out_clusters:
event = ZHAEvent(hass, out_cluster, discovery_info)
if discovery_info['new_join']:
await configure_reporting(event.event_id, out_cluster, 0,
False, 0, 600, 1)
hass.data[zha_const.DATA_ZHA_EVENT].append(
event
)


class ZHAEvent(object):
"""When you want signals instead of entities.
Stateless sensors such as remotes are expected to generate an event
instead of a sensor entity in hass.
"""

def __init__(self, hass, cluster, discovery_info):
"""Register callback that will be used for signals."""
self._hass = hass
self._cluster = cluster
self._cluster.add_listener(self)
ieee = discovery_info['endpoint'].device.ieee
ieeetail = ''.join(['%02x' % (o, ) for o in ieee[-4:]])
if discovery_info['manufacturer'] and discovery_info['model'] is not \
None:
self.event_id = "{}.{}_{}_{}{}".format(
slugify(discovery_info['manufacturer']),
slugify(discovery_info['model']),
ieeetail,
discovery_info['endpoint'].endpoint_id,
discovery_info.get('entity_suffix', '')
)
else:
self.event_id = "{}.event_{}{}".format(
ieeetail,
discovery_info['endpoint'].endpoint_id,
discovery_info.get('entity_suffix', '')
)

@callback
def cluster_command(self, tsn, command_id, args):
"""Handle commands received to this cluster."""
self._hass.bus.async_fire(
'zha_' + self._cluster.server_commands.get(command_id)[0],
{'device': self.event_id, 'args': args},
EventOrigin.remote
)
_LOGGER.debug(
Comment thread
dmulcahey marked this conversation as resolved.
Outdated
"%s: fired %s event with arguments: %s", self.event_id,
self._cluster.server_commands.get(command_id)[0],
args
)

@callback
def attribute_updated(self, attrid, value):
self._hass.bus.async_fire(
'zha_attribute_updated',
{'device': self.event_id,
'attribute': self._cluster.attributes.get(attrid, ['Unknown'])[0],
'attribute_id': attrid,
'value': value},
EventOrigin.remote
)
_LOGGER.debug(
Comment thread
dmulcahey marked this conversation as resolved.
Outdated
"%s: updated attribute %s with value: %s and id: %s",
self.event_id,
self._cluster.attributes.get(attrid, ['Unknown'])[0],
value,
attrid
)

@callback
def zdo_command(self, *args, **kwargs):
Comment thread
MartinHjelmare marked this conversation as resolved.
_LOGGER.debug(
"%s: issued zdo command %s with args: %s", self.event_id,
args,
kwargs
)