camstreamerlib 4.0.0-beta.3 → 4.0.0-beta.30

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