Experimental Python client for Reolink cameras
Project description
PyNeolink
PyNeolink is a Python client for Reolink/Neolink-style Baichuan cameras. It focuses on UID/P2P access, camera information, SD-card recordings, live viewing, snapshots, local recording, motion events, battery status, voice/talk, and siren control.
Version: 0.3.1 alpha.
This project was developed with OpenAI Codex as an AI-assisted implementation effort. It is a Python port inspired by and based on protocol knowledge from the Rust neolink project, especially QuantumEntangledAndy/neolink and surfzoid/neolink. The reverse-engineering foundation belongs to the Neolink contributors. The goal is not to replace Neolink, but to make a working Python implementation available for people who want to study, adapt, or extend this protocol without working in Rust.
Neolink and Reolink protocol support are reverse engineered. This project is not affiliated with Reolink.
Status
PyNeolink is experimental alpha software. It works against a limited set of real cameras, but Reolink firmware and model behavior can differ. APIs may change before 1.0.0.
What Works
- JSON camera configuration with
addressoruid - Reolink UID/P2P lookup, registration, UDP relay, and local UDP connection
- Baichuan login and command framing
- BC XOR encryption and AES-CFB support through
cryptography - Camera information, UID, LED command, and reboot command
- Battery status, including reconnect and online polling modes
- SD-card recording list with pagination and time sorting
- SD-card recording download with high/low quality selection
- Snapshot download to bytes or JPEG file
- Local MPEG-TS recording from the live stream
- Live HTTP MPEG-TS viewing with H264/H265 video and AAC audio
- HLS timeshift viewing with an in-memory sliding buffer
- Motion status and motion event watch mode
- Two-way voice/talk from microphone, audio file, or generated test tone
- Camera siren trigger
- PIR status and PIR on/off settings
- IR light status and IR on/off/auto settings
Current Limits
- This is reverse engineered and tested against a small number of real cameras, so behavior may differ between models and firmware versions.
- Voice file playback uses
ffmpeg/ffprobefor format validation and conversion. Install FFmpeg and make sure both commands are available inPATH. - Microphone voice input needs the Python
sounddevicepackage and a working local input device. - Local stream recording writes MPEG-TS (
.ts) files, not MP4. - SD-card
remove()andformat()exist, but are intentionally guarded. - PTZ, image settings, alarm schedules, floodlight settings, and Web UI are not implemented yet.
Install
From PyPI:
python -m pip install pyneolink==0.3.1
With microphone voice input support:
python -m pip install "pyneolink[voice]==0.3.1"
For local development from a checkout:
python -m venv .venv
.venv\Scripts\activate
python -m pip install -e ".[dev,voice]"
The cryptography package is required for AES-encrypted cameras. It is installed automatically when installing the package.
FFmpeg
FFmpeg is a system dependency for audio-file voice playback and some media conversion paths. PyNeolink expects both ffmpeg and ffprobe to be available in PATH.
Official FFmpeg download page:
- Windows: https://ffmpeg.org/download.html
- Linux: https://ffmpeg.org/download.html
- macOS: https://ffmpeg.org/download.html
On Linux and macOS, using the OS package manager is usually the simplest path when available. On Windows, install one of the builds linked from the official FFmpeg download page and add its bin directory to PATH.
Configuration
Create a local config.json:
{
"bind": "0.0.0.0",
"bind_port": 8554,
"cameras": [
{
"name": "Home-Front",
"username": "admin",
"password": "password",
"uid": "ABCDEF0123456789",
"discovery": "relay"
}
]
}
config.json is ignored by Git because it can contain camera credentials.
CLI
Camera info:
python pyneolink/cli.py info --camera "Home-Front" --config config.json
python pyneolink/cli.py --info --camera "Home-Front"
Battery:
python pyneolink/cli.py battery --camera "Home-Front"
python pyneolink/cli.py battery --camera "Home-Front" --watch --interval 60
python pyneolink/cli.py battery --camera "Home-Front" --watch --interval 60 --mode online
Snapshots and local recording:
python pyneolink/cli.py snapshot --camera "Home-Front" --out snapshots/
python pyneolink/cli.py record --camera "Home-Front" --out recordings/ --duration 30 --quality high
python pyneolink/cli.py record --camera "Home-Front" --out recordings/live.ts --quality low
Motion:
python pyneolink/cli.py motion --camera "Home-Front"
python pyneolink/cli.py motion --camera "Home-Front" --watch --duration 30
PIR:
python pyneolink/cli.py pir --config config.json --camera "Home-Front" status
python pyneolink/cli.py pir --config config.json --camera "Home-Front" on
python pyneolink/cli.py pir --config config.json --camera "Home-Front" off
IR light:
python pyneolink/cli.py ir --config config.json --camera "Home-Front" status
python pyneolink/cli.py ir --config config.json --camera "Home-Front" on
python pyneolink/cli.py ir --config config.json --camera "Home-Front" off
python pyneolink/cli.py ir --config config.json --camera "Home-Front" auto
python pyneolink/cli.py led --config config.json --camera "Home-Front" auto
Voice and siren:
python pyneolink/cli.py voice --camera "Home-Front" --file alert.mp3
python pyneolink/cli.py voice --camera "Home-Front" --microphone --seconds 10
python pyneolink/cli.py voice --camera "Home-Front" --tone 1000 --seconds 3
python pyneolink/cli.py voice --camera "Home-Front" --siren
Live view server:
python pyneolink/cli.py serve --config config.json
Open direct MPEG-TS in VLC/ffplay:
http://127.0.0.1:8554/Home-Front/high
http://127.0.0.1:8554/Home-Front/low
Open HLS timeshift in VLC/ffplay:
http://127.0.0.1:8554/Home-Front/high/hls.m3u8
http://127.0.0.1:8554/Home-Front/low/hls.m3u8
HLS keeps a sliding in-memory buffer. By default it stores up to 100 MB and cuts segments around 2 seconds:
python pyneolink/cli.py serve --config config.json --hls-buffer-mb 100 --hls-segment-seconds 2
When binding to 0.0.0.0, do not open 0.0.0.0 in VLC. Use 127.0.0.1 on the same PC or the PC's LAN IP from another device.
Library Use
Camera information:
from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
info = camera.info()
print(info)
Battery:
from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
battery = camera.battery()
with battery.info() as info:
print(info["level_percent"], info["is_charging"], info["adapter_status"])
with battery.info(interval=60, mode="reconnect", count=3) as updates:
for update in updates:
print(update["level_percent"], update["adapter_status"])
SD-card download:
from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
sd = camera.sd_card()
files = sd.list(start="2026-06-03", end="2026-06-03")
videos = sd.filter(files, name=".mp4")
if videos:
sd.download(videos[-1], "downloads", quality="high", progress=True)
Motion:
from pyneolink import Camera, EVENTS
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
motion = camera.motion()
print(motion.status())
with motion.watch(duration=30) as events:
for event in events:
if event == EVENTS.human and event.active:
print("human detected")
Snapshot and local recording:
from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
camera.snapshot(out="snapshots")
camera.record(out="recordings", duration=30, stream="mainStream")
Voice and siren:
from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
voice = camera.voice()
voice.play("alert.mp3")
voice.siren()
Settings and PIR:
from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
settings = camera.settings()
print(settings.pir.status())
settings.pir.on()
settings.pir.off()
print(settings.ir.status())
settings.ir.on()
settings.ir.off()
settings.ir.auto()
Live stream server from a dict:
from pyneolink import StreamServer
config = {
"bind": "0.0.0.0",
"bind_port": 8554,
"cameras": [
{
"name": "Home-Front",
"username": "admin",
"password": "password",
"uid": "ABCDEF0123456789",
"discovery": "relay",
}
],
}
StreamServer(config, buffer_seconds=1.5, hls_buffer_mb=100, hls_segment_seconds=2).serve_forever()
Examples
See the examples/ directory:
camera_example.py: info, snapshot, LED, and guarded reboot helperssd_card_example.py: list, filter, download, remove, and guarded format callsbattery_example.py: one-shot battery status plus reconnect/online pollingmotion_example.py: motion status and watch moderecord_example.py: duration and manual local stream recordingvoice_example.py: file, microphone, tone, and siren helperssettings_example.py: PIR and IR status plus guarded setting helpersstream_example.py: live MPEG-TS and HLS timeshift server from a dict config
Each example keeps camera settings as a small local dict near the top of the file. Edit those values directly or replace the dict with your own configuration loader.
Internals
See docs/ for a sorted internal documentation set that explains the core files, connection flow, login, encryption, Baichuan messages, UDP/P2P transport, SD-card downloads, media streaming, motion, voice, and camera controls.
Credits
PyNeolink exists because of the reverse-engineering work done by the Neolink community. In particular, the Rust neolink implementations and documentation were used as the protocol reference for this Python port.
This Python implementation was developed with OpenAI Codex. Human testing against real cameras guided the implementation and bug fixes.
Reference
The Rust neolink/ checkout can be kept locally as a protocol reference, but it is ignored by Git in this workspace. The Python code here is intended to be understandable and hackable for people who want to experiment with Reolink cameras from Python.
Tested Cameras
- Reolink Argus Eco
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyneolink-0.3.1.tar.gz.
File metadata
- Download URL: pyneolink-0.3.1.tar.gz
- Upload date:
- Size: 97.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
18a5ddd01fb6be64b3a1636052a0e420ecb5192ddac20d8ab0f335b8a3f7f53b
|
|
| MD5 |
51af41cca1016236bef91883183fab92
|
|
| BLAKE2b-256 |
a033c6c186835f8cf62491073c169b59d763ca4ef732b03a35fea8803d1a256f
|
File details
Details for the file pyneolink-0.3.1-py3-none-any.whl.
File metadata
- Download URL: pyneolink-0.3.1-py3-none-any.whl
- Upload date:
- Size: 73.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e78e5ff2cdf9029eaf94ee54cfd47e585a87cd28c671b57bbbb8e0e58b4570c6
|
|
| MD5 |
59afa0db34d41682588985c85dba281f
|
|
| BLAKE2b-256 |
7f74fa06b9c63a52c94b4569a04080b07e6412940773f7633b62d74c1d0800fd
|