diff --git a/CODEOWNERS b/CODEOWNERS index f6737c2e044..6321847b41a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -151,8 +151,8 @@ build.json @home-assistant/supervisor /homeassistant/components/bizkaibus/ @UgaitzEtxebarria /homeassistant/components/blebox/ @bbx-a @riokuu /tests/components/blebox/ @bbx-a @riokuu -/homeassistant/components/blink/ @fronzbot -/tests/components/blink/ @fronzbot +/homeassistant/components/blink/ @fronzbot @mkmer +/tests/components/blink/ @fronzbot @mkmer /homeassistant/components/bluemaestro/ @bdraco /tests/components/bluemaestro/ @bdraco /homeassistant/components/blueprint/ @home-assistant/core diff --git a/homeassistant/components/blink/diagnostics.py b/homeassistant/components/blink/diagnostics.py new file mode 100644 index 00000000000..f69c1721bf1 --- /dev/null +++ b/homeassistant/components/blink/diagnostics.py @@ -0,0 +1,33 @@ +"""Diagnostics support for Blink.""" +from __future__ import annotations + +from typing import Any + +from blinkpy.blinkpy import Blink + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +TO_REDACT = {"serial", "macaddress", "username", "password", "token"} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, + config_entry: ConfigEntry, +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + + api: Blink = hass.data[DOMAIN][config_entry.entry_id].api + + data = { + camera.name: dict(camera.attributes.items()) + for _, camera in api.cameras.items() + } + + return { + "config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT), + "cameras": async_redact_data(data, TO_REDACT), + } diff --git a/homeassistant/components/blink/manifest.json b/homeassistant/components/blink/manifest.json index bb8fd4a5a51..db3ab91de11 100644 --- a/homeassistant/components/blink/manifest.json +++ b/homeassistant/components/blink/manifest.json @@ -1,7 +1,7 @@ { "domain": "blink", "name": "Blink", - "codeowners": ["@fronzbot"], + "codeowners": ["@fronzbot", "@mkmer"], "config_flow": true, "dhcp": [ { diff --git a/tests/components/blink/conftest.py b/tests/components/blink/conftest.py new file mode 100644 index 00000000000..382a1689595 --- /dev/null +++ b/tests/components/blink/conftest.py @@ -0,0 +1,95 @@ +"""Fixtures for the Blink integration tests.""" +from unittest.mock import AsyncMock, MagicMock, create_autospec, patch +from uuid import uuid4 + +import blinkpy +import pytest + +from homeassistant.components.blink.const import DOMAIN +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME + +from tests.common import MockConfigEntry + +CAMERA_ATTRIBUTES = { + "name": "Camera 1", + "camera_id": "111111", + "serial": "serail", + "temperature": None, + "temperature_c": 25.1, + "temperature_calibrated": None, + "battery": "ok", + "battery_voltage": None, + "thumbnail": "https://rest-u034.immedia-semi.com/api/v3/media/accounts/111111/networks/222222/lotus/333333/thumbnail/thumbnail.jpg?ts=1698141602&ext=", + "video": None, + "recent_clips": [], + "motion_enabled": True, + "motion_detected": False, + "wifi_strength": None, + "network_id": 222222, + "sync_module": "sync module", + "last_record": None, + "type": "lotus", +} + + +@pytest.fixture +def camera() -> MagicMock: + """Set up a Blink camera fixture.""" + mock_blink_camera = create_autospec(blinkpy.camera.BlinkCamera, instance=True) + mock_blink_camera.sync = AsyncMock(return_value=True) + mock_blink_camera.name = "Camera 1" + mock_blink_camera.camera_id = "111111" + mock_blink_camera.serial = "12345" + mock_blink_camera.motion_enabled = True + mock_blink_camera.temperature = 25.1 + mock_blink_camera.motion_detected = False + mock_blink_camera.wifi_strength = 2.1 + mock_blink_camera.camera_type = "lotus" + mock_blink_camera.attributes = CAMERA_ATTRIBUTES + return mock_blink_camera + + +@pytest.fixture(name="mock_blink_api") +def blink_api_fixture(camera) -> MagicMock: + """Set up Blink API fixture.""" + mock_blink_api = create_autospec(blinkpy.blinkpy.Blink, instance=True) + mock_blink_api.available = True + mock_blink_api.start = AsyncMock(return_value=True) + mock_blink_api.refresh = AsyncMock(return_value=True) + mock_blink_api.sync = MagicMock(return_value=True) + mock_blink_api.cameras = {camera.name: camera} + + with patch("homeassistant.components.blink.Blink") as class_mock: + class_mock.return_value = mock_blink_api + yield mock_blink_api + + +@pytest.fixture(name="mock_blink_auth_api") +def blink_auth_api_fixture(): + """Set up Blink API fixture.""" + with patch( + "homeassistant.components.blink.Auth", autospec=True + ) as mock_blink_auth_api: + mock_blink_auth_api.check_key_required.return_value = False + yield mock_blink_auth_api + + +@pytest.fixture(name="mock_config_entry") +def mock_config_fixture(): + """Return a fake config entry.""" + return MockConfigEntry( + domain=DOMAIN, + data={ + CONF_USERNAME: "test_user", + CONF_PASSWORD: "Password", + "device_id": "Home Assistant", + "uid": "BlinkCamera_e1233333e2-0909-09cd-777a-123456789012", + "token": "A_token", + "host": "u034.immedia-semi.com", + "region_id": "u034", + "client_id": 123456, + "account_id": 654321, + }, + entry_id=str(uuid4()), + version=3, + ) diff --git a/tests/components/blink/snapshots/test_diagnostics.ambr b/tests/components/blink/snapshots/test_diagnostics.ambr new file mode 100644 index 00000000000..7fb13c97548 --- /dev/null +++ b/tests/components/blink/snapshots/test_diagnostics.ambr @@ -0,0 +1,52 @@ +# serializer version: 1 +# name: test_entry_diagnostics + dict({ + 'cameras': dict({ + 'Camera 1': dict({ + 'battery': 'ok', + 'battery_voltage': None, + 'camera_id': '111111', + 'last_record': None, + 'motion_detected': False, + 'motion_enabled': True, + 'name': 'Camera 1', + 'network_id': 222222, + 'recent_clips': list([ + ]), + 'serial': '**REDACTED**', + 'sync_module': 'sync module', + 'temperature': None, + 'temperature_c': 25.1, + 'temperature_calibrated': None, + 'thumbnail': 'https://rest-u034.immedia-semi.com/api/v3/media/accounts/111111/networks/222222/lotus/333333/thumbnail/thumbnail.jpg?ts=1698141602&ext=', + 'type': 'lotus', + 'video': None, + 'wifi_strength': None, + }), + }), + 'config_entry': dict({ + 'data': dict({ + 'account_id': 654321, + 'client_id': 123456, + 'device_id': 'Home Assistant', + 'host': 'u034.immedia-semi.com', + 'password': '**REDACTED**', + 'region_id': 'u034', + 'token': '**REDACTED**', + 'uid': 'BlinkCamera_e1233333e2-0909-09cd-777a-123456789012', + 'username': '**REDACTED**', + }), + 'disabled_by': None, + 'domain': 'blink', + 'options': dict({ + 'scan_interval': 300, + }), + 'pref_disable_new_entities': False, + 'pref_disable_polling': False, + 'source': 'user', + 'title': 'Mock Title', + 'unique_id': None, + 'version': 3, + }), + }) +# --- diff --git a/tests/components/blink/test_diagnostics.py b/tests/components/blink/test_diagnostics.py new file mode 100644 index 00000000000..d447203dae6 --- /dev/null +++ b/tests/components/blink/test_diagnostics.py @@ -0,0 +1,33 @@ +"""Test Blink diagnostics.""" +from unittest.mock import MagicMock + +from syrupy import SnapshotAssertion +from syrupy.filters import props + +from homeassistant.core import HomeAssistant + +from tests.components.diagnostics import get_diagnostics_for_config_entry +from tests.typing import ClientSessionGenerator + +YAML_CONFIG = {"username": "test-user", "password": "test-password"} + + +async def test_entry_diagnostics( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + snapshot: SnapshotAssertion, + mock_blink_api: MagicMock, + mock_config_entry: MagicMock, +) -> None: + """Test config entry diagnostics.""" + + mock_config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + result = await get_diagnostics_for_config_entry( + hass, hass_client, mock_config_entry + ) + + assert result == snapshot(exclude=props("entry_id"))