-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.py
112 lines (90 loc) · 3.74 KB
/
router.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
"""Provides the backend for a Cudy router"""
from datetime import timedelta
from typing import Any
import requests
import logging
import urllib.parse
from http.cookies import SimpleCookie
from .const import MODULE_DEVICES, MODULE_MODEM, OPTIONS_DEVICELIST
from .parser import parse_devices, parse_modem_info
from homeassistant.core import HomeAssistant
_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=15)
SCAN_INTERVAL = timedelta(seconds=30)
RETRY_INTERVAL = timedelta(seconds=300)
class CudyRouter:
"""Represents a router and provides functions for communication."""
def __init__(
self, hass: HomeAssistant, host: str, username: str, password: str
) -> None:
"""Initialize."""
self.host = host
self.auth_cookie = None
self.hass = hass
self.username = username
self.password = password
def get_cookie_header(self, force_auth: bool) -> str:
"""Returns a cookie header that should be used for authentication."""
if not force_auth and self.auth_cookie:
return f"sysauth={self.auth_cookie}"
if self.authenticate():
return f"sysauth={self.auth_cookie}"
else:
return ""
def authenticate(self) -> bool:
"""Test if we can authenticate with the host."""
data_url = f"http://{self.host}/cgi-bin/luci"
headers = {"Content-Type": "application/x-www-form-urlencoded", "Cookie": ""}
body = f"luci_username={urllib.parse.quote(self.username)}&luci_password={urllib.parse.quote(self.password)}&luci_language=en"
try:
response = requests.post(
data_url, timeout=30, headers=headers, data=body, allow_redirects=False
)
if response.ok:
cookie = SimpleCookie()
cookie.load(response.headers.get("set-cookie"))
self.auth_cookie = cookie.get("sysauth").value
return True
except requests.exceptions.ConnectionError:
_LOGGER.debug("Connection error?")
return False
def get(self, url: str) -> str:
"""Retrieves data from the given URL using an authenticated session."""
retries = 2
while retries > 0:
retries -= 1
data_url = f"http://{self.host}/cgi-bin/luci/{url}"
headers = {"Cookie": f"{self.get_cookie_header(False)}"}
try:
response = requests.get(
data_url, timeout=30, headers=headers, allow_redirects=False
)
if response.status_code == 403:
if self.authenticate():
continue
else:
_LOGGER.error("Error during authentication to %s", url)
break
if response.ok:
return response.text
else:
break
except Exception: # pylint: disable=broad-except
pass
_LOGGER.error("Error retrieving data from %s", url)
return ""
async def get_data(
self, hass: HomeAssistant, options: dict[str, Any]
) -> dict[str, Any]:
"""Retrieves data from the router"""
data: dict[str, Any] = {}
data[MODULE_MODEM] = parse_modem_info(
f"{await hass.async_add_executor_job(self.get, 'admin/network/gcom/status')}{await hass.async_add_executor_job(self.get, 'admin/network/gcom/status?detail=1')}"
)
data[MODULE_DEVICES] = parse_devices(
await hass.async_add_executor_job(
self.get, "admin/network/devices/devlist?detail=1"
),
options and options.get(OPTIONS_DEVICELIST),
)
return data