From e3db43595c8ca326e9ea7f42a1cc2ecc9e9326e6 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Mon, 2 Mar 2020 12:57:04 +0200 Subject: [PATCH 01/33] Dockerized and added MQTT capabilities --- DahuaEventHandler.php | 350 +++++++++++++++++++++-------------- Dockerfile | 18 ++ phpMQTT.php | 421 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 650 insertions(+), 139 deletions(-) create mode 100644 Dockerfile create mode 100644 phpMQTT.php diff --git a/DahuaEventHandler.php b/DahuaEventHandler.php index b098dd7..dfac73d 100644 --- a/DahuaEventHandler.php +++ b/DahuaEventHandler.php @@ -1,19 +1,20 @@ \n"; -$Dahua = new Dahua_Functions("192.168.1.208", "admin", "password"); # VTO's IP and user/pwd + +logging("<*** Dahua VTO Event Listener START ***>"); + +$Dahua = new DahuaVTOEventListener(); $status = $Dahua->Main(); + logging("All done"); + function logging($text){ - list($ts) = explode(".",microtime(true)); - $dt = new DateTime(date("Y-m-d H:i:s.",$ts)); - $logdate = $dt->format("Y-m-d H:i:s.u"); - echo $logdate.": "; - print_r($text); - echo "\n"; + echo $text."\n"; } -class Dahua_Functions -{ + +class DahuaVTOEventListener { private $sock, $host, $port, $credentials; private $ID = 0; # Our Request / Responce ID that must be in all requests and initated by us private $SessionID = 0; # Session ID will be returned after successful login @@ -23,24 +24,52 @@ class Dahua_Functions private $keepAliveInterval = 60; private $lastKeepAlive = 0; - function Dahua_Functions($host, $user, $pass) - { - $this->host = $host; - $this->username = $user; - $this->password = $pass; + function DahuaVTOEventListener() { + $this->host = getenv("DAHUA_VTO_HOST"); + $this->username = getenv("DAHUA_VTO_USERNAME"); + $this->password = getenv("DAHUA_VTO_PASSWORD"); } - function Gen_md5_hash($Dahua_random, $Dahua_realm, $username, $password) - { + + function Gen_md5_hash($Dahua_random, $Dahua_realm, $username, $password) { $PWDDB_HASH = strtoupper(md5($username.':'.$Dahua_realm.':'.$password)); $PASS = $username.':'.$Dahua_random.':'.$PWDDB_HASH; $RANDOM_HASH = strtoupper(md5($PASS)); return $RANDOM_HASH; } - function KeepAlive($delay) - { + + function publish($data){ + $server = getenv("MQTT_BROKER_HOST"); + $port = getenv("MQTT_BROKER_PORT"); + $username = getenv("MQTT_BROKER_USERNAME"); + $password = getenv("MQTT_BROKER_PASSWORD"); + $client_id = "dahua-vto"; + + $mqtt_message = json_encode($data); + + try { + $mqtt = new phpMQTT($server, $port, $client_id); + + if ($mqtt->connect(true, NULL, $username, $password)) { + $mqtt->publish(getenv("MQTT_BROKER_TOPIC"), $mqtt_message, 0); + $mqtt->close(); + + logging("Published message".$mqtt_message); + + } else { + logging("Publishing message failed due to timeout, Message: ".$mqtt_message); + } + } + catch (Exception $e) { + logging("Publishing message failed due to error: ".$e.", Message: ".$mqtt_message); + } + } + + function KeepAlive($delay){ global $debug; + logging("Started keepAlive thread"); - while(true){ + + while(true){ $query_args = array( 'method'=>"global.keepAlive", 'magic'=>"0x1234", @@ -50,16 +79,23 @@ function KeepAlive($delay) ), 'id'=>$this->ID, 'session'=>$this->SessionID); + $this->Send(json_encode($query_args)); $lastKeepAlive = time(); $keepAliveReceived = false; + while($lastKeepAlive + $delay > time()){ $data = $this->Receive(); + if (!empty($data)){ foreach($data as $packet) { $packet = json_decode($packet, true); + if (array_key_exists('result', $packet)){ - if($debug) logging("keepAlive back"); + if($debug) { + logging("keepAlive back"); + } + $keepAliveReceived = true; } elseif ($packet['method'] == 'client.notifyEventStream'){ @@ -68,17 +104,17 @@ function KeepAlive($delay) } } } - if (!$keepAliveReceived){ + if (!$keepAliveReceived) { logging("keepAlive failed"); return false; } } } - function Send($packet) - { + function Send($packet) { if (empty($packet)){ $packet = ''; } + $header = pack("N",0x20000000); $header .= pack("N",0x44484950); $header .= pack("V",$this->SessionID); @@ -95,15 +131,16 @@ function Send($packet) $this->ID += 1; - try{ + try { $msg = $header.$packet; $result = fwrite($this->sock, $msg); - } catch (Exception $e) { + } + catch (Exception $e) { logging($e); } } - function Receive($timeout = 5) - { + + function Receive($timeout = 5) { # # We must expect there is no output from remote device # Some debug cmd do not return any output, some will return after timeout/failure, most will return directly @@ -114,17 +151,20 @@ function Receive($timeout = 5) $P2P_return_data = []; $header_LEN = 0; - try{ + try { $len = strlen($data); $read = array($this->sock); $write = null; $except = null; $ready = stream_select($read, $write, $except, $timeout); + if ($ready > 0) { $data .= stream_socket_recvfrom($this->sock, 8192); } - } catch (Exception $e) { + + } + catch (Exception $e) { return ""; } @@ -135,11 +175,13 @@ function Receive($timeout = 5) $LEN_RECVED = 1; $LEN_EXPECT = 1; + while (strlen($data)>0){ if (substr($data,0,8) == pack("N",0x20000000).pack("N",0x44484950)){ # DHIP $P2P_header = substr($data,0,32); $LEN_RECVED = unpack("V",substr($data,16,4))[1]; $LEN_EXPECT = unpack("V",substr($data,24,4))[1]; + $data = substr($data,32); } else{ @@ -147,7 +189,9 @@ function Receive($timeout = 5) $P2P_data = substr($data,0,$LEN_RECVED); $P2P_return_data[] = $P2P_data; } + $data = substr($data,$LEN_RECVED); + if ($LEN_RECVED == $LEN_EXPECT && strlen($data)==0){ break; } @@ -155,6 +199,7 @@ function Receive($timeout = 5) } return $P2P_return_data; } + function Login() { logging("Start login"); @@ -175,10 +220,12 @@ function Login() $this->Send(json_encode($query_args)); $data = $this->Receive(); + if (empty($data)){ logging("global.login [random]"); return false; } + $data = json_decode($data[0], true); $this->SessionID = $data['session']; @@ -201,36 +248,45 @@ function Login() 'authorityType'=>"Default", ) ); - $this->Send(json_encode($query_args)); + + $this->Send(json_encode($query_args)); $data = $this->Receive(); - if (empty($data)){ + + if (empty($data)){ return false; } - $data = json_decode($data[0], true); - if (array_key_exists('result', $data) && $data['result']){ + + $data = json_decode($data[0], true); + + if (array_key_exists('result', $data) && $data['result']){ logging("Login success"); $this->keepAliveInterval = $data['params']['keepAliveInterval']; return true; } + logging("Login failed: ".$data['error']['code']." ".$data['error']['message']); return false; } - function Main($reconnectTimeout=60) - { + + function Main($reconnectTimeout=60) { $error = false; while (true){ if($error){ sleep($reconnectTimeout); } + $error = true; $this->sock = @fsockopen($this->host, 5000, $errno, $errstr, 5); + if($errno){ logging("Socket open failed"); continue; } + if (!$this->Login()){ continue; } + #Listen to all events $query_args = array( 'id'=>$this->ID, @@ -241,14 +297,17 @@ function Main($reconnectTimeout=60) ), 'session'=>$this->SessionID ); - $this->Send(json_encode($query_args)); + + $this->Send(json_encode($query_args)); $data = $this->Receive(); + if (!count($data) || !array_key_exists('result', json_decode($data[0], true))){ logging("Failure eventManager.attach"); continue; } else{ unset($data[0]); + foreach($data as $packet) { $packet = json_decode($packet, true); if ($packet['method'] == 'client.notifyEventStream'){ @@ -260,124 +319,137 @@ function Main($reconnectTimeout=60) logging("Failure no keep alive received"); } } - function EventHandler($data) - { - global $debug; - $eventList = $data['params']['eventList'][0]; - $eventCode = $eventList['Code']; - $eventData = $eventList['Data']; - if(count($data['params']['eventList'])>1){ - logging("Event Manager subscription reply"); - } - elseif($eventCode == 'CallNoAnswered'){ - logging("Event Call from VTO"); - } - elseif($eventCode == 'IgnoreInvite'){ - logging("Event VTH answered call from VTO"); - } - elseif($eventCode == 'VideoMotion'){ - logging("Event VideoMotion"); - $this->SaveSnapshot(); - } - elseif($eventCode == 'RtspSessionDisconnect'){ - if($eventList['Action'] == 'Start'){ - logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." disconnected"); + + function EventHandler($data) { + global $debug; + + $eventList = $data['params']['eventList'][0]; + $eventCode = $eventList['Code']; + $eventData = $eventList['Data']; + + if(count($data['params']['eventList'])>1){ + logging("Event Manager subscription reply"); } - elseif($eventList['Action'] == 'Stop'){ - logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." connected"); + elseif($eventCode == 'CallNoAnswered'){ + logging("Event Call from VTO"); } - } - elseif($eventCode == 'BackKeyLight'){ - logging("Event BackKeyLight with State ".$eventData['State']." "); - } - elseif($eventCode == 'TimeChange'){ - logging("Event TimeChange, BeforeModifyTime: ".$eventData['BeforeModifyTime'].", ModifiedTime: ".$eventData['ModifiedTime'].""); - } - elseif($eventCode == 'NTPAdjustTime'){ - if($eventData['result']) logging("Event NTPAdjustTime with ".$eventData['Address']." success"); - else logging("Event NTPAdjustTime failed"); - } - elseif($eventCode == 'KeepLightOn'){ - if($eventData['Status'] == 'On'){ - logging("Event KeepLightOn"); + elseif($eventCode == 'IgnoreInvite'){ + logging("Event VTH answered call from VTO"); } - elseif($eventData['Status'] == 'Off'){ - logging("Event KeepLightOff"); + elseif($eventCode == 'VideoMotion'){ + logging("Event VideoMotion"); + //$this->SaveSnapshot(); } - } - elseif($eventCode == 'VideoBlind'){ - if($eventList['Action'] == 'Start'){ - logging("Event VideoBlind started"); + elseif($eventCode == 'RtspSessionDisconnect'){ + if($eventList['Action'] == 'Start'){ + logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." disconnected"); + } + elseif($eventList['Action'] == 'Stop'){ + logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." connected"); + } } - elseif($eventList['Action'] == 'Stop'){ - logging("Event VideoBlind stopped"); + elseif($eventCode == 'BackKeyLight'){ + logging("Event BackKeyLight with State ".$eventData['State']." "); } - } - elseif($eventCode == 'FingerPrintCheck'){ - if($eventData['FingerPrintID'] > -1){ - $finger=($eventData['FingerPrintID']); - $users = array( #From VTO FingerprintManager/FingerprintID - "0" => "Papa", - "1" => "Mama", - "2" => "Kind1", - "3" => "Kind2"); - $name=$users[$finger]; - logging("Event FingerPrintCheck success, Finger number ".$eventData['FingerPrintID'].", User ".$name.""); + elseif($eventCode == 'TimeChange'){ + logging("Event TimeChange, BeforeModifyTime: ".$eventData['BeforeModifyTime'].", ModifiedTime: ".$eventData['ModifiedTime'].""); } - else{ - logging("Event FingerPrintCheck failed, unknown Finger"); + elseif($eventCode == 'NTPAdjustTime'){ + if($eventData['result']) { + logging("Event NTPAdjustTime with ".$eventData['Address']." success"); + } + else { + logging("Event NTPAdjustTime failed"); + } } - } - elseif($eventCode == 'SIPRegisterResult'){ - if($eventList['Action'] == 'Pulse'){ - if($eventData['Success']) logging("Event SIPRegisterResult, Success"); - else logging("Event SIPRegisterResult, Failed)"); + elseif($eventCode == 'KeepLightOn'){ + if($eventData['Status'] == 'On'){ + logging("Event KeepLightOn"); + } + elseif($eventData['Status'] == 'Off'){ + logging("Event KeepLightOff"); + } } - } - elseif($eventCode == 'AccessControl'){ - #Method:4=Remote/WebIf/SIPext,6=FingerPrint; UserID: from VTO FingerprintManager/Room Number or SIPext; - logging("Event: AccessControl, Name ".$eventData['Name']." Method ".$eventData['Method'].", ReaderID ".$eventData['ReaderID'].", UserID ".$eventData['UserID']); + elseif($eventCode == 'VideoBlind'){ + if($eventList['Action'] == 'Start'){ + logging("Event VideoBlind started"); + } + elseif($eventList['Action'] == 'Stop'){ + logging("Event VideoBlind stopped"); + } } - elseif($eventCode == 'CallSnap'){ - logging("Event: CallSnap, DeviceType ".$eventData['DeviceType']." RemoteID ".$eventData['RemoteID'].", RemoteIP ".$eventData['RemoteIP']." CallStatus ".$eventData['ChannelStates'][0]); + elseif($eventCode == 'FingerPrintCheck'){ + if($eventData['FingerPrintID'] > -1){ + $finger=($eventData['FingerPrintID']); + $users = array( #From VTO FingerprintManager/FingerprintID + "0" => "Papa", + "1" => "Mama", + "2" => "Kind1", + "3" => "Kind2"); + $name=$users[$finger]; + + logging("Event FingerPrintCheck success, Finger number ".$eventData['FingerPrintID'].", User ".$name.""); + } + else { + logging("Event FingerPrintCheck failed, unknown Finger"); + } } - elseif($eventCode == 'Invite'){ - logging("Event: Invite, Action ".$eventList['Action'].", CallID ".$eventData['CallID']." Lock Number ".$eventData['LockNum']); + elseif($eventCode == 'SIPRegisterResult'){ + if($eventList['Action'] == 'Pulse'){ + if($eventData['Success']) { + logging("Event SIPRegisterResult, Success"); + } + else{ + logging("Event SIPRegisterResult, Failed)"); + } + } } - elseif($eventCode == 'AccessSnap'){ - logging("Event: AccessSnap, FTP upload to ".$eventData['FtpUrl']); + elseif($eventCode == 'AccessControl'){ + #Method:4=Remote/WebIf/SIPext,6=FingerPrint; UserID: from VTO FingerprintManager/Room Number or SIPext; + logging("Event: AccessControl, Name ".$eventData['Name']." Method ".$eventData['Method'].", ReaderID ".$eventData['ReaderID'].", UserID ".$eventData['UserID']); } - elseif($eventCode == 'RequestCallState'){ - logging("Event: RequestCallState, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Index ".$eventData['Index']); + elseif($eventCode == 'CallSnap'){ + logging("Event: CallSnap, DeviceType ".$eventData['DeviceType']." RemoteID ".$eventData['RemoteID'].", RemoteIP ".$eventData['RemoteIP']." CallStatus ".$eventData['ChannelStates'][0]); } - elseif($eventCode == 'PassiveHungup'){ - logging("Event: PassiveHungup, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Index ".$eventData['Index']); + elseif($eventCode == 'Invite'){ + logging("Event: Invite, Action ".$eventList['Action'].", CallID ".$eventData['CallID']." Lock Number ".$eventData['LockNum']); } - elseif($eventCode == 'ProfileAlarmTransmit'){ - logging("Event: ProfileAlarmTransmit, Action ".$eventList['Action'].", AlarmType ".$eventData['AlarmType']." DevSrcType ".$eventData['DevSrcType'].", SenseMethod ".$eventData['SenseMethod']); + elseif($eventCode == 'AccessSnap'){ + logging("Event: AccessSnap, FTP upload to ".$eventData['FtpUrl']); } - else{ - logging("Unknown event received"); - if($debug) var_dump($data); - } - return true; + elseif($eventCode == 'RequestCallState'){ + logging("Event: RequestCallState, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Index ".$eventData['Index']); + } + elseif($eventCode == 'PassiveHungup'){ + logging("Event: PassiveHungup, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Index ".$eventData['Index']); + } + elseif($eventCode == 'ProfileAlarmTransmit'){ + logging("Event: ProfileAlarmTransmit, Action ".$eventList['Action'].", AlarmType ".$eventData['AlarmType']." DevSrcType ".$eventData['DevSrcType'].", SenseMethod ".$eventData['SenseMethod']); + } + else{ + logging("Unknown event received"); + } + + $this->publish($data); + + return true; } - function SaveSnapshot($path="/tmp/") - { - $filename = $path."/DoorBell_".date("Y-m-d_H-i-s").".jpg"; - $fp = fopen($filename, 'wb'); - $url = "http://".$this->host."/cgi-bin/snapshot.cgi"; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password); - curl_setopt($ch, CURLOPT_FILE, $fp); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_HTTPGET, 1); - curl_exec($ch); - curl_close($ch); - fclose($fp); - copy($filename, $path."/Doorbell.jpg"); + + function SaveSnapshot($path="/tmp/") { + $filename = $path."/DoorBell_".date("Y-m-d_H-i-s").".jpg"; + $fp = fopen($filename, 'wb'); + $url = "http://".$this->host."/cgi-bin/snapshot.cgi"; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_HTTPGET, 1); + curl_exec($ch); + curl_close($ch); + fclose($fp); + copy($filename, $path."/Doorbell.jpg"); } } -?> +?> \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c774729 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM php:7.2.2-apache +MAINTAINER Elad Bar + +WORKDIR /app + +COPY DahuaEventHandler.php ./DahuaVTO.php +COPY phpMQTT.php ./phpMQTT.php + +ENV DAHUA_VTO_HOST=vto-host +ENV DAHUA_VTO_USERNAME=Username +ENV DAHUA_VTO_PASSWORD=Password +ENV MQTT_BROKER_HOST=mqtt-host +ENV MQTT_BROKER_PORT=1883 +ENV MQTT_BROKER_USERNAME=Username +ENV MQTT_BROKER_PASSWORD=Password +ENV MQTT_BROKER_TOPIC=DahuaVTO/Events + +CMD ["php" "-f" "/app/DahuaVTO.php"] diff --git a/phpMQTT.php b/phpMQTT.php new file mode 100644 index 0000000..7adedd0 --- /dev/null +++ b/phpMQTT.php @@ -0,0 +1,421 @@ +broker($address, $port, $clientid, $cafile); + } + + /* sets the broker details */ + function broker($address, $port, $clientid, $cafile = NULL){ + $this->address = $address; + $this->port = $port; + $this->clientid = $clientid; + $this->cafile = $cafile; + } + + function connect_auto($clean = true, $will = NULL, $username = NULL, $password = NULL){ + while($this->connect($clean, $will, $username, $password)==false){ + sleep(10); + } + return true; + } + + /* connects to the broker + inputs: $clean: should the client send a clean session flag */ + function connect($clean = true, $will = NULL, $username = NULL, $password = NULL){ + + if($will) $this->will = $will; + if($username) $this->username = $username; + if($password) $this->password = $password; + + + if ($this->cafile) { + $socketContext = stream_context_create(["ssl" => [ + "verify_peer_name" => true, + "cafile" => $this->cafile + ]]); + $this->socket = stream_socket_client("tls://" . $this->address . ":" . $this->port, $errno, $errstr, 60, STREAM_CLIENT_CONNECT, $socketContext); + } else { + $this->socket = stream_socket_client("tcp://" . $this->address . ":" . $this->port, $errno, $errstr, 60, STREAM_CLIENT_CONNECT); + } + + if (!$this->socket ) { + if($this->debug) error_log("stream_socket_create() $errno, $errstr \n"); + return false; + } + + stream_set_timeout($this->socket, 5); + stream_set_blocking($this->socket, 0); + + $i = 0; + $buffer = ""; + + $buffer .= chr(0x00); $i++; + $buffer .= chr(0x06); $i++; + $buffer .= chr(0x4d); $i++; + $buffer .= chr(0x51); $i++; + $buffer .= chr(0x49); $i++; + $buffer .= chr(0x73); $i++; + $buffer .= chr(0x64); $i++; + $buffer .= chr(0x70); $i++; + $buffer .= chr(0x03); $i++; + + //No Will + $var = 0; + if($clean) $var+=2; + + //Add will info to header + if($this->will != NULL){ + $var += 4; // Set will flag + $var += ($this->will['qos'] << 3); //Set will qos + if($this->will['retain']) $var += 32; //Set will retain + } + + if($this->username != NULL) $var += 128; //Add username to header + if($this->password != NULL) $var += 64; //Add password to header + + $buffer .= chr($var); $i++; + + //Keep alive + $buffer .= chr($this->keepalive >> 8); $i++; + $buffer .= chr($this->keepalive & 0xff); $i++; + + $buffer .= $this->strwritestring($this->clientid,$i); + + //Adding will to payload + if($this->will != NULL){ + $buffer .= $this->strwritestring($this->will['topic'],$i); + $buffer .= $this->strwritestring($this->will['content'],$i); + } + + if($this->username) $buffer .= $this->strwritestring($this->username,$i); + if($this->password) $buffer .= $this->strwritestring($this->password,$i); + + $head = " "; + $head{0} = chr(0x10); + $head{1} = chr($i); + + fwrite($this->socket, $head, 2); + fwrite($this->socket, $buffer); + + $string = $this->read(4); + + if(ord($string{0})>>4 == 2 && $string{3} == chr(0)){ + if($this->debug) echo "Connected to Broker\n"; + }else{ + error_log(sprintf("Connection failed! (Error: 0x%02x 0x%02x)\n", + ord($string{0}),ord($string{3}))); + return false; + } + + $this->timesinceping = time(); + + return true; + } + + /* read: reads in so many bytes */ + function read($int = 8192, $nb = false){ + + // print_r(socket_get_status($this->socket)); + + $string=""; + $togo = $int; + + if($nb){ + return fread($this->socket, $togo); + } + + while (!feof($this->socket) && $togo>0) { + $fread = fread($this->socket, $togo); + $string .= $fread; + $togo = $int - strlen($string); + } + + + + + return $string; + } + + /* subscribe: subscribes to topics */ + function subscribe($topics, $qos = 0){ + $i = 0; + $buffer = ""; + $id = $this->msgid; + $buffer .= chr($id >> 8); $i++; + $buffer .= chr($id % 256); $i++; + + foreach($topics as $key => $topic){ + $buffer .= $this->strwritestring($key,$i); + $buffer .= chr($topic["qos"]); $i++; + $this->topics[$key] = $topic; + } + + $cmd = 0x80; + //$qos + $cmd += ($qos << 1); + + + $head = chr($cmd); + $head .= chr($i); + + fwrite($this->socket, $head, 2); + fwrite($this->socket, $buffer, $i); + $string = $this->read(2); + + $bytes = ord(substr($string,1,1)); + $string = $this->read($bytes); + } + + /* ping: sends a keep alive ping */ + function ping(){ + $head = " "; + $head = chr(0xc0); + $head .= chr(0x00); + fwrite($this->socket, $head, 2); + if($this->debug) echo "ping sent\n"; + } + + /* disconnect: sends a proper disconect cmd */ + function disconnect(){ + $head = " "; + $head{0} = chr(0xe0); + $head{1} = chr(0x00); + fwrite($this->socket, $head, 2); + } + + /* close: sends a proper disconect, then closes the socket */ + function close(){ + $this->disconnect(); + stream_socket_shutdown($this->socket, STREAM_SHUT_WR); + } + + /* publish: publishes $content on a $topic */ + function publish($topic, $content, $qos = 0, $retain = 0){ + + $i = 0; + $buffer = ""; + + $buffer .= $this->strwritestring($topic,$i); + + //$buffer .= $this->strwritestring($content,$i); + + if($qos){ + $id = $this->msgid++; + $buffer .= chr($id >> 8); $i++; + $buffer .= chr($id % 256); $i++; + } + + $buffer .= $content; + $i+=strlen($content); + + + $head = " "; + $cmd = 0x30; + if($qos) $cmd += $qos << 1; + if($retain) $cmd += 1; + + $head{0} = chr($cmd); + $head .= $this->setmsglength($i); + + fwrite($this->socket, $head, strlen($head)); + fwrite($this->socket, $buffer, $i); + + } + + /* message: processes a recieved topic */ + function message($msg){ + $tlen = (ord($msg{0})<<8) + ord($msg{1}); + $topic = substr($msg,2,$tlen); + $msg = substr($msg,($tlen+2)); + $found = 0; + foreach($this->topics as $key=>$top){ + if( preg_match("/^".str_replace("#",".*", + str_replace("+","[^\/]*", + str_replace("/","\/", + str_replace("$",'\$', + $key))))."$/",$topic) ){ + if(is_callable($top['function'])){ + call_user_func($top['function'],$topic,$msg); + $found = 1; + } + } + } + + if($this->debug && !$found) echo "msg recieved but no match in subscriptions\n"; + } + + /* proc: the processing loop for an "allways on" client + set true when you are doing other stuff in the loop good for watching something else at the same time */ + function proc( $loop = true){ + + if(1){ + $sockets = array($this->socket); + $w = $e = NULL; + $cmd = 0; + + //$byte = fgetc($this->socket); + if(feof($this->socket)){ + if($this->debug) echo "eof receive going to reconnect for good measure\n"; + fclose($this->socket); + $this->connect_auto(false); + if(count($this->topics)) + $this->subscribe($this->topics); + } + + $byte = $this->read(1, true); + + if(!strlen($byte)){ + if($loop){ + usleep(100000); + } + + }else{ + + $cmd = (int)(ord($byte)/16); + if($this->debug) echo "Recevid: $cmd\n"; + + $multiplier = 1; + $value = 0; + do{ + $digit = ord($this->read(1)); + $value += ($digit & 127) * $multiplier; + $multiplier *= 128; + }while (($digit & 128) != 0); + + if($this->debug) echo "Fetching: $value\n"; + + if($value) + $string = $this->read($value); + + if($cmd){ + switch($cmd){ + case 3: + $this->message($string); + break; + } + + $this->timesinceping = time(); + } + } + + if($this->timesinceping < (time() - $this->keepalive )){ + if($this->debug) echo "not found something so ping\n"; + $this->ping(); + } + + + if($this->timesinceping<(time()-($this->keepalive*2))){ + if($this->debug) echo "not seen a package in a while, disconnecting\n"; + fclose($this->socket); + $this->connect_auto(false); + if(count($this->topics)) + $this->subscribe($this->topics); + } + + } + return 1; + } + + /* getmsglength: */ + function getmsglength(&$msg, &$i){ + + $multiplier = 1; + $value = 0 ; + do{ + $digit = ord($msg{$i}); + $value += ($digit & 127) * $multiplier; + $multiplier *= 128; + $i++; + }while (($digit & 128) != 0); + + return $value; + } + + + /* setmsglength: */ + function setmsglength($len){ + $string = ""; + do{ + $digit = $len % 128; + $len = $len >> 7; + // if there are more digits to encode, set the top bit of this digit + if ( $len > 0 ) + $digit = ($digit | 0x80); + $string .= chr($digit); + }while ( $len > 0 ); + return $string; + } + + /* strwritestring: writes a string to a buffer */ + function strwritestring($str, &$i){ + $ret = " "; + $len = strlen($str); + $msb = $len >> 8; + $lsb = $len % 256; + $ret = chr($msb); + $ret .= chr($lsb); + $ret .= $str; + $i += ($len+2); + return $ret; + } + + function printstr($string){ + $strlen = strlen($string); + for($j=0;$j<$strlen;$j++){ + $num = ord($string{$j}); + if($num > 31) + $chr = $string{$j}; else $chr = " "; + printf("%4d: %08b : 0x%02x : %s \n",$j,$num,$num,$chr); + } + } +} From f30f883d770f96906d52ddcc05bc547226700167 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Mon, 2 Mar 2020 13:20:26 +0200 Subject: [PATCH 02/33] Fix docker CMD --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c774729..8fa5eff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,4 +15,4 @@ ENV MQTT_BROKER_USERNAME=Username ENV MQTT_BROKER_PASSWORD=Password ENV MQTT_BROKER_TOPIC=DahuaVTO/Events -CMD ["php" "-f" "/app/DahuaVTO.php"] +CMD php -f /app/DahuaVTO.php From 6e8f2bf3e000df0fc6985f60f44a183a2fe09231 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Mon, 2 Mar 2020 15:54:50 +0200 Subject: [PATCH 03/33] Added readme --- README.md | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..08b7a53 --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +# DahuaVTO2MQTT + +## Description +Listens to events from Dahua VTO unit and publishes them via MQTT Message + +## Credits +All credits goes to @riogrande75 who wrote that complicated integration +Original code can be found in @riogrande75/Dahua + +## Change-log +2020-Feb-03 - Initial version combing the event listener with MQTT + +## Environment Variables +``` +DAHUA_VTO_HOST: Dahua VTO hostname or IP +DAHUA_VTO_USERNAME: Dahua VTO username to access (should be admin) +DAHUA_VTO_PASSWORD: Dahua VTO administrator password (same as accessing web management) +MQTT_BROKER_HOST: MQTT Broker hostname or IP +MQTT_BROKER_PORT: MQTT Broker port, default=1883 +MQTT_BROKER_USERNAME: MQTT Broker username +MQTT_BROKER_PASSWORD: MQTT Broker password +MQTT_BROKER_TOPIC: Topic to publish all events, default=DahuaVTO/Events +``` + +## Docker Compose +``` +version: '2' +services: + dahuavto2mqtt: + image: "eladbar/dahuavto2mqtt:latest" + container_name: "dahuavto2mqtt" + hostname: "dahuavto2mqtt" + restart: always + environment: + - DAHUA_VTO_HOST=vto-host + - DAHUA_VTO_USERNAME=Username + - DAHUA_VTO_PASSWORD=Password + - MQTT_BROKER_HOST=mqtt-host + - MQTT_BROKER_PORT=1883 + - MQTT_BROKER_USERNAME=Username + - MQTT_BROKER_PASSWORD=Password + - MQTT_BROKER_TOPIC=DahuaVTO/Events +``` + +## MQTT Message (Dahua VTO Event Payload) +Message with more than one event can take place by subscription, +By default each message should be with one event in the list. + +#### Events (With dedicated additional data) +``` +CallNoAnswered: Call from VTO + +IgnoreInvite: VTH answered call from VTO + +VideoMotion: Video motion detected + +RtspSessionDisconnect: Rtsp-Session connection connection state changed + Action: Represented whether event Start or Stop + Data.Device: IP of the device connected / disconnected + +BackKeyLight: BackKeyLight with State + Data.State: Represents the new state + +TimeChange: Time changed + Data.BeforeModifyTime: Time before change + Data.ModifiedTime: Time after change + +NTPAdjustTime: NTP Adjusted time + Data.result: Whether the action succesded or not + Data.Address: URL of the NTP + +KeepLightOn: Keep light state changed + Data.Status: Repesents whether the state changed to On or Off + +VideoBlind: Video got blind state changed + Action: Represented whether event Start or Stop + +FingerPrintCheck: Finger print check status + Data.FingerPrintID: Finger print ID, if 0, check failed + +SIPRegisterResult: SIP Device registration status + Action: Should be Pulse + Data.Success: Whether the registration completed or failed + +AccessControl: Someone opened the door + Data.Name: ? + Data.Method + 4=Remote/WebIf/SIPext + 6=FingerPrint + Data.UserID: By FingerprintManager / Room Number / SIPext + Data.ReaderId: ? + +CallSnap: Call + Data.DeviceType: Which device type + Data.RemoteID: UserID + Data.RemoteIP: IP of VTH / SIP device + Data.ChannelStates: Status + +Invite - Invite for a call (calling) + Action: ? + Data.CallID: Call ID + Data.LockNum: ? + +AccessSnap: ? + Data.FtpUrl: FTP uploaded to + +RequestCallState: ? + Action: ? + Data.LocaleTime: Date and time of the event + Data.Index: Index of the call + +PassiveHungup: Call was dropped + Action + Data.LocaleTime: Date and time of the event + Data.Index: Index of the call + +ProfileAlarmTransmit: Alarm triggered + Action: ? + Data.AlarmType: Alarm type + Data.DevSrcType: Device triggered the alarm + Data.SenseMethod: What triggered the alarm +``` + +#### Structure +``` +{ + "id":2, + "method":"client.notifyEventStream", + "params":{ + "SID":513, + "eventList":[ + { + "Action":"Pulse", + "Code":"TimeChange", + "Data":{ + "BeforeModifyTime":"02-03-2020 15:11:39", + "LocaleTime":"2020-03-02 15:11:39", + "ModifiedTime":"02-03-2020 15:11:38", + "UTC":1583154699},"Index":0}]},"session":2147388684} + +{ + "id": [MESSAGE ID], + "method":"client.notifyEventStream", + "params":{ + "SID":513, + "eventList":[ + { + "Action":"[EVENT ACTION]", + "Code":"[EVENT NAME]", + "Data":{ + "LocaleTime":"YYYY-MM-DD HH:mm:SS", + "UTC": [EPOCH TIMESTAMP] + }, + "Index": [EVENT ID IN MESSAGE], + "Param":[] + } + ] + }, + "session": [SESSION IDENTIFIER] +} +``` \ No newline at end of file From d5f19eb4c745c0654100b1eba8ba3c6887843f39 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Mon, 2 Mar 2020 15:55:32 +0200 Subject: [PATCH 04/33] Fix readme --- README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/README.md b/README.md index 08b7a53..4fc0446 100644 --- a/README.md +++ b/README.md @@ -123,21 +123,6 @@ ProfileAlarmTransmit: Alarm triggered #### Structure ``` -{ - "id":2, - "method":"client.notifyEventStream", - "params":{ - "SID":513, - "eventList":[ - { - "Action":"Pulse", - "Code":"TimeChange", - "Data":{ - "BeforeModifyTime":"02-03-2020 15:11:39", - "LocaleTime":"2020-03-02 15:11:39", - "ModifiedTime":"02-03-2020 15:11:38", - "UTC":1583154699},"Index":0}]},"session":2147388684} - { "id": [MESSAGE ID], "method":"client.notifyEventStream", From 1032ff26fd8faf4eb2f4325dbdce92e4b2327fdf Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Mon, 2 Mar 2020 15:58:55 +0200 Subject: [PATCH 05/33] Added instructions to run the script manually --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 4fc0446..15663e4 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,15 @@ MQTT_BROKER_PASSWORD: MQTT Broker password MQTT_BROKER_TOPIC: Topic to publish all events, default=DahuaVTO/Events ``` +## Run manuall +Requirements: +* All environment variables above +* PHP + +``` +php -f DahuaEventHandler.php +``` + ## Docker Compose ``` version: '2' From 6aa6cd02153c8796279c199363547d28d66cde9c Mon Sep 17 00:00:00 2001 From: elad-bar Date: Mon, 2 Mar 2020 16:05:20 +0200 Subject: [PATCH 06/33] Fix typo in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 15663e4..2d7b1aa 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ MQTT_BROKER_PASSWORD: MQTT Broker password MQTT_BROKER_TOPIC: Topic to publish all events, default=DahuaVTO/Events ``` -## Run manuall +## Run manually Requirements: * All environment variables above * PHP @@ -152,4 +152,4 @@ ProfileAlarmTransmit: Alarm triggered }, "session": [SESSION IDENTIFIER] } -``` \ No newline at end of file +``` From 4c3dd444c67a2dfceb41283ed390609d480c240b Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Mon, 2 Mar 2020 21:58:47 +0200 Subject: [PATCH 07/33] Added more events to be handled, changed topic structure --- DahuaEventHandler.php | 87 +++++++++----- Dockerfile | 1 - README.md | 259 +++++++++++++++++++++++++++++++----------- 3 files changed, 256 insertions(+), 91 deletions(-) diff --git a/DahuaEventHandler.php b/DahuaEventHandler.php index dfac73d..182b737 100644 --- a/DahuaEventHandler.php +++ b/DahuaEventHandler.php @@ -37,30 +37,32 @@ function Gen_md5_hash($Dahua_random, $Dahua_realm, $username, $password) { return $RANDOM_HASH; } - function publish($data){ + function publish($name, $payload){ $server = getenv("MQTT_BROKER_HOST"); $port = getenv("MQTT_BROKER_PORT"); $username = getenv("MQTT_BROKER_USERNAME"); $password = getenv("MQTT_BROKER_PASSWORD"); $client_id = "dahua-vto"; - $mqtt_message = json_encode($data); + $mqtt_message = json_encode($payload); + $topic = "DahuaVTO/".$name."/Event"; + $log_message = "Topic: ".$topic.", Payload: ".$mqtt_message; try { $mqtt = new phpMQTT($server, $port, $client_id); if ($mqtt->connect(true, NULL, $username, $password)) { - $mqtt->publish(getenv("MQTT_BROKER_TOPIC"), $mqtt_message, 0); + $mqtt->publish($topic, $mqtt_message, 0); $mqtt->close(); - logging("Published message".$mqtt_message); + logging("MQTT message published, ".$log_message); } else { - logging("Publishing message failed due to timeout, Message: ".$mqtt_message); + logging("Failed to publish MQTT message due to timeout, ".$log_message); } } catch (Exception $e) { - logging("Publishing message failed due to error: ".$e.", Message: ".$mqtt_message); + logging("Failed to publish MQTT message due to error: ".$e.", ".$log_message); } } @@ -320,17 +322,36 @@ function Main($reconnectTimeout=60) { } } - function EventHandler($data) { - global $debug; - - $eventList = $data['params']['eventList'][0]; - $eventCode = $eventList['Code']; - $eventData = $eventList['Data']; + function EventHandler($data){ + $allEvents = $data['params']['eventList']; - if(count($data['params']['eventList'])>1){ + if(count($allEvents) > 1){ logging("Event Manager subscription reply"); } - elseif($eventCode == 'CallNoAnswered'){ + else { + foreach ($allEvents as $item) { + $eventCode = $item['Code']; + $eventData = $item['Data']; + $eventAction = $item['Action']; + + $this->SingleEventHandler($eventCode, $eventAction, $eventData); + + $payload = array( + "Action" => $eventAction, + "Data" => $eventData + ); + + $this->publish($eventCode, $payload); + } + } + + return true; + } + + function SingleEventHandler($eventCode, $eventAction, $eventData) { + global $debug; + + if($eventCode == 'CallNoAnswered'){ logging("Event Call from VTO"); } elseif($eventCode == 'IgnoreInvite'){ @@ -341,10 +362,10 @@ function EventHandler($data) { //$this->SaveSnapshot(); } elseif($eventCode == 'RtspSessionDisconnect'){ - if($eventList['Action'] == 'Start'){ + if($eventAction == 'Start'){ logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." disconnected"); } - elseif($eventList['Action'] == 'Stop'){ + elseif($eventAction == 'Stop'){ logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." connected"); } } @@ -371,23 +392,24 @@ function EventHandler($data) { } } elseif($eventCode == 'VideoBlind'){ - if($eventList['Action'] == 'Start'){ + if($eventAction == 'Start'){ logging("Event VideoBlind started"); } - elseif($eventList['Action'] == 'Stop'){ + elseif($eventAction == 'Stop'){ logging("Event VideoBlind stopped"); } } elseif($eventCode == 'FingerPrintCheck'){ if($eventData['FingerPrintID'] > -1){ $finger=($eventData['FingerPrintID']); - $users = array( #From VTO FingerprintManager/FingerprintID + /* + $users = array( #From VTO FingerprintManager/FingerprintID "0" => "Papa", "1" => "Mama", "2" => "Kind1", "3" => "Kind2"); $name=$users[$finger]; - + */ logging("Event FingerPrintCheck success, Finger number ".$eventData['FingerPrintID'].", User ".$name.""); } else { @@ -395,7 +417,7 @@ function EventHandler($data) { } } elseif($eventCode == 'SIPRegisterResult'){ - if($eventList['Action'] == 'Pulse'){ + if($eventAction == 'Pulse'){ if($eventData['Success']) { logging("Event SIPRegisterResult, Success"); } @@ -420,18 +442,31 @@ function EventHandler($data) { elseif($eventCode == 'RequestCallState'){ logging("Event: RequestCallState, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Index ".$eventData['Index']); } - elseif($eventCode == 'PassiveHungup'){ - logging("Event: PassiveHungup, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Index ".$eventData['Index']); + elseif($eventCode == 'APConnect'){ + logging("Event: AlarmLocal"); + } + elseif($eventCode == 'BackLightOn'){ + logging("Event: BackLightOn"); + } + elseif($eventCode == 'BackLightOff'){ + logging("Event: BackLightOff"); + } + elseif($eventCode == 'AlarmLocal'){ + logging("Event: AlarmLocal"); + } + elseif($eventCode == 'APConnect'){ + logging("Event: APAccess, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Result ".$eventData['Result']." Timerconnect ".$eventData['Timerconnect']." Error ".$eventData['Error']); + } + elseif($eventCode == 'ProfileAlarmTransmit'){ + logging("Event: ProfileAlarmTransmit, Action ".$eventList['Action'].", AlarmType ".$eventData['AlarmType']." DevSrcType ".$eventData['DevSrcType'].", SenseMethod ".$eventData['SenseMethod']); } elseif($eventCode == 'ProfileAlarmTransmit'){ logging("Event: ProfileAlarmTransmit, Action ".$eventList['Action'].", AlarmType ".$eventData['AlarmType']." DevSrcType ".$eventData['DevSrcType'].", SenseMethod ".$eventData['SenseMethod']); } else{ - logging("Unknown event received"); + logging("Unmapped event (".$eventCode)."), Please report"; } - $this->publish($data); - return true; } diff --git a/Dockerfile b/Dockerfile index 8fa5eff..abec061 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,5 @@ ENV MQTT_BROKER_HOST=mqtt-host ENV MQTT_BROKER_PORT=1883 ENV MQTT_BROKER_USERNAME=Username ENV MQTT_BROKER_PASSWORD=Password -ENV MQTT_BROKER_TOPIC=DahuaVTO/Events CMD php -f /app/DahuaVTO.php diff --git a/README.md b/README.md index 2d7b1aa..74e03a1 100644 --- a/README.md +++ b/README.md @@ -47,90 +47,221 @@ services: - MQTT_BROKER_HOST=mqtt-host - MQTT_BROKER_PORT=1883 - MQTT_BROKER_USERNAME=Username - - MQTT_BROKER_PASSWORD=Password - - MQTT_BROKER_TOPIC=DahuaVTO/Events + - MQTT_BROKER_PASSWORD=Password ``` ## MQTT Message (Dahua VTO Event Payload) -Message with more than one event can take place by subscription, -By default each message should be with one event in the list. +Topic will be always DahuaVTO/[EVENT NAME]/Event +Message represent an event #### Events (With dedicated additional data) +####### CallNoAnswered: Call from VTO ``` -CallNoAnswered: Call from VTO +{ + "Action": "Start", + "Data": { + "CallID": "1", + "IsEncryptedStream": false, + "LocaleTime": "2020-03-02 20:11:13", + "LockNum": 2, + "SupportPaas": false, + "TCPPort": 37777, + "UTC": 1583172673 + } +} +``` + +####### IgnoreInvite: VTH answered call from VTO -IgnoreInvite: VTH answered call from VTO -VideoMotion: Video motion detected +####### VideoMotion: Video motion detected +``` +{ + "Action": "Start", + "Data": { + "LocaleTime": "2020-03-02 20:44:28", + "UTC": 1583174668 + } +} +``` -RtspSessionDisconnect: Rtsp-Session connection connection state changed - Action: Represented whether event Start or Stop - Data.Device: IP of the device connected / disconnected +####### RtspSessionDisconnect: Rtsp-Session connection connection state changed +Action: Represented whether event Start or Stop +Data.Device: IP of the device connected / disconnected -BackKeyLight: BackKeyLight with State - Data.State: Represents the new state +####### BackKeyLight: BackKeyLight with State +``` +{ + "Action": "Pulse", + "Data": { + "LocaleTime": "2020-03-02 20:24:07", + "State": 8, + "UTC": 1583173447 + } +} +``` -TimeChange: Time changed - Data.BeforeModifyTime: Time before change - Data.ModifiedTime: Time after change +####### TimeChange: Time changed +``` +{ + "Action": "Pulse", + "Data": { + "BeforeModifyTime": "02-03-2020 21:41:40", + "LocaleTime": "2020-03-02 21:41:40", + "ModifiedTime": "02-03-2020 21:41:39", + "UTC": 1583178100 + } +} +``` -NTPAdjustTime: NTP Adjusted time - Data.result: Whether the action succesded or not - Data.Address: URL of the NTP +####### NTPAdjustTime: NTP Adjusted time +``` +{ + "Action": "Pulse", + "Data": { + "Address": "time.windows.com", + "Before": "02-03-2020 21:41:38", + "LocaleTime": "2020-03-02 21:41:40", + "UTC": 1583178100, + "result": true + } +} +``` -KeepLightOn: Keep light state changed - Data.Status: Repesents whether the state changed to On or Off +####### KeepLightOn: Keep light state changed +Data.Status: Repesents whether the state changed to On or Off -VideoBlind: Video got blind state changed - Action: Represented whether event Start or Stop - -FingerPrintCheck: Finger print check status - Data.FingerPrintID: Finger print ID, if 0, check failed - -SIPRegisterResult: SIP Device registration status - Action: Should be Pulse - Data.Success: Whether the registration completed or failed - -AccessControl: Someone opened the door - Data.Name: ? - Data.Method - 4=Remote/WebIf/SIPext - 6=FingerPrint - Data.UserID: By FingerprintManager / Room Number / SIPext - Data.ReaderId: ? - -CallSnap: Call - Data.DeviceType: Which device type - Data.RemoteID: UserID - Data.RemoteIP: IP of VTH / SIP device - Data.ChannelStates: Status - -Invite - Invite for a call (calling) - Action: ? - Data.CallID: Call ID - Data.LockNum: ? +####### VideoBlind: Video got blind state changed +Action: Represents whether event Start or Stop + +####### FingerPrintCheck: Finger print check status +Data.FingerPrintID: Finger print ID, if 0, check failed + +####### SIPRegisterResult: SIP Device registration status +``` +{ + "Action": "Pulse", + "Data": { + "Date": "02-03-2020 21:42:59", + "LocaleTime": "2020-03-02 21:42:59", + "Success": true, + "UTC": 1583178179 + } +} +``` + +####### AccessControl: Someone opened the door +``` +{ + "Action": "Pulse", + "Data": { + "CardNo": "", + "CardType": null, + "LocaleTime": "2020-03-02 20:24:08", + "Method": 4, // 4=Remote/WebIf/SIPext | 6=FingerPrint + "Name": "OpenDoor", // Access control action name + "Password": "", + "ReaderID": "1", + "RecNo": 691, + "SnapURL": "", + "Status": 1, + "Type": "Entry", + "UTC": 1583173448, + "UserID": "" // By FingerprintManager / Room Number / SIPext + } +} +``` + + +####### CallSnap: Call +Data.DeviceType: Which device type +Data.RemoteID: UserID +Data.RemoteIP: IP of VTH / SIP device +Data.ChannelStates: Status + +####### Invite: Invite for a call (calling) +``` +{ + "Action": "Pulse", + "Data": { + "CallID": "1", + "IsEncryptedStream": false, + "LocaleTime": "2020-03-02 20:11:13", + "LockNum": 2, + "SupportPaas": false, + "TCPPort": 37777, + "UTC": 1583172673 + } +} +``` -AccessSnap: ? - Data.FtpUrl: FTP uploaded to +####### AccessSnap: ? +Data.FtpUrl: FTP uploaded to + +####### RequestCallState: ? +Action: ? +Data.LocaleTime: Date and time of the event +Data.Index: Index of the call + +####### PassiveHungup: Call was dropped +Action +Data.LocaleTime: Date and time of the event +Data.Index: Index of the call + +####### ProfileAlarmTransmit: Alarm triggered +Action: ? +Data.AlarmType: Alarm type +Data.DevSrcType: Device triggered the alarm +Data.SenseMethod: What triggered the alarm + +####### BackLightOn: Back light turned-on +``` +{ + "Action": "Pulse", + "Data": { + "LocaleTime": "2020-03-02 20:24:07", + "UTC": 1583173447 + } +} +``` -RequestCallState: ? - Action: ? - Data.LocaleTime: Date and time of the event - Data.Index: Index of the call +####### BackLightOff: Back light turned-on +``` +{ + "Action": "Pulse", + "Data": { + "LocaleTime": "2020-03-02 20:23:39", + "UTC": 1583173419 + } +} +``` -PassiveHungup: Call was dropped - Action - Data.LocaleTime: Date and time of the event - Data.Index: Index of the call +####### AlarmLocal: Alarm triggered by the VTO unit +``` +{ + "Action": "Stop", //Represents whether event for Start or Stop + "Data": { + "LocaleTime": "2020-03-02 20:11:16", + "UTC": 1583172676 + } +} +``` -ProfileAlarmTransmit: Alarm triggered - Action: ? - Data.AlarmType: Alarm type - Data.DevSrcType: Device triggered the alarm - Data.SenseMethod: What triggered the alarm +####### APConnect: AccessPoint got connected (Stop) or disconnected (Start) +``` +{ + "Action": "Stop", + "Data": { + "Error": "SSIDNotValid", + "LocaleTime": "2020-03-02 19:20:07", + "Result": false, + "Type": "Timerconnect", + "UTC": 1583158807 + } +} ``` -#### Structure +#### Generic structure ``` { "id": [MESSAGE ID], From b51618a03250fab008c7e75ae8b10c030abdc7c7 Mon Sep 17 00:00:00 2001 From: elad-bar Date: Mon, 2 Mar 2020 23:05:22 +0200 Subject: [PATCH 08/33] Update README.md --- README.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 74e03a1..1c7272b 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Topic will be always DahuaVTO/[EVENT NAME]/Event Message represent an event #### Events (With dedicated additional data) -####### CallNoAnswered: Call from VTO +###### CallNoAnswered: Call from VTO ``` { "Action": "Start", @@ -71,10 +71,10 @@ Message represent an event } ``` -####### IgnoreInvite: VTH answered call from VTO +###### IgnoreInvite: VTH answered call from VTO -####### VideoMotion: Video motion detected +###### VideoMotion: Video motion detected ``` { "Action": "Start", @@ -85,11 +85,11 @@ Message represent an event } ``` -####### RtspSessionDisconnect: Rtsp-Session connection connection state changed +###### RtspSessionDisconnect: Rtsp-Session connection connection state changed Action: Represented whether event Start or Stop Data.Device: IP of the device connected / disconnected -####### BackKeyLight: BackKeyLight with State +###### BackKeyLight: BackKeyLight with State ``` { "Action": "Pulse", @@ -101,7 +101,7 @@ Data.Device: IP of the device connected / disconnected } ``` -####### TimeChange: Time changed +###### TimeChange: Time changed ``` { "Action": "Pulse", @@ -114,7 +114,7 @@ Data.Device: IP of the device connected / disconnected } ``` -####### NTPAdjustTime: NTP Adjusted time +###### NTPAdjustTime: NTP Adjusted time ``` { "Action": "Pulse", @@ -128,16 +128,16 @@ Data.Device: IP of the device connected / disconnected } ``` -####### KeepLightOn: Keep light state changed +###### KeepLightOn: Keep light state changed Data.Status: Repesents whether the state changed to On or Off -####### VideoBlind: Video got blind state changed +###### VideoBlind: Video got blind state changed Action: Represents whether event Start or Stop -####### FingerPrintCheck: Finger print check status +###### FingerPrintCheck: Finger print check status Data.FingerPrintID: Finger print ID, if 0, check failed -####### SIPRegisterResult: SIP Device registration status +###### SIPRegisterResult: SIP Device registration status ``` { "Action": "Pulse", @@ -150,7 +150,7 @@ Data.FingerPrintID: Finger print ID, if 0, check failed } ``` -####### AccessControl: Someone opened the door +###### AccessControl: Someone opened the door ``` { "Action": "Pulse", @@ -173,13 +173,13 @@ Data.FingerPrintID: Finger print ID, if 0, check failed ``` -####### CallSnap: Call +###### CallSnap: Call Data.DeviceType: Which device type Data.RemoteID: UserID Data.RemoteIP: IP of VTH / SIP device Data.ChannelStates: Status -####### Invite: Invite for a call (calling) +###### Invite: Invite for a call (calling) ``` { "Action": "Pulse", @@ -195,26 +195,26 @@ Data.ChannelStates: Status } ``` -####### AccessSnap: ? +###### AccessSnap: ? Data.FtpUrl: FTP uploaded to -####### RequestCallState: ? +###### RequestCallState: ? Action: ? Data.LocaleTime: Date and time of the event Data.Index: Index of the call -####### PassiveHungup: Call was dropped +###### PassiveHungup: Call was dropped Action Data.LocaleTime: Date and time of the event Data.Index: Index of the call -####### ProfileAlarmTransmit: Alarm triggered +###### ProfileAlarmTransmit: Alarm triggered Action: ? Data.AlarmType: Alarm type Data.DevSrcType: Device triggered the alarm Data.SenseMethod: What triggered the alarm -####### BackLightOn: Back light turned-on +###### BackLightOn: Back light turned-on ``` { "Action": "Pulse", @@ -225,7 +225,7 @@ Data.SenseMethod: What triggered the alarm } ``` -####### BackLightOff: Back light turned-on +###### BackLightOff: Back light turned-on ``` { "Action": "Pulse", @@ -236,7 +236,7 @@ Data.SenseMethod: What triggered the alarm } ``` -####### AlarmLocal: Alarm triggered by the VTO unit +###### AlarmLocal: Alarm triggered by the VTO unit ``` { "Action": "Stop", //Represents whether event for Start or Stop @@ -247,7 +247,7 @@ Data.SenseMethod: What triggered the alarm } ``` -####### APConnect: AccessPoint got connected (Stop) or disconnected (Start) +###### APConnect: AccessPoint got connected (Stop) or disconnected (Start) ``` { "Action": "Stop", From e114b62affa1c6669c75ea7a524544f71e292f3a Mon Sep 17 00:00:00 2001 From: elad-bar Date: Mon, 2 Mar 2020 23:07:12 +0200 Subject: [PATCH 09/33] Update README.md --- README.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 1c7272b..881e335 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Topic will be always DahuaVTO/[EVENT NAME]/Event Message represent an event #### Events (With dedicated additional data) -###### CallNoAnswered: Call from VTO +##### CallNoAnswered: Call from VTO ``` { "Action": "Start", @@ -71,10 +71,10 @@ Message represent an event } ``` -###### IgnoreInvite: VTH answered call from VTO +##### IgnoreInvite: VTH answered call from VTO -###### VideoMotion: Video motion detected +##### VideoMotion: Video motion detected ``` { "Action": "Start", @@ -85,11 +85,11 @@ Message represent an event } ``` -###### RtspSessionDisconnect: Rtsp-Session connection connection state changed +##### RtspSessionDisconnect: Rtsp-Session connection connection state changed Action: Represented whether event Start or Stop Data.Device: IP of the device connected / disconnected -###### BackKeyLight: BackKeyLight with State +##### BackKeyLight: BackKeyLight with State ``` { "Action": "Pulse", @@ -101,7 +101,7 @@ Data.Device: IP of the device connected / disconnected } ``` -###### TimeChange: Time changed +##### TimeChange: Time changed ``` { "Action": "Pulse", @@ -114,7 +114,7 @@ Data.Device: IP of the device connected / disconnected } ``` -###### NTPAdjustTime: NTP Adjusted time +##### NTPAdjustTime: NTP Adjusted time ``` { "Action": "Pulse", @@ -128,16 +128,16 @@ Data.Device: IP of the device connected / disconnected } ``` -###### KeepLightOn: Keep light state changed +##### KeepLightOn: Keep light state changed Data.Status: Repesents whether the state changed to On or Off -###### VideoBlind: Video got blind state changed +##### VideoBlind: Video got blind state changed Action: Represents whether event Start or Stop -###### FingerPrintCheck: Finger print check status +##### FingerPrintCheck: Finger print check status Data.FingerPrintID: Finger print ID, if 0, check failed -###### SIPRegisterResult: SIP Device registration status +##### SIPRegisterResult: SIP Device registration status ``` { "Action": "Pulse", @@ -150,7 +150,7 @@ Data.FingerPrintID: Finger print ID, if 0, check failed } ``` -###### AccessControl: Someone opened the door +##### AccessControl: Someone opened the door ``` { "Action": "Pulse", @@ -173,13 +173,13 @@ Data.FingerPrintID: Finger print ID, if 0, check failed ``` -###### CallSnap: Call +##### CallSnap: Call Data.DeviceType: Which device type Data.RemoteID: UserID Data.RemoteIP: IP of VTH / SIP device Data.ChannelStates: Status -###### Invite: Invite for a call (calling) +##### Invite: Invite for a call (calling) ``` { "Action": "Pulse", @@ -195,26 +195,26 @@ Data.ChannelStates: Status } ``` -###### AccessSnap: ? +##### AccessSnap: ? Data.FtpUrl: FTP uploaded to -###### RequestCallState: ? +##### RequestCallState: ? Action: ? Data.LocaleTime: Date and time of the event Data.Index: Index of the call -###### PassiveHungup: Call was dropped +##### PassiveHungup: Call was dropped Action Data.LocaleTime: Date and time of the event Data.Index: Index of the call -###### ProfileAlarmTransmit: Alarm triggered +##### ProfileAlarmTransmit: Alarm triggered Action: ? Data.AlarmType: Alarm type Data.DevSrcType: Device triggered the alarm Data.SenseMethod: What triggered the alarm -###### BackLightOn: Back light turned-on +##### BackLightOn: Back light turned-on ``` { "Action": "Pulse", @@ -225,7 +225,7 @@ Data.SenseMethod: What triggered the alarm } ``` -###### BackLightOff: Back light turned-on +##### BackLightOff: Back light turned-on ``` { "Action": "Pulse", @@ -236,7 +236,7 @@ Data.SenseMethod: What triggered the alarm } ``` -###### AlarmLocal: Alarm triggered by the VTO unit +##### AlarmLocal: Alarm triggered by the VTO unit ``` { "Action": "Stop", //Represents whether event for Start or Stop @@ -247,7 +247,7 @@ Data.SenseMethod: What triggered the alarm } ``` -###### APConnect: AccessPoint got connected (Stop) or disconnected (Start) +##### APConnect: AccessPoint got connected (Stop) or disconnected (Start) ``` { "Action": "Stop", From a4c7b93fb33fffb7b2fe19f7a5a09452042296b0 Mon Sep 17 00:00:00 2001 From: elad-bar Date: Mon, 2 Mar 2020 23:07:52 +0200 Subject: [PATCH 10/33] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 881e335..7f78772 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ MQTT_BROKER_HOST: MQTT Broker hostname or IP MQTT_BROKER_PORT: MQTT Broker port, default=1883 MQTT_BROKER_USERNAME: MQTT Broker username MQTT_BROKER_PASSWORD: MQTT Broker password -MQTT_BROKER_TOPIC: Topic to publish all events, default=DahuaVTO/Events ``` ## Run manually From 0bdc84770513c0b4c121baf8882b4cf8a2388a45 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 21 Mar 2020 18:43:28 +0200 Subject: [PATCH 11/33] Hassio Add-on support #1 --- Dockerfile | 7 +++++-- config.json | 42 ++++++++++++++++++++++++++++++++++++++++++ data/run.sh | 15 +++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 config.json create mode 100644 data/run.sh diff --git a/Dockerfile b/Dockerfile index abec061..d89c9bb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ -FROM php:7.2.2-apache +ARG BUILD_FROM +FROM $BUILD_FROM + MAINTAINER Elad Bar WORKDIR /app @@ -14,4 +16,5 @@ ENV MQTT_BROKER_PORT=1883 ENV MQTT_BROKER_USERNAME=Username ENV MQTT_BROKER_PASSWORD=Password -CMD php -f /app/DahuaVTO.php +COPY data/run.sh . +CMD ./run.sh \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..1ca26fe --- /dev/null +++ b/config.json @@ -0,0 +1,42 @@ +{ + "name": "DahuaVTO2MQTT", + "version": "1.0.1", + "slug": "dahua_mqtt", + "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", + "arch": [ + "armhf", + "armv7", + "aarch64", + "amd64", + "i386" + ], + "startup": "services", + "boot": "auto", + "options": { + "intercom": { + "host": "192.168.1.110", + "username": "admin", + "password": "admin" + }, + "mqtt": { + "host": "core-mosquitto", + "port": "1883", + "username": "homeassistant", + "password": "homeassistant" + } + }, + "schema": { + "intercom": { + "host": "str?", + "username": "str?", + "password": "str?" + }, + "mqtt": { + "host": "str?", + "port": "int?", + "username": "str?", + "password": "str?" + } + }, + "image": "eladbar/{arch}-addon-dahuavto2mqtt" + } \ No newline at end of file diff --git a/data/run.sh b/data/run.sh new file mode 100644 index 0000000..ac67a7a --- /dev/null +++ b/data/run.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bashio + +export DAHUA_VTO_HOST=$(bashio::config 'intercom.host') +export DAHUA_VTO_USERNAME=$(bashio::config 'intercom.username') +export DAHUA_VTO_PASSWORD=$(bashio::config 'intercom.password') +export MQTT_BROKER_HOST=$(bashio::config 'mqtt.host') +export MQTT_BROKER_PORT=$(bashio::config 'mqtt.port') +export MQTT_BROKER_USERNAME=$(bashio::config 'mqtt.username') +export MQTT_BROKER_PASSWORD=$(bashio::config 'mqtt.password') + +bashio::log.info "Staring Dahua to MQTT" +bashio::log.debug "Connecting to Intercom ${DAHUA_VTO_HOST} with username ${DAHUA_VTO_USERNAME}" +bashio::log.debug "Connecting to Broker ${MQTT_BROKER_HOST} with username ${MQTT_BROKER_USERNAME}" +php -f ./DahuaVTO.php +bashio::log.info "Finished Dahua to MQTT" \ No newline at end of file From a443c0c96bf67c88d0a9874d7f39a9340a764362 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 21 Mar 2020 18:51:27 +0200 Subject: [PATCH 12/33] Updated Dockerfile --- Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index d89c9bb..f129d28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,5 +16,10 @@ ENV MQTT_BROKER_PORT=1883 ENV MQTT_BROKER_USERNAME=Username ENV MQTT_BROKER_PASSWORD=Password +RUN apk add --no-cache --update argon2-libs php php-json && \ +apk add --no-cache --virtual .build-dependencies git && \ +chmod +x /app/DahuaVTO.php && \ +apk del .build-dependencies + COPY data/run.sh . CMD ./run.sh \ No newline at end of file From 6900eae634ddabf190be87dffa7cf177ca6a5514 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 21 Mar 2020 19:19:44 +0200 Subject: [PATCH 13/33] Set FROM --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f129d28..a7852cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ -ARG BUILD_FROM -FROM $BUILD_FROM +FROM php:7.2.2-apache MAINTAINER Elad Bar From e52985a973a6ae8b67070f892103983e000ebc92 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 21 Mar 2020 19:27:51 +0200 Subject: [PATCH 14/33] Another try --- Dockerfile | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index a7852cb..0414b3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM php:7.2.2-apache +ARG BUILD_FROM +FROM $BUILD_FROM MAINTAINER Elad Bar @@ -7,14 +8,6 @@ WORKDIR /app COPY DahuaEventHandler.php ./DahuaVTO.php COPY phpMQTT.php ./phpMQTT.php -ENV DAHUA_VTO_HOST=vto-host -ENV DAHUA_VTO_USERNAME=Username -ENV DAHUA_VTO_PASSWORD=Password -ENV MQTT_BROKER_HOST=mqtt-host -ENV MQTT_BROKER_PORT=1883 -ENV MQTT_BROKER_USERNAME=Username -ENV MQTT_BROKER_PASSWORD=Password - RUN apk add --no-cache --update argon2-libs php php-json && \ apk add --no-cache --virtual .build-dependencies git && \ chmod +x /app/DahuaVTO.php && \ From dc5a3e4c24194a4dcd490caa06b166c8c73a51ae Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 21 Mar 2020 18:43:28 +0200 Subject: [PATCH 15/33] Revert "Hassio Add-on support #1" This reverts commit 0bdc84770513c0b4c121baf8882b4cf8a2388a45. --- Dockerfile | 7 ++----- config.json | 42 ------------------------------------------ data/run.sh | 15 --------------- 3 files changed, 2 insertions(+), 62 deletions(-) delete mode 100644 config.json delete mode 100644 data/run.sh diff --git a/Dockerfile b/Dockerfile index 0414b3a..5aa611d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,4 @@ -ARG BUILD_FROM -FROM $BUILD_FROM - +FROM php:7.2.2-apache MAINTAINER Elad Bar WORKDIR /app @@ -13,5 +11,4 @@ apk add --no-cache --virtual .build-dependencies git && \ chmod +x /app/DahuaVTO.php && \ apk del .build-dependencies -COPY data/run.sh . -CMD ./run.sh \ No newline at end of file +CMD php -f /app/DahuaVTO.php diff --git a/config.json b/config.json deleted file mode 100644 index 1ca26fe..0000000 --- a/config.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "DahuaVTO2MQTT", - "version": "1.0.1", - "slug": "dahua_mqtt", - "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", - "arch": [ - "armhf", - "armv7", - "aarch64", - "amd64", - "i386" - ], - "startup": "services", - "boot": "auto", - "options": { - "intercom": { - "host": "192.168.1.110", - "username": "admin", - "password": "admin" - }, - "mqtt": { - "host": "core-mosquitto", - "port": "1883", - "username": "homeassistant", - "password": "homeassistant" - } - }, - "schema": { - "intercom": { - "host": "str?", - "username": "str?", - "password": "str?" - }, - "mqtt": { - "host": "str?", - "port": "int?", - "username": "str?", - "password": "str?" - } - }, - "image": "eladbar/{arch}-addon-dahuavto2mqtt" - } \ No newline at end of file diff --git a/data/run.sh b/data/run.sh deleted file mode 100644 index ac67a7a..0000000 --- a/data/run.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bashio - -export DAHUA_VTO_HOST=$(bashio::config 'intercom.host') -export DAHUA_VTO_USERNAME=$(bashio::config 'intercom.username') -export DAHUA_VTO_PASSWORD=$(bashio::config 'intercom.password') -export MQTT_BROKER_HOST=$(bashio::config 'mqtt.host') -export MQTT_BROKER_PORT=$(bashio::config 'mqtt.port') -export MQTT_BROKER_USERNAME=$(bashio::config 'mqtt.username') -export MQTT_BROKER_PASSWORD=$(bashio::config 'mqtt.password') - -bashio::log.info "Staring Dahua to MQTT" -bashio::log.debug "Connecting to Intercom ${DAHUA_VTO_HOST} with username ${DAHUA_VTO_USERNAME}" -bashio::log.debug "Connecting to Broker ${MQTT_BROKER_HOST} with username ${MQTT_BROKER_USERNAME}" -php -f ./DahuaVTO.php -bashio::log.info "Finished Dahua to MQTT" \ No newline at end of file From 814c6c7ca47d990dcc6ecf5d4cdbb456e34f60c1 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 10:43:48 +0300 Subject: [PATCH 16/33] Remove previous setup --- Dockerfile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5aa611d..cbfe7e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,4 @@ WORKDIR /app COPY DahuaEventHandler.php ./DahuaVTO.php COPY phpMQTT.php ./phpMQTT.php -RUN apk add --no-cache --update argon2-libs php php-json && \ -apk add --no-cache --virtual .build-dependencies git && \ -chmod +x /app/DahuaVTO.php && \ -apk del .build-dependencies - CMD php -f /app/DahuaVTO.php From 67a197295207aa8da13e68728616132d0c084154 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 10:50:06 +0300 Subject: [PATCH 17/33] Support also Hass.io Addon --- Dockerfile.hassio | 17 +++++++++++++++++ config.json | 42 ++++++++++++++++++++++++++++++++++++++++++ data/run.sh | 15 +++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 Dockerfile.hassio create mode 100644 config.json create mode 100644 data/run.sh diff --git a/Dockerfile.hassio b/Dockerfile.hassio new file mode 100644 index 0000000..c582852 --- /dev/null +++ b/Dockerfile.hassio @@ -0,0 +1,17 @@ +ARG BUILD_FROM +FROM $(BUILD_FROM) + +MAINTAINER Elad Bar + +WORKDIR /app + +COPY DahuaEventHandler.php ./DahuaVTO.php +COPY phpMQTT.php ./phpMQTT.php +COPY data/run.sh . + +RUN apk add --no-cache --update argon2-libs php php-json && \ +apk add --no-cache --virtual .build-dependencies git && \ +chmod +x /app/DahuaVTO.php && \ +apk del .build-dependencies + +CMD ./run.sh diff --git a/config.json b/config.json new file mode 100644 index 0000000..2ad762d --- /dev/null +++ b/config.json @@ -0,0 +1,42 @@ +{ + "name": "DahuaVTO2MQTT", + "version": "1.0.1", + "slug": "dahua_mqtt", + "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", + "arch": [ + "armhf", + "armv7", + "aarch64", + "amd64", + "i386" + ], + "startup": "services", + "boot": "auto", + "options": { + "intercom": { + "host": "192.168.1.110", + "username": "admin", + "password": "admin" + }, + "mqtt": { + "host": "core-mosquitto", + "port": "1883", + "username": "homeassistant", + "password": "homeassistant" + } + }, + "schema": { + "intercom": { + "host": "str?", + "username": "str?", + "password": "str?" + }, + "mqtt": { + "host": "str?", + "port": "int?", + "username": "str?", + "password": "str?" + } + }, + "image": "eladbar/{arch}-addon-dahuavto2mqtt" + } \ No newline at end of file diff --git a/data/run.sh b/data/run.sh new file mode 100644 index 0000000..ac67a7a --- /dev/null +++ b/data/run.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bashio + +export DAHUA_VTO_HOST=$(bashio::config 'intercom.host') +export DAHUA_VTO_USERNAME=$(bashio::config 'intercom.username') +export DAHUA_VTO_PASSWORD=$(bashio::config 'intercom.password') +export MQTT_BROKER_HOST=$(bashio::config 'mqtt.host') +export MQTT_BROKER_PORT=$(bashio::config 'mqtt.port') +export MQTT_BROKER_USERNAME=$(bashio::config 'mqtt.username') +export MQTT_BROKER_PASSWORD=$(bashio::config 'mqtt.password') + +bashio::log.info "Staring Dahua to MQTT" +bashio::log.debug "Connecting to Intercom ${DAHUA_VTO_HOST} with username ${DAHUA_VTO_USERNAME}" +bashio::log.debug "Connecting to Broker ${MQTT_BROKER_HOST} with username ${MQTT_BROKER_USERNAME}" +php -f ./DahuaVTO.php +bashio::log.info "Finished Dahua to MQTT" \ No newline at end of file From f25676347ff60e2de7010176d407c88805121919 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 11:04:24 +0300 Subject: [PATCH 18/33] Another try for Hassio --- Dockerfile.hassio | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile.hassio b/Dockerfile.hassio index c582852..9b9c8a8 100644 --- a/Dockerfile.hassio +++ b/Dockerfile.hassio @@ -1,5 +1,7 @@ ARG BUILD_FROM -FROM $(BUILD_FROM) +FROM $BUILD_FROM + +ENV LANG C.UTF-8 MAINTAINER Elad Bar @@ -12,6 +14,7 @@ COPY data/run.sh . RUN apk add --no-cache --update argon2-libs php php-json && \ apk add --no-cache --virtual .build-dependencies git && \ chmod +x /app/DahuaVTO.php && \ +chmod +x /app/run.sh && \ apk del .build-dependencies CMD ./run.sh From 6a8cb9d718cd607dd211c2710a1bf5200c43f74b Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 11:27:32 +0300 Subject: [PATCH 19/33] Added build.json --- build.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 build.json diff --git a/build.json b/build.json new file mode 100644 index 0000000..e56a2a7 --- /dev/null +++ b/build.json @@ -0,0 +1,10 @@ +{ + "build_from": { + "aarch64": "hassioaddons/base-aarch64:7.0.2", + "amd64": "hassioaddons/base-amd64:7.0.2", + "armhf": "hassioaddons/base-armhf:7.0.2", + "armv7": "hassioaddons/base-armv7:7.0.2", + "i386": "hassioaddons/base-i386:7.0.2" + }, + "args": {} +} \ No newline at end of file From 6f9b53f221985a751f049dd50ed177dc0d87f0e2 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 11:34:07 +0300 Subject: [PATCH 20/33] Another fix for dockerfile --- Dockerfile.hassio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.hassio b/Dockerfile.hassio index 9b9c8a8..8ff7af8 100644 --- a/Dockerfile.hassio +++ b/Dockerfile.hassio @@ -1,5 +1,5 @@ ARG BUILD_FROM -FROM $BUILD_FROM +FROM ${BUILD_FROM} ENV LANG C.UTF-8 From 1b19660532d255f3fb3c5ae25dd9ae7132141efd Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 11:55:56 +0300 Subject: [PATCH 21/33] Another fix --- .../DahuaCancelCall.php | 0 .../DahuaEventHandler.php | 0 Dockerfile.hassio => dahuavto2mqtt/Dockerfile | 6 ++---- dahuavto2mqtt/Dockerfile.hassio | 20 +++++++++++++++++++ .../Dockerfile.original | 0 README.md => dahuavto2mqtt/README.md | 0 build.json => dahuavto2mqtt/build.json | 0 config.json => dahuavto2mqtt/config.json | 0 phpMQTT.php => dahuavto2mqtt/phpMQTT.php | 0 {data => dahuavto2mqtt}/run.sh | 0 repository.json | 5 +++++ 11 files changed, 27 insertions(+), 4 deletions(-) rename DahuaCancelCall.php => dahuavto2mqtt/DahuaCancelCall.php (100%) rename DahuaEventHandler.php => dahuavto2mqtt/DahuaEventHandler.php (100%) rename Dockerfile.hassio => dahuavto2mqtt/Dockerfile (80%) create mode 100644 dahuavto2mqtt/Dockerfile.hassio rename Dockerfile => dahuavto2mqtt/Dockerfile.original (100%) rename README.md => dahuavto2mqtt/README.md (100%) rename build.json => dahuavto2mqtt/build.json (100%) rename config.json => dahuavto2mqtt/config.json (100%) rename phpMQTT.php => dahuavto2mqtt/phpMQTT.php (100%) rename {data => dahuavto2mqtt}/run.sh (100%) create mode 100644 repository.json diff --git a/DahuaCancelCall.php b/dahuavto2mqtt/DahuaCancelCall.php similarity index 100% rename from DahuaCancelCall.php rename to dahuavto2mqtt/DahuaCancelCall.php diff --git a/DahuaEventHandler.php b/dahuavto2mqtt/DahuaEventHandler.php similarity index 100% rename from DahuaEventHandler.php rename to dahuavto2mqtt/DahuaEventHandler.php diff --git a/Dockerfile.hassio b/dahuavto2mqtt/Dockerfile similarity index 80% rename from Dockerfile.hassio rename to dahuavto2mqtt/Dockerfile index 8ff7af8..a3283ea 100644 --- a/Dockerfile.hassio +++ b/dahuavto2mqtt/Dockerfile @@ -1,15 +1,13 @@ ARG BUILD_FROM -FROM ${BUILD_FROM} +FROM $BUILD_FROM ENV LANG C.UTF-8 -MAINTAINER Elad Bar - WORKDIR /app COPY DahuaEventHandler.php ./DahuaVTO.php COPY phpMQTT.php ./phpMQTT.php -COPY data/run.sh . +COPY run.sh . RUN apk add --no-cache --update argon2-libs php php-json && \ apk add --no-cache --virtual .build-dependencies git && \ diff --git a/dahuavto2mqtt/Dockerfile.hassio b/dahuavto2mqtt/Dockerfile.hassio new file mode 100644 index 0000000..0a4971c --- /dev/null +++ b/dahuavto2mqtt/Dockerfile.hassio @@ -0,0 +1,20 @@ +ARG BUILD_FROM +FROM ${BUILD_FROM} + +ENV LANG C.UTF-8 + +MAINTAINER Elad Bar + +WORKDIR /app + +COPY dahuavto2mqtt/DahuaEventHandler.php ./DahuaVTO.php +COPY dahuavto2mqtt/phpMQTT.php ./phpMQTT.php +COPY dahuavto2mqtt/run.sh . + +RUN apk add --no-cache --update argon2-libs php php-json && \ +apk add --no-cache --virtual .build-dependencies git && \ +chmod +x /app/DahuaVTO.php && \ +chmod +x /app/run.sh && \ +apk del .build-dependencies + +CMD ./run.sh diff --git a/Dockerfile b/dahuavto2mqtt/Dockerfile.original similarity index 100% rename from Dockerfile rename to dahuavto2mqtt/Dockerfile.original diff --git a/README.md b/dahuavto2mqtt/README.md similarity index 100% rename from README.md rename to dahuavto2mqtt/README.md diff --git a/build.json b/dahuavto2mqtt/build.json similarity index 100% rename from build.json rename to dahuavto2mqtt/build.json diff --git a/config.json b/dahuavto2mqtt/config.json similarity index 100% rename from config.json rename to dahuavto2mqtt/config.json diff --git a/phpMQTT.php b/dahuavto2mqtt/phpMQTT.php similarity index 100% rename from phpMQTT.php rename to dahuavto2mqtt/phpMQTT.php diff --git a/data/run.sh b/dahuavto2mqtt/run.sh similarity index 100% rename from data/run.sh rename to dahuavto2mqtt/run.sh diff --git a/repository.json b/repository.json new file mode 100644 index 0000000..7ae5eec --- /dev/null +++ b/repository.json @@ -0,0 +1,5 @@ +{ + "name": "Hass.io DahuaVTO2MQTT add-on", + "url": "https://github.com/elad-bar/Dahua", + "maintainer": "Elad Bar " +} \ No newline at end of file From 1a0c1c4bae5b261fc2655d99de0fe68b98d96815 Mon Sep 17 00:00:00 2001 From: elad-bar Date: Fri, 27 Mar 2020 11:59:26 +0300 Subject: [PATCH 22/33] Update repository.json --- repository.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repository.json b/repository.json index 7ae5eec..1e77aff 100644 --- a/repository.json +++ b/repository.json @@ -1,5 +1,5 @@ { "name": "Hass.io DahuaVTO2MQTT add-on", - "url": "https://github.com/elad-bar/Dahua", + "url": "https://github.com/elad-bar/Hassio-addons", "maintainer": "Elad Bar " -} \ No newline at end of file +} From 3e8527f12b73d238c3332be04a623944de9d5f1f Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 16:11:25 +0300 Subject: [PATCH 23/33] Updated BUILD_FROM --- dahuavto2mqtt/Dockerfile | 5 +++-- dahuavto2mqtt/config.json | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dahuavto2mqtt/Dockerfile b/dahuavto2mqtt/Dockerfile index a3283ea..587abdd 100644 --- a/dahuavto2mqtt/Dockerfile +++ b/dahuavto2mqtt/Dockerfile @@ -1,5 +1,6 @@ -ARG BUILD_FROM -FROM $BUILD_FROM +ARG BUILD_FROM=hassioaddons/base:7.0.3 +# hadolint ignore=DL3006 +FROM ${BUILD_FROM} ENV LANG C.UTF-8 diff --git a/dahuavto2mqtt/config.json b/dahuavto2mqtt/config.json index 2ad762d..b1d351d 100644 --- a/dahuavto2mqtt/config.json +++ b/dahuavto2mqtt/config.json @@ -1,7 +1,7 @@ { "name": "DahuaVTO2MQTT", "version": "1.0.1", - "slug": "dahua_mqtt", + "slug": "dahuavto2mqtt", "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", "arch": [ "armhf", @@ -37,6 +37,5 @@ "username": "str?", "password": "str?" } - }, - "image": "eladbar/{arch}-addon-dahuavto2mqtt" + } } \ No newline at end of file From d51312db4267d1dcb911d61181d0486bcff424a3 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 16:13:45 +0300 Subject: [PATCH 24/33] Updated repo.json --- repository.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repository.json b/repository.json index 1e77aff..20f418f 100644 --- a/repository.json +++ b/repository.json @@ -1,5 +1,5 @@ { - "name": "Hass.io DahuaVTO2MQTT add-on", + "name": "Hass.io Bar's add-on", "url": "https://github.com/elad-bar/Hassio-addons", "maintainer": "Elad Bar " } From eee7271162f837edf25beb325412f76b60268397 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 16:19:26 +0300 Subject: [PATCH 25/33] Removed old Dockerfiles --- dahuavto2mqtt/Dockerfile.hassio | 20 -------------------- dahuavto2mqtt/Dockerfile.original | 9 --------- 2 files changed, 29 deletions(-) delete mode 100644 dahuavto2mqtt/Dockerfile.hassio delete mode 100644 dahuavto2mqtt/Dockerfile.original diff --git a/dahuavto2mqtt/Dockerfile.hassio b/dahuavto2mqtt/Dockerfile.hassio deleted file mode 100644 index 0a4971c..0000000 --- a/dahuavto2mqtt/Dockerfile.hassio +++ /dev/null @@ -1,20 +0,0 @@ -ARG BUILD_FROM -FROM ${BUILD_FROM} - -ENV LANG C.UTF-8 - -MAINTAINER Elad Bar - -WORKDIR /app - -COPY dahuavto2mqtt/DahuaEventHandler.php ./DahuaVTO.php -COPY dahuavto2mqtt/phpMQTT.php ./phpMQTT.php -COPY dahuavto2mqtt/run.sh . - -RUN apk add --no-cache --update argon2-libs php php-json && \ -apk add --no-cache --virtual .build-dependencies git && \ -chmod +x /app/DahuaVTO.php && \ -chmod +x /app/run.sh && \ -apk del .build-dependencies - -CMD ./run.sh diff --git a/dahuavto2mqtt/Dockerfile.original b/dahuavto2mqtt/Dockerfile.original deleted file mode 100644 index cbfe7e6..0000000 --- a/dahuavto2mqtt/Dockerfile.original +++ /dev/null @@ -1,9 +0,0 @@ -FROM php:7.2.2-apache -MAINTAINER Elad Bar - -WORKDIR /app - -COPY DahuaEventHandler.php ./DahuaVTO.php -COPY phpMQTT.php ./phpMQTT.php - -CMD php -f /app/DahuaVTO.php From 83831fb3be5f11ffacdb4c953c7b491bfa25bbbc Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 16:49:06 +0300 Subject: [PATCH 26/33] Updated README files --- README.md | 18 +++ dahuavto2mqtt/Events.md | 236 +++++++++++++++++++++++++++++ dahuavto2mqtt/README.md | 322 +++++++--------------------------------- 3 files changed, 309 insertions(+), 267 deletions(-) create mode 100644 README.md create mode 100644 dahuavto2mqtt/Events.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..578166f --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# About + +This repository contains unofficial addons: +* [DahuaVTO2MQTT](https://github.com/elad-bar/Hassio-addons/tree/master/dahuavto2mqtt). + +# Installation + +Add this repository to your [Hass.io](https://home-assistant.io/hassio/) instance: + +`https://github.com/elad-bar/Hassio-addons` + +If you have trouble you can follow the [official docs](https://home-assistant.io/hassio/installing_third_party_addons/). + +Then install the add-on. + +# Issues + +If you have an issue with this plugin, please [file an issue](https://github.com/elad-bar/Hassio-addons/issues). diff --git a/dahuavto2mqtt/Events.md b/dahuavto2mqtt/Events.md new file mode 100644 index 0000000..ebd8855 --- /dev/null +++ b/dahuavto2mqtt/Events.md @@ -0,0 +1,236 @@ +# DahuaVTO2MQTT MQTT Events + +#### Topic +will be always DahuaVTO/[EVENT NAME]/Event +Message represent a single event + +#### Events (With dedicated additional data) +##### CallNoAnswered: Call from VTO +``` +{ + "Action": "Start", + "Data": { + "CallID": "1", + "IsEncryptedStream": false, + "LocaleTime": "2020-03-02 20:11:13", + "LockNum": 2, + "SupportPaas": false, + "TCPPort": 37777, + "UTC": 1583172673 + } +} +``` + +##### IgnoreInvite: VTH answered call from VTO + + +##### VideoMotion: Video motion detected +``` +{ + "Action": "Start", + "Data": { + "LocaleTime": "2020-03-02 20:44:28", + "UTC": 1583174668 + } +} +``` + +##### RtspSessionDisconnect: Rtsp-Session connection connection state changed +Action: Represented whether event Start or Stop +Data.Device: IP of the device connected / disconnected + +##### BackKeyLight: BackKeyLight with State +``` +{ + "Action": "Pulse", + "Data": { + "LocaleTime": "2020-03-02 20:24:07", + "State": 8, + "UTC": 1583173447 + } +} +``` + +##### TimeChange: Time changed +``` +{ + "Action": "Pulse", + "Data": { + "BeforeModifyTime": "02-03-2020 21:41:40", + "LocaleTime": "2020-03-02 21:41:40", + "ModifiedTime": "02-03-2020 21:41:39", + "UTC": 1583178100 + } +} +``` + +##### NTPAdjustTime: NTP Adjusted time +``` +{ + "Action": "Pulse", + "Data": { + "Address": "time.windows.com", + "Before": "02-03-2020 21:41:38", + "LocaleTime": "2020-03-02 21:41:40", + "UTC": 1583178100, + "result": true + } +} +``` + +##### KeepLightOn: Keep light state changed +Data.Status: Repesents whether the state changed to On or Off + +##### VideoBlind: Video got blind state changed +Action: Represents whether event Start or Stop + +##### FingerPrintCheck: Finger print check status +Data.FingerPrintID: Finger print ID, if 0, check failed + +##### SIPRegisterResult: SIP Device registration status +``` +{ + "Action": "Pulse", + "Data": { + "Date": "02-03-2020 21:42:59", + "LocaleTime": "2020-03-02 21:42:59", + "Success": true, + "UTC": 1583178179 + } +} +``` + +##### AccessControl: Someone opened the door +``` +{ + "Action": "Pulse", + "Data": { + "CardNo": "", + "CardType": null, + "LocaleTime": "2020-03-02 20:24:08", + "Method": 4, // 4=Remote/WebIf/SIPext | 6=FingerPrint + "Name": "OpenDoor", // Access control action name + "Password": "", + "ReaderID": "1", + "RecNo": 691, + "SnapURL": "", + "Status": 1, + "Type": "Entry", + "UTC": 1583173448, + "UserID": "" // By FingerprintManager / Room Number / SIPext + } +} +``` + + +##### CallSnap: Call +Data.DeviceType: Which device type +Data.RemoteID: UserID +Data.RemoteIP: IP of VTH / SIP device +Data.ChannelStates: Status + +##### Invite: Invite for a call (calling) +``` +{ + "Action": "Pulse", + "Data": { + "CallID": "1", + "IsEncryptedStream": false, + "LocaleTime": "2020-03-02 20:11:13", + "LockNum": 2, + "SupportPaas": false, + "TCPPort": 37777, + "UTC": 1583172673 + } +} +``` + +##### AccessSnap: ? +Data.FtpUrl: FTP uploaded to + +##### RequestCallState: ? +Action: ? +Data.LocaleTime: Date and time of the event +Data.Index: Index of the call + +##### PassiveHungup: Call was dropped +Action +Data.LocaleTime: Date and time of the event +Data.Index: Index of the call + +##### ProfileAlarmTransmit: Alarm triggered +Action: ? +Data.AlarmType: Alarm type +Data.DevSrcType: Device triggered the alarm +Data.SenseMethod: What triggered the alarm + +##### BackLightOn: Back light turned-on +``` +{ + "Action": "Pulse", + "Data": { + "LocaleTime": "2020-03-02 20:24:07", + "UTC": 1583173447 + } +} +``` + +##### BackLightOff: Back light turned-on +``` +{ + "Action": "Pulse", + "Data": { + "LocaleTime": "2020-03-02 20:23:39", + "UTC": 1583173419 + } +} +``` + +##### AlarmLocal: Alarm triggered by the VTO unit +``` +{ + "Action": "Stop", //Represents whether event for Start or Stop + "Data": { + "LocaleTime": "2020-03-02 20:11:16", + "UTC": 1583172676 + } +} +``` + +##### APConnect: AccessPoint got connected (Stop) or disconnected (Start) +``` +{ + "Action": "Stop", + "Data": { + "Error": "SSIDNotValid", + "LocaleTime": "2020-03-02 19:20:07", + "Result": false, + "Type": "Timerconnect", + "UTC": 1583158807 + } +} +``` + +#### Generic structure +``` +{ + "id": [MESSAGE ID], + "method":"client.notifyEventStream", + "params":{ + "SID":513, + "eventList":[ + { + "Action":"[EVENT ACTION]", + "Code":"[EVENT NAME]", + "Data":{ + "LocaleTime":"YYYY-MM-DD HH:mm:SS", + "UTC": [EPOCH TIMESTAMP] + }, + "Index": [EVENT ID IN MESSAGE], + "Param":[] + } + ] + }, + "session": [SESSION IDENTIFIER] +} +``` diff --git a/dahuavto2mqtt/README.md b/dahuavto2mqtt/README.md index 7f78772..031f8da 100644 --- a/dahuavto2mqtt/README.md +++ b/dahuavto2mqtt/README.md @@ -1,285 +1,73 @@ -# DahuaVTO2MQTT +# Hass.io Add-on: Dahua Intercom to MQTT Gateway -## Description -Listens to events from Dahua VTO unit and publishes them via MQTT Message +Sends Dahua Intercome events to the MQTT Gateway -## Credits -All credits goes to @riogrande75 who wrote that complicated integration -Original code can be found in @riogrande75/Dahua - -## Change-log -2020-Feb-03 - Initial version combing the event listener with MQTT - -## Environment Variables -``` -DAHUA_VTO_HOST: Dahua VTO hostname or IP -DAHUA_VTO_USERNAME: Dahua VTO username to access (should be admin) -DAHUA_VTO_PASSWORD: Dahua VTO administrator password (same as accessing web management) -MQTT_BROKER_HOST: MQTT Broker hostname or IP -MQTT_BROKER_PORT: MQTT Broker port, default=1883 -MQTT_BROKER_USERNAME: MQTT Broker username -MQTT_BROKER_PASSWORD: MQTT Broker password -``` +![Supports aarch64 Architecture][aarch64-shield] ![Supports amd64 Architecture][amd64-shield] ![Supports armhf Architecture][armhf-shield] ![Supports armv7 Architecture][armv7-shield] ![Supports i386 Architecture][i386-shield] -## Run manually -Requirements: -* All environment variables above -* PHP +## Installation -``` -php -f DahuaEventHandler.php -``` - -## Docker Compose -``` -version: '2' -services: - dahuavto2mqtt: - image: "eladbar/dahuavto2mqtt:latest" - container_name: "dahuavto2mqtt" - hostname: "dahuavto2mqtt" - restart: always - environment: - - DAHUA_VTO_HOST=vto-host - - DAHUA_VTO_USERNAME=Username - - DAHUA_VTO_PASSWORD=Password - - MQTT_BROKER_HOST=mqtt-host - - MQTT_BROKER_PORT=1883 - - MQTT_BROKER_USERNAME=Username - - MQTT_BROKER_PASSWORD=Password -``` +The installation of this add-on is straightforward and easy to do. -## MQTT Message (Dahua VTO Event Payload) -Topic will be always DahuaVTO/[EVENT NAME]/Event -Message represent an event +1. Navigate in your Home Assistant frontend to **Hass.io** -> **Add-on Store**. +2. Add a new repository by URL `https://github.com/elad-bar/Hassio-addons` +3. Find the "DahuaVTO2MQTT" add-on and click it. +4. Click on the "INSTALL" button. -#### Events (With dedicated additional data) -##### CallNoAnswered: Call from VTO -``` -{ - "Action": "Start", - "Data": { - "CallID": "1", - "IsEncryptedStream": false, - "LocaleTime": "2020-03-02 20:11:13", - "LockNum": 2, - "SupportPaas": false, - "TCPPort": 37777, - "UTC": 1583172673 - } -} -``` +## How to use -##### IgnoreInvite: VTH answered call from VTO +To use this add-on, you need to supply the config for your intercom and MQTT gateway +- Requires you to use one of the supported Dahua Intercoms +- Requires you to run a local MQTT server +- [MQTT Event's][mqtt-events] -##### VideoMotion: Video motion detected -``` -{ - "Action": "Start", - "Data": { - "LocaleTime": "2020-03-02 20:44:28", - "UTC": 1583174668 - } -} -``` -##### RtspSessionDisconnect: Rtsp-Session connection connection state changed -Action: Represented whether event Start or Stop -Data.Device: IP of the device connected / disconnected - -##### BackKeyLight: BackKeyLight with State -``` -{ - "Action": "Pulse", - "Data": { - "LocaleTime": "2020-03-02 20:24:07", - "State": 8, - "UTC": 1583173447 - } -} -``` +## Configuration -##### TimeChange: Time changed -``` -{ - "Action": "Pulse", - "Data": { - "BeforeModifyTime": "02-03-2020 21:41:40", - "LocaleTime": "2020-03-02 21:41:40", - "ModifiedTime": "02-03-2020 21:41:39", - "UTC": 1583178100 - } -} -``` +Add-on configuration: -##### NTPAdjustTime: NTP Adjusted time -``` +```json { - "Action": "Pulse", - "Data": { - "Address": "time.windows.com", - "Before": "02-03-2020 21:41:38", - "LocaleTime": "2020-03-02 21:41:40", - "UTC": 1583178100, - "result": true + "intercom": { + "host": "192.168.1.110", + "username": "admin", + "password": "admin" + }, + "mqtt": { + "host": "core-mosquitto", + "port": "1883", + "username": "homeassistant", + "password": "homeassistant" + } } -} ``` -##### KeepLightOn: Keep light state changed -Data.Status: Repesents whether the state changed to On or Off - -##### VideoBlind: Video got blind state changed -Action: Represents whether event Start or Stop +## Known issues and limitations -##### FingerPrintCheck: Finger print check status -Data.FingerPrintID: Finger print ID, if 0, check failed +- None. Let me know. -##### SIPRegisterResult: SIP Device registration status -``` -{ - "Action": "Pulse", - "Data": { - "Date": "02-03-2020 21:42:59", - "LocaleTime": "2020-03-02 21:42:59", - "Success": true, - "UTC": 1583178179 - } -} -``` - -##### AccessControl: Someone opened the door -``` -{ - "Action": "Pulse", - "Data": { - "CardNo": "", - "CardType": null, - "LocaleTime": "2020-03-02 20:24:08", - "Method": 4, // 4=Remote/WebIf/SIPext | 6=FingerPrint - "Name": "OpenDoor", // Access control action name - "Password": "", - "ReaderID": "1", - "RecNo": 691, - "SnapURL": "", - "Status": 1, - "Type": "Entry", - "UTC": 1583173448, - "UserID": "" // By FingerprintManager / Room Number / SIPext - } -} -``` - - -##### CallSnap: Call -Data.DeviceType: Which device type -Data.RemoteID: UserID -Data.RemoteIP: IP of VTH / SIP device -Data.ChannelStates: Status - -##### Invite: Invite for a call (calling) -``` -{ - "Action": "Pulse", - "Data": { - "CallID": "1", - "IsEncryptedStream": false, - "LocaleTime": "2020-03-02 20:11:13", - "LockNum": 2, - "SupportPaas": false, - "TCPPort": 37777, - "UTC": 1583172673 - } -} -``` - -##### AccessSnap: ? -Data.FtpUrl: FTP uploaded to - -##### RequestCallState: ? -Action: ? -Data.LocaleTime: Date and time of the event -Data.Index: Index of the call - -##### PassiveHungup: Call was dropped -Action -Data.LocaleTime: Date and time of the event -Data.Index: Index of the call - -##### ProfileAlarmTransmit: Alarm triggered -Action: ? -Data.AlarmType: Alarm type -Data.DevSrcType: Device triggered the alarm -Data.SenseMethod: What triggered the alarm - -##### BackLightOn: Back light turned-on -``` -{ - "Action": "Pulse", - "Data": { - "LocaleTime": "2020-03-02 20:24:07", - "UTC": 1583173447 - } -} -``` - -##### BackLightOff: Back light turned-on -``` -{ - "Action": "Pulse", - "Data": { - "LocaleTime": "2020-03-02 20:23:39", - "UTC": 1583173419 - } -} -``` - -##### AlarmLocal: Alarm triggered by the VTO unit -``` -{ - "Action": "Stop", //Represents whether event for Start or Stop - "Data": { - "LocaleTime": "2020-03-02 20:11:16", - "UTC": 1583172676 - } -} -``` - -##### APConnect: AccessPoint got connected (Stop) or disconnected (Start) -``` -{ - "Action": "Stop", - "Data": { - "Error": "SSIDNotValid", - "LocaleTime": "2020-03-02 19:20:07", - "Result": false, - "Type": "Timerconnect", - "UTC": 1583158807 - } -} -``` - -#### Generic structure -``` -{ - "id": [MESSAGE ID], - "method":"client.notifyEventStream", - "params":{ - "SID":513, - "eventList":[ - { - "Action":"[EVENT ACTION]", - "Code":"[EVENT NAME]", - "Data":{ - "LocaleTime":"YYYY-MM-DD HH:mm:SS", - "UTC": [EPOCH TIMESTAMP] - }, - "Index": [EVENT ID IN MESSAGE], - "Param":[] - } - ] - }, - "session": [SESSION IDENTIFIER] -} -``` +## Credits +Using [riogrande75's][original-author] PHP code that connects your intercom and publishes events via MQTT broker. +Thanks to [troykelly's][original-addon-author], I created that Hass.io addon. + +## Support + +Got questions? + +You have several options to get them answered: +- The Home Assistant [Community Forum][forum]. + +In case you've found a bug, please [open an issue on our GitHub][issue]. + +[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg +[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg +[armhf-shield]: https://img.shields.io/badge/armhf-yes-green.svg +[armv7-shield]: https://img.shields.io/badge/armv7-yes-green.svg +[i386-shield]: https://img.shields.io/badge/i386-yes-green.svg +[forum]: https://community.home-assistant.io +[issue]: https://github.com/elad-bar/Hassio-addons/issues +[source-shield]: https://img.shields.io/badge/version-master-blue.svg +[source]: https://github.com/elad-bar/Dahua/tree/master +[original-author]: https://github.com/riogrande75/Dahua +[original-addon-author]: https://github.com/troykelly/ +[mqtt-events]: https://github.com/elad-bar/Hassio-addons/blob/master/dahuavto2mqtt/Events.md From 3dcc10abf0dbd314de85c1973cf0eafc16738f66 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 17:06:59 +0300 Subject: [PATCH 27/33] Separated docker from Hass.io add-on --- dahuavto2mqtt/DahuaCancelCall.php | 208 ------------ dahuavto2mqtt/DahuaEventHandler.php | 490 ---------------------------- dahuavto2mqtt/Dockerfile | 13 +- dahuavto2mqtt/Events.md | 236 -------------- dahuavto2mqtt/README.md | 10 +- dahuavto2mqtt/config.json | 2 +- dahuavto2mqtt/icon.png | Bin 0 -> 3850 bytes dahuavto2mqtt/logo.png | Bin 0 -> 3850 bytes dahuavto2mqtt/phpMQTT.php | 421 ------------------------ 9 files changed, 13 insertions(+), 1367 deletions(-) delete mode 100644 dahuavto2mqtt/DahuaCancelCall.php delete mode 100644 dahuavto2mqtt/DahuaEventHandler.php delete mode 100644 dahuavto2mqtt/Events.md create mode 100644 dahuavto2mqtt/icon.png create mode 100644 dahuavto2mqtt/logo.png delete mode 100644 dahuavto2mqtt/phpMQTT.php diff --git a/dahuavto2mqtt/DahuaCancelCall.php b/dahuavto2mqtt/DahuaCancelCall.php deleted file mode 100644 index 60ba99c..0000000 --- a/dahuavto2mqtt/DahuaCancelCall.php +++ /dev/null @@ -1,208 +0,0 @@ -\n"; -$Dahua = new Dahua_Functions("192.168.1.208", "admin", "password"); # VTO's IP and user/pwd -$status = $Dahua->Main(); -logging("All done"); -function logging($text){ - list($ts) = explode(".",microtime(true)); - $dt = new DateTime(date("Y-m-d H:i:s.",$ts)); - $logdate = $dt->format("Y-m-d H:i:s.u"); - echo $logdate.": "; - print_r($text); - echo "\n"; -} -class Dahua_Functions -{ - private $sock, $host, $port, $credentials; - private $ID = 0; # Our Request / Responce ID that must be in all requests and initated by us - private $SessionID = 0; # Session ID will be returned after successful login - private $SID = 0; # SID will be returned after we called .attach with 'Object ID' - private $FakeIPaddr = '(null)'; # WebGUI: mask our real IP - private $clientType = ''; # WebGUI: We do not show up in logs or online users - function Dahua_Functions($host, $user, $pass) - { - $this->host = $host; - $this->username = $user; - $this->password = $pass; - } - function Gen_md5_hash($Dahua_random, $Dahua_realm, $username, $password) - { - $PWDDB_HASH = strtoupper(md5($username.':'.$Dahua_realm.':'.$password)); - $PASS = $username.':'.$Dahua_random.':'.$PWDDB_HASH; - $RANDOM_HASH = strtoupper(md5($PASS)); - return $RANDOM_HASH; - } - - function disconnect() - { - global $id; - if ($this->sock) - fclose($this->sock); - } - - function Send($packet) - { - if (empty($packet)){ - $packet = ''; - } - $header = pack("N",0x20000000); - $header .= pack("N",0x44484950); - $header .= pack("V",$this->SessionID); - $header .= pack("V",$this->ID); - $header .= pack("V",strlen($packet)); - $header .= pack("V",0); - $header .= pack("V",strlen($packet)); - $header .= pack("V",0); - - if (strlen($header) != 32){ - logging("Binary header != 32 ({})"); - return; - } - - $this->ID += 1; - - try{ - $msg = $header.$packet; - $result = fwrite($this->sock, $msg); - } catch (Exception $e) { - logging($e); - } - } - function Receive($timeout = 5) - { - # - # We must expect there is no output from remote device - # Some debug cmd do not return any output, some will return after timeout/failure, most will return directly - # - $data = ""; - $P2P_header = ""; - $P2P_data = ""; - $P2P_return_data = []; - $header_LEN = 0; - - try{ - $len = strlen($data); - - $read = array($this->sock); - $write = null; - $except = null; - $ready = stream_select($read, $write, $except, $timeout); - if ($ready > 0) { - $data .= stream_socket_recvfrom($this->sock, 8192); - } - } catch (Exception $e) { - return ""; - } - - if (strlen($data)==0){ - #logging("Nothing received anything from remote"); - return ""; - } - - $LEN_RECVED = 1; - $LEN_EXPECT = 1; - while (strlen($data)>0){ - if (substr($data,0,8) == pack("N",0x20000000).pack("N",0x44484950)){ # DHIP - $P2P_header = substr($data,0,32); - $LEN_RECVED = unpack("V",substr($data,16,4))[1]; - $LEN_EXPECT = unpack("V",substr($data,24,4))[1]; - $data = substr($data,32); - } - else{ - if($LEN_RECVED > 1){ - $P2P_data = substr($data,0,$LEN_RECVED); - $P2P_return_data[] = $P2P_data; - } - $data = substr($data,$LEN_RECVED); - if ($LEN_RECVED == $LEN_EXPECT && strlen($data)==0){ - break; - } - } - } - return $P2P_return_data; - } - function Login() - { - logging("Start login"); - - $query_args = array( - 'id'=>10000, - 'magic'=>"0x1234", - 'method'=>"global.login", - 'params'=>array( - 'clientType'=>$this->clientType, - 'ipAddr'=>$this->FakeIPaddr, - 'loginType'=>"Direct", - 'password'=>"", - 'userName'=>$this->username, - ), - 'session'=>0 - ); - - $this->Send(json_encode($query_args)); - $data = $this->Receive(); - if (empty($data)){ - logging("global.login [random]"); - return false; - } - $data = json_decode($data[0], true); - - $this->SessionID = $data['session']; - $RANDOM = $data['params']['random']; - $REALM = $data['params']['realm']; - - $RANDOM_HASH = $this->Gen_md5_hash($RANDOM, $REALM, $this->username, $this->password); - - $query_args = array( - 'id'=>10000, - 'magic'=>"0x1234", - 'method'=>"global.login", - 'session'=>$this->SessionID, - 'params'=>array( - 'userName'=>$this->username, - 'password'=>$RANDOM_HASH, - 'clientType'=>$this->clientType, - 'ipAddr'=>$this->FakeIPaddr, - 'loginType'=>"Direct", - 'authorityType'=>"Default", - ) - ); - $this->Send(json_encode($query_args)); - $data = $this->Receive(); - if (empty($data)){ - return false; - } - $data = json_decode($data[0], true); - if (array_key_exists('result', $data) && $data['result']){ - logging("Login success"); - return true; - } - logging("Login failed: ".$data['error']['code']." ".$data['error']['message']); - return false; - } - function Main() - { - $this->sock = @fsockopen($this->host, 5000, $errno, $errstr, 5); - if($errno){ - logging("Socket open failed"); - return; - } - if (!$this->Login()){ - return; - } - - $query_args = array( - 'id'=>$this->ID, - 'magic'=>"0x1234", - 'method'=>"console.runCmd", - 'params'=>array( - 'command'=>"hc" //oder "hc" - ), - 'session'=>$this->SessionID); - - $this->Send(json_encode($query_args)); - $this->Receive(); - } -} -?> diff --git a/dahuavto2mqtt/DahuaEventHandler.php b/dahuavto2mqtt/DahuaEventHandler.php deleted file mode 100644 index 182b737..0000000 --- a/dahuavto2mqtt/DahuaEventHandler.php +++ /dev/null @@ -1,490 +0,0 @@ -"); - -$Dahua = new DahuaVTOEventListener(); -$status = $Dahua->Main(); - -logging("All done"); - -function logging($text){ - echo $text."\n"; -} - -class DahuaVTOEventListener { - private $sock, $host, $port, $credentials; - private $ID = 0; # Our Request / Responce ID that must be in all requests and initated by us - private $SessionID = 0; # Session ID will be returned after successful login - private $SID = 0; # SID will be returned after we called .attach with 'Object ID' - private $FakeIPaddr = '(null)'; # WebGUI: mask our real IP - private $clientType = ''; # WebGUI: We do not show up in logs or online users - private $keepAliveInterval = 60; - private $lastKeepAlive = 0; - - function DahuaVTOEventListener() { - $this->host = getenv("DAHUA_VTO_HOST"); - $this->username = getenv("DAHUA_VTO_USERNAME"); - $this->password = getenv("DAHUA_VTO_PASSWORD"); - } - - function Gen_md5_hash($Dahua_random, $Dahua_realm, $username, $password) { - $PWDDB_HASH = strtoupper(md5($username.':'.$Dahua_realm.':'.$password)); - $PASS = $username.':'.$Dahua_random.':'.$PWDDB_HASH; - $RANDOM_HASH = strtoupper(md5($PASS)); - return $RANDOM_HASH; - } - - function publish($name, $payload){ - $server = getenv("MQTT_BROKER_HOST"); - $port = getenv("MQTT_BROKER_PORT"); - $username = getenv("MQTT_BROKER_USERNAME"); - $password = getenv("MQTT_BROKER_PASSWORD"); - $client_id = "dahua-vto"; - - $mqtt_message = json_encode($payload); - $topic = "DahuaVTO/".$name."/Event"; - $log_message = "Topic: ".$topic.", Payload: ".$mqtt_message; - - try { - $mqtt = new phpMQTT($server, $port, $client_id); - - if ($mqtt->connect(true, NULL, $username, $password)) { - $mqtt->publish($topic, $mqtt_message, 0); - $mqtt->close(); - - logging("MQTT message published, ".$log_message); - - } else { - logging("Failed to publish MQTT message due to timeout, ".$log_message); - } - } - catch (Exception $e) { - logging("Failed to publish MQTT message due to error: ".$e.", ".$log_message); - } - } - - function KeepAlive($delay){ - global $debug; - - logging("Started keepAlive thread"); - - while(true){ - $query_args = array( - 'method'=>"global.keepAlive", - 'magic'=>"0x1234", - 'params'=>array( - 'timeout'=>$delay, - 'active'=>true - ), - 'id'=>$this->ID, - 'session'=>$this->SessionID); - - $this->Send(json_encode($query_args)); - $lastKeepAlive = time(); - $keepAliveReceived = false; - - while($lastKeepAlive + $delay > time()){ - $data = $this->Receive(); - - if (!empty($data)){ - foreach($data as $packet) { - $packet = json_decode($packet, true); - - if (array_key_exists('result', $packet)){ - if($debug) { - logging("keepAlive back"); - } - - $keepAliveReceived = true; - } - elseif ($packet['method'] == 'client.notifyEventStream'){ - $status = $this->EventHandler($packet); - } - } - } - } - if (!$keepAliveReceived) { - logging("keepAlive failed"); - return false; - } - } - } - function Send($packet) { - if (empty($packet)){ - $packet = ''; - } - - $header = pack("N",0x20000000); - $header .= pack("N",0x44484950); - $header .= pack("V",$this->SessionID); - $header .= pack("V",$this->ID); - $header .= pack("V",strlen($packet)); - $header .= pack("V",0); - $header .= pack("V",strlen($packet)); - $header .= pack("V",0); - - if (strlen($header) != 32){ - logging("Binary header != 32 ({})"); - return; - } - - $this->ID += 1; - - try { - $msg = $header.$packet; - $result = fwrite($this->sock, $msg); - } - catch (Exception $e) { - logging($e); - } - } - - function Receive($timeout = 5) { - # - # We must expect there is no output from remote device - # Some debug cmd do not return any output, some will return after timeout/failure, most will return directly - # - $data = ""; - $P2P_header = ""; - $P2P_data = ""; - $P2P_return_data = []; - $header_LEN = 0; - - try { - $len = strlen($data); - - $read = array($this->sock); - $write = null; - $except = null; - $ready = stream_select($read, $write, $except, $timeout); - - if ($ready > 0) { - $data .= stream_socket_recvfrom($this->sock, 8192); - } - - } - catch (Exception $e) { - return ""; - } - - if (strlen($data)==0){ - #logging("Nothing received anything from remote"); - return ""; - } - - $LEN_RECVED = 1; - $LEN_EXPECT = 1; - - while (strlen($data)>0){ - if (substr($data,0,8) == pack("N",0x20000000).pack("N",0x44484950)){ # DHIP - $P2P_header = substr($data,0,32); - $LEN_RECVED = unpack("V",substr($data,16,4))[1]; - $LEN_EXPECT = unpack("V",substr($data,24,4))[1]; - - $data = substr($data,32); - } - else{ - if($LEN_RECVED > 1){ - $P2P_data = substr($data,0,$LEN_RECVED); - $P2P_return_data[] = $P2P_data; - } - - $data = substr($data,$LEN_RECVED); - - if ($LEN_RECVED == $LEN_EXPECT && strlen($data)==0){ - break; - } - } - } - return $P2P_return_data; - } - - function Login() - { - logging("Start login"); - - $query_args = array( - 'id'=>10000, - 'magic'=>"0x1234", - 'method'=>"global.login", - 'params'=>array( - 'clientType'=>$this->clientType, - 'ipAddr'=>$this->FakeIPaddr, - 'loginType'=>"Direct", - 'password'=>"", - 'userName'=>$this->username, - ), - 'session'=>0 - ); - - $this->Send(json_encode($query_args)); - $data = $this->Receive(); - - if (empty($data)){ - logging("global.login [random]"); - return false; - } - - $data = json_decode($data[0], true); - - $this->SessionID = $data['session']; - $RANDOM = $data['params']['random']; - $REALM = $data['params']['realm']; - - $RANDOM_HASH = $this->Gen_md5_hash($RANDOM, $REALM, $this->username, $this->password); - - $query_args = array( - 'id'=>10000, - 'magic'=>"0x1234", - 'method'=>"global.login", - 'session'=>$this->SessionID, - 'params'=>array( - 'userName'=>$this->username, - 'password'=>$RANDOM_HASH, - 'clientType'=>$this->clientType, - 'ipAddr'=>$this->FakeIPaddr, - 'loginType'=>"Direct", - 'authorityType'=>"Default", - ) - ); - - $this->Send(json_encode($query_args)); - $data = $this->Receive(); - - if (empty($data)){ - return false; - } - - $data = json_decode($data[0], true); - - if (array_key_exists('result', $data) && $data['result']){ - logging("Login success"); - $this->keepAliveInterval = $data['params']['keepAliveInterval']; - return true; - } - - logging("Login failed: ".$data['error']['code']." ".$data['error']['message']); - return false; - } - - function Main($reconnectTimeout=60) { - $error = false; - while (true){ - if($error){ - sleep($reconnectTimeout); - } - - $error = true; - $this->sock = @fsockopen($this->host, 5000, $errno, $errstr, 5); - - if($errno){ - logging("Socket open failed"); - continue; - } - - if (!$this->Login()){ - continue; - } - - #Listen to all events - $query_args = array( - 'id'=>$this->ID, - 'magic'=>"0x1234", - 'method'=>"eventManager.attach", - 'params'=>array( - 'codes'=>["All"] - ), - 'session'=>$this->SessionID - ); - - $this->Send(json_encode($query_args)); - $data = $this->Receive(); - - if (!count($data) || !array_key_exists('result', json_decode($data[0], true))){ - logging("Failure eventManager.attach"); - continue; - } - else{ - unset($data[0]); - - foreach($data as $packet) { - $packet = json_decode($packet, true); - if ($packet['method'] == 'client.notifyEventStream'){ - $status = $this->EventHandler($packet); - } - } - } - $this->KeepAlive($this->keepAliveInterval); - logging("Failure no keep alive received"); - } - } - - function EventHandler($data){ - $allEvents = $data['params']['eventList']; - - if(count($allEvents) > 1){ - logging("Event Manager subscription reply"); - } - else { - foreach ($allEvents as $item) { - $eventCode = $item['Code']; - $eventData = $item['Data']; - $eventAction = $item['Action']; - - $this->SingleEventHandler($eventCode, $eventAction, $eventData); - - $payload = array( - "Action" => $eventAction, - "Data" => $eventData - ); - - $this->publish($eventCode, $payload); - } - } - - return true; - } - - function SingleEventHandler($eventCode, $eventAction, $eventData) { - global $debug; - - if($eventCode == 'CallNoAnswered'){ - logging("Event Call from VTO"); - } - elseif($eventCode == 'IgnoreInvite'){ - logging("Event VTH answered call from VTO"); - } - elseif($eventCode == 'VideoMotion'){ - logging("Event VideoMotion"); - //$this->SaveSnapshot(); - } - elseif($eventCode == 'RtspSessionDisconnect'){ - if($eventAction == 'Start'){ - logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." disconnected"); - } - elseif($eventAction == 'Stop'){ - logging("Event Rtsp-Session from ".str_replace("::ffff:","",$eventData['Device'])." connected"); - } - } - elseif($eventCode == 'BackKeyLight'){ - logging("Event BackKeyLight with State ".$eventData['State']." "); - } - elseif($eventCode == 'TimeChange'){ - logging("Event TimeChange, BeforeModifyTime: ".$eventData['BeforeModifyTime'].", ModifiedTime: ".$eventData['ModifiedTime'].""); - } - elseif($eventCode == 'NTPAdjustTime'){ - if($eventData['result']) { - logging("Event NTPAdjustTime with ".$eventData['Address']." success"); - } - else { - logging("Event NTPAdjustTime failed"); - } - } - elseif($eventCode == 'KeepLightOn'){ - if($eventData['Status'] == 'On'){ - logging("Event KeepLightOn"); - } - elseif($eventData['Status'] == 'Off'){ - logging("Event KeepLightOff"); - } - } - elseif($eventCode == 'VideoBlind'){ - if($eventAction == 'Start'){ - logging("Event VideoBlind started"); - } - elseif($eventAction == 'Stop'){ - logging("Event VideoBlind stopped"); - } - } - elseif($eventCode == 'FingerPrintCheck'){ - if($eventData['FingerPrintID'] > -1){ - $finger=($eventData['FingerPrintID']); - /* - $users = array( #From VTO FingerprintManager/FingerprintID - "0" => "Papa", - "1" => "Mama", - "2" => "Kind1", - "3" => "Kind2"); - $name=$users[$finger]; - */ - logging("Event FingerPrintCheck success, Finger number ".$eventData['FingerPrintID'].", User ".$name.""); - } - else { - logging("Event FingerPrintCheck failed, unknown Finger"); - } - } - elseif($eventCode == 'SIPRegisterResult'){ - if($eventAction == 'Pulse'){ - if($eventData['Success']) { - logging("Event SIPRegisterResult, Success"); - } - else{ - logging("Event SIPRegisterResult, Failed)"); - } - } - } - elseif($eventCode == 'AccessControl'){ - #Method:4=Remote/WebIf/SIPext,6=FingerPrint; UserID: from VTO FingerprintManager/Room Number or SIPext; - logging("Event: AccessControl, Name ".$eventData['Name']." Method ".$eventData['Method'].", ReaderID ".$eventData['ReaderID'].", UserID ".$eventData['UserID']); - } - elseif($eventCode == 'CallSnap'){ - logging("Event: CallSnap, DeviceType ".$eventData['DeviceType']." RemoteID ".$eventData['RemoteID'].", RemoteIP ".$eventData['RemoteIP']." CallStatus ".$eventData['ChannelStates'][0]); - } - elseif($eventCode == 'Invite'){ - logging("Event: Invite, Action ".$eventList['Action'].", CallID ".$eventData['CallID']." Lock Number ".$eventData['LockNum']); - } - elseif($eventCode == 'AccessSnap'){ - logging("Event: AccessSnap, FTP upload to ".$eventData['FtpUrl']); - } - elseif($eventCode == 'RequestCallState'){ - logging("Event: RequestCallState, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Index ".$eventData['Index']); - } - elseif($eventCode == 'APConnect'){ - logging("Event: AlarmLocal"); - } - elseif($eventCode == 'BackLightOn'){ - logging("Event: BackLightOn"); - } - elseif($eventCode == 'BackLightOff'){ - logging("Event: BackLightOff"); - } - elseif($eventCode == 'AlarmLocal'){ - logging("Event: AlarmLocal"); - } - elseif($eventCode == 'APConnect'){ - logging("Event: APAccess, Action ".$eventList['Action'].", LocaleTime ".$eventData['LocaleTime']." Result ".$eventData['Result']." Timerconnect ".$eventData['Timerconnect']." Error ".$eventData['Error']); - } - elseif($eventCode == 'ProfileAlarmTransmit'){ - logging("Event: ProfileAlarmTransmit, Action ".$eventList['Action'].", AlarmType ".$eventData['AlarmType']." DevSrcType ".$eventData['DevSrcType'].", SenseMethod ".$eventData['SenseMethod']); - } - elseif($eventCode == 'ProfileAlarmTransmit'){ - logging("Event: ProfileAlarmTransmit, Action ".$eventList['Action'].", AlarmType ".$eventData['AlarmType']." DevSrcType ".$eventData['DevSrcType'].", SenseMethod ".$eventData['SenseMethod']); - } - else{ - logging("Unmapped event (".$eventCode)."), Please report"; - } - - return true; - } - - function SaveSnapshot($path="/tmp/") { - $filename = $path."/DoorBell_".date("Y-m-d_H-i-s").".jpg"; - $fp = fopen($filename, 'wb'); - $url = "http://".$this->host."/cgi-bin/snapshot.cgi"; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password); - curl_setopt($ch, CURLOPT_FILE, $fp); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_HTTPGET, 1); - curl_exec($ch); - curl_close($ch); - fclose($fp); - copy($filename, $path."/Doorbell.jpg"); - } -} -?> \ No newline at end of file diff --git a/dahuavto2mqtt/Dockerfile b/dahuavto2mqtt/Dockerfile index 587abdd..51c2664 100644 --- a/dahuavto2mqtt/Dockerfile +++ b/dahuavto2mqtt/Dockerfile @@ -4,16 +4,17 @@ FROM ${BUILD_FROM} ENV LANG C.UTF-8 -WORKDIR /app - -COPY DahuaEventHandler.php ./DahuaVTO.php -COPY phpMQTT.php ./phpMQTT.php -COPY run.sh . +ARG DAHUA_BRANCH="master" RUN apk add --no-cache --update argon2-libs php php-json && \ apk add --no-cache --virtual .build-dependencies git && \ +git clone https://github.com/elad-bar/DahuaVTO2MQTT.git --branch ${DAHUA_BRANCH} --depth 1 /app && \ +mv /app/DahuaEventHandler.php /app/DahuaVTO.php && \ chmod +x /app/DahuaVTO.php && \ -chmod +x /app/run.sh && \ apk del .build-dependencies +WORKDIR /app + +COPY run.sh . + CMD ./run.sh diff --git a/dahuavto2mqtt/Events.md b/dahuavto2mqtt/Events.md deleted file mode 100644 index ebd8855..0000000 --- a/dahuavto2mqtt/Events.md +++ /dev/null @@ -1,236 +0,0 @@ -# DahuaVTO2MQTT MQTT Events - -#### Topic -will be always DahuaVTO/[EVENT NAME]/Event -Message represent a single event - -#### Events (With dedicated additional data) -##### CallNoAnswered: Call from VTO -``` -{ - "Action": "Start", - "Data": { - "CallID": "1", - "IsEncryptedStream": false, - "LocaleTime": "2020-03-02 20:11:13", - "LockNum": 2, - "SupportPaas": false, - "TCPPort": 37777, - "UTC": 1583172673 - } -} -``` - -##### IgnoreInvite: VTH answered call from VTO - - -##### VideoMotion: Video motion detected -``` -{ - "Action": "Start", - "Data": { - "LocaleTime": "2020-03-02 20:44:28", - "UTC": 1583174668 - } -} -``` - -##### RtspSessionDisconnect: Rtsp-Session connection connection state changed -Action: Represented whether event Start or Stop -Data.Device: IP of the device connected / disconnected - -##### BackKeyLight: BackKeyLight with State -``` -{ - "Action": "Pulse", - "Data": { - "LocaleTime": "2020-03-02 20:24:07", - "State": 8, - "UTC": 1583173447 - } -} -``` - -##### TimeChange: Time changed -``` -{ - "Action": "Pulse", - "Data": { - "BeforeModifyTime": "02-03-2020 21:41:40", - "LocaleTime": "2020-03-02 21:41:40", - "ModifiedTime": "02-03-2020 21:41:39", - "UTC": 1583178100 - } -} -``` - -##### NTPAdjustTime: NTP Adjusted time -``` -{ - "Action": "Pulse", - "Data": { - "Address": "time.windows.com", - "Before": "02-03-2020 21:41:38", - "LocaleTime": "2020-03-02 21:41:40", - "UTC": 1583178100, - "result": true - } -} -``` - -##### KeepLightOn: Keep light state changed -Data.Status: Repesents whether the state changed to On or Off - -##### VideoBlind: Video got blind state changed -Action: Represents whether event Start or Stop - -##### FingerPrintCheck: Finger print check status -Data.FingerPrintID: Finger print ID, if 0, check failed - -##### SIPRegisterResult: SIP Device registration status -``` -{ - "Action": "Pulse", - "Data": { - "Date": "02-03-2020 21:42:59", - "LocaleTime": "2020-03-02 21:42:59", - "Success": true, - "UTC": 1583178179 - } -} -``` - -##### AccessControl: Someone opened the door -``` -{ - "Action": "Pulse", - "Data": { - "CardNo": "", - "CardType": null, - "LocaleTime": "2020-03-02 20:24:08", - "Method": 4, // 4=Remote/WebIf/SIPext | 6=FingerPrint - "Name": "OpenDoor", // Access control action name - "Password": "", - "ReaderID": "1", - "RecNo": 691, - "SnapURL": "", - "Status": 1, - "Type": "Entry", - "UTC": 1583173448, - "UserID": "" // By FingerprintManager / Room Number / SIPext - } -} -``` - - -##### CallSnap: Call -Data.DeviceType: Which device type -Data.RemoteID: UserID -Data.RemoteIP: IP of VTH / SIP device -Data.ChannelStates: Status - -##### Invite: Invite for a call (calling) -``` -{ - "Action": "Pulse", - "Data": { - "CallID": "1", - "IsEncryptedStream": false, - "LocaleTime": "2020-03-02 20:11:13", - "LockNum": 2, - "SupportPaas": false, - "TCPPort": 37777, - "UTC": 1583172673 - } -} -``` - -##### AccessSnap: ? -Data.FtpUrl: FTP uploaded to - -##### RequestCallState: ? -Action: ? -Data.LocaleTime: Date and time of the event -Data.Index: Index of the call - -##### PassiveHungup: Call was dropped -Action -Data.LocaleTime: Date and time of the event -Data.Index: Index of the call - -##### ProfileAlarmTransmit: Alarm triggered -Action: ? -Data.AlarmType: Alarm type -Data.DevSrcType: Device triggered the alarm -Data.SenseMethod: What triggered the alarm - -##### BackLightOn: Back light turned-on -``` -{ - "Action": "Pulse", - "Data": { - "LocaleTime": "2020-03-02 20:24:07", - "UTC": 1583173447 - } -} -``` - -##### BackLightOff: Back light turned-on -``` -{ - "Action": "Pulse", - "Data": { - "LocaleTime": "2020-03-02 20:23:39", - "UTC": 1583173419 - } -} -``` - -##### AlarmLocal: Alarm triggered by the VTO unit -``` -{ - "Action": "Stop", //Represents whether event for Start or Stop - "Data": { - "LocaleTime": "2020-03-02 20:11:16", - "UTC": 1583172676 - } -} -``` - -##### APConnect: AccessPoint got connected (Stop) or disconnected (Start) -``` -{ - "Action": "Stop", - "Data": { - "Error": "SSIDNotValid", - "LocaleTime": "2020-03-02 19:20:07", - "Result": false, - "Type": "Timerconnect", - "UTC": 1583158807 - } -} -``` - -#### Generic structure -``` -{ - "id": [MESSAGE ID], - "method":"client.notifyEventStream", - "params":{ - "SID":513, - "eventList":[ - { - "Action":"[EVENT ACTION]", - "Code":"[EVENT NAME]", - "Data":{ - "LocaleTime":"YYYY-MM-DD HH:mm:SS", - "UTC": [EPOCH TIMESTAMP] - }, - "Index": [EVENT ID IN MESSAGE], - "Param":[] - } - ] - }, - "session": [SESSION IDENTIFIER] -} -``` diff --git a/dahuavto2mqtt/README.md b/dahuavto2mqtt/README.md index 031f8da..52d5879 100644 --- a/dahuavto2mqtt/README.md +++ b/dahuavto2mqtt/README.md @@ -1,6 +1,6 @@ -# Hass.io Add-on: Dahua Intercom to MQTT Gateway +# Hass.io Add-on: Dahua VTO to MQTT Broker -Sends Dahua Intercome events to the MQTT Gateway +Sends Dahua Intercom events to the MQTT Broker ![Supports aarch64 Architecture][aarch64-shield] ![Supports amd64 Architecture][amd64-shield] ![Supports armhf Architecture][armhf-shield] ![Supports armv7 Architecture][armv7-shield] ![Supports i386 Architecture][i386-shield] @@ -15,11 +15,11 @@ The installation of this add-on is straightforward and easy to do. ## How to use -To use this add-on, you need to supply the config for your intercom and MQTT gateway +To use this add-on, you need to supply the config for your intercom and MQTT Broker - Requires you to use one of the supported Dahua Intercoms - Requires you to run a local MQTT server -- [MQTT Event's][mqtt-events] +- [DahuaVTO2MQTT's documentation][documentation] ## Configuration @@ -70,4 +70,4 @@ In case you've found a bug, please [open an issue on our GitHub][issue]. [source]: https://github.com/elad-bar/Dahua/tree/master [original-author]: https://github.com/riogrande75/Dahua [original-addon-author]: https://github.com/troykelly/ -[mqtt-events]: https://github.com/elad-bar/Hassio-addons/blob/master/dahuavto2mqtt/Events.md +[documentation]: https://github.com/elad-bar/DahuaVTO2MQTT diff --git a/dahuavto2mqtt/config.json b/dahuavto2mqtt/config.json index b1d351d..b2157f2 100644 --- a/dahuavto2mqtt/config.json +++ b/dahuavto2mqtt/config.json @@ -1,6 +1,6 @@ { "name": "DahuaVTO2MQTT", - "version": "1.0.1", + "version": "1.0.2", "slug": "dahuavto2mqtt", "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", "arch": [ diff --git a/dahuavto2mqtt/icon.png b/dahuavto2mqtt/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..52dd72ee6a3f6070c47763b5a0b405fa717cbe5e GIT binary patch literal 3850 zcmaJ^c{r5&-*)V~5Dg_`$X1q_A~OubjAbmPu}xG=Gh>NajFGXHEZIVq;wY)emPnF) z$u=QW3RxOUcIqG$@s9I5o!|S%+xuMC^L&@-6fY{M! z1a}w)W%f@j_6ZL1q|>P|EvfnAh)ff&NzSA1>kdxfuVJtHn-6Yj3#!YuLZ8*bds;{%c+K;a~F;$!y2d*w*$s z_pF(NLnsK1GPY-YoN^YTI<`p}Zk)b~U??jg0%NXKs$vjjm?l0s?hp>=^o|_Ioc)g- zT;eO*5-G5@TWyr7NIlxwF6LfwG}DngDgi@5KES23ACxb!Z|1$BG}DW+5X1EmBoj! zubozkefF?{xww#vs6@9pRO$A7^-=grXAM~uK@WH(k2iU;q+9C!n$&;-1@cw-!mHe6 zy18GtnuyKua}t?#$W9g2h_v{+YgS3J+2?!Pd|y?z3Z=>OSm&tNt3g5KIk)t@-NLzM zzv|ucFztV4O)EqVvnT=PQwry5WHyBv;*8F|-51vF2-nX76V}ehJkr7T2ML*LT z{3SaT+|<=1GJnr<7G_L#zF*sVYjJ1dMC@Xu;_|VS&a*rc zOK}T!zTTidjDg}DDaMOx@XRD|6f*EVH{zA&TDRD)yzt7wC!R*yjqfoa+X6qIgY+^9 zY=nQUx%$+;1k3}O+(Hz~QoI^@sf#Pdtwafjue1-5-^k}QieehF0!fecZ6qzynMUhw z38xJrM!Yj;Os12qYTlF;ge^&ooC%oUIaw%j9%rCPR!O`v;#y$GTf`Rbe%t z(PM5S)!Lo5BbVBzN|^}JeFBk3Tc5VNms-7w^~L0BlKz0nMB9$7ukRKa_j9@mtl94( z<`!5|5%&x4c(Ay7IekK}UTAubI10=h=(-xh(*6o90}SSo323B@sy}!3gA?+f_0(UC zh;>7{iAW*ztRcWa!K$a|P^#R-687ZVsMYydSfRz?GGWHR*8~tpFOQ5Il^-CPTrUJz zFkLK?9uJ1QXW9w5arB8)b^2eXWM7gyz~#yrj5dRes-zhUV}(#sqUg>Q)v;pX(9!6= zmJdk@YC(ShmW=Am{{+sQh;p~fp3=M2EwhsWvpnLz9mZ4%Ofn?T1*@kY*C&LeKJ{=W z*i?JoHC-2p@R4(aP^UT9p2-$?K-xoI16*1Y6B1UOD-p%ze&1M55{y=fqYh64pnbBNqGe5>{Gz}$Kzc+UzWm^mzwE$M2#8ZjtX?J*5UemSRsm28PCRN^RdsityGsn zlP(oiiU0&urTgwYNBAkXB~^F?*u8q>nDa~}BBkhQv66Ppd!}w%_FB$9)8gvWiY~#? z+|B)Ka@X{aL1e;r7owOA2e1I6@dxPq6|I>&(Fav)3g53F9g+|Q7O$KAr8Ie4B?7Tl z?jsJJSjmYLGCb80PLN53E`PC`G@8=TOGouM|JXk7ruo{WonoB1aBQPj)^QUNzc?mD zs8GN2GiC}j@YW;!dR?xITtnJ*^>g-Bo7>x#4|QD&j?a+~D3>eyTF;02#>b%grNyDk zVCDzqX5NWoYRy0ME>z7gJjq@i7H18Fd%rl_?ueVjii|xS9HkeGMYqqPn{5Wjk8i`y zoL-vj>n%r3CuVczkn?PAoo32qs*TcmLskrKrw<%(u92D?%((0CC zR+cG(QqPJh{S8>I;Gu3nnBFGymWYnxKgiC&c{h}Do0ezsaz-E4xJ(2^qzWm%2meO{`DD-6Y(;eJY6m7 z#=CJ^fRU-=ua5pH5GNik6o_}L6HtzrLU(p|0!EjKb6;X`Q8y}?Pu>yQT?i17pP`2^ zF+Vhp5SowWTz~&&%JnHzbM@#q_@9uS2X%N&PF@$ju9l8tPp_n#?obot2mD`|RGKS` zV&j^}?p0aqRjdcLls9{({3NluTOHLF_Fr~n#x{EmoCf zpLH=dp3ERPu*3AmhR(SqZA( zs~BwB^OP_C`7Vur4x>8-h|$<7ckZ1=c&?!xDVDPD?m$hdYNCSx38}n$NTn3;feZ6< z_Cp2-AFiO-_*SBoX=sPb_Zec%w1iC1exZwx%rC=y-$4dfM0iHv;eD(Z^~VN7)+H}b zQSpbYJ#VhpI#{fPUB7Vdp6Sw(LMVA{6$3hZQ(hkS!{xKX*41f8{e8SI2a!**Iz}h! z2m|`8EAnO+^;?`Bl#mii<&)q@0!-LEU8vXKl1int zyAcsL)iPOCusg%k)*OYNVc2M?gOMuud?)kB# zLM}MuF@B7ZI#A>>@r|oU^GB}2S^gtBr&3!x?=^epw@;^8u8$|LOt;p0-vlb0sPU~$PGBb!o+)8SkM^TXpwz-ncCD$f6?tjGxhPLztj)dzzuOY-4Mj5U; zh;3P5QZ^HzhUl6M$(GapFN}HW_ZL@QQGC2;l+%kwG<>#GNjIg0zYH*;1SZe%=P$%v z9pD=f&B?$0H9L0M*M;=rdQe6{>-%WMTHh;LWkRZTbfdftd5Th-yU?EDKGmh^A|`p ze959cGSsWOE%~j|I4m2~6Q?x!AwEv!48!cmMAT%E$xW1gi>u1`&>tE~ZzV2J*PJ(< zs`E3>b|1e~yHh$MccHECK}I{hyh0UU4=X;bJhtIhA|v{ds;{3qyjXhXrrO9u*{;F6 z%_T_#G7mQ(ll0jNZdEm2`XG9Ai-CgJEA7=7JYkyDo3$L>|+<4V8Lyya}@I1aCkQk>y}#b;&Pe2b(D^v zL8*ad=tSn3IZY39M@3p%>k3jMns+wR4i2migl OQ?%(R)Dsi8YyShu2FBk2 literal 0 HcmV?d00001 diff --git a/dahuavto2mqtt/logo.png b/dahuavto2mqtt/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..52dd72ee6a3f6070c47763b5a0b405fa717cbe5e GIT binary patch literal 3850 zcmaJ^c{r5&-*)V~5Dg_`$X1q_A~OubjAbmPu}xG=Gh>NajFGXHEZIVq;wY)emPnF) z$u=QW3RxOUcIqG$@s9I5o!|S%+xuMC^L&@-6fY{M! z1a}w)W%f@j_6ZL1q|>P|EvfnAh)ff&NzSA1>kdxfuVJtHn-6Yj3#!YuLZ8*bds;{%c+K;a~F;$!y2d*w*$s z_pF(NLnsK1GPY-YoN^YTI<`p}Zk)b~U??jg0%NXKs$vjjm?l0s?hp>=^o|_Ioc)g- zT;eO*5-G5@TWyr7NIlxwF6LfwG}DngDgi@5KES23ACxb!Z|1$BG}DW+5X1EmBoj! zubozkefF?{xww#vs6@9pRO$A7^-=grXAM~uK@WH(k2iU;q+9C!n$&;-1@cw-!mHe6 zy18GtnuyKua}t?#$W9g2h_v{+YgS3J+2?!Pd|y?z3Z=>OSm&tNt3g5KIk)t@-NLzM zzv|ucFztV4O)EqVvnT=PQwry5WHyBv;*8F|-51vF2-nX76V}ehJkr7T2ML*LT z{3SaT+|<=1GJnr<7G_L#zF*sVYjJ1dMC@Xu;_|VS&a*rc zOK}T!zTTidjDg}DDaMOx@XRD|6f*EVH{zA&TDRD)yzt7wC!R*yjqfoa+X6qIgY+^9 zY=nQUx%$+;1k3}O+(Hz~QoI^@sf#Pdtwafjue1-5-^k}QieehF0!fecZ6qzynMUhw z38xJrM!Yj;Os12qYTlF;ge^&ooC%oUIaw%j9%rCPR!O`v;#y$GTf`Rbe%t z(PM5S)!Lo5BbVBzN|^}JeFBk3Tc5VNms-7w^~L0BlKz0nMB9$7ukRKa_j9@mtl94( z<`!5|5%&x4c(Ay7IekK}UTAubI10=h=(-xh(*6o90}SSo323B@sy}!3gA?+f_0(UC zh;>7{iAW*ztRcWa!K$a|P^#R-687ZVsMYydSfRz?GGWHR*8~tpFOQ5Il^-CPTrUJz zFkLK?9uJ1QXW9w5arB8)b^2eXWM7gyz~#yrj5dRes-zhUV}(#sqUg>Q)v;pX(9!6= zmJdk@YC(ShmW=Am{{+sQh;p~fp3=M2EwhsWvpnLz9mZ4%Ofn?T1*@kY*C&LeKJ{=W z*i?JoHC-2p@R4(aP^UT9p2-$?K-xoI16*1Y6B1UOD-p%ze&1M55{y=fqYh64pnbBNqGe5>{Gz}$Kzc+UzWm^mzwE$M2#8ZjtX?J*5UemSRsm28PCRN^RdsityGsn zlP(oiiU0&urTgwYNBAkXB~^F?*u8q>nDa~}BBkhQv66Ppd!}w%_FB$9)8gvWiY~#? z+|B)Ka@X{aL1e;r7owOA2e1I6@dxPq6|I>&(Fav)3g53F9g+|Q7O$KAr8Ie4B?7Tl z?jsJJSjmYLGCb80PLN53E`PC`G@8=TOGouM|JXk7ruo{WonoB1aBQPj)^QUNzc?mD zs8GN2GiC}j@YW;!dR?xITtnJ*^>g-Bo7>x#4|QD&j?a+~D3>eyTF;02#>b%grNyDk zVCDzqX5NWoYRy0ME>z7gJjq@i7H18Fd%rl_?ueVjii|xS9HkeGMYqqPn{5Wjk8i`y zoL-vj>n%r3CuVczkn?PAoo32qs*TcmLskrKrw<%(u92D?%((0CC zR+cG(QqPJh{S8>I;Gu3nnBFGymWYnxKgiC&c{h}Do0ezsaz-E4xJ(2^qzWm%2meO{`DD-6Y(;eJY6m7 z#=CJ^fRU-=ua5pH5GNik6o_}L6HtzrLU(p|0!EjKb6;X`Q8y}?Pu>yQT?i17pP`2^ zF+Vhp5SowWTz~&&%JnHzbM@#q_@9uS2X%N&PF@$ju9l8tPp_n#?obot2mD`|RGKS` zV&j^}?p0aqRjdcLls9{({3NluTOHLF_Fr~n#x{EmoCf zpLH=dp3ERPu*3AmhR(SqZA( zs~BwB^OP_C`7Vur4x>8-h|$<7ckZ1=c&?!xDVDPD?m$hdYNCSx38}n$NTn3;feZ6< z_Cp2-AFiO-_*SBoX=sPb_Zec%w1iC1exZwx%rC=y-$4dfM0iHv;eD(Z^~VN7)+H}b zQSpbYJ#VhpI#{fPUB7Vdp6Sw(LMVA{6$3hZQ(hkS!{xKX*41f8{e8SI2a!**Iz}h! z2m|`8EAnO+^;?`Bl#mii<&)q@0!-LEU8vXKl1int zyAcsL)iPOCusg%k)*OYNVc2M?gOMuud?)kB# zLM}MuF@B7ZI#A>>@r|oU^GB}2S^gtBr&3!x?=^epw@;^8u8$|LOt;p0-vlb0sPU~$PGBb!o+)8SkM^TXpwz-ncCD$f6?tjGxhPLztj)dzzuOY-4Mj5U; zh;3P5QZ^HzhUl6M$(GapFN}HW_ZL@QQGC2;l+%kwG<>#GNjIg0zYH*;1SZe%=P$%v z9pD=f&B?$0H9L0M*M;=rdQe6{>-%WMTHh;LWkRZTbfdftd5Th-yU?EDKGmh^A|`p ze959cGSsWOE%~j|I4m2~6Q?x!AwEv!48!cmMAT%E$xW1gi>u1`&>tE~ZzV2J*PJ(< zs`E3>b|1e~yHh$MccHECK}I{hyh0UU4=X;bJhtIhA|v{ds;{3qyjXhXrrO9u*{;F6 z%_T_#G7mQ(ll0jNZdEm2`XG9Ai-CgJEA7=7JYkyDo3$L>|+<4V8Lyya}@I1aCkQk>y}#b;&Pe2b(D^v zL8*ad=tSn3IZY39M@3p%>k3jMns+wR4i2migl OQ?%(R)Dsi8YyShu2FBk2 literal 0 HcmV?d00001 diff --git a/dahuavto2mqtt/phpMQTT.php b/dahuavto2mqtt/phpMQTT.php deleted file mode 100644 index 7adedd0..0000000 --- a/dahuavto2mqtt/phpMQTT.php +++ /dev/null @@ -1,421 +0,0 @@ -broker($address, $port, $clientid, $cafile); - } - - /* sets the broker details */ - function broker($address, $port, $clientid, $cafile = NULL){ - $this->address = $address; - $this->port = $port; - $this->clientid = $clientid; - $this->cafile = $cafile; - } - - function connect_auto($clean = true, $will = NULL, $username = NULL, $password = NULL){ - while($this->connect($clean, $will, $username, $password)==false){ - sleep(10); - } - return true; - } - - /* connects to the broker - inputs: $clean: should the client send a clean session flag */ - function connect($clean = true, $will = NULL, $username = NULL, $password = NULL){ - - if($will) $this->will = $will; - if($username) $this->username = $username; - if($password) $this->password = $password; - - - if ($this->cafile) { - $socketContext = stream_context_create(["ssl" => [ - "verify_peer_name" => true, - "cafile" => $this->cafile - ]]); - $this->socket = stream_socket_client("tls://" . $this->address . ":" . $this->port, $errno, $errstr, 60, STREAM_CLIENT_CONNECT, $socketContext); - } else { - $this->socket = stream_socket_client("tcp://" . $this->address . ":" . $this->port, $errno, $errstr, 60, STREAM_CLIENT_CONNECT); - } - - if (!$this->socket ) { - if($this->debug) error_log("stream_socket_create() $errno, $errstr \n"); - return false; - } - - stream_set_timeout($this->socket, 5); - stream_set_blocking($this->socket, 0); - - $i = 0; - $buffer = ""; - - $buffer .= chr(0x00); $i++; - $buffer .= chr(0x06); $i++; - $buffer .= chr(0x4d); $i++; - $buffer .= chr(0x51); $i++; - $buffer .= chr(0x49); $i++; - $buffer .= chr(0x73); $i++; - $buffer .= chr(0x64); $i++; - $buffer .= chr(0x70); $i++; - $buffer .= chr(0x03); $i++; - - //No Will - $var = 0; - if($clean) $var+=2; - - //Add will info to header - if($this->will != NULL){ - $var += 4; // Set will flag - $var += ($this->will['qos'] << 3); //Set will qos - if($this->will['retain']) $var += 32; //Set will retain - } - - if($this->username != NULL) $var += 128; //Add username to header - if($this->password != NULL) $var += 64; //Add password to header - - $buffer .= chr($var); $i++; - - //Keep alive - $buffer .= chr($this->keepalive >> 8); $i++; - $buffer .= chr($this->keepalive & 0xff); $i++; - - $buffer .= $this->strwritestring($this->clientid,$i); - - //Adding will to payload - if($this->will != NULL){ - $buffer .= $this->strwritestring($this->will['topic'],$i); - $buffer .= $this->strwritestring($this->will['content'],$i); - } - - if($this->username) $buffer .= $this->strwritestring($this->username,$i); - if($this->password) $buffer .= $this->strwritestring($this->password,$i); - - $head = " "; - $head{0} = chr(0x10); - $head{1} = chr($i); - - fwrite($this->socket, $head, 2); - fwrite($this->socket, $buffer); - - $string = $this->read(4); - - if(ord($string{0})>>4 == 2 && $string{3} == chr(0)){ - if($this->debug) echo "Connected to Broker\n"; - }else{ - error_log(sprintf("Connection failed! (Error: 0x%02x 0x%02x)\n", - ord($string{0}),ord($string{3}))); - return false; - } - - $this->timesinceping = time(); - - return true; - } - - /* read: reads in so many bytes */ - function read($int = 8192, $nb = false){ - - // print_r(socket_get_status($this->socket)); - - $string=""; - $togo = $int; - - if($nb){ - return fread($this->socket, $togo); - } - - while (!feof($this->socket) && $togo>0) { - $fread = fread($this->socket, $togo); - $string .= $fread; - $togo = $int - strlen($string); - } - - - - - return $string; - } - - /* subscribe: subscribes to topics */ - function subscribe($topics, $qos = 0){ - $i = 0; - $buffer = ""; - $id = $this->msgid; - $buffer .= chr($id >> 8); $i++; - $buffer .= chr($id % 256); $i++; - - foreach($topics as $key => $topic){ - $buffer .= $this->strwritestring($key,$i); - $buffer .= chr($topic["qos"]); $i++; - $this->topics[$key] = $topic; - } - - $cmd = 0x80; - //$qos - $cmd += ($qos << 1); - - - $head = chr($cmd); - $head .= chr($i); - - fwrite($this->socket, $head, 2); - fwrite($this->socket, $buffer, $i); - $string = $this->read(2); - - $bytes = ord(substr($string,1,1)); - $string = $this->read($bytes); - } - - /* ping: sends a keep alive ping */ - function ping(){ - $head = " "; - $head = chr(0xc0); - $head .= chr(0x00); - fwrite($this->socket, $head, 2); - if($this->debug) echo "ping sent\n"; - } - - /* disconnect: sends a proper disconect cmd */ - function disconnect(){ - $head = " "; - $head{0} = chr(0xe0); - $head{1} = chr(0x00); - fwrite($this->socket, $head, 2); - } - - /* close: sends a proper disconect, then closes the socket */ - function close(){ - $this->disconnect(); - stream_socket_shutdown($this->socket, STREAM_SHUT_WR); - } - - /* publish: publishes $content on a $topic */ - function publish($topic, $content, $qos = 0, $retain = 0){ - - $i = 0; - $buffer = ""; - - $buffer .= $this->strwritestring($topic,$i); - - //$buffer .= $this->strwritestring($content,$i); - - if($qos){ - $id = $this->msgid++; - $buffer .= chr($id >> 8); $i++; - $buffer .= chr($id % 256); $i++; - } - - $buffer .= $content; - $i+=strlen($content); - - - $head = " "; - $cmd = 0x30; - if($qos) $cmd += $qos << 1; - if($retain) $cmd += 1; - - $head{0} = chr($cmd); - $head .= $this->setmsglength($i); - - fwrite($this->socket, $head, strlen($head)); - fwrite($this->socket, $buffer, $i); - - } - - /* message: processes a recieved topic */ - function message($msg){ - $tlen = (ord($msg{0})<<8) + ord($msg{1}); - $topic = substr($msg,2,$tlen); - $msg = substr($msg,($tlen+2)); - $found = 0; - foreach($this->topics as $key=>$top){ - if( preg_match("/^".str_replace("#",".*", - str_replace("+","[^\/]*", - str_replace("/","\/", - str_replace("$",'\$', - $key))))."$/",$topic) ){ - if(is_callable($top['function'])){ - call_user_func($top['function'],$topic,$msg); - $found = 1; - } - } - } - - if($this->debug && !$found) echo "msg recieved but no match in subscriptions\n"; - } - - /* proc: the processing loop for an "allways on" client - set true when you are doing other stuff in the loop good for watching something else at the same time */ - function proc( $loop = true){ - - if(1){ - $sockets = array($this->socket); - $w = $e = NULL; - $cmd = 0; - - //$byte = fgetc($this->socket); - if(feof($this->socket)){ - if($this->debug) echo "eof receive going to reconnect for good measure\n"; - fclose($this->socket); - $this->connect_auto(false); - if(count($this->topics)) - $this->subscribe($this->topics); - } - - $byte = $this->read(1, true); - - if(!strlen($byte)){ - if($loop){ - usleep(100000); - } - - }else{ - - $cmd = (int)(ord($byte)/16); - if($this->debug) echo "Recevid: $cmd\n"; - - $multiplier = 1; - $value = 0; - do{ - $digit = ord($this->read(1)); - $value += ($digit & 127) * $multiplier; - $multiplier *= 128; - }while (($digit & 128) != 0); - - if($this->debug) echo "Fetching: $value\n"; - - if($value) - $string = $this->read($value); - - if($cmd){ - switch($cmd){ - case 3: - $this->message($string); - break; - } - - $this->timesinceping = time(); - } - } - - if($this->timesinceping < (time() - $this->keepalive )){ - if($this->debug) echo "not found something so ping\n"; - $this->ping(); - } - - - if($this->timesinceping<(time()-($this->keepalive*2))){ - if($this->debug) echo "not seen a package in a while, disconnecting\n"; - fclose($this->socket); - $this->connect_auto(false); - if(count($this->topics)) - $this->subscribe($this->topics); - } - - } - return 1; - } - - /* getmsglength: */ - function getmsglength(&$msg, &$i){ - - $multiplier = 1; - $value = 0 ; - do{ - $digit = ord($msg{$i}); - $value += ($digit & 127) * $multiplier; - $multiplier *= 128; - $i++; - }while (($digit & 128) != 0); - - return $value; - } - - - /* setmsglength: */ - function setmsglength($len){ - $string = ""; - do{ - $digit = $len % 128; - $len = $len >> 7; - // if there are more digits to encode, set the top bit of this digit - if ( $len > 0 ) - $digit = ($digit | 0x80); - $string .= chr($digit); - }while ( $len > 0 ); - return $string; - } - - /* strwritestring: writes a string to a buffer */ - function strwritestring($str, &$i){ - $ret = " "; - $len = strlen($str); - $msb = $len >> 8; - $lsb = $len % 256; - $ret = chr($msb); - $ret .= chr($lsb); - $ret .= $str; - $i += ($len+2); - return $ret; - } - - function printstr($string){ - $strlen = strlen($string); - for($j=0;$j<$strlen;$j++){ - $num = ord($string{$j}); - if($num > 31) - $chr = $string{$j}; else $chr = " "; - printf("%4d: %08b : 0x%02x : %s \n",$j,$num,$num,$chr); - } - } -} From fc6015d4ba7d1760b36c9ccdb52110721db82448 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 17:35:48 +0300 Subject: [PATCH 28/33] Fix addon docker --- dahuavto2mqtt/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dahuavto2mqtt/Dockerfile b/dahuavto2mqtt/Dockerfile index 51c2664..fc218e0 100644 --- a/dahuavto2mqtt/Dockerfile +++ b/dahuavto2mqtt/Dockerfile @@ -15,6 +15,6 @@ apk del .build-dependencies WORKDIR /app -COPY run.sh . +COPY run.sh /app/run.sh -CMD ./run.sh +CMD /app/run.sh From d5ac9fedf9967af0416569a9781a2ca0c8d957bc Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 17:41:00 +0300 Subject: [PATCH 29/33] Another try to fix the docker --- dahuavto2mqtt/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/dahuavto2mqtt/Dockerfile b/dahuavto2mqtt/Dockerfile index fc218e0..3c87d32 100644 --- a/dahuavto2mqtt/Dockerfile +++ b/dahuavto2mqtt/Dockerfile @@ -16,5 +16,6 @@ apk del .build-dependencies WORKDIR /app COPY run.sh /app/run.sh +RUN chmod +x /app/run.sh CMD /app/run.sh From 2c0c1f51ca791ef43a98656250f8d90fc876fca1 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 17:42:35 +0300 Subject: [PATCH 30/33] Updated version --- dahuavto2mqtt/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dahuavto2mqtt/config.json b/dahuavto2mqtt/config.json index b2157f2..2861c46 100644 --- a/dahuavto2mqtt/config.json +++ b/dahuavto2mqtt/config.json @@ -1,6 +1,6 @@ { "name": "DahuaVTO2MQTT", - "version": "1.0.2", + "version": "1.0.3", "slug": "dahuavto2mqtt", "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", "arch": [ From 83f3f9e4e9b79623523abea61273c7436e99d12f Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 17:50:48 +0300 Subject: [PATCH 31/33] Another try to use DockerHub --- dahuavto2mqtt/config.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dahuavto2mqtt/config.json b/dahuavto2mqtt/config.json index 2861c46..a5d7572 100644 --- a/dahuavto2mqtt/config.json +++ b/dahuavto2mqtt/config.json @@ -37,5 +37,6 @@ "username": "str?", "password": "str?" } - } + }, + "image": "eladbar/{arch}-hassio-addon-dahuavto2mqtt" } \ No newline at end of file From 4e051343c793ec147cefe44a7d34ff24028d377c Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 17:56:42 +0300 Subject: [PATCH 32/33] Local build --- dahuavto2mqtt/config.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dahuavto2mqtt/config.json b/dahuavto2mqtt/config.json index a5d7572..7196d2b 100644 --- a/dahuavto2mqtt/config.json +++ b/dahuavto2mqtt/config.json @@ -1,6 +1,6 @@ { "name": "DahuaVTO2MQTT", - "version": "1.0.3", + "version": "1.0.4", "slug": "dahuavto2mqtt", "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", "arch": [ @@ -37,6 +37,5 @@ "username": "str?", "password": "str?" } - }, - "image": "eladbar/{arch}-hassio-addon-dahuavto2mqtt" - } \ No newline at end of file + } +} \ No newline at end of file From 84e1f71e7d00760d3fb936b4c5385dcfbce86bd6 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Fri, 27 Mar 2020 23:15:07 +0300 Subject: [PATCH 33/33] Added mqtt.topic_prefix parameter --- dahuavto2mqtt/README.md | 3 ++- dahuavto2mqtt/config.json | 8 +++++--- dahuavto2mqtt/run.sh | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dahuavto2mqtt/README.md b/dahuavto2mqtt/README.md index 52d5879..978a47b 100644 --- a/dahuavto2mqtt/README.md +++ b/dahuavto2mqtt/README.md @@ -37,7 +37,8 @@ Add-on configuration: "host": "core-mosquitto", "port": "1883", "username": "homeassistant", - "password": "homeassistant" + "password": "homeassistant", + "topic_prefix": "DahuaVTO" } } ``` diff --git a/dahuavto2mqtt/config.json b/dahuavto2mqtt/config.json index 7196d2b..bc6788f 100644 --- a/dahuavto2mqtt/config.json +++ b/dahuavto2mqtt/config.json @@ -1,6 +1,6 @@ { "name": "DahuaVTO2MQTT", - "version": "1.0.4", + "version": "1.0.5", "slug": "dahuavto2mqtt", "description": "Listens to events from Dahua VTO unit and publishes them via MQTT Message", "arch": [ @@ -22,7 +22,8 @@ "host": "core-mosquitto", "port": "1883", "username": "homeassistant", - "password": "homeassistant" + "password": "homeassistant", + "topic_prefix": "DahuaVTO" } }, "schema": { @@ -35,7 +36,8 @@ "host": "str?", "port": "int?", "username": "str?", - "password": "str?" + "password": "str?", + "topic_prefix": "str?" } } } \ No newline at end of file diff --git a/dahuavto2mqtt/run.sh b/dahuavto2mqtt/run.sh index ac67a7a..f8cc9d5 100644 --- a/dahuavto2mqtt/run.sh +++ b/dahuavto2mqtt/run.sh @@ -7,9 +7,10 @@ export MQTT_BROKER_HOST=$(bashio::config 'mqtt.host') export MQTT_BROKER_PORT=$(bashio::config 'mqtt.port') export MQTT_BROKER_USERNAME=$(bashio::config 'mqtt.username') export MQTT_BROKER_PASSWORD=$(bashio::config 'mqtt.password') +export MQTT_BROKER_TOPIC_PREFIX=$(bashio::config 'mqtt.topic_prefix') bashio::log.info "Staring Dahua to MQTT" bashio::log.debug "Connecting to Intercom ${DAHUA_VTO_HOST} with username ${DAHUA_VTO_USERNAME}" -bashio::log.debug "Connecting to Broker ${MQTT_BROKER_HOST} with username ${MQTT_BROKER_USERNAME}" +bashio::log.debug "Connecting to Broker ${MQTT_BROKER_HOST} with username ${MQTT_BROKER_USERNAME}, Topic prefix: ${MQTT_BROKER_TOPIC_PREFIX}" php -f ./DahuaVTO.php bashio::log.info "Finished Dahua to MQTT" \ No newline at end of file