python-library-lan-router 0.1.0__tar.gz
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.
- python_library_lan_router-0.1.0/.gitignore +11 -0
- python_library_lan_router-0.1.0/PKG-INFO +6 -0
- python_library_lan_router-0.1.0/lan_router/__init__.py +16 -0
- python_library_lan_router-0.1.0/lan_router/base.py +24 -0
- python_library_lan_router-0.1.0/lan_router/device.py +11 -0
- python_library_lan_router-0.1.0/lan_router/tplink.py +33 -0
- python_library_lan_router-0.1.0/pyproject.toml +15 -0
- python_library_lan_router-0.1.0/requirements.txt +2 -0
- python_library_lan_router-0.1.0/settings.py +46 -0
- python_library_lan_router-0.1.0/test.bat +11 -0
- python_library_lan_router-0.1.0/tests/router_scan_demo.py +25 -0
- python_library_lan_router-0.1.0/tests/test_lan_router.py +19 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .device import Device
|
|
2
|
+
from .base import BaseRouter
|
|
3
|
+
from .tplink import TPLinkRouter
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
def create_router(vendor: Literal["tplink"],**kwargs) -> BaseRouter:
|
|
7
|
+
if vendor == "tplink":
|
|
8
|
+
return TPLinkRouter(**kwargs)
|
|
9
|
+
else:
|
|
10
|
+
raise ValueError(f"不支持的厂商: {vendor}")
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Device",
|
|
14
|
+
"BaseRouter",
|
|
15
|
+
"TPLinkRouter",
|
|
16
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from .device import Device
|
|
4
|
+
|
|
5
|
+
class BaseRouter(BaseModel, ABC):
|
|
6
|
+
hostname: str
|
|
7
|
+
"""路由器主机名"""
|
|
8
|
+
username: str
|
|
9
|
+
"""路由器用户名"""
|
|
10
|
+
password: str
|
|
11
|
+
"""路由器密码"""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def scan(self) -> list[Device]:
|
|
15
|
+
"""扫描设备"""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
def login(self):
|
|
19
|
+
"""登录"""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def logout(self):
|
|
23
|
+
"""登出"""
|
|
24
|
+
pass
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .base import BaseRouter
|
|
2
|
+
from .device import Device
|
|
3
|
+
from tplinkrouterc6u import TPLinkXDRClient
|
|
4
|
+
|
|
5
|
+
class TPLinkRouter(BaseRouter):
|
|
6
|
+
_router: TPLinkXDRClient = None
|
|
7
|
+
|
|
8
|
+
def model_post_init(self,ctx):
|
|
9
|
+
self._router = TPLinkXDRClient(self.hostname, self.username, self.password)
|
|
10
|
+
|
|
11
|
+
def login(self):
|
|
12
|
+
self._router.authorize()
|
|
13
|
+
|
|
14
|
+
def logout(self):
|
|
15
|
+
self._router.logout()
|
|
16
|
+
|
|
17
|
+
def scan(self) -> list[Device]:
|
|
18
|
+
devices = []
|
|
19
|
+
status = self._router.get_status()
|
|
20
|
+
|
|
21
|
+
for data in status.devices:
|
|
22
|
+
type = getattr(data.type, "value", str(data.type))
|
|
23
|
+
name = data.hostname or ""
|
|
24
|
+
ip = self._show(data.ipaddr)
|
|
25
|
+
mac = self._show(data.macaddr)
|
|
26
|
+
device = Device(name=name, ip=ip, mac=mac, type=type)
|
|
27
|
+
devices.append(device)
|
|
28
|
+
|
|
29
|
+
return devices
|
|
30
|
+
|
|
31
|
+
def _show(self, v: str | None) -> str:
|
|
32
|
+
"""显示空值"""
|
|
33
|
+
return "-" if v is None or v == "" else v
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python-library-lan-router"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"tplinkrouterc6u>=0.1.0",
|
|
11
|
+
"pydantic>=2.0.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[tool.hatch.build.targets.wheel]
|
|
15
|
+
packages = ["lan_router"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import yaml
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
def find_env_file() -> Path | None:
|
|
7
|
+
"""从当前文件所在目录开始,逐层向上查找 .env"""
|
|
8
|
+
start = Path(__file__).resolve().parent
|
|
9
|
+
for parent in [start, *start.parents]:
|
|
10
|
+
env_file = parent / ".env"
|
|
11
|
+
if env_file.is_file():
|
|
12
|
+
return env_file
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
class Settings(BaseSettings):
|
|
16
|
+
model_config = SettingsConfigDict(
|
|
17
|
+
env_file=find_env_file(),
|
|
18
|
+
env_file_encoding="utf-8",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
router_vendor: Literal["tplink"]
|
|
22
|
+
"""路由器厂商"""
|
|
23
|
+
router_hostname: str
|
|
24
|
+
"""路由器主机名"""
|
|
25
|
+
router_username: str
|
|
26
|
+
"""路由器用户名"""
|
|
27
|
+
router_password: str
|
|
28
|
+
"""路由器密码"""
|
|
29
|
+
|
|
30
|
+
settings = Settings()
|
|
31
|
+
|
|
32
|
+
def load_settings(input_yaml:str|None):
|
|
33
|
+
"""加载配置文件"""
|
|
34
|
+
global settings
|
|
35
|
+
|
|
36
|
+
if input_yaml:
|
|
37
|
+
with open(input_yaml) as f:
|
|
38
|
+
local_overrides = yaml.safe_load(f)
|
|
39
|
+
for key, value in local_overrides.items():
|
|
40
|
+
if hasattr(settings, key):
|
|
41
|
+
setattr(settings, key, value)
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"settings",
|
|
45
|
+
"load_settings",
|
|
46
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""需配置 .env 与真实路由器,在包根目录执行: python tests/router_scan_demo.py"""
|
|
2
|
+
|
|
3
|
+
from lan_router import create_router
|
|
4
|
+
from settings import settings
|
|
5
|
+
|
|
6
|
+
if __name__ == "__main__":
|
|
7
|
+
router = create_router(
|
|
8
|
+
settings.router_vendor,
|
|
9
|
+
hostname=settings.router_hostname,
|
|
10
|
+
username=settings.router_username,
|
|
11
|
+
password=settings.router_password,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
router.login()
|
|
15
|
+
|
|
16
|
+
devices = router.scan()
|
|
17
|
+
for i, device in enumerate(devices):
|
|
18
|
+
print(f"[{i}] {device.name}")
|
|
19
|
+
print(f" 连接类型 : {device.type}")
|
|
20
|
+
print(f" 主机名 : {device.name}")
|
|
21
|
+
print(f" IP : {device.ip}")
|
|
22
|
+
print(f" MAC : {device.mac}")
|
|
23
|
+
print("-" * 90)
|
|
24
|
+
|
|
25
|
+
router.logout()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from lan_router import Device, create_router
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LanRouterTests(unittest.TestCase):
|
|
7
|
+
def test_create_router_rejects_unknown_vendor(self) -> None:
|
|
8
|
+
with self.assertRaises(ValueError) as ctx:
|
|
9
|
+
create_router("unknown") # type: ignore[arg-type]
|
|
10
|
+
self.assertIn("不支持", str(ctx.exception))
|
|
11
|
+
|
|
12
|
+
def test_device_model(self) -> None:
|
|
13
|
+
d = Device(name="n", ip="192.168.0.1", mac="00:00:00:00:00:01", type="wifi")
|
|
14
|
+
self.assertEqual(d.name, "n")
|
|
15
|
+
self.assertEqual(d.ip, "192.168.0.1")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
unittest.main()
|