@camstack/addon-provider-petkit 0.2.97 → 0.2.99

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 +151 -22
  2. package/dist/addon.mjs +151 -22
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -8405,6 +8405,23 @@ function errMsg(err) {
8405
8405
  if (typeof err === "string") return err;
8406
8406
  return String(err);
8407
8407
  }
8408
+ new Set([
8409
+ "track",
8410
+ "summary",
8411
+ "face",
8412
+ "identity",
8413
+ "plate",
8414
+ "vehicle",
8415
+ "scene",
8416
+ "motion",
8417
+ "object",
8418
+ "audio"
8419
+ ]);
8420
+ new Set([
8421
+ "motion",
8422
+ "object",
8423
+ "audio"
8424
+ ]);
8408
8425
  var EncodeProfileSchema = object({
8409
8426
  video: object({
8410
8427
  codec: _enum([
@@ -13605,8 +13622,17 @@ method(object({
13605
13622
  }), array(SettingsRecordSchema).readonly()), method(object({
13606
13623
  namespace: string().optional(),
13607
13624
  collection: string(),
13608
- record: SettingsRecordSchema
13609
- }), _void(), { kind: "mutation" }), method(object({
13625
+ record: object({
13626
+ id: string().optional(),
13627
+ data: record(string(), unknown())
13628
+ })
13629
+ }), object({
13630
+ /**
13631
+ * The id the row ACTUALLY got (D473): the one supplied, the UUID minted
13632
+ * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
13633
+ * primary key — which is the only place an auto key is knowable.
13634
+ */
13635
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
13610
13636
  namespace: string().optional(),
13611
13637
  collection: string(),
13612
13638
  records: array(BulkRecordSchema).readonly()
@@ -13715,8 +13741,17 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13715
13741
  }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
13716
13742
  namespace: string().optional(),
13717
13743
  collection: string(),
13718
- record: SettingsRecordSchema
13719
- }), _void(), {
13744
+ record: object({
13745
+ id: string().optional(),
13746
+ data: record(string(), unknown())
13747
+ })
13748
+ }), object({
13749
+ /**
13750
+ * The id the row ACTUALLY got (D473): the one supplied, the UUID minted
13751
+ * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
13752
+ * primary key — which is the only place an auto key is knowable.
13753
+ */
13754
+ id: union([string(), number()]) }), {
13720
13755
  kind: "mutation",
13721
13756
  auth: "admin"
13722
13757
  }), method(object({
@@ -20641,7 +20676,20 @@ var TrackSchema = object({
20641
20676
  ...TrackRetrainFields
20642
20677
  });
20643
20678
  var BaseEventFields = {
20644
- id: string(),
20679
+ /**
20680
+ * A SQLite ROWID, assigned by the database (D474).
20681
+ *
20682
+ * Was a 36-character UUID and cost 263 MB of a 1 117 MB database — paid
20683
+ * TWICE per row, in the row and in the primary-key index, across 2.1 million
20684
+ * motion, audio and object events. An `INTEGER PRIMARY KEY` in SQLite **is**
20685
+ * the rowid: the table itself is that B-tree, so the index stops existing
20686
+ * rather than getting smaller. No shorter string does that.
20687
+ *
20688
+ * Defined once here for all three event kinds, which is why they move
20689
+ * together: a per-table migration would have forked this and
20690
+ * `COMMON_BASE_COLUMNS` and reunited them two stages later.
20691
+ */
20692
+ id: number().int(),
20645
20693
  deviceId: number(),
20646
20694
  timestamp: number()
20647
20695
  };
@@ -20660,7 +20708,34 @@ var MotionEventSchema = object({
20660
20708
  /** Omitted in slim projection. */
20661
20709
  frameHeight: number().optional(),
20662
20710
  /** Populated by B5 (recording playback URL for this event). */
20663
- mediaUrl: string().optional()
20711
+ mediaUrl: string().optional(),
20712
+ /**
20713
+ * One row per motion EPISODE, not one per push (D475). `null` while the
20714
+ * episode is still open — a further rising edge extends it in place rather
20715
+ * than inserting a new row. Set once, at close, to `lastOnAt - startedAt`
20716
+ * (the span from the first rising edge to the LAST one, deliberately NOT
20717
+ * `closedAt - startedAt` — the close delay is a quiet CONFIRMATION, not
20718
+ * movement, and folding it in would report `MOTION_CLOSE_AFTER_MS` of
20719
+ * motion for an instantaneous trigger).
20720
+ *
20721
+ * **Absent** (not merely `null`) on a row written before D475 — that means
20722
+ * "closed the old way, before this column existed", never "still open".
20723
+ * Nothing in this codebase may read an absent `durationMs` as an open
20724
+ * episode; only `null` means open.
20725
+ */
20726
+ durationMs: number().nullable().optional(),
20727
+ /**
20728
+ * Ms offsets from `timestamp` (the episode's own first rising edge, so the
20729
+ * first entry is always `0`) of every genuine off→on transition the
20730
+ * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
20731
+ * push: a firmware source that keepalives at ~1 Hz for the whole burst
20732
+ * (Reolink, Hikvision) produces exactly one edge; a source that reports an
20733
+ * explicit `false` mid-episode and then resumes before the quiet window
20734
+ * elapses produces another. Stored compactly — see `motion-edge-codec.ts`
20735
+ * — and decoded back to this shape on read. Absent/empty on a legacy row,
20736
+ * which must never be read as "no episode happened here".
20737
+ */
20738
+ edges: array(number()).readonly().optional()
20664
20739
  });
20665
20740
  /**
20666
20741
  * Which detection SOURCE produced an object event. `pipeline` = the ML
@@ -20715,6 +20790,23 @@ var ObjectEventSchema = object({
20715
20790
  * includes it (it is light). Absent on rows written before this field.
20716
20791
  */
20717
20792
  frameId: string().optional(),
20793
+ /**
20794
+ * A PRODUCER-chosen key that makes a synthetic event's emission idempotent
20795
+ * (D474).
20796
+ *
20797
+ * Only the package detector writes it, and it exists because the event id
20798
+ * stopped being choosable: the delivery and pick-up rows used to BE their
20799
+ * dedupe key (`pa-pkg-<entryId>-delivered`), which is how "never emit a
20800
+ * second delivery for this entry" survived a restart. An `INTEGER` rowid is
20801
+ * assigned by SQLite, so that key had to move off the primary key rather
20802
+ * than be dropped — a detector that cannot recognise its own row re-delivers
20803
+ * every parcel on every boot.
20804
+ *
20805
+ * Absent on every other object event, and on every row written before this
20806
+ * field. Never a substitute for `id`: it is unique per (producer, occasion),
20807
+ * not per row, and nothing addresses a row by it.
20808
+ */
20809
+ idempotencyKey: string().optional(),
20718
20810
  /** Omitted in slim projection. */
20719
20811
  trackId: string().optional(),
20720
20812
  className: string(),
@@ -28972,26 +29064,59 @@ authKey: string().optional() }), object({
28972
29064
  /** Human-readable error when `ok: false`. */
28973
29065
  error: string().optional()
28974
29066
  }), { kind: "mutation" });
28975
- /**
28976
- * Hardware / firmware motion sensor cap — binary detected state plus
28977
- * a timestamp of the last observation. Distinct from
28978
- * `motion-detection.cap.ts` which owns the LOCAL ML motion pipeline;
28979
- * `motion` is the lightweight readout from on-camera motion (Reolink
28980
- * `GetMdState`, Baichuan push `type: motion`, ONVIF analytics).
28981
- *
28982
- * Native-motion providers also fan out to `detection.camera-native`
28983
- * with `source: 'onboard'` so cross-cutting system services
28984
- * (alert-center, advanced-notifier) can subscribe once and receive
28985
- * motion from every camera.
28986
- */
28987
29067
  var MotionStatusSchema = object({
28988
29068
  detected: boolean(),
28989
29069
  /** Ms epoch of the last detected-true observation. Null if never detected. */
28990
29070
  lastDetectedAt: number().nullable(),
28991
29071
  /**
28992
- * Ms after which `detected` auto-reverts to false if no fresh push
28993
- * arrives. Null means the provider leaves detected state until a
28994
- * native "clear" event.
29072
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
29073
+ * `null` while false and on every `Sensor` device (D475) — see that
29074
+ * constant's doc for the one-authority rule.
29075
+ *
29076
+ * ## Reading this field still arms nothing
29077
+ *
29078
+ * It reads like an instruction to the consumer ("revert after N ms if
29079
+ * no fresh push arrives") and it is not one: nothing in this repo reads
29080
+ * the LIVE cap value to drive a timer. `pipeline-analytics`'s motion-episode
29081
+ * close DOES now use the same number — `MOTION_CLOSE_AFTER_MS` — but as an
29082
+ * imported constant, not as a read of `device.state.motion.value`, so this
29083
+ * field stays what it always was: DESCRIPTIVE output, mirroring an answer
29084
+ * computed elsewhere. Building a self-clear timer out of a READ of this
29085
+ * field would add a second falling-edge authority beside whichever one
29086
+ * already owns the device, and two that can disagree are worse than one.
29087
+ * Consumers that need a falling edge SHAPED differently — held open across
29088
+ * a flapping source — debounce on their own side and say so, as
29089
+ * `addon-export-alexa/src/motion-clear-hold.ts` and
29090
+ * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
29091
+ *
29092
+ * ## Who writes it
29093
+ *
29094
+ * - **Cameras** — the runner's phase machine, `active → watching` on
29095
+ * `cooldown_expired`, which then writes this slice with
29096
+ * `detected: false` (`handlePhaseChanged` in
29097
+ * `pipeline-runner/index.ts`). It produces the FALLING edge, which
29098
+ * matters most for the sources that only ever push a rising one:
29099
+ * Reolink emits `MotionOnMotionChanged { detected: true }` and never
29100
+ * a false.
29101
+ * - **Sensors** (Home Assistant binary sensors, Homematic) — the
29102
+ * provider pushes the false itself, from the upstream system's own
29103
+ * state change. No phase machine is involved.
29104
+ *
29105
+ * ### The phase machine is CANONICAL, not sole — and that is a defect
29106
+ *
29107
+ * An earlier revision of this docblock (mine, 2026-09-12) claimed the
29108
+ * phase machine is the sole writer for a camera. It is not.
29109
+ * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
29110
+ * `setCapSlice(motionCapability, …)` on their own rising edge, and
29111
+ * Hikvision's comment says why: it read THIS docblock, agreed the
29112
+ * runner is canonical, and wrote anyway to avoid per-tick churn. So
29113
+ * two authorities can disagree about one slice, which this repo
29114
+ * forbids, and the doc said otherwise — which is worse than saying
29115
+ * nothing, because it reads as verification.
29116
+ *
29117
+ * This predates D475 and is not fixed there: the fix touches every
29118
+ * camera provider. Recorded in D475's Consequences. Do not restore the
29119
+ * "sole writer" wording without also removing the other writers.
28995
29120
  */
28996
29121
  autoClearAfterMs: number().nullable()
28997
29122
  });
@@ -29061,7 +29186,11 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
29061
29186
  */
29062
29187
  runtimeState: MotionStatusSchema,
29063
29188
  /**
29064
- * Runtime-state durability: **session** — self-clearing by construction (`autoClearAfterMs`); a restored `detected: true` is a frozen event, and the next frame re-publishes the real one.
29189
+ * Runtime-state durability: **session** — every writer of this slice
29190
+ * writes only on an EDGE, so a restored `detected: true` would stay
29191
+ * frozen until the next one instead of being corrected. The next edge
29192
+ * re-publishes the real state. (On who the writers are, and why there
29193
+ * is more than one, see `autoClearAfterMs` above.)
29065
29194
  *
29066
29195
  * See `RuntimeStateDurability`. Enforced by
29067
29196
  * `scripts/check-runtime-state-durability.ts`.
package/dist/addon.mjs CHANGED
@@ -8404,6 +8404,23 @@ function errMsg(err) {
8404
8404
  if (typeof err === "string") return err;
8405
8405
  return String(err);
8406
8406
  }
8407
+ new Set([
8408
+ "track",
8409
+ "summary",
8410
+ "face",
8411
+ "identity",
8412
+ "plate",
8413
+ "vehicle",
8414
+ "scene",
8415
+ "motion",
8416
+ "object",
8417
+ "audio"
8418
+ ]);
8419
+ new Set([
8420
+ "motion",
8421
+ "object",
8422
+ "audio"
8423
+ ]);
8407
8424
  var EncodeProfileSchema = object({
8408
8425
  video: object({
8409
8426
  codec: _enum([
@@ -13604,8 +13621,17 @@ method(object({
13604
13621
  }), array(SettingsRecordSchema).readonly()), method(object({
13605
13622
  namespace: string().optional(),
13606
13623
  collection: string(),
13607
- record: SettingsRecordSchema
13608
- }), _void(), { kind: "mutation" }), method(object({
13624
+ record: object({
13625
+ id: string().optional(),
13626
+ data: record(string(), unknown())
13627
+ })
13628
+ }), object({
13629
+ /**
13630
+ * The id the row ACTUALLY got (D473): the one supplied, the UUID minted
13631
+ * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
13632
+ * primary key — which is the only place an auto key is knowable.
13633
+ */
13634
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
13609
13635
  namespace: string().optional(),
13610
13636
  collection: string(),
13611
13637
  records: array(BulkRecordSchema).readonly()
@@ -13714,8 +13740,17 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13714
13740
  }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
13715
13741
  namespace: string().optional(),
13716
13742
  collection: string(),
13717
- record: SettingsRecordSchema
13718
- }), _void(), {
13743
+ record: object({
13744
+ id: string().optional(),
13745
+ data: record(string(), unknown())
13746
+ })
13747
+ }), object({
13748
+ /**
13749
+ * The id the row ACTUALLY got (D473): the one supplied, the UUID minted
13750
+ * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
13751
+ * primary key — which is the only place an auto key is knowable.
13752
+ */
13753
+ id: union([string(), number()]) }), {
13719
13754
  kind: "mutation",
13720
13755
  auth: "admin"
13721
13756
  }), method(object({
@@ -20640,7 +20675,20 @@ var TrackSchema = object({
20640
20675
  ...TrackRetrainFields
20641
20676
  });
20642
20677
  var BaseEventFields = {
20643
- id: string(),
20678
+ /**
20679
+ * A SQLite ROWID, assigned by the database (D474).
20680
+ *
20681
+ * Was a 36-character UUID and cost 263 MB of a 1 117 MB database — paid
20682
+ * TWICE per row, in the row and in the primary-key index, across 2.1 million
20683
+ * motion, audio and object events. An `INTEGER PRIMARY KEY` in SQLite **is**
20684
+ * the rowid: the table itself is that B-tree, so the index stops existing
20685
+ * rather than getting smaller. No shorter string does that.
20686
+ *
20687
+ * Defined once here for all three event kinds, which is why they move
20688
+ * together: a per-table migration would have forked this and
20689
+ * `COMMON_BASE_COLUMNS` and reunited them two stages later.
20690
+ */
20691
+ id: number().int(),
20644
20692
  deviceId: number(),
20645
20693
  timestamp: number()
20646
20694
  };
@@ -20659,7 +20707,34 @@ var MotionEventSchema = object({
20659
20707
  /** Omitted in slim projection. */
20660
20708
  frameHeight: number().optional(),
20661
20709
  /** Populated by B5 (recording playback URL for this event). */
20662
- mediaUrl: string().optional()
20710
+ mediaUrl: string().optional(),
20711
+ /**
20712
+ * One row per motion EPISODE, not one per push (D475). `null` while the
20713
+ * episode is still open — a further rising edge extends it in place rather
20714
+ * than inserting a new row. Set once, at close, to `lastOnAt - startedAt`
20715
+ * (the span from the first rising edge to the LAST one, deliberately NOT
20716
+ * `closedAt - startedAt` — the close delay is a quiet CONFIRMATION, not
20717
+ * movement, and folding it in would report `MOTION_CLOSE_AFTER_MS` of
20718
+ * motion for an instantaneous trigger).
20719
+ *
20720
+ * **Absent** (not merely `null`) on a row written before D475 — that means
20721
+ * "closed the old way, before this column existed", never "still open".
20722
+ * Nothing in this codebase may read an absent `durationMs` as an open
20723
+ * episode; only `null` means open.
20724
+ */
20725
+ durationMs: number().nullable().optional(),
20726
+ /**
20727
+ * Ms offsets from `timestamp` (the episode's own first rising edge, so the
20728
+ * first entry is always `0`) of every genuine off→on transition the
20729
+ * episode saw — "ogni evento on si deve salvare" (D475). NOT one entry per
20730
+ * push: a firmware source that keepalives at ~1 Hz for the whole burst
20731
+ * (Reolink, Hikvision) produces exactly one edge; a source that reports an
20732
+ * explicit `false` mid-episode and then resumes before the quiet window
20733
+ * elapses produces another. Stored compactly — see `motion-edge-codec.ts`
20734
+ * — and decoded back to this shape on read. Absent/empty on a legacy row,
20735
+ * which must never be read as "no episode happened here".
20736
+ */
20737
+ edges: array(number()).readonly().optional()
20663
20738
  });
20664
20739
  /**
20665
20740
  * Which detection SOURCE produced an object event. `pipeline` = the ML
@@ -20714,6 +20789,23 @@ var ObjectEventSchema = object({
20714
20789
  * includes it (it is light). Absent on rows written before this field.
20715
20790
  */
20716
20791
  frameId: string().optional(),
20792
+ /**
20793
+ * A PRODUCER-chosen key that makes a synthetic event's emission idempotent
20794
+ * (D474).
20795
+ *
20796
+ * Only the package detector writes it, and it exists because the event id
20797
+ * stopped being choosable: the delivery and pick-up rows used to BE their
20798
+ * dedupe key (`pa-pkg-<entryId>-delivered`), which is how "never emit a
20799
+ * second delivery for this entry" survived a restart. An `INTEGER` rowid is
20800
+ * assigned by SQLite, so that key had to move off the primary key rather
20801
+ * than be dropped — a detector that cannot recognise its own row re-delivers
20802
+ * every parcel on every boot.
20803
+ *
20804
+ * Absent on every other object event, and on every row written before this
20805
+ * field. Never a substitute for `id`: it is unique per (producer, occasion),
20806
+ * not per row, and nothing addresses a row by it.
20807
+ */
20808
+ idempotencyKey: string().optional(),
20717
20809
  /** Omitted in slim projection. */
20718
20810
  trackId: string().optional(),
20719
20811
  className: string(),
@@ -28971,26 +29063,59 @@ authKey: string().optional() }), object({
28971
29063
  /** Human-readable error when `ok: false`. */
28972
29064
  error: string().optional()
28973
29065
  }), { kind: "mutation" });
