Would be good to see a Dahua IP Camera Component

I don’t know if there is any other demand for this but I’ve struggled to use the limited information from the two other examples on the community for Dahua IP camera integration. It would be great if someone was able to develop / further develop these so there could be a Dahua camera component (API/MQTT events seem to be viable). These are great fully featured cameras for a reasonable price and good quality. I use the mini PTZ zoom dome and I thought they were popular. I can integrate the images no problem but the integration for motion detection (both video and IVS) and PTZ is a minefield.

Got this working in the end- a sensor that tracks movement and an automation that picks up on these, seems to be working fine for movement and tripwire on the Dahua IP Dome camera

Can you please share your setup and config. I havent got tripwire to work iam also using MQTT IPC component.

I’m running Pi3 with HA in VEnv and MQTT server. Back everything up on image, mine was OK but you never know. Dahua SD1A203T Dome Camera.

I have an input Boolean to turn the HA sensor (script) on/off;

input_boolean:
   cam_movement_sensor_on_off:
     name: Cam Movement Sensor On Off
     initial: off

Have a binary MQTT sensor detecting via script from the Dahua camera detection;

binary_sensor:
  - platform: mqtt
    state_topic: "home-assistant-1/cameras/motion"
    name: "Cam Movement Sensor"  

I have this script in a ‘scripts’ folder in same folder as the .yaml’s, named dahua.py (will need to input your Dahua camaera IP and MQTT information into this and save this script);

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import socket
import pycurl
import time
import shlex
import subprocess
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish

ALARM_DELAY = 2

#PLAY_TEMPLATE = "gst-launch-1.0 playbin uri=\"rtsp://{user}:{pass}@{host}:554/cam/realmonitor?channel=1&subtype=2\" latency=300000000 audio-sink=\"autoaudiosink sync=false\""
URL_TEMPLATE = "http://{host}:{port}/cgi-bin/eventManager.cgi?action=attach&codes=%5B{events}%5D"

CAMERAS = [
	{
		"host": "your IP cam IP address here",
		"port": your IP cam Port here,
		"user": "your username",
		"pass": "your password",
		"events": "VideoMotion"
	},
	{
		"host": "your IP cam IP address here",
		"port": your IP cam Port here,
		"user": "your username",
		"pass": "your password",
		"events": "CrossLineDetection"
	}
]

