@camstack/addon-provider-homematic 1.2.42 → 1.2.44

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 (3) hide show
  1. package/dist/addon.js +154 -19
  2. package/dist/addon.mjs +154 -19
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -8048,18 +8048,61 @@ var RelocateFootageInputSchema = object({
8048
8048
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8049
8049
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8050
8050
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8051
+ /**
8052
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8053
+ * mover (the engine already walks both collections with a timestamp cursor and
8054
+ * already has a stamp-without-copy path).
8055
+ *
8056
+ * - `move` — the default and the historical behaviour: event-media and
8057
+ * retrain blobs move to `toLocationId` and their rows are
8058
+ * stamped. The enrolled gallery is skipped (D197).
8059
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8060
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8061
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8062
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8063
+ * instant a repoint moves that pointer the row reads from the
8064
+ * new disk while its bytes are on the old one.
8065
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8066
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8067
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8068
+ * never run beside a live second location: it is stop-the-world
8069
+ * by construction, which is acceptable only because the gallery
8070
+ * is a few KB per enrolled sample.
8071
+ */
8072
+ var MediaRelocateModeSchema = _enum([
8073
+ "move",
8074
+ "seal",
8075
+ "gallery"
8076
+ ]);
8051
8077
  var RelocateMediaInputSchema = object({
8052
8078
  toLocationId: string(),
8053
- throttleMbps: number().min(1).max(1e3).optional()
8079
+ throttleMbps: number().min(1).max(1e3).optional(),
8080
+ /** Omitted = `move`, the pre-existing behaviour. */
8081
+ mode: MediaRelocateModeSchema.optional()
8082
+ });
8083
+ /** How many rows still carry NO `locationId` — the population a repoint would
8084
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8085
+ * value that permits a non-blocking `eventMedia` cutover. */
8086
+ var UnstampedEventMediaCountSchema = object({
8087
+ media: number().int().nonnegative(),
8088
+ retrainFrames: number().int().nonnegative(),
8089
+ total: number().int().nonnegative()
8054
8090
  });
8055
8091
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8056
- /** The independently selectable logical storage classes. `recordings`
8057
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8058
- * segments; `eventMedia` is post-analysis blobs. */
8092
+ /** The independently selectable logical storage classes — every class
8093
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8094
+ * Zod enum error where they should meet an explanation.
8095
+ *
8096
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8097
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8098
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8099
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8059
8100
  var StorageMigrationClassSchema = _enum([
8060
8101
  "recordings",
8061
8102
  "recordingsLow",
8062
- "eventMedia"
8103
+ "eventMedia",
8104
+ "backups",
8105
+ "galleryMedia"
8063
8106
  ]);
8064
8107
  /** A destination is always an existing, fully-qualified location id. The
8065
8108
  * migration API intentionally never changes a source location's `basePath`:
@@ -8067,20 +8110,56 @@ var StorageMigrationClassSchema = _enum([
8067
8110
  var StorageMigrationDestinationsSchema = object({
8068
8111
  recordings: string().min(1).optional(),
8069
8112
  recordingsLow: string().min(1).optional(),
8070
- eventMedia: string().min(1).optional()
8113
+ eventMedia: string().min(1).optional(),
8114
+ backups: string().min(1).optional(),
8115
+ galleryMedia: string().min(1).optional()
8071
8116
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8117
+ /**
8118
+ * How a migration sequences the cutover against the byte move.
8119
+ *
8120
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8121
+ * resume. Recording is stopped for the whole move. Right
8122
+ * for a small or a cold class, and the only legal mode for
8123
+ * a `cardinality: 'single'` class.
8124
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8125
+ * refresh, resume, then move the past with everything
8126
+ * running. The pause is three bounded instants (a detach +
8127
+ * attach round, a write-gate drain, a lease) instead of one
8128
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8129
+ * stopped recording under `blocking`; the same move is
8130
+ * seconds of stopped recording under `nonBlocking`.
8131
+ *
8132
+ * The mode is on the JOB, not only on the input, because `status` is where an
8133
+ * operator finds out which one is running.
8134
+ */
8135
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8072
8136
  /** Shared input for planning and starting an orchestrated storage migration. */
8073
8137
  var StorageMigrationInputSchema = object({
8074
8138
  destinations: StorageMigrationDestinationsSchema,
8075
- throttleMbps: number().min(1).max(1e3).optional()
8139
+ throttleMbps: number().min(1).max(1e3).optional(),
8140
+ /** Omitted = `blocking`, which stays the default. */
8141
+ mode: StorageMigrationModeSchema.optional()
8076
8142
  });
8077
- /** The durable coordinator state machine. The only phase that changes default
8078
- * locations is `repointing`, after every selected mover has completed and been
8079
- * verified. */
8143
+ /**
8144
+ * The durable coordinator state machine.
8145
+ *
8146
+ * `blocking`:
8147
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8148
+ *
8149
+ * `nonBlocking`:
8150
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8151
+ *
8152
+ * Same phases, different order plus two new ones — not a second mover.
8153
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8154
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8155
+ * `repointing` is still the only phase that changes a default location.
8156
+ */
8080
8157
  var StorageMigrationPhaseSchema = _enum([
8081
8158
  "planning",
8159
+ "sealing",
8082
8160
  "pausing",
8083
8161
  "moving",
8162
+ "draining",
8084
8163
  "verifying",
8085
8164
  "repointing",
8086
8165
  "refreshing",
@@ -8105,6 +8184,9 @@ var StorageMigrationMoveSchema = object({
8105
8184
  var StorageMigrationJobSchema = object({
8106
8185
  jobId: string(),
8107
8186
  phase: StorageMigrationPhaseSchema,
8187
+ /** Which order this job is running. `status` is the only place an operator
8188
+ * can tell a seconds-long cutover from a thirty-hour one. */
8189
+ mode: StorageMigrationModeSchema,
8108
8190
  destinations: StorageMigrationDestinationsSchema,
8109
8191
  throttleMbps: number(),
8110
8192
  moves: array(StorageMigrationMoveSchema),
@@ -8117,13 +8199,30 @@ var StorageMigrationJobSchema = object({
8117
8199
  finishedAt: number().nullable(),
8118
8200
  error: string().nullable()
8119
8201
  });
8202
+ var StorageMigrationFindingSchema = object({
8203
+ code: _enum([
8204
+ "sharesDeviceWithSource",
8205
+ "deviceIdentityUnknown",
8206
+ "unstampedEventMediaRows",
8207
+ "blockingOnly",
8208
+ "noMover"
8209
+ ]),
8210
+ storageClass: StorageMigrationClassSchema,
8211
+ /** Human-readable, already carrying the ids and counts. */
8212
+ message: string()
8213
+ });
8120
8214
  var StorageMigrationPlanSchema = object({
8121
8215
  destinations: StorageMigrationDestinationsSchema,
8216
+ /** The mode this plan was built for. A plan is only valid for its mode: the
8217
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
8218
+ * it. */
8219
+ mode: StorageMigrationModeSchema,
8122
8220
  moves: array(object({
8123
8221
  storageClass: StorageMigrationClassSchema,
8124
8222
  fromLocationId: string(),
8125
8223
  toLocationId: string()
8126
- }))
8224
+ })),
8225
+ findings: array(StorageMigrationFindingSchema)
8127
8226
  });
8128
8227
  /**
8129
8228
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -19042,7 +19141,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19042
19141
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
19043
19142
  kind: "mutation",
19044
19143
  auth: "admin"
19045
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19144
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19046
19145
  kind: "query",
19047
19146
  auth: "admin"
19048
19147
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20949,7 +21048,10 @@ method(object({
20949
21048
  }), StorageLocationSchema, {
20950
21049
  kind: "mutation",
20951
21050
  auth: "admin"
20952
- }), method(object({ id: string() }), _void(), {
21051
+ }), method(object({
21052
+ id: string(),
21053
+ force: boolean().optional()
21054
+ }), _void(), {
20953
21055
  kind: "mutation",
20954
21056
  auth: "admin"
20955
21057
  }), method(object({ id: string() }), object({
@@ -25017,6 +25119,33 @@ var intercomCapability = {
25017
25119
  deviceNative: true,
25018
25120
  mode: "singleton",
25019
25121
  deviceTypes: [DeviceType.Camera],
25122
+ /**
25123
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25124
+ *
25125
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25126
+ * cap has no configuration surface at all: all six methods open, feed and
25127
+ * close one live audio session against one `deviceId`. That is the same
25128
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25129
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25130
+ * `admin` because they change what the device IS.
25131
+ *
25132
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25133
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25134
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25135
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25136
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25137
+ * camera 7.
25138
+ *
25139
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25140
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25141
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25142
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25143
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25144
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25145
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25146
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25147
+ * preset promises a cap no row of that preset can reach.
25148
+ */
25020
25149
  methods: {
25021
25150
  /**
25022
25151
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -25029,7 +25158,7 @@ var intercomCapability = {
25029
25158
  sdpOffer: string()
25030
25159
  }), {
25031
25160
  kind: "mutation",
25032
- auth: "admin"
25161
+ auth: "protected"
25033
25162
  }),
25034
25163
  handleAnswer: method(object({
25035
25164
  deviceId: number(),
@@ -25037,7 +25166,7 @@ var intercomCapability = {
25037
25166
  sdpAnswer: string()
25038
25167
  }), _void(), {
25039
25168
  kind: "mutation",
25040
- auth: "admin"
25169
+ auth: "protected"
25041
25170
  }),
25042
25171
  /** Close explicitly. Server also auto-closes on 30s idle. */
25043
25172
  stopSession: method(object({
@@ -25045,7 +25174,7 @@ var intercomCapability = {
25045
25174
  sessionId: string()
25046
25175
  }), _void(), {
25047
25176
  kind: "mutation",
25048
- auth: "admin"
25177
+ auth: "protected"
25049
25178
  }),
25050
25179
  /**
25051
25180
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -25058,7 +25187,7 @@ var intercomCapability = {
25058
25187
  */
25059
25188
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
25060
25189
  kind: "mutation",
25061
- auth: "admin"
25190
+ auth: "protected"
25062
25191
  }),
25063
25192
  /**
25064
25193
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25093,12 +25222,12 @@ var intercomCapability = {
25093
25222
  sequenceNumber: number().int()
25094
25223
  }), object({ accepted: boolean() }), {
25095
25224
  kind: "mutation",
25096
- auth: "admin"
25225
+ auth: "protected"
25097
25226
  }),
25098
25227
  /** Close the raw-PCM talk session. Idempotent. */
25099
25228
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25100
25229
  kind: "mutation",
25101
- auth: "admin"
25230
+ auth: "protected"
25102
25231
  })
25103
25232
  },
25104
25233
  events: { onStatusChanged: { data: object({
@@ -34779,6 +34908,12 @@ Object.freeze({
34779
34908
  addonId: null,
34780
34909
  access: "create"
34781
34910
  },
34911
+ "pipelineAnalytics.countUnstampedEventMedia": {
34912
+ capName: "pipeline-analytics",
34913
+ capScope: "device",
34914
+ addonId: null,
34915
+ access: "view"
34916
+ },
34782
34917
  "pipelineAnalytics.deleteDeviceEvents": {
34783
34918
  capName: "pipeline-analytics",
34784
34919
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -8049,18 +8049,61 @@ var RelocateFootageInputSchema = object({
8049
8049
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8050
8050
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8051
8051
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8052
+ /**
8053
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8054
+ * mover (the engine already walks both collections with a timestamp cursor and
8055
+ * already has a stamp-without-copy path).
8056
+ *
8057
+ * - `move` — the default and the historical behaviour: event-media and
8058
+ * retrain blobs move to `toLocationId` and their rows are
8059
+ * stamped. The enrolled gallery is skipped (D197).
8060
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8061
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8062
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8063
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8064
+ * instant a repoint moves that pointer the row reads from the
8065
+ * new disk while its bytes are on the old one.
8066
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8067
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8068
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8069
+ * never run beside a live second location: it is stop-the-world
8070
+ * by construction, which is acceptable only because the gallery
8071
+ * is a few KB per enrolled sample.
8072
+ */
8073
+ var MediaRelocateModeSchema = _enum([
8074
+ "move",
8075
+ "seal",
8076
+ "gallery"
8077
+ ]);
8052
8078
  var RelocateMediaInputSchema = object({
8053
8079
  toLocationId: string(),
8054
- throttleMbps: number().min(1).max(1e3).optional()
8080
+ throttleMbps: number().min(1).max(1e3).optional(),
8081
+ /** Omitted = `move`, the pre-existing behaviour. */
8082
+ mode: MediaRelocateModeSchema.optional()
8083
+ });
8084
+ /** How many rows still carry NO `locationId` — the population a repoint would
8085
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8086
+ * value that permits a non-blocking `eventMedia` cutover. */
8087
+ var UnstampedEventMediaCountSchema = object({
8088
+ media: number().int().nonnegative(),
8089
+ retrainFrames: number().int().nonnegative(),
8090
+ total: number().int().nonnegative()
8055
8091
  });
8056
8092
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8057
- /** The independently selectable logical storage classes. `recordings`
8058
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8059
- * segments; `eventMedia` is post-analysis blobs. */
8093
+ /** The independently selectable logical storage classes — every class
8094
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8095
+ * Zod enum error where they should meet an explanation.
8096
+ *
8097
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8098
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8099
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8100
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8060
8101
  var StorageMigrationClassSchema = _enum([
8061
8102
  "recordings",
8062
8103
  "recordingsLow",
8063
- "eventMedia"
8104
+ "eventMedia",
8105
+ "backups",
8106
+ "galleryMedia"
8064
8107
  ]);
8065
8108
  /** A destination is always an existing, fully-qualified location id. The
8066
8109
  * migration API intentionally never changes a source location's `basePath`:
@@ -8068,20 +8111,56 @@ var StorageMigrationClassSchema = _enum([
8068
8111
  var StorageMigrationDestinationsSchema = object({
8069
8112
  recordings: string().min(1).optional(),
8070
8113
  recordingsLow: string().min(1).optional(),
8071
- eventMedia: string().min(1).optional()
8114
+ eventMedia: string().min(1).optional(),
8115
+ backups: string().min(1).optional(),
8116
+ galleryMedia: string().min(1).optional()
8072
8117
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8118
+ /**
8119
+ * How a migration sequences the cutover against the byte move.
8120
+ *
8121
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8122
+ * resume. Recording is stopped for the whole move. Right
8123
+ * for a small or a cold class, and the only legal mode for
8124
+ * a `cardinality: 'single'` class.
8125
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8126
+ * refresh, resume, then move the past with everything
8127
+ * running. The pause is three bounded instants (a detach +
8128
+ * attach round, a write-gate drain, a lease) instead of one
8129
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8130
+ * stopped recording under `blocking`; the same move is
8131
+ * seconds of stopped recording under `nonBlocking`.
8132
+ *
8133
+ * The mode is on the JOB, not only on the input, because `status` is where an
8134
+ * operator finds out which one is running.
8135
+ */
8136
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8073
8137
  /** Shared input for planning and starting an orchestrated storage migration. */
8074
8138
  var StorageMigrationInputSchema = object({
8075
8139
  destinations: StorageMigrationDestinationsSchema,
8076
- throttleMbps: number().min(1).max(1e3).optional()
8140
+ throttleMbps: number().min(1).max(1e3).optional(),
8141
+ /** Omitted = `blocking`, which stays the default. */
8142
+ mode: StorageMigrationModeSchema.optional()
8077
8143
  });
8078
- /** The durable coordinator state machine. The only phase that changes default
8079
- * locations is `repointing`, after every selected mover has completed and been
8080
- * verified. */
8144
+ /**
8145
+ * The durable coordinator state machine.
8146
+ *
8147
+ * `blocking`:
8148
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8149
+ *
8150
+ * `nonBlocking`:
8151
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8152
+ *
8153
+ * Same phases, different order plus two new ones — not a second mover.
8154
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8155
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8156
+ * `repointing` is still the only phase that changes a default location.
8157
+ */
8081
8158
  var StorageMigrationPhaseSchema = _enum([
8082
8159
  "planning",
8160
+ "sealing",
8083
8161
  "pausing",
8084
8162
  "moving",
8163
+ "draining",
8085
8164
  "verifying",
8086
8165
  "repointing",
8087
8166
  "refreshing",
@@ -8106,6 +8185,9 @@ var StorageMigrationMoveSchema = object({
8106
8185
  var StorageMigrationJobSchema = object({
8107
8186
  jobId: string(),
8108
8187
  phase: StorageMigrationPhaseSchema,
8188
+ /** Which order this job is running. `status` is the only place an operator
8189
+ * can tell a seconds-long cutover from a thirty-hour one. */
8190
+ mode: StorageMigrationModeSchema,
8109
8191
  destinations: StorageMigrationDestinationsSchema,
8110
8192
  throttleMbps: number(),
8111
8193
  moves: array(StorageMigrationMoveSchema),
@@ -8118,13 +8200,30 @@ var StorageMigrationJobSchema = object({
8118
8200
  finishedAt: number().nullable(),
8119
8201
  error: string().nullable()
8120
8202
  });
8203
+ var StorageMigrationFindingSchema = object({
8204
+ code: _enum([
8205
+ "sharesDeviceWithSource",
8206
+ "deviceIdentityUnknown",
8207
+ "unstampedEventMediaRows",
8208
+ "blockingOnly",
8209
+ "noMover"
8210
+ ]),
8211
+ storageClass: StorageMigrationClassSchema,
8212
+ /** Human-readable, already carrying the ids and counts. */
8213
+ message: string()
8214
+ });
8121
8215
  var StorageMigrationPlanSchema = object({
8122
8216
  destinations: StorageMigrationDestinationsSchema,
8217
+ /** The mode this plan was built for. A plan is only valid for its mode: the
8218
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
8219
+ * it. */
8220
+ mode: StorageMigrationModeSchema,
8123
8221
  moves: array(object({
8124
8222
  storageClass: StorageMigrationClassSchema,
8125
8223
  fromLocationId: string(),
8126
8224
  toLocationId: string()
8127
- }))
8225
+ })),
8226
+ findings: array(StorageMigrationFindingSchema)
8128
8227
  });
8129
8228
  /**
8130
8229
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -19043,7 +19142,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19043
19142
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
19044
19143
  kind: "mutation",
19045
19144
  auth: "admin"
19046
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19145
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19047
19146
  kind: "query",
19048
19147
  auth: "admin"
19049
19148
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20950,7 +21049,10 @@ method(object({
20950
21049
  }), StorageLocationSchema, {
20951
21050
  kind: "mutation",
20952
21051
  auth: "admin"
20953
- }), method(object({ id: string() }), _void(), {
21052
+ }), method(object({
21053
+ id: string(),
21054
+ force: boolean().optional()
21055
+ }), _void(), {
20954
21056
  kind: "mutation",
20955
21057
  auth: "admin"
20956
21058
  }), method(object({ id: string() }), object({
@@ -25018,6 +25120,33 @@ var intercomCapability = {
25018
25120
  deviceNative: true,
25019
25121
  mode: "singleton",
25020
25122
  deviceTypes: [DeviceType.Camera],
25123
+ /**
25124
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25125
+ *
25126
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25127
+ * cap has no configuration surface at all: all six methods open, feed and
25128
+ * close one live audio session against one `deviceId`. That is the same
25129
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25130
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25131
+ * `admin` because they change what the device IS.
25132
+ *
25133
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25134
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25135
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25136
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25137
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25138
+ * camera 7.
25139
+ *
25140
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25141
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25142
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25143
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25144
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25145
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25146
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25147
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25148
+ * preset promises a cap no row of that preset can reach.
25149
+ */
25021
25150
  methods: {
25022
25151
  /**
25023
25152
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -25030,7 +25159,7 @@ var intercomCapability = {
25030
25159
  sdpOffer: string()
25031
25160
  }), {
25032
25161
  kind: "mutation",
25033
- auth: "admin"
25162
+ auth: "protected"
25034
25163
  }),
25035
25164
  handleAnswer: method(object({
25036
25165
  deviceId: number(),
@@ -25038,7 +25167,7 @@ var intercomCapability = {
25038
25167
  sdpAnswer: string()
25039
25168
  }), _void(), {
25040
25169
  kind: "mutation",
25041
- auth: "admin"
25170
+ auth: "protected"
25042
25171
  }),
25043
25172
  /** Close explicitly. Server also auto-closes on 30s idle. */
25044
25173
  stopSession: method(object({
@@ -25046,7 +25175,7 @@ var intercomCapability = {
25046
25175
  sessionId: string()
25047
25176
  }), _void(), {
25048
25177
  kind: "mutation",
25049
- auth: "admin"
25178
+ auth: "protected"
25050
25179
  }),
25051
25180
  /**
25052
25181
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -25059,7 +25188,7 @@ var intercomCapability = {
25059
25188
  */
25060
25189
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
25061
25190
  kind: "mutation",
25062
- auth: "admin"
25191
+ auth: "protected"
25063
25192
  }),
25064
25193
  /**
25065
25194
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25094,12 +25223,12 @@ var intercomCapability = {
25094
25223
  sequenceNumber: number().int()
25095
25224
  }), object({ accepted: boolean() }), {
25096
25225
  kind: "mutation",
25097
- auth: "admin"
25226
+ auth: "protected"
25098
25227
  }),
25099
25228
  /** Close the raw-PCM talk session. Idempotent. */
25100
25229
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25101
25230
  kind: "mutation",
25102
- auth: "admin"
25231
+ auth: "protected"
25103
25232
  })
25104
25233
  },
25105
25234
  events: { onStatusChanged: { data: object({
@@ -34780,6 +34909,12 @@ Object.freeze({
34780
34909
  addonId: null,
34781
34910
  access: "create"
34782
34911
  },
34912
+ "pipelineAnalytics.countUnstampedEventMedia": {
34913
+ capName: "pipeline-analytics",
34914
+ capScope: "device",
34915
+ addonId: null,
34916
+ access: "view"
34917
+ },
34783
34918
  "pipelineAnalytics.deleteDeviceEvents": {
34784
34919
  capName: "pipeline-analytics",
34785
34920
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.2.42",
3
+ "version": "1.2.44",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",