diff --git a/homeassistant/components/tado/manifest.json b/homeassistant/components/tado/manifest.json index eba13d469f3..b252a396689 100644 --- a/homeassistant/components/tado/manifest.json +++ b/homeassistant/components/tado/manifest.json @@ -14,5 +14,5 @@ }, "iot_class": "cloud_polling", "loggers": ["PyTado"], - "requirements": ["python-tado==0.18.11"] + "requirements": ["python-tado==0.18.14"] } diff --git a/requirements_all.txt b/requirements_all.txt index fbe5bd68f22..2ff0f9df162 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2491,7 +2491,7 @@ python-snoo==0.6.5 python-songpal==0.16.2 # homeassistant.components.tado -python-tado==0.18.11 +python-tado==0.18.14 # homeassistant.components.technove python-technove==2.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 022214afd9d..12869598f9b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2025,7 +2025,7 @@ python-snoo==0.6.5 python-songpal==0.16.2 # homeassistant.components.tado -python-tado==0.18.11 +python-tado==0.18.14 # homeassistant.components.technove python-technove==2.0.0 diff --git a/tests/components/tado/test_init.py b/tests/components/tado/test_init.py index 2f2ccacf3c0..10acd8eef59 100644 --- a/tests/components/tado/test_init.py +++ b/tests/components/tado/test_init.py @@ -1,7 +1,13 @@ """Test the Tado integration.""" +import asyncio +import threading +import time +from unittest.mock import patch + +from PyTado.http import Http + from homeassistant.components.tado import DOMAIN -from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant @@ -24,7 +30,37 @@ async def test_v1_migration(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert entry.version == 2 assert CONF_USERNAME not in entry.data - assert CONF_PASSWORD not in entry.data - assert entry.state is ConfigEntryState.SETUP_ERROR - assert len(hass.config_entries.flow.async_progress()) == 1 + +async def test_refresh_token_threading_lock(hass: HomeAssistant) -> None: + """Test that threading.Lock in Http._refresh_token serializes concurrent calls.""" + + timestamps: list[tuple[str, float]] = [] + lock = threading.Lock() + + def fake_refresh_token(*args, **kwargs) -> bool: + """Simulate the refresh token process with a threading lock.""" + with lock: + timestamps.append(("start", time.monotonic())) + time.sleep(0.2) + timestamps.append(("end", time.monotonic())) + return True + + with ( + patch("PyTado.http.Http._refresh_token", side_effect=fake_refresh_token), + patch("PyTado.http.Http.__init__", return_value=None), + ): + http_instance = Http() + + # Run two concurrent refresh token calls, should do the trick + await asyncio.gather( + hass.async_add_executor_job(http_instance._refresh_token), + hass.async_add_executor_job(http_instance._refresh_token), + ) + + end1 = timestamps[1][1] + start2 = timestamps[2][1] + + assert start2 >= end1, ( + f"Second refresh started before first ended: start2={start2}, end1={end1}." + )