class DahuaCamera():
	def __init__(self, master, index, camera):
		self.Master = master
		self.Index = index
		self.Camera = camera
		self.CurlObj = None
		self.Connected = None
		self.Reconnect = None

		self.Alarm = dict({
			"Active": None,
			"Last": None
		})
		#self.Player = None

	#def StartPlayer(self):
	#	if self.Player:
	#		return

	#	print("[{0}] StartPlayer()".format(self.Index))
	#	self.Master.OnStartPlayer()

	#	Command = PLAY_TEMPLATE.format(**self.Camera)
	#	Args = shlex.split(Command)
	#	self.Player = subprocess.Popen(Args,
	#					stdin = subprocess.DEVNULL,
	#					stdout = subprocess.DEVNULL,
	#					stderr = subprocess.DEVNULL)

	#def StopPlayer(self):
	#	if not self.Player:
	#		return

	#	print("[{0}] StopPlayer()".format(self.Index))
	#	self.Player.kill()
	#	self.Player.wait()
	#	self.Player = None
	#	self.Master.OnStopPlayer()
		
	def SensorOn(self):
		client = mqtt.Client
		client.connect("your MQTT IP address, mine same as HA",1883,60)
		client.publish("home-assistant-1/cameras/motion", "ON");
		client.disconnect();
		
	def SensorOff(self)
		client = mqtt.Client
		client.connect("your MQTT IP address, mine same as HA",1883,60)
		client.publish("home-assistant-1/cameras/motion", "OFF");
		client.disconnect();

	def OnAlarm(self, State):
		#print("[{0}] Alarm triggered! -> {1}".format(self.Index, "ON" if State else "OFF"))

		if State:
			self.SensorOn()
			print("Motion Detected")
		else:
			self.SensorOff()
			print("Motion Stopped")

	def OnConnect(self):
		print("[{0}] OnConnect()".format(self.Index))
		self.Connected = True

	def OnDisconnect(self, reason):
		print("[{0}] OnDisconnect({1})".format(self.Index, reason))
		self.Connected = False
	#	self.StopPlayer()

	def OnTimer(self):
		#if self.Player:
		#	self.Player.poll()
		#	if self.Player.returncode != None:
		#		self.StopPlayer()

		if self.Alarm["Active"] == False and time.time() - self.Alarm["Last"] > ALARM_DELAY:
			self.Alarm["Active"] = None
			self.Alarm["Last"] = None

			self.OnAlarm(False)

	def OnReceive(self, data):
		Data = data.decode("utf-8", errors="ignore")
		#print("[{0}]: {1}".format(self.Index, Data))

		for Line in Data.split("\r\n"):
			if Line == "HTTP/1.1 200 OK":
				self.OnConnect()

			if not Line.startswith("Code="):
				continue

			Alarm = dict()
			for KeyValue in Line.split(';'):
				Key, Value = KeyValue.split('=')
				Alarm[Key] = Value

			self.ParseAlarm(Alarm)

	def ParseAlarm(self, Alarm):
		print("[{0}] ParseAlarm({1})".format(self.Index, Alarm))

		if Alarm["Code"] not in self.Camera["events"].split(','):
			return

		if Alarm["action"] == "Start":
			if self.Alarm["Active"] == None:
				self.OnAlarm(True)
			self.Alarm["Active"] = True
		elif Alarm["action"] == "Stop":
			self.Alarm["Active"] = False
			self.Alarm["Last"] = time.time()


class DahuaMaster():
	def __init__(self):
		self.Cameras = []
		self.NumActivePlayers = 0

		self.CurlMultiObj = pycurl.CurlMulti()
		self.NumCurlObjs = 0

		for Index, Camera in enumerate(CAMERAS):
			DahuaCam = DahuaCamera(self, Index, Camera)
			self.Cameras.append(DahuaCam)
			Url = URL_TEMPLATE.format(**Camera)

			CurlObj = pycurl.Curl()
			DahuaCam.CurlObj = CurlObj

			CurlObj.setopt(pycurl.URL, Url)
			CurlObj.setopt(pycurl.CONNECTTIMEOUT, 30)
			CurlObj.setopt(pycurl.TCP_KEEPALIVE, 1)
			CurlObj.setopt(pycurl.TCP_KEEPIDLE, 30)
			CurlObj.setopt(pycurl.TCP_KEEPINTVL, 15)
			CurlObj.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)
			CurlObj.setopt(pycurl.USERPWD, "%s:%s" % (Camera["user"], Camera["pass"]))
			CurlObj.setopt(pycurl.WRITEFUNCTION, DahuaCam.OnReceive)

			self.CurlMultiObj.add_handle(CurlObj)
			self.NumCurlObjs += 1

	#def OnStartPlayer(self):
	#	self.NumActivePlayers += 1
	#	if self.NumActivePlayers == 1:
	#		subprocess.run(["xset", "dpms", "force", "on"])

	#def OnStopPlayer(self):
	#	self.NumActivePlayers -= 1
	#	if self.NumActivePlayers == 0:
	#		subprocess.run(["xset", "dpms", "force", "off"])

	def OnTimer(self):
		for Camera in self.Cameras:
			Camera.OnTimer()

	def Run(self, timeout = 1.0):
		while 1:
			Ret, NumHandles = self.CurlMultiObj.perform()
			if Ret != pycurl.E_CALL_MULTI_PERFORM:
				break

		while 1:
			Ret = self.CurlMultiObj.select(timeout)
			if Ret == -1:
				self.OnTimer()
				continue

			while 1:
				Ret, NumHandles = self.CurlMultiObj.perform()

				if NumHandles != self.NumCurlObjs:
					_, Success, Error = self.CurlMultiObj.info_read()

					for CurlObj in Success:
						Camera = next(filter(lambda x: x.CurlObj == CurlObj, self.Cameras))
						if Camera.Reconnect:
							continue

						Camera.OnDisconnect("Success")
						Camera.Reconnect = time.time() + 5

					for CurlObj, ErrorNo, ErrorStr in Error:
						Camera = next(filter(lambda x: x.CurlObj == CurlObj, self.Cameras))
						if Camera.Reconnect:
							continue

						Camera.OnDisconnect("{0} ({1})".format(ErrorStr, ErrorNo))
						Camera.Reconnect = time.time() + 5

					for Camera in self.Cameras:
						if Camera.Reconnect and Camera.Reconnect < time.time():
							self.CurlMultiObj.remove_handle(Camera.CurlObj)
							self.CurlMultiObj.add_handle(Camera.CurlObj)
							Camera.Reconnect = None

				if Ret != pycurl.E_CALL_MULTI_PERFORM:
					break

			self.OnTimer()

