@camstack/addon-provider-homematic 1.2.34 → 1.2.35

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 +322 -25
  2. package/dist/addon.mjs +322 -25
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7477,6 +7477,111 @@ var CameraSwitchGroupSchema = object({
7477
7477
  fetchedAt: number()
7478
7478
  });
7479
7479
  /**
7480
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
7481
+ * an addon declares its channels in.
7482
+ *
7483
+ * ## Two axes, deliberately separated
7484
+ *
7485
+ * - **DECLARATION** — which channels exist. Only the addon knows:
7486
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7487
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
7488
+ * and rots silently. So a channel is declared where it is consulted, and the
7489
+ * `log-channels` capability enumerates the declarations.
7490
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
7491
+ * thing: the logging settings document on the `system` cap. Two authorities
7492
+ * over the values is the exact defect
7493
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7494
+ * remove; re-introducing it from the cure side would be grotesque.
7495
+ *
7496
+ * Nothing in this file reads a clock, an env var or a store. The registry is
7497
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7498
+ * the hot path with a value somebody actually read, and by
7499
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7500
+ * never reaches here, so it can neither disarm an armed channel nor arm a
7501
+ * disarmed one (D49).
7502
+ *
7503
+ * ## The canonical call shape
7504
+ *
7505
+ * ```ts
7506
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7507
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7508
+ * }
7509
+ * ```
7510
+ *
7511
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7512
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
7513
+ * object literal is never constructed because it lives inside the branch. It
7514
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
7515
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
7516
+ * destination floor (measured at 1.93 ns/call when off).
7517
+ *
7518
+ * ## Why a channel emits at `info`
7519
+ *
7520
+ * `loki-logging.addon.ts` pins the destination default at `info` and
7521
+ * `loki-destination.ts` drops everything below it, so a line emitted at
7522
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7523
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
7524
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7525
+ * emits at the channel's declared level, whose schema floor is `info`.
7526
+ */
7527
+ /**
7528
+ * The level a channel writes at once armed.
7529
+ *
7530
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7531
+ * not leave the process for Loki, and the whole point of arming a channel is
7532
+ * to read it later.
7533
+ */
7534
+ var LogChannelLevelSchema = _enum([
7535
+ "info",
7536
+ "warn",
7537
+ "error"
7538
+ ]);
7539
+ /**
7540
+ * What an addon declares about one channel. No value, no state — a
7541
+ * declaration is inert.
7542
+ */
7543
+ var LogChannelDescriptorSchema = object({
7544
+ /**
7545
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7546
+ * the addon's short name so an operator reading a channel list can tell who
7547
+ * owns it without a second lookup.
7548
+ */
7549
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7550
+ /** One sentence: what the operator will SEE after arming it. */
7551
+ description: string().min(1),
7552
+ /** The level its lines are emitted at. Never below `info`. */
7553
+ defaultLevel: LogChannelLevelSchema,
7554
+ /**
7555
+ * Whether this channel can be narrowed to a camera.
7556
+ *
7557
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
7558
+ * consulted with the numeric device id, AND every line the channel admits
7559
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
7560
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7561
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7562
+ * the body is the only way to filter.
7563
+ *
7564
+ * A channel whose lines carry the device only in `meta` (or not at all) is
7565
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7566
+ * the operator narrows to one camera, sees nothing, and concludes the code
7567
+ * path was never taken.
7568
+ */
7569
+ perDevice: boolean()
7570
+ });
7571
+ /**
7572
+ * An armed window over one channel, as the document hands it to a mirror.
7573
+ *
7574
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7575
+ * expires by itself, which is the one failure a boolean cannot avoid.
7576
+ */
7577
+ var LogChannelWindowSchema = object({
7578
+ channel: string().min(1),
7579
+ /** Epoch ms the window closes at. */
7580
+ armedUntilMs: number(),
7581
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7582
+ deviceIds: array(number().int()).readonly().nullable()
7583
+ });
7584
+ /**
7480
7585
  * Ops-log — the durable, append-only operations audit shared by the
7481
7586
  * recordings and events management surfaces.
7482
7587
  *
@@ -11146,6 +11251,35 @@ var MutationFilterSchema = object({
11146
11251
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11147
11252
  whereNot: record(string(), unknown()).optional()
11148
11253
  });
11254
+ /**
11255
+ * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
11256
+ *
11257
+ * `as` names the slot in the result, so the SAME column may be asked twice with
11258
+ * two operations (`MIN(startMs)` and `MAX(startMs)` in one round trip) — which
11259
+ * a `Record<column, op>` shape could not express.
11260
+ */
11261
+ var AggregateFieldSchema = object({
11262
+ /** Result key. */
11263
+ as: string().min(1),
11264
+ /** Column to aggregate. Must be a real column of a declared collection. */
11265
+ field: string().min(1),
11266
+ op: _enum([
11267
+ "sum",
11268
+ "min",
11269
+ "max"
11270
+ ])
11271
+ });
11272
+ /**
11273
+ * `COUNT(*)` plus one number per requested field.
11274
+ *
11275
+ * `null` means NO ROW MATCHED, never zero: a `SUM` over an empty set and a sum
11276
+ * that really is 0 are different facts, and an accounting caller that renders
11277
+ * "0 bytes, oldest = 0" for "nothing here" reports a lie about a disk.
11278
+ */
11279
+ var AggregateResultSchema = object({
11280
+ count: number().int(),
11281
+ values: record(string(), number().nullable())
11282
+ });
11149
11283
  /** A single stored record: `{ id, data }`. */
