Imou on Linux – Run Your IP Camera Without Windows or the Cloud

Imou on Linux — local RTSP, ONVIF and DVRIP control

Imou on Linux

> 🚀 Full local access to an Imou / Lechange (Dahua-chipset) camera from Linux — live view, image and night tuning, motion detection, SD-card inventory and 24/7 local recording. No vendor app, no cloud, no Windows.

For two years the only way to look at my camera was to boot Windows and open the vendor app. Not because Linux is missing something, but because of how these cameras are actually sold.

🔍 The problem

Imou ships no Linux client. That sounds like the usual “unlucky, no drivers” story. It is not.

The Windows and mobile apps are not LAN clients. They log in to Imou’s cloud and tunnel a peer-to-peer connection down to the device. The app never talks to the camera on your own network. That is the entire reason the camera “needs Windows” — there is nothing to port, because the client is not the thing talking to the hardware.

Meanwhile the camera itself speaks standard protocols. It has to: that is how it talks to an NVR, to Home Assistant, to anything that is not the vendor’s app.

So I mapped what it actually serves on the local network. Everything below was verified against a real device, not read from a datasheet.

⚖️ What the camera actually speaks

  • ONVIF — port 80. Device information, media profiles, imaging, events. Answers GetDeviceInformation without credentials on most devices, so you can identify the model before you even have the password.
  • Snapshot — port 80. A plain JPEG over HTTP digest. The cheapest possible proof that everything works.
  • RTSP — port 554. The real thing: 1080p HEVC main stream and a 640×480 H.264 sub stream.
  • DVRIP — port 37777. The Dahua binary protocol. This is the surface nobody documents and the one that makes the whole project possible: it exposes configuration read/write and the SD-card inventory.
  • XAV — port 8086. A second live stream. Easy to miss in a port scan, and missing it leads to the wrong conclusion that the device is ONVIF-only.

Five interfaces, every one of them usable from Linux with stock tools. No reverse engineering of an encrypted protocol, no bypassing anything — just finding the doors that were already open to anything holding the camera’s own password.

⚠️ The flag that costs you an evening

This one is worth the whole article, because it is the trap that makes people give up and conclude “the Linux tools just don’t work with this camera”.

Request the RTSP stream with the correct password and you get 403 Forbidden. Perfectly valid credentials, hard rejection. You try the sub stream, you try digest auth, you re-check the password, you start suspecting a lockout.

The stream works fine. It just refuses to start without the ONVIF flag on the URL:

rtsp://USER:PASS@CAMERA-IP:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
                                                                                ^^^^^^^^^^^

Without &proto=Onvif you get 403 on a password that is completely correct. With it, the stream opens immediately. If you take one line away from this post, take that one.

✨ What I built

A control centre, because remembering six command lines is not a way to check on your house at 2am. It is a single Python window — live preview on the left, everything else on the right.

  • Image — brightness, contrast, saturation, hue, gamma, chroma suppression and style, written back to the camera.
  • Night — auto / colour / black-and-white, IR LED, switch sensitivity, white balance, anti-flicker, mirror and flip.
  • Motion — enable and sensitivity, read from and written to the device.
  • SD — card capacity plus every clip with times, duration, size and the motion events attached to it.
  • Record — the local recorder, start and stop.
  • Settings — autostart, recorder on/off, segment length, retention, and a toggle for the camera’s own SD recording.

The settings toggles read their state from the live system when the window opens — from systemd and from the camera — so they show what is actually true rather than what you last clicked. That sounds obvious until you have a toggle that confidently says “recording: off” while the camera is writing to its card, because one config read failed and the code rendered the failure as off. It does not do that any more.

🚀 Using it

git clone https://github.com/dailybarid/imou-linux.git
cd imou-linux
./install.sh              # app + icon + menu entry
./install.sh --service    # ...and the 24/7 recorder as a systemd service

The installer asks for the camera’s own account — the device password you set in the vendor app, not your Imou cloud login — and writes it to a 600 file. Then it builds a virtualenv on purpose: the distro’s python3-pil ships Pillow without ImageTk, so the GUI dies instantly and silently when launched from a menu. That one cost me a confusing half hour of “it works in my terminal but not from the menu”.

Everything is also scriptable:

./imou_sd.py --storage          # card usage
./imou_sd.py --list 7           # every clip from the last 7 days
./imou_sd.py --list 7 --json    # same, machine-readable
./imou-record.sh --test 20      # record 20 seconds to prove the pipeline

🛠️ Under the hood

The interesting part is DVRIP on port 37777, a binary protocol with JSON bodies. Two details cost real time and are worth recording here:

  • The search token comes back in result, not params. Reading the wrong field gives you “no such token”, which looks exactly like a permissions problem.
  • setConfig takes the whole table. It is a round-trip, not a patch: read the table, change the one field you care about, write the entire thing back.

Recording schedules are bitmasks. 393222 means “record all day”, 0 means “do not” — which is all the SD toggle in the Settings tab actually does.

📷 What is not possible, honestly

This is the part most project pages leave out, and the part that decides whether the tool is useful to you.

  • You cannot pull the SD card’s video files over the network on this firmware. The metadata is readable — every clip, its timestamps, its size — but the bytes are not exposed. Dahua’s RPC_Loadfile authenticates and then answers 500; RTSP /cam/playback answers 404 for every timestamp I could construct. Playback in the vendor app goes through their cloud relay. That is why the project records the live stream to your own disk instead: plain MP4 files, your filesystem, no relay.
  • PTZ is advertised but absent. ONVIF lists a PTZ service; the hardware is a fixed lens with no motors. A fixed-lens model listing PTZ is not a bug you can fix, it is marketing.
  • ter:NotImplemented means exactly that. Consumer firmware implements a subset of ONVIF and answers the rest politely. Do not retry it with different parameters.

If you do pull the microSD, the .dav clips are not locked away either — ffmpeg carries a native dhav demuxer and remuxes them losslessly:

ffmpeg -f dhav -i clip.dav -c copy clip.mp4

📦 Getting it

The code is MIT licensed and the full protocol write-up ships with it — ports, the proto=Onvif trap, the DVRIP calls, the bitmask meaning, and the list of what the firmware refuses to do. If you own a different Imou, Lechange, Dahua or Amcrest camera on the same firmware family, that document is the useful part.

The recorder runs as a systemd user service with loginctl enable-linger, so it keeps recording after you log out — 300-second segments, about 1.7 GB a day, pruned to whatever retention you set.

📜 License

MIT. Use it, fork it, point it at your own camera.

💡 Author

Built by Khaled Mimoune. Source code and the full protocol notes: github.com/dailybarid/imou-linux.

share:


Discover more from Khaled Mimoune

Subscribe to get the latest posts sent to your email.

Comments

Leave a Reply