RSS Amplifier

PyPI recent updates · Aug 21, 2026

pyneolink 0.4.2

0
Sign in to vote or save

PyPI

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.4.2 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 address or uid
  • 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
  • SD-card preview playback cache with an HTTP stream helper for players such as VLC
  • Snapshot download to bytes or JPEG file
  • Local MPEG-TS recording from the live stream with Camera.record() or the record CLI command
  • 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/ffprobe for format validation and conversion. Install FFmpeg and make sure both commands are available in PATH.
  • Microphone voice input needs the Python sounddevice package and a working local input device.
  • Local stream recording writes MPEG-TS (.ts) files, not MP4. It records the live stream on the client machine; it does not start or stop the camera's own SD-card recording schedule.
  • SD-card remove() and format() 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.4.2

With microphone voice input support:

python -m pip install "pyneolink[voice]==0.4.2"

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:

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/ --stream-type main
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

record opens the selected live stream and saves it locally as MPEG-TS. Omit --duration to record until Ctrl+C. This is not a command to force the camera to start writing a new SD-card recording.

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()
    videos = sd.files(start="2026-06-03", end="2026-06-03", name=".mp4")
    if videos:
        videos[-1].download("downloads", quality="high", rewrite_exists=False, 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:

from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
    camera.snapshot(out="snapshots", stream_type="main")
    image = camera.snapshot(stream_type="sub")

Local recording from the SDK:

import time
from pyneolink import Camera
with Camera(uuid="ABCDEF0123456789", username="admin", password="password") as camera:
    path = camera.record(out="recordings", duration=30, stream="mainStream")
    print(f"saved to {path}")
    with camera.record(out="recordings/live.ts", stream="subStream") as recorder:
        time.sleep(10)
    print(f"saved to {recorder.path}")

Camera.record() records the live stream on the client machine and writes MPEG-TS (.ts). With duration, it waits and returns the final Path. Without duration, it returns a running StreamRecorder; use it as a context manager or call stop() to finalize the file. This does not enable or disable the camera's own SD-card recording.

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 helpers
  • sd_card_example.py: list, filter, download, preview playback, remove, and guarded format calls
  • battery_example.py: one-shot battery status plus reconnect/online polling
  • motion_example.py: motion status and watch mode
  • record_example.py: duration and manual local stream recording
  • voice_example.py: file, microphone, tone, and siren helpers
  • settings_example.py: PIR and IR status plus guarded setting helpers
  • stream_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

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 name

Interpreter

ABI

Platform

File details

Details for the file pyneolink-0.4.2.tar.gz.

File metadata

  • Download URL: pyneolink-0.4.2.tar.gz
  • Upload date:
  • Size: 122.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for pyneolink-0.4.2.tar.gz
Algorithm Hash digest
SHA256 53aabb08df09cf4439a6210d2685a1f26bfa5852d199f2a60e56f60b0f169778
MD5 7e1b86988def8f5bef06a9cbe0dc29c1
BLAKE2b-256 f567b10238e53f5eb3c7aa61f68a047f5bbc7911da4b31272cb68de640195cd8

See more details on using hashes here.

File details

Details for the file pyneolink-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: pyneolink-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 93.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for pyneolink-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 dfff0d00f56ca02ddc0f6a3c0a7a3a64d904cdc0fe9542116ccea49804329f3b
MD5 c404ae1878f2d4a7709fd0fdb9a999bd
BLAKE2b-256 6274615ed3462ac88231b09f0988b941fa266528f0d9b40fb7c104325e20171c

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

Read the original on pypi.org

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.