@camstack/addon-provider-homematic 1.2.79 → 1.2.80

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 +125 -127
  2. package/dist/addon.mjs +125 -127
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7624,111 +7624,6 @@ var CameraSwitchGroupSchema = object({
7624
7624
  fetchedAt: number()
7625
7625
  });
7626
7626
  /**
7627
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7628
- * an addon declares its channels in.
7629
- *
7630
- * ## Two axes, deliberately separated
7631
- *
7632
- * - **DECLARATION** — which channels exist. Only the addon knows:
7633
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7634
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7635
- * and rots silently. So a channel is declared where it is consulted, and the
7636
- * `log-channels` capability enumerates the declarations.
7637
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7638
- * thing: the logging settings document on the `system` cap. Two authorities
7639
- * over the values is the exact defect
7640
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7641
- * remove; re-introducing it from the cure side would be grotesque.
7642
- *
7643
- * Nothing in this file reads a clock, an env var or a store. The registry is
7644
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7645
- * the hot path with a value somebody actually read, and by
7646
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7647
- * never reaches here, so it can neither disarm an armed channel nor arm a
7648
- * disarmed one (D49).
7649
- *
7650
- * ## The canonical call shape
7651
- *
7652
- * ```ts
7653
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7654
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7655
- * }
7656
- * ```
7657
- *
7658
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7659
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7660
- * object literal is never constructed because it lives inside the branch. It
7661
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7662
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7663
- * destination floor (measured at 1.93 ns/call when off).
7664
- *
7665
- * ## Why a channel emits at `info`
7666
- *
7667
- * `loki-logging.addon.ts` pins the destination default at `info` and
7668
- * `loki-destination.ts` drops everything below it, so a line emitted at
7669
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7670
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7671
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7672
- * emits at the channel's declared level, whose schema floor is `info`.
7673
- */
7674
- /**
7675
- * The level a channel writes at once armed.
7676
- *
7677
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7678
- * not leave the process for Loki, and the whole point of arming a channel is
7679
- * to read it later.
7680
- */
7681
- var LogChannelLevelSchema = _enum([
7682
- "info",
7683
- "warn",
7684
- "error"
7685
- ]);
7686
- /**
7687
- * What an addon declares about one channel. No value, no state — a
7688
- * declaration is inert.
7689
- */
7690
- var LogChannelDescriptorSchema = object({
7691
- /**
7692
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7693
- * the addon's short name so an operator reading a channel list can tell who
7694
- * owns it without a second lookup.
7695
- */
7696
- name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7697
- /** One sentence: what the operator will SEE after arming it. */
7698
- description: string().min(1),
7699
- /** The level its lines are emitted at. Never below `info`. */
7700
- defaultLevel: LogChannelLevelSchema,
7701
- /**
7702
- * Whether this channel can be narrowed to a camera.
7703
- *
7704
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7705
- * consulted with the numeric device id, AND every line the channel admits
7706
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7707
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7708
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7709
- * the body is the only way to filter.
7710
- *
7711
- * A channel whose lines carry the device only in `meta` (or not at all) is
7712
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7713
- * the operator narrows to one camera, sees nothing, and concludes the code
7714
- * path was never taken.
7715
- */
7716
- perDevice: boolean()
7717
- });
7718
- /**
7719
- * An armed window over one channel, as the document hands it to a mirror.
7720
- *
7721
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7722
- * expires by itself, which is the one failure a boolean cannot avoid.
7723
- */
7724
- var LogChannelWindowSchema = object({
7725
- channel: string().min(1),
7726
- /** Epoch ms the window closes at. */
7727
- armedUntilMs: number(),
7728
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7729
- deviceIds: array(number().int()).readonly().nullable()
7730
- });
7731
- /**
7732
7627
  * Ops-log — the durable, append-only operations audit shared by the
7733
7628
  * recordings and events management surfaces.
7734
7629
  *
@@ -8674,8 +8569,11 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8674
8569
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8675
8570
  *
8676
8571
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8677
- * The default location for a type uses `id === <type>:default` by
8678
- * convention (the bare type ref like `'backups'` resolves to it).
8572
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8573
+ * There is no default location any more (D383): `enabled` is the whole write
8574
+ * model, and a bare type ref resolves to the sole location of the type, or —
8575
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8576
+ * slug is `default`.
8679
8577
  *
8680
8578
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8681
8579
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8696,21 +8594,20 @@ var StorageLocationSchema = object({
8696
8594
  * flag at upsert time, not here (the schema is provider-agnostic).
8697
8595
  */
