Skip to content

Commit 096468b

Browse files
authored
airq: add more verbose debug logging (#138192)
1 parent 3659fa4 commit 096468b

File tree

4 files changed

+148
-3
lines changed

4 files changed

+148
-3
lines changed

homeassistant/components/airq/config_flow.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ async def async_step_user(
8383
await self.async_set_unique_id(device_info["id"])
8484
self._abort_if_unique_id_configured()
8585

86+
_LOGGER.debug("Creating an entry for %s", device_info["name"])
8687
return self.async_create_entry(title=device_info["name"], data=user_input)
8788

8889
return self.async_show_form(

homeassistant/components/airq/coordinator.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from datetime import timedelta
66
import logging
77

8-
from aioairq import AirQ
8+
from aioairq.core import AirQ, identify_warming_up_sensors
99

1010
from homeassistant.config_entries import ConfigEntry
1111
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD
@@ -55,6 +55,9 @@ def __init__(
5555
async def _async_update_data(self) -> dict:
5656
"""Fetch the data from the device."""
5757
if "name" not in self.device_info:
58+
_LOGGER.debug(
59+
"'name' not found in AirQCoordinator.device_info, fetching from the device"
60+
)
5861
info = await self.airq.fetch_device_info()
5962
self.device_info.update(
6063
DeviceInfo(
@@ -64,7 +67,16 @@ async def _async_update_data(self) -> dict:
6467
hw_version=info["hw_version"],
6568
)
6669
)
67-
return await self.airq.get_latest_data( # type: ignore[no-any-return]
70+
_LOGGER.debug(
71+
"Updated AirQCoordinator.device_info for 'name' %s",
72+
self.device_info.get("name"),
73+
)
74+
data: dict = await self.airq.get_latest_data(
6875
return_average=self.return_average,
6976
clip_negative_values=self.clip_negative,
7077
)
78+
if warming_up_sensors := identify_warming_up_sensors(data):
79+
_LOGGER.debug(
80+
"Following sensors are still warming up: %s", warming_up_sensors
81+
)
82+
return data

tests/components/airq/test_config_flow.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Test the air-Q config flow."""
22

3+
import logging
34
from unittest.mock import patch
45

56
from aioairq import DeviceInfo, InvalidAuth
@@ -37,8 +38,9 @@
3738
}
3839

3940

40-
async def test_form(hass: HomeAssistant) -> None:
41+
async def test_form(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) -> None:
4142
"""Test we get the form."""
43+
caplog.set_level(logging.DEBUG)
4244
result = await hass.config_entries.flow.async_init(
4345
DOMAIN, context={"source": config_entries.SOURCE_USER}
4446
)
@@ -54,6 +56,7 @@ async def test_form(hass: HomeAssistant) -> None:
5456
TEST_USER_DATA,
5557
)
5658
await hass.async_block_till_done()
59+
assert f"Creating an entry for {TEST_DEVICE_INFO['name']}" in caplog.text
5760

5861
assert result2["type"] is FlowResultType.CREATE_ENTRY
5962
assert result2["title"] == TEST_DEVICE_INFO["name"]
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""Test the air-Q coordinator."""
2+
3+
import logging
4+
from unittest.mock import patch
5+
6+
from aioairq import DeviceInfo as AirQDeviceInfo
7+
import pytest
8+
9+
from homeassistant.components.airq import AirQCoordinator
10+
from homeassistant.components.airq.const import DOMAIN
11+
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD
12+
from homeassistant.core import HomeAssistant
13+
from homeassistant.helpers.device_registry import DeviceInfo
14+
15+
from tests.common import MockConfigEntry
16+
17+
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
18+
MOCKED_ENTRY = MockConfigEntry(
19+
domain=DOMAIN,
20+
data={
21+
CONF_IP_ADDRESS: "192.168.0.0",
22+
CONF_PASSWORD: "password",
23+
},
24+
unique_id="123-456",
25+
)
26+
27+
TEST_DEVICE_INFO = AirQDeviceInfo(
28+
id="id",
29+
name="name",
30+
model="model",
31+
sw_version="sw",
32+
hw_version="hw",
33+
)
34+
TEST_DEVICE_DATA = {"co2": 500.0, "Status": "OK"}
35+
STATUS_WARMUP = {
36+
"co": "co sensor still in warm up phase; waiting time = 18 s",
37+
"tvoc": "tvoc sensor still in warm up phase; waiting time = 18 s",
38+
"so2": "so2 sensor still in warm up phase; waiting time = 17 s",
39+
}
40+
41+
42+
async def test_logging_in_coordinator_first_update_data(
43+
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
44+
) -> None:
45+
"""Test that the first AirQCoordinator._async_update_data call logs necessary setup.
46+
47+
The fields of AirQCoordinator.device_info that are specific to the device are only
48+
populated upon the first call to AirQCoordinator._async_update_data. The one field
49+
which is actually necessary is 'name', and its absence is checked and logged,
50+
as well as its being set.
51+
"""
52+
caplog.set_level(logging.DEBUG)
53+
coordinator = AirQCoordinator(hass, MOCKED_ENTRY)
54+
55+
# check that the name _is_ missing
56+
assert "name" not in coordinator.device_info
57+
58+
# First call: fetch missing device info
59+
with (
60+
patch("aioairq.AirQ.fetch_device_info", return_value=TEST_DEVICE_INFO),
61+
patch("aioairq.AirQ.get_latest_data", return_value=TEST_DEVICE_DATA),
62+
):
63+
await coordinator._async_update_data()
64+
65+
# check that the missing name is logged...
66+
assert (
67+
"'name' not found in AirQCoordinator.device_info, fetching from the device"
68+
in caplog.text
69+
)
70+
# ...and fixed
71+
assert coordinator.device_info.get("name") == TEST_DEVICE_INFO["name"]
72+
assert (
73+
f"Updated AirQCoordinator.device_info for 'name' {TEST_DEVICE_INFO['name']}"
74+
in caplog.text
75+
)
76+
77+
# Also that no warming up sensors is found as none are mocked
78+
assert "Following sensors are still warming up" not in caplog.text
79+
80+
81+
async def test_logging_in_coordinator_subsequent_update_data(
82+
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
83+
) -> None:
84+
"""Test that the second AirQCoordinator._async_update_data call has nothing to log.
85+
86+
The second call is emulated by setting up AirQCoordinator.device_info correctly,
87+
instead of actually calling the _async_update_data, which would populate the log
88+
with the messages we want to see not being repeated.
89+
"""
90+
caplog.set_level(logging.DEBUG)
91+
coordinator = AirQCoordinator(hass, MOCKED_ENTRY)
92+
coordinator.device_info.update(DeviceInfo(**TEST_DEVICE_INFO))
93+
94+
with (
95+
patch("aioairq.AirQ.fetch_device_info", return_value=TEST_DEVICE_INFO),
96+
patch("aioairq.AirQ.get_latest_data", return_value=TEST_DEVICE_DATA),
97+
):
98+
await coordinator._async_update_data()
99+
# check that the name _is not_ missing
100+
assert "name" in coordinator.device_info
101+
# and that nothing of the kind is logged
102+
assert (
103+
"'name' not found in AirQCoordinator.device_info, fetching from the device"
104+
not in caplog.text
105+
)
106+
assert (
107+
f"Updated AirQCoordinator.device_info for 'name' {TEST_DEVICE_INFO['name']}"
108+
not in caplog.text
109+
)
110+
111+
112+
async def test_logging_when_warming_up_sensor_present(
113+
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
114+
) -> None:
115+
"""Test that warming up sensors are logged."""
116+
caplog.set_level(logging.DEBUG)
117+
coordinator = AirQCoordinator(hass, MOCKED_ENTRY)
118+
with (
119+
patch("aioairq.AirQ.fetch_device_info", return_value=TEST_DEVICE_INFO),
120+
patch(
121+
"aioairq.AirQ.get_latest_data",
122+
return_value=TEST_DEVICE_DATA | {"Status": STATUS_WARMUP},
123+
),
124+
):
125+
await coordinator._async_update_data()
126+
assert (
127+
f"Following sensors are still warming up: {set(STATUS_WARMUP.keys())}"
128+
in caplog.text
129+
)

0 commit comments

Comments
 (0)