@camstack/addon-provider-amcrest 0.2.113 → 0.2.115

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/dist/addon.js CHANGED
@@ -5367,7 +5367,7 @@ var ZodIssueCode = {
5367
5367
  var ZodFirstPartyTypeKind;
5368
5368
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5369
5369
  //#endregion
5370
- //#region ../types/dist/sleep-B1y0Fo1U.mjs
5370
+ //#region ../types/dist/sleep-Dk-BdnBf.mjs
5371
5371
  /**
5372
5372
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5373
5373
  * window to float samples (D455).
@@ -31222,6 +31222,26 @@ var PtzHomePresetSchema = object({
31222
31222
  "none"
31223
31223
  ])
31224
31224
  });
31225
+ /**
31226
+ * The camera's own idle-return behaviour: whether it goes home by itself, and
31227
+ * after how long.
31228
+ *
31229
+ * ONE shape, not two scalars, because on the camera they are ONE fact —
31230
+ * Reolink's guard point carries `benable` and `timeout` in the same element,
31231
+ * written by the same command. Reading them apart means two round-trips that
31232
+ * can disagree.
31233
+ *
31234
+ * `enabled` and `seconds` are INDEPENDENT: a camera can hold a 68 s delay with
31235
+ * the behaviour switched off, which is exactly what the vendor app leaves when
31236
+ * you turn the guard point off without clearing it. The first version of this
31237
+ * cap reported only the delay, so that state was invisible — a camera could be
31238
+ * configured to return after 68 seconds with no way to say whether it should
31239
+ * return at all.
31240
+ */
31241
+ var PtzHomeReturnSchema = object({
31242
+ enabled: boolean(),
31243
+ seconds: number().int()
31244
+ });
31225
31245
  var ptzCapability = {
31226
31246
  name: "ptz",
31227
31247
  scope: "device",
@@ -31281,6 +31301,54 @@ var ptzCapability = {
31281
31301
  auth: "admin"
31282
31302
  }),
31283
31303
  /**
31304
+ * Make WHERE THE HEAD IS POINTING RIGHT NOW this camera's home.
31305
+ *
31306
+ * `setHomePreset` takes a preset id, and a preset id is the wrong shape for
31307
+ * the most natural thing an operator does: aim the camera, then say "here".
31308
+ * It is also the only shape a camera whose home is NOT a preset can accept
31309
+ * -- Reolink's home is a guard point, a stored position with no id at all,
31310
+ * and hiding the control for that case (as the UI briefly did) left the one
31311
+ * vendor with a native home unable to set it.
31312
+ *
31313
+ * Each provider does it its own way:
31314
+ * reolink `setGrd` with `needSetPos`, pinning the guard point here
31315
+ * hikvision / amcrest save a preset, then pin it as home
31316
+ * onvif REFUSES -- its home is the PTZ origin and cannot be moved
31317
+ *
31318
+ * A provider that cannot honour it throws rather than silently doing
31319
+ * nothing: an operator who pressed "set home here" and saw no error would
31320
+ * believe it took.
31321
+ */
31322
+ captureHomeHere: method(object({ deviceId: number() }), _void(), {
31323
+ kind: "mutation",
31324
+ auth: "admin"
31325
+ }),
31326
+ /**
31327
+ * Whether the camera returns home on its own when left idle, and after how
31328
+ * long. `null` when it has no such behaviour at all.
31329
+ *
31330
+ * Reolink's guard point carries one (`timeout`, freely settable in
31331
+ * seconds). It is a real function of the camera that we did not expose at
31332
+ * all, and it belongs next to the home it governs rather than in a vendor
31333
+ * settings page nobody associates with Home.
31334
+ */
31335
+ getHomeReturn: method(object({ deviceId: number() }), PtzHomeReturnSchema.nullable()),
31336
+ /**
31337
+ * Set both together. Refused by a camera with no idle-return behaviour.
31338
+ *
31339
+ * They travel together for the same reason they are read together: the
31340
+ * underlying write carries both, so setting one alone means reading the
31341
+ * other back first and racing whoever else is writing.
31342
+ */
31343
+ setHomeReturn: method(object({
31344
+ deviceId: number(),
31345
+ enabled: boolean(),
31346
+ seconds: number().int().min(0).max(3600)
31347
+ }), _void(), {
31348
+ kind: "mutation",
31349
+ auth: "admin"
31350
+ }),
31351
+ /**
31284
31352
  * Pull the current PTZ position. Redundant with the auto-injected
31285
31353
  * `getStatus` method (see `status` below); kept for callers that
31286
31354
  * haven't migrated. Will be folded into `getStatus` in a future
@@ -40941,6 +41009,12 @@ Object.freeze({
40941
41009
  addonId: null,
40942
41010
  access: "create"
40943
41011
  },
41012
+ "ptz.captureHomeHere": {
41013
+ capName: "ptz",
41014
+ capScope: "device",
41015
+ addonId: null,
41016
+ access: "create"
41017
+ },
40944
41018
  "ptz.continuousMove": {
40945
41019
  capName: "ptz",
40946
41020
  capScope: "device",
@@ -40959,6 +41033,12 @@ Object.freeze({
40959
41033
  addonId: null,
40960
41034
  access: "view"
40961
41035
  },
41036
+ "ptz.getHomeReturn": {
41037
+ capName: "ptz",
41038
+ capScope: "device",
41039
+ addonId: null,
41040
+ access: "view"
41041
+ },
40962
41042
  "ptz.getOptions": {
40963
41043
  capName: "ptz",
40964
41044
  capScope: "device",
@@ -41013,6 +41093,12 @@ Object.freeze({
41013
41093
  addonId: null,
41014
41094
  access: "create"
41015
41095
  },
41096
+ "ptz.setHomeReturn": {
41097
+ capName: "ptz",
41098
+ capScope: "device",
41099
+ addonId: null,
41100
+ access: "create"
41101
+ },
41016
41102
  "ptz.stop": {
41017
41103
  capName: "ptz",
41018
41104
  capScope: "device",
@@ -44176,6 +44262,11 @@ Object.freeze({
44176
44262
  form: "single",
44177
44263
  optional: false
44178
44264
  }],
44265
+ "ptz.captureHomeHere": [{
44266
+ name: "deviceId",
44267
+ form: "single",
44268
+ optional: false
44269
+ }],
44179
44270
  "ptz.continuousMove": [{
44180
44271
  name: "deviceId",
44181
44272
  form: "single",
@@ -44191,6 +44282,11 @@ Object.freeze({
44191
44282
  form: "single",
44192
44283
  optional: false
44193
44284
  }],
44285
+ "ptz.getHomeReturn": [{
44286
+ name: "deviceId",
44287
+ form: "single",
44288
+ optional: false
44289
+ }],
44194
44290
  "ptz.getOptions": [{
44195
44291
  name: "deviceId",
44196
44292
  form: "single",
@@ -44236,6 +44332,11 @@ Object.freeze({
44236
44332
  form: "single",
44237
44333
  optional: false
44238
44334
  }],
44335
+ "ptz.setHomeReturn": [{
44336
+ name: "deviceId",
44337
+ form: "single",
44338
+ optional: false
44339
+ }],
44239
44340
  "ptz.stop": [{
44240
44341
  name: "deviceId",
44241
44342
  form: "single",
@@ -46959,6 +47060,8 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
46959
47060
  * granularity is reported through `getOptions` instead of being hidden
46960
47061
  * behind a 0..1 float.
46961
47062
  */
47063
+ /** Dahua exposes 25 fixed preset slots, 1-based. */
47064
+ static DAHUA_MAX_PRESETS = 25;
46962
47065
  static DAHUA_SPEED_STEPS = 8;
46963
47066
  /**
46964
47067
  * How long one `ptz.move` pulse runs, ms. The DENOMINATOR of "how far did
@@ -47162,6 +47265,27 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
47162
47265
  };
47163
47266
  return this.resolveHomePreset();
47164
47267
  },
47268
+ captureHomeHere: async ({ deviceId }) => {
47269
+ if (deviceId !== this.id) return;
47270
+ const client = this.ensureClient();
47271
+ const ceiling = (await client.getPtzCaps(this.channel))?.maxPresets ?? AmcrestCamera.DAHUA_MAX_PRESETS;
47272
+ const used = new Set((await client.getPtzPresets(this.channel)).map((p) => p.id));
47273
+ let slot = 1;
47274
+ while (used.has(slot) && slot <= ceiling) slot += 1;
47275
+ if (slot > ceiling) throw new Error("No free preset slot on this camera to store a home position — free one first");
47276
+ await client.ptzSetPreset(this.channel, slot, AmcrestCamera.PTZ_CGI_TIMEOUT_MS);
47277
+ await this.config.setAll({ homePresetId: String(slot) });
47278
+ this.ctx.logger.info("amcrest: home pinned to the current position", {
47279
+ tags: { deviceId: this.id },
47280
+ meta: { presetId: slot }
47281
+ });
47282
+ },
47283
+ getHomeReturn: async () => {
47284
+ return null;
47285
+ },
47286
+ setHomeReturn: async () => {
47287
+ throw new Error("Amcrest: the idle-return timer is not wired on this driver");
47288
+ },
47165
47289
  setHomePreset: async ({ deviceId, presetId }) => {
47166
47290
  if (deviceId !== this.id) return;
47167
47291
  await this.config.setAll({ homePresetId: presetId ?? "" });
package/dist/addon.mjs CHANGED
@@ -5368,7 +5368,7 @@ var ZodIssueCode = {
5368
5368
  var ZodFirstPartyTypeKind;
5369
5369
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5370
5370
  //#endregion
5371
- //#region ../types/dist/sleep-B1y0Fo1U.mjs
5371
+ //#region ../types/dist/sleep-Dk-BdnBf.mjs
5372
5372
  /**
5373
5373
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5374
5374
  * window to float samples (D455).
@@ -31223,6 +31223,26 @@ var PtzHomePresetSchema = object({
31223
31223
  "none"
31224
31224
  ])
31225
31225
  });
31226
+ /**
31227
+ * The camera's own idle-return behaviour: whether it goes home by itself, and
31228
+ * after how long.
31229
+ *
31230
+ * ONE shape, not two scalars, because on the camera they are ONE fact —
31231
+ * Reolink's guard point carries `benable` and `timeout` in the same element,
31232
+ * written by the same command. Reading them apart means two round-trips that
31233
+ * can disagree.
31234
+ *
31235
+ * `enabled` and `seconds` are INDEPENDENT: a camera can hold a 68 s delay with
31236
+ * the behaviour switched off, which is exactly what the vendor app leaves when
31237
+ * you turn the guard point off without clearing it. The first version of this
31238
+ * cap reported only the delay, so that state was invisible — a camera could be
31239
+ * configured to return after 68 seconds with no way to say whether it should
31240
+ * return at all.
31241
+ */
31242
+ var PtzHomeReturnSchema = object({
31243
+ enabled: boolean(),
31244
+ seconds: number().int()
31245
+ });
31226
31246
  var ptzCapability = {
31227
31247
  name: "ptz",
31228
31248
  scope: "device",
@@ -31282,6 +31302,54 @@ var ptzCapability = {
31282
31302
  auth: "admin"
31283
31303
  }),
31284
31304
  /**
31305
+ * Make WHERE THE HEAD IS POINTING RIGHT NOW this camera's home.
31306
+ *
31307
+ * `setHomePreset` takes a preset id, and a preset id is the wrong shape for
31308
+ * the most natural thing an operator does: aim the camera, then say "here".
31309
+ * It is also the only shape a camera whose home is NOT a preset can accept
31310
+ * -- Reolink's home is a guard point, a stored position with no id at all,
31311
+ * and hiding the control for that case (as the UI briefly did) left the one
31312
+ * vendor with a native home unable to set it.
31313
+ *
31314
+ * Each provider does it its own way:
31315
+ * reolink `setGrd` with `needSetPos`, pinning the guard point here
31316
+ * hikvision / amcrest save a preset, then pin it as home
31317
+ * onvif REFUSES -- its home is the PTZ origin and cannot be moved
31318
+ *
31319
+ * A provider that cannot honour it throws rather than silently doing
31320
+ * nothing: an operator who pressed "set home here" and saw no error would
31321
+ * believe it took.
31322
+ */
31323
+ captureHomeHere: method(object({ deviceId: number() }), _void(), {
31324
+ kind: "mutation",
31325
+ auth: "admin"
31326
+ }),
31327
+ /**
31328
+ * Whether the camera returns home on its own when left idle, and after how
31329
+ * long. `null` when it has no such behaviour at all.
31330
+ *
31331
+ * Reolink's guard point carries one (`timeout`, freely settable in
31332
+ * seconds). It is a real function of the camera that we did not expose at
31333
+ * all, and it belongs next to the home it governs rather than in a vendor
31334
+ * settings page nobody associates with Home.
31335
+ */
31336
+ getHomeReturn: method(object({ deviceId: number() }), PtzHomeReturnSchema.nullable()),
31337
+ /**
31338
+ * Set both together. Refused by a camera with no idle-return behaviour.
31339
+ *
31340
+ * They travel together for the same reason they are read together: the
31341
+ * underlying write carries both, so setting one alone means reading the
31342
+ * other back first and racing whoever else is writing.
31343
+ */
31344
+ setHomeReturn: method(object({
31345
+ deviceId: number(),
31346
+ enabled: boolean(),
31347
+ seconds: number().int().min(0).max(3600)
31348
+ }), _void(), {
31349
+ kind: "mutation",
31350
+ auth: "admin"
31351
+ }),
31352
+ /**
31285
31353
  * Pull the current PTZ position. Redundant with the auto-injected
31286
31354
  * `getStatus` method (see `status` below); kept for callers that
31287
31355
  * haven't migrated. Will be folded into `getStatus` in a future
@@ -40942,6 +41010,12 @@ Object.freeze({
40942
41010
  addonId: null,
40943
41011
  access: "create"
40944
41012
  },
41013
+ "ptz.captureHomeHere": {
41014
+ capName: "ptz",
41015
+ capScope: "device",
41016
+ addonId: null,
41017
+ access: "create"
41018
+ },
40945
41019
  "ptz.continuousMove": {
40946
41020
  capName: "ptz",
40947
41021
  capScope: "device",
@@ -40960,6 +41034,12 @@ Object.freeze({
40960
41034
  addonId: null,
40961
41035
  access: "view"
40962
41036
  },
41037
+ "ptz.getHomeReturn": {
41038
+ capName: "ptz",
41039
+ capScope: "device",
41040
+ addonId: null,
41041
+ access: "view"
41042
+ },
40963
41043
  "ptz.getOptions": {
40964
41044
  capName: "ptz",
40965
41045
  capScope: "device",
@@ -41014,6 +41094,12 @@ Object.freeze({
41014
41094
  addonId: null,
41015
41095
  access: "create"
41016
41096
  },
41097
+ "ptz.setHomeReturn": {
41098
+ capName: "ptz",
41099
+ capScope: "device",
41100
+ addonId: null,
41101
+ access: "create"
41102
+ },
41017
41103
  "ptz.stop": {
41018
41104
  capName: "ptz",
41019
41105
  capScope: "device",
@@ -44177,6 +44263,11 @@ Object.freeze({
44177
44263
  form: "single",
44178
44264
  optional: false
44179
44265
  }],
44266
+ "ptz.captureHomeHere": [{
44267
+ name: "deviceId",
44268
+ form: "single",
44269
+ optional: false
44270
+ }],
44180
44271
  "ptz.continuousMove": [{
44181
44272
  name: "deviceId",
44182
44273
  form: "single",
@@ -44192,6 +44283,11 @@ Object.freeze({
44192
44283
  form: "single",
44193
44284
  optional: false
44194
44285
  }],
44286
+ "ptz.getHomeReturn": [{
44287
+ name: "deviceId",
44288
+ form: "single",
44289
+ optional: false
44290
+ }],
44195
44291
  "ptz.getOptions": [{
44196
44292
  name: "deviceId",
44197
44293
  form: "single",
@@ -44237,6 +44333,11 @@ Object.freeze({
44237
44333
  form: "single",
44238
44334
  optional: false
44239
44335
  }],
44336
+ "ptz.setHomeReturn": [{
44337
+ name: "deviceId",
44338
+ form: "single",
44339
+ optional: false
44340
+ }],
44240
44341
  "ptz.stop": [{
44241
44342
  name: "deviceId",
44242
44343
  form: "single",
@@ -46960,6 +47061,8 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
46960
47061
  * granularity is reported through `getOptions` instead of being hidden
46961
47062
  * behind a 0..1 float.
46962
47063
  */
47064
+ /** Dahua exposes 25 fixed preset slots, 1-based. */
47065
+ static DAHUA_MAX_PRESETS = 25;
46963
47066
  static DAHUA_SPEED_STEPS = 8;
46964
47067
  /**
46965
47068
  * How long one `ptz.move` pulse runs, ms. The DENOMINATOR of "how far did
@@ -47163,6 +47266,27 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
47163
47266
  };
47164
47267
  return this.resolveHomePreset();
47165
47268
  },
47269
+ captureHomeHere: async ({ deviceId }) => {
47270
+ if (deviceId !== this.id) return;
47271
+ const client = this.ensureClient();
47272
+ const ceiling = (await client.getPtzCaps(this.channel))?.maxPresets ?? AmcrestCamera.DAHUA_MAX_PRESETS;
47273
+ const used = new Set((await client.getPtzPresets(this.channel)).map((p) => p.id));
47274
+ let slot = 1;
47275
+ while (used.has(slot) && slot <= ceiling) slot += 1;
47276
+ if (slot > ceiling) throw new Error("No free preset slot on this camera to store a home position — free one first");
47277
+ await client.ptzSetPreset(this.channel, slot, AmcrestCamera.PTZ_CGI_TIMEOUT_MS);
47278
+ await this.config.setAll({ homePresetId: String(slot) });
47279
+ this.ctx.logger.info("amcrest: home pinned to the current position", {
47280
+ tags: { deviceId: this.id },
47281
+ meta: { presetId: slot }
47282
+ });
47283
+ },
47284
+ getHomeReturn: async () => {
47285
+ return null;
47286
+ },
47287
+ setHomeReturn: async () => {
47288
+ throw new Error("Amcrest: the idle-return timer is not wired on this driver");
47289
+ },
47166
47290
  setHomePreset: async ({ deviceId, presetId }) => {
47167
47291
  if (deviceId !== this.id) return;
47168
47292
  await this.config.setAll({ homePresetId: presetId ?? "" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-amcrest",
3
- "version": "0.2.113",
3
+ "version": "0.2.115",
4
4
  "description": "Amcrest/Dahua camera device provider addon for CamStack — Dahua CGI over HTTP(S) with digest auth (snapshot, RTSP catalog, PTZ, image/day-night config)",
5
5
  "keywords": [
6
6
  "camstack",