8698
8596
  nodeId: string().optional(),
8699
- isDefault: boolean().default(false),
8700
8597
  isSystem: boolean().default(false),
8701
8598
  /**
8702
- * Operator opt-in: whether consumers that BALANCE across several locations
8703
- * of a type may write here. Recordings reads it today; event media and
8704
- * backups are the next consumers, which is why the flag lives on the
8705
- * location rather than in any one addon's store — nothing has to be
8706
- * extended to add the next consumer.
8599
+ * THE write switch, and the only one (D383). `enabled: true` means every
8600
+ * consumer that chooses a write target for this type may write here, and all
8601
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8602
+ * still read, still played back, still age-swept, still drained, never
8603
+ * written.
8707
8604
  *
8708
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8709
- * flag existed reads back with no flag and keeps working exactly as before;
8710
- * that is the whole compat story, and it is why no migration ships with it.
8711
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8712
- * disk must not silently start writing to it); the default of a type is
8713
- * always stamped `true`.
8605
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8606
+ * stored" on an update and "born inert unless it is the first location of its
8607
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8608
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8609
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8610
+ * stops existing rather than being re-derived on every read.
8714
8611
  */
8715
8612
  enabled: boolean().optional(),
8716
8613
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8723,10 +8620,12 @@ var StorageLocationSchema = object({
8723
8620
  createdAt: number(),
8724
8621
  updatedAt: number()
8725
8622
  });
8623
+ object({ isDefault: boolean().optional() });
8726
8624
  /**
8727
8625
  * Reference accepted by consumer-facing `api.storage.*` calls.
8728
8626
  * Either:
8729
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8627
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8628
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8730
8629
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8731
8630
  *
8732
8631
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -8902,6 +8801,111 @@ var DecoderSessionConfigSchema = object({
8902
8801
  */
8903
8802
  debug: boolean().optional()
8904
8803
  });
