1
+ #!/usr/bin/nodejs
2
+ // Dahua HTTP API Module
3
+ var events = require ( 'events' ) ;
4
+
5
+ const NetKeepAlive = require ( 'net-keepalive' )
6
+ const rp = require ( 'request-promise' ) ;
7
+ const request = require ( 'request' ) ;
8
+ const debug = require ( 'debug' ) ( 'http' )
9
+
10
+ class DahuaCam extends events . EventEmitter {
11
+ constructor ( options ) {
12
+ super ( ) ;
13
+
14
+ this . username = options . username ;
15
+ this . password = options . password ;
16
+
17
+ this . baseUri = `http://${ options . hostname } :${ options . port || 80 } ` ;
18
+ } ;
19
+
20
+ // set up persistent connection to receive alarm events from camera
21
+ listenForEvents ( eventNames ) {
22
+ eventNames = eventNames || [
23
+ 'VideoMotion' ,
24
+ 'VideoLoss' ,
25
+ 'VideoBlind' ,
26
+ 'AlarmLocal' ,
27
+ 'CrossLineDetection' ,
28
+ 'CrossRegionDetection' ,
29
+ 'LeftDetection' ,
30
+ 'TakenAwayDetection' ,
31
+ 'VideoAbnormalDetection' ,
32
+ 'FaceDetection' ,
33
+ 'AudioMutation' ,
34
+ 'AudioAnomaly' ,
35
+ 'VideoUnFocus' ,
36
+ 'WanderDetection' ,
37
+ 'RioterDetection' ] ;
38
+
39
+ let client = request ( {
40
+ url : `${ this . baseUri } /cgi-bin/eventManager.cgi?action=attach&codes=[${ eventNames . join ( "," ) } ]` ,
41
+ forever : true ,
42
+ headers : { 'Accept' :'multipart/x-mixed-replace' } ,
43
+ auth : {
44
+ user : this . username ,
45
+ password : this . password ,
46
+ sendImmediately : false
47
+ }
48
+ } ) ;
49
+
50
+ client . on ( 'socket' , ( socket ) => {
51
+ NetKeepAlive . setKeepAliveInterval ( socket , 1000 ) ;
52
+ NetKeepAlive . setKeepAliveProbes ( socket , 1 ) ;
53
+ } ) ;
54
+
55
+ client . on ( 'response' , ( ) => {
56
+ debug ( `Connected to ${ this . baseUri } ` ) ;
57
+ this . emit ( "connect" )
58
+ } ) ;
59
+ client . on ( 'error' , err => this . emit ( "error" , err ) ) ;
60
+ client . on ( 'data' , this . handleDahuaEventData . bind ( this ) ) ;
61
+ client . on ( 'close' , ( ) => { // Try to reconnect after 30s
62
+ ( ) => setTimeout ( ( ) => this . listenForEvents ( eventNames ) , 30000 ) ;
63
+ this . emit ( "end" ) ;
64
+ } ) ;
65
+ } ;
66
+
67
+ _req ( path ) {
68
+ return rp ( {
69
+ uri : `${ this . baseUri } /cgi-bin/${ path } ` ,
70
+ auth : {
71
+ user : this . username ,
72
+ password : this . password ,
73
+ sendImmediately : false
74
+ }
75
+ } ) ;
76
+ }
77
+
78
+ name ( ) {
79
+ return this . _req ( 'magicBox.cgi?action=getMachineName' ) . then ( d => d . split ( '=' ) [ 1 ] ) ;
80
+ }
81
+
82
+ snapshot ( channel ) {
83
+ return new Promise ( ( resolve , reject ) => {
84
+ let chunks = [ ] ;
85
+ request ( { 'uri' : this . baseUri + '/cgi-bin/snapshot.cgi?' + channel } )
86
+ . auth ( this . camUser , this . camPass , false )
87
+ . on ( 'data' , chunk => chunks . push ( chunk ) )
88
+ . on ( 'end' , ( ) => resolve ( Buffer . concat ( chunks ) ) )
89
+ . on ( 'error' , reject )
90
+ } )
91
+ }
92
+
93
+ handleDahuaEventData ( data ) {
94
+ data . toString ( )
95
+ . split ( '\r\n' )
96
+ . filter ( s => s . startsWith ( 'Code=' ) ) //Code=VideoMotion;action=Start;index=0
97
+ . map ( s => s . split ( ';' ) )
98
+ . forEach ( segments => {
99
+ let [ code , action , index ] = segments . map ( s => s . split ( '=' ) [ 1 ] ) ;
100
+ this . emit ( "alarm" , code , action , index ) ;
101
+ } )
102
+ }
103
+ }
104
+
105
+ class KeepAliveAgent extends require ( 'http' ) . Agent {
106
+ constructor ( ) {
107
+ super ( {
108
+ keepAlive : true
109
+ } )
110
+ }
111
+
112
+ createSocket ( req , options , cb ) {
113
+ super . createSocket ( req , options , ( err , socket ) => {
114
+
115
+ if ( ! err ) {
116
+ socket . on ( 'connect' , ( ) => {
117
+ NetKeepAlive . setKeepAliveInterval ( socket , 1000 ) ;
118
+ NetKeepAlive . setKeepAliveProbes ( socket , 1 ) ;
119
+ } ) ;
120
+ }
121
+
122
+ cb ( err , socket ) ;
123
+ } ) ;
124
+ }
125
+ }
126
+
127
+ exports . DahuaCam = DahuaCam ;
0 commit comments