diff --git a/homeassistant/components/isy994/strings.json b/homeassistant/components/isy994/strings.json index 08092c2482c..eaba5f7a9da 100644 --- a/homeassistant/components/isy994/strings.json +++ b/homeassistant/components/isy994/strings.json @@ -36,5 +36,13 @@ } } } + }, + "system_health": { + "info": { + "host_reachable": "Host Reachable", + "device_connected": "ISY Connected", + "last_heartbeat": "Last Heartbeat Time", + "websocket_status": "Event Socket Status" + } } } diff --git a/homeassistant/components/isy994/system_health.py b/homeassistant/components/isy994/system_health.py new file mode 100644 index 00000000000..f550b8ed07b --- /dev/null +++ b/homeassistant/components/isy994/system_health.py @@ -0,0 +1,36 @@ +"""Provide info to system health.""" +from pyisy import ISY + +from homeassistant.components import system_health +from homeassistant.const import CONF_HOST +from homeassistant.core import HomeAssistant, callback + +from .const import DOMAIN, ISY994_ISY, ISY_URL_POSTFIX + + +@callback +def async_register( + hass: HomeAssistant, register: system_health.SystemHealthRegistration +) -> None: + """Register system health callbacks.""" + register.async_register_info(system_health_info) + + +async def system_health_info(hass): + """Get info for the info page.""" + + health_info = {} + config_entry_id = next( + iter(hass.data[DOMAIN]) + ) # Only first ISY is supported for now + isy: ISY = hass.data[DOMAIN][config_entry_id][ISY994_ISY] + + entry = hass.config_entries.async_get_entry(config_entry_id) + health_info["host_reachable"] = await system_health.async_check_can_reach_url( + hass, f"{entry.data[CONF_HOST]}{ISY_URL_POSTFIX}" + ) + health_info["device_connected"] = isy.connected + health_info["last_heartbeat"] = isy.websocket.last_heartbeat + health_info["websocket_status"] = isy.websocket.status + + return health_info diff --git a/homeassistant/components/isy994/translations/en.json b/homeassistant/components/isy994/translations/en.json index 724160c6d3d..e413affaa50 100644 --- a/homeassistant/components/isy994/translations/en.json +++ b/homeassistant/components/isy994/translations/en.json @@ -36,5 +36,13 @@ "title": "ISY994 Options" } } - } + }, + "system_health": { + "info": { + "host_reachable": "Host Reachable", + "device_connected": "Connected to Device", + "last_heartbeat": "Last Heartbeat Time", + "websocket_status": "Event Socket Status" + } + } } \ No newline at end of file diff --git a/tests/components/isy994/test_system_health.py b/tests/components/isy994/test_system_health.py new file mode 100644 index 00000000000..63810b10464 --- /dev/null +++ b/tests/components/isy994/test_system_health.py @@ -0,0 +1,86 @@ +"""Test ISY994 system health.""" +import asyncio +from unittest.mock import Mock + +from aiohttp import ClientError + +from homeassistant.components.isy994.const import DOMAIN, ISY994_ISY, ISY_URL_POSTFIX +from homeassistant.const import CONF_HOST +from homeassistant.setup import async_setup_component + +from .test_config_flow import MOCK_HOSTNAME, MOCK_UUID + +from tests.common import MockConfigEntry, get_system_health_info + +MOCK_ENTRY_ID = "cad4af20b811990e757588519917d6af" +MOCK_CONNECTED = "connected" +MOCK_HEARTBEAT = "2021-05-01T00:00:00.000000" + + +async def test_system_health(hass, aioclient_mock): + """Test system health.""" + aioclient_mock.get(f"http://{MOCK_HOSTNAME}{ISY_URL_POSTFIX}", text="") + + hass.config.components.add(DOMAIN) + assert await async_setup_component(hass, "system_health", {}) + + MockConfigEntry( + domain=DOMAIN, + entry_id=MOCK_ENTRY_ID, + data={CONF_HOST: f"http://{MOCK_HOSTNAME}"}, + unique_id=MOCK_UUID, + ).add_to_hass(hass) + + hass.data[DOMAIN] = {} + hass.data[DOMAIN][MOCK_ENTRY_ID] = {} + hass.data[DOMAIN][MOCK_ENTRY_ID][ISY994_ISY] = Mock( + connected=True, + websocket=Mock( + last_heartbeat=MOCK_HEARTBEAT, + status=MOCK_CONNECTED, + ), + ) + + info = await get_system_health_info(hass, DOMAIN) + + for key, val in info.items(): + if asyncio.iscoroutine(val): + info[key] = await val + + assert info["host_reachable"] == "ok" + assert info["device_connected"] + assert info["last_heartbeat"] == MOCK_HEARTBEAT + assert info["websocket_status"] == MOCK_CONNECTED + + +async def test_system_health_failed_connect(hass, aioclient_mock): + """Test system health.""" + aioclient_mock.get(f"http://{MOCK_HOSTNAME}{ISY_URL_POSTFIX}", exc=ClientError) + + hass.config.components.add(DOMAIN) + assert await async_setup_component(hass, "system_health", {}) + + MockConfigEntry( + domain=DOMAIN, + entry_id=MOCK_ENTRY_ID, + data={CONF_HOST: f"http://{MOCK_HOSTNAME}"}, + unique_id=MOCK_UUID, + ).add_to_hass(hass) + + hass.data[DOMAIN] = {} + hass.data[DOMAIN][MOCK_ENTRY_ID] = {} + hass.data[DOMAIN][MOCK_ENTRY_ID][ISY994_ISY] = Mock( + connected=True, + websocket=Mock( + last_heartbeat=MOCK_HEARTBEAT, + status=MOCK_CONNECTED, + ), + ) + + info = await get_system_health_info(hass, DOMAIN) + + for key, val in info.items(): + if asyncio.iscoroutine(val): + info[key] = await val + + assert info["host_reachable"] == {"error": "unreachable", "type": "failed"}