Add diagnostics support to Huawei LTE (#131085)

Co-authored-by: abmantis <amfcalt@gmail.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
This commit is contained in:
Ville Skyttä 2025-07-21 20:52:48 +00:00 committed by GitHub
parent 79dd91ebc6
commit ef2531d28d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 642 additions and 0 deletions

View File

@ -0,0 +1,86 @@
"""Diagnostics support for Huawei LTE."""
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
ENTRY_FIELDS_DATA_TO_REDACT = {
"mac",
"username",
"password",
}
DEVICE_INFORMATION_DATA_TO_REDACT = {
"SerialNumber",
"Imei",
"Imsi",
"Iccid",
"Msisdn",
"MacAddress1",
"MacAddress2",
"WanIPAddress",
"wan_dns_address",
"WanIPv6Address",
"wan_ipv6_dns_address",
"Mccmnc",
"WifiMacAddrWl0",
"WifiMacAddrWl1",
}
DEVICE_SIGNAL_DATA_TO_REDACT = {
"pci",
"cell_id",
"enodeb_id",
"rac",
"lac",
"tac",
"nei_cellid",
"plmn",
"bsic",
}
MONITORING_STATUS_DATA_TO_REDACT = {
"PrimaryDns",
"SecondaryDns",
"PrimaryIPv6Dns",
"SecondaryIPv6Dns",
}
NET_CURRENT_PLMN_DATA_TO_REDACT = {
"net_current_plmn",
}
LAN_HOST_INFO_DATA_TO_REDACT = {
"lan_host_info",
}
WLAN_WIFI_GUEST_NETWORK_SWITCH_DATA_TO_REDACT = {
"Ssid",
"WifiSsid",
}
WLAN_MULTI_BASIC_SETTINGS_DATA_TO_REDACT = {
"WifiMac",
}
TO_REDACT = {
*ENTRY_FIELDS_DATA_TO_REDACT,
*DEVICE_INFORMATION_DATA_TO_REDACT,
*DEVICE_SIGNAL_DATA_TO_REDACT,
*MONITORING_STATUS_DATA_TO_REDACT,
*NET_CURRENT_PLMN_DATA_TO_REDACT,
*LAN_HOST_INFO_DATA_TO_REDACT,
*WLAN_WIFI_GUEST_NETWORK_SWITCH_DATA_TO_REDACT,
*WLAN_MULTI_BASIC_SETTINGS_DATA_TO_REDACT,
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
return async_redact_data(
{
"entry": entry.data,
"router": hass.data[DOMAIN].routers[entry.entry_id].data,
},
TO_REDACT,
)

View File

@ -21,3 +21,320 @@ def magic_client(multi_basic_settings_value: dict) -> MagicMock:
wifi_feature_switch=wifi_feature_switch, wifi_feature_switch=wifi_feature_switch,
) )
return MagicMock(device=device, monitoring=monitoring, wlan=wlan) return MagicMock(device=device, monitoring=monitoring, wlan=wlan)
def magic_client_full() -> MagicMock:
"""Extended mock for huawei_lte.Client with all API methods."""
information = MagicMock(
return_value={
"DeviceName": "Test Router",
"SerialNumber": "test-serial-number",
"Imei": "123456789012345",
"Imsi": "123451234567890",
"Iccid": "12345678901234567890",
"Msisdn": None,
"HardwareVersion": "1.0.0",
"SoftwareVersion": "2.0.0",
"WebUIVersion": "3.0.0",
"MacAddress1": "22:22:33:44:55:66",
"MacAddress2": None,
"WanIPAddress": "23.215.0.138",
"wan_dns_address": "8.8.8.8",
"WanIPv6Address": "2600:1406:3a00:21::173e:2e66",
"wan_ipv6_dns_address": "2001:4860:4860:0:0:0:0:8888",
"ProductFamily": "LTE",
"Classify": "cpe",
"supportmode": "LTE|WCDMA|GSM",
"workmode": "LTE",
"submask": "255.255.255.255",
"Mccmnc": "20499",
"iniversion": "test-ini-version",
"uptime": "4242424",
"ImeiSvn": "01",
"WifiMacAddrWl0": "22:22:33:44:55:77",
"WifiMacAddrWl1": "22:22:33:44:55:88",
"spreadname_en": "Huawei 4G Router N123",
"spreadname_zh": "\u534e\u4e3a4G\u8def\u7531 N123",
}
)
basic_information = MagicMock(
return_value={
"classify": "cpe",
"devicename": "Test Router",
"multimode": "0",
"productfamily": "LTE",
"restore_default_status": "0",
"sim_save_pin_enable": "1",
"spreadname_en": "Huawei 4G Router N123",
"spreadname_zh": "\u534e\u4e3a4G\u8def\u7531 N123",
}
)
signal = MagicMock(
return_value={
"pci": "123",
"sc": None,
"cell_id": "12345678",
"rssi": "-70dBm",
"rsrp": "-100dBm",
"rsrq": "-10.0dB",
"sinr": "10dB",
"rscp": None,
"ecio": None,
"mode": "7",
"ulbandwidth": "20MHz",
"dlbandwidth": "20MHz",
"txpower": "PPusch:-1dBm PPucch:-11dBm PSrs:10dBm PPrach:0dBm",
"tdd": None,
"ul_mcs": "mcsUpCarrier1:20",
"dl_mcs": "mcsDownCarrier1Code0:8 mcsDownCarrier1Code1:9",
"earfcn": "DL:123 UL:45678",
"rrc_status": "1",
"rac": None,
"lac": None,
"tac": "12345",
"band": "1",
"nei_cellid": "23456789",
"plmn": "20499",
"ims": "0",
"wdlfreq": None,
"lteulfreq": "19697",
"ltedlfreq": "21597",
"transmode": "TM[4]",
"enodeb_id": "0012345",
"cqi0": "11",
"cqi1": "5",
"ulfrequency": "1969700kHz",
"dlfrequency": "2159700kHz",
"arfcn": None,
"bsic": None,
"rxlev": None,
}
)
check_notifications = MagicMock(
return_value={
"UnreadMessage": "2",
"SmsStorageFull": "0",
"OnlineUpdateStatus": "42",
"SimOperEvent": "0",
}
)
status = MagicMock(
return_value={
"ConnectionStatus": "901",
"WifiConnectionStatus": None,
"SignalStrength": None,
"SignalIcon": "5",
"CurrentNetworkType": "19",
"CurrentServiceDomain": "3",
"RoamingStatus": "0",
"BatteryStatus": None,
"BatteryLevel": None,
"BatteryPercent": None,
"simlockStatus": "0",
"PrimaryDns": "8.8.8.8",
"SecondaryDns": "8.8.4.4",
"wififrequence": "1",
"flymode": "0",
"PrimaryIPv6Dns": "2001:4860:4860:0:0:0:0:8888",
"SecondaryIPv6Dns": "2001:4860:4860:0:0:0:0:8844",
"CurrentWifiUser": "42",
"TotalWifiUser": "64",
"currenttotalwifiuser": "0",
"ServiceStatus": "2",
"SimStatus": "1",
"WifiStatus": "1",
"CurrentNetworkTypeEx": "101",
"maxsignal": "5",
"wifiindooronly": "0",
"cellroam": "1",
"classify": "cpe",
"usbup": "0",
"wifiswitchstatus": "1",
"WifiStatusExCustom": "0",
"hvdcp_online": "0",
}
)
month_statistics = MagicMock(
return_value={
"CurrentMonthDownload": "1000000000",
"CurrentMonthUpload": "500000000",
"MonthDuration": "720000",
"MonthLastClearTime": "2025-07-01",
"CurrentDayUsed": "123456789",
"CurrentDayDuration": "10000",
}
)
traffic_statistics = MagicMock(
return_value={
"CurrentConnectTime": "123456",
"CurrentUpload": "2000000000",
"CurrentDownload": "5000000000",
"CurrentDownloadRate": "700",
"CurrentUploadRate": "600",
"TotalUpload": "20000000000",
"TotalDownload": "50000000000",
"TotalConnectTime": "1234567",
"showtraffic": "1",
}
)
current_plmn = MagicMock(
return_value={
"State": "1",
"FullName": "Test Network",
"ShortName": "Test",
"Numeric": "12345",
}
)
net_mode = MagicMock(
return_value={
"NetworkMode": "03",
"NetworkBand": "3FFFFFFF",
"LTEBand": "7FFFFFFFFFFFFFFF",
}
)
sms_count = MagicMock(
return_value={
"LocalUnread": "0",
"LocalInbox": "5",
"LocalOutbox": "2",
"LocalDraft": "1",
"LocalDeleted": "0",
"SimUnread": "0",
"SimInbox": "0",
"SimOutbox": "0",
"SimDraft": "0",
"LocalMax": "500",
"SimMax": "30",
"SimUsed": "0",
"NewMsg": "0",
}
)
mobile_dataswitch = MagicMock(return_value={"dataswitch": "1"})
lan_host_info = MagicMock(
return_value={
"Hosts": {
"Host": [
{
"Active": "0",
"ActualName": "TestDevice1",
"AddressSource": "DHCP",
"AssociatedSsid": None,
"AssociatedTime": None,
"HostName": "TestDevice1",
"ID": "InternetGatewayDevice.LANDevice.1.Hosts.Host.9.",
"InterfaceType": "Wireless",
"IpAddress": "192.168.1.100",
"LeaseTime": "2204542",
"MacAddress": "AA:BB:CC:DD:EE:FF",
"isLocalDevice": "0",
},
{
"Active": "1",
"ActualName": "TestDevice2",
"AddressSource": "DHCP",
"AssociatedSsid": "TestSSID",
"AssociatedTime": "258632",
"HostName": "TestDevice2",
"ID": "InternetGatewayDevice.LANDevice.1.Hosts.Host.17.",
"InterfaceType": "Wireless",
"IpAddress": "192.168.1.101",
"LeaseTime": "552115",
"MacAddress": "11:22:33:44:55:66",
"isLocalDevice": "0",
},
]
}
}
)
wlan_host_list = MagicMock(
return_value={
"Hosts": {
"Host": [
{
"ActualName": "TestDevice2",
"AssociatedSsid": "TestSSID",
"AssociatedTime": "258632",
"Frequency": "2.4GHz",
"HostName": "TestDevice2",
"ID": "InternetGatewayDevice.LANDevice.1.Hosts.Host.17.",
"IpAddress": "192.168.1.101;fe80::b222:33ff:fe44:5566",
"MacAddress": "11:22:33:44:55:66",
}
]
}
}
)
multi_basic_settings = MagicMock(
return_value={"Ssid": [{"wifiisguestnetwork": "1", "WifiEnable": "0"}]}
)
wifi_feature_switch = MagicMock(
return_value={
"wifi_dbdc_enable": "0",
"acmode_enable": "1",
"wifiautocountry_enabled": "0",
"wps_cancel_enable": "1",
"wifimacfilterextendenable": "1",
"wifimaxmacfilternum": "32",
"paraimmediatework_enable": "1",
"guestwifi_enable": "0",
"wifi5gnamepostfix": "_5G",
"wifiguesttimeextendenable": "1",
"chinesessid_enable": "0",
"isdoublechip": "1",
"opennonewps_enable": "1",
"wifi_country_enable": "0",
"wifi5g_enabled": "1",
"wifiwpsmode": "0",
"pmf_enable": "1",
"support_trigger_dualband_wps": "1",
"maxapnum": "4",
"wifi_chip_maxassoc": "32",
"wifiwpssuportwepnone": "0",
"maxassocoffloadon": None,
"guidefrequencyenable": "0",
"showssid_enable": "0",
"wifishowradioswitch": "3",
"wifispecialcharenable": "1",
"wifi24g_switch_enable": "1",
"wifi_dfs_enable": "0",
"show_maxassoc": "0",
"hilink_dbho_enable": "1",
"oledshowpassword": "1",
"doubleap5g_enable": "0",
"wps_switch_enable": "1",
}
)
device = MagicMock(
information=information, basic_information=basic_information, signal=signal
)
monitoring = MagicMock(
check_notifications=check_notifications,
status=status,
month_statistics=month_statistics,
traffic_statistics=traffic_statistics,
)
net = MagicMock(current_plmn=current_plmn, net_mode=net_mode)
sms = MagicMock(sms_count=sms_count)
dial_up = MagicMock(mobile_dataswitch=mobile_dataswitch)
lan = MagicMock(host_info=lan_host_info)
wlan = MagicMock(
multi_basic_settings=multi_basic_settings,
wifi_feature_switch=wifi_feature_switch,
host_list=wlan_host_list,
)
return MagicMock(
device=device,
monitoring=monitoring,
net=net,
sms=sms,
dial_up=dial_up,
lan=lan,
wlan=wlan,
)