28974
- /**
28975
- * Hardware / firmware motion sensor cap — binary detected state plus
28976
- * a timestamp of the last observation. Distinct from
28977
- * `motion-detection.cap.ts` which owns the LOCAL ML motion pipeline;
28978
- * `motion` is the lightweight readout from on-camera motion (Reolink
28979
- * `GetMdState`, Baichuan push `type: motion`, ONVIF analytics).
28980
- *
28981
- * Native-motion providers also fan out to `detection.camera-native`
28982
- * with `source: 'onboard'` so cross-cutting system services
28983
- * (alert-center, advanced-notifier) can subscribe once and receive
28984
- * motion from every camera.
28985
- */
28986
29066
  var MotionStatusSchema = object({
28987
29067
  detected: boolean(),
28988
29068
  /** Ms epoch of the last detected-true observation. Null if never detected. */
28989
29069
  lastDetectedAt: number().nullable(),
28990
29070
  /**
28991
- * Ms after which `detected` auto-reverts to false if no fresh push
28992
- * arrives. Null means the provider leaves detected state until a
28993
- * native "clear" event.
29071
+ * `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device,
29072
+ * `null` while false and on every `Sensor` device (D475) — see that
29073
+ * constant's doc for the one-authority rule.
29074
+ *
29075
+ * ## Reading this field still arms nothing
29076
+ *
29077
+ * It reads like an instruction to the consumer ("revert after N ms if
29078
+ * no fresh push arrives") and it is not one: nothing in this repo reads
29079
+ * the LIVE cap value to drive a timer. `pipeline-analytics`'s motion-episode
29080
+ * close DOES now use the same number — `MOTION_CLOSE_AFTER_MS` — but as an
29081
+ * imported constant, not as a read of `device.state.motion.value`, so this
29082
+ * field stays what it always was: DESCRIPTIVE output, mirroring an answer
29083
+ * computed elsewhere. Building a self-clear timer out of a READ of this
29084
+ * field would add a second falling-edge authority beside whichever one
29085
+ * already owns the device, and two that can disagree are worse than one.
29086
+ * Consumers that need a falling edge SHAPED differently — held open across
29087
+ * a flapping source — debounce on their own side and say so, as
29088
+ * `addon-export-alexa/src/motion-clear-hold.ts` and
29089
+ * `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
29090
+ *
29091
+ * ## Who writes it
29092
+ *
29093
+ * - **Cameras** — the runner's phase machine, `active → watching` on
29094
+ * `cooldown_expired`, which then writes this slice with
29095
+ * `detected: false` (`handlePhaseChanged` in
29096
+ * `pipeline-runner/index.ts`). It produces the FALLING edge, which
29097
+ * matters most for the sources that only ever push a rising one:
29098
+ * Reolink emits `MotionOnMotionChanged { detected: true }` and never
29099
+ * a false.
29100
+ * - **Sensors** (Home Assistant binary sensors, Homematic) — the
29101
+ * provider pushes the false itself, from the upstream system's own
29102
+ * state change. No phase machine is involved.
29103
+ *
29104
+ * ### The phase machine is CANONICAL, not sole — and that is a defect
29105
+ *
29106
+ * An earlier revision of this docblock (mine, 2026-09-12) claimed the
29107
+ * phase machine is the sole writer for a camera. It is not.
29108
+ * `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
29109
+ * `setCapSlice(motionCapability, …)` on their own rising edge, and
29110
+ * Hikvision's comment says why: it read THIS docblock, agreed the
29111
+ * runner is canonical, and wrote anyway to avoid per-tick churn. So
29112
+ * two authorities can disagree about one slice, which this repo
29113
+ * forbids, and the doc said otherwise — which is worse than saying
29114
+ * nothing, because it reads as verification.
29115
+ *
29116
+ * This predates D475 and is not fixed there: the fix touches every
29117
+ * camera provider. Recorded in D475's Consequences. Do not restore the
29118
+ * "sole writer" wording without also removing the other writers.
28994
29119
  */
28995
29120
  autoClearAfterMs: number().nullable()
28996
29121
  });
@@ -29060,7 +29185,11 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
29060
29185
  */
29061
29186
  runtimeState: MotionStatusSchema,
29062
29187
  /**
29063
- * Runtime-state durability: **session** — self-clearing by construction (`autoClearAfterMs`); a restored `detected: true` is a frozen event, and the next frame re-publishes the real one.
29188
+ * Runtime-state durability: **session** — every writer of this slice
29189
+ * writes only on an EDGE, so a restored `detected: true` would stay
29190
+ * frozen until the next one instead of being corrected. The next edge
29191
+ * re-publishes the real state. (On who the writers are, and why there
29192
+ * is more than one, see `autoClearAfterMs` above.)
29064
29193
  *
29065
29194
  * See `RuntimeStateDurability`. Enforced by
29066
29195
  * `scripts/check-runtime-state-durability.ts`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-petkit",
3
- "version": "0.2.97",
3
+ "version": "0.2.99",
4
4
  "description": "PetKit smart-feeder device-provider addon for CamStack — wraps the @apocaliss92/nodepetkit PetKit cloud client",
5
5
  "keywords": [
6
6
  "camstack",