"""Diagnostic sensors.""" from __future__ import annotations from datetime import timedelta from typing import Any from freezegun.api import FrozenDateTimeFactory from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.util import dt as dt_util from pytest_homeassistant_custom_component.common import MockConfigEntry, async_fire_time_changed from tests.helpers import entity_id_for DISABLED_KEYS = ("free_heap", "last_ota_error", "boot_time") async def _enable_all( hass: HomeAssistant, entry: MockConfigEntry, freezer: FrozenDateTimeFactory ) -> None: registry = er.async_get(hass) for key in DISABLED_KEYS: registry.async_update_entity(entity_id_for(hass, "sensor", key), disabled_by=None) await hass.config_entries.async_reload(entry.entry_id) await hass.async_block_till_done() # HA 2026.9: registry.async_update_entity(disabled_by=None) also arms the config entry's # own debounced reload (config_entries.RELOAD_AFTER_UPDATE_DELAY = 30 s), independent of # the explicit reload above. Left alone, that second reload fires mid-test - typically # right on top of the first _poll() below - and silently replaces the boot-time entity, # defeating the jitter-tolerance assertion. Drain it here so the entities are settled # before the test takes its "first" reading. freezer.tick(timedelta(seconds=31)) async_fire_time_changed(hass) await hass.async_block_till_done() async def _poll(hass: HomeAssistant, freezer: FrozenDateTimeFactory, seconds: int) -> None: freezer.tick(timedelta(seconds=seconds)) async_fire_time_changed(hass) await hass.async_block_till_done() async def test_signal_and_ip_enabled_others_disabled_by_default( hass: HomeAssistant, init_integration ) -> None: registry = er.async_get(hass) assert hass.states.get(entity_id_for(hass, "sensor", "rssi")).state == "-61" assert hass.states.get(entity_id_for(hass, "sensor", "ip_address")).state == "192.168.2.22" for key in DISABLED_KEYS: entry = registry.async_get(entity_id_for(hass, "sensor", key)) assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION, key async def test_diagnostic_values( hass: HomeAssistant, init_integration: MockConfigEntry, freezer ) -> None: await _enable_all(hass, init_integration, freezer) assert hass.states.get(entity_id_for(hass, "sensor", "ip_address")).state == "192.168.2.22" assert hass.states.get(entity_id_for(hass, "sensor", "free_heap")).state == "119872" assert hass.states.get(entity_id_for(hass, "sensor", "last_ota_error")).state == "unknown" expected_boot = dt_util.utcnow() - timedelta(seconds=129832) boot = dt_util.parse_datetime(hass.states.get(entity_id_for(hass, "sensor", "boot_time")).state) assert abs((boot - expected_boot).total_seconds()) < 2 async def test_boot_time_ignores_jitter_but_follows_reboots( hass: HomeAssistant, init_integration: MockConfigEntry, lamp_info: dict[str, Any], freezer: FrozenDateTimeFactory, ) -> None: await _enable_all(hass, init_integration, freezer) entity_id = entity_id_for(hass, "sensor", "boot_time") first = hass.states.get(entity_id).state lamp_info["uptime"] += 31 + 20 # clock drift of 20 s: below the 60 s threshold await _poll(hass, freezer, 31) assert hass.states.get(entity_id).state == first lamp_info["uptime"] = 12 # the lamp rebooted await _poll(hass, freezer, 31) rebooted = dt_util.parse_datetime(hass.states.get(entity_id).state) assert abs((rebooted - (dt_util.utcnow() - timedelta(seconds=12))).total_seconds()) < 2