@camstack/addon-provider-wyze 0.2.55 → 0.2.57

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
@@ -7750,7 +7750,8 @@ var OpsLogReasonSchema = _enum([
7750
7750
  "quota",
7751
7751
  "manual",
7752
7752
  "operator",
7753
- "maintenance"
7753
+ "maintenance",
7754
+ "orphaned-device"
7754
7755
  ]);
7755
7756
  /** One audit row, shared verbatim by both domains. */
7756
7757
  var OpsLogEntrySchema = object({
@@ -13062,10 +13063,39 @@ var DevicePersistConfigPayloadSchema = object({
13062
13063
  deviceId: number(),
13063
13064
  data: record(string(), unknown())
13064
13065
  });
13066
+ /** What a migration actually did, per switch. `unreachable` is a first-class
13067
+ * answer: a camera that could not be asked is not a camera that was silenced. */
13068
+ var MigrateSwitchOutcomeSchema = _enum([
13069
+ "off",
13070
+ "on",
13071
+ "not-offered",
13072
+ "unreachable"
13073
+ ]);
13074
+ var MigrateSwitchReportSchema = object({
13075
+ deviceId: number(),
13076
+ switchId: CameraSwitchIdSchema,
13077
+ outcome: MigrateSwitchOutcomeSchema,
13078
+ detail: string().optional()
13079
+ });
13080
+ var MigrateDeviceResultSchema = object({
13081
+ sourceId: number(),
13082
+ targetId: number(),
13083
+ switches: array(MigrateSwitchReportSchema).readonly(),
13084
+ /** Switches that could NOT be turned off on the source. Empty is the only
13085
+ * value meaning the replaced hardware is quiet. */
13086
+ sourceStillLive: array(CameraSwitchIdSchema).readonly(),
13087
+ swapped: boolean()
13088
+ });
13065
13089
  method(object({
13066
13090
  addonId: string(),
13067
13091
  stableId: string()
13068
- }), object({ id: number() }), { kind: "mutation" }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
13092
+ }), object({ id: number() }), { kind: "mutation" }), method(object({
13093
+ sourceId: number(),
13094
+ targetId: number()
13095
+ }), MigrateDeviceResultSchema, {
13096
+ kind: "mutation",
13097
+ auth: "admin"
13098
+ }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
13069
13099
  deviceId: number(),
13070
13100
  name: string()
13071
13101
  }), _void(), {
@@ -19240,6 +19270,34 @@ var KeyEventsForDeviceSchema = object({
19240
19270
  deviceId: number(),
19241
19271
  events: array(KeyEventSchema).readonly()
19242
19272
  });
19273
+ /** One non-empty bucket of the timeline histogram. */
19274
+ var EventDensityBucketSchema = object({
19275
+ bucketStart: number(),
19276
+ motion: number().int(),
19277
+ object: number().int(),
19278
+ audio: number().int()
19279
+ });
19280
+ /**
19281
+ * One camera's row in a `getEventDensityBatch` answer.
19282
+ *
19283
+ * TWO facts, and the timeline draws them differently:
19284
+ *
19285
+ * - `read: 'read'` — the histogram for this camera. `buckets: []` is a real
19286
+ * claim: read, and nothing happened in the window.
19287
+ * - `read: 'unreadable'` — nobody could answer for this camera. `buckets` is
19288
+ * empty and that emptiness means NOTHING; the timeline must render unknown,
19289
+ * not quiet.
19290
+ *
19291
+ * Fanned out per camera the difference was free — one query rejected while the
19292
+ * others resolved — and collapsing to a batch is the exact moment it is lost
19293
+ * silently. Every requested id gets a row: a caller that asked for twelve and
19294
+ * got eleven cannot tell which one is missing, or that any is.
19295
+ */
19296
+ var EventDensityForDeviceSchema = object({
19297
+ deviceId: number(),
19298
+ read: _enum(["read", "unreadable"]),
19299
+ buckets: array(EventDensityBucketSchema).readonly()
19300
+ });
19243
19301
  object({
19244
19302
  trackId: string(),
19245
19303
  className: string(),
@@ -19550,12 +19608,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19550
19608
  since: number(),
19551
19609
  until: number(),
19552
19610
  bucketMs: number().int().positive()
19553
- }), array(object({
19554
- bucketStart: number(),
19555
- motion: number().int(),
19556
- object: number().int(),
19557
- audio: number().int()
19558
- })).readonly()), method(object({
19611
+ }), array(EventDensityBucketSchema).readonly()), method(object({
19612
+ deviceIds: array(number()).min(1).max(200),
19613
+ since: number(),
19614
+ until: number(),
19615
+ bucketMs: number().int().positive()
19616
+ }), array(EventDensityForDeviceSchema).readonly()), method(object({
19559
19617
  deviceId: number(),
19560
19618
  cutoffMs: number()
19561
19619
  }), object({
@@ -28692,6 +28750,39 @@ var RecordingDaysSchema = object({
28692
28750
  /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
28693
28751
  days: array(number())
28694
28752
  });
28753
+ /**
28754
+ * One camera's row in a `getAvailabilityBatch` answer.
28755
+ *
28756
+ * `ranges` is EXACTLY what `getAvailability` returns for that camera — the
28757
+ * batch collapses the transport, not the work — plus the one thing the singular
28758
+ * method never had to say:
28759
+ *
28760
+ * - `read: 'read'` — answered. `ranges: []` means "read, and this camera has
28761
+ * no footage in the window", which is a real claim.
28762
+ * - `read: 'unreadable'` — the recorder could not answer for this camera (its
28763
+ * calendar rung threw, its location is unmounted). `ranges` is empty and
28764
+ * that emptiness means NOTHING.
28765
+ *
28766
+ * A timeline that renders the second as the first tells an operator there is no
28767
+ * footage when the truth is that nobody looked.
28768
+ */
28769
+ var RecordingAvailabilityForDeviceSchema = object({
28770
+ deviceId: number(),
28771
+ read: _enum(["read", "unreadable"]),
28772
+ ranges: array(RecordingRangeSchema).readonly()
28773
+ });
28774
+ /**
28775
+ * One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
28776
+ * {@link RecordingAvailabilityForDeviceSchema}: `days: []` on a `'read'` row is
28777
+ * "no footage in this month", `read: 'unreadable'` is "nobody could look", and
28778
+ * the date-picker's day dots must not spell them the same.
28779
+ */
28780
+ var RecordingDaysForDeviceSchema = object({
28781
+ deviceId: number(),
28782
+ read: _enum(["read", "unreadable"]),
28783
+ /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
28784
+ days: array(number()).readonly()
28785
+ });
28695
28786
  var RecordingManifestSchema = object({
28696
28787
  deviceId: number(),
28697
28788
  /** Local filesystem path to the master playlist; null when no recording exists for the requested range. */
@@ -28926,6 +29017,13 @@ method(object({
28926
29017
  }), RecordingAvailabilitySchema, {
28927
29018
  kind: "query",
28928
29019
  auth: "protected"
29020
+ }), method(object({
29021
+ deviceIds: array(number()).min(1).max(200),
29022
+ fromMs: number(),
29023
+ toMs: number()
29024
+ }), array(RecordingAvailabilityForDeviceSchema).readonly(), {
29025
+ kind: "query",
29026
+ auth: "protected"
28929
29027
  }), method(object({
28930
29028
  deviceId: number(),
28931
29029
  fromMs: number(),
@@ -28934,6 +29032,14 @@ method(object({
28934
29032
  }), RecordingDaysSchema, {
28935
29033
  kind: "query",
28936
29034
  auth: "protected"
29035
+ }), method(object({
29036
+ deviceIds: array(number()).min(1).max(200),
29037
+ fromMs: number(),
29038
+ toMs: number(),
29039
+ tzOffsetMinutes: number()
29040
+ }), array(RecordingDaysForDeviceSchema).readonly(), {
29041
+ kind: "query",
29042
+ auth: "protected"
28937
29043
  }), method(object({
28938
29044
  deviceId: number(),
28939
29045
  fromMs: number(),
@@ -34096,6 +34202,12 @@ Object.freeze({
34096
34202
  addonId: null,
34097
34203
  access: "view"
34098
34204
  },
34205
+ "deviceManager.migrateDevice": {
34206
+ capName: "device-manager",
34207
+ capScope: "system",
34208
+ addonId: null,
34209
+ access: "create"
34210
+ },
34099
34211
  "deviceManager.persistConfig": {
34100
34212
  capName: "device-manager",
34101
34213
  capScope: "system",
@@ -35830,6 +35942,12 @@ Object.freeze({
35830
35942
  addonId: null,
35831
35943
  access: "view"
35832
35944
  },
35945
+ "pipelineAnalytics.getEventDensityBatch": {
35946
+ capName: "pipeline-analytics",
35947
+ capScope: "device",
35948
+ addonId: null,
35949
+ access: "view"
35950
+ },
35833
35951
  "pipelineAnalytics.getEventMedia": {
35834
35952
  capName: "pipeline-analytics",
35835
35953
  capScope: "device",
@@ -36952,12 +37070,24 @@ Object.freeze({
36952
37070
  addonId: null,
36953
37071
  access: "view"
36954
37072
  },
37073
+ "recording.getAvailabilityBatch": {
37074
+ capName: "recording",
37075
+ capScope: "system",
37076
+ addonId: null,
37077
+ access: "view"
37078
+ },
36955
37079
  "recording.getDaysWithRecordings": {
36956
37080
  capName: "recording",
36957
37081
  capScope: "system",
36958
37082
  addonId: null,
36959
37083
  access: "view"
36960
37084
  },
37085
+ "recording.getDaysWithRecordingsBatch": {
37086
+ capName: "recording",
37087
+ capScope: "system",
37088
+ addonId: null,
37089
+ access: "view"
37090
+ },
36961
37091
  "recording.getDeviceConfig": {
36962
37092
  capName: "recording",
36963
37093
  capScope: "system",
@@ -39562,6 +39692,11 @@ Object.freeze({
39562
39692
  form: "single",
39563
39693
  optional: false
39564
39694
  }],
39695
+ "pipelineAnalytics.getEventDensityBatch": [{
39696
+ name: "deviceIds",
39697
+ form: "array",
39698
+ optional: false
39699
+ }],
39565
39700
  "pipelineAnalytics.getEventMedia": [{
39566
39701
  name: "deviceId",
39567
39702
  form: "single",
@@ -39977,11 +40112,21 @@ Object.freeze({
39977
40112
  form: "single",
39978
40113
  optional: false
39979
40114
  }],
40115
+ "recording.getAvailabilityBatch": [{
40116
+ name: "deviceIds",
40117
+ form: "array",
40118
+ optional: false
40119
+ }],
39980
40120
  "recording.getDaysWithRecordings": [{
39981
40121
  name: "deviceId",
39982
40122
  form: "single",
39983
40123
  optional: false
39984
40124
  }],
40125
+ "recording.getDaysWithRecordingsBatch": [{
40126
+ name: "deviceIds",
40127
+ form: "array",
40128
+ optional: false
40129
+ }],
39985
40130
  "recording.getDeviceConfig": [{
39986
40131
  name: "deviceId",
39987
40132
  form: "single",
package/dist/addon.mjs CHANGED
@@ -7729,7 +7729,8 @@ var OpsLogReasonSchema = _enum([
7729
7729
  "quota",
7730
7730
  "manual",
7731
7731
  "operator",
7732
- "maintenance"
7732
+ "maintenance",
7733
+ "orphaned-device"
7733
7734
  ]);
7734
7735
  /** One audit row, shared verbatim by both domains. */
7735
7736
  var OpsLogEntrySchema = object({
@@ -13041,10 +13042,39 @@ var DevicePersistConfigPayloadSchema = object({
13041
13042
  deviceId: number(),
13042
13043
  data: record(string(), unknown())
13043
13044
  });
13045
+ /** What a migration actually did, per switch. `unreachable` is a first-class
13046
+ * answer: a camera that could not be asked is not a camera that was silenced. */
13047
+ var MigrateSwitchOutcomeSchema = _enum([
13048
+ "off",
13049
+ "on",
13050
+ "not-offered",
13051
+ "unreachable"
13052
+ ]);
13053
+ var MigrateSwitchReportSchema = object({
13054
+ deviceId: number(),
13055
+ switchId: CameraSwitchIdSchema,
13056
+ outcome: MigrateSwitchOutcomeSchema,
13057
+ detail: string().optional()
13058
+ });
13059
+ var MigrateDeviceResultSchema = object({
13060
+ sourceId: number(),
13061
+ targetId: number(),
13062
+ switches: array(MigrateSwitchReportSchema).readonly(),
13063
+ /** Switches that could NOT be turned off on the source. Empty is the only
13064
+ * value meaning the replaced hardware is quiet. */
13065
+ sourceStillLive: array(CameraSwitchIdSchema).readonly(),
13066
+ swapped: boolean()
13067
+ });
13044
13068
  method(object({
13045
13069
  addonId: string(),
13046
13070
  stableId: string()
13047
- }), object({ id: number() }), { kind: "mutation" }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
13071
+ }), object({ id: number() }), { kind: "mutation" }), method(object({
13072
+ sourceId: number(),
13073
+ targetId: number()
13074
+ }), MigrateDeviceResultSchema, {
13075
+ kind: "mutation",
13076
+ auth: "admin"
13077
+ }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
13048
13078
  deviceId: number(),
13049
13079
  name: string()
13050
13080
  }), _void(), {
@@ -19219,6 +19249,34 @@ var KeyEventsForDeviceSchema = object({
19219
19249
  deviceId: number(),
19220
19250
  events: array(KeyEventSchema).readonly()
19221
19251
  });
19252
+ /** One non-empty bucket of the timeline histogram. */
19253
+ var EventDensityBucketSchema = object({
19254
+ bucketStart: number(),
19255
+ motion: number().int(),
19256
+ object: number().int(),
19257
+ audio: number().int()
19258
+ });
19259
+ /**
19260
+ * One camera's row in a `getEventDensityBatch` answer.
19261
+ *
19262
+ * TWO facts, and the timeline draws them differently:
19263
+ *
19264
+ * - `read: 'read'` — the histogram for this camera. `buckets: []` is a real
19265
+ * claim: read, and nothing happened in the window.
19266
+ * - `read: 'unreadable'` — nobody could answer for this camera. `buckets` is
19267
+ * empty and that emptiness means NOTHING; the timeline must render unknown,
19268
+ * not quiet.
19269
+ *
19270
+ * Fanned out per camera the difference was free — one query rejected while the
19271
+ * others resolved — and collapsing to a batch is the exact moment it is lost
19272
+ * silently. Every requested id gets a row: a caller that asked for twelve and
19273
+ * got eleven cannot tell which one is missing, or that any is.
19274
+ */
19275
+ var EventDensityForDeviceSchema = object({
19276
+ deviceId: number(),
19277
+ read: _enum(["read", "unreadable"]),
19278
+ buckets: array(EventDensityBucketSchema).readonly()
19279
+ });
19222
19280
  object({
19223
19281
  trackId: string(),
19224
19282
  className: string(),
@@ -19529,12 +19587,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19529
19587
  since: number(),
19530
19588
  until: number(),
19531
19589
  bucketMs: number().int().positive()
19532
- }), array(object({
19533
- bucketStart: number(),
19534
- motion: number().int(),
19535
- object: number().int(),
19536
- audio: number().int()
19537
- })).readonly()), method(object({
19590
+ }), array(EventDensityBucketSchema).readonly()), method(object({
19591
+ deviceIds: array(number()).min(1).max(200),
19592
+ since: number(),
19593
+ until: number(),
19594
+ bucketMs: number().int().positive()
19595
+ }), array(EventDensityForDeviceSchema).readonly()), method(object({
19538
19596
  deviceId: number(),
19539
19597
  cutoffMs: number()
19540
19598
  }), object({
@@ -28671,6 +28729,39 @@ var RecordingDaysSchema = object({
28671
28729
  /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
28672
28730
  days: array(number())
28673
28731
  });
28732
+ /**
28733
+ * One camera's row in a `getAvailabilityBatch` answer.
28734
+ *
28735
+ * `ranges` is EXACTLY what `getAvailability` returns for that camera — the
28736
+ * batch collapses the transport, not the work — plus the one thing the singular
28737
+ * method never had to say:
28738
+ *
28739
+ * - `read: 'read'` — answered. `ranges: []` means "read, and this camera has
28740
+ * no footage in the window", which is a real claim.
28741
+ * - `read: 'unreadable'` — the recorder could not answer for this camera (its
28742
+ * calendar rung threw, its location is unmounted). `ranges` is empty and
28743
+ * that emptiness means NOTHING.
28744
+ *
28745
+ * A timeline that renders the second as the first tells an operator there is no
28746
+ * footage when the truth is that nobody looked.
28747
+ */
28748
+ var RecordingAvailabilityForDeviceSchema = object({
28749
+ deviceId: number(),
28750
+ read: _enum(["read", "unreadable"]),
28751
+ ranges: array(RecordingRangeSchema).readonly()
28752
+ });
28753
+ /**
28754
+ * One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
28755
+ * {@link RecordingAvailabilityForDeviceSchema}: `days: []` on a `'read'` row is
28756
+ * "no footage in this month", `read: 'unreadable'` is "nobody could look", and
28757
+ * the date-picker's day dots must not spell them the same.
28758
+ */
28759
+ var RecordingDaysForDeviceSchema = object({
28760
+ deviceId: number(),
28761
+ read: _enum(["read", "unreadable"]),
28762
+ /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
28763
+ days: array(number()).readonly()
28764
+ });
28674
28765
  var RecordingManifestSchema = object({
28675
28766
  deviceId: number(),
28676
28767
  /** Local filesystem path to the master playlist; null when no recording exists for the requested range. */
@@ -28905,6 +28996,13 @@ method(object({
28905
28996
  }), RecordingAvailabilitySchema, {
28906
28997
  kind: "query",
28907
28998
  auth: "protected"
28999
+ }), method(object({
29000
+ deviceIds: array(number()).min(1).max(200),
29001
+ fromMs: number(),
29002
+ toMs: number()
29003
+ }), array(RecordingAvailabilityForDeviceSchema).readonly(), {
29004
+ kind: "query",
29005
+ auth: "protected"
28908
29006
  }), method(object({
28909
29007
  deviceId: number(),
28910
29008
  fromMs: number(),
@@ -28913,6 +29011,14 @@ method(object({
28913
29011
  }), RecordingDaysSchema, {
28914
29012
  kind: "query",
28915
29013
  auth: "protected"
29014
+ }), method(object({
29015
+ deviceIds: array(number()).min(1).max(200),
29016
+ fromMs: number(),
29017
+ toMs: number(),
29018
+ tzOffsetMinutes: number()
29019
+ }), array(RecordingDaysForDeviceSchema).readonly(), {
29020
+ kind: "query",
29021
+ auth: "protected"
28916
29022
  }), method(object({
28917
29023
  deviceId: number(),
28918
29024
  fromMs: number(),
@@ -34075,6 +34181,12 @@ Object.freeze({
34075
34181
  addonId: null,
34076
34182
  access: "view"
34077
34183
  },
34184
+ "deviceManager.migrateDevice": {
34185
+ capName: "device-manager",
34186
+ capScope: "system",
34187
+ addonId: null,
34188
+ access: "create"
34189
+ },
34078
34190
  "deviceManager.persistConfig": {
34079
34191
  capName: "device-manager",
34080
34192
  capScope: "system",
@@ -35809,6 +35921,12 @@ Object.freeze({
35809
35921
  addonId: null,
35810
35922
  access: "view"
35811
35923
  },
35924
+ "pipelineAnalytics.getEventDensityBatch": {
35925
+ capName: "pipeline-analytics",
35926
+ capScope: "device",
35927
+ addonId: null,
35928
+ access: "view"
35929
+ },
35812
35930
  "pipelineAnalytics.getEventMedia": {
35813
35931
  capName: "pipeline-analytics",
35814
35932
  capScope: "device",
@@ -36931,12 +37049,24 @@ Object.freeze({
36931
37049
  addonId: null,
36932
37050
  access: "view"
36933
37051
  },
37052
+ "recording.getAvailabilityBatch": {
37053
+ capName: "recording",
37054
+ capScope: "system",
37055
+ addonId: null,
37056
+ access: "view"
37057
+ },
36934
37058
  "recording.getDaysWithRecordings": {
36935
37059
  capName: "recording",
36936
37060
  capScope: "system",
36937
37061
  addonId: null,
36938
37062
  access: "view"
36939
37063
  },
37064
+ "recording.getDaysWithRecordingsBatch": {
37065
+ capName: "recording",
37066
+ capScope: "system",
37067
+ addonId: null,
37068
+ access: "view"
37069
+ },
36940
37070
  "recording.getDeviceConfig": {
36941
37071
  capName: "recording",
36942
37072
  capScope: "system",
@@ -39541,6 +39671,11 @@ Object.freeze({
39541
39671
  form: "single",
39542
39672
  optional: false
39543
39673
  }],
39674
+ "pipelineAnalytics.getEventDensityBatch": [{
39675
+ name: "deviceIds",
39676
+ form: "array",
39677
+ optional: false
39678
+ }],
39544
39679
  "pipelineAnalytics.getEventMedia": [{
39545
39680
  name: "deviceId",
39546
39681
  form: "single",
@@ -39956,11 +40091,21 @@ Object.freeze({
39956
40091
  form: "single",
39957
40092
  optional: false
39958
40093
  }],
40094
+ "recording.getAvailabilityBatch": [{
40095
+ name: "deviceIds",
40096
+ form: "array",
40097
+ optional: false
40098
+ }],
39959
40099
  "recording.getDaysWithRecordings": [{
39960
40100
  name: "deviceId",
39961
40101
  form: "single",
39962
40102
  optional: false
39963
40103
  }],
40104
+ "recording.getDaysWithRecordingsBatch": [{
40105
+ name: "deviceIds",
40106
+ form: "array",
40107
+ optional: false
40108
+ }],
39964
40109
  "recording.getDeviceConfig": [{
39965
40110
  name: "deviceId",
39966
40111
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-wyze",
3
- "version": "0.2.55",
3
+ "version": "0.2.57",
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",