diff --git a/README.md b/README.md index 6615009..2d3d3b0 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,10 @@ access to settings not exposed by the official API, consider [uiprotect](https://github.com/uilibs/uiprotect) — an unofficial Python library wrapping the private UniFi Protect API. -## Run +## Scripts ```sh -uv run main.py +uv run play_speaker.py # play hello.wav through the camera speaker +uv run record_mic.py # record from the camera mic (Ctrl+C to stop) +uv run dump_camera.py # dump camera data from integration + private API ``` diff --git a/main.py b/play_speaker.py similarity index 100% rename from main.py rename to play_speaker.py diff --git a/record_mic.py b/record_mic.py new file mode 100644 index 0000000..af5125d --- /dev/null +++ b/record_mic.py @@ -0,0 +1,62 @@ +import json +import os +import subprocess +from datetime import datetime + +import httpx +from dotenv import load_dotenv + +load_dotenv() + +HOST = os.environ["HOST"] +PRIVATE_BASE = f"https://{HOST}/proxy/protect/api" +USERNAME = os.environ["UNIFI_USERNAME"] +PASSWORD = os.environ["UNIFI_PASSWORD"] + +CAMERA_NAME = "G6 Pro 360" +OUTPUT_DIR = "./data" + + +def main(): + body = json.dumps({"username": USERNAME, "password": PASSWORD, "rememberMe": False}).encode() + + # Accept-Encoding must be suppressed at request level — server returns 403 when present + with httpx.Client(verify=False, http2=True) as client: + client.post( + f"https://{HOST}/api/auth/login", + content=body, + headers={"Content-Type": "application/json", "Accept-Encoding": ""}, + ).raise_for_status() + + cameras = client.get(f"{PRIVATE_BASE}/cameras").raise_for_status().json() + camera = next(c for c in cameras if CAMERA_NAME in c["name"]) + + rtsp_alias = next(ch["rtspAlias"] for ch in camera["channels"] if ch.get("isRtspEnabled")) + rtsp_url = f"rtsps://{HOST}:7441/{rtsp_alias}?enableSrtp" + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output = f"{OUTPUT_DIR}/recording_{timestamp}.wav" + + print(f"Recording from: {camera['name']}") + print(f"Output: {output}") + print("Press Ctrl+C to stop.") + + proc = subprocess.Popen([ + "ffmpeg", "-y", + "-rtsp_transport", "tcp", + "-i", rtsp_url, + "-vn", + "-acodec", "pcm_s16le", + output, + ]) + + try: + proc.wait() + except KeyboardInterrupt: + proc.terminate() + proc.wait() + print(f"\nSaved to {output}") + + +if __name__ == "__main__": + main()