8804
+ /**
8805
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
8806
+ * an addon declares its channels in.
8807
+ *
8808
+ * ## Two axes, deliberately separated
8809
+ *
8810
+ * - **DECLARATION** — which channels exist. Only the addon knows:
8811
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
8812
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
8813
+ * and rots silently. So a channel is declared where it is consulted, and the
8814
+ * `log-channels` capability enumerates the declarations.
8815
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
8816
+ * thing: the logging settings document on the `system` cap. Two authorities
8817
+ * over the values is the exact defect
8818
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
8819
+ * remove; re-introducing it from the cure side would be grotesque.
8820
+ *
8821
+ * Nothing in this file reads a clock, an env var or a store. The registry is
8822
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
8823
+ * the hot path with a value somebody actually read, and by
8824
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
8825
+ * never reaches here, so it can neither disarm an armed channel nor arm a
8826
+ * disarmed one (D49).
8827
+ *
8828
+ * ## The canonical call shape
8829
+ *
8830
+ * ```ts
8831
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
8832
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
8833
+ * }
8834
+ * ```
8835
+ *
8836
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
8837
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
8838
+ * object literal is never constructed because it lives inside the branch. It
8839
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
8840
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
8841
+ * destination floor (measured at 1.93 ns/call when off).
8842
+ *
8843
+ * ## Why a channel emits at `info`
8844
+ *
8845
+ * `loki-logging.addon.ts` pins the destination default at `info` and
8846
+ * `loki-destination.ts` drops everything below it, so a line emitted at
8847
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
8848
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
8849
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
8850
+ * emits at the channel's declared level, whose schema floor is `info`.
8851
+ */
8852
+ /**
8853
+ * The level a channel writes at once armed.
8854
+ *
8855
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
8856
+ * not leave the process for Loki, and the whole point of arming a channel is
8857
+ * to read it later.
8858
+ */
8859
+ var LogChannelLevelSchema = _enum([
8860
+ "info",
8861
+ "warn",
8862
+ "error"
8863
+ ]);
8864
+ /**
8865
+ * What an addon declares about one channel. No value, no state — a
8866
+ * declaration is inert.
8867
+ */
8868
+ var LogChannelDescriptorSchema = object({
8869
+ /**
8870
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
8871
+ * the addon's short name so an operator reading a channel list can tell who
8872
+ * owns it without a second lookup.
8873
+ */
8874
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
8875
+ /** One sentence: what the operator will SEE after arming it. */
8876
+ description: string().min(1),
8877
+ /** The level its lines are emitted at. Never below `info`. */
8878
+ defaultLevel: LogChannelLevelSchema,
8879
+ /**
8880
+ * Whether this channel can be narrowed to a camera.
8881
+ *
8882
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
8883
+ * consulted with the numeric device id, AND every line the channel admits
8884
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
8885
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
8886
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
8887
+ * the body is the only way to filter.
8888
+ *
8889
+ * A channel whose lines carry the device only in `meta` (or not at all) is
8890
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
8891
+ * the operator narrows to one camera, sees nothing, and concludes the code
8892
+ * path was never taken.
8893
+ */
8894
+ perDevice: boolean()
8895
+ });
8896
+ /**
8897
+ * An armed window over one channel, as the document hands it to a mirror.
8898
+ *
8899
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
8900
+ * expires by itself, which is the one failure a boolean cannot avoid.
8901
+ */
8902
+ var LogChannelWindowSchema = object({
8903
+ channel: string().min(1),
8904
+ /** Epoch ms the window closes at. */
8905
+ armedUntilMs: number(),
8906
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
8907
+ deviceIds: array(number().int()).readonly().nullable()
8908
+ });
8905
8909
  var MODEL_FORMATS = [
8906
8910
  "onnx",
8907
8911
  "coreml",
@@ -22000,7 +22004,7 @@ method(object({
22000
22004
  downloadId: string(),
22001
22005
  offset: number(),
22002
22006
  length: number()
22003
- }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
22007
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
22004
22008
  createdAt: true,
22005
22009
  updatedAt: true
22006
22010
  }), StorageLocationSchema, {
@@ -38845,12 +38849,6 @@ Object.freeze({
38845
38849
  addonId: null,
38846
38850
  access: "view"
38847
38851
  },
38848
- "storage.getDefaultLocation": {
38849
- capName: "storage",
38850
- capScope: "system",
38851
- addonId: null,
38852
- access: "view"
38853
- },
38854
38852
  "storage.list": {
38855
38853
  capName: "storage",
38856
38854
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -7625,111 +7625,6 @@ var CameraSwitchGroupSchema = object({
7625
7625
  fetchedAt: number()
7626
7626
  });
7627
7627
  /**
7628
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7629
- * an addon declares its channels in.
7630
- *
7631
- * ## Two axes, deliberately separated
7632
- *
7633
- * - **DECLARATION** — which channels exist. Only the addon knows:
7634
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7635
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7636
- * and rots silently. So a channel is declared where it is consulted, and the
7637
- * `log-channels` capability enumerates the declarations.
7638
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7639
- * thing: the logging settings document on the `system` cap. Two authorities
7640
- * over the values is the exact defect
7641
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7642
- * remove; re-introducing it from the cure side would be grotesque.
7643
- *
7644
- * Nothing in this file reads a clock, an env var or a store. The registry is
7645
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7646
- * the hot path with a value somebody actually read, and by
7647
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7648
- * never reaches here, so it can neither disarm an armed channel nor arm a
7649
- * disarmed one (D49).
7650
- *
7651
- * ## The canonical call shape
7652
- *
7653
- * ```ts
7654
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7655
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7656
- * }
7657
- * ```
7658
- *
7659
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7660
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7661
- * object literal is never constructed because it lives inside the branch. It
7662
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7663
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7664
- * destination floor (measured at 1.93 ns/call when off).
7665
- *
7666
- * ## Why a channel emits at `info`
7667
- *
7668
- * `loki-logging.addon.ts` pins the destination default at `info` and
7669
- * `loki-destination.ts` drops everything below it, so a line emitted at
7670
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7671
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7672
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7673
- * emits at the channel's declared level, whose schema floor is `info`.
7674
- */
7675
- /**
7676
- * The level a channel writes at once armed.
7677
- *
7678
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7679
- * not leave the process for Loki, and the whole point of arming a channel is
7680
- * to read it later.
7681
- */
7682
- var LogChannelLevelSchema = _enum([
7683
- "info",
7684
- "warn",
7685
- "error"
7686
- ]);
7687
- /**
7688
- * What an addon declares about one channel. No value, no state — a
7689
- * declaration is inert.
7690
- */
7691
- var LogChannelDescriptorSchema = object({
7692
- /**
7693
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7694
- * the addon's short name so an operator reading a channel list can tell who
7695
- * owns it without a second lookup.
7696
- */
7697
- name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7698
- /** One sentence: what the operator will SEE after arming it. */
7699
- description: string().min(1),
7700
- /** The level its lines are emitted at. Never below `info`. */
7701
- defaultLevel: LogChannelLevelSchema,
7702
- /**
7703
- * Whether this channel can be narrowed to a camera.
7704
- *
7705
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7706
- * consulted with the numeric device id, AND every line the channel admits
7707
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7708
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7709
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7710
- * the body is the only way to filter.
7711
- *
7712
- * A channel whose lines carry the device only in `meta` (or not at all) is
7713
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7714
- * the operator narrows to one camera, sees nothing, and concludes the code
7715
- * path was never taken.
7716
- */
7717
- perDevice: boolean()
7718
- });
7719
- /**
7720
- * An armed window over one channel, as the document hands it to a mirror.
7721
- *
7722
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7723
- * expires by itself, which is the one failure a boolean cannot avoid.
7724
- */
7725
- var LogChannelWindowSchema = object({
7726
- channel: string().min(1),
7727
- /** Epoch ms the window closes at. */
7728
- armedUntilMs: number(),
7729
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7730
- deviceIds: array(number().int()).readonly().nullable()
7731
- });
7732
- /**
7733
7628
  * Ops-log — the durable, append-only operations audit shared by the
7734
7629
  * recordings and events management surfaces.
7735
7630
  *
@@ -8675,8 +8570,11 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8675
8570
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8676
8571
  *
8677
8572
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8678
- * The default location for a type uses `id === <type>:default` by
8679
- * convention (the bare type ref like `'backups'` resolves to it).
8573
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8574
+ * There is no default location any more (D383): `enabled` is the whole write
8575
+ * model, and a bare type ref resolves to the sole location of the type, or —
8576
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8577
+ * slug is `default`.
8680
8578
  *
8681
8579
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8682
8580
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8697,21 +8595,20 @@ var StorageLocationSchema = object({
8697
8595
  * flag at upsert time, not here (the schema is provider-agnostic).
8698
8596
  */
8699
8597
  nodeId: string().optional(),
8700
- isDefault: boolean().default(false),
8701
8598
  isSystem: boolean().default(false),
8702
8599
  /**
8703
- * Operator opt-in: whether consumers that BALANCE across several locations
8704
- * of a type may write here. Recordings reads it today; event media and
8705
- * backups are the next consumers, which is why the flag lives on the
8706
- * location rather than in any one addon's store — nothing has to be
8707
- * extended to add the next consumer.
8600
+ * THE write switch, and the only one (D383). `enabled: true` means every
8601
+ * consumer that chooses a write target for this type may write here, and all
8602
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8603
+ * still read, still played back, still age-swept, still drained, never
8604
+ * written.
8708
8605
  *
8709
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8710
- * flag existed reads back with no flag and keeps working exactly as before;
8711
- * that is the whole compat story, and it is why no migration ships with it.
8712
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8713
- * disk must not silently start writing to it); the default of a type is
8714
- * always stamped `true`.
8606
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8607
+ * stored" on an update and "born inert unless it is the first location of its
8608
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8609
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8610
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8611
+ * stops existing rather than being re-derived on every read.
8715
8612
  */
8716
8613
  enabled: boolean().optional(),
8717
8614
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8724,10 +8621,12 @@ var StorageLocationSchema = object({
8724
8621
  createdAt: number(),
8725
8622
  updatedAt: number()
8726
8623
  });
8624
+ object({ isDefault: boolean().optional() });
8727
8625
  /**
8728
8626
  * Reference accepted by consumer-facing `api.storage.*` calls.
8729
8627
  * Either:
8730
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8628
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8629
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8731
8630
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8732
8631
  *
8733
8632
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -8903,6 +8802,111 @@ var DecoderSessionConfigSchema = object({
8903
8802
  */
8904
8803
  debug: boolean().optional()
8905
8804
  });
8805
+ /**
8806
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
8807
+ * an addon declares its channels in.
8808
+ *
8809
+ * ## Two axes, deliberately separated
8810
+ *
8811
+ * - **DECLARATION** — which channels exist. Only the addon knows:
8812
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
8813
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
8814
+ * and rots silently. So a channel is declared where it is consulted, and the
8815
+ * `log-channels` capability enumerates the declarations.
8816
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
8817
+ * thing: the logging settings document on the `system` cap. Two authorities
8818
+ * over the values is the exact defect
8819
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
8820
+ * remove; re-introducing it from the cure side would be grotesque.
8821
+ *
8822
+ * Nothing in this file reads a clock, an env var or a store. The registry is
8823
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
8824
+ * the hot path with a value somebody actually read, and by
8825
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
8826
+ * never reaches here, so it can neither disarm an armed channel nor arm a
8827
+ * disarmed one (D49).
8828
+ *
8829
+ * ## The canonical call shape
8830
+ *
8831
+ * ```ts
8832
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
8833
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
8834
+ * }
8835
+ * ```
8836
+ *
8837
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
8838
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
8839
+ * object literal is never constructed because it lives inside the branch. It
8840
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
8841
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
8842
+ * destination floor (measured at 1.93 ns/call when off).
8843
+ *
8844
+ * ## Why a channel emits at `info`
8845
+ *
8846
+ * `loki-logging.addon.ts` pins the destination default at `info` and
8847
+ * `loki-destination.ts` drops everything below it, so a line emitted at
8848
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
8849
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
8850
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
8851
+ * emits at the channel's declared level, whose schema floor is `info`.
8852
+ */
8853
+ /**
8854
+ * The level a channel writes at once armed.
8855
+ *
8856
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
8857
+ * not leave the process for Loki, and the whole point of arming a channel is
8858
+ * to read it later.
8859
+ */
8860
+ var LogChannelLevelSchema = _enum([
8861
+ "info",
8862
+ "warn",
8863
+ "error"
8864
+ ]);
8865
+ /**
8866
+ * What an addon declares about one channel. No value, no state — a
8867
+ * declaration is inert.
8868
+ */
8869
+ var LogChannelDescriptorSchema = object({
8870
+ /**
8871
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
8872
+ * the addon's short name so an operator reading a channel list can tell who
8873
+ * owns it without a second lookup.
8874
+ */
8875
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
8876
+ /** One sentence: what the operator will SEE after arming it. */
8877
+ description: string().min(1),
8878
+ /** The level its lines are emitted at. Never below `info`. */
8879
+ defaultLevel: LogChannelLevelSchema,
8880
+ /**
8881
+ * Whether this channel can be narrowed to a camera.
8882
+ *
8883
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
8884
+ * consulted with the numeric device id, AND every line the channel admits
8885
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
8886
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
8887
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
8888
+ * the body is the only way to filter.
8889
+ *
8890
+ * A channel whose lines carry the device only in `meta` (or not at all) is
8891
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
8892
+ * the operator narrows to one camera, sees nothing, and concludes the code
8893
+ * path was never taken.
8894
+ */
8895
+ perDevice: boolean()
8896
+ });
8897
+ /**
8898
+ * An armed window over one channel, as the document hands it to a mirror.
8899
+ *
8900
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
8901
+ * expires by itself, which is the one failure a boolean cannot avoid.
8902
+ */
8903
+ var LogChannelWindowSchema = object({
8904
+ channel: string().min(1),
8905
+ /** Epoch ms the window closes at. */
8906
+ armedUntilMs: number(),
8907
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
8908
+ deviceIds: array(number().int()).readonly().nullable()
8909
+ });
8906
8910
  var MODEL_FORMATS = [
8907
8911
  "onnx",
8908
8912
  "coreml",
@@ -22001,7 +22005,7 @@ method(object({
22001
22005
  downloadId: string(),
22002
22006
  offset: number(),
22003
22007
  length: number()
22004
- }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
22008
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
22005
22009
  createdAt: true,
22006
22010
  updatedAt: true
22007
22011
  }), StorageLocationSchema, {
@@ -38846,12 +38850,6 @@ Object.freeze({
38846
38850
  addonId: null,
38847
38851
  access: "view"
38848
38852
  },
38849
- "storage.getDefaultLocation": {
38850
- capName: "storage",
38851
- capScope: "system",
38852
- addonId: null,
38853
- access: "view"
38854
- },
38855
38853
  "storage.list": {
38856
38854
  capName: "storage",
38857
38855
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.2.79",
3
+ "version": "1.2.80",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",