mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Fix Schlage removed locks (#123627)
* Fix bugs when a lock is no longer returned by the API * Changes requested during review * Only mark unavailable if lock is not present * Remove stale comment * Remove over-judicious nullability checks * Remove another unnecessary null check
This commit is contained in:
parent
634582eab7
commit
5405279273
@ -42,5 +42,4 @@ class SchlageEntity(CoordinatorEntity[SchlageDataUpdateCoordinator]):
|
|||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return if entity is available."""
|
"""Return if entity is available."""
|
||||||
# When is_locked is None the lock is unavailable.
|
return super().available and self.device_id in self.coordinator.data.locks
|
||||||
return super().available and self._lock.is_locked is not None
|
|
||||||
|
@ -42,8 +42,9 @@ class SchlageLockEntity(SchlageEntity, LockEntity):
|
|||||||
@callback
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
self._update_attrs()
|
if self.device_id in self.coordinator.data.locks:
|
||||||
return super()._handle_coordinator_update()
|
self._update_attrs()
|
||||||
|
super()._handle_coordinator_update()
|
||||||
|
|
||||||
def _update_attrs(self) -> None:
|
def _update_attrs(self) -> None:
|
||||||
"""Update our internal state attributes."""
|
"""Update our internal state attributes."""
|
||||||
|
@ -64,5 +64,6 @@ class SchlageBatterySensor(SchlageEntity, SensorEntity):
|
|||||||
@callback
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
self._attr_native_value = getattr(self._lock, self.entity_description.key)
|
if self.device_id in self.coordinator.data.locks:
|
||||||
return super()._handle_coordinator_update()
|
self._attr_native_value = getattr(self._lock, self.entity_description.key)
|
||||||
|
super()._handle_coordinator_update()
|
||||||
|
@ -3,37 +3,56 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
from pyschlage.exceptions import UnknownError
|
from pyschlage.exceptions import UnknownError
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import STATE_ON, STATE_UNAVAILABLE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.util.dt import utcnow
|
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
async def test_keypad_disabled_binary_sensor(
|
async def test_keypad_disabled_binary_sensor(
|
||||||
hass: HomeAssistant, mock_lock: Mock, mock_added_config_entry: ConfigEntry
|
hass: HomeAssistant,
|
||||||
|
mock_schlage: Mock,
|
||||||
|
mock_lock: Mock,
|
||||||
|
mock_added_config_entry: ConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the keypad_disabled binary_sensor."""
|
"""Test the keypad_disabled binary_sensor."""
|
||||||
mock_lock.keypad_disabled.reset_mock()
|
mock_lock.keypad_disabled.reset_mock()
|
||||||
mock_lock.keypad_disabled.return_value = True
|
mock_lock.keypad_disabled.return_value = True
|
||||||
|
|
||||||
# Make the coordinator refresh data.
|
# Make the coordinator refresh data.
|
||||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=31))
|
freezer.tick(timedelta(seconds=30))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
|
||||||
keypad = hass.states.get("binary_sensor.vault_door_keypad_disabled")
|
keypad = hass.states.get("binary_sensor.vault_door_keypad_disabled")
|
||||||
assert keypad is not None
|
assert keypad is not None
|
||||||
assert keypad.state == "on"
|
assert keypad.state == STATE_ON
|
||||||
assert keypad.attributes["device_class"] == BinarySensorDeviceClass.PROBLEM
|
assert keypad.attributes["device_class"] == BinarySensorDeviceClass.PROBLEM
|
||||||
|
|
||||||
mock_lock.keypad_disabled.assert_called_once_with([])
|
mock_lock.keypad_disabled.assert_called_once_with([])
|
||||||
|
|
||||||
|
mock_schlage.locks.return_value = []
|
||||||
|
# Make the coordinator refresh data.
|
||||||
|
freezer.tick(timedelta(seconds=30))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
keypad = hass.states.get("binary_sensor.vault_door_keypad_disabled")
|
||||||
|
assert keypad is not None
|
||||||
|
assert keypad.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
async def test_keypad_disabled_binary_sensor_use_previous_logs_on_failure(
|
async def test_keypad_disabled_binary_sensor_use_previous_logs_on_failure(
|
||||||
hass: HomeAssistant, mock_lock: Mock, mock_added_config_entry: ConfigEntry
|
hass: HomeAssistant,
|
||||||
|
mock_schlage: Mock,
|
||||||
|
mock_lock: Mock,
|
||||||
|
mock_added_config_entry: ConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the keypad_disabled binary_sensor."""
|
"""Test the keypad_disabled binary_sensor."""
|
||||||
mock_lock.keypad_disabled.reset_mock()
|
mock_lock.keypad_disabled.reset_mock()
|
||||||
@ -42,12 +61,13 @@ async def test_keypad_disabled_binary_sensor_use_previous_logs_on_failure(
|
|||||||
mock_lock.logs.side_effect = UnknownError("Cannot load logs")
|
mock_lock.logs.side_effect = UnknownError("Cannot load logs")
|
||||||
|
|
||||||
# Make the coordinator refresh data.
|
# Make the coordinator refresh data.
|
||||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=31))
|
freezer.tick(timedelta(seconds=30))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
|
||||||
keypad = hass.states.get("binary_sensor.vault_door_keypad_disabled")
|
keypad = hass.states.get("binary_sensor.vault_door_keypad_disabled")
|
||||||
assert keypad is not None
|
assert keypad is not None
|
||||||
assert keypad.state == "on"
|
assert keypad.state == STATE_ON
|
||||||
assert keypad.attributes["device_class"] == BinarySensorDeviceClass.PROBLEM
|
assert keypad.attributes["device_class"] == BinarySensorDeviceClass.PROBLEM
|
||||||
|
|
||||||
mock_lock.keypad_disabled.assert_called_once_with([])
|
mock_lock.keypad_disabled.assert_called_once_with([])
|
||||||
|
@ -3,12 +3,20 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
|
||||||
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_LOCK, SERVICE_UNLOCK
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
SERVICE_LOCK,
|
||||||
|
SERVICE_UNLOCK,
|
||||||
|
STATE_JAMMED,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
STATE_UNLOCKED,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.util.dt import utcnow
|
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
@ -26,6 +34,40 @@ async def test_lock_device_registry(
|
|||||||
assert device.manufacturer == "Schlage"
|
assert device.manufacturer == "Schlage"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_lock_attributes(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_added_config_entry: ConfigEntry,
|
||||||
|
mock_schlage: Mock,
|
||||||
|
mock_lock: Mock,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test lock attributes."""
|
||||||
|
lock = hass.states.get("lock.vault_door")
|
||||||
|
assert lock is not None
|
||||||
|
assert lock.state == STATE_UNLOCKED
|
||||||
|
assert lock.attributes["changed_by"] == "thumbturn"
|
||||||
|
|
||||||
|
mock_lock.is_locked = False
|
||||||
|
mock_lock.is_jammed = True
|
||||||
|
# Make the coordinator refresh data.
|
||||||
|
freezer.tick(timedelta(seconds=30))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
lock = hass.states.get("lock.vault_door")
|
||||||
|
assert lock is not None
|
||||||
|
assert lock.state == STATE_JAMMED
|
||||||
|
|
||||||
|
mock_schlage.locks.return_value = []
|
||||||
|
# Make the coordinator refresh data.
|
||||||
|
freezer.tick(timedelta(seconds=30))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
lock = hass.states.get("lock.vault_door")
|
||||||
|
assert lock is not None
|
||||||
|
assert lock.state == STATE_UNAVAILABLE
|
||||||
|
assert "changed_by" not in lock.attributes
|
||||||
|
|
||||||
|
|
||||||
async def test_lock_services(
|
async def test_lock_services(
|
||||||
hass: HomeAssistant, mock_lock: Mock, mock_added_config_entry: ConfigEntry
|
hass: HomeAssistant, mock_lock: Mock, mock_added_config_entry: ConfigEntry
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -52,14 +94,18 @@ async def test_lock_services(
|
|||||||
|
|
||||||
|
|
||||||
async def test_changed_by(
|
async def test_changed_by(
|
||||||
hass: HomeAssistant, mock_lock: Mock, mock_added_config_entry: ConfigEntry
|
hass: HomeAssistant,
|
||||||
|
mock_lock: Mock,
|
||||||
|
mock_added_config_entry: ConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test population of the changed_by attribute."""
|
"""Test population of the changed_by attribute."""
|
||||||
mock_lock.last_changed_by.reset_mock()
|
mock_lock.last_changed_by.reset_mock()
|
||||||
mock_lock.last_changed_by.return_value = "access code - foo"
|
mock_lock.last_changed_by.return_value = "access code - foo"
|
||||||
|
|
||||||
# Make the coordinator refresh data.
|
# Make the coordinator refresh data.
|
||||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=31))
|
freezer.tick(timedelta(seconds=30))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
mock_lock.last_changed_by.assert_called_once_with()
|
mock_lock.last_changed_by.assert_called_once_with()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user