11150
11284
  var SettingsRecordSchema = object({
11151
11285
  id: string(),
@@ -11230,6 +11364,11 @@ method(object({
11230
11364
  collection: string(),
11231
11365
  filter: QueryFilterSchema.optional()
11232
11366
  }), number()), method(object({
11367
+ namespace: string().optional(),
11368
+ collection: string(),
11369
+ fields: array(AggregateFieldSchema).readonly(),
11370
+ filter: QueryFilterSchema.optional()
11371
+ }), AggregateResultSchema), method(object({
11233
11372
  namespace: string().optional(),
11234
11373
  collection: string(),
11235
11374
  field: string(),
@@ -11346,6 +11485,11 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11346
11485
  collection: string(),
11347
11486
  filter: QueryFilterSchema.optional()
11348
11487
  }), number(), { auth: "admin" }), method(object({
11488
+ namespace: string().optional(),
11489
+ collection: string(),
11490
+ fields: array(AggregateFieldSchema).readonly(),
11491
+ filter: QueryFilterSchema.optional()
11492
+ }), AggregateResultSchema, { auth: "admin" }), method(object({
11349
11493
  namespace: string().optional(),
11350
11494
  collection: string(),
11351
11495
  field: string(),
@@ -12086,24 +12230,6 @@ var deviceProviderCapability = {
12086
12230
  })
12087
12231
  }
12088
12232
  };
12089
- /**
12090
- * Device Manager capability — hub-side singleton that unifies device persistence,
12091
- * live registry access, and all management operations into a single tRPC surface.
12092
- *
12093
- * Replaces:
12094
- * - `device-persistence` capability (persistence methods absorbed here)
12095
- * - `device-management.router.ts` (deleted in Phase 2)
12096
- * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
12097
- *
12098
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
12099
- * fork into separate processes but never run on remote cluster agents. Therefore:
12100
- * - No nodeId routing needed — this is a pure hub singleton.
12101
- * - The hub's DeviceRegistry is the single source of truth for all live devices.
12102
- * - No shadow registry or cross-node aggregation required.
12103
- *
12104
- * Forked workers register devices back to the hub via `ctx.devices`
12105
- * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
12106
- */
12107
12233
  /** One child-placement directive on a container's `childLayout`. Structurally
12108
12234
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
12109
12235
  * shape for the same field. The child is identified by its re-sync-stable
@@ -12472,7 +12598,7 @@ method(object({
12472
12598
  * it answers today and the caller filters as it already does.
12473
12599
  */
12474
12600
  deviceIds: array(number()).optional()
12475
- }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12601
+ }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ parentDeviceIds: array(number()).max(256) }), record(string(), array(DeviceInfoSchema))), method(object({ deviceId: number() }), object({
12476
12602
  mode: LinkedDevicesModeSchema,
12477
12603
  devices: array(LinkedDeviceSchema)
12478
12604
  })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
@@ -13196,6 +13322,59 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
13196
13322
  kind: "mutation",
13197
13323
  auth: "admin"
13198
13324
  });