View File

@ -0,0 +1,201 @@
# serializer version: 1
# name: test_entry_diagnostics
dict({
'entry': dict({
'mac': '**REDACTED**',
'url': 'http://huawei-lte.example.com',
}),
'router': dict({
'device_information': dict({
'Classify': 'cpe',
'DeviceName': 'Test Router',
'HardwareVersion': '1.0.0',
'Iccid': '**REDACTED**',
'Imei': '**REDACTED**',
'ImeiSvn': '01',
'Imsi': '**REDACTED**',
'MacAddress1': '**REDACTED**',
'MacAddress2': None,
'Mccmnc': '**REDACTED**',
'Msisdn': None,
'ProductFamily': 'LTE',
'SerialNumber': '**REDACTED**',
'SoftwareVersion': '2.0.0',
'WanIPAddress': '**REDACTED**',
'WanIPv6Address': '**REDACTED**',
'WebUIVersion': '3.0.0',
'WifiMacAddrWl0': '**REDACTED**',
'WifiMacAddrWl1': '**REDACTED**',
'iniversion': 'test-ini-version',
'spreadname_en': 'Huawei 4G Router N123',
'spreadname_zh': '华为4G路由 N123',
'submask': '255.255.255.255',
'supportmode': 'LTE|WCDMA|GSM',
'uptime': '4242424',
'wan_dns_address': '**REDACTED**',
'wan_ipv6_dns_address': '**REDACTED**',
'workmode': 'LTE',
}),
'device_signal': dict({
'arfcn': None,
'band': '1',
'bsic': None,
'cell_id': '**REDACTED**',
'cqi0': '11',
'cqi1': '5',
'dl_mcs': 'mcsDownCarrier1Code0:8 mcsDownCarrier1Code1:9',
'dlbandwidth': '20MHz',
'dlfrequency': '2159700kHz',
'earfcn': 'DL:123 UL:45678',
'ecio': None,
'enodeb_id': '**REDACTED**',
'ims': '0',
'lac': None,
'ltedlfreq': '21597',
'lteulfreq': '19697',
'mode': '7',
'nei_cellid': '**REDACTED**',
'pci': '**REDACTED**',
'plmn': '**REDACTED**',
'rac': None,
'rrc_status': '1',
'rscp': None,
'rsrp': '-100dBm',
'rsrq': '-10.0dB',
'rssi': '-70dBm',
'rxlev': None,
'sc': None,
'sinr': '10dB',
'tac': '**REDACTED**',
'tdd': None,
'transmode': 'TM[4]',
'txpower': 'PPusch:-1dBm PPucch:-11dBm PSrs:10dBm PPrach:0dBm',
'ul_mcs': 'mcsUpCarrier1:20',
'ulbandwidth': '20MHz',
'ulfrequency': '1969700kHz',
'wdlfreq': None,
}),
'dialup_mobile_dataswitch': dict({
'dataswitch': '1',
}),
'lan_host_info': '**REDACTED**',
'monitoring_check_notifications': dict({
'OnlineUpdateStatus': '42',
'SimOperEvent': '0',
'SmsStorageFull': '0',
'UnreadMessage': '2',
}),
'monitoring_month_statistics': dict({
'CurrentDayDuration': '10000',
'CurrentDayUsed': '123456789',
'CurrentMonthDownload': '1000000000',
'CurrentMonthUpload': '500000000',
'MonthDuration': '720000',
'MonthLastClearTime': '2025-07-01',
}),
'monitoring_status': dict({
'BatteryLevel': None,
'BatteryPercent': None,
'BatteryStatus': None,
'ConnectionStatus': '901',
'CurrentNetworkType': '19',
'CurrentNetworkTypeEx': '101',
'CurrentServiceDomain': '3',
'CurrentWifiUser': '42',
'PrimaryDns': '**REDACTED**',
'PrimaryIPv6Dns': '**REDACTED**',
'RoamingStatus': '0',
'SecondaryDns': '**REDACTED**',
'SecondaryIPv6Dns': '**REDACTED**',
'ServiceStatus': '2',
'SignalIcon': '5',
'SignalStrength': None,
'SimStatus': '1',
'TotalWifiUser': '64',
'WifiConnectionStatus': None,
'WifiStatus': '1',
'WifiStatusExCustom': '0',
'cellroam': '1',
'classify': 'cpe',
'currenttotalwifiuser': '0',
'flymode': '0',
'hvdcp_online': '0',
'maxsignal': '5',
'simlockStatus': '0',
'usbup': '0',
'wififrequence': '1',
'wifiindooronly': '0',
'wifiswitchstatus': '1',
}),
'monitoring_traffic_statistics': dict({
'CurrentConnectTime': '123456',
'CurrentDownload': '5000000000',
'CurrentDownloadRate': '700',
'CurrentUpload': '2000000000',
'CurrentUploadRate': '600',
'TotalConnectTime': '1234567',
'TotalDownload': '50000000000',
'TotalUpload': '20000000000',
'showtraffic': '1',
}),
'net_current_plmn': '**REDACTED**',
'net_net_mode': dict({
'LTEBand': '7FFFFFFFFFFFFFFF',
'NetworkBand': '3FFFFFFF',
'NetworkMode': '03',
}),
'sms_sms_count': dict({
'LocalDeleted': '0',
'LocalDraft': '1',
'LocalInbox': '5',
'LocalMax': '500',
'LocalOutbox': '2',
'LocalUnread': '0',
'NewMsg': '0',
'SimDraft': '0',
'SimInbox': '0',
'SimMax': '30',
'SimOutbox': '0',
'SimUnread': '0',
'SimUsed': '0',
}),
'wlan_wifi_feature_switch': dict({
'acmode_enable': '1',
'chinesessid_enable': '0',
'doubleap5g_enable': '0',
'guestwifi_enable': '0',
'guidefrequencyenable': '0',
'hilink_dbho_enable': '1',
'isdoublechip': '1',
'maxapnum': '4',
'maxassocoffloadon': None,
'oledshowpassword': '1',
'opennonewps_enable': '1',
'paraimmediatework_enable': '1',
'pmf_enable': '1',
'show_maxassoc': '0',
'showssid_enable': '0',
'support_trigger_dualband_wps': '1',
'wifi24g_switch_enable': '1',
'wifi5g_enabled': '1',
'wifi5gnamepostfix': '_5G',
'wifi_chip_maxassoc': '32',
'wifi_country_enable': '0',
'wifi_dbdc_enable': '0',
'wifi_dfs_enable': '0',
'wifiautocountry_enabled': '0',
'wifiguesttimeextendenable': '1',
'wifimacfilterextendenable': '1',
'wifimaxmacfilternum': '32',
'wifishowradioswitch': '3',
'wifispecialcharenable': '1',
'wifiwpsmode': '0',
'wifiwpssuportwepnone': '0',
'wps_cancel_enable': '1',
'wps_switch_enable': '1',
}),
'wlan_wifi_guest_network_switch': dict({
}),
}),
})
# ---

View File

@ -0,0 +1,38 @@
"""Test huawei_lte diagnostics."""
from unittest.mock import MagicMock, patch
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.huawei_lte.const import DOMAIN
from homeassistant.const import CONF_URL
from homeassistant.core import HomeAssistant
from . import magic_client_full
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@patch("homeassistant.components.huawei_lte.Connection", MagicMock())
@patch("homeassistant.components.huawei_lte.Client")
async def test_entry_diagnostics(
client,
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
client.return_value = magic_client_full()
huawei_lte = MockConfigEntry(
domain=DOMAIN, data={CONF_URL: "http://huawei-lte.example.com"}
)
huawei_lte.add_to_hass(hass)
await hass.config_entries.async_setup(huawei_lte.entry_id)
await hass.async_block_till_done()
result = await get_diagnostics_for_config_entry(hass, hass_client, huawei_lte)
assert result == snapshot(exclude=props("entry_id", "created_at", "modified_at"))