camstreamerlib 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CameraVapix.js +340 -230
- package/package.json +1 -1
package/CameraVapix.js
CHANGED
|
@@ -5,259 +5,369 @@ const prettifyXml = require('prettify-xml')
|
|
|
5
5
|
const RtspClient = require('./RtspClient');
|
|
6
6
|
const httpRequest = require('./HTTPRequest');
|
|
7
7
|
|
|
8
|
+
const WebSocket = require('ws');
|
|
9
|
+
const Digest = require('./Digest');
|
|
8
10
|
|
|
9
|
-
class CameraVapix extends EventEmitter{
|
|
10
|
-
constructor (options) {
|
|
11
|
-
super();
|
|
12
|
-
this.protocol = 'http';
|
|
13
|
-
this.ip = '127.0.0.1';
|
|
14
|
-
this.port = 80;
|
|
15
|
-
this.auth = '';
|
|
16
|
-
if (options) {
|
|
17
|
-
this.protocol = options['protocol'] || this.protocol;
|
|
18
|
-
this.ip = options['ip'] || this.ip;
|
|
19
|
-
this.port = options['port'];
|
|
20
|
-
if (this.port == undefined) {
|
|
21
|
-
this.port = this.protocol == 'http' ? 80 : 443
|
|
22
|
-
}
|
|
23
|
-
this.auth = options['auth'] || this.auth;
|
|
24
|
-
}
|
|
25
11
|
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
class CameraVapix extends EventEmitter {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
super();
|
|
15
|
+
this.protocol = 'http';
|
|
16
|
+
this.ip = '127.0.0.1';
|
|
17
|
+
this.port = 80;
|
|
18
|
+
this.auth = '';
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (p.length >= 2) {
|
|
38
|
-
params[p[0]] = p[1];
|
|
39
|
-
}
|
|
20
|
+
if (options) {
|
|
21
|
+
this.protocol = options['protocol'] || this.protocol;
|
|
22
|
+
this.ip = options['ip'] || this.ip;
|
|
23
|
+
this.port = options['port'];
|
|
24
|
+
if (this.port == undefined) {
|
|
25
|
+
this.port = this.protocol == 'http' ? 80 : 443
|
|
26
|
+
}
|
|
27
|
+
this.auth = options['auth'] || this.auth;
|
|
40
28
|
}
|
|
41
|
-
}
|
|
42
|
-
resolve(params);
|
|
43
|
-
}, reject);
|
|
44
|
-
}.bind(this));
|
|
45
|
-
return promise;
|
|
46
|
-
}
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
postData += key + '=' + params[key] + '&';
|
|
52
|
-
});
|
|
53
|
-
postData = postData.slice(0, postData.length - 1);
|
|
54
|
-
return this.vapixPost('/axis-cgi/param.cgi', postData);
|
|
55
|
-
}
|
|
30
|
+
this.rtsp = null;
|
|
31
|
+
this.ws = null;
|
|
32
|
+
}
|
|
56
33
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
34
|
+
getParameterGroup(groupNames) {
|
|
35
|
+
let promise = new Promise((resolve, reject) => {
|
|
36
|
+
this.vapixGet('/axis-cgi/param.cgi?action=list&group=' + encodeURIComponent(groupNames)).then((response) => {
|
|
37
|
+
let params = {};
|
|
38
|
+
let lines = response.split(/[\r\n]/);
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
if (lines[i].length) {
|
|
41
|
+
let p = lines[i].split('=');
|
|
42
|
+
if (p.length >= 2) {
|
|
43
|
+
params[p[0]] = p[1];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
resolve(params);
|
|
48
|
+
}, reject);
|
|
49
|
+
});
|
|
50
|
+
return promise;
|
|
51
|
+
}
|
|
75
52
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
53
|
+
setParameter(params) {
|
|
54
|
+
let postData = 'action=update&';
|
|
55
|
+
Object.keys(params).forEach((key) => {
|
|
56
|
+
postData += key + '=' + params[key] + '&';
|
|
57
|
+
});
|
|
58
|
+
postData = postData.slice(0, postData.length - 1);
|
|
59
|
+
return this.vapixPost('/axis-cgi/param.cgi', postData);
|
|
60
|
+
}
|
|
79
61
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (tourBaseName + '.MoveSpeed' in response) {
|
|
99
|
-
let tour = {
|
|
100
|
-
'MoveSpeed': response[tourBaseName + '.MoveSpeed'],
|
|
101
|
-
'Position': response[tourBaseName + '.Position'],
|
|
102
|
-
'PresetNbr': response[tourBaseName + '.PresetNbr'],
|
|
103
|
-
'WaitTime': response[tourBaseName + '.WaitTime'],
|
|
104
|
-
'WaitTimeViewType': response[tourBaseName + '.WaitTimeViewType']
|
|
105
|
-
};
|
|
106
|
-
gTour.Tour.push(tour);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
gTourList.push(gTour);
|
|
110
|
-
} else {
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
resolve(gTourList);
|
|
115
|
-
}, reject)
|
|
116
|
-
}.bind(this));
|
|
117
|
-
return promise;
|
|
118
|
-
}
|
|
62
|
+
getPTZPresetList(channel) {
|
|
63
|
+
let promise = new Promise((resolve, reject) => {
|
|
64
|
+
this.vapixGet('/axis-cgi/com/ptz.cgi?query=presetposcam&camera=' + encodeURIComponent(channel)).then((response) => {
|
|
65
|
+
let positions = [];
|
|
66
|
+
let lines = response.split(/[\r\n]/);
|
|
67
|
+
for (let i = 0; i < lines.length; i++) {
|
|
68
|
+
if (lines[i].length && lines[i].indexOf('presetposno') != -1) {
|
|
69
|
+
let p = lines[i].split('=');
|
|
70
|
+
if (p.length >= 2) {
|
|
71
|
+
positions.push(p[1]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
resolve(positions);
|
|
76
|
+
}, reject);
|
|
77
|
+
});
|
|
78
|
+
return promise;
|
|
79
|
+
}
|
|
119
80
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return this.setParameter(options);
|
|
124
|
-
}
|
|
81
|
+
goToPreset(channel, presetName) {
|
|
82
|
+
return this.vapixPost('/axis-cgi/com/ptz.cgi', 'camera=' + encodeURIComponent(channel) + '&gotoserverpresetname=' + encodeURIComponent(presetName));
|
|
83
|
+
}
|
|
125
84
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
85
|
+
getGuardTourList() {
|
|
86
|
+
let promise = new Promise((resolve, reject) => {
|
|
87
|
+
let gTourList = [];
|
|
88
|
+
this.getParameterGroup('GuardTour').then((response) => {
|
|
89
|
+
for (let i = 0; i < 20; i++) {
|
|
90
|
+
let gTourBaseName = 'root.GuardTour.G' + i;
|
|
91
|
+
if (gTourBaseName + '.CamNbr' in response) {
|
|
92
|
+
let gTour = {
|
|
93
|
+
'ID': gTourBaseName,
|
|
94
|
+
'CamNbr': response[gTourBaseName + '.CamNbr'],
|
|
95
|
+
'Name': response[gTourBaseName + '.Name'],
|
|
96
|
+
'RandomEnabled': response[gTourBaseName + '.RandomEnabled'],
|
|
97
|
+
'Running': response[gTourBaseName + '.Running'],
|
|
98
|
+
'TimeBetweenSequences': response[gTourBaseName + '.TimeBetweenSequences'],
|
|
99
|
+
'Tour': []
|
|
100
|
+
};
|
|
101
|
+
for (let j = 0; j < 100; j++) {
|
|
102
|
+
let tourBaseName = 'root.GuardTour.G' + i + '.Tour.T' + j;
|
|
103
|
+
if (tourBaseName + '.MoveSpeed' in response) {
|
|
104
|
+
let tour = {
|
|
105
|
+
'MoveSpeed': response[tourBaseName + '.MoveSpeed'],
|
|
106
|
+
'Position': response[tourBaseName + '.Position'],
|
|
107
|
+
'PresetNbr': response[tourBaseName + '.PresetNbr'],
|
|
108
|
+
'WaitTime': response[tourBaseName + '.WaitTime'],
|
|
109
|
+
'WaitTimeViewType': response[tourBaseName + '.WaitTimeViewType']
|
|
110
|
+
};
|
|
111
|
+
gTour.Tour.push(tour);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
gTourList.push(gTour);
|
|
115
|
+
} else {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
resolve(gTourList);
|
|
120
|
+
}, reject)
|
|
121
|
+
});
|
|
122
|
+
return promise;
|
|
123
|
+
}
|
|
134
124
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
setGuardTourEnabled(gourTourID, enable) {
|
|
126
|
+
let options = {};
|
|
127
|
+
options[gourTourID + '.Running'] = enable ? 'yes' : 'no';
|
|
128
|
+
return this.setParameter(options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getInputState(port) {
|
|
132
|
+
let promise = new Promise((resolve, reject) => {
|
|
133
|
+
this.vapixPost('/axis-cgi/io/port.cgi', 'checkactive=' + encodeURIComponent(port)).then((response) => {
|
|
134
|
+
resolve(response.split('=')[1].indexOf('active') == 0);
|
|
135
|
+
}, reject);
|
|
136
|
+
});
|
|
137
|
+
return promise;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
setOutputState(port, active) {
|
|
141
|
+
return this.vapixPost('/axis-cgi/io/port.cgi', 'action=' + encodeURIComponent(port) + ':' + (active ? '/' : '\\'));
|
|
142
|
+
}
|
|
138
143
|
|
|
139
|
-
getApplicationList() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
getApplicationList() {
|
|
145
|
+
const promise = new Promise((resolve, reject) => {
|
|
146
|
+
this.vapixGet('/axis-cgi/applications/list.cgi').then((xml) => {
|
|
147
|
+
parseString(xml, (err, result) => {
|
|
148
|
+
if (err) {
|
|
149
|
+
reject(err);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
let apps = [];
|
|
153
|
+
for (let i = 0; i < result.reply.application.length; i++) {
|
|
154
|
+
apps.push(result.reply.application[i].$);
|
|
155
|
+
}
|
|
156
|
+
resolve(apps);
|
|
157
|
+
});
|
|
158
|
+
}, reject);
|
|
159
|
+
});
|
|
160
|
+
return promise;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
getEventDeclarations() {
|
|
164
|
+
const promise = new Promise((resolve, reject) => {
|
|
165
|
+
let data =
|
|
166
|
+
'<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
|
|
167
|
+
'<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
|
|
168
|
+
'xmlns:xsd="http://www.w3.org/2001/XMLSchema">' +
|
|
169
|
+
'<GetEventInstances xmlns="http://www.axis.com/vapix/ws/event1"/>' +
|
|
170
|
+
'</s:Body>' +
|
|
171
|
+
'</s:Envelope>';
|
|
172
|
+
this.vapixPost('/vapix/services', data, 'application/soap+xml').then((declarations) => {
|
|
173
|
+
resolve(prettifyXml(declarations));
|
|
174
|
+
}, reject);
|
|
175
|
+
});
|
|
176
|
+
return promise;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
isReservedEventName(eventName) {
|
|
180
|
+
return (eventName == 'eventsConnect' || eventName == 'eventsDisconnect');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
eventsConnect(channel = "RTSP") {
|
|
184
|
+
if (this.ws != null) {
|
|
185
|
+
throw new Error("Websocket is already opened.");
|
|
146
186
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
apps.push(result.reply.application[i].$);
|
|
187
|
+
if (this.rtsp != null) {
|
|
188
|
+
throw new Error("RTSP is already opened.");
|
|
150
189
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
'<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
|
|
162
|
-
'<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
|
|
163
|
-
'xmlns:xsd="http://www.w3.org/2001/XMLSchema">' +
|
|
164
|
-
'<GetEventInstances xmlns="http://www.axis.com/vapix/ws/event1"/>' +
|
|
165
|
-
'</s:Body>' +
|
|
166
|
-
'</s:Envelope>';
|
|
167
|
-
this.vapixPost('/vapix/services', data, 'application/soap+xml').then(function(declarations) {
|
|
168
|
-
resolve(prettifyXml(declarations));
|
|
169
|
-
}, reject);
|
|
170
|
-
});
|
|
171
|
-
return promise;
|
|
172
|
-
}
|
|
190
|
+
if (channel == "RTSP") {
|
|
191
|
+
this.rtspConnect();
|
|
192
|
+
}
|
|
193
|
+
else if (channel == "websocket") {
|
|
194
|
+
this.websocketConnect();
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw new Error("Unknown channel.");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
173
200
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
'port': this.port,
|
|
178
|
-
'auth': this.auth,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
this.rtsp.on('connect', function() { this.emit('eventsConnect'); }.bind(this));
|
|
182
|
-
this.rtsp.on('disconnect', function(err) { this.emit('eventsDisconnect', err); }.bind(this));
|
|
183
|
-
this.rtsp.on('event', (event) => {
|
|
184
|
-
let eventNames = this.eventNames();
|
|
185
|
-
for (let i = 0; i < eventNames.length; i++) {
|
|
186
|
-
if (eventNames[i] != 'eventsConnect' && eventNames[i] != 'eventsDisconnect') {
|
|
187
|
-
let name = eventNames[i];
|
|
188
|
-
// Remove special chars from the end
|
|
189
|
-
while (name[name.length - 1] == '.' || name[name.length - 1] == '/') {
|
|
190
|
-
name = name.substring(0, name.length - 1);
|
|
201
|
+
eventsDisconnect() {
|
|
202
|
+
if (this.rtsp != null) {
|
|
203
|
+
this.rtsp.disconnect();
|
|
191
204
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// Convert to JSON and emit signal
|
|
195
|
-
parseString(event, function (err, eventJson) {
|
|
196
|
-
if (err) {
|
|
197
|
-
this.eventsDisconnect();
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
this.emit(eventNames[i], eventJson);
|
|
201
|
-
}.bind(this));
|
|
202
|
-
break;
|
|
205
|
+
if (this.ws != null) {
|
|
206
|
+
this.ws.close();
|
|
203
207
|
}
|
|
204
|
-
}
|
|
205
208
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
209
|
+
|
|
210
|
+
rtspConnect() {
|
|
211
|
+
this.rtsp = new RtspClient({
|
|
212
|
+
'ip': this.ip,
|
|
213
|
+
'port': this.port,
|
|
214
|
+
'auth': this.auth,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
this.rtsp.on('connect', () => {
|
|
218
|
+
this.emit('eventsConnect');
|
|
219
|
+
});
|
|
220
|
+
this.rtsp.on('disconnect', (err) => {
|
|
221
|
+
this.emit('eventsDisconnect', err);
|
|
222
|
+
this.rtsp = null;
|
|
223
|
+
});
|
|
224
|
+
this.rtsp.on('event', (event) => {
|
|
225
|
+
let eventNames = this.eventNames();
|
|
226
|
+
for (let i = 0; i < eventNames.length; i++) {
|
|
227
|
+
if (!this.isReservedEventName(eventNames[i])) {
|
|
228
|
+
let name = eventNames[i];
|
|
229
|
+
// Remove special chars from the end
|
|
230
|
+
while (name[name.length - 1] == '.' || name[name.length - 1] == '/') {
|
|
231
|
+
name = name.substring(0, name.length - 1);
|
|
232
|
+
}
|
|
233
|
+
// Find registered event name in the message
|
|
234
|
+
if (event.indexOf(name) != -1) {
|
|
235
|
+
// Convert to JSON and emit signal
|
|
236
|
+
parseString(event, (err, eventJson) => {
|
|
237
|
+
if (err) {
|
|
238
|
+
this.eventsDisconnect();
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
this.emit(eventNames[i], eventJson);
|
|
242
|
+
});
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
let eventTopicFilter = '';
|
|
250
|
+
let eventNames = this.eventNames();
|
|
251
|
+
for (let i = 0; i < eventNames.length; i++) {
|
|
252
|
+
if (!this.isReservedEventName(eventNames[i])) {
|
|
253
|
+
if (eventTopicFilter.length != 0) {
|
|
254
|
+
eventTopicFilter += '|';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let topic = eventNames[i].replace(/tns1/g, 'onvif');
|
|
258
|
+
topic = topic.replace(/tnsaxis/g, 'axis');
|
|
259
|
+
eventTopicFilter += topic;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
this.rtsp.connect(eventTopicFilter);
|
|
219
263
|
}
|
|
220
|
-
}
|
|
221
|
-
this.rtsp.connect(eventTopicFilter);
|
|
222
|
-
}
|
|
223
264
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this.rtsp.disconnect();
|
|
227
|
-
this.rtsp = null;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
265
|
+
websocketConnect(digestHeader) {
|
|
266
|
+
const address = `ws://${this.ip}:${this.port}/vapix/ws-data-stream?sources=events`;
|
|
230
267
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
268
|
+
let options =
|
|
269
|
+
{
|
|
270
|
+
'auth': this.auth
|
|
271
|
+
};
|
|
236
272
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
273
|
+
if (digestHeader !== undefined) {
|
|
274
|
+
let userPass = this.auth.split(':');
|
|
275
|
+
options.headers = options.headers || {};
|
|
276
|
+
options['headers']['Authorization'] = Digest.getAuthHeader(userPass[0], userPass[1], 'GET', '/vapix/ws-data-stream?sources=events', digestHeader);
|
|
277
|
+
}
|
|
243
278
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
options['method'] = 'POST';
|
|
247
|
-
options['path'] = path;
|
|
248
|
-
if (contentType) {
|
|
249
|
-
options['headers'] = {'Content-Type': contentType};
|
|
250
|
-
}
|
|
251
|
-
return httpRequest(options, data);
|
|
252
|
-
}
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
this.ws = new WebSocket(address, options);
|
|
253
281
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
282
|
+
this.ws.on('open', () => {
|
|
283
|
+
let topics = [];
|
|
284
|
+
let eventNames = this.eventNames();
|
|
285
|
+
for (let i = 0; i < eventNames.length; i++) {
|
|
286
|
+
if (!this.isReservedEventName(eventNames[i])) {
|
|
287
|
+
let topic =
|
|
288
|
+
{
|
|
289
|
+
"topicFilter": eventNames[i]
|
|
290
|
+
}
|
|
291
|
+
topics.push(topic);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const topicFilter = {
|
|
296
|
+
"apiVersion": "1.0",
|
|
297
|
+
"method": "events:configure",
|
|
298
|
+
"params": {
|
|
299
|
+
"eventFilterList": topics
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
this.ws.send(JSON.stringify(topicFilter));
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
this.ws.on('unexpected-response', (req, res) => {
|
|
306
|
+
if (res.statusCode == 401 && res.headers['www-authenticate'] != undefined)
|
|
307
|
+
this.websocketConnect(res.headers['www-authenticate']).then(resolve, reject);
|
|
308
|
+
else {
|
|
309
|
+
reject('Error: status code: ' + res.statusCode + ', ' + res.data);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
this.ws.on('message', (data) => {
|
|
314
|
+
let dataJSON = JSON.parse(data);
|
|
315
|
+
if (dataJSON.method === 'events:configure') {
|
|
316
|
+
if (dataJSON.error === undefined) {
|
|
317
|
+
this.emit("eventsConnect");
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
this.emit("eventsDisconnect", dataJSON.error);
|
|
321
|
+
this.eventsDisconnect();
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
let eventName = dataJSON.params.notification.topic;
|
|
326
|
+
this.emit(eventName, dataJSON);
|
|
327
|
+
});
|
|
328
|
+
this.ws.on('error', (error) => {
|
|
329
|
+
this.emit("eventsDisconnect", error);
|
|
330
|
+
this.ws = null;
|
|
331
|
+
});
|
|
332
|
+
this.ws.on('close', () => {
|
|
333
|
+
if (this.ws !== null) {
|
|
334
|
+
this.emit("websocketDisconnect");
|
|
335
|
+
}
|
|
336
|
+
this.ws = null;
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
vapixGet(path, noWaitForData) {
|
|
342
|
+
let options = this.getBaseVapixConnectionParams();
|
|
343
|
+
options['path'] = encodeURI(path);
|
|
344
|
+
return httpRequest(options, undefined, noWaitForData);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async getCameraImage(camera, compression, resolution, outputStream) {
|
|
348
|
+
const path = `/axis-cgi/jpg/image.cgi?resolution=${resolution}&compression=${compression}&camera=${camera}`;
|
|
349
|
+
const res = await this.vapixGet(path, true);
|
|
350
|
+
res.pipe(outputStream);
|
|
351
|
+
return outputStream;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
vapixPost(path, data, contentType) {
|
|
355
|
+
let options = this.getBaseVapixConnectionParams();
|
|
356
|
+
options['method'] = 'POST';
|
|
357
|
+
options['path'] = path;
|
|
358
|
+
if (contentType) {
|
|
359
|
+
options['headers'] = { 'Content-Type': contentType };
|
|
360
|
+
}
|
|
361
|
+
return httpRequest(options, data);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
getBaseVapixConnectionParams(options, postData) {
|
|
365
|
+
return {
|
|
366
|
+
'protocol': this.protocol + ':',
|
|
367
|
+
'host': this.ip,
|
|
368
|
+
'port': this.port,
|
|
369
|
+
'auth': this.auth
|
|
370
|
+
};
|
|
371
|
+
}
|
|
262
372
|
}
|
|
263
373
|
module.exports = CameraVapix;
|