mirror of
https://github.com/UGA-Innovation-Factory/UR10-Sentry.git
synced 2025-12-19 08:56:11 +00:00
robot looks at you
This commit is contained in:
75
UnifiWebsockets/Unifi.py
Normal file
75
UnifiWebsockets/Unifi.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import asyncio
|
||||
import queue
|
||||
import json
|
||||
import requests
|
||||
import websockets
|
||||
import ssl
|
||||
import re
|
||||
import dotenv
|
||||
|
||||
# from UnifiWebsockets.decode import decode_packet
|
||||
|
||||
|
||||
# REST login script to get the CSRF token
|
||||
def get_token(base_url, username, password):
|
||||
login_url = f"{base_url}/api/auth/login"
|
||||
payload = {"username": username, "password": password}
|
||||
headers = {"Content-Type": "application/json"}
|
||||
try:
|
||||
session = requests.Session()
|
||||
response = session.post(login_url, json=payload, headers=headers, verify=False)
|
||||
response.raise_for_status() # Raise an exception for HTTP errors
|
||||
return session.cookies
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Failed to obtain token: {e}")
|
||||
return None, None
|
||||
|
||||
|
||||
# Function to connect to the websocket and print the event stream
|
||||
async def listen_to_event_stream(ws_url, cookies, queue):
|
||||
cookie_header = "".join([f"{key}={value}" for key, value in cookies.items()])
|
||||
headers = {"Cookie": cookie_header}
|
||||
|
||||
ssl_context = ssl._create_unverified_context() # Create an unverified SSL context
|
||||
try:
|
||||
async with websockets.connect(
|
||||
ws_url, extra_headers=headers, ssl=ssl_context
|
||||
) as websocket:
|
||||
while True:
|
||||
try:
|
||||
message = await websocket.recv()
|
||||
message = str(message)
|
||||
|
||||
queue.put(find_coords(message))
|
||||
# print(find_coords(message))
|
||||
|
||||
# coords_begin = message.find('coord') + 7
|
||||
# coords_end = message[coords_begin:].find(']')
|
||||
# print(message[coords_begin:])
|
||||
|
||||
# print(decode_packet(message))
|
||||
except websockets.ConnectionClosed:
|
||||
print("Connection closed")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"WebSocket connection failed: {e}")
|
||||
|
||||
|
||||
def find_coords(message: str) -> list[str]:
|
||||
coords_begin = [m.start() + 8 for m in re.finditer('"coord":', message)]
|
||||
coords_end = [m.start() + 1 for m in re.finditer('],"depth":', message)]
|
||||
coords = [
|
||||
json.loads(message[coords_begin[i] : coords_end[i]])
|
||||
for i in range(len(coords_begin))
|
||||
]
|
||||
return coords
|
||||
|
||||
|
||||
def run(q: queue.Queue) -> None:
|
||||
base_url = "https://172.22.114.176" # Replace with your UniFi Protect base URL
|
||||
username = "engr-ugaif" # Replace with your username
|
||||
password = dotenv.get_key(dotenv.find_dotenv(), "UNIFI_PASSWORD")
|
||||
ws_url = "wss://172.22.114.176/proxy/protect/ws/liveDetectTrack?camera=668daa1e019ce603e4002d31" # Replace with your WebSocket URL
|
||||
|
||||
cookies = get_token(base_url, username, password)
|
||||
asyncio.run(listen_to_event_stream(ws_url, cookies, q))
|
||||
108
UnifiWebsockets/decode.py
Normal file
108
UnifiWebsockets/decode.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import zlib
|
||||
import struct
|
||||
import json
|
||||
from typing import Tuple, Union
|
||||
|
||||
# Constants
|
||||
EVENT_PACKET_HEADER_SIZE = 8
|
||||
|
||||
# Packet Types
|
||||
HEADER = 1
|
||||
PAYLOAD = 2
|
||||
|
||||
# Payload Types
|
||||
JSON_TYPE = 1
|
||||
STRING_TYPE = 2
|
||||
BUFFER_TYPE = 3
|
||||
|
||||
def decode_packet(packet: bytes) -> Union[Tuple[dict, Union[dict, str, bytes]], None]:
|
||||
try:
|
||||
data_offset = struct.unpack('>I', packet[ProtectEventPacketHeader.PAYLOAD_SIZE:ProtectEventPacketHeader.PAYLOAD_SIZE + 4])[0] + EVENT_PACKET_HEADER_SIZE
|
||||
|
||||
if len(packet) != (data_offset + EVENT_PACKET_HEADER_SIZE + struct.unpack('>I', packet[data_offset + ProtectEventPacketHeader.PAYLOAD_SIZE:data_offset + ProtectEventPacketHeader.PAYLOAD_SIZE + 4])[0]):
|
||||
raise ValueError("Packet length doesn't match header information.")
|
||||
except Exception as error:
|
||||
print(f"Error decoding update packet: {error}")
|
||||
return None
|
||||
|
||||
header_frame = decode_frame(packet[:data_offset], HEADER)
|
||||
payload_frame = decode_frame(packet[data_offset:], PAYLOAD)
|
||||
|
||||
if not header_frame or not payload_frame:
|
||||
return None
|
||||
|
||||
return header_frame, payload_frame
|
||||
|
||||
def decode_frame(packet: bytes, packet_type: int) -> Union[dict, str, bytes, None]:
|
||||
frame_type = packet[ProtectEventPacketHeader.TYPE]
|
||||
|
||||
if packet_type != frame_type:
|
||||
return None
|
||||
|
||||
payload_format = packet[ProtectEventPacketHeader.PAYLOAD_FORMAT]
|
||||
is_deflated = packet[ProtectEventPacketHeader.DEFLATED]
|
||||
|
||||
if is_deflated:
|
||||
payload = zlib.decompress(packet[EVENT_PACKET_HEADER_SIZE:])
|
||||
else:
|
||||
payload = packet[EVENT_PACKET_HEADER_SIZE:]
|
||||
|
||||
if frame_type == HEADER:
|
||||
if payload_format == JSON_TYPE:
|
||||
return json.loads(payload.decode('utf-8'))
|
||||
else:
|
||||
return None
|
||||
|
||||
if payload_format == JSON_TYPE:
|
||||
return json.loads(payload.decode('utf-8'))
|
||||
elif payload_format == STRING_TYPE:
|
||||
return payload.decode('utf-8')
|
||||
elif payload_format == BUFFER_TYPE:
|
||||
return payload
|
||||
else:
|
||||
print(f"Unknown payload packet type received in the realtime events API: {payload_format}")
|
||||
return None
|
||||
|
||||
def decode_packet_from_mac(packet: bytes, mac: str) -> Union[Tuple[dict, Union[dict, str, bytes]], None]:
|
||||
try:
|
||||
data_offset = struct.unpack('>I', packet[ProtectEventPacketHeader.PAYLOAD_SIZE:ProtectEventPacketHeader.PAYLOAD_SIZE + 4])[0] + EVENT_PACKET_HEADER_SIZE
|
||||
|
||||
if len(packet) != (data_offset + EVENT_PACKET_HEADER_SIZE + struct.unpack('>I', packet[data_offset + ProtectEventPacketHeader.PAYLOAD_SIZE:data_offset + ProtectEventPacketHeader.PAYLOAD_SIZE + 4])[0]):
|
||||
raise ValueError("Packet length doesn't match header information.")
|
||||
except Exception as error:
|
||||
print(f"Error decoding update packet: {error}")
|
||||
return None
|
||||
|
||||
header_frame = decode_frame(packet[:data_offset], HEADER)
|
||||
|
||||
if not header_frame:
|
||||
return None
|
||||
|
||||
# Check if modelKey is 'camera' and mac matches
|
||||
if header_frame.get('modelKey') != 'camera' or header_frame.get('mac') != mac:
|
||||
return None
|
||||
|
||||
payload_frame = decode_frame(packet[data_offset:], PAYLOAD)
|
||||
|
||||
if not payload_frame:
|
||||
return None
|
||||
|
||||
return header_frame, payload_frame
|
||||
|
||||
class ProtectEventPacketHeader:
|
||||
TYPE = 0
|
||||
PAYLOAD_FORMAT = 1
|
||||
DEFLATED = 2
|
||||
UNKNOWN = 3
|
||||
PAYLOAD_SIZE = 4
|
||||
|
||||
# Example usage:
|
||||
# packet = b'...' # binary string from websocket
|
||||
# mac_address = 'XX:XX:XX:XX:XX:XX' # Replace with the desired MAC address
|
||||
# result = decode_packet_from_mac(packet, mac_address)
|
||||
# if result:
|
||||
# action_frame, data_frame = result
|
||||
# print("Action Frame:", action_frame)
|
||||
# print("Data Frame:", data_frame)
|
||||
# else:
|
||||
# print("No matching packet found.")
|
||||
Reference in New Issue
Block a user