@camstack/addon-provider-wyze 0.2.67 → 0.2.68

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.
package/dist/addon.js CHANGED
@@ -12976,6 +12976,24 @@ var deviceProviderCapability = {
12976
12976
  })
12977
12977
  }
12978
12978
  };
12979
+ /**
12980
+ * Device Manager capability — hub-side singleton that unifies device persistence,
12981
+ * live registry access, and all management operations into a single tRPC surface.
12982
+ *
12983
+ * Replaces:
12984
+ * - `device-persistence` capability (persistence methods absorbed here)
12985
+ * - `device-management.router.ts` (deleted in Phase 2)
12986
+ * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
12987
+ *
12988
+ * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
12989
+ * fork into separate processes but never run on remote cluster agents. Therefore:
12990
+ * - No nodeId routing needed — this is a pure hub singleton.
12991
+ * - The hub's DeviceRegistry is the single source of truth for all live devices.
12992
+ * - No shadow registry or cross-node aggregation required.
12993
+ *
12994
+ * Forked workers register devices back to the hub via `ctx.devices`
12995
+ * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
12996
+ */
12979
12997
  /** One child-placement directive on a container's `childLayout`. Structurally
12980
12998
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
12981
12999
  * shape for the same field. The child is identified by its re-sync-stable
@@ -18702,6 +18720,9 @@ var RetrainStatusSchema = _enum([
18702
18720
  *
18703
18721
  * `debug` does NOT pin; it is attention, not durability.
18704
18722
  *
18723
+ * `debugNote` is the operator's own words about WHAT should be checked on this
18724
+ * track, and it lives and dies with `debug` — see {@link MAX_TRACK_DEBUG_NOTE_LEN}.
18725
+ *
18705
18726
  * `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
18706
18727
  * A favourited track is skipped by retention the same way `staging` is, but
18707
18728
  * it does not enter `none|staging|trained` and has no staging budget.
@@ -18712,6 +18733,21 @@ var TrackFlagFields = {
18712
18733
  markForTrain: boolean().optional(),
18713
18734
  /** Operator marked this track for diagnostic attention. */
18714
18735
  debug: boolean().optional(),
18736
+ /**
18737
+ * What the operator wants CHECKED on this track, in their own words.
18738
+ *
18739
+ * The flag alone says "look at this" and nothing about what for; a debug set
18740
+ * reviewed hours later is a list of tracks with no question attached. Bound
18741
+ * to `debug` on the WRITE side — the body clears it when `debug` goes off,
18742
+ * and refuses it on a track whose debug is off — so a note can never outlive
18743
+ * the attention it belongs to.
18744
+ *
18745
+ * `''` is a real value ("marked, nothing to add"); the ABSENT key means
18746
+ * "leave whatever is stored alone", which is what lets a surface turn debug
18747
+ * on without a note (a cancelled prompt) and what lets it edit the note
18748
+ * without touching the flag.
18749
+ */
18750
+ debugNote: string().max(500).optional(),
18715
18751
  /** Operator favourited this track. Pins it against pruning. */
18716
18752
  favourited: boolean().optional()
18717
18753
  };