if __name__ == '__main__':
	logging.basicConfig(level=logging.DEBUG)

	Master = DahuaMaster()
	Master.Run()

I have this automation to turn the sensor script on and off from the input boolean;

#dahua cam movement
- alias: cam movement on
  initial_state: true
  trigger:
    entity_id: input_boolean.cam_movement_sensor_on_off
    platform: state
    to: 'on'
  #condition: []
  action:
  - service: shell_command.cam_motion_on
  
- alias: cam movement off
  initial_state: true
  trigger:
    entity_id: input_boolean.cam_movement_sensor_on_off
    platform: state
    to: 'off'
  #condition: []
  action:
  - service: shell_command.cam_motion_off
  - delay: 00:00:02
  - service: mqtt.publish
    data_template:
      payload: 'OFF'
      topic: 'home-assistant-1/cameras/motion'

I have this in my shell scripts to turn the camera movement detection script on and off from the automation;

shell_command:
  cam_motion_on:  'python3 /home/homeassistant/.homeassistant/scripts/dahua.py'
  cam_motion_off:  'pkill -f /home/homeassistant/.homeassistant/scripts/dahua.py'

I have this automation for the alarm trigger and to send me the pictures, now I’ve been using a while I find this as good if not better then the Dahua one as I can integrate other things, you can still adjust sensitivity and detection areas on the camera and you can still control if motion is on from the camera, a shortened version is below as mine emails pictures, pushbullets, reads it out from TTS on another HA hub, turns light on and so on (I have the camera as an entity via url stream so am able to camera.snapshot ;

#cam alarm
- alias: Cam Movement
  initial_state: false
  trigger:
    entity_id: binary_sensor.cam_movement_sensor
    platform: state
    to: 'on'
  #condition: []
  action:
  - service: camera.snapshot
    data:
      entity_id: camera.cam
      filename: /home/homeassistant/.homeassistant/tmp/campic.jpg
  #- delay: 00:00:02   
  - delay: 00:00:01
  - service: notify.email_notification
    data:
      message: movement 
      title: Movement
      data:
        #html: <h4>Movement in Dining Room</h4>
        images:
        - /home/homeassistant/.homeassistant/tmp/campic.jpg

If needed;

I also had to install/update python components, also sudo apt-get update and sudo apt get-upgrade , can’t recall exactly which ones but think it was;

pip3 install paho-mqtt

install pycurl info I found http://pycurl.io/docs/latest/install.html

There were some libraries to update and elements when these were installed but was informed on installs, I’m pretty sure I had to install/update setuptools.

The various folders may need the correct permissions 0755 (I just use WinSCP and right click properties on files/folders). I have the group/owner of dahua.py as homeassistant

As I’m VEnv I installed any updates/upgrades through VEnv and normal.

The camera is still pretty sensitive however you get movement alarms. I turned sensitivity and thresholds down and still get false alarms with rain/pigeons and spiders! Cross line detection is better but I wanted both.

1 Like

Thank you for the info. Did you get other events then motion to work?