camstreamerlib 4.0.0-beta.3 → 4.0.0-beta.31
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/README.md +8 -2
- package/cjs/CamOverlayAPI.d.ts +48 -26
- package/cjs/CamOverlayAPI.js +171 -88
- package/cjs/CamOverlayDrawingAPI.d.ts +2 -47
- package/cjs/CamOverlayDrawingAPI.js +6 -3
- package/cjs/CamOverlayPainter/Frame.d.ts +8 -37
- package/cjs/CamOverlayPainter/Frame.js +33 -0
- package/cjs/CamOverlayPainter/Painter.d.ts +16 -10
- package/cjs/CamOverlayPainter/Painter.js +6 -5
- package/cjs/CamOverlayPainter/ResourceManager.d.ts +3 -2
- package/cjs/CamOverlayPainter/ResourceManager.js +8 -11
- package/cjs/CamScripterAPI.d.ts +34 -16
- package/cjs/CamScripterAPI.js +50 -41
- package/cjs/CamScripterAPICameraEventsGenerator.d.ts +1 -1
- package/cjs/CamScripterAPICameraEventsGenerator.js +6 -3
- package/cjs/CamStreamerAPI.d.ts +17 -14
- package/cjs/CamStreamerAPI.js +32 -32
- package/cjs/CamSwitcherAPI.d.ts +43 -37
- package/cjs/CamSwitcherAPI.js +123 -114
- package/cjs/CamSwitcherEvents.d.ts +1 -1
- package/cjs/PlaneTrackerAPI.d.ts +43 -0
- package/cjs/PlaneTrackerAPI.js +211 -0
- package/cjs/VapixAPI.d.ts +56 -42
- package/cjs/VapixAPI.js +305 -216
- package/cjs/VapixEvents.d.ts +1 -1
- package/cjs/VapixEvents.js +3 -3
- package/cjs/errors/errors.d.ts +3 -0
- package/cjs/errors/errors.js +8 -1
- package/cjs/events/AxisCameraStationEvents.d.ts +7 -4
- package/cjs/events/AxisCameraStationEvents.js +23 -18
- package/cjs/events/GenetecAgent.d.ts +6 -3
- package/cjs/events/GenetecAgent.js +30 -19
- package/cjs/index.d.ts +14 -1
- package/cjs/index.js +23 -2
- package/cjs/internal/Digest.js +6 -6
- package/cjs/internal/ProxyClient.d.ts +8 -9
- package/cjs/internal/ProxyClient.js +25 -29
- package/cjs/internal/types.d.ts +42 -0
- package/cjs/internal/types.js +2 -0
- package/cjs/internal/utils.d.ts +4 -1
- package/cjs/internal/utils.js +22 -3
- package/cjs/internal/versionCompare.d.ts +2 -2
- package/cjs/node/DefaultClient.d.ts +5 -6
- package/cjs/node/DefaultClient.js +12 -14
- package/cjs/node/HttpRequestSender.d.ts +1 -0
- package/cjs/node/HttpRequestSender.js +13 -3
- package/cjs/node/HttpServer.js +1 -1
- package/cjs/node/WsClient.d.ts +2 -1
- package/cjs/node/index.d.ts +2 -0
- package/cjs/node/index.js +18 -1
- package/cjs/types/CamOverlayAPI/CamOverlayAPI.d.ts +3071 -0
- package/cjs/types/CamOverlayAPI/CamOverlayAPI.js +127 -0
- package/cjs/types/CamOverlayAPI/accuweatherSchema.d.ts +114 -0
- package/cjs/types/CamOverlayAPI/accuweatherSchema.js +50 -0
- package/cjs/types/CamOverlayAPI/customGraphicsSchema.d.ts +783 -0
- package/cjs/types/CamOverlayAPI/customGraphicsSchema.js +75 -0
- package/cjs/types/CamOverlayAPI/imagesSchema.d.ts +122 -0
- package/cjs/types/CamOverlayAPI/imagesSchema.js +12 -0
- package/cjs/types/CamOverlayAPI/index.d.ts +9 -0
- package/cjs/types/CamOverlayAPI/index.js +25 -0
- package/cjs/types/CamOverlayAPI/infotickerSchema.d.ts +130 -0
- package/cjs/types/CamOverlayAPI/infotickerSchema.js +29 -0
- package/cjs/types/CamOverlayAPI/pipSchema.d.ts +166 -0
- package/cjs/types/CamOverlayAPI/pipSchema.js +42 -0
- package/cjs/types/CamOverlayAPI/ptzCompassSchema.d.ts +126 -0
- package/cjs/types/CamOverlayAPI/ptzCompassSchema.js +28 -0
- package/cjs/types/CamOverlayAPI/ptzSchema.d.ts +146 -0
- package/cjs/types/CamOverlayAPI/ptzSchema.js +15 -0
- package/cjs/types/CamOverlayAPI/screenSharingSchema.d.ts +79 -0
- package/cjs/types/CamOverlayAPI/screenSharingSchema.js +11 -0
- package/cjs/types/CamOverlayAPI/webCameraSharingSchema.d.ts +79 -0
- package/cjs/types/CamOverlayAPI/webCameraSharingSchema.js +11 -0
- package/cjs/types/CamOverlayDrawingAPI.d.ts +58 -0
- package/cjs/types/CamOverlayDrawingAPI.js +2 -0
- package/cjs/types/CamOverlayPainter.d.ts +74 -0
- package/cjs/types/CamOverlayPainter.js +2 -0
- package/cjs/types/CamScripterAPI.d.ts +82 -17
- package/cjs/types/CamScripterAPI.js +22 -7
- package/cjs/types/CamStreamerAPI.d.ts +16 -5
- package/cjs/types/CamStreamerAPI.js +5 -1
- package/cjs/types/CamSwitcherAPI.d.ts +4 -6
- package/cjs/types/CamSwitcherEvents.d.ts +77 -0
- package/cjs/types/CamSwitcherEvents.js +8 -0
- package/cjs/types/PlaneTrackerAPI.d.ts +8 -0
- package/cjs/types/PlaneTrackerAPI.js +2 -0
- package/cjs/types/VapixAPI.d.ts +635 -520
- package/cjs/types/VapixAPI.js +62 -24
- package/cjs/types/common.d.ts +14 -5
- package/cjs/web/DefaultClient.d.ts +5 -5
- package/cjs/web/DefaultClient.js +22 -10
- package/cjs/web/WsClient.js +2 -2
- package/esm/CamOverlayAPI.d.ts +48 -26
- package/esm/CamOverlayAPI.js +167 -84
- package/esm/CamOverlayDrawingAPI.d.ts +2 -47
- package/esm/CamOverlayDrawingAPI.js +6 -3
- package/esm/CamOverlayPainter/Frame.d.ts +8 -37
- package/esm/CamOverlayPainter/Frame.js +33 -0
- package/esm/CamOverlayPainter/Painter.d.ts +16 -10
- package/esm/CamOverlayPainter/Painter.js +5 -3
- package/esm/CamOverlayPainter/ResourceManager.d.ts +3 -2
- package/esm/CamOverlayPainter/ResourceManager.js +7 -11
- package/esm/CamScripterAPI.d.ts +34 -16
- package/esm/CamScripterAPI.js +46 -37
- package/esm/CamScripterAPICameraEventsGenerator.d.ts +1 -1
- package/esm/CamScripterAPICameraEventsGenerator.js +6 -3
- package/esm/CamStreamerAPI.d.ts +17 -14
- package/esm/CamStreamerAPI.js +32 -32
- package/esm/CamSwitcherAPI.d.ts +43 -37
- package/esm/CamSwitcherAPI.js +116 -107
- package/esm/CamSwitcherEvents.d.ts +1 -1
- package/esm/PlaneTrackerAPI.d.ts +43 -0
- package/esm/PlaneTrackerAPI.js +207 -0
- package/esm/VapixAPI.d.ts +56 -42
- package/esm/VapixAPI.js +297 -208
- package/esm/VapixEvents.d.ts +1 -1
- package/esm/VapixEvents.js +3 -3
- package/esm/errors/errors.d.ts +3 -0
- package/esm/errors/errors.js +6 -0
- package/esm/events/AxisCameraStationEvents.d.ts +7 -4
- package/esm/events/AxisCameraStationEvents.js +18 -13
- package/esm/events/GenetecAgent.d.ts +6 -3
- package/esm/events/GenetecAgent.js +20 -9
- package/esm/index.d.ts +14 -1
- package/esm/index.js +14 -1
- package/esm/internal/Digest.js +6 -6
- package/esm/internal/ProxyClient.d.ts +8 -9
- package/esm/internal/ProxyClient.js +25 -29
- package/esm/internal/types.d.ts +42 -0
- package/esm/internal/types.js +1 -0
- package/esm/internal/utils.d.ts +4 -1
- package/esm/internal/utils.js +17 -1
- package/esm/internal/versionCompare.d.ts +2 -2
- package/esm/node/DefaultClient.d.ts +5 -6
- package/esm/node/DefaultClient.js +12 -14
- package/esm/node/HttpRequestSender.d.ts +1 -0
- package/esm/node/HttpRequestSender.js +13 -3
- package/esm/node/HttpServer.js +1 -1
- package/esm/node/WsClient.d.ts +2 -1
- package/esm/node/index.d.ts +2 -0
- package/esm/node/index.js +2 -0
- package/esm/types/CamOverlayAPI/CamOverlayAPI.d.ts +3071 -0
- package/esm/types/CamOverlayAPI/CamOverlayAPI.js +124 -0
- package/esm/types/CamOverlayAPI/accuweatherSchema.d.ts +114 -0
- package/esm/types/CamOverlayAPI/accuweatherSchema.js +46 -0
- package/esm/types/CamOverlayAPI/customGraphicsSchema.d.ts +783 -0
- package/esm/types/CamOverlayAPI/customGraphicsSchema.js +71 -0
- package/esm/types/CamOverlayAPI/imagesSchema.d.ts +122 -0
- package/esm/types/CamOverlayAPI/imagesSchema.js +8 -0
- package/esm/types/CamOverlayAPI/index.d.ts +9 -0
- package/esm/types/CamOverlayAPI/index.js +9 -0
- package/esm/types/CamOverlayAPI/infotickerSchema.d.ts +130 -0
- package/esm/types/CamOverlayAPI/infotickerSchema.js +25 -0
- package/esm/types/CamOverlayAPI/pipSchema.d.ts +166 -0
- package/esm/types/CamOverlayAPI/pipSchema.js +38 -0
- package/esm/types/CamOverlayAPI/ptzCompassSchema.d.ts +126 -0
- package/esm/types/CamOverlayAPI/ptzCompassSchema.js +24 -0
- package/esm/types/CamOverlayAPI/ptzSchema.d.ts +146 -0
- package/esm/types/CamOverlayAPI/ptzSchema.js +11 -0
- package/esm/types/CamOverlayAPI/screenSharingSchema.d.ts +79 -0
- package/esm/types/CamOverlayAPI/screenSharingSchema.js +7 -0
- package/esm/types/CamOverlayAPI/webCameraSharingSchema.d.ts +79 -0
- package/esm/types/CamOverlayAPI/webCameraSharingSchema.js +7 -0
- package/esm/types/CamOverlayDrawingAPI.d.ts +58 -0
- package/esm/types/CamOverlayDrawingAPI.js +1 -0
- package/esm/types/CamOverlayPainter.d.ts +74 -0
- package/esm/types/CamOverlayPainter.js +1 -0
- package/esm/types/CamScripterAPI.d.ts +82 -17
- package/esm/types/CamScripterAPI.js +21 -6
- package/esm/types/CamStreamerAPI.d.ts +16 -5
- package/esm/types/CamStreamerAPI.js +4 -0
- package/esm/types/CamSwitcherAPI.d.ts +4 -6
- package/esm/types/CamSwitcherEvents.d.ts +77 -0
- package/esm/types/CamSwitcherEvents.js +8 -0
- package/esm/types/PlaneTrackerAPI.d.ts +8 -0
- package/esm/types/PlaneTrackerAPI.js +1 -0
- package/esm/types/VapixAPI.d.ts +635 -520
- package/esm/types/VapixAPI.js +61 -23
- package/esm/types/common.d.ts +14 -5
- package/esm/web/DefaultClient.d.ts +5 -5
- package/esm/web/DefaultClient.js +22 -10
- package/esm/web/WsClient.js +2 -2
- package/package.json +9 -8
- package/cjs/internal/common.d.ts +0 -39
- package/cjs/internal/common.js +0 -27
- package/cjs/node/WsEventClient.d.ts +0 -13
- package/cjs/node/WsEventClient.js +0 -22
- package/cjs/types/CamOverlayAPI.d.ts +0 -188
- package/cjs/types/CamOverlayAPI.js +0 -47
- package/esm/internal/common.d.ts +0 -39
- package/esm/internal/common.js +0 -20
- package/esm/node/WsEventClient.d.ts +0 -13
- package/esm/node/WsEventClient.js +0 -18
- package/esm/types/CamOverlayAPI.d.ts +0 -188
- package/esm/types/CamOverlayAPI.js +0 -44
package/esm/VapixAPI.js
CHANGED
|
@@ -1,55 +1,65 @@
|
|
|
1
1
|
import * as prettifyXml from 'prettify-xml';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { ApplicationAPIError, MaxFPSError, NoDeviceInfoError, SDCardActionError, SDCardJobError, } from './errors/errors';
|
|
2
|
+
import { arrayToUrl, isNullish, paramToUrl, responseStringify } from './internal/utils';
|
|
3
|
+
import { sdCardWatchedStatuses, APP_IDS, maxFpsResponseSchema, dateTimeinfoSchema, audioDeviceRequestSchema, audioSampleRatesResponseSchema, timeZoneSchema, getPortsResponseSchema, } from './types/VapixAPI';
|
|
4
|
+
import { ApplicationAPIError, MaxFPSError, NoDeviceInfoError, PtzNotSupportedError, SDCardActionError, SDCardJobError, } from './errors/errors';
|
|
6
5
|
import { ProxyClient } from './internal/ProxyClient';
|
|
7
|
-
import { arrayToUrl, paramToUrl } from './internal/utils';
|
|
8
6
|
import { z } from 'zod';
|
|
7
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
9
8
|
export class VapixAPI {
|
|
10
9
|
client;
|
|
11
|
-
constructor(client
|
|
12
|
-
this.client =
|
|
10
|
+
constructor(client) {
|
|
11
|
+
this.client = client;
|
|
13
12
|
}
|
|
14
|
-
|
|
13
|
+
getClient(proxyParams) {
|
|
14
|
+
return proxyParams ? new ProxyClient(this.client, proxyParams) : this.client;
|
|
15
|
+
}
|
|
16
|
+
async postUrlEncoded(path, parameters, headers, options) {
|
|
15
17
|
const data = paramToUrl(parameters);
|
|
16
18
|
const head = { ...headers, 'Content-Type': 'application/x-www-form-urlencoded' };
|
|
17
|
-
const
|
|
19
|
+
const agent = this.getClient(options?.proxyParams);
|
|
20
|
+
const res = await agent.post({ path, data, headers: head, timeout: options?.timeout });
|
|
18
21
|
if (!res.ok) {
|
|
19
22
|
throw new Error(await responseStringify(res));
|
|
20
23
|
}
|
|
21
24
|
return res;
|
|
22
25
|
}
|
|
23
|
-
async postJson(
|
|
26
|
+
async postJson(path, jsonData, headers, options) {
|
|
24
27
|
const data = JSON.stringify(jsonData);
|
|
25
28
|
const head = { ...headers, 'Content-Type': 'application/json' };
|
|
26
|
-
const
|
|
29
|
+
const agent = this.getClient(options?.proxyParams);
|
|
30
|
+
const res = await agent.post({ path, data, headers: head, timeout: options?.timeout });
|
|
27
31
|
if (!res.ok) {
|
|
28
32
|
throw new Error(await responseStringify(res));
|
|
29
33
|
}
|
|
30
34
|
return res;
|
|
31
35
|
}
|
|
32
|
-
async getCameraImage(
|
|
33
|
-
|
|
36
|
+
async getCameraImage(parameters, options) {
|
|
37
|
+
const agent = this.getClient(options?.proxyParams);
|
|
38
|
+
return await agent.get({ path: '/axis-cgi/jpg/image.cgi', parameters, timeout: options?.timeout });
|
|
34
39
|
}
|
|
35
|
-
async getEventDeclarations(
|
|
40
|
+
async getEventDeclarations(options) {
|
|
36
41
|
const data = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
|
|
37
42
|
'<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
|
|
38
43
|
'xmlns:xsd="http://www.w3.org/2001/XMLSchema">' +
|
|
39
44
|
'<GetEventInstances xmlns="http://www.axis.com/vapix/ws/event1"/>' +
|
|
40
45
|
'</s:Body>' +
|
|
41
46
|
'</s:Envelope>';
|
|
42
|
-
const
|
|
47
|
+
const agent = this.getClient(options?.proxyParams);
|
|
48
|
+
const res = await agent.post({
|
|
49
|
+
path: '/vapix/services',
|
|
50
|
+
data,
|
|
51
|
+
headers: { 'Content-Type': 'application/soap+xml' },
|
|
52
|
+
});
|
|
43
53
|
if (!res.ok) {
|
|
44
54
|
throw new Error(await responseStringify(res));
|
|
45
55
|
}
|
|
46
56
|
const declarations = await res.text();
|
|
47
57
|
return prettifyXml(declarations);
|
|
48
58
|
}
|
|
49
|
-
async getSupportedAudioSampleRate(
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const res = await this.postJson(
|
|
59
|
+
async getSupportedAudioSampleRate(options) {
|
|
60
|
+
const path = '/axis-cgi/audio/streamingcapabilities.cgi';
|
|
61
|
+
const jsonData = { apiVersion: '1.0', method: 'list' };
|
|
62
|
+
const res = await this.postJson(path, jsonData, undefined, options);
|
|
53
63
|
const encoders = audioSampleRatesResponseSchema.parse(await res.json()).data.encoders;
|
|
54
64
|
const data = encoders.aac ?? encoders.AAC ?? [];
|
|
55
65
|
return data.map((item) => {
|
|
@@ -59,7 +69,7 @@ export class VapixAPI {
|
|
|
59
69
|
};
|
|
60
70
|
});
|
|
61
71
|
}
|
|
62
|
-
async performAutofocus(
|
|
72
|
+
async performAutofocus(options) {
|
|
63
73
|
try {
|
|
64
74
|
const data = {
|
|
65
75
|
apiVersion: '1',
|
|
@@ -72,24 +82,26 @@ export class VapixAPI {
|
|
|
72
82
|
],
|
|
73
83
|
},
|
|
74
84
|
};
|
|
75
|
-
await this.postJson(
|
|
85
|
+
await this.postJson('/axis-cgi/opticscontrol.cgi', data, undefined, options);
|
|
76
86
|
}
|
|
77
87
|
catch (err) {
|
|
78
|
-
await this.
|
|
88
|
+
await this.postUrlEncoded('/axis-cgi/opticssetup.cgi', {
|
|
79
89
|
autofocus: 'perform',
|
|
80
90
|
source: '1',
|
|
81
|
-
});
|
|
91
|
+
}, undefined, options);
|
|
82
92
|
}
|
|
83
93
|
}
|
|
84
|
-
async checkSDCard(
|
|
85
|
-
const res = await this.
|
|
94
|
+
async checkSDCard(options) {
|
|
95
|
+
const res = await this.postUrlEncoded('/axis-cgi/disks/list.cgi', {
|
|
86
96
|
diskid: 'SD_DISK',
|
|
97
|
+
}, undefined, options);
|
|
98
|
+
const xmlText = await res.text();
|
|
99
|
+
const parser = new XMLParser({
|
|
100
|
+
ignoreAttributes: false,
|
|
101
|
+
attributeNamePrefix: '',
|
|
102
|
+
allowBooleanAttributes: true,
|
|
87
103
|
});
|
|
88
|
-
const result =
|
|
89
|
-
ignoreAttrs: false,
|
|
90
|
-
mergeAttrs: true,
|
|
91
|
-
explicitArray: false,
|
|
92
|
-
});
|
|
104
|
+
const result = parser.parse(xmlText);
|
|
93
105
|
const data = result.root.disks.disk;
|
|
94
106
|
return {
|
|
95
107
|
totalSize: parseInt(data.totalsize),
|
|
@@ -97,53 +109,56 @@ export class VapixAPI {
|
|
|
97
109
|
status: sdCardWatchedStatuses.includes(data.status) ? data.status : 'disconnected',
|
|
98
110
|
};
|
|
99
111
|
}
|
|
100
|
-
mountSDCard(
|
|
101
|
-
return this._doSDCardMountAction('MOUNT',
|
|
112
|
+
mountSDCard(options) {
|
|
113
|
+
return this._doSDCardMountAction('MOUNT', options);
|
|
102
114
|
}
|
|
103
|
-
unmountSDCard(
|
|
104
|
-
return this._doSDCardMountAction('UNMOUNT',
|
|
115
|
+
unmountSDCard(options) {
|
|
116
|
+
return this._doSDCardMountAction('UNMOUNT', options);
|
|
105
117
|
}
|
|
106
|
-
async _doSDCardMountAction(action,
|
|
107
|
-
const res = await this.
|
|
118
|
+
async _doSDCardMountAction(action, options) {
|
|
119
|
+
const res = await this.postUrlEncoded('/axis-cgi/disks/mount.cgi', {
|
|
108
120
|
action: action,
|
|
109
121
|
diskid: 'SD_DISK',
|
|
122
|
+
}, undefined, options);
|
|
123
|
+
const textXml = await res.text();
|
|
124
|
+
const parser = new XMLParser({
|
|
125
|
+
ignoreAttributes: false,
|
|
126
|
+
attributeNamePrefix: '',
|
|
127
|
+
allowBooleanAttributes: true,
|
|
110
128
|
});
|
|
111
|
-
const result =
|
|
112
|
-
ignoreAttrs: false,
|
|
113
|
-
mergeAttrs: true,
|
|
114
|
-
explicitArray: false,
|
|
115
|
-
});
|
|
129
|
+
const result = parser.parse(textXml);
|
|
116
130
|
const job = result.root.job;
|
|
117
131
|
if (job.result !== 'OK') {
|
|
118
132
|
throw new SDCardActionError(action, await responseStringify(res));
|
|
119
133
|
}
|
|
120
134
|
return Number(job.jobid);
|
|
121
135
|
}
|
|
122
|
-
async fetchSDCardJobProgress(jobId,
|
|
123
|
-
const res = await this.
|
|
136
|
+
async fetchSDCardJobProgress(jobId, options) {
|
|
137
|
+
const res = await this.postUrlEncoded('/disks/job.cgi', {
|
|
124
138
|
jobid: String(jobId),
|
|
125
139
|
diskid: 'SD_DISK',
|
|
140
|
+
}, undefined, options);
|
|
141
|
+
const textXml = await res.text();
|
|
142
|
+
const parser = new XMLParser({
|
|
143
|
+
ignoreAttributes: false,
|
|
144
|
+
attributeNamePrefix: '',
|
|
145
|
+
allowBooleanAttributes: true,
|
|
126
146
|
});
|
|
127
|
-
const
|
|
128
|
-
ignoreAttrs: false,
|
|
129
|
-
mergeAttrs: true,
|
|
130
|
-
explicitArray: false,
|
|
131
|
-
});
|
|
132
|
-
const job = result.root.job;
|
|
147
|
+
const job = parser.parse(textXml).root.job;
|
|
133
148
|
if (job.result !== 'OK') {
|
|
134
149
|
throw new SDCardJobError();
|
|
135
150
|
}
|
|
136
151
|
return Number(job.progress);
|
|
137
152
|
}
|
|
138
|
-
downloadCameraReport(
|
|
139
|
-
return this.
|
|
153
|
+
downloadCameraReport(options) {
|
|
154
|
+
return this.postUrlEncoded('/axis-cgi/serverreport.cgi', { mode: 'text' }, undefined, options);
|
|
140
155
|
}
|
|
141
|
-
getSystemLog(
|
|
142
|
-
return this.
|
|
156
|
+
getSystemLog(options) {
|
|
157
|
+
return this.postUrlEncoded('/axis-cgi/admin/systemlog.cgi', undefined, undefined, options);
|
|
143
158
|
}
|
|
144
|
-
async getMaxFps(channel,
|
|
159
|
+
async getMaxFps(channel, options) {
|
|
145
160
|
const data = { apiVersion: '1.0', method: 'getCaptureModes' };
|
|
146
|
-
const res = await this.postJson(
|
|
161
|
+
const res = await this.postJson('/axis-cgi/capturemode.cgi', data, undefined, options);
|
|
147
162
|
const response = maxFpsResponseSchema.parse(await res.json());
|
|
148
163
|
const channels = response.data;
|
|
149
164
|
if (channels === undefined) {
|
|
@@ -163,77 +178,92 @@ export class VapixAPI {
|
|
|
163
178
|
}
|
|
164
179
|
return captureMode.maxFPS;
|
|
165
180
|
}
|
|
166
|
-
async getTimezone(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
181
|
+
async getTimezone(options) {
|
|
182
|
+
try {
|
|
183
|
+
const agent = this.getClient(options?.proxyParams);
|
|
184
|
+
const resV2 = await agent.get({ path: '/config/rest/time/v2/timeZone', timeout: options?.timeout });
|
|
185
|
+
if (!resV2.ok) {
|
|
186
|
+
throw new Error(await responseStringify(resV2));
|
|
187
|
+
}
|
|
188
|
+
const json = await resV2.json();
|
|
189
|
+
const data = timeZoneSchema.parse(json);
|
|
190
|
+
if (data.status === 'error') {
|
|
191
|
+
throw new Error(data.error.message);
|
|
192
|
+
}
|
|
193
|
+
return data.data.activeTimeZone;
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
console.warn('Failed to fetch time zone data from time API v2:', error instanceof Error ? error.message : JSON.stringify(error));
|
|
197
|
+
console.warn('Falling back to deprecated time API v1');
|
|
198
|
+
}
|
|
199
|
+
const data = await this.getDateTimeInfo(options);
|
|
200
|
+
if (data.data.timeZone === undefined) {
|
|
201
|
+
throw new Error('Time zone not setup on the device');
|
|
202
|
+
}
|
|
203
|
+
return data.data.timeZone;
|
|
170
204
|
}
|
|
171
|
-
async getDateTimeInfo(
|
|
205
|
+
async getDateTimeInfo(options) {
|
|
172
206
|
const data = { apiVersion: '1.0', method: 'getDateTimeInfo' };
|
|
173
|
-
const res = await this.postJson(
|
|
207
|
+
const res = await this.postJson('/axis-cgi/time.cgi', data, undefined, options);
|
|
174
208
|
return dateTimeinfoSchema.parse(await res.json());
|
|
175
209
|
}
|
|
176
|
-
async getDevicesSettings(
|
|
210
|
+
async getDevicesSettings(options) {
|
|
177
211
|
const data = { apiVersion: '1.0', method: 'getDevicesSettings' };
|
|
178
|
-
const res = await this.postJson(
|
|
212
|
+
const res = await this.postJson('/axis-cgi/audiodevicecontrol.cgi', data, undefined, options);
|
|
179
213
|
const result = audioDeviceRequestSchema.parse(await res.json());
|
|
180
|
-
return result.devices.map((device) => ({
|
|
214
|
+
return result.data.devices.map((device) => ({
|
|
181
215
|
...device,
|
|
182
216
|
inputs: (device.inputs || []).sort((a, b) => a.id.localeCompare(b.id)),
|
|
183
217
|
outputs: (device.outputs || []).sort((a, b) => a.id.localeCompare(b.id)),
|
|
184
218
|
}));
|
|
185
219
|
}
|
|
186
|
-
async fetchRemoteDeviceInfo(payload,
|
|
187
|
-
const res = await this.postJson(
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
mergeAttrs: true,
|
|
191
|
-
explicitArray: false,
|
|
192
|
-
});
|
|
193
|
-
if (isNullish(result.body.data)) {
|
|
220
|
+
async fetchRemoteDeviceInfo(payload, options) {
|
|
221
|
+
const res = await this.postJson('/axis-cgi/basicdeviceinfo.cgi', payload, undefined, options);
|
|
222
|
+
const json = await res.json();
|
|
223
|
+
if (isNullish(json.data)) {
|
|
194
224
|
throw new NoDeviceInfoError();
|
|
195
225
|
}
|
|
196
|
-
return
|
|
226
|
+
return json.data;
|
|
197
227
|
}
|
|
198
|
-
async getHeaders(
|
|
228
|
+
async getHeaders(options) {
|
|
199
229
|
const data = { apiVersion: '1.0', method: 'list' };
|
|
200
|
-
const res = await this.postJson(
|
|
230
|
+
const res = await this.postJson('/axis-cgi/customhttpheader.cgi', data, undefined, options);
|
|
201
231
|
return z.object({ data: z.record(z.string()) }).parse(await res.json()).data;
|
|
202
232
|
}
|
|
203
|
-
async setHeaders(headers,
|
|
233
|
+
async setHeaders(headers, options) {
|
|
204
234
|
const data = { apiVersion: '1.0', method: 'set', params: headers };
|
|
205
|
-
return this.postJson(
|
|
235
|
+
return this.postJson('/axis-cgi/customhttpheader.cgi', data, undefined, options);
|
|
206
236
|
}
|
|
207
|
-
async getParameter(paramNames,
|
|
208
|
-
const response = await this.
|
|
237
|
+
async getParameter(paramNames, options) {
|
|
238
|
+
const response = await this.postUrlEncoded('/axis-cgi/param.cgi', {
|
|
209
239
|
action: 'list',
|
|
210
240
|
group: arrayToUrl(paramNames),
|
|
211
|
-
});
|
|
212
|
-
return parseParameters(await response.text());
|
|
241
|
+
}, undefined, options);
|
|
242
|
+
return VapixAPI.parseParameters(await response.text());
|
|
213
243
|
}
|
|
214
|
-
async setParameter(params,
|
|
215
|
-
const res = await this.
|
|
244
|
+
async setParameter(params, options) {
|
|
245
|
+
const res = await this.postUrlEncoded('/axis-cgi/param.cgi', {
|
|
216
246
|
...params,
|
|
217
247
|
action: 'update',
|
|
218
|
-
});
|
|
248
|
+
}, undefined, options);
|
|
219
249
|
const responseText = await res.text();
|
|
220
250
|
if (responseText.startsWith('# Error')) {
|
|
221
251
|
throw new Error(responseText);
|
|
222
252
|
}
|
|
223
253
|
return true;
|
|
224
254
|
}
|
|
225
|
-
async getGuardTourList(
|
|
255
|
+
async getGuardTourList(options) {
|
|
226
256
|
const gTourList = new Array();
|
|
227
|
-
const response = await this.getParameter('GuardTour',
|
|
257
|
+
const response = await this.getParameter('GuardTour', options);
|
|
228
258
|
for (let i = 0; i < 20; i++) {
|
|
229
259
|
const gTourBaseName = 'root.GuardTour.G' + i;
|
|
230
260
|
if (gTourBaseName + '.CamNbr' in response) {
|
|
231
261
|
const gTour = {
|
|
232
262
|
id: gTourBaseName,
|
|
233
263
|
camNbr: response[gTourBaseName + '.CamNbr'],
|
|
234
|
-
name: response[gTourBaseName + '.Name'],
|
|
264
|
+
name: response[gTourBaseName + '.Name'] ?? 'Guard Tour ' + (i + 1),
|
|
235
265
|
randomEnabled: response[gTourBaseName + '.RandomEnabled'],
|
|
236
|
-
running: response[gTourBaseName + '.Running'],
|
|
266
|
+
running: response[gTourBaseName + '.Running'] ?? 'no',
|
|
237
267
|
timeBetweenSequences: response[gTourBaseName + '.TimeBetweenSequences'],
|
|
238
268
|
tour: [],
|
|
239
269
|
};
|
|
@@ -258,16 +288,16 @@ export class VapixAPI {
|
|
|
258
288
|
}
|
|
259
289
|
return gTourList;
|
|
260
290
|
}
|
|
261
|
-
setGuardTourEnabled(guardTourID, enable,
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
return this.setParameter(
|
|
291
|
+
setGuardTourEnabled(guardTourID, enable, options) {
|
|
292
|
+
const params = {};
|
|
293
|
+
params[guardTourID + '.Running'] = enable ? 'yes' : 'no';
|
|
294
|
+
return this.setParameter(params, options);
|
|
265
295
|
}
|
|
266
|
-
async getPTZPresetList(channel,
|
|
267
|
-
const res = await this.
|
|
296
|
+
async getPTZPresetList(channel, options) {
|
|
297
|
+
const res = await this.postUrlEncoded('/axis-cgi/com/ptz.cgi', {
|
|
268
298
|
query: 'presetposcam',
|
|
269
299
|
camera: channel.toString(),
|
|
270
|
-
});
|
|
300
|
+
}, undefined, options);
|
|
271
301
|
const text = await res.text();
|
|
272
302
|
const lines = text.split(/[\r\n]/);
|
|
273
303
|
const positions = [];
|
|
@@ -282,100 +312,159 @@ export class VapixAPI {
|
|
|
282
312
|
}
|
|
283
313
|
return positions;
|
|
284
314
|
}
|
|
285
|
-
async listPTZ(camera,
|
|
315
|
+
async listPTZ(camera, options) {
|
|
286
316
|
const url = `/axis-cgi/com/ptz.cgi`;
|
|
287
|
-
const response = await this.
|
|
317
|
+
const response = await this.postUrlEncoded(url, {
|
|
288
318
|
camera,
|
|
289
319
|
query: 'presetposcamdata',
|
|
290
320
|
format: 'json',
|
|
291
|
-
});
|
|
292
|
-
|
|
321
|
+
}, undefined, options);
|
|
322
|
+
const text = await response.text();
|
|
323
|
+
if (text === '') {
|
|
324
|
+
throw new PtzNotSupportedError();
|
|
325
|
+
}
|
|
326
|
+
return VapixAPI.parseCameraPtzResponse(text)[camera] ?? [];
|
|
293
327
|
}
|
|
294
|
-
async listPtzVideoSourceOverview(
|
|
295
|
-
const response = await this.
|
|
328
|
+
async listPtzVideoSourceOverview(options) {
|
|
329
|
+
const response = await this.postUrlEncoded('/axis-cgi/com/ptz.cgi', {
|
|
296
330
|
query: 'presetposall',
|
|
297
331
|
format: 'json',
|
|
298
|
-
});
|
|
299
|
-
const
|
|
332
|
+
}, undefined, options);
|
|
333
|
+
const text = await response.text();
|
|
334
|
+
if (text === '') {
|
|
335
|
+
throw new PtzNotSupportedError();
|
|
336
|
+
}
|
|
337
|
+
const data = VapixAPI.parseCameraPtzResponse(text);
|
|
300
338
|
const res = {};
|
|
301
|
-
Object.keys(data)
|
|
302
|
-
|
|
339
|
+
Object.keys(data)
|
|
340
|
+
.map(Number)
|
|
341
|
+
.forEach((camera) => {
|
|
342
|
+
if (data[camera] !== undefined) {
|
|
343
|
+
res[camera - 1] = data[camera]?.map(({ data: itemData, ...d }) => d);
|
|
344
|
+
}
|
|
303
345
|
});
|
|
304
346
|
return res;
|
|
305
347
|
}
|
|
306
|
-
goToPreset(channel, presetName,
|
|
307
|
-
return this.
|
|
348
|
+
goToPreset(channel, presetName, options) {
|
|
349
|
+
return this.postUrlEncoded('/axis-cgi/com/ptz.cgi', {
|
|
308
350
|
camera: channel.toString(),
|
|
309
351
|
gotoserverpresetname: presetName,
|
|
310
|
-
});
|
|
352
|
+
}, undefined, options);
|
|
311
353
|
}
|
|
312
|
-
async getPtzPosition(camera,
|
|
313
|
-
const res = await this.
|
|
354
|
+
async getPtzPosition(camera, options) {
|
|
355
|
+
const res = await this.postUrlEncoded('/axis-cgi/com/ptz.cgi', {
|
|
314
356
|
query: 'position',
|
|
315
357
|
camera: camera.toString(),
|
|
316
|
-
});
|
|
317
|
-
const params = parseParameters(await res.text());
|
|
358
|
+
}, undefined, options);
|
|
359
|
+
const params = VapixAPI.parseParameters(await res.text());
|
|
318
360
|
return {
|
|
319
361
|
pan: Number(params.pan),
|
|
320
362
|
tilt: Number(params.tilt),
|
|
321
363
|
zoom: Number(params.zoom),
|
|
322
364
|
};
|
|
323
365
|
}
|
|
324
|
-
async
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
366
|
+
async getPorts(options) {
|
|
367
|
+
const res = await this.postJson('/axis-cgi/io/portmanagement.cgi', {
|
|
368
|
+
apiVersion: '1.0',
|
|
369
|
+
context: '',
|
|
370
|
+
method: 'getPorts',
|
|
371
|
+
}, undefined, options);
|
|
372
|
+
const portResponseParsed = await getPortsResponseSchema.parse(res.json());
|
|
373
|
+
return portResponseParsed.data.items;
|
|
374
|
+
}
|
|
375
|
+
async setPorts(ports, options) {
|
|
376
|
+
await this.postJson('/axis-cgi/io/portmanagement.cgi', {
|
|
377
|
+
apiVersion: '1.0',
|
|
378
|
+
context: '',
|
|
379
|
+
method: 'setPorts',
|
|
380
|
+
params: { ports },
|
|
381
|
+
}, undefined, options);
|
|
382
|
+
}
|
|
383
|
+
async setPortStateSequence(port, sequence, options) {
|
|
384
|
+
await this.postJson('/axis-cgi/io/portmanagement.cgi', {
|
|
385
|
+
apiVersion: '1.0',
|
|
386
|
+
context: '',
|
|
387
|
+
method: 'setStateSequence',
|
|
388
|
+
params: { port, sequence },
|
|
389
|
+
}, undefined, options);
|
|
390
|
+
}
|
|
391
|
+
async getApplicationList(options) {
|
|
392
|
+
const agent = this.getClient(options?.proxyParams);
|
|
393
|
+
const res = await agent.get({ path: '/axis-cgi/applications/list.cgi', timeout: options?.timeout });
|
|
333
394
|
const xml = await res.text();
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
395
|
+
const parser = new XMLParser({
|
|
396
|
+
ignoreAttributes: false,
|
|
397
|
+
attributeNamePrefix: '',
|
|
398
|
+
allowBooleanAttributes: true,
|
|
399
|
+
});
|
|
400
|
+
const result = parser.parse(xml);
|
|
401
|
+
let apps = result.reply.application ?? [];
|
|
402
|
+
if (!Array.isArray(apps)) {
|
|
403
|
+
apps = [apps];
|
|
341
404
|
}
|
|
342
|
-
return apps
|
|
405
|
+
return apps.map((app) => {
|
|
406
|
+
return {
|
|
407
|
+
...app,
|
|
408
|
+
appId: APP_IDS.find((id) => id.toLowerCase() === app.Name.toLowerCase()) ?? null,
|
|
409
|
+
};
|
|
410
|
+
});
|
|
343
411
|
}
|
|
344
|
-
async startApplication(applicationID,
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
412
|
+
async startApplication(applicationID, options) {
|
|
413
|
+
const agent = this.getClient(options?.proxyParams);
|
|
414
|
+
const res = await agent.get({
|
|
415
|
+
path: '/axis-cgi/applications/control.cgi',
|
|
416
|
+
parameters: {
|
|
417
|
+
package: applicationID.toLowerCase(),
|
|
418
|
+
action: 'start',
|
|
419
|
+
},
|
|
420
|
+
timeout: options?.timeout,
|
|
348
421
|
});
|
|
349
422
|
const text = (await res.text()).trim().toLowerCase();
|
|
350
423
|
if (text !== 'ok' && !(text.startsWith('error:') && text.substring(7) === '6')) {
|
|
351
424
|
throw new ApplicationAPIError('START', await responseStringify(res));
|
|
352
425
|
}
|
|
353
426
|
}
|
|
354
|
-
async restartApplication(applicationID,
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
427
|
+
async restartApplication(applicationID, options) {
|
|
428
|
+
const agent = this.getClient(options?.proxyParams);
|
|
429
|
+
const res = await agent.get({
|
|
430
|
+
path: '/axis-cgi/applications/control.cgi',
|
|
431
|
+
parameters: {
|
|
432
|
+
package: applicationID.toLowerCase(),
|
|
433
|
+
action: 'restart',
|
|
434
|
+
},
|
|
435
|
+
timeout: options?.timeout,
|
|
358
436
|
});
|
|
359
437
|
const text = (await res.text()).trim().toLowerCase();
|
|
360
438
|
if (text !== 'ok') {
|
|
361
439
|
throw new ApplicationAPIError('RESTART', await responseStringify(res));
|
|
362
440
|
}
|
|
363
441
|
}
|
|
364
|
-
async stopApplication(applicationID,
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
442
|
+
async stopApplication(applicationID, options) {
|
|
443
|
+
const agent = this.getClient(options?.proxyParams);
|
|
444
|
+
const res = await agent.get({
|
|
445
|
+
path: '/axis-cgi/applications/control.cgi',
|
|
446
|
+
parameters: {
|
|
447
|
+
package: applicationID.toLowerCase(),
|
|
448
|
+
action: 'stop',
|
|
449
|
+
},
|
|
450
|
+
timeout: options?.timeout,
|
|
368
451
|
});
|
|
369
452
|
const text = (await res.text()).trim().toLowerCase();
|
|
370
453
|
if (text !== 'ok' && !(text.startsWith('error:') && text.substring(7) === '6')) {
|
|
371
454
|
throw new ApplicationAPIError('STOP', await responseStringify(res));
|
|
372
455
|
}
|
|
373
456
|
}
|
|
374
|
-
async installApplication(data, fileName) {
|
|
457
|
+
async installApplication(data, fileName, options) {
|
|
375
458
|
const formData = new FormData();
|
|
376
459
|
formData.append('packfil', data, fileName);
|
|
377
|
-
const
|
|
378
|
-
|
|
460
|
+
const agent = this.getClient(options?.proxyParams);
|
|
461
|
+
const res = await agent.post({
|
|
462
|
+
path: '/axis-cgi/applications/upload.cgi',
|
|
463
|
+
data: formData,
|
|
464
|
+
headers: {
|
|
465
|
+
contentType: 'application/octet-stream',
|
|
466
|
+
},
|
|
467
|
+
timeout: options?.timeout ?? 120000,
|
|
379
468
|
});
|
|
380
469
|
if (!res.ok) {
|
|
381
470
|
throw new Error(await responseStringify(res));
|
|
@@ -385,70 +474,70 @@ export class VapixAPI {
|
|
|
385
474
|
throw new Error('installing error: ' + text);
|
|
386
475
|
}
|
|
387
476
|
}
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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;
|
|
477
|
+
static parseParameters = (response) => {
|
|
478
|
+
const params = {};
|
|
479
|
+
const lines = response.split(/[\r\n]/);
|
|
480
|
+
for (const line of lines) {
|
|
481
|
+
if (line.length === 0 || line.substring(0, 7) === '# Error') {
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
const delimiterPos = line.indexOf('=');
|
|
485
|
+
if (delimiterPos !== -1) {
|
|
486
|
+
const paramName = line.substring(0, delimiterPos).replace('root.', '');
|
|
487
|
+
const paramValue = line.substring(delimiterPos + 1);
|
|
488
|
+
params[paramName] = paramValue;
|
|
489
|
+
}
|
|
432
490
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
491
|
+
return params;
|
|
492
|
+
};
|
|
493
|
+
static parseCameraPtzResponse = (response) => {
|
|
494
|
+
const json = JSON.parse(response);
|
|
495
|
+
const parsed = {};
|
|
496
|
+
Object.keys(json).forEach((key) => {
|
|
497
|
+
if (!key.startsWith('Camera ')) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const camera = Number(key.replace('Camera ', ''));
|
|
501
|
+
if (json[key].presets !== undefined) {
|
|
502
|
+
parsed[camera] = VapixAPI.parsePtz(json[key].presets);
|
|
440
503
|
}
|
|
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
504
|
});
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
505
|
+
return parsed;
|
|
506
|
+
};
|
|
507
|
+
static parsePtz = (parsed) => {
|
|
508
|
+
const res = [];
|
|
509
|
+
parsed.forEach((value) => {
|
|
510
|
+
const delimiterPos = value.indexOf('=');
|
|
511
|
+
if (delimiterPos === -1) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (!value.startsWith('presetposno')) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const id = Number(value.substring(11, delimiterPos));
|
|
518
|
+
if (Number.isNaN(id)) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const data = value.substring(delimiterPos + 1).split(':');
|
|
522
|
+
const getValue = (valueName) => {
|
|
523
|
+
for (const d of data) {
|
|
524
|
+
const p = d.split('=');
|
|
525
|
+
if (p[0] === valueName) {
|
|
526
|
+
return Number(p[1]);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return 0;
|
|
530
|
+
};
|
|
531
|
+
res.push({
|
|
532
|
+
id,
|
|
533
|
+
name: data[0] ?? 'Preset ' + id,
|
|
534
|
+
data: {
|
|
535
|
+
pan: getValue('pan'),
|
|
536
|
+
tilt: getValue('tilt'),
|
|
537
|
+
zoom: getValue('zoom'),
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
return res;
|
|
542
|
+
};
|
|
543
|
+
}
|