zte-wrapper 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zte_wrapper/__init__.py +0 -0
- zte_wrapper/authwrapper.py +167 -0
- zte_wrapper/helpers.py +17 -0
- zte_wrapper/types.py +233 -0
- zte_wrapper/wrapper.py +83 -0
- zte_wrapper/wrappers/apn.py +83 -0
- zte_wrapper/wrappers/bindings.py +67 -0
- zte_wrapper/wrappers/ddns.py +54 -0
- zte_wrapper/wrappers/devices.py +161 -0
- zte_wrapper/wrappers/dhcp.py +84 -0
- zte_wrapper/wrappers/firewall.py +140 -0
- zte_wrapper/wrappers/networktools.py +131 -0
- zte_wrapper/wrappers/portforwarding.py +89 -0
- zte_wrapper/wrappers/portmapping.py +90 -0
- zte_wrapper/wrappers/signal.py +64 -0
- zte_wrapper/wrappers/sms.py +84 -0
- zte_wrapper-1.0.0.dist-info/METADATA +97 -0
- zte_wrapper-1.0.0.dist-info/RECORD +20 -0
- zte_wrapper-1.0.0.dist-info/WHEEL +4 -0
- zte_wrapper-1.0.0.dist-info/licenses/LICENSE +21 -0
zte_wrapper/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import aiohttp
|
|
3
|
+
from hashlib import sha256
|
|
4
|
+
import urllib.parse
|
|
5
|
+
import time
|
|
6
|
+
import json
|
|
7
|
+
from typing import Literal, Dict, Any, Tuple
|
|
8
|
+
from copy import deepcopy
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("zte")
|
|
11
|
+
|
|
12
|
+
GOFORM_COMMANDS = Literal["goform_get_cmd_process", "goform_set_cmd_process"]
|
|
13
|
+
HEADERS = {
|
|
14
|
+
"Accept": "application/json, text/javascript, */*; q=0.01",
|
|
15
|
+
"Accept-Encoding": "gzip, deflate",
|
|
16
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
17
|
+
"Connection": "keep-alive",
|
|
18
|
+
"Host": "192.168.32.1",
|
|
19
|
+
"Referer": "http://192.168.32.1/",
|
|
20
|
+
"Sec-Gpc": "1",
|
|
21
|
+
"User-Agent": "Mozilla",
|
|
22
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def zte_sha256_string(input: str) -> str:
|
|
27
|
+
return sha256(input.encode("UTF-8")).hexdigest().upper()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ZTEAuthWrapper:
|
|
31
|
+
def __init__(self, webui_address: str, password: str) -> None:
|
|
32
|
+
self.address: str = webui_address
|
|
33
|
+
self.__password = zte_sha256_string(password)
|
|
34
|
+
self.__auth_code: str | None = None
|
|
35
|
+
|
|
36
|
+
self.headers = HEADERS
|
|
37
|
+
self.headers["Referer"] = f"http://{self.address}/"
|
|
38
|
+
self.headers["Host"] = self.address
|
|
39
|
+
|
|
40
|
+
self.session: aiohttp.ClientSession | None = None
|
|
41
|
+
|
|
42
|
+
def construct_url(self, command: GOFORM_COMMANDS, args: Dict[str, Any]) -> str:
|
|
43
|
+
base_url = f"http://{self.address}/goform/{command}/?"
|
|
44
|
+
|
|
45
|
+
url = base_url + urllib.parse.urlencode(
|
|
46
|
+
{k: urllib.parse.quote(str(v)) for k, v in args.items()}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
logger.debug(f"Constructed url {url}")
|
|
50
|
+
return url
|
|
51
|
+
|
|
52
|
+
def get_timestamp(self) -> int:
|
|
53
|
+
return round(time.time() * 1000)
|
|
54
|
+
|
|
55
|
+
async def __get_ld(self) -> str:
|
|
56
|
+
async with aiohttp.ClientSession() as session:
|
|
57
|
+
res = await session.get(
|
|
58
|
+
self.construct_url(
|
|
59
|
+
"goform_get_cmd_process",
|
|
60
|
+
{"isTest": "false", "cmd": "LD", "_": self.get_timestamp()},
|
|
61
|
+
),
|
|
62
|
+
headers=self.headers,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
logger.info("Got LD token")
|
|
66
|
+
return json.loads((await res.text())).get(
|
|
67
|
+
"LD"
|
|
68
|
+
) # json.loads instead of res.json() because the stupid API returns the stuff as text/html
|
|
69
|
+
|
|
70
|
+
async def __get_rd0_rd1(self) -> Tuple[str, str]:
|
|
71
|
+
res = await self.request(
|
|
72
|
+
"GET",
|
|
73
|
+
self.construct_url(
|
|
74
|
+
"goform_get_cmd_process",
|
|
75
|
+
{
|
|
76
|
+
"isTest": "false",
|
|
77
|
+
"cmd": "cr_version,wa_inner_version",
|
|
78
|
+
"multi_data": "1",
|
|
79
|
+
},
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
res_text = await res.text()
|
|
83
|
+
logger.debug(f"Retrieved rd0 and rd1: {res_text}")
|
|
84
|
+
|
|
85
|
+
data = json.loads(res_text)
|
|
86
|
+
return (data["wa_inner_version"], data["cr_version"])
|
|
87
|
+
|
|
88
|
+
async def __get_rd_token(self) -> str:
|
|
89
|
+
res = await self.request(
|
|
90
|
+
"GET",
|
|
91
|
+
self.construct_url(
|
|
92
|
+
"goform_get_cmd_process",
|
|
93
|
+
{"isTest": "false", "cmd": "RD", "_": self.get_timestamp()},
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
logger.debug("")
|
|
97
|
+
return json.loads(await res.text())["RD"]
|
|
98
|
+
|
|
99
|
+
# please check notes/tokens.md if it breaks, it might help you a little
|
|
100
|
+
async def construct_ad_token(self) -> str:
|
|
101
|
+
rd0, rd1 = await self.__get_rd0_rd1()
|
|
102
|
+
first_step = zte_sha256_string(rd0 + rd1)
|
|
103
|
+
rd_token = await self.__get_rd_token()
|
|
104
|
+
return zte_sha256_string(first_step + rd_token)
|
|
105
|
+
|
|
106
|
+
async def refresh_auth(self) -> str:
|
|
107
|
+
ld_token: str = await self.__get_ld()
|
|
108
|
+
hashed_password: str = zte_sha256_string(self.__password + ld_token)
|
|
109
|
+
|
|
110
|
+
async with aiohttp.ClientSession() as session:
|
|
111
|
+
logger.debug("Refreshing authentication")
|
|
112
|
+
res = await session.post(
|
|
113
|
+
self.construct_url("goform_set_cmd_process", {}),
|
|
114
|
+
data={
|
|
115
|
+
"isTest": "false",
|
|
116
|
+
"goformId": "LOGIN",
|
|
117
|
+
"password": hashed_password,
|
|
118
|
+
},
|
|
119
|
+
headers=self.headers,
|
|
120
|
+
)
|
|
121
|
+
self.__auth_code = str(res.cookies.get("stok"))
|
|
122
|
+
|
|
123
|
+
return self.__auth_code
|
|
124
|
+
|
|
125
|
+
async def confirm_auth(self) -> bool:
|
|
126
|
+
res = await self.request(
|
|
127
|
+
"GET",
|
|
128
|
+
self.construct_url(
|
|
129
|
+
"goform_get_cmd_process",
|
|
130
|
+
{"isTest": "false", "cmd": "date_month", "_": self.get_timestamp()},
|
|
131
|
+
),
|
|
132
|
+
skip_auth_check=True,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
result = bool(json.loads(await res.text())["date_month"])
|
|
136
|
+
logger.debug(f"Logged in? {result}")
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
async def request(
|
|
141
|
+
self, method: Literal["GET", "POST"], *args, **kwargs
|
|
142
|
+
) -> aiohttp.ClientResponse:
|
|
143
|
+
if not self.session:
|
|
144
|
+
self.session = aiohttp.ClientSession()
|
|
145
|
+
|
|
146
|
+
headers = deepcopy(self.headers)
|
|
147
|
+
headers["Cookie"] = f'stok="{self.__auth_code}"'
|
|
148
|
+
|
|
149
|
+
if kwargs.get("skip_auth_check"):
|
|
150
|
+
logger.debug("Skipping authentication checks...")
|
|
151
|
+
del kwargs["skip_auth_check"]
|
|
152
|
+
else:
|
|
153
|
+
if not await self.confirm_auth():
|
|
154
|
+
await self.refresh_auth()
|
|
155
|
+
headers["Cookie"] = f'stok="{self.__auth_code}"'
|
|
156
|
+
|
|
157
|
+
if method == "GET":
|
|
158
|
+
res = await self.session.get(*args, **kwargs, headers=headers)
|
|
159
|
+
elif method == "POST":
|
|
160
|
+
res = await self.session.post(*args, **kwargs, headers=headers)
|
|
161
|
+
|
|
162
|
+
return res
|
|
163
|
+
|
|
164
|
+
async def close(self) -> None:
|
|
165
|
+
logger.info("Closign aiohttp client")
|
|
166
|
+
if self.session:
|
|
167
|
+
await self.session.close()
|
zte_wrapper/helpers.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def utf_16_decode(inp: str) -> str:
|
|
5
|
+
return bytes.fromhex(inp).decode("utf-16-be")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def utf_16_encode(inp: str) -> str:
|
|
9
|
+
return inp.encode("utf-16-be").hex().upper()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_zte_timestring(timezone_offset: int) -> str:
|
|
13
|
+
now = datetime.now()
|
|
14
|
+
|
|
15
|
+
output = f"{str(now.year)[2:]};{now.month};{now.day};{now.hour+1};{now.minute};{now.second};+{timezone_offset}"
|
|
16
|
+
|
|
17
|
+
return output
|
zte_wrapper/types.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import List, Literal, TypedDict
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
PhoneNumber = str
|
|
6
|
+
RuleType = Literal["TCP"] | Literal["UDP"] | Literal["TCP&UDP"]
|
|
7
|
+
InterfaceType = (
|
|
8
|
+
Literal["WIFI6"]
|
|
9
|
+
| Literal["WIFI1"]
|
|
10
|
+
| Literal["Ethernet"]
|
|
11
|
+
| Literal["WIFI"]
|
|
12
|
+
| Literal[""]
|
|
13
|
+
)
|
|
14
|
+
FirewallProtocolTarget = (
|
|
15
|
+
Literal["TCP"] | Literal["UDP"] | Literal["ICMP"] | Literal["ALL"]
|
|
16
|
+
)
|
|
17
|
+
FirewallAction = Literal["DROP"] | Literal["ACCEPT"]
|
|
18
|
+
Hostname = TypedDict("Hostname", {"hostname": str, "mac": str})
|
|
19
|
+
AutoOrManual = Literal["auto"] | Literal["manual"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AuthError(Exception):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PortRange:
|
|
27
|
+
start: int
|
|
28
|
+
end: int
|
|
29
|
+
iter: int
|
|
30
|
+
|
|
31
|
+
def __init__(self, start: int, end: int):
|
|
32
|
+
self.start = start
|
|
33
|
+
self.end = end
|
|
34
|
+
|
|
35
|
+
def __iter__(self):
|
|
36
|
+
self.iter = self.start
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def __next__(self):
|
|
40
|
+
if self.iter > self.end:
|
|
41
|
+
raise StopIteration
|
|
42
|
+
|
|
43
|
+
val = self.iter
|
|
44
|
+
self.iter += 1
|
|
45
|
+
return val
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class SMSMessage:
|
|
50
|
+
content: str
|
|
51
|
+
tag: int
|
|
52
|
+
id: int
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class SignalStrength:
|
|
57
|
+
sinr_5g: float
|
|
58
|
+
rsrp_5g: float
|
|
59
|
+
rsrq_5g: float
|
|
60
|
+
rssi_5g: float
|
|
61
|
+
|
|
62
|
+
band_5g: str
|
|
63
|
+
band_lte: str
|
|
64
|
+
|
|
65
|
+
pci_lte: float
|
|
66
|
+
rsrq_lte: float
|
|
67
|
+
rsrp_lte: float
|
|
68
|
+
rssi_lte: float
|
|
69
|
+
snr_lte: float
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class NetworkDetails:
|
|
74
|
+
isp_name: str
|
|
75
|
+
download_mbps: float
|
|
76
|
+
upload_mbps: float
|
|
77
|
+
monthly_download_megabytes: float
|
|
78
|
+
monthly_upload_megabytes: float
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class PortforwardingRule:
|
|
83
|
+
ip_addr: str
|
|
84
|
+
comment: str
|
|
85
|
+
port_start: int
|
|
86
|
+
port_end: int
|
|
87
|
+
protocol: RuleType
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class PortforwardingTable:
|
|
92
|
+
gateway_addr: str
|
|
93
|
+
enabled: bool
|
|
94
|
+
rules_amount: int
|
|
95
|
+
rules: List[PortforwardingRule]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class PortmappingRule:
|
|
100
|
+
ip_addr: str
|
|
101
|
+
comment: str
|
|
102
|
+
port_external: int
|
|
103
|
+
port_internal: int
|
|
104
|
+
protocol: RuleType
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class PortmappingTable:
|
|
109
|
+
gateway_addr: str
|
|
110
|
+
enabled: bool
|
|
111
|
+
rules_amount: int
|
|
112
|
+
rules: List[PortmappingRule]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class WirelessStation: # basically a wireless device
|
|
117
|
+
addr_type: Literal["DHCP"] | str
|
|
118
|
+
connect_time: int
|
|
119
|
+
hostname: str
|
|
120
|
+
interface_type: InterfaceType
|
|
121
|
+
ip_address: str
|
|
122
|
+
mac_address: str
|
|
123
|
+
mac_bound: bool
|
|
124
|
+
ssid_index: int
|
|
125
|
+
wifi_rssi: int
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class LanStation:
|
|
130
|
+
addr_type: Literal["Static"] | str
|
|
131
|
+
agreed_rate_mbps: int
|
|
132
|
+
connect_time: int
|
|
133
|
+
hostname: str
|
|
134
|
+
ip_address: str
|
|
135
|
+
mac_address: str
|
|
136
|
+
mac_bound: bool
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass
|
|
140
|
+
class OfflineStation:
|
|
141
|
+
interface_type: InterfaceType
|
|
142
|
+
offline_time: datetime
|
|
143
|
+
start_time: datetime
|
|
144
|
+
start_time_t: datetime
|
|
145
|
+
hostname: str
|
|
146
|
+
ip_address: str
|
|
147
|
+
mac_address: str
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class DDNSSettings:
|
|
152
|
+
provider: (
|
|
153
|
+
Literal["freedns.afraid.org"]
|
|
154
|
+
| Literal["dyndns.org"]
|
|
155
|
+
| Literal["zoneedit.org"]
|
|
156
|
+
| Literal["no-ip.com"]
|
|
157
|
+
| str
|
|
158
|
+
)
|
|
159
|
+
account_username: str
|
|
160
|
+
account_password: str
|
|
161
|
+
hash_value: str
|
|
162
|
+
mode: AutoOrManual
|
|
163
|
+
enabled: bool
|
|
164
|
+
domain: str
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@dataclass
|
|
168
|
+
class MacBinding:
|
|
169
|
+
domain: str | Literal["(null)"]
|
|
170
|
+
hostname: str | Literal["(null)"]
|
|
171
|
+
ip: str
|
|
172
|
+
mac: str
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@dataclass
|
|
176
|
+
class APNProfile:
|
|
177
|
+
data: str
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@dataclass
|
|
181
|
+
class APNSettings:
|
|
182
|
+
apn_mode: AutoOrManual
|
|
183
|
+
profile_name: str
|
|
184
|
+
apn_wan_dial: str
|
|
185
|
+
apn_select: AutoOrManual
|
|
186
|
+
pdp_type: Literal["IP"] | str
|
|
187
|
+
pdp_select: AutoOrManual
|
|
188
|
+
pdp_address: str
|
|
189
|
+
index: int
|
|
190
|
+
wan_apn: str
|
|
191
|
+
ppp_auth_mode: str | Literal["none"]
|
|
192
|
+
ppp_username: str
|
|
193
|
+
ppp_password: str
|
|
194
|
+
dns_mode: AutoOrManual
|
|
195
|
+
prefer_dns_manual: str # find later
|
|
196
|
+
standby_dns_manual: str # find later x2
|
|
197
|
+
profiles: List[APNProfile]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass
|
|
201
|
+
class DHCPSettings:
|
|
202
|
+
enabled: bool
|
|
203
|
+
end_ip: str
|
|
204
|
+
start_ip: str
|
|
205
|
+
lease_time_hours: int
|
|
206
|
+
dhcp_type: Literal["SERVER"] | str
|
|
207
|
+
lan_ip_addr: str
|
|
208
|
+
lan_netmask: str
|
|
209
|
+
mac_addr: str
|
|
210
|
+
mtu: int
|
|
211
|
+
tcp_mss: int
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@dataclass
|
|
215
|
+
class FirewallRule:
|
|
216
|
+
mac_addr: str
|
|
217
|
+
source_ip: str
|
|
218
|
+
dest_ip: str
|
|
219
|
+
protocol: FirewallProtocolTarget
|
|
220
|
+
|
|
221
|
+
source_port: PortRange
|
|
222
|
+
dest_port: PortRange
|
|
223
|
+
|
|
224
|
+
action: FirewallAction
|
|
225
|
+
comment: str
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@dataclass
|
|
229
|
+
class FirewallConfig:
|
|
230
|
+
default_policy: FirewallAction
|
|
231
|
+
enabled: bool
|
|
232
|
+
rules_ipv4: List[FirewallRule]
|
|
233
|
+
rules_ipv6: List[FirewallRule]
|
zte_wrapper/wrapper.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, List
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from .authwrapper import ZTEAuthWrapper
|
|
5
|
+
from .wrappers import (
|
|
6
|
+
portforwarding,
|
|
7
|
+
sms,
|
|
8
|
+
signal,
|
|
9
|
+
devices,
|
|
10
|
+
portmapping,
|
|
11
|
+
ddns,
|
|
12
|
+
networktools,
|
|
13
|
+
bindings,
|
|
14
|
+
apn,
|
|
15
|
+
dhcp,
|
|
16
|
+
firewall,
|
|
17
|
+
)
|
|
18
|
+
from dataclasses import dataclass, asdict
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger("zte")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ZTEWrapper(ZTEAuthWrapper):
|
|
26
|
+
def __init__(self, webui_address: str, password: str) -> None:
|
|
27
|
+
super().__init__(webui_address, password)
|
|
28
|
+
|
|
29
|
+
self.portforwarding = portforwarding.PortforwardingWrapper(self)
|
|
30
|
+
self.portmapping = portmapping.PortmappingWrapper(self)
|
|
31
|
+
self.sms = sms.SmsWrapper(self)
|
|
32
|
+
self.signal = signal.SignalWrapper(self)
|
|
33
|
+
self.devices = devices.DeviceWrapper(self)
|
|
34
|
+
self.ddns = ddns.DDNSWrapper(self)
|
|
35
|
+
self.network_tools = networktools.NetworkToolWrapper(self)
|
|
36
|
+
self.bindings = bindings.BindingWrapper(self)
|
|
37
|
+
self.apn = apn.APNWrapper(self)
|
|
38
|
+
self.dhcp = dhcp.DHCPWrapper(self)
|
|
39
|
+
self.firewall = firewall.FirewallWrapper(self)
|
|
40
|
+
|
|
41
|
+
async def perform_dbg_query(self, items: List[str]) -> dict:
|
|
42
|
+
res = await self.request(
|
|
43
|
+
"GET",
|
|
44
|
+
self.construct_url(
|
|
45
|
+
"goform_get_cmd_process",
|
|
46
|
+
{"cmd": ",".join(items), "isTest": "false", "multi_data": "true"},
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
txt = await res.text()
|
|
51
|
+
try:
|
|
52
|
+
data = json.loads(txt)
|
|
53
|
+
return data
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(txt)
|
|
56
|
+
print(e)
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
async def backup_settings(self) -> dict:
|
|
60
|
+
data = {}
|
|
61
|
+
|
|
62
|
+
logger.info("DDNS...")
|
|
63
|
+
data["ddns"] = await self.ddns.get_ddns_settings()
|
|
64
|
+
|
|
65
|
+
logger.info("Port forwarding...")
|
|
66
|
+
data["portforw"] = await self.portforwarding.get_port_forwarding_rules()
|
|
67
|
+
|
|
68
|
+
logger.info("Port Mapping...")
|
|
69
|
+
data["portmap"] = await self.portmapping.get_portmap_rules()
|
|
70
|
+
|
|
71
|
+
logger.info("Bindings...")
|
|
72
|
+
data["bindings"] = await self.bindings.get_mac_bindings()
|
|
73
|
+
|
|
74
|
+
logger.info("APN...")
|
|
75
|
+
data["apn"] = await self.apn.get_apn_settings()
|
|
76
|
+
|
|
77
|
+
logger.info("DHCP...")
|
|
78
|
+
data["dhcp"] = await self.dhcp.get_settings()
|
|
79
|
+
|
|
80
|
+
logger.info("Firewall...")
|
|
81
|
+
data["firewall"] = await self.firewall.get_config()
|
|
82
|
+
|
|
83
|
+
return data
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from ..authwrapper import ZTEAuthWrapper
|
|
2
|
+
import json
|
|
3
|
+
from ..types import APNSettings, APNProfile
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class APNWrapper:
|
|
8
|
+
def __init__(self, auth: ZTEAuthWrapper):
|
|
9
|
+
self.auth = auth
|
|
10
|
+
|
|
11
|
+
async def get_apn_settings(self) -> APNSettings:
|
|
12
|
+
res = await self.auth.request(
|
|
13
|
+
"GET",
|
|
14
|
+
self.auth.construct_url(
|
|
15
|
+
"goform_get_cmd_process",
|
|
16
|
+
{
|
|
17
|
+
"cmd": "apn_interface_version,APN_config0,APN_config1,APN_config2,APN_config3,APN_config4,APN_config5,APN_config6,APN_config7,APN_config8,APN_config9,APN_config10,APN_config11,APN_config12,APN_config13,APN_config14,APN_config15,APN_config16,APN_config17,APN_config18,APN_config19,ipv6_APN_config0,ipv6_APN_config1,ipv6_APN_config2,ipv6_APN_config3,ipv6_APN_config4,ipv6_APN_config5,ipv6_APN_config6,ipv6_APN_config7,ipv6_APN_config8,ipv6_APN_config9,ipv6_APN_config10,ipv6_APN_config11,ipv6_APN_config12,ipv6_APN_config13,ipv6_APN_config14,ipv6_APN_config15,ipv6_APN_config16,ipv6_APN_config17,ipv6_APN_config18,ipv6_APN_config19,apn_m_profile_name,profile_name,apn_wan_dial,apn_select,apn_pdp_type,apn_pdp_select,apn_pdp_addr,index,apn_Current_index,apn_auto_config,apn_ipv6_apn_auto_config,apn_mode,apn_wan_apn,apn_ppp_auth_mode,apn_ppp_username,apn_ppp_passwd,dns_mode,prefer_dns_manual,standby_dns_manual,apn_ipv6_wan_apn,apn_ipv6_pdp_type,apn_ipv6_ppp_auth_mode,apn_ipv6_ppp_username,apn_ipv6_ppp_passwd,ipv6_dns_mode,ipv6_prefer_dns_manual,ipv6_standby_dns_manual,apn_num_preset,wan_apn_ui,profile_name_ui,pdp_type_ui,ppp_auth_mode_ui,ppp_username_ui,ppp_passwd_ui,dns_mode_ui,prefer_dns_manual_ui,standby_dns_manual_ui,ipv6_wan_apn_ui,ipv6_ppp_auth_mode_ui,ipv6_ppp_username_ui,ipv6_ppp_passwd_ui,ipv6_dns_mode_ui,ipv6_prefer_dns_manual_ui,ipv6_standby_dns_manual_ui",
|
|
18
|
+
"isTest": "false",
|
|
19
|
+
"multi_data": "1",
|
|
20
|
+
},
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
data: dict = json.loads(await res.text())
|
|
25
|
+
profiles: List[APNProfile] = []
|
|
26
|
+
|
|
27
|
+
for k, v in data.items():
|
|
28
|
+
k: str = k
|
|
29
|
+
v: str = v
|
|
30
|
+
|
|
31
|
+
if k.startswith("ipv6_APN_config") or k.startswith("APN_config"):
|
|
32
|
+
profiles.append(APNProfile(data=v))
|
|
33
|
+
|
|
34
|
+
settings = APNSettings(
|
|
35
|
+
apn_mode=data["apn_mode"],
|
|
36
|
+
apn_select=data["apn_select"],
|
|
37
|
+
apn_wan_dial="*99#",
|
|
38
|
+
dns_mode=data["dns_mode"],
|
|
39
|
+
pdp_address=data["apn_pdp_addr"],
|
|
40
|
+
pdp_select="auto",
|
|
41
|
+
pdp_type=data["apn_pdp_type"],
|
|
42
|
+
ppp_auth_mode=data["apn_ppp_auth_mode"],
|
|
43
|
+
ppp_password=data["apn_ppp_passwd"],
|
|
44
|
+
ppp_username=data["apn_ppp_username"],
|
|
45
|
+
prefer_dns_manual=data["prefer_dns_manual"],
|
|
46
|
+
profile_name=data["profile_name_ui"],
|
|
47
|
+
standby_dns_manual=data["standby_dns_manual"],
|
|
48
|
+
wan_apn=data["apn_wan_apn"],
|
|
49
|
+
index=int(data["apn_Current_index"]),
|
|
50
|
+
profiles=profiles,
|
|
51
|
+
)
|
|
52
|
+
return settings
|
|
53
|
+
|
|
54
|
+
async def set_apn_settings(self, settings: APNSettings) -> bool:
|
|
55
|
+
d = {
|
|
56
|
+
"isTest": "false",
|
|
57
|
+
"goformId": "APN_PROC_EX",
|
|
58
|
+
"apn_action": "save",
|
|
59
|
+
"apn_mode": settings.apn_mode,
|
|
60
|
+
"profile_name": settings.profile_name,
|
|
61
|
+
"apn_wan_dial": settings.apn_wan_dial,
|
|
62
|
+
"apn_select": settings.apn_select,
|
|
63
|
+
"apn_pdp_type": settings.pdp_type,
|
|
64
|
+
"apn_pdp_select": settings.pdp_select,
|
|
65
|
+
"apn_pdp_addr": settings.pdp_address,
|
|
66
|
+
"index": str(settings.index),
|
|
67
|
+
"apn_wan_apn": settings.wan_apn,
|
|
68
|
+
"apn_ppp_auth_mode": settings.ppp_auth_mode,
|
|
69
|
+
"apn_ppp_username": settings.ppp_username,
|
|
70
|
+
"apn_ppp_passwd": settings.ppp_password,
|
|
71
|
+
"dns_mode": settings.dns_mode,
|
|
72
|
+
"prefer_dns_manual": settings.prefer_dns_manual,
|
|
73
|
+
"standby_dns_manual": settings.standby_dns_manual,
|
|
74
|
+
"AD": await self.auth.construct_ad_token(),
|
|
75
|
+
}
|
|
76
|
+
res = await self.auth.request(
|
|
77
|
+
"POST",
|
|
78
|
+
self.auth.construct_url("goform_set_cmd_process", {}),
|
|
79
|
+
data=d,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
data = json.loads(await res.text())
|
|
83
|
+
return data["result"] == "success"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from ..authwrapper import ZTEAuthWrapper
|
|
2
|
+
import json
|
|
3
|
+
from ..types import MacBinding
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BindingWrapper:
|
|
8
|
+
def __init__(self, auth: ZTEAuthWrapper):
|
|
9
|
+
self.auth = auth
|
|
10
|
+
|
|
11
|
+
async def get_mac_bindings(self) -> List[MacBinding]:
|
|
12
|
+
res = await self.auth.request(
|
|
13
|
+
"GET",
|
|
14
|
+
self.auth.construct_url(
|
|
15
|
+
"goform_get_cmd_process",
|
|
16
|
+
{
|
|
17
|
+
"isTest": "false",
|
|
18
|
+
"cmd": "current_static_addr_list",
|
|
19
|
+
},
|
|
20
|
+
),
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
data: dict = json.loads(await res.text())
|
|
24
|
+
bindings: List[MacBinding] = []
|
|
25
|
+
|
|
26
|
+
for binding in data["current_static_addr_list"]:
|
|
27
|
+
bindings.append(
|
|
28
|
+
MacBinding(
|
|
29
|
+
domain=binding["domain"],
|
|
30
|
+
hostname=binding["hostname"],
|
|
31
|
+
ip=binding["ip"],
|
|
32
|
+
mac=binding["mac"],
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return bindings
|
|
37
|
+
|
|
38
|
+
async def create_mac_binding(self, mac_address: str, ip: str) -> bool:
|
|
39
|
+
res = await self.auth.request(
|
|
40
|
+
"POST",
|
|
41
|
+
self.auth.construct_url("goform_set_cmd_process", {}),
|
|
42
|
+
data={
|
|
43
|
+
"isTest": "false",
|
|
44
|
+
"goformId": "BIND_STATIC_ADDRESS_ADD",
|
|
45
|
+
"mac_address": mac_address,
|
|
46
|
+
"ip_address": ip,
|
|
47
|
+
"AD": await self.auth.construct_ad_token(),
|
|
48
|
+
},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
data = json.loads(await res.text())
|
|
52
|
+
return data["result"] == "success"
|
|
53
|
+
|
|
54
|
+
async def delete_mac_binding(self, mac_address: str) -> bool:
|
|
55
|
+
res = await self.auth.request(
|
|
56
|
+
"POST",
|
|
57
|
+
self.auth.construct_url("goform_set_cmd_process", {}),
|
|
58
|
+
data={
|
|
59
|
+
"isTest": "false",
|
|
60
|
+
"goformId": "BIND_STATIC_ADDRESS_DEL",
|
|
61
|
+
"mac_address": mac_address,
|
|
62
|
+
"AD": await self.auth.construct_ad_token(),
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
data = json.loads(await res.text())
|
|
67
|
+
return data["result"] == "success"
|