aiopppp/binary_camera.py

171 lines
6.7 KiB
Python
Raw Permalink Normal View History

2025-03-22 21:38:18 +04:00
import asyncio
import struct
import aiopppp.const
from aiopppp.packets import (
make_punch_pkt,
make_p2palive_pkt,
parse_drw_pkt,
make_drw_ack_pkt,
BinaryCmdPkt,
xq_bytes_decode,
DrwPkt,
)
from aiopppp.types import DeviceID
class UDPProtocol(asyncio.DatagramProtocol):
def __init__(self, on_receive):
super().__init__()
self.on_receive = on_receive
def connection_made(self, transport):
self.transport = transport
def datagram_received(self, data, addr):
self.on_receive(data, addr)
async def create_udp_server(port, on_receive):
# Bind to localhost on UDP port
loop = asyncio.get_running_loop()
transport, _ = await loop.create_datagram_endpoint(
lambda: UDPProtocol(on_receive),
local_addr=('127.0.0.1', port),
allow_broadcast=True,
)
return transport
class BinaryCamera:
def __init__(self):
self.transport = None
self.dev_id = DeviceID('TEST',123456, 'CAMERA')
self.input = asyncio.Queue()
self.output = asyncio.Queue()
self.client_addr = None
self.ticket = b'abcd'
self.cmd_idx = 1
def on_receive(self, data, addr):
# print(f"Received {data} from {addr}")
if data[0] == aiopppp.const.CAM_MAGIC:
self.input.put_nowait((data, addr))
async def send_task(self):
while True:
data, addr = await self.output.get()
assert isinstance(data, bytes), 'Data must be bytes'
if data[1] in [aiopppp.const.PacketType.PunchPkt.value, aiopppp.const.PacketType.P2PAlive.value]:
print(f'Send {data} to {addr}')
self.transport.sendto(data, addr)
elif self.client_addr:
print(f'Send {data}')
self.transport.sendto(data, self.client_addr)
self.output.task_done()
async def send_p2p_rdy_set(self):
pkt = make_punch_pkt(self.dev_id)
pkt.type = aiopppp.const.PacketType.P2pRdy
for _ in range(10):
self.output.put_nowait((bytes(pkt), self.client_addr))
await asyncio.sleep(0.1)
async def process_drw(self, data):
cmd_header_len = 12
pkt = parse_drw_pkt(data[4:])
self.output.put_nowait((bytes(make_drw_ack_pkt(pkt)), self.client_addr))
payload = pkt.get_drw_payload()
# print('... DRW payload:', payload)
if payload[:2] == BinaryCmdPkt.START_CMD:
cmd_id = aiopppp.const.BinaryCommands(int.from_bytes(payload[2:4], "little"))
data = payload[cmd_header_len:]
if len(data) > 4:
data = xq_bytes_decode(data, 4)
2025-03-25 15:58:48 +04:00
if cmd_id == aiopppp.const.BinaryCommands.CMD_SYSTEM_USER_CHK:
2025-03-22 21:38:18 +04:00
INCORRECT_USER_RESP = '11 0a 20 11 0c 00 ff 00 00 00 00 00 57 56 6c 37 fe 01 01 01'
CORRECT_USER_RESP = '11 0a 20 11 04 00 ff 00 0e fc ff ff'
username, password = struct.unpack('<32s128s', data)
username = username.decode('utf-8').strip('\x00')
password = password.decode('utf-8').strip('\x00')
resp = CORRECT_USER_RESP if username == 'admin' and password == 'admin' else INCORRECT_USER_RESP
print('... BinaryCommand: cmd_id:', cmd_id, 'data:', data)
await asyncio.sleep(0.3)
2025-03-25 15:58:48 +04:00
print('... send ACK_SYSTEM_USER_CHK')
2025-03-22 21:38:18 +04:00
self.output.put_nowait((bytes(
DrwPkt(cmd_idx=0, channel=0, drw_payload=bytes.fromhex(resp)),
), self.client_addr))
# self.output.put_nowait((bytes(BinaryCmdPkt(
# cmd_idx=self.cmd_idx,
2025-03-25 15:58:48 +04:00
# command=aiopppp.const.BinaryCommands.ACK_SYSTEM_USER_CHK,
2025-03-22 21:38:18 +04:00
# ticket=self.ticket,
# cmd_payload=b'\xff\x00\x00\x00',
# )), self.client_addr))
self.cmd_idx += 1
2025-03-25 15:58:48 +04:00
elif cmd_id == aiopppp.const.BinaryCommands.CMD_SYSTEM_STATUS_GET:
2025-03-22 21:38:18 +04:00
await asyncio.sleep(0.3)
2025-03-25 15:58:48 +04:00
print('... send ACK_SYSTEM_STATUS_GET')
2025-03-22 21:38:18 +04:00
self.output.put_nowait(
(
bytes(
BinaryCmdPkt(
cmd_idx=self.cmd_idx,
2025-03-25 15:58:48 +04:00
command=aiopppp.const.BinaryCommands.ACK_SYSTEM_STATUS_GET,
2025-03-22 21:38:18 +04:00
# token=b'\x0a\xfc\xff\xff',
token=b"\x00\x00\x00\x00",
# cmd_payload=bytes(range(0x15)),
cmd_payload=bytes.fromhex(
"0d 02 01 3d 74 0f 00 00 00 00 00 00 ff ff ff ff bf ff ff ff "
"01 01 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
"00 00 00 00 00 00 00 00 00 01 00 00 02 00 00 00 00 00 00 00 "
"00 00 00 00 00 ff ff ff 00 00 00 00 ff ff ff ff 00 00 00 00 "
"00 00 00 00",
),
)
),
self.client_addr,
)
)
async def on_packet(self, data, addr):
print(f"Received packet: {data} {addr}")
if data[1] == aiopppp.const.PacketType.LanSearch.value:
print('Received LanSearch packet')
self.output.put_nowait((bytes(make_punch_pkt(self.dev_id)), addr))
if data[1] == aiopppp.const.PacketType.PunchPkt.value:
self.client_addr = addr
print('Received PunchPkt packet, starting p2prdy sending')
asyncio.create_task(self.send_p2p_rdy_set())
# self.output.put_nowait((bytes(make_punch_pkt(self.dev_id)), addr
elif data[1] == aiopppp.const.PacketType.P2PAlive.value:
self.output.put_nowait((bytes(make_p2palive_pkt()), addr))
elif data[1] == aiopppp.const.PacketType.Drw.value:
asyncio.create_task(self.process_drw(data))
async def receive_task(self):
while True:
data, addr = await self.input.get()
await self.on_packet(data, addr)
self.input.task_done()
async def run(self):
self.transport = await create_udp_server(32108, self.on_receive)
out_t = asyncio.create_task(self.send_task())
in_t = asyncio.create_task(self.receive_task())
await asyncio.gather(*[out_t, in_t])
async def main():
camera = BinaryCamera()
await camera.run()
asyncio.run(main())