Fix ZHA device healthcheck pings (#32425)

This commit is contained in:
Alexei Chetroi 2020-03-03 13:37:17 -05:00 committed by GitHub
parent cfa61a6b74
commit 896df9267a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 3 deletions

View file

@ -87,7 +87,7 @@ class ZHADevice(LogMixin):
self._available_signal = "{}_{}_{}".format(
self.name, self.ieee, SIGNAL_AVAILABLE
)
self._checkins_missed_count = 2
self._checkins_missed_count = 0
self._unsub = async_dispatcher_connect(
self.hass, self._available_signal, self.async_initialize
)
@ -284,8 +284,12 @@ class ZHADevice(LogMixin):
)
if not self._channels.pools:
return
pool = self._channels.pools[0]
basic_ch = pool.all_channels[f"{pool.id}:0"]
try:
pool = self._channels.pools[0]
basic_ch = pool.all_channels[f"{pool.id}:0x0000"]
except KeyError:
self.debug("%s %s does not have a mandatory basic cluster")
return
self.hass.async_create_task(
basic_ch.get_attribute_value(
ATTR_MANUFACTURER, from_cache=False

View file

@ -0,0 +1,146 @@
"""Test zha device switch."""
from datetime import timedelta
import time
from unittest import mock
import asynctest
import pytest
import zigpy.zcl.clusters.general as general
import homeassistant.components.zha.core.device as zha_core_device
import homeassistant.core as ha
import homeassistant.util.dt as dt_util
from .common import async_enable_traffic
@pytest.fixture
def zigpy_device(zigpy_device_mock):
"""Device tracker zigpy device."""
def _dev(with_basic_channel: bool = True):
in_clusters = [general.OnOff.cluster_id]
if with_basic_channel:
in_clusters.append(general.Basic.cluster_id)
endpoints = {
3: {"in_clusters": in_clusters, "out_clusters": [], "device_type": 0}
}
return zigpy_device_mock(endpoints)
return _dev
@pytest.fixture
def device_with_basic_channel(zigpy_device):
"""Return a zha device with a basic channel present."""
return zigpy_device(with_basic_channel=True)
@pytest.fixture
def device_without_basic_channel(zigpy_device):
"""Return a zha device with a basic channel present."""
return zigpy_device(with_basic_channel=False)
def _send_time_changed(hass, seconds):
"""Send a time changed event."""
now = dt_util.utcnow() + timedelta(seconds)
hass.bus.async_fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: now})
@asynctest.patch(
"homeassistant.components.zha.core.channels.general.BasicChannel.async_initialize",
new=mock.MagicMock(),
)
async def test_check_available_success(
hass, device_with_basic_channel, zha_device_restored
):
"""Check device availability success on 1st try."""
# pylint: disable=protected-access
zha_device = await zha_device_restored(device_with_basic_channel)
await async_enable_traffic(hass, [zha_device])
basic_ch = device_with_basic_channel.endpoints[3].basic
basic_ch.read_attributes.reset_mock()
device_with_basic_channel.last_seen = None
assert zha_device.available is True
_send_time_changed(hass, 61)
await hass.async_block_till_done()
assert zha_device.available is False
assert basic_ch.read_attributes.await_count == 0
device_with_basic_channel.last_seen = (
time.time() - zha_core_device._KEEP_ALIVE_INTERVAL - 2
)
_seens = [time.time(), device_with_basic_channel.last_seen]
def _update_last_seen(*args, **kwargs):
device_with_basic_channel.last_seen = _seens.pop()
basic_ch.read_attributes.side_effect = _update_last_seen
# successfully ping zigpy device, but zha_device is not yet available
_send_time_changed(hass, 61)
await hass.async_block_till_done()
assert basic_ch.read_attributes.await_count == 1
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is False
# There was traffic from the device: pings, but not yet available
_send_time_changed(hass, 61)
await hass.async_block_till_done()
assert basic_ch.read_attributes.await_count == 2
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is False
# There was traffic from the device: don't try to ping, marked as available
_send_time_changed(hass, 61)
await hass.async_block_till_done()
assert basic_ch.read_attributes.await_count == 2
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is True
@asynctest.patch(
"homeassistant.components.zha.core.channels.general.BasicChannel.async_initialize",
new=mock.MagicMock(),
)
async def test_check_available_unsuccessful(
hass, device_with_basic_channel, zha_device_restored
):
"""Check device availability all tries fail."""
# pylint: disable=protected-access
zha_device = await zha_device_restored(device_with_basic_channel)
await async_enable_traffic(hass, [zha_device])
basic_ch = device_with_basic_channel.endpoints[3].basic
assert zha_device.available is True
assert basic_ch.read_attributes.await_count == 0
device_with_basic_channel.last_seen = (
time.time() - zha_core_device._KEEP_ALIVE_INTERVAL - 2
)
# unsuccessfuly ping zigpy device, but zha_device is still available
_send_time_changed(hass, 61)
await hass.async_block_till_done()
assert basic_ch.read_attributes.await_count == 1
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is True
# still no traffic, but zha_device is still available
_send_time_changed(hass, 61)
await hass.async_block_till_done()
assert basic_ch.read_attributes.await_count == 2
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is True
# not even trying to update, device is unavailble
_send_time_changed(hass, 61)
await hass.async_block_till_done()
assert basic_ch.read_attributes.await_count == 2
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is False