@@ -18738,6 +18774,12 @@ var TrackFlagsSchema = object({
18738
18774
  trackId: string(),
18739
18775
  markForTrain: boolean(),
18740
18776
  debug: boolean(),
18777
+ /** The note as it STANDS after the write — never absent here (a track with no
18778
+ * note reports `''`), for the same reason the booleans are required: a
18779
+ * surface that has just written must be able to render the note it now owns
18780
+ * without a re-fetch, and an absent key would read as "unchanged" on a
18781
+ * screen that has no previous value to keep. */
18782
+ debugNote: string(),
18741
18783
  favourited: boolean(),
18742
18784
  /** The lifecycle state the boolean was derived from. Required here (unlike on
18743
18785
  * a track row) because this shape is only ever produced by the write body,
@@ -41667,12 +41709,12 @@ var WyzeCloud = class {
41667
41709
  clearSession;
41668
41710
  constructor(apiKeyOrOpts, apiId) {
41669
41711
  if (typeof apiKeyOrOpts === "string") {
41670
- this.apiKey = apiKeyOrOpts;
41671
- this.keyId = apiId;
41712
+ this.apiKey = apiKeyOrOpts.trim();
41713
+ this.keyId = (apiId ?? "").trim();
41672
41714
  this.phoneId = generatePhoneId();
41673
41715
  } else {
41674
- this.apiKey = apiKeyOrOpts.apiKey;
41675
- this.keyId = apiKeyOrOpts.apiId;
41716
+ this.apiKey = apiKeyOrOpts.apiKey.trim();
41717
+ this.keyId = apiKeyOrOpts.apiId.trim();
41676
41718
  this.loadSession = apiKeyOrOpts.loadSession;
41677
41719
  this.saveSession = apiKeyOrOpts.saveSession;
41678
41720
  this.clearSession = apiKeyOrOpts.clearSession;
@@ -41693,6 +41735,7 @@ var WyzeCloud = class {
41693
41735
  * @throws WyzeAuthError if MFA is required
41694
41736
  */
41695
41737
  async login(email, password) {
41738
+ if (!this.apiKey || !this.keyId) throw new Error("Wyze login: missing API key or key id");
41696
41739
  const payload = {
41697
41740
  email: email.trim(),
41698
41741
  password: hashPassword(password)
@@ -42829,6 +42872,7 @@ var WyzeDTLSConn = class extends events.EventEmitter {
42829
42872
  frameHandler;
42830
42873
  closed = false;
42831
42874
  messageListener = null;
42875
+ statsTimer = null;
42832
42876
  constructor(opts) {
42833
42877
  super();
42834
42878
  this.host = opts.host;
@@ -42849,6 +42893,14 @@ var WyzeDTLSConn = class extends events.EventEmitter {
42849
42893
  * After this, call startVideo()/startAudio() to begin streaming.
42850
42894
  */
42851
42895
  async connect() {
42896
+ try {
42897
+ return await this.doConnect();
42898
+ } catch (err) {
42899
+ this.close();
42900
+ throw err;
42901
+ }
42902
+ }
42903
+ async doConnect() {
42852
42904
  this.socket = dgram.createSocket("udp4");
42853
42905
  await new Promise((r) => this.socket.bind(0, r));
42854
42906
  const localPort = this.socket.address().port;
@@ -43468,6 +43520,10 @@ var WyzeDTLSConn = class extends events.EventEmitter {
43468
43520
  this.serverConn = null;
43469
43521
  }
43470
43522
  if (this.ackTicker) clearInterval(this.ackTicker);
43523
+ if (this.statsTimer) {
43524
+ clearInterval(this.statsTimer);
43525
+ this.statsTimer = null;
43526
+ }
43471
43527
  if (this.messageListener && this.socket) this.socket.removeListener("message", this.messageListener);
43472
43528
  this.frameHandler.close();
43473
43529
  if (this.socket) {
@@ -43768,9 +43824,12 @@ var WyzeDTLSConn = class extends events.EventEmitter {
43768
43824
  let frameData = 0;
43769
43825
  let keepalives = 0;
43770
43826
  const startedAt = Date.now();
43771
- const statsTimer = setInterval(() => {
43827
+ this.statsTimer = setInterval(() => {
43772
43828
  if (this.closed) {
43773
- clearInterval(statsTimer);
43829
+ if (this.statsTimer) {
43830
+ clearInterval(this.statsTimer);
43831
+ this.statsTimer = null;
43832
+ }
43774
43833
  return;
43775
43834
  }
43776
43835
  const elapsed = ((Date.now() - startedAt) / 1e3).toFixed(0);
@@ -43820,7 +43879,7 @@ var WyzeDTLSConn = class extends events.EventEmitter {
43820
43879
  b.writeUInt32LE(this.avSeq, 4);
43821
43880
  this.avSeq++;
43822
43881
  b.writeUInt16LE(28672, 16);
43823
- b.writeUInt16LE(this.seqCmd, 18);
43882
+ b.writeUInt16LE(this.seqCmd & 65535, 18);
43824
43883
  b.writeUInt32LE(1, 20);
43825
43884
  b.writeUInt32LE(payload.length + 4, 24);
43826
43885
  b.writeUInt32LE(this.seqCmd, 28);
@@ -43893,7 +43952,7 @@ var WyzeDTLSConn = class extends events.EventEmitter {
43893
43952
  b[3] = 11;
43894
43953
  b.writeUInt16LE(bs, 4);
43895
43954
  b.writeUInt16LE(this.txSeq, 6);
43896
- this.txSeq++;
43955
+ this.txSeq = this.txSeq + 1 & 65535;
43897
43956
  b.writeUInt16LE(1031, 8);
43898
43957
  b.writeUInt16LE(33, 10);
43899
43958
  this.sid.copy(b, 12, 0, 2);
@@ -44475,10 +44534,17 @@ async function createWyzeRfc4571Server(opts) {
44475
44534
  logger
44476
44535
  });
44477
44536
  logger.log?.(`[wyze-rfc4571] Connecting to ${camera.nickname} (${camera.ip})...`);
44478
- await conn.connect();
44479
- logger.log?.(`[wyze-rfc4571] Connected, starting video...`);
44480
- await conn.startVideo(frameSize, bitrate);
44481
- await conn.startAudio();
44537
+ try {
44538
+ await conn.connect();
44539
+ logger.log?.(`[wyze-rfc4571] Connected, starting video...`);
44540
+ await conn.startVideo(frameSize, bitrate);
44541
+ await conn.startAudio();
44542
+ } catch (e) {
44543
+ try {
44544
+ conn.close();
44545
+ } catch {}
44546
+ throw e;
44547
+ }
44482
44548
  const rebuildSdp = () => {
44483
44549
  if (videoSdpPart) sdp = buildFullSdp(videoSdpPart, audioSdpPart || void 0);
44484
44550
  };
@@ -44636,7 +44702,9 @@ async function createWyzeRfc4571Server(opts) {
44636
44702
  return {
44637
44703
  host: addr.address,
44638
44704
  port: addr.port,
44639
- sdp,
44705
+ get sdp() {
44706
+ return sdp;
44707
+ },
44640
44708
  videoType,
44641
44709
  connection: conn,
44642
44710
  close: closeFn,
package/dist/addon.mjs CHANGED
@@ -12955,6 +12955,24 @@ var deviceProviderCapability = {
12955
12955
  })
12956
12956
  }
12957
12957
  };
12958
+ /**
12959
+ * Device Manager capability — hub-side singleton that unifies device persistence,
12960
+ * live registry access, and all management operations into a single tRPC surface.
12961
+ *
12962
+ * Replaces:
12963
+ * - `device-persistence` capability (persistence methods absorbed here)
12964
+ * - `device-management.router.ts` (deleted in Phase 2)
12965
+ * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
12966
+ *
12967
+ * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
12968
+ * fork into separate processes but never run on remote cluster agents. Therefore:
12969
+ * - No nodeId routing needed — this is a pure hub singleton.
12970
+ * - The hub's DeviceRegistry is the single source of truth for all live devices.
12971
+ * - No shadow registry or cross-node aggregation required.
12972
+ *
12973
+ * Forked workers register devices back to the hub via `ctx.devices`
12974
+ * (DeviceManagerApi → ctx.api.deviceManager.registerDevice), same as today.
12975
+ */
12958
12976
  /** One child-placement directive on a container's `childLayout`. Structurally
12959
12977
  * identical to `ChildLayoutEntry` in `device-management.ts` — the cap wire
12960
12978
  * shape for the same field. The child is identified by its re-sync-stable
@@ -18681,6 +18699,9 @@ var RetrainStatusSchema = _enum([
18681
18699
  *
18682
18700
  * `debug` does NOT pin; it is attention, not durability.
18683
18701
  *
18702
+ * `debugNote` is the operator's own words about WHAT should be checked on this
18703
+ * track, and it lives and dies with `debug` — see {@link MAX_TRACK_DEBUG_NOTE_LEN}.
18704
+ *
18684
18705
  * `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
18685
18706
  * A favourited track is skipped by retention the same way `staging` is, but
18686
18707
  * it does not enter `none|staging|trained` and has no staging budget.
@@ -18691,6 +18712,21 @@ var TrackFlagFields = {
18691
18712
  markForTrain: boolean().optional(),
18692
18713
  /** Operator marked this track for diagnostic attention. */
18693
18714
  debug: boolean().optional(),
18715
+ /**
18716
+ * What the operator wants CHECKED on this track, in their own words.
18717
+ *
18718
+ * The flag alone says "look at this" and nothing about what for; a debug set
18719
+ * reviewed hours later is a list of tracks with no question attached. Bound
18720
+ * to `debug` on the WRITE side — the body clears it when `debug` goes off,
18721
+ * and refuses it on a track whose debug is off — so a note can never outlive
18722
+ * the attention it belongs to.
18723
+ *
18724
+ * `''` is a real value ("marked, nothing to add"); the ABSENT key means
18725
+ * "leave whatever is stored alone", which is what lets a surface turn debug
18726
+ * on without a note (a cancelled prompt) and what lets it edit the note
18727
+ * without touching the flag.
18728
+ */
18729
+ debugNote: string().max(500).optional(),
18694
18730
  /** Operator favourited this track. Pins it against pruning. */
18695
18731
  favourited: boolean().optional()
18696
18732
  };
@@ -18717,6 +18753,12 @@ var TrackFlagsSchema = object({
18717
18753
  trackId: string(),
18718
18754
  markForTrain: boolean(),
18719
18755
  debug: boolean(),
18756
+ /** The note as it STANDS after the write — never absent here (a track with no
18757
+ * note reports `''`), for the same reason the booleans are required: a
18758
+ * surface that has just written must be able to render the note it now owns
18759
+ * without a re-fetch, and an absent key would read as "unchanged" on a
18760
+ * screen that has no previous value to keep. */
18761
+ debugNote: string(),
18720
18762
  favourited: boolean(),
18721
18763
  /** The lifecycle state the boolean was derived from. Required here (unlike on
18722
18764
  * a track row) because this shape is only ever produced by the write body,
@@ -41646,12 +41688,12 @@ var WyzeCloud = class {
41646
41688
  clearSession;
41647
41689
  constructor(apiKeyOrOpts, apiId) {
41648
41690
  if (typeof apiKeyOrOpts === "string") {
41649
- this.apiKey = apiKeyOrOpts;
41650
- this.keyId = apiId;
41691
+ this.apiKey = apiKeyOrOpts.trim();
41692
+ this.keyId = (apiId ?? "").trim();
41651
41693
  this.phoneId = generatePhoneId();
41652
41694
  } else {
41653
- this.apiKey = apiKeyOrOpts.apiKey;
41654
- this.keyId = apiKeyOrOpts.apiId;
41695
+ this.apiKey = apiKeyOrOpts.apiKey.trim();
41696
+ this.keyId = apiKeyOrOpts.apiId.trim();
41655
41697
  this.loadSession = apiKeyOrOpts.loadSession;
41656
41698
  this.saveSession = apiKeyOrOpts.saveSession;
41657
41699
  this.clearSession = apiKeyOrOpts.clearSession;
@@ -41672,6 +41714,7 @@ var WyzeCloud = class {
41672
41714
  * @throws WyzeAuthError if MFA is required
41673
41715
  */
41674
41716
  async login(email, password) {
41717
+ if (!this.apiKey || !this.keyId) throw new Error("Wyze login: missing API key or key id");
41675
41718
  const payload = {
41676
41719
  email: email.trim(),
41677
41720
  password: hashPassword(password)
@@ -42808,6 +42851,7 @@ var WyzeDTLSConn = class extends EventEmitter {
42808
42851
  frameHandler;
42809
42852
  closed = false;
42810
42853
  messageListener = null;
42854
+ statsTimer = null;
42811
42855
  constructor(opts) {
42812
42856
  super();
42813
42857
  this.host = opts.host;
@@ -42828,6 +42872,14 @@ var WyzeDTLSConn = class extends EventEmitter {
42828
42872
  * After this, call startVideo()/startAudio() to begin streaming.
42829
42873
  */
42830
42874
  async connect() {
42875
+ try {
42876
+ return await this.doConnect();
42877
+ } catch (err) {
42878
+ this.close();
42879
+ throw err;
42880
+ }
42881
+ }
42882
+ async doConnect() {
42831
42883
  this.socket = dgram.createSocket("udp4");
42832
42884
  await new Promise((r) => this.socket.bind(0, r));
42833
42885
  const localPort = this.socket.address().port;
@@ -43447,6 +43499,10 @@ var WyzeDTLSConn = class extends EventEmitter {
43447
43499
  this.serverConn = null;
43448
43500
  }
43449
43501
  if (this.ackTicker) clearInterval(this.ackTicker);
43502
+ if (this.statsTimer) {
43503
+ clearInterval(this.statsTimer);
43504
+ this.statsTimer = null;
43505
+ }
43450
43506
  if (this.messageListener && this.socket) this.socket.removeListener("message", this.messageListener);
43451
43507
  this.frameHandler.close();
43452
43508
  if (this.socket) {
@@ -43747,9 +43803,12 @@ var WyzeDTLSConn = class extends EventEmitter {
43747
43803
  let frameData = 0;
43748
43804
  let keepalives = 0;
43749
43805
  const startedAt = Date.now();
43750
- const statsTimer = setInterval(() => {
43806
+ this.statsTimer = setInterval(() => {
43751
43807
  if (this.closed) {
43752
- clearInterval(statsTimer);
43808
+ if (this.statsTimer) {
43809
+ clearInterval(this.statsTimer);
43810
+ this.statsTimer = null;
43811
+ }
43753
43812
  return;
43754
43813
  }
43755
43814
  const elapsed = ((Date.now() - startedAt) / 1e3).toFixed(0);
@@ -43799,7 +43858,7 @@ var WyzeDTLSConn = class extends EventEmitter {
43799
43858
  b.writeUInt32LE(this.avSeq, 4);
43800
43859
  this.avSeq++;
43801
43860
  b.writeUInt16LE(28672, 16);
43802
- b.writeUInt16LE(this.seqCmd, 18);
43861
+ b.writeUInt16LE(this.seqCmd & 65535, 18);
43803
43862
  b.writeUInt32LE(1, 20);
43804
43863
  b.writeUInt32LE(payload.length + 4, 24);
43805
43864
  b.writeUInt32LE(this.seqCmd, 28);
@@ -43872,7 +43931,7 @@ var WyzeDTLSConn = class extends EventEmitter {
43872
43931
  b[3] = 11;
43873
43932
  b.writeUInt16LE(bs, 4);
43874
43933
  b.writeUInt16LE(this.txSeq, 6);
43875
- this.txSeq++;
43934
+ this.txSeq = this.txSeq + 1 & 65535;
43876
43935
  b.writeUInt16LE(1031, 8);
43877
43936
  b.writeUInt16LE(33, 10);
43878
43937
  this.sid.copy(b, 12, 0, 2);
@@ -44454,10 +44513,17 @@ async function createWyzeRfc4571Server(opts) {
44454
44513
  logger
44455
44514
  });
44456
44515
  logger.log?.(`[wyze-rfc4571] Connecting to ${camera.nickname} (${camera.ip})...`);
44457
- await conn.connect();
44458
- logger.log?.(`[wyze-rfc4571] Connected, starting video...`);
44459
- await conn.startVideo(frameSize, bitrate);
44460
- await conn.startAudio();
44516
+ try {
44517
+ await conn.connect();
44518
+ logger.log?.(`[wyze-rfc4571] Connected, starting video...`);
44519
+ await conn.startVideo(frameSize, bitrate);
44520
+ await conn.startAudio();
44521
+ } catch (e) {
44522
+ try {
44523
+ conn.close();
44524
+ } catch {}
44525
+ throw e;
44526
+ }
44461
44527
  const rebuildSdp = () => {
44462
44528
  if (videoSdpPart) sdp = buildFullSdp(videoSdpPart, audioSdpPart || void 0);
44463
44529
  };
@@ -44615,7 +44681,9 @@ async function createWyzeRfc4571Server(opts) {
44615
44681
  return {
44616
44682
  host: addr.address,
44617
44683
  port: addr.port,
44618
- sdp,
44684
+ get sdp() {
44685
+ return sdp;
44686
+ },
44619
44687
  videoType,
44620
44688
  connection: conn,
44621
44689
  close: closeFn,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-wyze",
3
- "version": "0.2.67",
3
+ "version": "0.2.68",
4
4
  "description": "Wyze camera device-provider addon for CamStack — wraps the @apocaliss92/wyze-bridge-js P2P/DTLS client, feeding the stream-broker via the pull-rfc4571 lazy-publish path (a structural twin of addon-provider-reolink)",
5
5
  "keywords": [
6
6
  "camstack",
@@ -74,7 +74,7 @@
74
74
  "publish": "npm publish --access public"
75
75
  },
76
76
  "devDependencies": {
77
- "@apocaliss92/wyze-bridge-js": "file:./vendor/apocaliss92-wyze-bridge-js-0.1.0.tgz",
77
+ "@apocaliss92/wyze-bridge-js": "file:./vendor/apocaliss92-wyze-bridge-js-0.1.2.tgz",
78
78
  "@camstack/types": "*",
79
79
  "typescript": "~6.0.3",
80
80
  "vite": "^8.0.11",