camstreamerlib 3.5.2 → 4.0.0-beta.2
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/CamOverlayAPI.d.ts +11 -28
- package/CamOverlayAPI.js +116 -138
- package/CamOverlayDrawingAPI.js +26 -18
- package/CamOverlayPainter/Frame.js +167 -182
- package/CamOverlayPainter/Painter.js +80 -101
- package/CamOverlayPainter/ResourceManager.js +31 -46
- package/CamScripterAPI.d.ts +19 -0
- package/CamScripterAPI.js +66 -0
- package/CamScripterAPICameraEventsGenerator.js +22 -16
- package/CamStreamerAPI.d.ts +5 -27
- package/CamStreamerAPI.js +45 -71
- package/CamSwitcherAPI.d.ts +38 -71
- package/CamSwitcherAPI.js +329 -91
- package/CamSwitcherEvents.d.ts +15 -33
- package/CamSwitcherEvents.js +53 -97
- package/CreatePackage.js +5 -7
- package/README.md +3 -1
- package/VapixAPI.d.ts +66 -0
- package/VapixAPI.js +454 -0
- package/VapixEvents.js +18 -16
- package/errors/errors.d.ts +34 -0
- package/errors/errors.js +66 -0
- package/events/AxisCameraStationEvents.js +29 -42
- package/events/GenetecAgent.d.ts +14 -15
- package/events/GenetecAgent.js +81 -100
- package/internal/Digest.js +5 -11
- package/internal/ProxyClient.d.ts +11 -0
- package/internal/ProxyClient.js +40 -0
- package/internal/common.d.ts +19 -4
- package/internal/common.js +11 -26
- package/internal/constants.d.ts +1 -0
- package/internal/constants.js +1 -0
- package/internal/transformers.d.ts +5 -0
- package/internal/transformers.js +25 -0
- package/internal/utils.d.ts +11 -0
- package/internal/utils.js +34 -0
- package/internal/versionCompare.d.ts +6 -0
- package/internal/versionCompare.js +44 -0
- package/node/DefaultClient.d.ts +15 -0
- package/node/DefaultClient.js +50 -0
- package/{internal → node}/HttpRequestSender.d.ts +2 -2
- package/node/HttpRequestSender.js +85 -0
- package/{HttpServer.d.ts → node/HttpServer.d.ts} +1 -1
- package/{HttpServer.js → node/HttpServer.js} +22 -24
- package/{internal → node}/WsClient.d.ts +1 -1
- package/{internal → node}/WsClient.js +32 -39
- package/node/WsEventClient.d.ts +13 -0
- package/node/WsEventClient.js +18 -0
- package/package.json +7 -3
- package/types/CamOverlayAPI.d.ts +188 -0
- package/types/CamOverlayAPI.js +44 -0
- package/types/CamScripterAPI.d.ts +67 -0
- package/types/CamScripterAPI.js +17 -0
- package/types/CamStreamerAPI.d.ts +139 -0
- package/types/CamStreamerAPI.js +25 -0
- package/types/CamSwitcherAPI.d.ts +814 -0
- package/types/CamSwitcherAPI.js +134 -0
- package/types/CamswitcherEvents.d.ts +491 -0
- package/types/CamswitcherEvents.js +59 -0
- package/types/VapixAPI.d.ts +1704 -0
- package/types/VapixAPI.js +129 -0
- package/types/common.d.ts +37 -0
- package/types/common.js +11 -0
- package/web/DefaultClient.d.ts +6 -0
- package/web/DefaultClient.js +16 -0
- package/web/WsClient.d.ts +13 -0
- package/web/WsClient.js +58 -0
- package/CameraVapix.d.ts +0 -98
- package/CameraVapix.js +0 -441
- package/DefaultAgent.d.ts +0 -15
- package/DefaultAgent.js +0 -68
- package/internal/HttpRequestSender.js +0 -117
package/CreatePackage.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const fs = require("fs");
|
|
6
|
-
const child_process_1 = require("child_process");
|
|
1
|
+
import * as AdmZip from 'adm-zip';
|
|
2
|
+
import * as Path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
7
5
|
const productionModulesFolder = 'production_modules';
|
|
8
6
|
function isDirectory(path) {
|
|
9
7
|
const stat = fs.statSync(path);
|
|
@@ -55,7 +53,7 @@ function installDependencies() {
|
|
|
55
53
|
}
|
|
56
54
|
fs.cpSync('package.json', Path.join(productionModulesFolder, 'package.json'));
|
|
57
55
|
fs.cpSync('package-lock.json', Path.join(productionModulesFolder, 'package-lock.json'));
|
|
58
|
-
|
|
56
|
+
execSync(`npm ci --omit=dev`, {
|
|
59
57
|
cwd: Path.join(process.cwd(), productionModulesFolder),
|
|
60
58
|
});
|
|
61
59
|
}
|
package/README.md
CHANGED
package/VapixAPI.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { IClient, TParameters, TResponse } from './internal/common';
|
|
2
|
+
import { TApplication, TAudioSampleRates, TSDCardInfo, TPtzOverview, TCameraPTZItem, TCameraPTZItemData, TAudioDevice } from './types/VapixAPI';
|
|
3
|
+
import { ProxyClient } from './internal/ProxyClient';
|
|
4
|
+
import { TCameraImageConfig, TProxyParam } from './types/common';
|
|
5
|
+
export declare class VapixAPI<Client extends IClient = IClient> {
|
|
6
|
+
client: ProxyClient<Client>;
|
|
7
|
+
constructor(client: Client, getProxyUrl: () => string);
|
|
8
|
+
getUrlEncoded(proxy: TProxyParam | undefined, path: string, parameters?: TParameters, headers?: Record<string, string>): Promise<TResponse>;
|
|
9
|
+
postJson(proxy: TProxyParam | undefined, path: string, jsonData: Record<string, any>, headers?: Record<string, string>): Promise<TResponse>;
|
|
10
|
+
getCameraImage(params: TCameraImageConfig, proxy?: TProxyParam): Promise<TResponse>;
|
|
11
|
+
getEventDeclarations(proxy?: TProxyParam): Promise<string>;
|
|
12
|
+
getSupportedAudioSampleRate(proxy?: TProxyParam): Promise<TAudioSampleRates[]>;
|
|
13
|
+
performAutofocus(proxy?: TProxyParam): Promise<void>;
|
|
14
|
+
checkSDCard(proxy?: TProxyParam): Promise<TSDCardInfo>;
|
|
15
|
+
mountSDCard(proxy?: TProxyParam): Promise<number>;
|
|
16
|
+
unmountSDCard(proxy?: TProxyParam): Promise<number>;
|
|
17
|
+
private _doSDCardMountAction;
|
|
18
|
+
fetchSDCardJobProgress(jobId: number, proxy?: TProxyParam): Promise<number>;
|
|
19
|
+
downloadCameraReport(proxy?: TProxyParam): Promise<TResponse>;
|
|
20
|
+
getSystemLog(proxy?: TProxyParam): Promise<TResponse>;
|
|
21
|
+
getMaxFps(channel: number, proxy?: TProxyParam): Promise<number>;
|
|
22
|
+
getTimezone(proxy?: TProxyParam): Promise<string>;
|
|
23
|
+
getDateTimeInfo(proxy?: TProxyParam): Promise<{
|
|
24
|
+
data: {
|
|
25
|
+
dateTime: string;
|
|
26
|
+
dstEnabled: boolean;
|
|
27
|
+
localDateTime: string;
|
|
28
|
+
posixTimeZone: string;
|
|
29
|
+
timeZone: string;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
getDevicesSettings(proxy?: TProxyParam): Promise<TAudioDevice[]>;
|
|
33
|
+
fetchRemoteDeviceInfo<T extends Record<string, any>>(payload: T, proxy?: TProxyParam): Promise<any>;
|
|
34
|
+
getHeaders(proxy?: TProxyParam): Promise<Record<string, string>>;
|
|
35
|
+
setHeaders(headers: Record<string, string>, proxy?: TProxyParam): Promise<TResponse>;
|
|
36
|
+
getParameter(paramNames: string | string[], proxy?: TProxyParam): Promise<Record<string, string>>;
|
|
37
|
+
setParameter(params: Record<string, string | number | boolean>, proxy?: TProxyParam): Promise<boolean>;
|
|
38
|
+
getGuardTourList(proxy?: TProxyParam): Promise<{
|
|
39
|
+
name: string;
|
|
40
|
+
id: string;
|
|
41
|
+
running: string;
|
|
42
|
+
tour: {
|
|
43
|
+
moveSpeed?: unknown;
|
|
44
|
+
position?: unknown;
|
|
45
|
+
presetNbr?: unknown;
|
|
46
|
+
waitTime?: unknown;
|
|
47
|
+
waitTimeViewType?: unknown;
|
|
48
|
+
}[];
|
|
49
|
+
camNbr?: unknown;
|
|
50
|
+
randomEnabled?: unknown;
|
|
51
|
+
timeBetweenSequences?: unknown;
|
|
52
|
+
}[]>;
|
|
53
|
+
setGuardTourEnabled(guardTourID: string, enable: boolean, proxy?: TProxyParam): Promise<boolean>;
|
|
54
|
+
getPTZPresetList(channel: number, proxy?: TProxyParam): Promise<string[]>;
|
|
55
|
+
listPTZ(camera: number, proxy?: TProxyParam): Promise<TCameraPTZItem[]>;
|
|
56
|
+
listPtzVideoSourceOverview(proxy?: TProxyParam): Promise<TPtzOverview>;
|
|
57
|
+
goToPreset(channel: number, presetName: string, proxy?: TProxyParam): Promise<TResponse>;
|
|
58
|
+
getPtzPosition(camera: number, proxy?: TProxyParam): Promise<TCameraPTZItemData>;
|
|
59
|
+
getInputState(port: number, proxy?: TProxyParam): Promise<boolean>;
|
|
60
|
+
setOutputState(port: number, active: boolean, proxy?: TProxyParam): Promise<TResponse>;
|
|
61
|
+
getApplicationList(proxy?: TProxyParam): Promise<TApplication[]>;
|
|
62
|
+
startApplication(applicationID: string, proxy?: TProxyParam): Promise<void>;
|
|
63
|
+
restartApplication(applicationID: string, proxy?: TProxyParam): Promise<void>;
|
|
64
|
+
stopApplication(applicationID: string, proxy?: TProxyParam): Promise<void>;
|
|
65
|
+
installApplication(data: Blob, fileName: string): Promise<void>;
|
|
66
|
+
}
|
package/VapixAPI.js
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import * as prettifyXml from 'prettify-xml';
|
|
2
|
+
import { parseStringPromise } from 'xml2js';
|
|
3
|
+
import { isNullish, responseStringify } from './internal/common';
|
|
4
|
+
import { sdCardWatchedStatuses, APP_IDS, maxFpsResponseSchema, dateTimeinfoSchema, audioDeviceRequestSchema, audioSampleRatesResponseSchema, } from './types/VapixAPI';
|
|
5
|
+
import { ApplicationAPIError, MaxFPSError, NoDeviceInfoError, SDCardActionError, SDCardJobError, } from './errors/errors';
|
|
6
|
+
import { ProxyClient } from './internal/ProxyClient';
|
|
7
|
+
import { arrayToUrl, paramToUrl } from './internal/utils';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
export class VapixAPI {
|
|
10
|
+
client;
|
|
11
|
+
constructor(client, getProxyUrl) {
|
|
12
|
+
this.client = new ProxyClient(client, getProxyUrl);
|
|
13
|
+
}
|
|
14
|
+
async getUrlEncoded(proxy = null, path, parameters, headers = {}) {
|
|
15
|
+
const data = paramToUrl(parameters);
|
|
16
|
+
const head = { ...headers, 'Content-Type': 'application/x-www-form-urlencoded' };
|
|
17
|
+
const res = await this.client.post(proxy, path, data, {}, head);
|
|
18
|
+
if (!res.ok) {
|
|
19
|
+
throw new Error(await responseStringify(res));
|
|
20
|
+
}
|
|
21
|
+
return res;
|
|
22
|
+
}
|
|
23
|
+
async postJson(proxy = null, path, jsonData, headers = {}) {
|
|
24
|
+
const data = JSON.stringify(jsonData);
|
|
25
|
+
const head = { ...headers, 'Content-Type': 'application/json' };
|
|
26
|
+
const res = await this.client.post(proxy, path, data, {}, head);
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
throw new Error(await responseStringify(res));
|
|
29
|
+
}
|
|
30
|
+
return res;
|
|
31
|
+
}
|
|
32
|
+
async getCameraImage(params, proxy = null) {
|
|
33
|
+
return await this.client.get(proxy, '/axis-cgi/jpg/image.cgi', params);
|
|
34
|
+
}
|
|
35
|
+
async getEventDeclarations(proxy = null) {
|
|
36
|
+
const data = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
|
|
37
|
+
'<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
|
|
38
|
+
'xmlns:xsd="http://www.w3.org/2001/XMLSchema">' +
|
|
39
|
+
'<GetEventInstances xmlns="http://www.axis.com/vapix/ws/event1"/>' +
|
|
40
|
+
'</s:Body>' +
|
|
41
|
+
'</s:Envelope>';
|
|
42
|
+
const res = await this.client.post(proxy, '/vapix/services', data, { 'Content-Type': 'application/soap+xml' });
|
|
43
|
+
if (!res.ok) {
|
|
44
|
+
throw new Error(await responseStringify(res));
|
|
45
|
+
}
|
|
46
|
+
const declarations = await res.text();
|
|
47
|
+
return prettifyXml(declarations);
|
|
48
|
+
}
|
|
49
|
+
async getSupportedAudioSampleRate(proxy = null) {
|
|
50
|
+
const url = '/axis-cgi/audio/streamingcapabilities.cgi';
|
|
51
|
+
const formData = { apiVersion: '1.0', method: 'list' };
|
|
52
|
+
const res = await this.postJson(proxy, url, formData);
|
|
53
|
+
const encoders = audioSampleRatesResponseSchema.parse(await res.json()).data.encoders;
|
|
54
|
+
const data = encoders.aac ?? encoders.AAC ?? [];
|
|
55
|
+
return data.map((item) => {
|
|
56
|
+
return {
|
|
57
|
+
sampleRate: item.sample_rate,
|
|
58
|
+
bitRates: item.bit_rates,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async performAutofocus(proxy = null) {
|
|
63
|
+
try {
|
|
64
|
+
const data = {
|
|
65
|
+
apiVersion: '1',
|
|
66
|
+
method: 'performAutofocus',
|
|
67
|
+
params: {
|
|
68
|
+
optics: [
|
|
69
|
+
{
|
|
70
|
+
opticsId: '0',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
await this.postJson(proxy, '/axis-cgi/opticscontrol.cgi', data);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
await this.postJson(proxy, '/axis-cgi/opticssetup.cgi', {
|
|
79
|
+
autofocus: 'perform',
|
|
80
|
+
source: '1',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async checkSDCard(proxy = null) {
|
|
85
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/disks/list.cgi', {
|
|
86
|
+
diskid: 'SD_DISK',
|
|
87
|
+
});
|
|
88
|
+
const result = await parseStringPromise(await res.text(), {
|
|
89
|
+
ignoreAttrs: false,
|
|
90
|
+
mergeAttrs: true,
|
|
91
|
+
explicitArray: false,
|
|
92
|
+
});
|
|
93
|
+
const data = result.root.disks.disk;
|
|
94
|
+
return {
|
|
95
|
+
totalSize: parseInt(data.totalsize),
|
|
96
|
+
freeSize: parseInt(data.freesize),
|
|
97
|
+
status: sdCardWatchedStatuses.includes(data.status) ? data.status : 'disconnected',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
mountSDCard(proxy = null) {
|
|
101
|
+
return this._doSDCardMountAction('MOUNT', proxy);
|
|
102
|
+
}
|
|
103
|
+
unmountSDCard(proxy = null) {
|
|
104
|
+
return this._doSDCardMountAction('UNMOUNT', proxy);
|
|
105
|
+
}
|
|
106
|
+
async _doSDCardMountAction(action, proxy = null) {
|
|
107
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/disks/mount.cgi', {
|
|
108
|
+
action: action,
|
|
109
|
+
diskid: 'SD_DISK',
|
|
110
|
+
});
|
|
111
|
+
const result = await parseStringPromise(await res.text(), {
|
|
112
|
+
ignoreAttrs: false,
|
|
113
|
+
mergeAttrs: true,
|
|
114
|
+
explicitArray: false,
|
|
115
|
+
});
|
|
116
|
+
const job = result.root.job;
|
|
117
|
+
if (job.result !== 'OK') {
|
|
118
|
+
throw new SDCardActionError(action, await responseStringify(res));
|
|
119
|
+
}
|
|
120
|
+
return Number(job.jobid);
|
|
121
|
+
}
|
|
122
|
+
async fetchSDCardJobProgress(jobId, proxy = null) {
|
|
123
|
+
const res = await this.getUrlEncoded(proxy, '/disks/job.cgi', {
|
|
124
|
+
jobid: String(jobId),
|
|
125
|
+
diskid: 'SD_DISK',
|
|
126
|
+
});
|
|
127
|
+
const result = await parseStringPromise(await res.text(), {
|
|
128
|
+
ignoreAttrs: false,
|
|
129
|
+
mergeAttrs: true,
|
|
130
|
+
explicitArray: false,
|
|
131
|
+
});
|
|
132
|
+
const job = result.root.job;
|
|
133
|
+
if (job.result !== 'OK') {
|
|
134
|
+
throw new SDCardJobError();
|
|
135
|
+
}
|
|
136
|
+
return Number(job.progress);
|
|
137
|
+
}
|
|
138
|
+
downloadCameraReport(proxy = null) {
|
|
139
|
+
return this.getUrlEncoded(proxy, '/axis-cgi/serverreport.cgi', { mode: 'text' });
|
|
140
|
+
}
|
|
141
|
+
getSystemLog(proxy = null) {
|
|
142
|
+
return this.getUrlEncoded(proxy, '/axis-cgi/admin/systemlog.cgi');
|
|
143
|
+
}
|
|
144
|
+
async getMaxFps(channel, proxy = null) {
|
|
145
|
+
const data = { apiVersion: '1.0', method: 'getCaptureModes' };
|
|
146
|
+
const res = await this.postJson(proxy, '/axis-cgi/capturemode.cgi', data);
|
|
147
|
+
const response = maxFpsResponseSchema.parse(await res.json());
|
|
148
|
+
const channels = response.data;
|
|
149
|
+
if (channels === undefined) {
|
|
150
|
+
throw new MaxFPSError('MALFORMED_REPLY');
|
|
151
|
+
}
|
|
152
|
+
const channelData = channels.find((x) => x.channel === channel);
|
|
153
|
+
if (channelData === undefined) {
|
|
154
|
+
throw new MaxFPSError('CHANNEL_NOT_FOUND');
|
|
155
|
+
}
|
|
156
|
+
const captureModes = channelData.captureMode;
|
|
157
|
+
const captureMode = captureModes.find((x) => x.enabled === true);
|
|
158
|
+
if (captureMode === undefined) {
|
|
159
|
+
throw new MaxFPSError('CAPTURE_MODE_NOT_FOUND');
|
|
160
|
+
}
|
|
161
|
+
if (isNullish(captureMode.maxFPS)) {
|
|
162
|
+
throw new MaxFPSError('FPS_NOT_SPECIFIED');
|
|
163
|
+
}
|
|
164
|
+
return captureMode.maxFPS;
|
|
165
|
+
}
|
|
166
|
+
async getTimezone(proxy = null) {
|
|
167
|
+
const data = { apiVersion: '1.0', method: 'getDateTimeInfo' };
|
|
168
|
+
const res = await this.postJson(proxy, '/axis-cgi/time.cgi', data);
|
|
169
|
+
return (await res.json())?.timeZone ?? 'Europe/Prague';
|
|
170
|
+
}
|
|
171
|
+
async getDateTimeInfo(proxy = null) {
|
|
172
|
+
const data = { apiVersion: '1.0', method: 'getDateTimeInfo' };
|
|
173
|
+
const res = await this.postJson(proxy, '/axis-cgi/time.cgi', data);
|
|
174
|
+
return dateTimeinfoSchema.parse(await res.json());
|
|
175
|
+
}
|
|
176
|
+
async getDevicesSettings(proxy = null) {
|
|
177
|
+
const data = { apiVersion: '1.0', method: 'getDevicesSettings' };
|
|
178
|
+
const res = await this.postJson(proxy, '/axis-cgi/audiodevicecontrol.cgi', data);
|
|
179
|
+
const result = audioDeviceRequestSchema.parse(await res.json());
|
|
180
|
+
return result.devices.map((device) => ({
|
|
181
|
+
...device,
|
|
182
|
+
inputs: (device.inputs || []).sort((a, b) => a.id.localeCompare(b.id)),
|
|
183
|
+
outputs: (device.outputs || []).sort((a, b) => a.id.localeCompare(b.id)),
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
async fetchRemoteDeviceInfo(payload, proxy = null) {
|
|
187
|
+
const res = await this.postJson(proxy, '/axis-cgi/basicdeviceinfo.cgi', payload);
|
|
188
|
+
const result = await parseStringPromise(await res.text(), {
|
|
189
|
+
ignoreAttrs: false,
|
|
190
|
+
mergeAttrs: true,
|
|
191
|
+
explicitArray: false,
|
|
192
|
+
});
|
|
193
|
+
if (isNullish(result.body.data)) {
|
|
194
|
+
throw new NoDeviceInfoError();
|
|
195
|
+
}
|
|
196
|
+
return result.data;
|
|
197
|
+
}
|
|
198
|
+
async getHeaders(proxy = null) {
|
|
199
|
+
const data = { apiVersion: '1.0', method: 'list' };
|
|
200
|
+
const res = await this.postJson(proxy, '/axis-cgi/customhttpheader.cgi', data);
|
|
201
|
+
return z.object({ data: z.record(z.string()) }).parse(await res.json()).data;
|
|
202
|
+
}
|
|
203
|
+
async setHeaders(headers, proxy = null) {
|
|
204
|
+
const data = { apiVersion: '1.0', method: 'set', params: headers };
|
|
205
|
+
return this.postJson(proxy, '/axis-cgi/customhttpheader.cgi', data);
|
|
206
|
+
}
|
|
207
|
+
async getParameter(paramNames, proxy = null) {
|
|
208
|
+
const response = await this.getUrlEncoded(proxy, '/axis-cgi/param.cgi', {
|
|
209
|
+
action: 'list',
|
|
210
|
+
group: arrayToUrl(paramNames),
|
|
211
|
+
});
|
|
212
|
+
return parseParameters(await response.text());
|
|
213
|
+
}
|
|
214
|
+
async setParameter(params, proxy = null) {
|
|
215
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/param.cgi', {
|
|
216
|
+
...params,
|
|
217
|
+
action: 'update',
|
|
218
|
+
});
|
|
219
|
+
const responseText = await res.text();
|
|
220
|
+
if (responseText.startsWith('# Error')) {
|
|
221
|
+
throw new Error(responseText);
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
async getGuardTourList(proxy = null) {
|
|
226
|
+
const gTourList = new Array();
|
|
227
|
+
const response = await this.getParameter('GuardTour', proxy);
|
|
228
|
+
for (let i = 0; i < 20; i++) {
|
|
229
|
+
const gTourBaseName = 'root.GuardTour.G' + i;
|
|
230
|
+
if (gTourBaseName + '.CamNbr' in response) {
|
|
231
|
+
const gTour = {
|
|
232
|
+
id: gTourBaseName,
|
|
233
|
+
camNbr: response[gTourBaseName + '.CamNbr'],
|
|
234
|
+
name: response[gTourBaseName + '.Name'],
|
|
235
|
+
randomEnabled: response[gTourBaseName + '.RandomEnabled'],
|
|
236
|
+
running: response[gTourBaseName + '.Running'],
|
|
237
|
+
timeBetweenSequences: response[gTourBaseName + '.TimeBetweenSequences'],
|
|
238
|
+
tour: [],
|
|
239
|
+
};
|
|
240
|
+
for (let j = 0; j < 100; j++) {
|
|
241
|
+
const tourBaseName = 'root.GuardTour.G' + i + '.Tour.T' + j;
|
|
242
|
+
if (tourBaseName + '.MoveSpeed' in response) {
|
|
243
|
+
const tour = {
|
|
244
|
+
moveSpeed: response[tourBaseName + '.MoveSpeed'],
|
|
245
|
+
position: response[tourBaseName + '.Position'],
|
|
246
|
+
presetNbr: response[tourBaseName + '.PresetNbr'],
|
|
247
|
+
waitTime: response[tourBaseName + '.WaitTime'],
|
|
248
|
+
waitTimeViewType: response[tourBaseName + '.WaitTimeViewType'],
|
|
249
|
+
};
|
|
250
|
+
gTour.tour.push(tour);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
gTourList.push(gTour);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return gTourList;
|
|
260
|
+
}
|
|
261
|
+
setGuardTourEnabled(guardTourID, enable, proxy = null) {
|
|
262
|
+
const options = {};
|
|
263
|
+
options[guardTourID + '.Running'] = enable ? 'yes' : 'no';
|
|
264
|
+
return this.setParameter(options, proxy);
|
|
265
|
+
}
|
|
266
|
+
async getPTZPresetList(channel, proxy = null) {
|
|
267
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/com/ptz.cgi', {
|
|
268
|
+
query: 'presetposcam',
|
|
269
|
+
camera: channel.toString(),
|
|
270
|
+
});
|
|
271
|
+
const text = await res.text();
|
|
272
|
+
const lines = text.split(/[\r\n]/);
|
|
273
|
+
const positions = [];
|
|
274
|
+
for (const line of lines) {
|
|
275
|
+
if (line.indexOf('presetposno') !== -1) {
|
|
276
|
+
const delimiterPos = line.indexOf('=');
|
|
277
|
+
if (delimiterPos !== -1) {
|
|
278
|
+
const value = line.substring(delimiterPos + 1);
|
|
279
|
+
positions.push(value);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return positions;
|
|
284
|
+
}
|
|
285
|
+
async listPTZ(camera, proxy = null) {
|
|
286
|
+
const url = `/axis-cgi/com/ptz.cgi`;
|
|
287
|
+
const response = await this.getUrlEncoded(proxy, url, {
|
|
288
|
+
camera,
|
|
289
|
+
query: 'presetposcamdata',
|
|
290
|
+
format: 'json',
|
|
291
|
+
});
|
|
292
|
+
return parseCameraPtzResponse(await response.text())[camera] ?? [];
|
|
293
|
+
}
|
|
294
|
+
async listPtzVideoSourceOverview(proxy = null) {
|
|
295
|
+
const response = await this.getUrlEncoded(proxy, '/axis-cgi/com/ptz.cgi', {
|
|
296
|
+
query: 'presetposall',
|
|
297
|
+
format: 'json',
|
|
298
|
+
});
|
|
299
|
+
const data = parseCameraPtzResponse(await response.text());
|
|
300
|
+
const res = {};
|
|
301
|
+
Object.keys(data).forEach((camera) => {
|
|
302
|
+
res[Number(camera) - 1] = data[Number(camera)].map(({ data: itemData, ...d }) => d);
|
|
303
|
+
});
|
|
304
|
+
return res;
|
|
305
|
+
}
|
|
306
|
+
goToPreset(channel, presetName, proxy = null) {
|
|
307
|
+
return this.getUrlEncoded(proxy, '/axis-cgi/com/ptz.cgi', {
|
|
308
|
+
camera: channel.toString(),
|
|
309
|
+
gotoserverpresetname: presetName,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
async getPtzPosition(camera, proxy = null) {
|
|
313
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/com/ptz.cgi', {
|
|
314
|
+
query: 'position',
|
|
315
|
+
camera: camera.toString(),
|
|
316
|
+
});
|
|
317
|
+
const params = parseParameters(await res.text());
|
|
318
|
+
return {
|
|
319
|
+
pan: Number(params.pan),
|
|
320
|
+
tilt: Number(params.tilt),
|
|
321
|
+
zoom: Number(params.zoom),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
async getInputState(port, proxy = null) {
|
|
325
|
+
const response = await (await this.getUrlEncoded(proxy, '/axis-cgi/io/port.cgi', { checkactive: port.toString() })).text();
|
|
326
|
+
return response.split('=')[1].indexOf('active') === 0;
|
|
327
|
+
}
|
|
328
|
+
async setOutputState(port, active, proxy = null) {
|
|
329
|
+
return this.getUrlEncoded(proxy, '/axis-cgi/io/port.cgi', { action: active ? `${port}:/` : `${port}:\\` });
|
|
330
|
+
}
|
|
331
|
+
async getApplicationList(proxy = null) {
|
|
332
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/applications/list.cgi');
|
|
333
|
+
const xml = await res.text();
|
|
334
|
+
const result = (await parseStringPromise(xml));
|
|
335
|
+
const apps = [];
|
|
336
|
+
for (let i = 0; i < result.reply.application.length; i++) {
|
|
337
|
+
apps.push({
|
|
338
|
+
...result.reply.application[i].$,
|
|
339
|
+
appId: APP_IDS.find((id) => id.toLowerCase() === result.reply.application[i].$.Name.toLowerCase()) ?? null,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return apps;
|
|
343
|
+
}
|
|
344
|
+
async startApplication(applicationID, proxy = null) {
|
|
345
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/applications/control.cgi', {
|
|
346
|
+
package: applicationID.toLowerCase(),
|
|
347
|
+
action: 'start',
|
|
348
|
+
});
|
|
349
|
+
const text = (await res.text()).trim().toLowerCase();
|
|
350
|
+
if (text !== 'ok' && !(text.startsWith('error:') && text.substring(7) === '6')) {
|
|
351
|
+
throw new ApplicationAPIError('START', await responseStringify(res));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async restartApplication(applicationID, proxy = null) {
|
|
355
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/applications/control.cgi', {
|
|
356
|
+
package: applicationID.toLowerCase(),
|
|
357
|
+
action: 'restart',
|
|
358
|
+
});
|
|
359
|
+
const text = (await res.text()).trim().toLowerCase();
|
|
360
|
+
if (text !== 'ok') {
|
|
361
|
+
throw new ApplicationAPIError('RESTART', await responseStringify(res));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async stopApplication(applicationID, proxy = null) {
|
|
365
|
+
const res = await this.getUrlEncoded(proxy, '/axis-cgi/applications/control.cgi', {
|
|
366
|
+
package: applicationID.toLowerCase(),
|
|
367
|
+
action: 'stop',
|
|
368
|
+
});
|
|
369
|
+
const text = (await res.text()).trim().toLowerCase();
|
|
370
|
+
if (text !== 'ok' && !(text.startsWith('error:') && text.substring(7) === '6')) {
|
|
371
|
+
throw new ApplicationAPIError('STOP', await responseStringify(res));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async installApplication(data, fileName) {
|
|
375
|
+
const formData = new FormData();
|
|
376
|
+
formData.append('packfil', data, fileName);
|
|
377
|
+
const res = await this.client.post(null, '/axis-cgi/applications/upload.cgi', formData, {}, {
|
|
378
|
+
contentType: 'application/octet-stream',
|
|
379
|
+
});
|
|
380
|
+
if (!res.ok) {
|
|
381
|
+
throw new Error(await responseStringify(res));
|
|
382
|
+
}
|
|
383
|
+
const text = await res.text();
|
|
384
|
+
if (text.length > 5) {
|
|
385
|
+
throw new Error('installing error: ' + text);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const parseParameters = (response) => {
|
|
390
|
+
const params = {};
|
|
391
|
+
const lines = response.split(/[\r\n]/);
|
|
392
|
+
for (let i = 0; i < lines.length; i++) {
|
|
393
|
+
if (lines[i].length === 0 || lines[i].substring(0, 7) === '# Error') {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const delimiterPos = lines[i].indexOf('=');
|
|
397
|
+
if (delimiterPos !== -1) {
|
|
398
|
+
const paramName = lines[i].substring(0, delimiterPos);
|
|
399
|
+
const paramValue = lines[i].substring(delimiterPos + 1);
|
|
400
|
+
params[paramName] = paramValue;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return params;
|
|
404
|
+
};
|
|
405
|
+
const parseCameraPtzResponse = (response) => {
|
|
406
|
+
const json = JSON.parse(response);
|
|
407
|
+
const parsed = {};
|
|
408
|
+
Object.keys(json).forEach((key) => {
|
|
409
|
+
if (!key.startsWith('Camera ')) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const camera = Number(key.replace('Camera ', ''));
|
|
413
|
+
if (json[key].presets !== undefined) {
|
|
414
|
+
parsed[camera] = parsePtz(json[key].presets);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
return parsed;
|
|
418
|
+
};
|
|
419
|
+
const parsePtz = (parsed) => {
|
|
420
|
+
const res = [];
|
|
421
|
+
parsed.forEach((value) => {
|
|
422
|
+
const delimiterPos = value.indexOf('=');
|
|
423
|
+
if (delimiterPos === -1) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (!value.startsWith('presetposno')) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const id = Number(value.substring(11, delimiterPos));
|
|
430
|
+
if (Number.isNaN(id)) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const data = value.substring(delimiterPos + 1).split(':');
|
|
434
|
+
const getValue = (valueName) => {
|
|
435
|
+
for (const d of data) {
|
|
436
|
+
const p = d.split('=');
|
|
437
|
+
if (p[0] === valueName) {
|
|
438
|
+
return Number(p[1]);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return 0;
|
|
442
|
+
};
|
|
443
|
+
res.push({
|
|
444
|
+
id,
|
|
445
|
+
name: data[0],
|
|
446
|
+
data: {
|
|
447
|
+
pan: getValue('pan'),
|
|
448
|
+
tilt: getValue('tilt'),
|
|
449
|
+
zoom: getValue('zoom'),
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
return res;
|
|
454
|
+
};
|
package/VapixEvents.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { EventEmitter2 as EventEmitter } from 'eventemitter2';
|
|
2
|
+
import { WsClient } from './node/WsClient';
|
|
3
|
+
export class VapixEvents extends EventEmitter {
|
|
4
|
+
tls;
|
|
5
|
+
tlsInsecure;
|
|
6
|
+
ip;
|
|
7
|
+
port;
|
|
8
|
+
user;
|
|
9
|
+
pass;
|
|
10
|
+
ws;
|
|
7
11
|
constructor(options = {}) {
|
|
8
|
-
var _a, _b, _c, _d, _e, _f;
|
|
9
12
|
super();
|
|
10
|
-
this.tls =
|
|
11
|
-
this.tlsInsecure =
|
|
12
|
-
this.ip =
|
|
13
|
-
this.port =
|
|
14
|
-
this.user =
|
|
15
|
-
this.pass =
|
|
13
|
+
this.tls = options.tls ?? false;
|
|
14
|
+
this.tlsInsecure = options.tlsInsecure ?? false;
|
|
15
|
+
this.ip = options.ip ?? '127.0.0.1';
|
|
16
|
+
this.port = options.port ?? (this.tls ? 443 : 80);
|
|
17
|
+
this.user = options.user ?? 'root';
|
|
18
|
+
this.pass = options.pass ?? '';
|
|
16
19
|
this.createWsClient();
|
|
17
|
-
|
|
20
|
+
EventEmitter.call(this);
|
|
18
21
|
}
|
|
19
22
|
connect() {
|
|
20
23
|
this.ws.open();
|
|
@@ -32,7 +35,7 @@ class VapixEvents extends eventemitter2_1.EventEmitter2 {
|
|
|
32
35
|
port: this.port,
|
|
33
36
|
address: '/vapix/ws-data-stream?sources=events',
|
|
34
37
|
};
|
|
35
|
-
this.ws = new
|
|
38
|
+
this.ws = new WsClient(options);
|
|
36
39
|
this.ws.on('open', () => {
|
|
37
40
|
const topics = [];
|
|
38
41
|
const eventNames = this.eventNames();
|
|
@@ -79,4 +82,3 @@ class VapixEvents extends eventemitter2_1.EventEmitter2 {
|
|
|
79
82
|
return eventName === 'open' || eventName === 'close' || eventName === 'error';
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
|
-
exports.VapixEvents = VapixEvents;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare class ServiceUnavailableError extends Error {
|
|
2
|
+
constructor();
|
|
3
|
+
}
|
|
4
|
+
export declare class ServiceNotFoundError extends Error {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare class ParsingBlobError extends Error {
|
|
8
|
+
constructor(err: unknown);
|
|
9
|
+
}
|
|
10
|
+
type TApplicationAPIAction = 'START' | 'RESTART' | 'STOP';
|
|
11
|
+
export declare class ApplicationAPIError extends Error {
|
|
12
|
+
constructor(action: TApplicationAPIAction, res: string);
|
|
13
|
+
}
|
|
14
|
+
type TSDCardAction = 'MOUNT' | 'UNMOUNT';
|
|
15
|
+
export declare class SDCardActionError extends Error {
|
|
16
|
+
constructor(action: TSDCardAction, res: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class SDCardJobError extends Error {
|
|
19
|
+
constructor();
|
|
20
|
+
}
|
|
21
|
+
type TMaxFPSErrorType = 'MALFORMED_REPLY' | 'CHANNEL_NOT_FOUND' | 'CAPTURE_MODE_NOT_FOUND' | 'FPS_NOT_SPECIFIED';
|
|
22
|
+
export declare class MaxFPSError extends Error {
|
|
23
|
+
constructor(state: TMaxFPSErrorType);
|
|
24
|
+
}
|
|
25
|
+
export declare class NoDeviceInfoError extends Error {
|
|
26
|
+
constructor();
|
|
27
|
+
}
|
|
28
|
+
export declare class FetchDeviceInfoError extends Error {
|
|
29
|
+
constructor(err: unknown);
|
|
30
|
+
}
|
|
31
|
+
export declare class AddNewClipError extends Error {
|
|
32
|
+
constructor(message: string);
|
|
33
|
+
}
|
|
34
|
+
export {};
|