13325
+ /**
13326
+ * `log-channels` — the capability an addon DECLARES its diagnostic channels
13327
+ * through. It stores nothing.
13328
+ *
13329
+ * ## Why a capability at all, and why this shape
13330
+ *
13331
+ * Which channels exist is knowledge only the addon has: `stream-broker` knows
13332
+ * webrtc/ICE/RTP, `provider-reolink` knows baichuan/handshake. A central list
13333
+ * maintained by hand rots at the first addition and rots INVISIBLY — nothing
13334
+ * fails, an operator just never sees the channel somebody added. So the list
13335
+ * is assembled from declarations at runtime.
13336
+ *
13337
+ * The shape is copied from `log-destination.cap.ts`, which already does
13338
+ * exactly this job: `mode: 'collection'`, `internal: true`,
13339
+ * `mount: { kind: 'skip' }` — no tRPC route, no generated hooks — while
13340
+ * `addons.listCapabilityProviders` still enumerates it, and the hub's
13341
+ * `CapabilityRegistry` still holds an RPC proxy per provider so a forked
13342
+ * runner's declarations reach hub-main over the transport that already exists.
13343
+ * No new UDS message, no second registry.
13344
+ *
13345
+ * ## What it deliberately does NOT own
13346
+ *
13347
+ * The VALUES — which channel is armed, for which cameras, until when — live in
13348
+ * ONE place: the logging settings document on the `system` cap
13349
+ * (`getLoggingSettings` / `setLoggingSettings`). Two authorities over the same
13350
+ * value is the defect the plan behind this work exists to remove, and
13351
+ * `setRequestCensus` was retired (D245) rather than allowed to be a second
13352
+ * one. {@link logChannelsCapability} therefore has no getter for a level, no
13353
+ * setter for a window and no persistence of any kind.
13354
+ *
13355
+ * ## Why `apply` is here even so
13356
+ *
13357
+ * The gate lives in the addon's PROCESS; the document lives in hub-main. Some
13358
+ * seam has to carry the value from the authority to the mirror, and a channel
13359
+ * that cannot be reached is precisely the dead knob this whole slice exists to
13360
+ * make impossible (D62 — `audioThresholdDbfs`, the HA entities with no source).
13361
+ * `apply` is that seam and nothing more: it writes an in-memory mirror, it
13362
+ * persists nothing, it is never the source of a value, and it is called only
13363
+ * with a set the hub actually read (D49 — a read that fails does not call it
13364
+ * at all, so no channel is silently disarmed by a bad read).
13365
+ */
13366
+ /** What `apply` reports back — enough to log, not enough to be a second state. */
13367
+ var LogChannelApplyResultSchema = object({
13368
+ /** How many declared channels are armed in this process after the call. */
13369
+ armed: number().int().min(0),
13370
+ /**
13371
+ * Names the document armed that this process does not declare. Reported
13372
+ * rather than swallowed: a name here is either a typo or an addon that has
13373
+ * not booted, and both deserve a line instead of silence.
13374
+ */
13375
+ unknown: array(string()).readonly()
13376
+ });
13377
+ method(_void(), array(LogChannelDescriptorSchema).readonly()), method(object({ windows: array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" });
13199
13378
  var LogLevelSchema = _enum([
13200
13379
  "debug",
13201
13380
  "info",
@@ -28640,10 +28819,11 @@ var DiagnosticIdSchema = _enum(["request-census"]);
28640
28819
  * The layers of the level hierarchy, general → specific. The most specific
28641
28820
  * layer that carries an explicit value wins.
28642
28821
  *
28643
- * `component` is DECLARED and not yet resolvable: the per-component channels
28644
- * are a later slice of the same plan, and a `levelSource` enum that has to
28645
- * grow later would force every consumer of this document to change with it.
28646
- * Nothing returns `component` today.
28822
+ * `component` became RESOLVABLE on 2026-08-27: a component is a declared log
28823
+ * CHANNEL (`stream-broker.webrtc`, `provider-reolink.baichuan`), named by
28824
+ * `scopeComponent`. It was declared-but-dark in the first slice precisely so
28825
+ * that turning it on would not force every consumer of this document to widen
28826
+ * a `levelSource` enum — which is what has now not happened.
28647
28827
  */
28648
28828
  var LoggingScopeKindSchema = _enum([
28649
28829
  "cluster",
@@ -28670,6 +28850,14 @@ var LoggingLevelLayerSchema = object({
28670
28850
  scope: LoggingScopeKindSchema,
28671
28851
  /** The node this layer speaks for; `null` on the cluster layer. */
28672
28852
  nodeId: string().nullable(),
28853
+ /**
28854
+ * The declared channel this layer speaks for; `null` on every layer but
28855
+ * `component`. Never folded into `nodeId`: a component level is CLUSTER-WIDE
28856
+ * by design — the convention this repo settled on is one orchestrator-wide
28857
+ * setting, never per node (D52) — so a component layer that carried a node
28858
+ * would invite a per-node copy of a value that has no per-node meaning.
28859
+ */
28860
+ component: string().nullable(),
28673
28861
  /** Explicitly set here, or `null` when this layer inherits. */
28674
28862
  level: LogLevelSchema$1.nullable()
28675
28863
  });
@@ -28711,6 +28899,49 @@ var DiagnosticWindowPatchSchema = object({
28711
28899
  reportEveryMs: number().int().positive().optional()
28712
28900
  });
28713
28901
  /**
28902
+ * A channel ARMED, as the document reports it.
28903
+ *
28904
+ * `armMs` is not echoed back: what an operator needs to see is the deadline
28905
+ * and the time left, because a diagnostic left running is itself an incident
28906
+ * and "armed for 10 minutes" said an hour ago is not an answer.
28907
+ */
28908
+ var LogChannelWindowStateSchema = object({
28909
+ channel: string(),
28910
+ armed: boolean(),
28911
+ /** Epoch ms the window closes at. 0 when disarmed. */
28912
+ armedUntilMs: number(),
28913
+ /** Ms left before it expires on its own. 0 when disarmed. */
28914
+ remainingMs: number(),
28915
+ /**
28916
+ * The cameras it is narrowed to, or `null` for every camera.
28917
+ *
28918
+ * A channel declared `perDevice: false` can only ever report `null` here:
28919
+ * its lines do not carry `tags: { deviceId }`, so narrowing them would
28920
+ * produce a filter that silently matches nothing. The server REFUSES such a
28921
+ * patch rather than quietly widening it — ignoring the request would teach
28922
+ * the operator that per-camera filtering works on that channel when it does
28923
+ * not.
28924
+ */
28925
+ deviceIds: array(number().int()).readonly().nullable()
28926
+ });
28927
+ /**
28928
+ * `armMs: 0` DISARMS. Same grammar as {@link DiagnosticWindowPatchSchema}, and
28929
+ * for the same reason: a channel is a window with a deadline, never a switch.
28930
+ */
28931
+ var LogChannelWindowPatchSchema = object({
28932
+ channel: string().min(1),
28933
+ armMs: number().int().min(0),
28934
+ /**
28935
+ * Narrow to these numeric device ids. Absent or `null` = every camera.
28936
+ *
28937
+ * Numeric because the repo's own rule makes it possible: every log line
28938
+ * about a device carries `tags: { deviceId }` with the numeric id. That rule
28939
+ * was paid for with a 22% thumbnail gap and a 3-hour media blackout both
28940
+ * diagnosed by hand, and this is the first thing that collects on it.
28941
+ */
28942
+ deviceIds: array(number().int()).readonly().nullable().optional()
28943
+ });
28944
+ /**
28714
28945
  * A PATCH, and patches MERGE.
28715
28946
  *
28716
28947
  * A field absent from the patch is left exactly as it was — arming a
@@ -28729,7 +28960,14 @@ var LoggingSettingsPatchSchema = object({
28729
28960
  * Only the diagnostics NAMED here change. An armed window that is not listed
28730
28961
  * keeps running — a patch is never a full replacement.
28731
28962
  */
28732
- diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional()
28963
+ diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional(),
28964
+ /**
28965
+ * Only the channels NAMED here change. An armed channel that is not listed
28966
+ * keeps running — same rule as `diagnostics`, because a patch that silently
28967
+ * disarmed the channels it did not mention would make the Levels page and
28968
+ * the Diagnostics page fight over the same value.
28969
+ */
28970
+ channels: array(LogChannelWindowPatchSchema).readonly().optional()
28733
28971
  });
28734
28972
  /**
28735
28973
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -28742,9 +28980,22 @@ var LoggingSettingsPatchSchema = object({
28742
28980
  * authority over the whole hierarchy and answers for every layer, so the
28743
28981
  * layer selector needs a name the transport does not already own.
28744
28982
  */
28745
- var GetLoggingSettingsInputSchema = object({ scopeNodeId: string().optional() });
28983
+ var GetLoggingSettingsInputSchema = object({
28984
+ scopeNodeId: string().optional(),
28985
+ /**
28986
+ * The declared CHANNEL this document is addressed at, when the caller wants
28987
+ * the `component` layer. Absent = the node/cluster hierarchy only.
28988
+ *
28989
+ * Naming it separately rather than overloading `scopeNodeId` keeps the two
28990
+ * axes from collapsing: a component level is cluster-wide, a node level is
28991
+ * not, and one selector for both would make "which of these two did I just
28992
+ * set" unanswerable — the exact ambiguity `explicit` exists to remove.
28993
+ */
28994
+ scopeComponent: string().optional()
28995
+ });
28746
28996
  var SetLoggingSettingsInputSchema = object({
28747
28997
  scopeNodeId: string().optional(),
28998
+ scopeComponent: string().optional(),
28748
28999
  patch: LoggingSettingsPatchSchema
28749
29000
  });
28750
29001
  /**
@@ -28759,9 +29010,20 @@ var SetLoggingSettingsInputSchema = object({
28759
29010
  var LoggingSettingsStateSchema = object({
28760
29011
  /** The layer this document was read at. `null` = the cluster layer. */
28761
29012
  scopeNodeId: string().nullable(),
29013
+ /** The channel this document was read at. `null` = no component layer. */
29014
+ scopeComponent: string().nullable(),
28762
29015
  effective: LoggingEffectiveSchema,
28763
29016
  explicit: LoggingExplicitSchema,
28764
29017
  activeWindows: array(DiagnosticWindowSchema).readonly(),
29018
+ /**
29019
+ * Every channel the cluster's addons DECLARE, gathered from the
29020
+ * `log-channels` providers. Not stored anywhere: assembled per read, so a
29021
+ * channel added by a redeployed addon appears without anybody editing a
29022
+ * list, and a channel whose addon is gone stops being offered.
29023
+ */
29024
+ channels: array(LogChannelDescriptorSchema).readonly(),
29025
+ /** The channels ARMED right now, each with its deadline. */
29026
+ activeChannels: array(LogChannelWindowStateSchema).readonly(),
28765
29027
  persisted: boolean()
28766
29028
  });
28767
29029
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -31746,6 +32008,12 @@ Object.freeze({
31746
32008
  addonId: null,
31747
32009
  access: "view"
31748
32010
  },
32011
+ "dataStoreProvider.aggregate": {
32012
+ capName: "data-store-provider",
32013
+ capScope: "system",
32014
+ addonId: null,
32015
+ access: "view"
32016
+ },
31749
32017
  "dataStoreProvider.count": {
31750
32018
  capName: "data-store-provider",
31751
32019
  capScope: "system",
@@ -32160,6 +32428,12 @@ Object.freeze({
32160
32428
  addonId: null,
32161
32429
  access: "view"
32162
32430
  },
32431
+ "deviceManager.getChildrenBatch": {
32432
+ capName: "device-manager",
32433
+ capScope: "system",
32434
+ addonId: null,
32435
+ access: "view"
32436
+ },
32163
32437
  "deviceManager.getConfigSchema": {
32164
32438
  capName: "device-manager",
32165
32439
  capScope: "system",
@@ -33210,6 +33484,18 @@ Object.freeze({
33210
33484
  addonId: null,
33211
33485
  access: "create"
33212
33486
  },
33487
+ "logChannels.apply": {
33488
+ capName: "log-channels",
33489
+ capScope: "system",
33490
+ addonId: null,
33491
+ access: "create"
33492
+ },
33493
+ "logChannels.list": {
33494
+ capName: "log-channels",
33495
+ capScope: "system",
33496
+ addonId: null,
33497
+ access: "view"
33498
+ },
33213
33499
  "logDestination.query": {
33214
33500
  capName: "log-destination",
33215
33501
  capScope: "system",
@@ -35364,6 +35650,12 @@ Object.freeze({
35364
35650
  addonId: null,
35365
35651
  access: "create"
35366
35652
  },
35653
+ "settingsStore.aggregate": {
35654
+ capName: "settings-store",
35655
+ capScope: "system",
35656
+ addonId: null,
35657
+ access: "view"
35658
+ },
35367
35659
  "settingsStore.count": {
35368
35660
  capName: "settings-store",
35369
35661
  capScope: "system",
@@ -36943,6 +37235,11 @@ Object.freeze({
36943
37235
  form: "single",
36944
37236
  optional: false
36945
37237
  }],
37238
+ "deviceManager.getChildrenBatch": [{
37239
+ name: "parentDeviceIds",
37240
+ form: "array",
37241
+ optional: false
37242
+ }],
36946
37243
  "deviceManager.getConfigSchema": [{
36947
37244
  name: "deviceId",
36948
37245
  form: "single",
package/dist/addon.mjs CHANGED
@@ -7478,6 +7478,111 @@ var CameraSwitchGroupSchema = object({
7478
7478
  fetchedAt: number()
7479
7479
  });
7480
7480
  /**
7481
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
7482
+ * an addon declares its channels in.
7483
+ *
7484
+ * ## Two axes, deliberately separated
7485
+ *
7486
+ * - **DECLARATION** — which channels exist. Only the addon knows:
7487
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7488
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
7489
+ * and rots silently. So a channel is declared where it is consulted, and the
7490
+ * `log-channels` capability enumerates the declarations.
7491
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
7492
+ * thing: the logging settings document on the `system` cap. Two authorities
7493
+ * over the values is the exact defect
7494
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7495
+ * remove; re-introducing it from the cure side would be grotesque.
7496
+ *
7497
+ * Nothing in this file reads a clock, an env var or a store. The registry is
7498
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7499
+ * the hot path with a value somebody actually read, and by
7500
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7501
+ * never reaches here, so it can neither disarm an armed channel nor arm a
7502
+ * disarmed one (D49).
7503
+ *
7504
+ * ## The canonical call shape
7505
+ *
7506
+ * ```ts
7507
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7508
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7509
+ * }
7510
+ * ```
7511
+ *
7512
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7513
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
7514
+ * object literal is never constructed because it lives inside the branch. It
7515
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
7516
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
7517
+ * destination floor (measured at 1.93 ns/call when off).
7518
+ *
7519
+ * ## Why a channel emits at `info`
7520
+ *
7521
+ * `loki-logging.addon.ts` pins the destination default at `info` and
7522
+ * `loki-destination.ts` drops everything below it, so a line emitted at
7523
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7524
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
7525
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7526
+ * emits at the channel's declared level, whose schema floor is `info`.
7527
+ */
7528
+ /**
7529
+ * The level a channel writes at once armed.
7530
+ *
7531
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7532
+ * not leave the process for Loki, and the whole point of arming a channel is
7533
+ * to read it later.
7534
+ */
7535
+ var LogChannelLevelSchema = _enum([
7536
+ "info",
7537
+ "warn",
7538
+ "error"
7539
+ ]);
7540
+ /**
7541
+ * What an addon declares about one channel. No value, no state — a
7542
+ * declaration is inert.
7543
+ */
7544
+ var LogChannelDescriptorSchema = object({
7545
+ /**
7546
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7547
+ * the addon's short name so an operator reading a channel list can tell who
7548
+ * owns it without a second lookup.
7549
+ */
7550
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7551
+ /** One sentence: what the operator will SEE after arming it. */
7552
+ description: string().min(1),
7553
+ /** The level its lines are emitted at. Never below `info`. */
7554
+ defaultLevel: LogChannelLevelSchema,
7555
+ /**
7556
+ * Whether this channel can be narrowed to a camera.
7557
+ *
7558
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
7559
+ * consulted with the numeric device id, AND every line the channel admits
7560
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
7561
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7562
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7563
+ * the body is the only way to filter.
7564
+ *
7565
+ * A channel whose lines carry the device only in `meta` (or not at all) is
7566
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7567
+ * the operator narrows to one camera, sees nothing, and concludes the code
7568
+ * path was never taken.
7569
+ */
7570
+ perDevice: boolean()
7571
+ });
7572
+ /**
7573
+ * An armed window over one channel, as the document hands it to a mirror.
7574
+ *
7575
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7576
+ * expires by itself, which is the one failure a boolean cannot avoid.
7577
+ */
7578
+ var LogChannelWindowSchema = object({
7579
+ channel: string().min(1),
7580
+ /** Epoch ms the window closes at. */
7581
+ armedUntilMs: number(),
7582
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7583
+ deviceIds: array(number().int()).readonly().nullable()
7584
+ });
7585
+ /**
7481
7586
  * Ops-log — the durable, append-only operations audit shared by the
7482
7587
  * recordings and events management surfaces.
7483
7588
  *
@@ -11147,6 +11252,35 @@ var MutationFilterSchema = object({
11147
11252
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11148
11253
  whereNot: record(string(), unknown()).optional()
11149
11254
  });
11255
+ /**
11256
+ * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
11257
+ *
11258
+ * `as` names the slot in the result, so the SAME column may be asked twice with
11259
+ * two operations (`MIN(startMs)` and `MAX(startMs)` in one round trip) — which
11260
+ * a `Record<column, op>` shape could not express.
11261
+ */
11262
+ var AggregateFieldSchema = object({
11263
+ /** Result key. */
11264
+ as: string().min(1),
11265
+ /** Column to aggregate. Must be a real column of a declared collection. */
11266
+ field: string().min(1),
11267
+ op: _enum([
11268
+ "sum",
11269
+ "min",
11270
+ "max"
11271
+ ])
11272
+ });
11273
+ /**
11274
+ * `COUNT(*)` plus one number per requested field.
11275
+ *
11276
+ * `null` means NO ROW MATCHED, never zero: a `SUM` over an empty set and a sum
11277
+ * that really is 0 are different facts, and an accounting caller that renders
11278
+ * "0 bytes, oldest = 0" for "nothing here" reports a lie about a disk.
11279
+ */
11280
+ var AggregateResultSchema = object({
11281
+ count: number().int(),
11282
+ values: record(string(), number().nullable())
11283
+ });
11150
11284
  /** A single stored record: `{ id, data }`. */
11151
11285
  var SettingsRecordSchema = object({
11152
11286
  id: string(),
@@ -11231,6 +11365,11 @@ method(object({
11231
11365
  collection: string(),
11232
11366
  filter: QueryFilterSchema.optional()
11233
11367
  }), number()), method(object({
11368
+ namespace: string().optional(),
11369
+ collection: string(),
11370
+ fields: array(AggregateFieldSchema).readonly(),
11371
+ filter: QueryFilterSchema.optional()
11372
+ }), AggregateResultSchema), method(object({
11234
11373
  namespace: string().optional(),
11235
11374
  collection: string(),
11236
11375
  field: string(),
@@ -11347,6 +11486,11 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11347
11486
  collection: string(),
11348
11487
  filter: QueryFilterSchema.optional()
11349
11488
  }), number(), { auth: "admin" }), method(object({
11489
+ namespace: string().optional(),
11490
+ collection: string(),
11491
+ fields: array(AggregateFieldSchema).readonly(),
11492
+ filter: QueryFilterSchema.optional()
11493
+ }), AggregateResultSchema, { auth: "admin" }), method(object({
11350
11494
  namespace: string().optional(),
11351
11495
  collection: string(),
11352
11496
  field: string(),
@@ -12087,24 +12231,6 @@ var deviceProviderCapability = {
12087
12231
  })
12088
12232
  }
12089
12233
  };
12090
- /**
12091
- * Device Manager capability — hub-side singleton that unifies device persistence,
12092
- * live registry access, and all management operations into a single tRPC surface.
12093
- *
12094
- * Replaces:
12095
- * - `device-persistence` capability (persistence methods absorbed here)
12096
- * - `device-management.router.ts` (deleted in Phase 2)
12097
- * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
12098
- *
12099
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
12100
- * fork into separate processes but never run on remote cluster agents. Therefore:
12101
- * - No nodeId routing needed — this is a pure hub singleton.
12102
- * - The hub's DeviceRegistry is the single source of truth for all live devices.
12103
- * - No shadow registry or cross-node aggregation required.
12104
- *
12105
- * Forked workers register devices back to the hub via `ctx.devices`
12106
- * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
12107
- */
12108
12234
  /** One child-placement directive on a container's `childLayout`. Structurally
12109
12235
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
12110
12236
  * shape for the same field. The child is identified by its re-sync-stable
@@ -12473,7 +12599,7 @@ method(object({
12473
12599
  * it answers today and the caller filters as it already does.
12474
12600
  */
12475
12601
  deviceIds: array(number()).optional()
12476
- }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12602
+ }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ parentDeviceIds: array(number()).max(256) }), record(string(), array(DeviceInfoSchema))), method(object({ deviceId: number() }), object({
12477
12603
  mode: LinkedDevicesModeSchema,
12478
12604
  devices: array(LinkedDeviceSchema)
12479
12605
  })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
@@ -13197,6 +13323,59 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
13197
13323
  kind: "mutation",
13198
13324
  auth: "admin"
13199
13325
  });
13326
+ /**
13327
+ * `log-channels` — the capability an addon DECLARES its diagnostic channels
13328
+ * through. It stores nothing.
13329
+ *
13330
+ * ## Why a capability at all, and why this shape
13331
+ *
13332
+ * Which channels exist is knowledge only the addon has: `stream-broker` knows
13333
+ * webrtc/ICE/RTP, `provider-reolink` knows baichuan/handshake. A central list
13334
+ * maintained by hand rots at the first addition and rots INVISIBLY — nothing
13335
+ * fails, an operator just never sees the channel somebody added. So the list
13336
+ * is assembled from declarations at runtime.
13337
+ *
13338
+ * The shape is copied from `log-destination.cap.ts`, which already does
13339
+ * exactly this job: `mode: 'collection'`, `internal: true`,
13340
+ * `mount: { kind: 'skip' }` — no tRPC route, no generated hooks — while
13341
+ * `addons.listCapabilityProviders` still enumerates it, and the hub's
13342
+ * `CapabilityRegistry` still holds an RPC proxy per provider so a forked
13343
+ * runner's declarations reach hub-main over the transport that already exists.
13344
+ * No new UDS message, no second registry.
13345
+ *
13346
+ * ## What it deliberately does NOT own
13347
+ *
13348
+ * The VALUES — which channel is armed, for which cameras, until when — live in
13349
+ * ONE place: the logging settings document on the `system` cap
13350
+ * (`getLoggingSettings` / `setLoggingSettings`). Two authorities over the same
13351
+ * value is the defect the plan behind this work exists to remove, and
13352
+ * `setRequestCensus` was retired (D245) rather than allowed to be a second
13353
+ * one. {@link logChannelsCapability} therefore has no getter for a level, no
13354
+ * setter for a window and no persistence of any kind.
13355
+ *
13356
+ * ## Why `apply` is here even so
13357
+ *
13358
+ * The gate lives in the addon's PROCESS; the document lives in hub-main. Some
13359
+ * seam has to carry the value from the authority to the mirror, and a channel
13360
+ * that cannot be reached is precisely the dead knob this whole slice exists to
13361
+ * make impossible (D62 — `audioThresholdDbfs`, the HA entities with no source).
13362
+ * `apply` is that seam and nothing more: it writes an in-memory mirror, it
13363
+ * persists nothing, it is never the source of a value, and it is called only
13364
+ * with a set the hub actually read (D49 — a read that fails does not call it
13365
+ * at all, so no channel is silently disarmed by a bad read).
13366
+ */
13367
+ /** What `apply` reports back — enough to log, not enough to be a second state. */
13368
+ var LogChannelApplyResultSchema = object({
13369
+ /** How many declared channels are armed in this process after the call. */
13370
+ armed: number().int().min(0),
13371
+ /**
13372
+ * Names the document armed that this process does not declare. Reported
13373
+ * rather than swallowed: a name here is either a typo or an addon that has
13374
+ * not booted, and both deserve a line instead of silence.
13375
+ */
13376
+ unknown: array(string()).readonly()
13377
+ });
13378
+ method(_void(), array(LogChannelDescriptorSchema).readonly()), method(object({ windows: array(LogChannelWindowSchema).readonly() }), LogChannelApplyResultSchema, { kind: "mutation" });
13200
13379
  var LogLevelSchema = _enum([
13201
13380
  "debug",
13202
13381
  "info",
@@ -28641,10 +28820,11 @@ var DiagnosticIdSchema = _enum(["request-census"]);
28641
28820
  * The layers of the level hierarchy, general → specific. The most specific
28642
28821
  * layer that carries an explicit value wins.
28643
28822
  *
28644
- * `component` is DECLARED and not yet resolvable: the per-component channels
28645
- * are a later slice of the same plan, and a `levelSource` enum that has to
28646
- * grow later would force every consumer of this document to change with it.
28647
- * Nothing returns `component` today.
28823
+ * `component` became RESOLVABLE on 2026-08-27: a component is a declared log
28824
+ * CHANNEL (`stream-broker.webrtc`, `provider-reolink.baichuan`), named by
28825
+ * `scopeComponent`. It was declared-but-dark in the first slice precisely so
28826
+ * that turning it on would not force every consumer of this document to widen
28827
+ * a `levelSource` enum — which is what has now not happened.
28648
28828
  */
28649
28829
  var LoggingScopeKindSchema = _enum([
28650
28830
  "cluster",
@@ -28671,6 +28851,14 @@ var LoggingLevelLayerSchema = object({
28671
28851
  scope: LoggingScopeKindSchema,
28672
28852
  /** The node this layer speaks for; `null` on the cluster layer. */
28673
28853
  nodeId: string().nullable(),
28854
+ /**
28855
+ * The declared channel this layer speaks for; `null` on every layer but
28856
+ * `component`. Never folded into `nodeId`: a component level is CLUSTER-WIDE
28857
+ * by design — the convention this repo settled on is one orchestrator-wide
28858
+ * setting, never per node (D52) — so a component layer that carried a node
28859
+ * would invite a per-node copy of a value that has no per-node meaning.
28860
+ */
28861
+ component: string().nullable(),
28674
28862
  /** Explicitly set here, or `null` when this layer inherits. */
28675
28863
  level: LogLevelSchema$1.nullable()
28676
28864
  });
@@ -28712,6 +28900,49 @@ var DiagnosticWindowPatchSchema = object({
28712
28900
  reportEveryMs: number().int().positive().optional()
28713
28901
  });
28714
28902
  /**
28903
+ * A channel ARMED, as the document reports it.
28904
+ *
28905
+ * `armMs` is not echoed back: what an operator needs to see is the deadline
28906
+ * and the time left, because a diagnostic left running is itself an incident
28907
+ * and "armed for 10 minutes" said an hour ago is not an answer.
28908
+ */
28909
+ var LogChannelWindowStateSchema = object({
28910
+ channel: string(),
28911
+ armed: boolean(),
28912
+ /** Epoch ms the window closes at. 0 when disarmed. */
28913
+ armedUntilMs: number(),
28914
+ /** Ms left before it expires on its own. 0 when disarmed. */
28915
+ remainingMs: number(),
28916
+ /**
28917
+ * The cameras it is narrowed to, or `null` for every camera.
28918
+ *
28919
+ * A channel declared `perDevice: false` can only ever report `null` here:
28920
+ * its lines do not carry `tags: { deviceId }`, so narrowing them would
28921
+ * produce a filter that silently matches nothing. The server REFUSES such a
28922
+ * patch rather than quietly widening it — ignoring the request would teach
28923
+ * the operator that per-camera filtering works on that channel when it does
28924
+ * not.
28925
+ */
28926
+ deviceIds: array(number().int()).readonly().nullable()
28927
+ });
28928
+ /**
28929
+ * `armMs: 0` DISARMS. Same grammar as {@link DiagnosticWindowPatchSchema}, and
28930
+ * for the same reason: a channel is a window with a deadline, never a switch.
28931
+ */
28932
+ var LogChannelWindowPatchSchema = object({
28933
+ channel: string().min(1),
28934
+ armMs: number().int().min(0),
28935
+ /**
28936
+ * Narrow to these numeric device ids. Absent or `null` = every camera.
28937
+ *
28938
+ * Numeric because the repo's own rule makes it possible: every log line
28939
+ * about a device carries `tags: { deviceId }` with the numeric id. That rule
28940
+ * was paid for with a 22% thumbnail gap and a 3-hour media blackout both
28941
+ * diagnosed by hand, and this is the first thing that collects on it.
28942
+ */
28943
+ deviceIds: array(number().int()).readonly().nullable().optional()
28944
+ });
28945
+ /**
28715
28946
  * A PATCH, and patches MERGE.
28716
28947
  *
28717
28948
  * A field absent from the patch is left exactly as it was — arming a
@@ -28730,7 +28961,14 @@ var LoggingSettingsPatchSchema = object({
28730
28961
  * Only the diagnostics NAMED here change. An armed window that is not listed
28731
28962
  * keeps running — a patch is never a full replacement.
28732
28963
  */
28733
- diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional()
28964
+ diagnostics: array(DiagnosticWindowPatchSchema).readonly().optional(),
28965
+ /**
28966
+ * Only the channels NAMED here change. An armed channel that is not listed
28967
+ * keeps running — same rule as `diagnostics`, because a patch that silently
28968
+ * disarmed the channels it did not mention would make the Levels page and
28969
+ * the Diagnostics page fight over the same value.
28970
+ */
28971
+ channels: array(LogChannelWindowPatchSchema).readonly().optional()
28734
28972
  });
28735
28973
  /**
28736
28974
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -28743,9 +28981,22 @@ var LoggingSettingsPatchSchema = object({
28743
28981
  * authority over the whole hierarchy and answers for every layer, so the
28744
28982
  * layer selector needs a name the transport does not already own.
28745
28983
  */
28746
- var GetLoggingSettingsInputSchema = object({ scopeNodeId: string().optional() });
28984
+ var GetLoggingSettingsInputSchema = object({
28985
+ scopeNodeId: string().optional(),
28986
+ /**
28987
+ * The declared CHANNEL this document is addressed at, when the caller wants
28988
+ * the `component` layer. Absent = the node/cluster hierarchy only.
28989
+ *
28990
+ * Naming it separately rather than overloading `scopeNodeId` keeps the two
28991
+ * axes from collapsing: a component level is cluster-wide, a node level is
28992
+ * not, and one selector for both would make "which of these two did I just
28993
+ * set" unanswerable — the exact ambiguity `explicit` exists to remove.
28994
+ */
28995
+ scopeComponent: string().optional()
28996
+ });
28747
28997
  var SetLoggingSettingsInputSchema = object({
28748
28998
  scopeNodeId: string().optional(),
28999
+ scopeComponent: string().optional(),
28749
29000
  patch: LoggingSettingsPatchSchema
28750
29001
  });
28751
29002
  /**
@@ -28760,9 +29011,20 @@ var SetLoggingSettingsInputSchema = object({
28760
29011
  var LoggingSettingsStateSchema = object({
28761
29012
  /** The layer this document was read at. `null` = the cluster layer. */
28762
29013
  scopeNodeId: string().nullable(),
29014
+ /** The channel this document was read at. `null` = no component layer. */
29015
+ scopeComponent: string().nullable(),
28763
29016
  effective: LoggingEffectiveSchema,
28764
29017
  explicit: LoggingExplicitSchema,
28765
29018
  activeWindows: array(DiagnosticWindowSchema).readonly(),
29019
+ /**
29020
+ * Every channel the cluster's addons DECLARE, gathered from the
29021
+ * `log-channels` providers. Not stored anywhere: assembled per read, so a
29022
+ * channel added by a redeployed addon appears without anybody editing a
29023
+ * list, and a channel whose addon is gone stops being offered.
29024
+ */
29025
+ channels: array(LogChannelDescriptorSchema).readonly(),
29026
+ /** The channels ARMED right now, each with its deadline. */
29027
+ activeChannels: array(LogChannelWindowStateSchema).readonly(),
28766
29028
  persisted: boolean()
28767
29029
  });
28768
29030
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -31747,6 +32009,12 @@ Object.freeze({
31747
32009
  addonId: null,
31748
32010
  access: "view"
31749
32011
  },
32012
+ "dataStoreProvider.aggregate": {
32013
+ capName: "data-store-provider",
32014
+ capScope: "system",
32015
+ addonId: null,
32016
+ access: "view"
32017
+ },
31750
32018
  "dataStoreProvider.count": {
31751
32019
  capName: "data-store-provider",
31752
32020
  capScope: "system",
@@ -32161,6 +32429,12 @@ Object.freeze({
32161
32429
  addonId: null,
32162
32430
  access: "view"
32163
32431
  },
32432
+ "deviceManager.getChildrenBatch": {
32433
+ capName: "device-manager",
32434
+ capScope: "system",
32435
+ addonId: null,
32436
+ access: "view"
32437
+ },
32164
32438
  "deviceManager.getConfigSchema": {
32165
32439
  capName: "device-manager",
32166
32440
  capScope: "system",
@@ -33211,6 +33485,18 @@ Object.freeze({
33211
33485
  addonId: null,
33212
33486
  access: "create"
33213
33487
  },
33488
+ "logChannels.apply": {
33489
+ capName: "log-channels",
33490
+ capScope: "system",
33491
+ addonId: null,
33492
+ access: "create"
33493
+ },
33494
+ "logChannels.list": {
33495
+ capName: "log-channels",
33496
+ capScope: "system",
33497
+ addonId: null,
33498
+ access: "view"
33499
+ },
33214
33500
  "logDestination.query": {
33215
33501
  capName: "log-destination",
33216
33502
  capScope: "system",
@@ -35365,6 +35651,12 @@ Object.freeze({
35365
35651
  addonId: null,
35366
35652
  access: "create"
35367
35653
  },
35654
+ "settingsStore.aggregate": {
35655
+ capName: "settings-store",
35656
+ capScope: "system",
35657
+ addonId: null,
35658
+ access: "view"
35659
+ },
35368
35660
  "settingsStore.count": {
35369
35661
  capName: "settings-store",
35370
35662
  capScope: "system",
@@ -36944,6 +37236,11 @@ Object.freeze({
36944
37236
  form: "single",
36945
37237
  optional: false
36946
37238
  }],
37239
+ "deviceManager.getChildrenBatch": [{
37240
+ name: "parentDeviceIds",
37241
+ form: "array",
37242
+ optional: false
37243
+ }],
36947
37244
  "deviceManager.getConfigSchema": [{
36948
37245
  name: "deviceId",
36949
37246
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.2.34",
3
+ "version": "1.2.35",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",