Add ZHA occupancy sensor (#19365)

* occupancy sensor

* lint

* map occupancy cluster to binary_sensor

* update to use reporting configuration and async_configure

* refactor

* fix typo - review comment

* handle restore entity functionality
pull/19531/head
David F. Mulcahey 2018-12-22 14:53:15 -05:00 committed by Anders Melchiorsen
parent 2765440aa5
commit 2a2af80309
2 changed files with 76 additions and 3 deletions

View File

@ -28,6 +28,7 @@ CLASS_MAPPING = {
0x002b: 'gas',
0x002d: 'vibration',
}
DEVICE_CLASS_OCCUPANCY = 'occupancy'
async def async_setup_platform(hass, config, async_add_entities,
@ -59,9 +60,15 @@ async def _async_setup_entities(hass, config_entry, async_add_entities,
entities = []
for discovery_info in discovery_infos:
from zigpy.zcl.clusters.general import OnOff
from zigpy.zcl.clusters.measurement import OccupancySensing
from zigpy.zcl.clusters.security import IasZone
if IasZone.cluster_id in discovery_info['in_clusters']:
entities.append(await _async_setup_iaszone(discovery_info))
elif OccupancySensing.cluster_id in discovery_info['in_clusters']:
entities.append(await _async_setup_occupancy(
DEVICE_CLASS_OCCUPANCY,
discovery_info
))
elif OnOff.cluster_id in discovery_info['out_clusters']:
entities.append(await _async_setup_remote(discovery_info))
@ -84,7 +91,7 @@ async def _async_setup_iaszone(discovery_info):
# If we fail to read from the device, use a non-specific class
pass
return BinarySensor(device_class, **discovery_info)
return IasZoneSensor(device_class, **discovery_info)
async def _async_setup_remote(discovery_info):
@ -95,8 +102,15 @@ async def _async_setup_remote(discovery_info):
return remote
class BinarySensor(RestoreEntity, ZhaEntity, BinarySensorDevice):
"""The ZHA Binary Sensor."""
async def _async_setup_occupancy(device_class, discovery_info):
sensor = BinarySensor(device_class, **discovery_info)
if discovery_info['new_join']:
await sensor.async_configure()
return sensor
class IasZoneSensor(RestoreEntity, ZhaEntity, BinarySensorDevice):
"""The IasZoneSensor Binary Sensor."""
_domain = DOMAIN
@ -313,3 +327,61 @@ class Remote(RestoreEntity, ZhaEntity, BinarySensorDevice):
only_cache=(not self._initialized)
)
self._state = result.get('on_off', self._state)
class BinarySensor(RestoreEntity, ZhaEntity, BinarySensorDevice):
"""ZHA switch."""
_domain = DOMAIN
_device_class = None
value_attribute = 0
def __init__(self, device_class, **kwargs):
"""Initialize the ZHA binary sensor."""
super().__init__(**kwargs)
self._device_class = device_class
self._cluster = list(kwargs['in_clusters'].values())[0]
def attribute_updated(self, attribute, value):
"""Handle attribute update from device."""
_LOGGER.debug("Attribute updated: %s %s %s", self, attribute, value)
if attribute == self.value_attribute:
self._state = bool(value)
self.async_schedule_update_ha_state()
async def async_added_to_hass(self):
"""Run when about to be added to hass."""
await super().async_added_to_hass()
old_state = await self.async_get_last_state()
if self._state is not None or old_state is None:
return
_LOGGER.debug("%s restoring old state: %s", self.entity_id, old_state)
self._state = old_state.state == STATE_ON
@property
def should_poll(self) -> bool:
"""Let zha handle polling."""
return False
@property
def cluster(self):
"""Zigbee cluster for this entity."""
return self._cluster
@property
def zcl_reporting_config(self):
"""ZHA reporting configuration."""
return {self.cluster: {self.value_attribute: REPORT_CONFIG_IMMEDIATE}}
@property
def is_on(self) -> bool:
"""Return if the switch is on based on the statemachine."""
if self._state is None:
return False
return self._state
@property
def device_class(self) -> str:
"""Return device class from component DEVICE_CLASSES."""
return self._device_class

View File

@ -138,6 +138,7 @@ def populate_data():
zcl.clusters.homeautomation.ElectricalMeasurement: 'sensor',
zcl.clusters.general.PowerConfiguration: 'sensor',
zcl.clusters.security.IasZone: 'binary_sensor',
zcl.clusters.measurement.OccupancySensing: 'binary_sensor',
zcl.clusters.hvac.Fan: 'fan',
})
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS.update({