@camstack/types 1.2.140 → 1.2.141

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.
@@ -9,7 +9,7 @@ import { DeviceType } from '../device/device-type.js';
9
9
  * threshold.
10
10
  */
11
11
  export declare const BatteryStatusSchema: z.ZodObject<{
12
- percentage: z.ZodNumber;
12
+ percentage: z.ZodNullable<z.ZodNumber>;
13
13
  charging: z.ZodEnum<{
14
14
  none: "none";
15
15
  dc: "dc";
@@ -63,7 +63,7 @@ export declare const batteryCapability: {
63
63
  readonly data: z.ZodObject<{
64
64
  deviceId: z.ZodNumber;
65
65
  status: z.ZodObject<{
66
- percentage: z.ZodNumber;
66
+ percentage: z.ZodNullable<z.ZodNumber>;
67
67
  charging: z.ZodEnum<{
68
68
  none: "none";
69
69
  dc: "dc";
@@ -79,7 +79,7 @@ export declare const batteryCapability: {
79
79
  };
80
80
  readonly status: {
81
81
  readonly schema: z.ZodObject<{
82
- percentage: z.ZodNumber;
82
+ percentage: z.ZodNullable<z.ZodNumber>;
83
83
  charging: z.ZodEnum<{
84
84
  none: "none";
85
85
  dc: "dc";
@@ -108,7 +108,7 @@ export declare const batteryCapability: {
108
108
  * the underlying driver.
109
109
  */
110
110
  readonly runtimeState: z.ZodObject<{
111
- percentage: z.ZodNumber;
111
+ percentage: z.ZodNullable<z.ZodNumber>;
112
112
  charging: z.ZodEnum<{
113
113
  none: "none";
114
114
  dc: "dc";
@@ -1106,6 +1106,7 @@ declare const RecentTracksQueryInput: z.ZodObject<{
1106
1106
  slim: "slim";
1107
1107
  }>>;
1108
1108
  includeStationary: z.ZodOptional<z.ZodBoolean>;
1109
+ classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1109
1110
  }, z.core.$strip>;
1110
1111
  export type RecentTracksQuery = z.infer<typeof RecentTracksQueryInput>;
1111
1112
  declare const RecentTracksPageSchema: z.ZodObject<{
@@ -1907,6 +1908,7 @@ export declare const pipelineAnalyticsCapability: {
1907
1908
  slim: "slim";
1908
1909
  }>>;
1909
1910
  includeStationary: z.ZodOptional<z.ZodBoolean>;
1911
+ classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1910
1912
  }, z.core.$strip>, z.ZodObject<{
1911
1913
  tracks: z.ZodReadonly<z.ZodArray<z.ZodObject<{
1912
1914
  retrainStatus: z.ZodOptional<z.ZodEnum<{
@@ -14,6 +14,15 @@
14
14
  * Each carried the same list and a comment asking the others to stay in sync.
15
15
  * This is that list, in the one package all of them already depend on.
16
16
  *
17
+ * `ui-library` and the server's linked-devices expansion IMPORT it. Two
18
+ * consumers cannot, and keep a checked copy instead: the viewer resolves
19
+ * `@camstack/types` from its own `node_modules` (an installed release, where a
20
+ * newly added export simply is not there), and the Home Assistant provider
21
+ * expresses the same precedence over the `DeviceType` enum because it answers
22
+ * a different question from the same ordering. `scripts/check-container-
23
+ * priority-in-sync.ts` fails the build when either drifts — the comment that
24
+ * used to ask for this could not.
25
+ *
17
26
  * The rule has two halves and the ORDER matters: an operator's explicit pick
18
27
  * wins outright, and only in its absence does type priority decide. The pick is
19
28
  * keyed on the child's re-sync-stable `entityId`, not its numeric id, so it
@@ -34,8 +43,28 @@ export declare const CONTAINER_CHILD_PRIORITY: readonly string[];
34
43
  /** The slice of a child this resolution needs. */
35
44
  export interface ContainerChildRef {
36
45
  readonly id: number;
37
- /** Re-sync-stable identity (`sourceInfo.id`, falling back to `stableId`). */
38
- readonly entityId: string;
46
+ /**
47
+ * The CANONICAL key an operator's pick is matched on.
48
+ *
49
+ * `stableId` and not `sourceInfo.id`, because `sourceInfo` is rebuilt from
50
+ * the device's config blob and the projection that linked-devices must use
51
+ * (`slim`) does not read it — measured on the live hub, 677 of 925 container
52
+ * children have a `sourceInfo.id` that differs from their `stableId`, so a
53
+ * resolver keyed on it would have missed the operator's pick for three
54
+ * children in four and fallen back to priority WHILE LOOKING LIKE IT WORKED.
55
+ *
56
+ * `stableId` costs nothing to have: it is a NOT NULL column, and
57
+ * `(addonId, stableId)` is the very index the row store re-identifies a
58
+ * device by across a re-sync — which is exactly the durability the pick
59
+ * needs.
60
+ */
61
+ readonly stableId: string;
62
+ /**
63
+ * The integration's own entity id, when the caller has it. Accepted ONLY so
64
+ * a pick stored under the previous key still resolves; new picks are written
65
+ * as `stableId`. Absent is normal.
66
+ */
67
+ readonly entityId?: string;
39
68
  readonly type: string;
40
69
  }
41
70
  /**
package/dist/index.js CHANGED
@@ -18783,7 +18783,20 @@ var RecentTracksQueryInput = zod.z.object({
18783
18783
  projection: TrackProjectionSchema.optional(),
18784
18784
  /** Include stationary-promoted rows (parked objects). Default false: the
18785
18785
  * feed lists passages; parking records live on the stationary registry. */
18786
- includeStationary: zod.z.boolean().optional()
18786
+ includeStationary: zod.z.boolean().optional(),
18787
+ /**
18788
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
18789
+ *
18790
+ * The same filter `listTracks` takes, because the timeline's class chips must
18791
+ * mean the same thing whether the scope is one camera or twelve. Until this
18792
+ * existed the scoped feed downloaded a page and narrowed it on the phone
18793
+ * while the single-camera path narrowed the read — one filter, two costs.
18794
+ *
18795
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
18796
+ * whose class list is unreadable are kept, and the client's rule stays the
18797
+ * exact one.
18798
+ */
18799
+ classes: zod.z.array(zod.z.string()).optional()
18787
18800
  });
18788
18801
  var RecentTracksPageSchema = zod.z.object({
18789
18802
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -25123,8 +25136,22 @@ var automationControlCapability = {
25123
25136
  * threshold.
25124
25137
  */
25125
25138
  var BatteryStatusSchema = zod.z.object({
25126
- /** 0..100 inclusive. Firmware-reported. */
25127
- percentage: zod.z.number().min(0).max(100),
25139
+ /**
25140
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
25141
+ * provider has registered the capability but no reading has landed.
25142
+ *
25143
+ * It is nullable because it was not, and the only value a provider could
25144
+ * seed with was `0`. A battery camera behind an NVR therefore announced
25145
+ * itself at 0% on every start and corrected itself a moment later, which is
25146
+ * indistinguishable from a real flat battery: it fires the low-battery alert
25147
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
25148
+ * reading the difference is an alarm.
25149
+ *
25150
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
25151
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
25152
+ * declines to band a non-finite reading, which is the correct shape.
25153
+ */
25154
+ percentage: zod.z.number().min(0).max(100).nullable(),
25128
25155
  /**
25129
25156
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
25130
25157
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -35273,6 +35300,15 @@ var RUNTIME_DEFAULTS = {
35273
35300
  * Each carried the same list and a comment asking the others to stay in sync.
35274
35301
  * This is that list, in the one package all of them already depend on.
35275
35302
  *
35303
+ * `ui-library` and the server's linked-devices expansion IMPORT it. Two
35304
+ * consumers cannot, and keep a checked copy instead: the viewer resolves
35305
+ * `@camstack/types` from its own `node_modules` (an installed release, where a
35306
+ * newly added export simply is not there), and the Home Assistant provider
35307
+ * expresses the same precedence over the `DeviceType` enum because it answers
35308
+ * a different question from the same ordering. `scripts/check-container-
35309
+ * priority-in-sync.ts` fails the build when either drifts — the comment that
35310
+ * used to ask for this could not.
35311
+ *
35276
35312
  * The rule has two halves and the ORDER matters: an operator's explicit pick
35277
35313
  * wins outright, and only in its absence does type priority decide. The pick is
35278
35314
  * keyed on the child's re-sync-stable `entityId`, not its numeric id, so it
@@ -35328,7 +35364,7 @@ function rank(type) {
35328
35364
  */
35329
35365
  function resolveContainerPrimaryChild(children, overrideEntityId) {
35330
35366
  if (overrideEntityId !== void 0 && overrideEntityId !== null) {
35331
- const picked = children.find((c) => c.entityId === overrideEntityId);
35367
+ const picked = children.find((c) => c.stableId === overrideEntityId) ?? children.find((c) => c.entityId !== void 0 && c.entityId === overrideEntityId);
35332
35368
  if (picked !== void 0) return picked;
35333
35369
  }
35334
35370
  return [...children].toSorted((a, b) => rank(a.type) - rank(b.type))[0] ?? null;
package/dist/index.mjs CHANGED
@@ -18782,7 +18782,20 @@ var RecentTracksQueryInput = z.object({
18782
18782
  projection: TrackProjectionSchema.optional(),
18783
18783
  /** Include stationary-promoted rows (parked objects). Default false: the
18784
18784
  * feed lists passages; parking records live on the stationary registry. */
18785
- includeStationary: z.boolean().optional()
18785
+ includeStationary: z.boolean().optional(),
18786
+ /**
18787
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
18788
+ *
18789
+ * The same filter `listTracks` takes, because the timeline's class chips must
18790
+ * mean the same thing whether the scope is one camera or twelve. Until this
18791
+ * existed the scoped feed downloaded a page and narrowed it on the phone
18792
+ * while the single-camera path narrowed the read — one filter, two costs.
18793
+ *
18794
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
18795
+ * whose class list is unreadable are kept, and the client's rule stays the
18796
+ * exact one.
18797
+ */
18798
+ classes: z.array(z.string()).optional()
18786
18799
  });
18787
18800
  var RecentTracksPageSchema = z.object({
18788
18801
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -25122,8 +25135,22 @@ var automationControlCapability = {
25122
25135
  * threshold.
25123
25136
  */
25124
25137
  var BatteryStatusSchema = z.object({
25125
- /** 0..100 inclusive. Firmware-reported. */
25126
- percentage: z.number().min(0).max(100),
25138
+ /**
25139
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
25140
+ * provider has registered the capability but no reading has landed.
25141
+ *
25142
+ * It is nullable because it was not, and the only value a provider could
25143
+ * seed with was `0`. A battery camera behind an NVR therefore announced
25144
+ * itself at 0% on every start and corrected itself a moment later, which is
25145
+ * indistinguishable from a real flat battery: it fires the low-battery alert
25146
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
25147
+ * reading the difference is an alarm.
25148
+ *
25149
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
25150
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
25151
+ * declines to band a non-finite reading, which is the correct shape.
25152
+ */
25153
+ percentage: z.number().min(0).max(100).nullable(),
25127
25154
  /**
25128
25155
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
25129
25156
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -35265,6 +35292,15 @@ var RUNTIME_DEFAULTS = {
35265
35292
  * Each carried the same list and a comment asking the others to stay in sync.
35266
35293
  * This is that list, in the one package all of them already depend on.
35267
35294
  *
35295
+ * `ui-library` and the server's linked-devices expansion IMPORT it. Two
35296
+ * consumers cannot, and keep a checked copy instead: the viewer resolves
35297
+ * `@camstack/types` from its own `node_modules` (an installed release, where a
35298
+ * newly added export simply is not there), and the Home Assistant provider
35299
+ * expresses the same precedence over the `DeviceType` enum because it answers
35300
+ * a different question from the same ordering. `scripts/check-container-
35301
+ * priority-in-sync.ts` fails the build when either drifts — the comment that
35302
+ * used to ask for this could not.
35303
+ *
35268
35304
  * The rule has two halves and the ORDER matters: an operator's explicit pick
35269
35305
  * wins outright, and only in its absence does type priority decide. The pick is
35270
35306
  * keyed on the child's re-sync-stable `entityId`, not its numeric id, so it
@@ -35320,7 +35356,7 @@ function rank(type) {
35320
35356
  */
35321
35357
  function resolveContainerPrimaryChild(children, overrideEntityId) {
35322
35358
  if (overrideEntityId !== void 0 && overrideEntityId !== null) {
35323
- const picked = children.find((c) => c.entityId === overrideEntityId);
35359
+ const picked = children.find((c) => c.stableId === overrideEntityId) ?? children.find((c) => c.entityId !== void 0 && c.entityId === overrideEntityId);
35324
35360
  if (picked !== void 0) return picked;
35325
35361
  }
35326
35362
  return [...children].toSorted((a, b) => rank(a.type) - rank(b.type))[0] ?? null;
@@ -1084,11 +1084,17 @@ export interface EventCatalog {
1084
1084
  * `capabilities/battery.cap`) to avoid a cycle between the cap
1085
1085
  * definitions and the event bus typing — they're kept in lock-step
1086
1086
  * by hand (see also `BatteryStatus` in `capabilities/battery.cap.ts`).
1087
+ *
1088
+ * "By hand" failed exactly as the repo's no-duplicate-shapes rule predicts:
1089
+ * `percentage` became nullable in the cap and stayed `number` here, so this
1090
+ * copy refused the very value the cap now defines. Kept in lock-step is a
1091
+ * hope; the compiler noticing is the only reason it was caught.
1087
1092
  */
1088
1093
  'battery.onStatusChanged': {
1089
1094
  readonly deviceId: number;
1090
1095
  readonly status: {
1091
- readonly percentage: number;
1096
+ /** `null` = NOT YET KNOWN. See `BatteryStatusSchema.percentage`. */
1097
+ readonly percentage: number | null;
1092
1098
  readonly charging: 'dc' | 'solar' | 'none';
1093
1099
  readonly sleeping: boolean;
1094
1100
  readonly lastUpdated: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/types",
3
- "version": "1.2.140",
3
+ "version": "1.2.141",
4
4
  "description": "Shared types, interfaces, and model catalogs for the CamStack detection ecosystem",
5
5
  "keywords": [
6
6
  "camstack",