A custom Home Assistant integration for NVR/DVR devices built on the XMEye firmware family (also known as NetSurveillance, Sofia, or DVRIP — TCP port 34567). This covers a large number of rebranded/OEM Chinese DVRs and NVRs (Xiongmai-family chipsets), including hybrid HVRs with analog (AHD) inputs and pure IP/digital NVRs.
Supports multiple devices at once — each registrator is added as its own config entry, so you can mix e.g. a DVR at home, one in the garage, and one at a family member's place, all in the same Home Assistant instance.
camera— live view per channel (RTSP), on-demand snapshot, and a choice between the Main and Sub RTSP stream (see below)binary_sensor— motion / face / person detection per channel, pushed in real time from the device (not polling)sensor— uptime, firmware version, number of currently connected channelsxmeye_nvr.ptz_moveservice — PTZ (pan/tilt/zoom) control, if the camera on that channel physically supports it (see caveat below)
This integration intentionally does not include any archive/recording download features (browsing or exporting past footage, periodic snapshot scripts, etc.) — it focuses on live monitoring and alerting only. For that, see the companion CLI tools repo: xmeye-nvr-tools (device discovery, live alarm monitoring, periodic/post-alarm snapshot retrieval, archive download).
This integration is not (yet) in the default HACS store, but you can add it as a custom repository:
- HACS → the three-dot menu (top right) → Custom repositories.
- Repository:
https://github.com/Noneawe/ha-xmeye-nvr, category: Integration. - Install "XMEye NVR/DVR (DVRIP)" from the HACS list.
- Restart Home Assistant (Settings → System → Restart — required for HA to pick up the new integration code).
- Add the integration: Settings → Devices & services → Add integration → search for "XMEye NVR/DVR".
- Enter the device's IP address, port (default
34567), username and password. A dedicated, limited-permission account is strongly recommended — see the security section below. - Repeat step 5 for every additional device — each one becomes its own entry.
Copy custom_components/xmeye_nvr/ from this repository into your Home
Assistant config directory, so you end up with:
<config>/custom_components/xmeye_nvr/manifest.json
Then restart Home Assistant and continue from step 5 above.
The integration relies on the built-in ffmpeg Home Assistant component (for
snapshots and stream handling) and the ffmpeg binary itself. Both are already
present on Home Assistant OS / Supervised / the official container image; on a
Core/venv install you may need to install it manually (e.g. apt install ffmpeg).
- The
camera.*entities should show a live preview within a few seconds of adding the integration, and the snapshot button should work. - If a channel has no camera physically connected, its
cameraentity will correctly show asunavailable— this is expected, not a bug. - If something looks wrong: Settings → System → Logs, filter for
xmeye_nvr.
Settings → Devices & services → XMEye NVR/DVR → Configure lets you switch the RTSP stream used for live view and snapshots:
- Main (default) — full resolution, but requires the channel's encoding to be set to plain "H265" (not "H265+"/"Smart H265") on the device itself. With the "Smart" encoding variant, Main does not deliver video over RTSP at all (the same issue affects the official XMEye Pro app).
- Sub (substream) — lower resolution, but works regardless of that setting.
Changing this option reloads the integration automatically — no need to remove and re-add it.
Do not use the administrator account for this integration. Devices in this
firmware family have a protocol weakness: any logged-in user can read the
password hash (sofia_hash) of any other account via the Users command
(msg_id 1472). Since DVRIP login has no salt/challenge, that hash is
functionally equivalent to the plaintext password — so if the integration's
account is an admin account, its credentials are effectively exposed to
anyone who can reach the device (e.g. via captured network traffic, or a bug
in other software that also talks to it). This is a limitation of the device's
firmware, not something Home Assistant can fix.
Recommendation: create a dedicated, restricted account on the device
(Settings → User accounts), in the user group (not admin), with the
additional permission DigitalManager. This is the minimal permission set
confirmed to work for every feature of this integration:
Monitor_01 .. Monitor_10 (live view / RTSP / OPMonitor)
Replay_01 .. Replay_10 (playback — included by default in the "user" group)
DigitalManager (required for channel status — without it the
integration can't tell which cameras are
connected/offline)
Without DigitalManager, the integration still connects and shows live view,
but can't determine which channels are actually connected (camera entities
won't correctly report "unavailable" for empty channels).
If you plan to use the xmeye_nvr.ptz_move service, also add the
PTZControl permission — without it the service call fails with Ret=107
(permission denied).
An account with only these permissions cannot change network settings, user accounts, recording configuration, upgrade firmware, or reboot the device — so even if its hash leaks, the damage is far more limited than with an admin account.
Controls a PTZ (pan/tilt/zoom) camera, if the channel physically supports it.
Targets a specific camera entity:
service: xmeye_nvr.ptz_move
target:
entity_id: camera.hvr_channel_4
data:
direction: up # up / down / left / right / up_left / up_right /
# down_left / down_right / zoom_in / zoom_out
speed: 5 # 1-8, default 5
duration: 0.5 # seconds of movement before automatically stoppingImportant caveat, unlike the rest of this integration. Every other feature was verified against real hardware before being shipped. PTZ is the exception:
- The devices used to build this integration return success (
Ret=100) for literally any PTZ command content — real direction names, a random string, even a completely malformed payload all get the same "success". A successful service call is therefore not proof that the camera actually moved. - The test cameras used during development were fixed (no physical PTZ), so this could not be confirmed or denied visually.
- The direction/zoom command names in the code are unverified — based on
general community knowledge of the Xiongmai/DVRIP protocol family (the
python-dvrproject), not on confirmation against this specific firmware.
If you have a camera with physical PTZ and try this service, please open an issue and let us know whether it works (and which directions) — this will be used to correct the documentation/code.
Bug reports, hardware reports (different XMEye-family models/firmware) and pull requests are welcome — please open an issue.