Skip to content

Commit

Permalink
Add diagnostics
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmitry Mamontov committed Apr 15, 2022
1 parent 0dd49ae commit 13ec332
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 16 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Component for tracking devices and managing routers based on [MiWiFi](http://miw
- [Supported routers](#supported-routers)
- [API check list](#api-check-list)
- [Summary](#summary)
- [Diagnostics](#diagnostics)

## FAQ
**Q. Do I need to get telnet or ssh?**
Expand Down Expand Up @@ -163,3 +164,26 @@ Many more Xiaomi and Redmi routers supported by MiWiFi (OpenWRT - Luci API)
| ![](images/R1C.png) | **Mi Router Mini** | R1CM | <sub>🟢🟢🟢🟢</sub> | <sub>🟢🟢🟢🟢🟢🟢🟢🟢🔴</sub> | <sub>🟢🟢🟢🟢</sub> |
| ![](images/R2D.png) | **Mi Router R2D** | R2D | <sub>🟢🟢🟢🟢</sub> | <sub>🟢🟢🟢🟢🟢🟢🟢🟢🔴</sub> | <sub>🟢🟢🟢🟢</sub> |
| ![](images/R1D.png) | **Mi Router R1D** | R1D | <sub>🟢🟢🟢🟢</sub> | <sub>🟢🟢🟢🟢🟢🟢🟢🟢🔴</sub> | <sub>🟢🟢🟢🟢</sub> |

## Diagnostics
You will need to obtain diagnostic data to search for a problem or before creating an issue.

### Via GUI:

How to obtain diagnostic data can be found on the website HASS: [Diagnostic Documentation](https://www.home-assistant.io/integrations/diagnostics/)

### Via Debug:

❗ Check the data that you publish in the issue, they may contain secret data.

Set component to debug mode and reload HASS:

```yaml
logger:
default: error
logs:
...
custom_components.miwifi: debug
```
Then wait a bit and you can watch the logs, they will need information
5 changes: 5 additions & 0 deletions custom_components/miwifi/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
DISCOVERY: Final = "discovery"
DISCOVERY_INTERVAL: Final = timedelta(minutes=60)

"""Diagnostic const"""
DIAGNOSTIC_DATE_TIME: Final = "date_time"
DIAGNOSTIC_MESSAGE: Final = "message"
DIAGNOSTIC_CONTENT: Final = "content"

"""Helper const"""
UPDATER: Final = "updater"
UPDATE_LISTENER: Final = "update_listener"
Expand Down
57 changes: 57 additions & 0 deletions custom_components/miwifi/diagnostics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""MiWifi diagnostic."""

from __future__ import annotations

from typing import Final

from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_PASSWORD,
CONF_USERNAME,
CONF_URL,
CONF_TOKEN,
CONF_ID,
)
from homeassistant.core import HomeAssistant

from .const import (
DOMAIN,
UPDATER,
ATTR_CAMERA_IMAGE,
)

TO_REDACT: Final = {
CONF_PASSWORD,
CONF_USERNAME,
CONF_URL,
CONF_TOKEN,
CONF_ID,
ATTR_CAMERA_IMAGE,
"routerId",
"gateWay",
"hostname",
"ipv4",
"ssid",
"pwd",
}


async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict:
"""Return diagnostics for a config entry."""

_data: dict = {"config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT)}

if _updater := hass.data[DOMAIN][config_entry.entry_id].get(UPDATER):
if hasattr(_updater, "data"):
_data["data"] = async_redact_data(_updater.data, TO_REDACT)

if hasattr(_updater, "devices"):
_data["devices"] = _updater.devices

if len(_updater.luci.diagnostics) > 0:
_data["requests"] = async_redact_data(_updater.luci.diagnostics, TO_REDACT)

return _data
66 changes: 51 additions & 15 deletions custom_components/miwifi/luci.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import uuid
import urllib.parse

from datetime import datetime
from typing import Any
from httpx import AsyncClient, Response, HTTPError

from homeassistant.util import slugify
Expand All @@ -23,6 +25,9 @@
CLIENT_LOGIN_TYPE,
CLIENT_NONCE_TYPE,
CLIENT_PUBLIC_KEY,
DIAGNOSTIC_DATE_TIME,
DIAGNOSTIC_MESSAGE,
DIAGNOSTIC_CONTENT,
)
from .exceptions import LuciConnectionException, LuciTokenException

Expand Down Expand Up @@ -67,14 +72,17 @@ def __init__(

self._url = CLIENT_URL.format(ip=ip)

self.diagnostics: dict[str, Any] = {}

async def login(self) -> dict:
"""Login method
:return dict: dict with login data.
"""

_method: str = "xqsystem/login"
_nonce: str = self.generate_nonce()
_url: str = f"{self._url}/api/xqsystem/login"
_url: str = f"{self._url}/api/{_method}"

try:
async with self._client as client:
Expand All @@ -91,15 +99,17 @@ async def login(self) -> dict:
timeout=self._timeout,
)

_LOGGER.debug("Successful request %s: %s", _url, response.content)
self._debug("Successful request", _url, response.content, _method)

_data: dict = json.loads(response.content)
except (HTTPError, ValueError, TypeError) as _e:
_LOGGER.debug("Connection error %r", _e)
self._debug("Connection error", _url, _e, _method)

raise LuciConnectionException("Connection error") from _e

if response.status_code != 200 or "token" not in _data:
self._debug("Failed to get token", _url, _data, _method)

raise LuciTokenException("Failed to get token")

self._token = _data["token"]
Expand All @@ -112,15 +122,16 @@ async def logout(self) -> None:
if self._token is None:
return

_url: str = f"{self._url}/;stok={self._token}/web/logout"
_method: str = "logout"
_url: str = f"{self._url}/;stok={self._token}/web/{_method}"

try:
async with self._client as client:
response: Response = await client.get(_url, timeout=self._timeout)

_LOGGER.debug("Successful request %s: %s", _url, response.content)
self._debug("Successful request", _url, response.content, _method)
except (HTTPError, ValueError, TypeError) as _e:
_LOGGER.debug("Logout error: %r", _e)
self._debug("Logout error", _url, _e, _method)

async def get(
self, path: str, query_params: dict | None = None, use_stok: bool = True
Expand All @@ -143,15 +154,17 @@ async def get(
async with self._client as client:
response: Response = await client.get(_url, timeout=self._timeout)

_LOGGER.debug("Successful request %s: %s", _url, response.content)
self._debug("Successful request", _url, response.content, path)

_data: dict = json.loads(response.content)
except (HTTPError, ValueError, TypeError) as _e:
_LOGGER.debug("Connection error %r", _e)
self._debug("Connection error", _url, _e, path)

raise LuciConnectionException("Connection error") from _e

if "code" not in _data or _data["code"] > 0:
self._debug("Invalid error code received", _url, _data, path)

raise LuciTokenException("Invalid error code received")

return _data
Expand Down Expand Up @@ -308,20 +321,19 @@ async def image(self, hardware: str) -> bytes | None:
"""

hardware = slugify(hardware.lower())
# fmt: off
url: str = f"http://{self.ip}/xiaoqiang/web/img/icons/router_{hardware}_100_on.png"
# fmt: on
_path: str = f"icons/router_{hardware}_100_on.png"
_url: str = f"http://{self.ip}/xiaoqiang/web/img/{_path}"

try:
async with self._client as client:
response: Response = await client.get(url, timeout=self._timeout)
response: Response = await client.get(_url, timeout=self._timeout)

_LOGGER.debug("Successful request image %s", url)
self._debug("Successful request image", _url, response.status_code, _path)

if len(response.content) > 0:
return base64.b64encode(response.content)
except HTTPError:
return None
except HTTPError as _e:
self._debug("Error request image", _url, _e, _path)

return None

Expand Down Expand Up @@ -365,3 +377,27 @@ def generate_password_hash(self, nonce: str, password: str) -> str:
"""

return self.sha1(nonce + self.sha1(password + CLIENT_PUBLIC_KEY))

def _debug(self, message: str, url: str, content: Any, path: str) -> None:
"""Debug log
:param message: str: Message
:param url: str: URL
:param content: Any: Content
:param path: str: Path
"""

_LOGGER.debug("%s (%s): %s", message, url, str(content))

_content: dict | str = {}

try:
_content = json.loads(content)
except (ValueError, TypeError):
_content = str(content)

self.diagnostics[path] = {
DIAGNOSTIC_DATE_TIME: datetime.now().replace(microsecond=0).isoformat(),
DIAGNOSTIC_MESSAGE: message,
DIAGNOSTIC_CONTENT: _content,
}
2 changes: 1 addition & 1 deletion custom_components/miwifi/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"domain": "miwifi",
"name": "MiWiFi",
"version": "2.3.1",
"version": "2.4.0",
"documentation": "https://github.com/dmamontov/hass-miwifi/blob/main/README.md",
"issue_tracker": "https://github.com/dmamontov/hass-miwifi/issues",
"config_flow": true,
Expand Down

0 comments on commit 13ec332

Please sign in to comment.