From 7fe78fe9e43c7ce325ccb867529d53d76ed0ca6c Mon Sep 17 00:00:00 2001 From: Olen Date: Wed, 13 Sep 2023 13:09:57 +0200 Subject: [PATCH] Add diagnostics to Twinkly (#100146) --- CODEOWNERS | 4 +- .../components/twinkly/diagnostics.py | 40 ++++++++++++++ .../components/twinkly/manifest.json | 2 +- tests/components/twinkly/__init__.py | 7 +-- tests/components/twinkly/conftest.py | 54 +++++++++++++++++++ .../twinkly/snapshots/test_diagnostics.ambr | 43 +++++++++++++++ tests/components/twinkly/test_config_flow.py | 12 ++--- tests/components/twinkly/test_diagnostics.py | 28 ++++++++++ tests/components/twinkly/test_light.py | 10 ++-- 9 files changed, 183 insertions(+), 17 deletions(-) create mode 100644 homeassistant/components/twinkly/diagnostics.py create mode 100644 tests/components/twinkly/conftest.py create mode 100644 tests/components/twinkly/snapshots/test_diagnostics.ambr create mode 100644 tests/components/twinkly/test_diagnostics.py diff --git a/CODEOWNERS b/CODEOWNERS index bba1c2debbf..3aefaabb50b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1324,8 +1324,8 @@ build.json @home-assistant/supervisor /tests/components/tuya/ @Tuya @zlinoliver @frenck /homeassistant/components/twentemilieu/ @frenck /tests/components/twentemilieu/ @frenck -/homeassistant/components/twinkly/ @dr1rrb @Robbie1221 -/tests/components/twinkly/ @dr1rrb @Robbie1221 +/homeassistant/components/twinkly/ @dr1rrb @Robbie1221 @Olen +/tests/components/twinkly/ @dr1rrb @Robbie1221 @Olen /homeassistant/components/twitch/ @joostlek /tests/components/twitch/ @joostlek /homeassistant/components/ukraine_alarm/ @PaulAnnekov diff --git a/homeassistant/components/twinkly/diagnostics.py b/homeassistant/components/twinkly/diagnostics.py new file mode 100644 index 00000000000..06afba5782b --- /dev/null +++ b/homeassistant/components/twinkly/diagnostics.py @@ -0,0 +1,40 @@ +"""Diagnostics support for Twinkly.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_HOST, CONF_IP_ADDRESS, CONF_MAC +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from .const import DATA_DEVICE_INFO, DOMAIN + +TO_REDACT = [CONF_HOST, CONF_IP_ADDRESS, CONF_MAC] + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a Twinkly config entry.""" + attributes = None + state = None + entity_registry = er.async_get(hass) + + entity_id = entity_registry.async_get_entity_id( + LIGHT_DOMAIN, DOMAIN, str(entry.unique_id) + ) + if entity_id: + state = hass.states.get(entity_id) + if state: + attributes = state.attributes + return async_redact_data( + { + "entry": entry.as_dict(), + "device_info": hass.data[DOMAIN][entry.entry_id][DATA_DEVICE_INFO], + "attributes": attributes, + }, + TO_REDACT, + ) diff --git a/homeassistant/components/twinkly/manifest.json b/homeassistant/components/twinkly/manifest.json index 59deff915c3..c6ab0bab893 100644 --- a/homeassistant/components/twinkly/manifest.json +++ b/homeassistant/components/twinkly/manifest.json @@ -1,7 +1,7 @@ { "domain": "twinkly", "name": "Twinkly", - "codeowners": ["@dr1rrb", "@Robbie1221"], + "codeowners": ["@dr1rrb", "@Robbie1221", "@Olen"], "config_flow": true, "dhcp": [ { diff --git a/tests/components/twinkly/__init__.py b/tests/components/twinkly/__init__.py index 31d1eff2a61..0780bc0126f 100644 --- a/tests/components/twinkly/__init__.py +++ b/tests/components/twinkly/__init__.py @@ -1,6 +1,5 @@ """Constants and mock for the twkinly component tests.""" -from uuid import uuid4 from aiohttp.client_exceptions import ClientConnectionError @@ -8,6 +7,7 @@ from homeassistant.components.twinkly.const import DEV_NAME TEST_HOST = "test.twinkly.com" TEST_ID = "twinkly_test_device_id" +TEST_UID = "4c8fccf5-e08a-4173-92d5-49bf479252a2" TEST_NAME = "twinkly_test_device_name" TEST_NAME_ORIGINAL = "twinkly_test_original_device_name" # the original (deprecated) name stored in the conf TEST_MODEL = "twinkly_test_device_model" @@ -28,11 +28,12 @@ class ClientMock: self.mode = None self.version = "2.8.10" - self.id = str(uuid4()) + self.id = TEST_UID self.device_info = { "uuid": self.id, - "device_name": self.id, # we make sure that entity id is different for each test + "device_name": TEST_NAME, "product_code": TEST_MODEL, + "sw_version": self.version, } @property diff --git a/tests/components/twinkly/conftest.py b/tests/components/twinkly/conftest.py new file mode 100644 index 00000000000..5a689c31baa --- /dev/null +++ b/tests/components/twinkly/conftest.py @@ -0,0 +1,54 @@ +"""Configure tests for the Twinkly integration.""" +from collections.abc import Awaitable, Callable, Coroutine +from typing import Any +from unittest.mock import patch + +import pytest + +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from . import TEST_MODEL, TEST_NAME, TEST_UID, ClientMock + +from tests.common import MockConfigEntry + +ComponentSetup = Callable[[], Awaitable[ClientMock]] + +DOMAIN = "twinkly" +TITLE = "Twinkly" + + +@pytest.fixture(name="config_entry") +def mock_config_entry() -> MockConfigEntry: + """Create Twinkly entry in Home Assistant.""" + client = ClientMock() + return MockConfigEntry( + domain=DOMAIN, + title=TITLE, + unique_id=TEST_UID, + entry_id=TEST_UID, + data={ + "host": client.host, + "id": client.id, + "name": TEST_NAME, + "model": TEST_MODEL, + "device_name": TEST_NAME, + }, + ) + + +@pytest.fixture(name="setup_integration") +async def mock_setup_integration( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> Callable[[], Coroutine[Any, Any, ClientMock]]: + """Fixture for setting up the component.""" + config_entry.add_to_hass(hass) + + async def func() -> ClientMock: + mock = ClientMock() + with patch("homeassistant.components.twinkly.Twinkly", return_value=mock): + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + return mock + + return func diff --git a/tests/components/twinkly/snapshots/test_diagnostics.ambr b/tests/components/twinkly/snapshots/test_diagnostics.ambr new file mode 100644 index 00000000000..c5788444845 --- /dev/null +++ b/tests/components/twinkly/snapshots/test_diagnostics.ambr @@ -0,0 +1,43 @@ +# serializer version: 1 +# name: test_diagnostics + dict({ + 'attributes': dict({ + 'brightness': 26, + 'color_mode': 'brightness', + 'effect_list': list([ + ]), + 'friendly_name': 'twinkly_test_device_name', + 'icon': 'mdi:string-lights', + 'supported_color_modes': list([ + 'brightness', + ]), + 'supported_features': 4, + }), + 'device_info': dict({ + 'device_name': 'twinkly_test_device_name', + 'product_code': 'twinkly_test_device_model', + 'sw_version': '2.8.10', + 'uuid': '4c8fccf5-e08a-4173-92d5-49bf479252a2', + }), + 'entry': dict({ + 'data': dict({ + 'device_name': 'twinkly_test_device_name', + 'host': '**REDACTED**', + 'id': '4c8fccf5-e08a-4173-92d5-49bf479252a2', + 'model': 'twinkly_test_device_model', + 'name': 'twinkly_test_device_name', + }), + 'disabled_by': None, + 'domain': 'twinkly', + 'entry_id': '4c8fccf5-e08a-4173-92d5-49bf479252a2', + 'options': dict({ + }), + 'pref_disable_new_entities': False, + 'pref_disable_polling': False, + 'source': 'user', + 'title': 'Twinkly', + 'unique_id': '4c8fccf5-e08a-4173-92d5-49bf479252a2', + 'version': 1, + }), + }) +# --- diff --git a/tests/components/twinkly/test_config_flow.py b/tests/components/twinkly/test_config_flow.py index 1219130c197..2d335c69923 100644 --- a/tests/components/twinkly/test_config_flow.py +++ b/tests/components/twinkly/test_config_flow.py @@ -12,7 +12,7 @@ from homeassistant.components.twinkly.const import ( from homeassistant.const import CONF_MODEL from homeassistant.core import HomeAssistant -from . import TEST_MODEL, ClientMock +from . import TEST_MODEL, TEST_NAME, ClientMock from tests.common import MockConfigEntry @@ -60,11 +60,11 @@ async def test_success_flow(hass: HomeAssistant) -> None: ) assert result["type"] == "create_entry" - assert result["title"] == client.id + assert result["title"] == TEST_NAME assert result["data"] == { CONF_HOST: "dummy", CONF_ID: client.id, - CONF_NAME: client.id, + CONF_NAME: TEST_NAME, CONF_MODEL: TEST_MODEL, } @@ -113,11 +113,11 @@ async def test_dhcp_success(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == "create_entry" - assert result["title"] == client.id + assert result["title"] == TEST_NAME assert result["data"] == { CONF_HOST: "1.2.3.4", CONF_ID: client.id, - CONF_NAME: client.id, + CONF_NAME: TEST_NAME, CONF_MODEL: TEST_MODEL, } @@ -131,7 +131,7 @@ async def test_dhcp_already_exists(hass: HomeAssistant) -> None: data={ CONF_HOST: "1.2.3.4", CONF_ID: client.id, - CONF_NAME: client.id, + CONF_NAME: TEST_NAME, CONF_MODEL: TEST_MODEL, }, unique_id=client.id, diff --git a/tests/components/twinkly/test_diagnostics.py b/tests/components/twinkly/test_diagnostics.py new file mode 100644 index 00000000000..ab07cabef4a --- /dev/null +++ b/tests/components/twinkly/test_diagnostics.py @@ -0,0 +1,28 @@ +"""Tests for the diagnostics of the twinkly component.""" +from collections.abc import Awaitable, Callable + +from syrupy import SnapshotAssertion + +from homeassistant.core import HomeAssistant + +from . import ClientMock + +from tests.components.diagnostics import get_diagnostics_for_config_entry +from tests.typing import ClientSessionGenerator + +ComponentSetup = Callable[[], Awaitable[ClientMock]] + +DOMAIN = "twinkly" + + +async def test_diagnostics( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + setup_integration: ComponentSetup, + snapshot: SnapshotAssertion, +) -> None: + """Test diagnostics.""" + await setup_integration() + entry = hass.config_entries.async_entries(DOMAIN)[0] + + assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot diff --git a/tests/components/twinkly/test_light.py b/tests/components/twinkly/test_light.py index f66c82dc2ed..bcb40f22d08 100644 --- a/tests/components/twinkly/test_light.py +++ b/tests/components/twinkly/test_light.py @@ -16,7 +16,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.entity_registry import RegistryEntry -from . import TEST_MODEL, TEST_NAME_ORIGINAL, ClientMock +from . import TEST_MODEL, TEST_NAME, TEST_NAME_ORIGINAL, ClientMock from tests.common import MockConfigEntry @@ -28,16 +28,16 @@ async def test_initial_state(hass: HomeAssistant) -> None: state = hass.states.get(entity.entity_id) # Basic state properties - assert state.name == entity.unique_id + assert state.name == TEST_NAME assert state.state == "on" assert state.attributes[ATTR_BRIGHTNESS] == 26 - assert state.attributes["friendly_name"] == entity.unique_id + assert state.attributes["friendly_name"] == TEST_NAME assert state.attributes["icon"] == "mdi:string-lights" - assert entity.original_name == entity.unique_id + assert entity.original_name == TEST_NAME assert entity.original_icon == "mdi:string-lights" - assert device.name == entity.unique_id + assert device.name == TEST_NAME assert device.model == TEST_MODEL assert device.manufacturer == "LEDWORKS"