Add tplink diagnostics (#65822)

This commit is contained in:
Teemu R 2022-02-06 23:50:44 +01:00 committed by GitHub
parent 4cd00a1a6f
commit fd7e2e76e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 315 additions and 117 deletions

View File

@ -0,0 +1,46 @@
"""Diagnostics support for TPLink."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN
from .coordinator import TPLinkDataUpdateCoordinator
TO_REDACT = {
# Entry fields
"unique_id", # based on mac address
# Device identifiers
"alias",
"mac",
"mic_mac",
"host",
"hwId",
"oemId",
"deviceId",
# Device location
"latitude",
"latitude_i",
"longitude",
"longitude_i",
# Cloud connectivity info
"username",
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinator: TPLinkDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
device = coordinator.device
data = {}
data[
"device_last_response"
] = device._last_update # pylint: disable=protected-access
return async_redact_data(data, TO_REDACT)

View File

@ -2,10 +2,16 @@
from unittest.mock import AsyncMock, MagicMock, patch
from kasa import SmartBulb, SmartDimmer, SmartPlug, SmartStrip
from kasa import SmartBulb, SmartDevice, SmartDimmer, SmartPlug, SmartStrip
from kasa.exceptions import SmartDeviceException
from kasa.protocol import TPLinkSmartHomeProtocol
from homeassistant.components.tplink import CONF_HOST
from homeassistant.components.tplink.const import DOMAIN
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
MODULE = "homeassistant.components.tplink"
MODULE_CONFIG_FLOW = "homeassistant.components.tplink.config_flow"
IP_ADDRESS = "127.0.0.1"
@ -146,3 +152,23 @@ def _patch_single_discovery(device=None, no_device=False):
return patch(
"homeassistant.components.tplink.Discover.discover_single", new=_discover_single
)
async def initialize_config_entry_for_device(
hass: HomeAssistant, dev: SmartDevice
) -> MockConfigEntry:
"""Create a mocked configuration entry for the given device.
Note, the rest of the tests should probably be converted over to use this
instead of repeating the initialization routine for each test separately
"""
config_entry = MockConfigEntry(
title="TP-Link", domain=DOMAIN, unique_id=dev.mac, data={CONF_HOST: dev.host}
)
config_entry.add_to_hass(hass)
with _patch_discovery(device=dev), _patch_single_discovery(device=dev):
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
return config_entry

View File

@ -1,116 +0,0 @@
"""Constants for the TP-Link component tests."""
SMARTPLUG_HS110_DATA = {
"sysinfo": {
"sw_ver": "1.0.4 Build 191111 Rel.143500",
"hw_ver": "4.0",
"model": "HS110(EU)",
"deviceId": "4C56447B395BB7A2FAC68C9DFEE2E84163222581",
"oemId": "40F54B43071E9436B6395611E9D91CEA",
"hwId": "A6C77E4FDD238B53D824AC8DA361F043",
"rssi": -24,
"longitude_i": 130793,
"latitude_i": 480582,
"alias": "SmartPlug",
"status": "new",
"mic_type": "IOT.SMARTPLUGSWITCH",
"feature": "TIM:ENE",
"mac": "69:F2:3C:8E:E3:47",
"updating": 0,
"led_off": 0,
"relay_state": 0,
"on_time": 0,
"active_mode": "none",
"icon_hash": "",
"dev_name": "Smart Wi-Fi Plug With Energy Monitoring",
"next_action": {"type": -1},
"err_code": 0,
},
"realtime": {
"voltage_mv": 233957,
"current_ma": 21,
"power_mw": 0,
"total_wh": 1793,
"err_code": 0,
},
}
SMARTPLUG_HS100_DATA = {
"sysinfo": {
"sw_ver": "1.0.4 Build 191111 Rel.143500",
"hw_ver": "4.0",
"model": "HS100(EU)",
"deviceId": "4C56447B395BB7A2FAC68C9DFEE2E84163222581",
"oemId": "40F54B43071E9436B6395611E9D91CEA",
"hwId": "A6C77E4FDD238B53D824AC8DA361F043",
"rssi": -24,
"longitude_i": 130793,
"latitude_i": 480582,
"alias": "SmartPlug",
"status": "new",
"mic_type": "IOT.SMARTPLUGSWITCH",
"feature": "TIM:",
"mac": "A9:F4:3D:A4:E3:47",
"updating": 0,
"led_off": 0,
"relay_state": 0,
"on_time": 0,
"active_mode": "none",
"icon_hash": "",
"dev_name": "Smart Wi-Fi Plug",
"next_action": {"type": -1},
"err_code": 0,
}
}
SMARTSTRIP_KP303_DATA = {
"sysinfo": {
"sw_ver": "1.0.4 Build 210428 Rel.135415",
"hw_ver": "1.0",
"model": "KP303(AU)",
"deviceId": "03102547AB1A57A4E4AA5B4EFE34C3005726B97D",
"oemId": "1F950FC9BFF278D9D35E046C129D9411",
"hwId": "9E86D4F840D2787D3D7A6523A731BA2C",
"rssi": -74,
"longitude_i": 1158985,
"latitude_i": -319172,
"alias": "TP-LINK_Power Strip_00B1",
"status": "new",
"mic_type": "IOT.SMARTPLUGSWITCH",
"feature": "TIM",
"mac": "D4:DD:D6:95:B0:F9",
"updating": 0,
"led_off": 0,
"children": [
{
"id": "8006B399B7FE68D4E6991CCCEA239C081DFA913000",
"state": 0,
"alias": "R-Plug 1",
"on_time": 0,
"next_action": {"type": -1},
},
{
"id": "8006B399B7FE68D4E6991CCCEA239C081DFA913001",
"state": 1,
"alias": "R-Plug 2",
"on_time": 93835,
"next_action": {"type": -1},
},
{
"id": "8006B399B7FE68D4E6991CCCEA239C081DFA913002",
"state": 1,
"alias": "R-Plug 3",
"on_time": 93834,
"next_action": {"type": -1},
},
],
"child_num": 3,
"err_code": 0,
},
"realtime": {
"voltage_mv": 233957,
"current_ma": 21,
"power_mw": 0,
"total_wh": 1793,
"err_code": 0,
},
"context": "1",
}

View File

@ -0,0 +1,108 @@
{
"device_last_response": {
"system": {
"get_sysinfo": {
"sw_ver": "1.8.8 Build 190613 Rel.123436",
"hw_ver": "1.0",
"model": "KL130(EU)",
"description": "Smart Wi-Fi LED Bulb with Color Changing",
"alias": "bedroom light",
"mic_type": "IOT.SMARTBULB",
"dev_state": "normal",
"mic_mac": "aa:bb:cc:dd:ee:ff",
"deviceId": "1234",
"oemId": "1234",
"hwId": "1234",
"is_factory": false,
"disco_ver": "1.0",
"ctrl_protocols": {
"name": "Linkie",
"version": "1.0"
},
"light_state": {
"on_off": 1,
"mode": "normal",
"hue": 0,
"saturation": 0,
"color_temp": 2500,
"brightness": 58
},
"is_dimmable": 1,
"is_color": 1,
"is_variable_color_temp": 1,
"preferred_state": [
{
"index": 0,
"hue": 0,
"saturation": 0,
"color_temp": 2500,
"brightness": 10
},
{
"index": 1,
"hue": 299,
"saturation": 95,
"color_temp": 0,
"brightness": 100
},
{
"index": 2,
"hue": 120,
"saturation": 75,
"color_temp": 0,
"brightness": 100
},
{
"index": 3,
"hue": 240,
"saturation": 75,
"color_temp": 0,
"brightness": 100
}
],
"rssi": -66,
"active_mode": "none",
"heapsize": 334532,
"err_code": 0
}
},
"smartlife.iot.common.emeter": {
"get_realtime": {
"power_mw": 6600,
"err_code": 0
},
"get_monthstat": {
"month_list": [
{
"year": 2022,
"month": 1,
"energy_wh": 321
},
{
"year": 2022,
"month": 2,
"energy_wh": 321
}
],
"err_code": 0
},
"get_daystat": {
"day_list": [
{
"year": 2022,
"month": 2,
"day": 1,
"energy_wh": 123
},
{
"year": 2022,
"month": 2,
"day": 2,
"energy_wh": 123
}
],
"err_code": 0
}
}
}
}

View File

@ -0,0 +1,74 @@
{
"device_last_response": {
"system": {
"get_sysinfo": {
"sw_ver": "1.0.4 Build 191111 Rel.143500",
"hw_ver": "4.0",
"model": "HS110(EU)",
"deviceId": "1234",
"oemId": "1234",
"hwId": "1234",
"rssi": -57,
"longitude_i": "0.0",
"latitude_i": "0.0",
"alias": "some plug",
"status": "new",
"mic_type": "IOT.SMARTPLUGSWITCH",
"feature": "TIM:ENE",
"mac": "aa:bb:cc:dd:ee:ff",
"updating": 0,
"led_off": 1,
"relay_state": 1,
"on_time": 254454,
"active_mode": "none",
"icon_hash": "",
"dev_name": "Smart Wi-Fi Plug With Energy Monitoring",
"next_action": {
"type": -1
},
"err_code": 0
}
},
"emeter": {
"get_realtime": {
"voltage_mv": 230118,
"current_ma": 303,
"power_mw": 28825,
"total_wh": 18313,
"err_code": 0
},
"get_monthstat": {
"month_list": [
{
"year": 2022,
"month": 2,
"energy_wh": 321
},
{
"year": 2022,
"month": 1,
"energy_wh": 321
}
],
"err_code": 0
},
"get_daystat": {
"day_list": [
{
"year": 2022,
"month": 2,
"day": 1,
"energy_wh": 123
},
{
"year": 2022,
"month": 2,
"day": 2,
"energy_wh": 123
}
],
"err_code": 0
}
}
}
}

View File

@ -0,0 +1,60 @@
"""Tests for the diagnostics data provided by the TP-Link integration."""
import json
from aiohttp import ClientSession
from kasa import SmartDevice
import pytest
from homeassistant.core import HomeAssistant
from . import _mocked_bulb, _mocked_plug, initialize_config_entry_for_device
from tests.common import load_fixture
from tests.components.diagnostics import get_diagnostics_for_config_entry
@pytest.mark.parametrize(
"mocked_dev,fixture_file,sysinfo_vars",
[
(
_mocked_bulb(),
"tplink-diagnostics-data-bulb-kl130.json",
["mic_mac", "deviceId", "oemId", "hwId", "alias"],
),
(
_mocked_plug(),
"tplink-diagnostics-data-plug-hs110.json",
["mac", "deviceId", "oemId", "hwId", "alias", "longitude_i", "latitude_i"],
),
],
)
async def test_diagnostics(
hass: HomeAssistant,
hass_client: ClientSession,
mocked_dev: SmartDevice,
fixture_file: str,
sysinfo_vars: list[str],
):
"""Test diagnostics for config entry."""
diagnostics_data = json.loads(load_fixture(fixture_file, "tplink"))
mocked_dev._last_update = diagnostics_data["device_last_response"]
config_entry = await initialize_config_entry_for_device(hass, mocked_dev)
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert isinstance(result, dict)
assert "device_last_response" in result
# There must be some redactions in place, so the raw data must not match
assert result["device_last_response"] != diagnostics_data["device_last_response"]
last_response = result["device_last_response"]
# We should always have sysinfo available
assert "system" in last_response
assert "get_sysinfo" in last_response["system"]
sysinfo = last_response["system"]["get_sysinfo"]
for var in sysinfo_vars:
assert sysinfo[var] == "**REDACTED**"