Fix API regressions and refresh project docs
This commit is contained in:
@@ -1,6 +1,23 @@
|
||||
import json
|
||||
import asyncio
|
||||
import socket
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WizResponse:
|
||||
ok: bool
|
||||
ip: str
|
||||
kind: str
|
||||
payload: dict[str, Any] | None = None
|
||||
message: str | None = None
|
||||
|
||||
@property
|
||||
def result(self) -> dict[str, Any]:
|
||||
if not self.payload:
|
||||
return {}
|
||||
return self.payload.get("result", {})
|
||||
|
||||
|
||||
class WizDriver:
|
||||
@@ -41,24 +58,72 @@ class WizDriver:
|
||||
"steampunk": 35,
|
||||
}
|
||||
|
||||
async def send_udp(self, ip: str, payload: dict):
|
||||
async def send_udp(self, ip: str, payload: dict) -> WizResponse:
|
||||
loop = asyncio.get_running_loop()
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
|
||||
sock.settimeout(2.0)
|
||||
data = json.dumps(payload).encode()
|
||||
|
||||
await loop.run_in_executor(None, sock.sendto, data, (ip, self.PORT))
|
||||
try:
|
||||
await loop.run_in_executor(None, sock.sendto, data, (ip, self.PORT))
|
||||
except OSError as exc:
|
||||
return WizResponse(
|
||||
ok=False,
|
||||
ip=ip,
|
||||
kind="network_error",
|
||||
message=f"Ошибка отправки UDP: {exc}",
|
||||
)
|
||||
|
||||
try:
|
||||
resp, _ = await loop.run_in_executor(None, sock.recvfrom, 1024)
|
||||
return json.loads(resp.decode())
|
||||
except socket.timeout:
|
||||
return None
|
||||
return WizResponse(
|
||||
ok=False,
|
||||
ip=ip,
|
||||
kind="timeout",
|
||||
message="Таймаут ответа от лампы",
|
||||
)
|
||||
except OSError as exc:
|
||||
return WizResponse(
|
||||
ok=False,
|
||||
ip=ip,
|
||||
kind="network_error",
|
||||
message=f"Ошибка чтения UDP: {exc}",
|
||||
)
|
||||
|
||||
async def set_pilot(self, ip: str, params: dict):
|
||||
try:
|
||||
decoded = json.loads(resp.decode())
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||
return WizResponse(
|
||||
ok=False,
|
||||
ip=ip,
|
||||
kind="protocol_error",
|
||||
message=f"Невалидный ответ WiZ: {exc}",
|
||||
)
|
||||
|
||||
if isinstance(decoded, dict) and "error" in decoded:
|
||||
return WizResponse(
|
||||
ok=False,
|
||||
ip=ip,
|
||||
kind="device_error",
|
||||
payload=decoded,
|
||||
message=str(decoded["error"]),
|
||||
)
|
||||
|
||||
if not isinstance(decoded, dict):
|
||||
return WizResponse(
|
||||
ok=False,
|
||||
ip=ip,
|
||||
kind="protocol_error",
|
||||
message="Ответ WiZ имеет неожиданный формат",
|
||||
)
|
||||
|
||||
return WizResponse(ok=True, ip=ip, kind="ok", payload=decoded)
|
||||
|
||||
async def set_pilot(self, ip: str, params: dict) -> WizResponse:
|
||||
payload = {"method": "setPilot", "params": params}
|
||||
return await self.send_udp(ip, payload)
|
||||
|
||||
async def get_pilot(self, ip: str):
|
||||
async def get_pilot(self, ip: str) -> WizResponse:
|
||||
payload = {"method": "getPilot", "params": {}}
|
||||
return await self.send_udp(ip, payload)
|
||||
|
||||
Reference in New Issue
Block a user