Add gui config option consider device unavailable (#51218)

* Add gui config option consider device unavailable

* Update tests
This commit is contained in:
astronaut 2021-05-30 01:13:09 +02:00 committed by GitHub
parent d1f0ec8db8
commit ceadb0cba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 34 deletions

View File

@ -137,10 +137,23 @@ CONF_RADIO_TYPE = "radio_type"
CONF_USB_PATH = "usb_path"
CONF_ZIGPY = "zigpy_config"
CONF_CONSIDER_UNAVAILABLE_MAINS = "consider_unavailable_mains"
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS = 60 * 60 * 2 # 2 hours
CONF_CONSIDER_UNAVAILABLE_BATTERY = "consider_unavailable_battery"
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY = 60 * 60 * 6 # 6 hours
CONF_ZHA_OPTIONS_SCHEMA = vol.Schema(
{
vol.Optional(CONF_DEFAULT_LIGHT_TRANSITION): cv.positive_int,
vol.Required(CONF_ENABLE_IDENTIFY_ON_JOIN, default=True): cv.boolean,
vol.Optional(
CONF_CONSIDER_UNAVAILABLE_MAINS,
default=CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS,
): cv.positive_int,
vol.Optional(
CONF_CONSIDER_UNAVAILABLE_BATTERY,
default=CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY,
): cv.positive_int,
}
)

View File

@ -55,6 +55,10 @@ from .const import (
CLUSTER_COMMANDS_SERVER,
CLUSTER_TYPE_IN,
CLUSTER_TYPE_OUT,
CONF_CONSIDER_UNAVAILABLE_BATTERY,
CONF_CONSIDER_UNAVAILABLE_MAINS,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS,
CONF_ENABLE_IDENTIFY_ON_JOIN,
EFFECT_DEFAULT_VARIANT,
EFFECT_OKAY,
@ -70,8 +74,6 @@ from .const import (
from .helpers import LogMixin, async_get_zha_config_value
_LOGGER = logging.getLogger(__name__)
CONSIDER_UNAVAILABLE_MAINS = 60 * 60 * 2 # 2 hours
CONSIDER_UNAVAILABLE_BATTERY = 60 * 60 * 6 # 6 hours
_UPDATE_ALIVE_INTERVAL = (60, 90)
_CHECKIN_GRACE_PERIODS = 2
@ -107,9 +109,20 @@ class ZHADevice(LogMixin):
)
if self.is_mains_powered:
self._consider_unavailable_time = CONSIDER_UNAVAILABLE_MAINS
self.consider_unavailable_time = async_get_zha_config_value(
self._zha_gateway.config_entry,
ZHA_OPTIONS,
CONF_CONSIDER_UNAVAILABLE_MAINS,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS,
)
else:
self._consider_unavailable_time = CONSIDER_UNAVAILABLE_BATTERY
self.consider_unavailable_time = async_get_zha_config_value(
self._zha_gateway.config_entry,
ZHA_OPTIONS,
CONF_CONSIDER_UNAVAILABLE_BATTERY,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY,
)
keep_alive_interval = random.randint(*_UPDATE_ALIVE_INTERVAL)
self.unsubs.append(
async_track_time_interval(
@ -320,7 +333,7 @@ class ZHADevice(LogMixin):
return
difference = time.time() - self.last_seen
if difference < self._consider_unavailable_time:
if difference < self.consider_unavailable_time:
self.update_available(True)
self._checkins_missed_count = 0
return

View File

@ -77,12 +77,7 @@ from .const import (
ZHA_GW_MSG_RAW_INIT,
RadioType,
)
from .device import (
CONSIDER_UNAVAILABLE_BATTERY,
CONSIDER_UNAVAILABLE_MAINS,
DeviceStatus,
ZHADevice,
)
from .device import DeviceStatus, ZHADevice
from .group import GroupMember, ZHAGroup
from .registries import GROUP_ENTITY_DOMAINS
from .store import async_get_registry
@ -185,17 +180,15 @@ class ZHAGateway:
delta_msg = "not known"
if zha_dev_entry and zha_dev_entry.last_seen is not None:
delta = round(time.time() - zha_dev_entry.last_seen)
if zha_device.is_mains_powered:
zha_device.available = delta < CONSIDER_UNAVAILABLE_MAINS
else:
zha_device.available = delta < CONSIDER_UNAVAILABLE_BATTERY
zha_device.available = delta < zha_device.consider_unavailable_time
delta_msg = f"{str(timedelta(seconds=delta))} ago"
_LOGGER.debug(
"[%s](%s) restored as '%s', last seen: %s",
"[%s](%s) restored as '%s', last seen: %s, consider_unavailable_time: %s seconds",
zha_device.nwk,
zha_device.name,
"available" if zha_device.available else "unavailable",
delta_msg,
zha_device.consider_unavailable_time,
)
# update the last seen time for devices every 10 minutes to avoid thrashing
# writes and shutdown issues where storage isn't updated

View File

@ -33,7 +33,9 @@
"zha_options": {
"title": "Global Options",
"enable_identify_on_join": "Enable identify effect when devices join the network",
"default_light_transition": "Default light transition time (seconds)"
"default_light_transition": "Default light transition time (seconds)",
"consider_unavailable_mains": "Consider mains powered devices unavailable after (seconds)",
"consider_unavailable_battery": "Consider battery powered devices unavailable after (seconds)"
},
"zha_alarm_options": {
"title": "Alarm Control Panel Options",

View File

@ -43,6 +43,8 @@
"zha_options": {
"default_light_transition": "Default light transition time (seconds)",
"enable_identify_on_join": "Enable identify effect when devices join the network",
"consider_unavailable_mains": "Consider mains powered devices unavailable after (seconds)",
"consider_unavailable_battery": "Consider battery powered devices unavailable after (seconds)",
"title": "Global Options"
}
},

View File

@ -8,7 +8,10 @@ import pytest
import zigpy.profiles.zha
import zigpy.zcl.clusters.general as general
import homeassistant.components.zha.core.device as zha_core_device
from homeassistant.components.zha.core.const import (
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS,
)
from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE
import homeassistant.helpers.device_registry as dr
import homeassistant.util.dt as dt_util
@ -117,13 +120,13 @@ async def test_check_available_success(
basic_ch.read_attributes.reset_mock()
device_with_basic_channel.last_seen = None
assert zha_device.available is True
_send_time_changed(hass, zha_core_device.CONSIDER_UNAVAILABLE_MAINS + 2)
_send_time_changed(hass, zha_device.consider_unavailable_time + 2)
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.CONSIDER_UNAVAILABLE_MAINS - 2
time.time() - zha_device.consider_unavailable_time - 2
)
_seens = [time.time(), device_with_basic_channel.last_seen]
@ -172,7 +175,7 @@ async def test_check_available_unsuccessful(
assert basic_ch.read_attributes.await_count == 0
device_with_basic_channel.last_seen = (
time.time() - zha_core_device.CONSIDER_UNAVAILABLE_MAINS - 2
time.time() - zha_device.consider_unavailable_time - 2
)
# unsuccessfuly ping zigpy device, but zha_device is still available
@ -213,7 +216,7 @@ async def test_check_available_no_basic_channel(
assert zha_device.available is True
device_without_basic_channel.last_seen = (
time.time() - zha_core_device.CONSIDER_UNAVAILABLE_BATTERY - 2
time.time() - zha_device.consider_unavailable_time - 2
)
assert "does not have a mandatory basic cluster" not in caplog.text
@ -246,38 +249,38 @@ async def test_ota_sw_version(hass, ota_zha_device):
("zigpy_device", 0, True),
(
"zigpy_device",
zha_core_device.CONSIDER_UNAVAILABLE_MAINS + 2,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS + 2,
True,
),
(
"zigpy_device",
zha_core_device.CONSIDER_UNAVAILABLE_BATTERY - 2,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY - 2,
True,
),
(
"zigpy_device",
zha_core_device.CONSIDER_UNAVAILABLE_BATTERY + 2,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY + 2,
False,
),
("zigpy_device_mains", 0, True),
(
"zigpy_device_mains",
zha_core_device.CONSIDER_UNAVAILABLE_MAINS - 2,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS - 2,
True,
),
(
"zigpy_device_mains",
zha_core_device.CONSIDER_UNAVAILABLE_MAINS + 2,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS + 2,
False,
),
(
"zigpy_device_mains",
zha_core_device.CONSIDER_UNAVAILABLE_BATTERY - 2,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY - 2,
False,
),
(
"zigpy_device_mains",
zha_core_device.CONSIDER_UNAVAILABLE_BATTERY + 2,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY + 2,
False,
),
),

View File

@ -7,7 +7,6 @@ import zigpy.profiles.zha
import zigpy.zcl.clusters.general as general
import homeassistant.components.automation as automation
import homeassistant.components.zha.core.device as zha_core_device
from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
@ -252,9 +251,7 @@ async def test_device_offline_fires(
await hass.async_block_till_done()
assert zha_device.available is True
zigpy_device.last_seen = (
time.time() - zha_core_device.CONSIDER_UNAVAILABLE_BATTERY - 2
)
zigpy_device.last_seen = time.time() - zha_device.consider_unavailable_time - 2
# there are 3 checkins to perform before marking the device unavailable
future = dt_util.utcnow() + timedelta(seconds=90)
@ -266,7 +263,7 @@ async def test_device_offline_fires(
await hass.async_block_till_done()
future = dt_util.utcnow() + timedelta(
seconds=zha_core_device.CONSIDER_UNAVAILABLE_BATTERY + 100
seconds=zha_device.consider_unavailable_time + 100
)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()