Refactor ZHA (#18629)
* refactor ZHA * lint * review request * Exclude more zha modules from coverage
This commit is contained in:
parent
cccc41c23e
commit
67aa76d295
12 changed files with 343 additions and 284 deletions
|
@ -400,6 +400,8 @@ omit =
|
||||||
|
|
||||||
homeassistant/components/zha/__init__.py
|
homeassistant/components/zha/__init__.py
|
||||||
homeassistant/components/zha/const.py
|
homeassistant/components/zha/const.py
|
||||||
|
homeassistant/components/zha/entities/*
|
||||||
|
homeassistant/components/zha/helpers.py
|
||||||
homeassistant/components/*/zha.py
|
homeassistant/components/*/zha.py
|
||||||
|
|
||||||
homeassistant/components/zigbee.py
|
homeassistant/components/zigbee.py
|
||||||
|
|
|
@ -7,7 +7,8 @@ at https://home-assistant.io/components/binary_sensor.zha/
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
|
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
|
||||||
from homeassistant.components import zha
|
from homeassistant.components.zha.entities import ZhaEntity
|
||||||
|
from homeassistant.components.zha import helpers
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ CLASS_MAPPING = {
|
||||||
async def async_setup_platform(hass, config, async_add_entities,
|
async def async_setup_platform(hass, config, async_add_entities,
|
||||||
discovery_info=None):
|
discovery_info=None):
|
||||||
"""Set up the Zigbee Home Automation binary sensors."""
|
"""Set up the Zigbee Home Automation binary sensors."""
|
||||||
discovery_info = zha.get_discovery_info(hass, discovery_info)
|
discovery_info = helpers.get_discovery_info(hass, discovery_info)
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -72,13 +73,13 @@ async def _async_setup_remote(hass, config, async_add_entities,
|
||||||
out_clusters = discovery_info['out_clusters']
|
out_clusters = discovery_info['out_clusters']
|
||||||
if OnOff.cluster_id in out_clusters:
|
if OnOff.cluster_id in out_clusters:
|
||||||
cluster = out_clusters[OnOff.cluster_id]
|
cluster = out_clusters[OnOff.cluster_id]
|
||||||
await zha.configure_reporting(
|
await helpers.configure_reporting(
|
||||||
remote.entity_id, cluster, 0, min_report=0, max_report=600,
|
remote.entity_id, cluster, 0, min_report=0, max_report=600,
|
||||||
reportable_change=1
|
reportable_change=1
|
||||||
)
|
)
|
||||||
if LevelControl.cluster_id in out_clusters:
|
if LevelControl.cluster_id in out_clusters:
|
||||||
cluster = out_clusters[LevelControl.cluster_id]
|
cluster = out_clusters[LevelControl.cluster_id]
|
||||||
await zha.configure_reporting(
|
await helpers.configure_reporting(
|
||||||
remote.entity_id, cluster, 0, min_report=1, max_report=600,
|
remote.entity_id, cluster, 0, min_report=1, max_report=600,
|
||||||
reportable_change=1
|
reportable_change=1
|
||||||
)
|
)
|
||||||
|
@ -86,7 +87,7 @@ async def _async_setup_remote(hass, config, async_add_entities,
|
||||||
async_add_entities([remote], update_before_add=True)
|
async_add_entities([remote], update_before_add=True)
|
||||||
|
|
||||||
|
|
||||||
class BinarySensor(zha.Entity, BinarySensorDevice):
|
class BinarySensor(ZhaEntity, BinarySensorDevice):
|
||||||
"""The ZHA Binary Sensor."""
|
"""The ZHA Binary Sensor."""
|
||||||
|
|
||||||
_domain = DOMAIN
|
_domain = DOMAIN
|
||||||
|
@ -130,16 +131,16 @@ class BinarySensor(zha.Entity, BinarySensorDevice):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
from zigpy.types.basic import uint16_t
|
from zigpy.types.basic import uint16_t
|
||||||
|
|
||||||
result = await zha.safe_read(self._endpoint.ias_zone,
|
result = await helpers.safe_read(self._endpoint.ias_zone,
|
||||||
['zone_status'],
|
['zone_status'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
only_cache=(not self._initialized))
|
only_cache=(not self._initialized))
|
||||||
state = result.get('zone_status', self._state)
|
state = result.get('zone_status', self._state)
|
||||||
if isinstance(state, (int, uint16_t)):
|
if isinstance(state, (int, uint16_t)):
|
||||||
self._state = result.get('zone_status', self._state) & 3
|
self._state = result.get('zone_status', self._state) & 3
|
||||||
|
|
||||||
|
|
||||||
class Remote(zha.Entity, BinarySensorDevice):
|
class Remote(ZhaEntity, BinarySensorDevice):
|
||||||
"""ZHA switch/remote controller/button."""
|
"""ZHA switch/remote controller/button."""
|
||||||
|
|
||||||
_domain = DOMAIN
|
_domain = DOMAIN
|
||||||
|
@ -252,7 +253,7 @@ class Remote(zha.Entity, BinarySensorDevice):
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
from zigpy.zcl.clusters.general import OnOff
|
from zigpy.zcl.clusters.general import OnOff
|
||||||
result = await zha.safe_read(
|
result = await helpers.safe_read(
|
||||||
self._endpoint.out_clusters[OnOff.cluster_id],
|
self._endpoint.out_clusters[OnOff.cluster_id],
|
||||||
['on_off'],
|
['on_off'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
|
|
|
@ -5,7 +5,8 @@ For more details on this platform, please refer to the documentation
|
||||||
at https://home-assistant.io/components/fan.zha/
|
at https://home-assistant.io/components/fan.zha/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from homeassistant.components import zha
|
from homeassistant.components.zha.entities import ZhaEntity
|
||||||
|
from homeassistant.components.zha import helpers
|
||||||
from homeassistant.components.fan import (
|
from homeassistant.components.fan import (
|
||||||
DOMAIN, FanEntity, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH,
|
DOMAIN, FanEntity, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH,
|
||||||
SUPPORT_SET_SPEED)
|
SUPPORT_SET_SPEED)
|
||||||
|
@ -40,14 +41,14 @@ SPEED_TO_VALUE = {speed: i for i, speed in enumerate(SPEED_LIST)}
|
||||||
async def async_setup_platform(hass, config, async_add_entities,
|
async def async_setup_platform(hass, config, async_add_entities,
|
||||||
discovery_info=None):
|
discovery_info=None):
|
||||||
"""Set up the Zigbee Home Automation fans."""
|
"""Set up the Zigbee Home Automation fans."""
|
||||||
discovery_info = zha.get_discovery_info(hass, discovery_info)
|
discovery_info = helpers.get_discovery_info(hass, discovery_info)
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
async_add_entities([ZhaFan(**discovery_info)], update_before_add=True)
|
async_add_entities([ZhaFan(**discovery_info)], update_before_add=True)
|
||||||
|
|
||||||
|
|
||||||
class ZhaFan(zha.Entity, FanEntity):
|
class ZhaFan(ZhaEntity, FanEntity):
|
||||||
"""Representation of a ZHA fan."""
|
"""Representation of a ZHA fan."""
|
||||||
|
|
||||||
_domain = DOMAIN
|
_domain = DOMAIN
|
||||||
|
@ -101,9 +102,9 @@ class ZhaFan(zha.Entity, FanEntity):
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
result = await zha.safe_read(self._endpoint.fan, ['fan_mode'],
|
result = await helpers.safe_read(self._endpoint.fan, ['fan_mode'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
only_cache=(not self._initialized))
|
only_cache=(not self._initialized))
|
||||||
new_value = result.get('fan_mode', None)
|
new_value = result.get('fan_mode', None)
|
||||||
self._state = VALUE_TO_SPEED.get(new_value, None)
|
self._state = VALUE_TO_SPEED.get(new_value, None)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,9 @@ For more details on this platform, please refer to the documentation
|
||||||
at https://home-assistant.io/components/light.zha/
|
at https://home-assistant.io/components/light.zha/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from homeassistant.components import light, zha
|
from homeassistant.components import light
|
||||||
|
from homeassistant.components.zha.entities import ZhaEntity
|
||||||
|
from homeassistant.components.zha import helpers
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -23,13 +25,13 @@ UNSUPPORTED_ATTRIBUTE = 0x86
|
||||||
async def async_setup_platform(hass, config, async_add_entities,
|
async def async_setup_platform(hass, config, async_add_entities,
|
||||||
discovery_info=None):
|
discovery_info=None):
|
||||||
"""Set up the Zigbee Home Automation lights."""
|
"""Set up the Zigbee Home Automation lights."""
|
||||||
discovery_info = zha.get_discovery_info(hass, discovery_info)
|
discovery_info = helpers.get_discovery_info(hass, discovery_info)
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
endpoint = discovery_info['endpoint']
|
endpoint = discovery_info['endpoint']
|
||||||
if hasattr(endpoint, 'light_color'):
|
if hasattr(endpoint, 'light_color'):
|
||||||
caps = await zha.safe_read(
|
caps = await helpers.safe_read(
|
||||||
endpoint.light_color, ['color_capabilities'])
|
endpoint.light_color, ['color_capabilities'])
|
||||||
discovery_info['color_capabilities'] = caps.get('color_capabilities')
|
discovery_info['color_capabilities'] = caps.get('color_capabilities')
|
||||||
if discovery_info['color_capabilities'] is None:
|
if discovery_info['color_capabilities'] is None:
|
||||||
|
@ -37,7 +39,7 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||||
# attribute. In this version XY support is mandatory, but we need
|
# attribute. In this version XY support is mandatory, but we need
|
||||||
# to probe to determine if the device supports color temperature.
|
# to probe to determine if the device supports color temperature.
|
||||||
discovery_info['color_capabilities'] = CAPABILITIES_COLOR_XY
|
discovery_info['color_capabilities'] = CAPABILITIES_COLOR_XY
|
||||||
result = await zha.safe_read(
|
result = await helpers.safe_read(
|
||||||
endpoint.light_color, ['color_temperature'])
|
endpoint.light_color, ['color_temperature'])
|
||||||
if result.get('color_temperature') is not UNSUPPORTED_ATTRIBUTE:
|
if result.get('color_temperature') is not UNSUPPORTED_ATTRIBUTE:
|
||||||
discovery_info['color_capabilities'] |= CAPABILITIES_COLOR_TEMP
|
discovery_info['color_capabilities'] |= CAPABILITIES_COLOR_TEMP
|
||||||
|
@ -45,7 +47,7 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||||
async_add_entities([Light(**discovery_info)], update_before_add=True)
|
async_add_entities([Light(**discovery_info)], update_before_add=True)
|
||||||
|
|
||||||
|
|
||||||
class Light(zha.Entity, light.Light):
|
class Light(ZhaEntity, light.Light):
|
||||||
"""Representation of a ZHA or ZLL light."""
|
"""Representation of a ZHA or ZLL light."""
|
||||||
|
|
||||||
_domain = light.DOMAIN
|
_domain = light.DOMAIN
|
||||||
|
@ -181,31 +183,37 @@ class Light(zha.Entity, light.Light):
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
result = await zha.safe_read(self._endpoint.on_off, ['on_off'],
|
result = await helpers.safe_read(self._endpoint.on_off, ['on_off'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
only_cache=(not self._initialized))
|
only_cache=(not self._initialized))
|
||||||
self._state = result.get('on_off', self._state)
|
self._state = result.get('on_off', self._state)
|
||||||
|
|
||||||
if self._supported_features & light.SUPPORT_BRIGHTNESS:
|
if self._supported_features & light.SUPPORT_BRIGHTNESS:
|
||||||
result = await zha.safe_read(self._endpoint.level,
|
result = await helpers.safe_read(self._endpoint.level,
|
||||||
['current_level'],
|
['current_level'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
only_cache=(not self._initialized))
|
only_cache=(
|
||||||
|
not self._initialized
|
||||||
|
))
|
||||||
self._brightness = result.get('current_level', self._brightness)
|
self._brightness = result.get('current_level', self._brightness)
|
||||||
|
|
||||||
if self._supported_features & light.SUPPORT_COLOR_TEMP:
|
if self._supported_features & light.SUPPORT_COLOR_TEMP:
|
||||||
result = await zha.safe_read(self._endpoint.light_color,
|
result = await helpers.safe_read(self._endpoint.light_color,
|
||||||
['color_temperature'],
|
['color_temperature'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
only_cache=(not self._initialized))
|
only_cache=(
|
||||||
|
not self._initialized
|
||||||
|
))
|
||||||
self._color_temp = result.get('color_temperature',
|
self._color_temp = result.get('color_temperature',
|
||||||
self._color_temp)
|
self._color_temp)
|
||||||
|
|
||||||
if self._supported_features & light.SUPPORT_COLOR:
|
if self._supported_features & light.SUPPORT_COLOR:
|
||||||
result = await zha.safe_read(self._endpoint.light_color,
|
result = await helpers.safe_read(self._endpoint.light_color,
|
||||||
['current_x', 'current_y'],
|
['current_x', 'current_y'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
only_cache=(not self._initialized))
|
only_cache=(
|
||||||
|
not self._initialized
|
||||||
|
))
|
||||||
if 'current_x' in result and 'current_y' in result:
|
if 'current_x' in result and 'current_y' in result:
|
||||||
xy_color = (round(result['current_x']/65535, 3),
|
xy_color = (round(result['current_x']/65535, 3),
|
||||||
round(result['current_y']/65535, 3))
|
round(result['current_y']/65535, 3))
|
||||||
|
|
|
@ -7,7 +7,8 @@ at https://home-assistant.io/components/sensor.zha/
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.sensor import DOMAIN
|
from homeassistant.components.sensor import DOMAIN
|
||||||
from homeassistant.components import zha
|
from homeassistant.components.zha.entities import ZhaEntity
|
||||||
|
from homeassistant.components.zha import helpers
|
||||||
from homeassistant.const import TEMP_CELSIUS
|
from homeassistant.const import TEMP_CELSIUS
|
||||||
from homeassistant.util.temperature import convert as convert_temperature
|
from homeassistant.util.temperature import convert as convert_temperature
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ DEPENDENCIES = ['zha']
|
||||||
async def async_setup_platform(hass, config, async_add_entities,
|
async def async_setup_platform(hass, config, async_add_entities,
|
||||||
discovery_info=None):
|
discovery_info=None):
|
||||||
"""Set up Zigbee Home Automation sensors."""
|
"""Set up Zigbee Home Automation sensors."""
|
||||||
discovery_info = zha.get_discovery_info(hass, discovery_info)
|
discovery_info = helpers.get_discovery_info(hass, discovery_info)
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ async def make_sensor(discovery_info):
|
||||||
|
|
||||||
if discovery_info['new_join']:
|
if discovery_info['new_join']:
|
||||||
cluster = list(in_clusters.values())[0]
|
cluster = list(in_clusters.values())[0]
|
||||||
await zha.configure_reporting(
|
await helpers.configure_reporting(
|
||||||
sensor.entity_id, cluster, sensor.value_attribute,
|
sensor.entity_id, cluster, sensor.value_attribute,
|
||||||
reportable_change=sensor.min_reportable_change
|
reportable_change=sensor.min_reportable_change
|
||||||
)
|
)
|
||||||
|
@ -64,7 +65,7 @@ async def make_sensor(discovery_info):
|
||||||
return sensor
|
return sensor
|
||||||
|
|
||||||
|
|
||||||
class Sensor(zha.Entity):
|
class Sensor(ZhaEntity):
|
||||||
"""Base ZHA sensor."""
|
"""Base ZHA sensor."""
|
||||||
|
|
||||||
_domain = DOMAIN
|
_domain = DOMAIN
|
||||||
|
@ -92,7 +93,7 @@ class Sensor(zha.Entity):
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
result = await zha.safe_read(
|
result = await helpers.safe_read(
|
||||||
list(self._in_clusters.values())[0],
|
list(self._in_clusters.values())[0],
|
||||||
[self.value_attribute],
|
[self.value_attribute],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
|
@ -224,7 +225,7 @@ class ElectricalMeasurementSensor(Sensor):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
_LOGGER.debug("%s async_update", self.entity_id)
|
_LOGGER.debug("%s async_update", self.entity_id)
|
||||||
|
|
||||||
result = await zha.safe_read(
|
result = await helpers.safe_read(
|
||||||
self._endpoint.electrical_measurement, ['active_power'],
|
self._endpoint.electrical_measurement, ['active_power'],
|
||||||
allow_cache=False, only_cache=(not self._initialized))
|
allow_cache=False, only_cache=(not self._initialized))
|
||||||
self._state = result.get('active_power', self._state)
|
self._state = result.get('active_power', self._state)
|
||||||
|
|
|
@ -7,7 +7,8 @@ at https://home-assistant.io/components/switch.zha/
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.switch import DOMAIN, SwitchDevice
|
from homeassistant.components.switch import DOMAIN, SwitchDevice
|
||||||
from homeassistant.components import zha
|
from homeassistant.components.zha.entities import ZhaEntity
|
||||||
|
from homeassistant.components.zha import helpers
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||||
"""Set up the Zigbee Home Automation switches."""
|
"""Set up the Zigbee Home Automation switches."""
|
||||||
from zigpy.zcl.clusters.general import OnOff
|
from zigpy.zcl.clusters.general import OnOff
|
||||||
|
|
||||||
discovery_info = zha.get_discovery_info(hass, discovery_info)
|
discovery_info = helpers.get_discovery_info(hass, discovery_info)
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||||
if discovery_info['new_join']:
|
if discovery_info['new_join']:
|
||||||
in_clusters = discovery_info['in_clusters']
|
in_clusters = discovery_info['in_clusters']
|
||||||
cluster = in_clusters[OnOff.cluster_id]
|
cluster = in_clusters[OnOff.cluster_id]
|
||||||
await zha.configure_reporting(
|
await helpers.configure_reporting(
|
||||||
switch.entity_id, cluster, switch.value_attribute,
|
switch.entity_id, cluster, switch.value_attribute,
|
||||||
min_report=0, max_report=600, reportable_change=1
|
min_report=0, max_report=600, reportable_change=1
|
||||||
)
|
)
|
||||||
|
@ -36,7 +37,7 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||||
async_add_entities([switch], update_before_add=True)
|
async_add_entities([switch], update_before_add=True)
|
||||||
|
|
||||||
|
|
||||||
class Switch(zha.Entity, SwitchDevice):
|
class Switch(ZhaEntity, SwitchDevice):
|
||||||
"""ZHA switch."""
|
"""ZHA switch."""
|
||||||
|
|
||||||
_domain = DOMAIN
|
_domain = DOMAIN
|
||||||
|
@ -94,8 +95,8 @@ class Switch(zha.Entity, SwitchDevice):
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
result = await zha.safe_read(self._endpoint.on_off,
|
result = await helpers.safe_read(self._endpoint.on_off,
|
||||||
['on_off'],
|
['on_off'],
|
||||||
allow_cache=False,
|
allow_cache=False,
|
||||||
only_cache=(not self._initialized))
|
only_cache=(not self._initialized))
|
||||||
self._state = result.get('on_off', self._state)
|
self._state = result.get('on_off', self._state)
|
||||||
|
|
|
@ -7,15 +7,15 @@ https://home-assistant.io/components/zha/
|
||||||
import collections
|
import collections
|
||||||
import enum
|
import enum
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant import const as ha_const
|
from homeassistant import const as ha_const
|
||||||
from homeassistant.helpers import discovery, entity
|
from homeassistant.helpers import discovery
|
||||||
from homeassistant.util import slugify
|
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.components.zha.entities import ZhaDeviceEntity
|
||||||
|
from . import const as zha_const
|
||||||
|
|
||||||
REQUIREMENTS = [
|
REQUIREMENTS = [
|
||||||
'bellows==0.7.0',
|
'bellows==0.7.0',
|
||||||
|
@ -145,6 +145,7 @@ class ApplicationListener:
|
||||||
self._component = EntityComponent(_LOGGER, DOMAIN, hass)
|
self._component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||||
self._device_registry = collections.defaultdict(list)
|
self._device_registry = collections.defaultdict(list)
|
||||||
hass.data[DISCOVERY_KEY] = hass.data.get(DISCOVERY_KEY, {})
|
hass.data[DISCOVERY_KEY] = hass.data.get(DISCOVERY_KEY, {})
|
||||||
|
zha_const.populate_data()
|
||||||
|
|
||||||
def device_joined(self, device):
|
def device_joined(self, device):
|
||||||
"""Handle device joined.
|
"""Handle device joined.
|
||||||
|
@ -177,8 +178,6 @@ class ApplicationListener:
|
||||||
async def async_device_initialized(self, device, join):
|
async def async_device_initialized(self, device, join):
|
||||||
"""Handle device joined and basic information discovered (async)."""
|
"""Handle device joined and basic information discovered (async)."""
|
||||||
import zigpy.profiles
|
import zigpy.profiles
|
||||||
import homeassistant.components.zha.const as zha_const
|
|
||||||
zha_const.populate_data()
|
|
||||||
|
|
||||||
device_manufacturer = device_model = None
|
device_manufacturer = device_model = None
|
||||||
|
|
||||||
|
@ -276,7 +275,6 @@ class ApplicationListener:
|
||||||
device_classes, discovery_attr,
|
device_classes, discovery_attr,
|
||||||
is_new_join):
|
is_new_join):
|
||||||
"""Try to set up an entity from a "bare" cluster."""
|
"""Try to set up an entity from a "bare" cluster."""
|
||||||
import homeassistant.components.zha.const as zha_const
|
|
||||||
if cluster.cluster_id in profile_clusters:
|
if cluster.cluster_id in profile_clusters:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -320,226 +318,3 @@ class ApplicationListener:
|
||||||
{'discovery_key': cluster_key},
|
{'discovery_key': cluster_key},
|
||||||
self._config,
|
self._config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Entity(entity.Entity):
|
|
||||||
"""A base class for ZHA entities."""
|
|
||||||
|
|
||||||
_domain = None # Must be overridden by subclasses
|
|
||||||
|
|
||||||
def __init__(self, endpoint, in_clusters, out_clusters, manufacturer,
|
|
||||||
model, application_listener, unique_id, **kwargs):
|
|
||||||
"""Init ZHA entity."""
|
|
||||||
self._device_state_attributes = {}
|
|
||||||
ieee = endpoint.device.ieee
|
|
||||||
ieeetail = ''.join(['%02x' % (o, ) for o in ieee[-4:]])
|
|
||||||
if manufacturer and model is not None:
|
|
||||||
self.entity_id = "{}.{}_{}_{}_{}{}".format(
|
|
||||||
self._domain,
|
|
||||||
slugify(manufacturer),
|
|
||||||
slugify(model),
|
|
||||||
ieeetail,
|
|
||||||
endpoint.endpoint_id,
|
|
||||||
kwargs.get('entity_suffix', ''),
|
|
||||||
)
|
|
||||||
self._device_state_attributes['friendly_name'] = "{} {}".format(
|
|
||||||
manufacturer,
|
|
||||||
model,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.entity_id = "{}.zha_{}_{}{}".format(
|
|
||||||
self._domain,
|
|
||||||
ieeetail,
|
|
||||||
endpoint.endpoint_id,
|
|
||||||
kwargs.get('entity_suffix', ''),
|
|
||||||
)
|
|
||||||
|
|
||||||
self._endpoint = endpoint
|
|
||||||
self._in_clusters = in_clusters
|
|
||||||
self._out_clusters = out_clusters
|
|
||||||
self._state = None
|
|
||||||
self._unique_id = unique_id
|
|
||||||
|
|
||||||
# Normally the entity itself is the listener. Sub-classes may set this
|
|
||||||
# to a dict of cluster ID -> listener to receive messages for specific
|
|
||||||
# clusters separately
|
|
||||||
self._in_listeners = {}
|
|
||||||
self._out_listeners = {}
|
|
||||||
|
|
||||||
self._initialized = False
|
|
||||||
application_listener.register_entity(ieee, self)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Handle entity addition to hass.
|
|
||||||
|
|
||||||
It is now safe to update the entity state
|
|
||||||
"""
|
|
||||||
for cluster_id, cluster in self._in_clusters.items():
|
|
||||||
cluster.add_listener(self._in_listeners.get(cluster_id, self))
|
|
||||||
for cluster_id, cluster in self._out_clusters.items():
|
|
||||||
cluster.add_listener(self._out_listeners.get(cluster_id, self))
|
|
||||||
|
|
||||||
self._initialized = True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str:
|
|
||||||
"""Return a unique ID."""
|
|
||||||
return self._unique_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return device specific state attributes."""
|
|
||||||
return self._device_state_attributes
|
|
||||||
|
|
||||||
def attribute_updated(self, attribute, value):
|
|
||||||
"""Handle an attribute updated on this cluster."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def zdo_command(self, tsn, command_id, args):
|
|
||||||
"""Handle a ZDO command received on this cluster."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ZhaDeviceEntity(entity.Entity):
|
|
||||||
"""A base class for ZHA devices."""
|
|
||||||
|
|
||||||
def __init__(self, device, manufacturer, model, application_listener,
|
|
||||||
keepalive_interval=7200, **kwargs):
|
|
||||||
"""Init ZHA endpoint entity."""
|
|
||||||
self._device_state_attributes = {
|
|
||||||
'nwk': '0x{0:04x}'.format(device.nwk),
|
|
||||||
'ieee': str(device.ieee),
|
|
||||||
'lqi': device.lqi,
|
|
||||||
'rssi': device.rssi,
|
|
||||||
}
|
|
||||||
|
|
||||||
ieee = device.ieee
|
|
||||||
ieeetail = ''.join(['%02x' % (o, ) for o in ieee[-4:]])
|
|
||||||
if manufacturer is not None and model is not None:
|
|
||||||
self._unique_id = "{}_{}_{}".format(
|
|
||||||
slugify(manufacturer),
|
|
||||||
slugify(model),
|
|
||||||
ieeetail,
|
|
||||||
)
|
|
||||||
self._device_state_attributes['friendly_name'] = "{} {}".format(
|
|
||||||
manufacturer,
|
|
||||||
model,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._unique_id = str(ieeetail)
|
|
||||||
|
|
||||||
self._device = device
|
|
||||||
self._state = 'offline'
|
|
||||||
self._keepalive_interval = keepalive_interval
|
|
||||||
|
|
||||||
application_listener.register_entity(ieee, self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str:
|
|
||||||
"""Return a unique ID."""
|
|
||||||
return self._unique_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self) -> str:
|
|
||||||
"""Return the state of the entity."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return device specific state attributes."""
|
|
||||||
update_time = None
|
|
||||||
if self._device.last_seen is not None and self._state == 'offline':
|
|
||||||
time_struct = time.localtime(self._device.last_seen)
|
|
||||||
update_time = time.strftime("%Y-%m-%dT%H:%M:%S", time_struct)
|
|
||||||
self._device_state_attributes['last_seen'] = update_time
|
|
||||||
if ('last_seen' in self._device_state_attributes and
|
|
||||||
self._state != 'offline'):
|
|
||||||
del self._device_state_attributes['last_seen']
|
|
||||||
self._device_state_attributes['lqi'] = self._device.lqi
|
|
||||||
self._device_state_attributes['rssi'] = self._device.rssi
|
|
||||||
return self._device_state_attributes
|
|
||||||
|
|
||||||
async def async_update(self):
|
|
||||||
"""Handle polling."""
|
|
||||||
if self._device.last_seen is None:
|
|
||||||
self._state = 'offline'
|
|
||||||
else:
|
|
||||||
difference = time.time() - self._device.last_seen
|
|
||||||
if difference > self._keepalive_interval:
|
|
||||||
self._state = 'offline'
|
|
||||||
else:
|
|
||||||
self._state = 'online'
|
|
||||||
|
|
||||||
|
|
||||||
def get_discovery_info(hass, discovery_info):
|
|
||||||
"""Get the full discovery info for a device.
|
|
||||||
|
|
||||||
Some of the info that needs to be passed to platforms is not JSON
|
|
||||||
serializable, so it cannot be put in the discovery_info dictionary. This
|
|
||||||
component places that info we need to pass to the platform in hass.data,
|
|
||||||
and this function is a helper for platforms to retrieve the complete
|
|
||||||
discovery info.
|
|
||||||
"""
|
|
||||||
if discovery_info is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
discovery_key = discovery_info.get('discovery_key', None)
|
|
||||||
all_discovery_info = hass.data.get(DISCOVERY_KEY, {})
|
|
||||||
return all_discovery_info.get(discovery_key, None)
|
|
||||||
|
|
||||||
|
|
||||||
async def safe_read(cluster, attributes, allow_cache=True, only_cache=False):
|
|
||||||
"""Swallow all exceptions from network read.
|
|
||||||
|
|
||||||
If we throw during initialization, setup fails. Rather have an entity that
|
|
||||||
exists, but is in a maybe wrong state, than no entity. This method should
|
|
||||||
probably only be used during initialization.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
result, _ = await cluster.read_attributes(
|
|
||||||
attributes,
|
|
||||||
allow_cache=allow_cache,
|
|
||||||
only_cache=only_cache
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
async def configure_reporting(entity_id, cluster, attr, skip_bind=False,
|
|
||||||
min_report=300, max_report=900,
|
|
||||||
reportable_change=1):
|
|
||||||
"""Configure attribute reporting for a cluster.
|
|
||||||
|
|
||||||
while swallowing the DeliverError exceptions in case of unreachable
|
|
||||||
devices.
|
|
||||||
"""
|
|
||||||
from zigpy.exceptions import DeliveryError
|
|
||||||
|
|
||||||
attr_name = cluster.attributes.get(attr, [attr])[0]
|
|
||||||
cluster_name = cluster.ep_attribute
|
|
||||||
if not skip_bind:
|
|
||||||
try:
|
|
||||||
res = await cluster.bind()
|
|
||||||
_LOGGER.debug(
|
|
||||||
"%s: bound '%s' cluster: %s", entity_id, cluster_name, res[0]
|
|
||||||
)
|
|
||||||
except DeliveryError as ex:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"%s: Failed to bind '%s' cluster: %s",
|
|
||||||
entity_id, cluster_name, str(ex)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
res = await cluster.configure_reporting(attr, min_report,
|
|
||||||
max_report, reportable_change)
|
|
||||||
_LOGGER.debug(
|
|
||||||
"%s: reporting '%s' attr on '%s' cluster: %d/%d/%d: Result: '%s'",
|
|
||||||
entity_id, attr_name, cluster_name, min_report, max_report,
|
|
||||||
reportable_change, res
|
|
||||||
)
|
|
||||||
except DeliveryError as ex:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"%s: failed to set reporting for '%s' attr on '%s' cluster: %s",
|
|
||||||
entity_id, attr_name, cluster_name, str(ex)
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""All constants related to the ZHA component."""
|
"""All constants related to the ZHA component."""
|
||||||
|
|
||||||
|
DISCOVERY_KEY = 'zha_discovery_info'
|
||||||
DEVICE_CLASS = {}
|
DEVICE_CLASS = {}
|
||||||
SINGLE_INPUT_CLUSTER_DEVICE_CLASS = {}
|
SINGLE_INPUT_CLUSTER_DEVICE_CLASS = {}
|
||||||
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {}
|
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {}
|
||||||
|
@ -17,7 +18,12 @@ def populate_data():
|
||||||
from zigpy.profiles import PROFILES, zha, zll
|
from zigpy.profiles import PROFILES, zha, zll
|
||||||
from homeassistant.components.sensor import zha as sensor_zha
|
from homeassistant.components.sensor import zha as sensor_zha
|
||||||
|
|
||||||
DEVICE_CLASS[zha.PROFILE_ID] = {
|
if zha.PROFILE_ID not in DEVICE_CLASS:
|
||||||
|
DEVICE_CLASS[zha.PROFILE_ID] = {}
|
||||||
|
if zll.PROFILE_ID not in DEVICE_CLASS:
|
||||||
|
DEVICE_CLASS[zll.PROFILE_ID] = {}
|
||||||
|
|
||||||
|
DEVICE_CLASS[zha.PROFILE_ID].update({
|
||||||
zha.DeviceType.ON_OFF_SWITCH: 'binary_sensor',
|
zha.DeviceType.ON_OFF_SWITCH: 'binary_sensor',
|
||||||
zha.DeviceType.LEVEL_CONTROL_SWITCH: 'binary_sensor',
|
zha.DeviceType.LEVEL_CONTROL_SWITCH: 'binary_sensor',
|
||||||
zha.DeviceType.REMOTE_CONTROL: 'binary_sensor',
|
zha.DeviceType.REMOTE_CONTROL: 'binary_sensor',
|
||||||
|
@ -29,8 +35,8 @@ def populate_data():
|
||||||
zha.DeviceType.ON_OFF_LIGHT_SWITCH: 'binary_sensor',
|
zha.DeviceType.ON_OFF_LIGHT_SWITCH: 'binary_sensor',
|
||||||
zha.DeviceType.DIMMER_SWITCH: 'binary_sensor',
|
zha.DeviceType.DIMMER_SWITCH: 'binary_sensor',
|
||||||
zha.DeviceType.COLOR_DIMMER_SWITCH: 'binary_sensor',
|
zha.DeviceType.COLOR_DIMMER_SWITCH: 'binary_sensor',
|
||||||
}
|
})
|
||||||
DEVICE_CLASS[zll.PROFILE_ID] = {
|
DEVICE_CLASS[zll.PROFILE_ID].update({
|
||||||
zll.DeviceType.ON_OFF_LIGHT: 'light',
|
zll.DeviceType.ON_OFF_LIGHT: 'light',
|
||||||
zll.DeviceType.ON_OFF_PLUGIN_UNIT: 'switch',
|
zll.DeviceType.ON_OFF_PLUGIN_UNIT: 'switch',
|
||||||
zll.DeviceType.DIMMABLE_LIGHT: 'light',
|
zll.DeviceType.DIMMABLE_LIGHT: 'light',
|
||||||
|
@ -43,7 +49,7 @@ def populate_data():
|
||||||
zll.DeviceType.CONTROLLER: 'binary_sensor',
|
zll.DeviceType.CONTROLLER: 'binary_sensor',
|
||||||
zll.DeviceType.SCENE_CONTROLLER: 'binary_sensor',
|
zll.DeviceType.SCENE_CONTROLLER: 'binary_sensor',
|
||||||
zll.DeviceType.ON_OFF_SENSOR: 'binary_sensor',
|
zll.DeviceType.ON_OFF_SENSOR: 'binary_sensor',
|
||||||
}
|
})
|
||||||
|
|
||||||
SINGLE_INPUT_CLUSTER_DEVICE_CLASS.update({
|
SINGLE_INPUT_CLUSTER_DEVICE_CLASS.update({
|
||||||
zcl.clusters.general.OnOff: 'switch',
|
zcl.clusters.general.OnOff: 'switch',
|
||||||
|
|
10
homeassistant/components/zha/entities/__init__.py
Normal file
10
homeassistant/components/zha/entities/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
"""
|
||||||
|
Entities for Zigbee Home Automation.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/zha/
|
||||||
|
"""
|
||||||
|
|
||||||
|
# flake8: noqa
|
||||||
|
from .entity import ZhaEntity
|
||||||
|
from .device_entity import ZhaDeviceEntity
|
81
homeassistant/components/zha/entities/device_entity.py
Normal file
81
homeassistant/components/zha/entities/device_entity.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
"""
|
||||||
|
Device entity for Zigbee Home Automation.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/zha/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
from homeassistant.helpers import entity
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
|
|
||||||
|
class ZhaDeviceEntity(entity.Entity):
|
||||||
|
"""A base class for ZHA devices."""
|
||||||
|
|
||||||
|
def __init__(self, device, manufacturer, model, application_listener,
|
||||||
|
keepalive_interval=7200, **kwargs):
|
||||||
|
"""Init ZHA endpoint entity."""
|
||||||
|
self._device_state_attributes = {
|
||||||
|
'nwk': '0x{0:04x}'.format(device.nwk),
|
||||||
|
'ieee': str(device.ieee),
|
||||||
|
'lqi': device.lqi,
|
||||||
|
'rssi': device.rssi,
|
||||||
|
}
|
||||||
|
|
||||||
|
ieee = device.ieee
|
||||||
|
ieeetail = ''.join(['%02x' % (o, ) for o in ieee[-4:]])
|
||||||
|
if manufacturer is not None and model is not None:
|
||||||
|
self._unique_id = "{}_{}_{}".format(
|
||||||
|
slugify(manufacturer),
|
||||||
|
slugify(model),
|
||||||
|
ieeetail,
|
||||||
|
)
|
||||||
|
self._device_state_attributes['friendly_name'] = "{} {}".format(
|
||||||
|
manufacturer,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._unique_id = str(ieeetail)
|
||||||
|
|
||||||
|
self._device = device
|
||||||
|
self._state = 'offline'
|
||||||
|
self._keepalive_interval = keepalive_interval
|
||||||
|
|
||||||
|
application_listener.register_entity(ieee, self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self) -> str:
|
||||||
|
"""Return a unique ID."""
|
||||||
|
return self._unique_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self) -> str:
|
||||||
|
"""Return the state of the entity."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return device specific state attributes."""
|
||||||
|
update_time = None
|
||||||
|
if self._device.last_seen is not None and self._state == 'offline':
|
||||||
|
time_struct = time.localtime(self._device.last_seen)
|
||||||
|
update_time = time.strftime("%Y-%m-%dT%H:%M:%S", time_struct)
|
||||||
|
self._device_state_attributes['last_seen'] = update_time
|
||||||
|
if ('last_seen' in self._device_state_attributes and
|
||||||
|
self._state != 'offline'):
|
||||||
|
del self._device_state_attributes['last_seen']
|
||||||
|
self._device_state_attributes['lqi'] = self._device.lqi
|
||||||
|
self._device_state_attributes['rssi'] = self._device.rssi
|
||||||
|
return self._device_state_attributes
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Handle polling."""
|
||||||
|
if self._device.last_seen is None:
|
||||||
|
self._state = 'offline'
|
||||||
|
else:
|
||||||
|
difference = time.time() - self._device.last_seen
|
||||||
|
if difference > self._keepalive_interval:
|
||||||
|
self._state = 'offline'
|
||||||
|
else:
|
||||||
|
self._state = 'online'
|
89
homeassistant/components/zha/entities/entity.py
Normal file
89
homeassistant/components/zha/entities/entity.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
"""
|
||||||
|
Entity for Zigbee Home Automation.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/zha/
|
||||||
|
"""
|
||||||
|
from homeassistant.helpers import entity
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
from homeassistant.core import callback
|
||||||
|
|
||||||
|
|
||||||
|
class ZhaEntity(entity.Entity):
|
||||||
|
"""A base class for ZHA entities."""
|
||||||
|
|
||||||
|
_domain = None # Must be overridden by subclasses
|
||||||
|
|
||||||
|
def __init__(self, endpoint, in_clusters, out_clusters, manufacturer,
|
||||||
|
model, application_listener, unique_id, **kwargs):
|
||||||
|
"""Init ZHA entity."""
|
||||||
|
self._device_state_attributes = {}
|
||||||
|
ieee = endpoint.device.ieee
|
||||||
|
ieeetail = ''.join(['%02x' % (o, ) for o in ieee[-4:]])
|
||||||
|
if manufacturer and model is not None:
|
||||||
|
self.entity_id = "{}.{}_{}_{}_{}{}".format(
|
||||||
|
self._domain,
|
||||||
|
slugify(manufacturer),
|
||||||
|
slugify(model),
|
||||||
|
ieeetail,
|
||||||
|
endpoint.endpoint_id,
|
||||||
|
kwargs.get('entity_suffix', ''),
|
||||||
|
)
|
||||||
|
self._device_state_attributes['friendly_name'] = "{} {}".format(
|
||||||
|
manufacturer,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.entity_id = "{}.zha_{}_{}{}".format(
|
||||||
|
self._domain,
|
||||||
|
ieeetail,
|
||||||
|
endpoint.endpoint_id,
|
||||||
|
kwargs.get('entity_suffix', ''),
|
||||||
|
)
|
||||||
|
|
||||||
|
self._endpoint = endpoint
|
||||||
|
self._in_clusters = in_clusters
|
||||||
|
self._out_clusters = out_clusters
|
||||||
|
self._state = None
|
||||||
|
self._unique_id = unique_id
|
||||||
|
|
||||||
|
# Normally the entity itself is the listener. Sub-classes may set this
|
||||||
|
# to a dict of cluster ID -> listener to receive messages for specific
|
||||||
|
# clusters separately
|
||||||
|
self._in_listeners = {}
|
||||||
|
self._out_listeners = {}
|
||||||
|
|
||||||
|
self._initialized = False
|
||||||
|
application_listener.register_entity(ieee, self)
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""Handle entity addition to hass.
|
||||||
|
|
||||||
|
It is now safe to update the entity state
|
||||||
|
"""
|
||||||
|
for cluster_id, cluster in self._in_clusters.items():
|
||||||
|
cluster.add_listener(self._in_listeners.get(cluster_id, self))
|
||||||
|
for cluster_id, cluster in self._out_clusters.items():
|
||||||
|
cluster.add_listener(self._out_listeners.get(cluster_id, self))
|
||||||
|
|
||||||
|
self._initialized = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self) -> str:
|
||||||
|
"""Return a unique ID."""
|
||||||
|
return self._unique_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return device specific state attributes."""
|
||||||
|
return self._device_state_attributes
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def attribute_updated(self, attribute, value):
|
||||||
|
"""Handle an attribute updated on this cluster."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def zdo_command(self, tsn, command_id, args):
|
||||||
|
"""Handle a ZDO command received on this cluster."""
|
||||||
|
pass
|
84
homeassistant/components/zha/helpers.py
Normal file
84
homeassistant/components/zha/helpers.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
"""
|
||||||
|
Helpers for Zigbee Home Automation.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/zha/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_discovery_info(hass, discovery_info):
|
||||||
|
"""Get the full discovery info for a device.
|
||||||
|
|
||||||
|
Some of the info that needs to be passed to platforms is not JSON
|
||||||
|
serializable, so it cannot be put in the discovery_info dictionary. This
|
||||||
|
component places that info we need to pass to the platform in hass.data,
|
||||||
|
and this function is a helper for platforms to retrieve the complete
|
||||||
|
discovery info.
|
||||||
|
"""
|
||||||
|
if discovery_info is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
import homeassistant.components.zha.const as zha_const
|
||||||
|
discovery_key = discovery_info.get('discovery_key', None)
|
||||||
|
all_discovery_info = hass.data.get(zha_const.DISCOVERY_KEY, {})
|
||||||
|
return all_discovery_info.get(discovery_key, None)
|
||||||
|
|
||||||
|
|
||||||
|
async def safe_read(cluster, attributes, allow_cache=True, only_cache=False):
|
||||||
|
"""Swallow all exceptions from network read.
|
||||||
|
|
||||||
|
If we throw during initialization, setup fails. Rather have an entity that
|
||||||
|
exists, but is in a maybe wrong state, than no entity. This method should
|
||||||
|
probably only be used during initialization.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result, _ = await cluster.read_attributes(
|
||||||
|
attributes,
|
||||||
|
allow_cache=allow_cache,
|
||||||
|
only_cache=only_cache
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
async def configure_reporting(entity_id, cluster, attr, skip_bind=False,
|
||||||
|
min_report=300, max_report=900,
|
||||||
|
reportable_change=1):
|
||||||
|
"""Configure attribute reporting for a cluster.
|
||||||
|
|
||||||
|
while swallowing the DeliverError exceptions in case of unreachable
|
||||||
|
devices.
|
||||||
|
"""
|
||||||
|
from zigpy.exceptions import DeliveryError
|
||||||
|
|
||||||
|
attr_name = cluster.attributes.get(attr, [attr])[0]
|
||||||
|
cluster_name = cluster.ep_attribute
|
||||||
|
if not skip_bind:
|
||||||
|
try:
|
||||||
|
res = await cluster.bind()
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s: bound '%s' cluster: %s", entity_id, cluster_name, res[0]
|
||||||
|
)
|
||||||
|
except DeliveryError as ex:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s: Failed to bind '%s' cluster: %s",
|
||||||
|
entity_id, cluster_name, str(ex)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = await cluster.configure_reporting(attr, min_report,
|
||||||
|
max_report, reportable_change)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s: reporting '%s' attr on '%s' cluster: %d/%d/%d: Result: '%s'",
|
||||||
|
entity_id, attr_name, cluster_name, min_report, max_report,
|
||||||
|
reportable_change, res
|
||||||
|
)
|
||||||
|
except DeliveryError as ex:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s: failed to set reporting for '%s' attr on '%s' cluster: %s",
|
||||||
|
entity_id, attr_name, cluster_name, str(ex)
|
||||||
|
)
|
Loading…
Add table
Add a link
Reference in a new issue