Skip to content

Commit

Permalink
ZHA entity ZCL reporting configuration (home-assistant#19177)
Browse files Browse the repository at this point in the history
* Implement async_configure() method for ZHA entities.

Allow attribute reporting configuration to be stored as dict of zha
entity.

* Update ZHA platform to use new attribute reporting configuration.

* Use const declaration instead of magic numbers.

* Add support for manufacturer_id in ZCL attribute reporting configuration.

* Refactor async_configure() method.

Rename attribute reporting dict to zcl_reporting_config.
  • Loading branch information
Adminiuga authored and dshokouhi committed Dec 25, 2018
1 parent 421e7b1 commit 1fa9bbf
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 42 deletions.
31 changes: 15 additions & 16 deletions homeassistant/components/binary_sensor/zha.py
Expand Up @@ -9,7 +9,7 @@
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
from homeassistant.components.zha import helpers
from homeassistant.components.zha.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW)
DATA_ZHA, DATA_ZHA_DISPATCHERS, REPORT_CONFIG_IMMEDIATE, ZHA_DISCOVERY_NEW)
from homeassistant.components.zha.entities import ZhaEntity
from homeassistant.helpers.dispatcher import async_dispatcher_connect

Expand Down Expand Up @@ -89,21 +89,7 @@ async def _async_setup_remote(discovery_info):
remote = Remote(**discovery_info)

if discovery_info['new_join']:
from zigpy.zcl.clusters.general import OnOff, LevelControl
out_clusters = discovery_info['out_clusters']
if OnOff.cluster_id in out_clusters:
cluster = out_clusters[OnOff.cluster_id]
await helpers.configure_reporting(
remote.entity_id, cluster, 0, min_report=0, max_report=600,
reportable_change=1
)
if LevelControl.cluster_id in out_clusters:
cluster = out_clusters[LevelControl.cluster_id]
await helpers.configure_reporting(
remote.entity_id, cluster, 0, min_report=1, max_report=600,
reportable_change=1
)

await remote.async_configure()
return remote


Expand Down Expand Up @@ -238,6 +224,14 @@ def __init__(self, **kwargs):
general.OnOff.cluster_id: self.OnOffListener(self),
general.LevelControl.cluster_id: self.LevelListener(self),
}
out_clusters = kwargs.get('out_clusters')
self._zcl_reporting = {}
for cluster_id in [general.OnOff.cluster_id,
general.LevelControl.cluster_id]:
if cluster_id not in out_clusters:
continue
cluster = out_clusters[cluster_id]
self._zcl_reporting[cluster] = {0: REPORT_CONFIG_IMMEDIATE}

@property
def should_poll(self) -> bool:
Expand All @@ -257,6 +251,11 @@ def device_state_attributes(self):
})
return self._device_state_attributes

@property
def zcl_reporting_config(self):
"""Return ZCL attribute reporting configuration."""
return self._zcl_reporting

def move_level(self, change):
"""Increment the level, setting state if appropriate."""
if not self._state and change > 0:
Expand Down
31 changes: 28 additions & 3 deletions homeassistant/components/fan/zha.py
Expand Up @@ -11,7 +11,7 @@
FanEntity)
from homeassistant.components.zha import helpers
from homeassistant.components.zha.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW)
DATA_ZHA, DATA_ZHA_DISPATCHERS, REPORT_CONFIG_OP, ZHA_DISCOVERY_NEW)
from homeassistant.components.zha.entities import ZhaEntity
from homeassistant.helpers.dispatcher import async_dispatcher_connect

