mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 22:38:02 -04:00
Cancel otp sensor update timer when the entity is removed (#183320)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""Support for One-Time Password (OTP)."""
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
from typing import override
|
||||
|
||||
@@ -39,6 +40,7 @@ class TOTPSensor(SensorEntity):
|
||||
_attr_should_poll = False
|
||||
_attr_native_value: StateType = None
|
||||
_next_expiration: float | None = None
|
||||
_update_timer: asyncio.TimerHandle | None = None
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
@@ -58,6 +60,13 @@ class TOTPSensor(SensorEntity):
|
||||
"""Handle when an entity is about to be added to Home Assistant."""
|
||||
self._call_loop()
|
||||
|
||||
@override
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Cancel the update timer when the entity is removed."""
|
||||
if self._update_timer:
|
||||
self._update_timer.cancel()
|
||||
self._update_timer = None
|
||||
|
||||
@callback
|
||||
def _call_loop(self) -> None:
|
||||
self._attr_native_value = self._otp.now()
|
||||
@@ -66,4 +75,6 @@ class TOTPSensor(SensorEntity):
|
||||
# Update must occur at even TIME_STEP, e.g. 12:00:00, 12:00:30,
|
||||
# 12:01:00, etc. in order to have synced time (see RFC6238)
|
||||
self._next_expiration = TIME_STEP - (time.time() % TIME_STEP)
|
||||
self.hass.loop.call_later(self._next_expiration, self._call_loop)
|
||||
self._update_timer = self.hass.loop.call_later(
|
||||
self._next_expiration, self._call_loop
|
||||
)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
"""Test the One-Time Password (OTP) init."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def test_entry_setup_unload(
|
||||
@@ -21,3 +25,23 @@ async def test_entry_setup_unload(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert otp_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_update_timer_cancelled_on_unload(
|
||||
hass: HomeAssistant,
|
||||
otp_config_entry: MockConfigEntry,
|
||||
mock_pyotp: MagicMock,
|
||||
) -> None:
|
||||
"""Test the sensor stops updating after the entry is unloaded."""
|
||||
otp_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(otp_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.config_entries.async_unload(otp_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
mock_pyotp.TOTP().now.reset_mock()
|
||||
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=1))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_pyotp.TOTP().now.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user