Expand Down Expand Up @@ -70,7 +70,10 @@ async def _async_setup_entities(hass, config_entry, async_add_entities,
"""Set up the ZHA fans."""
entities = []
for discovery_info in discovery_infos:
entities.append(ZhaFan(**discovery_info))
fan = ZhaFan(**discovery_info)
if discovery_info['new_join']:
await fan.async_configure()
entities.append(fan)

async_add_entities(entities, update_before_add=True)

Expand All @@ -79,6 +82,19 @@ class ZhaFan(ZhaEntity, FanEntity):
"""Representation of a ZHA fan."""

_domain = DOMAIN
value_attribute = 0 # fan_mode

@property
def zcl_reporting_config(self) -> dict:
"""Return a dict of attribute reporting configuration."""
return {
self.cluster: {self.value_attribute: REPORT_CONFIG_OP}
}

@property
def cluster(self):
"""Fan ZCL Cluster."""
return self._endpoint.fan

@property
def supported_features(self) -> int:
Expand Down Expand Up @@ -129,7 +145,7 @@ async def async_set_speed(self, speed: str) -> None:

async def async_update(self):
"""Retrieve latest state."""
result = await helpers.safe_read(self._endpoint.fan, ['fan_mode'],
result = await helpers.safe_read(self.cluster, ['fan_mode'],
allow_cache=False,
only_cache=(not self._initialized))
new_value = result.get('fan_mode', None)
Expand All @@ -142,3 +158,12 @@ def should_poll(self) -> bool:
False if entity pushes its state to HA.
"""
return False

def attribute_updated(self, attribute, value):
"""Handle attribute update from device."""
attr_name = self.cluster.attributes.get(attribute, [attribute])[0]
_LOGGER.debug("%s: Attribute report '%s'[%s] = %s",
self.entity_id, self.cluster.name, attr_name, value)
if attribute == self.value_attribute:
self._state = VALUE_TO_SPEED.get(value, self._state)
self.async_schedule_update_ha_state()
21 changes: 19 additions & 2 deletions homeassistant/components/light/zha.py
Expand Up @@ -9,7 +9,8 @@
from homeassistant.components import light
from homeassistant.components.zha import helpers
from homeassistant.components.zha.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW)
DATA_ZHA, DATA_ZHA_DISPATCHERS, REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT,
REPORT_CONFIG_IMMEDIATE, ZHA_DISCOVERY_NEW)
from homeassistant.components.zha.entities import ZhaEntity
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import homeassistant.util.color as color_util
Expand Down Expand Up @@ -73,7 +74,10 @@ async def _async_setup_entities(hass, config_entry, async_add_entities,
UNSUPPORTED_ATTRIBUTE):
discovery_info['color_capabilities'] |= \
CAPABILITIES_COLOR_TEMP
entities.append(Light(**discovery_info))
zha_light = Light(**discovery_info)
if discovery_info['new_join']:
await zha_light.async_configure()
entities.append(zha_light)

async_add_entities(entities, update_before_add=True)

Expand Down Expand Up @@ -105,6 +109,19 @@ def __init__(self, **kwargs):
self._supported_features |= light.SUPPORT_COLOR
self._hs_color = (0, 0)

@property
def zcl_reporting_config(self) -> dict:
"""Return attribute reporting configuration."""
return {
'on_off': {'on_off': REPORT_CONFIG_IMMEDIATE},
'level': {'current_level': REPORT_CONFIG_ASAP},
'light_color': {
'current_x': REPORT_CONFIG_DEFAULT,
'current_y': REPORT_CONFIG_DEFAULT,
'color_temperature': REPORT_CONFIG_DEFAULT,
}
}

@property
def is_on(self) -> bool:
"""Return true if entity is on."""
Expand Down
36 changes: 27 additions & 9 deletions homeassistant/components/sensor/zha.py
Expand Up @@ -9,7 +9,8 @@
from homeassistant.components.sensor import DOMAIN
from homeassistant.components.zha import helpers
from homeassistant.components.zha.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW)
DATA_ZHA, DATA_ZHA_DISPATCHERS, REPORT_CONFIG_MAX_INT,
REPORT_CONFIG_MIN_INT, REPORT_CONFIG_RPT_CHANGE, ZHA_DISCOVERY_NEW)
from homeassistant.components.zha.entities import ZhaEntity
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.dispatcher import async_dispatcher_connect
Expand Down Expand Up @@ -81,11 +82,7 @@ async def make_sensor(discovery_info):
sensor = Sensor(**discovery_info)

if discovery_info['new_join']:
cluster = list(in_clusters.values())[0]
await helpers.configure_reporting(
sensor.entity_id, cluster, sensor.value_attribute,
reportable_change=sensor.min_reportable_change
)
await sensor.async_configure()

return sensor

Expand All @@ -95,7 +92,28 @@ class Sensor(ZhaEntity):

_domain = DOMAIN
value_attribute = 0
min_reportable_change = 1
min_report_interval = REPORT_CONFIG_MIN_INT
max_report_interval = REPORT_CONFIG_MAX_INT
min_reportable_change = REPORT_CONFIG_RPT_CHANGE
report_config = (min_report_interval, max_report_interval,
min_reportable_change)

def __init__(self, **kwargs):
"""Init ZHA Sensor instance."""
super().__init__(**kwargs)
self._cluster = list(kwargs['in_clusters'].values())[0]

@property
def zcl_reporting_config(self) -> dict:
"""Return a dict of attribute reporting configuration."""
return {
self.cluster: {self.value_attribute: self.report_config}
}

@property
def cluster(self):
"""Return Sensor's cluster."""
return self._cluster

@property
def should_poll(self) -> bool:
Expand All @@ -119,7 +137,7 @@ def attribute_updated(self, attribute, value):
async def async_update(self):
"""Retrieve latest state."""
result = await helpers.safe_read(
list(self._in_clusters.values())[0],
self.cluster,
[self.value_attribute],
allow_cache=False,
only_cache=(not self._initialized)
Expand Down Expand Up @@ -251,6 +269,6 @@ async def async_update(self):
_LOGGER.debug("%s async_update", self.entity_id)

result = await helpers.safe_read(
self._endpoint.electrical_measurement, ['active_power'],
self.cluster, ['active_power'],
allow_cache=False, only_cache=(not self._initialized))
self._state = result.get('active_power', self._state)
22 changes: 14 additions & 8 deletions homeassistant/components/switch/zha.py
Expand Up @@ -9,7 +9,7 @@
from homeassistant.components.switch import DOMAIN, SwitchDevice
from homeassistant.components.zha import helpers
from homeassistant.components.zha.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW)
DATA_ZHA, DATA_ZHA_DISPATCHERS, REPORT_CONFIG_IMMEDIATE, ZHA_DISCOVERY_NEW)
from homeassistant.components.zha.entities import ZhaEntity
from homeassistant.helpers.dispatcher import async_dispatcher_connect

Expand Down Expand Up @@ -44,17 +44,11 @@ async def async_discover(discovery_info):
async def _async_setup_entities(hass, config_entry, async_add_entities,
discovery_infos):
"""Set up the ZHA switches."""
from zigpy.zcl.clusters.general import OnOff
entities = []
for discovery_info in discovery_infos:
switch = Switch(**discovery_info)
if discovery_info['new_join']:
in_clusters = discovery_info['in_clusters']
cluster = in_clusters[OnOff.cluster_id]
await helpers.configure_reporting(
switch.entity_id, cluster, switch.value_attribute,
min_report=0, max_report=600, reportable_change=1
)
await switch.async_configure()
entities.append(switch)

async_add_entities(entities, update_before_add=True)
Expand All @@ -76,6 +70,18 @@ def attribute_updated(self, attribute, value):
self._state = value
self.async_schedule_update_ha_state()

@property
def zcl_reporting_config(self) -> dict:
"""Retrun a dict of attribute reporting configuration."""
return {
self.cluster: {'on_off': REPORT_CONFIG_IMMEDIATE}
}

@property
def cluster(self):
"""Entity's cluster."""
return self._endpoint.on_off

@property
def should_poll(self) -> bool:
"""Let zha handle polling."""
Expand Down
21 changes: 21 additions & 0 deletions homeassistant/components/zha/const.py
Expand Up @@ -57,6 +57,27 @@ def list(cls):
COMPONENT_CLUSTERS = {}
EVENTABLE_CLUSTERS = []

REPORT_CONFIG_MAX_INT = 900
REPORT_CONFIG_MAX_INT_BATTERY_SAVE = 10800
REPORT_CONFIG_MIN_INT = 30
REPORT_CONFIG_MIN_INT_ASAP = 1
REPORT_CONFIG_MIN_INT_IMMEDIATE = 0
REPORT_CONFIG_MIN_INT_OP = 5
REPORT_CONFIG_MIN_INT_BATTERY_SAVE = 3600
REPORT_CONFIG_RPT_CHANGE = 1
REPORT_CONFIG_DEFAULT = (REPORT_CONFIG_MIN_INT, REPORT_CONFIG_MAX_INT,
REPORT_CONFIG_RPT_CHANGE)
REPORT_CONFIG_ASAP = (REPORT_CONFIG_MIN_INT_ASAP, REPORT_CONFIG_MAX_INT,
REPORT_CONFIG_RPT_CHANGE)
REPORT_CONFIG_BATTERY_SAVE = (REPORT_CONFIG_MIN_INT_BATTERY_SAVE,
REPORT_CONFIG_MAX_INT,
REPORT_CONFIG_RPT_CHANGE)
REPORT_CONFIG_IMMEDIATE = (REPORT_CONFIG_MIN_INT_IMMEDIATE,
REPORT_CONFIG_MAX_INT,
REPORT_CONFIG_RPT_CHANGE)
REPORT_CONFIG_OP = (REPORT_CONFIG_MIN_INT_OP, REPORT_CONFIG_MAX_INT,
REPORT_CONFIG_RPT_CHANGE)


def populate_data():
"""Populate data using constants from bellows.
Expand Down

0 comments on commit 1fa9bbf

Please sign in to comment.