@mhome/core-protocol 1.41.0 → 1.47.0

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/README.md CHANGED
@@ -5,6 +5,15 @@ provider runtimes. Public `/app/*` contracts belong to
5
5
  `mhome-app-facade-api`; this crate owns internal MWS, authentication, LLM,
6
6
  Messaging normalization, Node runtime, and Storage contracts.
7
7
 
8
+ `messaging::IngressRedeem` carries a parsed identity code and a dedicated
9
+ `IdentityRedemptionContext`: event ID, receiving address, sender and optional
10
+ conversation display name. Redemption is independent of normalized message
11
+ content and its schema version. The receiver validates the context and authorizes
12
+ the registered account before consuming the code. External protocol 23 requires
13
+ Host and Core to use the dedicated context; cloud ingress must be upgraded with
14
+ the local receiver. There is no full-message redemption compatibility path. The shared wire example is
15
+ [`fixtures/messaging-redemption.json`](fixtures/messaging-redemption.json).
16
+
8
17
  The additive [`client_identity`](contract/client-identity.md) contract separates
9
18
  User and Space Client identity, installation ID and enrollment ID. Runtime ingress
10
19
  has not yet cut over to this contract; operation authorization remains separate.
@@ -156,3 +165,41 @@ same operations through `hostList` / `hostRuntime`. None accepts a Space ID. Cli
156
165
  discover `_mhome-host._tcp`, verify reply Host IDs, and unwrap the HTTP runtime envelope.
157
166
  Install/restart submissions are not automatically replayed after transport failure.
158
167
  First claim is direct LAN only; daily User management can use the current Core as a token-forwarding proxy.
168
+
169
+ ## Operation timeouts
170
+
171
+ `manifest/operation-timeouts.v1.json` is the code-owned catalog of shared operation
172
+ budgets, ingress selectors and caller reply-wait profiles. It has no runtime
173
+ configuration or database dependency. Edit the catalog and run
174
+ `npm run generate:operation-timeouts`; Rust consumes generated constants and Lion
175
+ loads the same packaged JSON once at startup. Both replay the packaged
176
+ `fixtures/operation-timeouts.conformance.json`. Java bindings reject unknown policy
177
+ IDs instead of silently choosing a different limit.
178
+
179
+ An execution budget and a reply wait are different: reply grace permits transfer
180
+ of a completed result or timeout error; it never grants the callee more execution
181
+ time. Existing domain-owned lifetimes (streams, subscriptions, long batch jobs)
182
+ and implementation-specific step timeouts are not replaced by a default request
183
+ budget. New shared operations need an explicit catalog entry and selector tests.
184
+
185
+ `execution_budget::Budgeted<T>` adds optional `executionBudget: { remainingMs }`
186
+ metadata alongside an object envelope's existing fields. Bridge uses it on MWS
187
+ requests; Host IPC uses it alongside `input` in `HandleCoreInput`. It is not an
188
+ App Facade `control` field or part of the business payload. Values are integral
189
+ milliseconds in `0..=2147483647`; zero is expired. Missing metadata retains local
190
+ policy. This additive metadata does not change external IPC protocol 23; both
191
+ ends need the new implementation for remaining-budget propagation.
192
+
193
+ Each process anchors received time to its monotonic clock, caps it by local policy,
194
+ and passes the remaining budget to children. Sender queueing/upload, receiver
195
+ queueing/download, attempts and backoff consume that same deadline. Round down
196
+ when emitting milliseconds. Durable Lion peer relays also deduct queue age using
197
+ the existing peer message timestamp/clock convention. Network transit and clock
198
+ skew are not a strict global stop guarantee; the caller keeps its own deadline.
199
+ Do not put a refreshed full duration around each retry.
200
+
201
+ Timeout means that the caller stopped waiting and cancelled owned asynchronous
202
+ work where supported. It does not roll back completed writes, stop arbitrary
203
+ blocking code, or prove that detached work drained. Request-owned spawned tasks
204
+ must explicitly carry the captured deadline. Responses and independent background
205
+ jobs have their own lifetime. Migration freezing/drain remains a separate feature.
@@ -38,7 +38,7 @@
38
38
  "state.cameraMotion"
39
39
  ],
40
40
  "health": "ready",
41
- "sourceId": null,
41
+ "connectionId": "connection-1",
42
42
  "resolutionStatus": "ready"
43
43
  }
44
44
  ],
@@ -50,7 +50,7 @@
50
50
  "source": "onvif"
51
51
  }
52
52
  ],
53
- "sources": [],
53
+ "connections": [],
54
54
  "cameraSettings": [
55
55
  {
56
56
  "deviceId": "camera-1",
@@ -59,7 +59,9 @@
59
59
  "watchMode": "off",
60
60
  "objectDetectionMode": "off",
61
61
  "personDetectionMode": "off",
62
- "recordMode": "off"
62
+ "recordMode": "off",
63
+ "preRollSeconds": 8,
64
+ "postRollSeconds": 15
63
65
  }
64
66
  }
65
67
  ]
@@ -78,7 +80,8 @@
78
80
  "deviceIds": [
79
81
  "camera-1"
80
82
  ],
81
- "changed": true
83
+ "changed": true,
84
+ "connectionId": "connection-1"
82
85
  }
83
86
  },
84
87
  {
@@ -90,7 +93,9 @@
90
93
  "watchMode": "off",
91
94
  "objectDetectionMode": "off",
92
95
  "personDetectionMode": "off",
93
- "recordMode": "off"
96
+ "recordMode": "off",
97
+ "preRollSeconds": 8,
98
+ "postRollSeconds": 15
94
99
  }
95
100
  }
96
101
  },
@@ -103,26 +108,66 @@
103
108
  }
104
109
  },
105
110
  {
106
- "name": "remove-ha-source",
111
+ "name": "connection-detail-request",
107
112
  "value": {
108
- "sourceId": "ha-1"
113
+ "connectionId": "ha-1"
109
114
  }
110
115
  },
111
116
  {
112
117
  "name": "connected-empty-ha",
113
118
  "value": {
114
119
  "providers": [],
115
- "sources": [
120
+ "connections": [
116
121
  {
117
122
  "id": "ha-1",
118
123
  "name": "http://homeassistant.local:8123",
119
- "deviceCount": 0
124
+ "deviceCount": 0,
125
+ "provider": "home_assistant",
126
+ "revision": 1
120
127
  }
121
128
  ],
122
129
  "devices": [],
123
130
  "onlineStates": [],
124
131
  "cameraSettings": []
125
132
  }
133
+ },
134
+ {
135
+ "name": "recording without preroll or postroll",
136
+ "value": {
137
+ "deviceId": "camera-1",
138
+ "settings": {
139
+ "preRollSeconds": 0,
140
+ "postRollSeconds": 0
141
+ }
142
+ }
143
+ },
144
+ {
145
+ "name": "recording retention limits",
146
+ "value": {
147
+ "deviceId": "camera-1",
148
+ "settings": {
149
+ "preRollSeconds": 120,
150
+ "postRollSeconds": 300
151
+ }
152
+ }
153
+ },
154
+ {
155
+ "name": "update only preroll",
156
+ "value": {
157
+ "deviceId": "camera-1",
158
+ "settings": {
159
+ "preRollSeconds": 60
160
+ }
161
+ }
162
+ },
163
+ {
164
+ "name": "update only postroll",
165
+ "value": {
166
+ "deviceId": "camera-1",
167
+ "settings": {
168
+ "postRollSeconds": 30
169
+ }
170
+ }
126
171
  }
127
172
  ],
128
173
  "invalid": [
@@ -143,12 +188,12 @@
143
188
  "name": "Front",
144
189
  "capabilities": [],
145
190
  "health": "unknown",
146
- "sourceId": null,
191
+ "connectionId": "connection-1",
147
192
  "resolutionStatus": "ready"
148
193
  }
149
194
  ],
150
195
  "onlineStates": [],
151
- "sources": [],
196
+ "connections": [],
152
197
  "cameraSettings": []
153
198
  }
154
199
  },
@@ -162,7 +207,7 @@
162
207
  "capabilities": [],
163
208
  "health": "ready",
164
209
  "sourceUid": "secret",
165
- "sourceId": null,
210
+ "connectionId": "connection-1",
166
211
  "resolutionStatus": "ready"
167
212
  }
168
213
  ],
@@ -184,6 +229,137 @@
184
229
  "watchMode": "on"
185
230
  }
186
231
  }
232
+ },
233
+ {
234
+ "name": "preRollSeconds null",
235
+ "value": {
236
+ "deviceId": "camera-1",
237
+ "settings": {
238
+ "preRollSeconds": null
239
+ }
240
+ }
241
+ },
242
+ {
243
+ "name": "preRollSeconds negative",
244
+ "value": {
245
+ "deviceId": "camera-1",
246
+ "settings": {
247
+ "preRollSeconds": -1
248
+ }
249
+ }
250
+ },
251
+ {
252
+ "name": "preRollSeconds fractional",
253
+ "value": {
254
+ "deviceId": "camera-1",
255
+ "settings": {
256
+ "preRollSeconds": 1.5
257
+ }
258
+ }
259
+ },
260
+ {
261
+ "name": "preRollSeconds string",
262
+ "value": {
263
+ "deviceId": "camera-1",
264
+ "settings": {
265
+ "preRollSeconds": "15"
266
+ }
267
+ }
268
+ },
269
+ {
270
+ "name": "preRollSeconds above limit",
271
+ "value": {
272
+ "deviceId": "camera-1",
273
+ "settings": {
274
+ "preRollSeconds": 121
275
+ }
276
+ }
277
+ },
278
+ {
279
+ "name": "settings snapshot missing preRollSeconds",
280
+ "value": {
281
+ "deviceId": "camera-1",
282
+ "revision": 1,
283
+ "settings": {
284
+ "watchMode": "off",
285
+ "objectDetectionMode": "off",
286
+ "personDetectionMode": "off",
287
+ "recordMode": "off",
288
+ "postRollSeconds": 15
289
+ }
290
+ }
291
+ },
292
+ {
293
+ "name": "postRollSeconds null",
294
+ "value": {
295
+ "deviceId": "camera-1",
296
+ "settings": {
297
+ "postRollSeconds": null
298
+ }
299
+ }
300
+ },
301
+ {
302
+ "name": "postRollSeconds negative",
303
+ "value": {
304
+ "deviceId": "camera-1",
305
+ "settings": {
306
+ "postRollSeconds": -1
307
+ }
308
+ }
309
+ },
310
+ {
311
+ "name": "postRollSeconds fractional",
312
+ "value": {
313
+ "deviceId": "camera-1",
314
+ "settings": {
315
+ "postRollSeconds": 1.5
316
+ }
317
+ }
318
+ },
319
+ {
320
+ "name": "postRollSeconds string",
321
+ "value": {
322
+ "deviceId": "camera-1",
323
+ "settings": {
324
+ "postRollSeconds": "15"
325
+ }
326
+ }
327
+ },
328
+ {
329
+ "name": "postRollSeconds above limit",
330
+ "value": {
331
+ "deviceId": "camera-1",
332
+ "settings": {
333
+ "postRollSeconds": 301
334
+ }
335
+ }
336
+ },
337
+ {
338
+ "name": "settings snapshot missing postRollSeconds",
339
+ "value": {
340
+ "deviceId": "camera-1",
341
+ "revision": 1,
342
+ "settings": {
343
+ "watchMode": "off",
344
+ "objectDetectionMode": "off",
345
+ "personDetectionMode": "off",
346
+ "recordMode": "off",
347
+ "preRollSeconds": 8
348
+ }
349
+ }
350
+ },
351
+ {
352
+ "name": "old source request",
353
+ "value": {
354
+ "sourceId": "ha-1"
355
+ }
356
+ },
357
+ {
358
+ "name": "disconnect without valid revision",
359
+ "value": {
360
+ "connectionId": "connection-1",
361
+ "expectedRevision": 0
362
+ }
187
363
  }
188
364
  ]
189
365
  }
@@ -0,0 +1,18 @@
1
+ [
2
+ {"target":"/app/widget/list","payload":{},"operation":"coreRequest","totalMs":60000},
3
+ {"target":"/harness/test/session/open","payload":{},"operation":"harnessSessionOpen","totalMs":150000},
4
+ {"target":"/app/automation/save","payload":{"input":{"script":{"scripts":{"lighting":"source"}}}},"operation":"scriptSave","totalMs":420000,"lionWaitMs":450000},
5
+ {"target":"/app/automation/verify","payload":{"input":{"script":{"scripts":{"lighting":"source"}}}},"operation":"scriptValidation","totalMs":205000,"lionWaitMs":230000},
6
+ {"target":"/automation/save","payload":{"script":{"scripts":{"lighting":"source"}}},"operation":"scriptSave","totalMs":420000},
7
+ {"target":"/invoke/app/automation/save","payload":{"call":{"input":{"script":{"scripts":{"lighting":"source"}}}}},"operation":"scriptSave","totalMs":420000},
8
+ {"target":"/dispatch/agent/app/automation/verify","payload":{"call":{"input":{"script":{"scripts":{"lighting":"source"}}}}},"operation":"scriptValidation","totalMs":205000},
9
+ {"target":"/dispatch/app/automation/save","payload":{"call":{"input":{"script":{"scripts":{"lighting":"source"}}}}},"operation":"scriptSave","totalMs":420000},
10
+ {"target":"/app/automation/save","payload":{"input":{"script":{"scripts":{}}}},"operation":"coreRequest","totalMs":60000},
11
+ {"target":"/app/automation/save","payload":{"script":{"scripts":{"lighting":"source"}}},"operation":"coreRequest","totalMs":60000},
12
+ {"target":"/app/automation/save","payload":{"input":{"script":{"scripts":[]}}},"operation":"coreRequest","totalMs":60000},
13
+ {"target":"/app/automation/snapshot/rollback","payload":{},"operation":"scriptSave","totalMs":420000},
14
+ {"target":"/automation/copy","payload":{},"operation":"scriptSave","totalMs":420000},
15
+ {"target":"/automation/enroll","payload":{},"operation":"scriptSave","totalMs":420000},
16
+ {"target":"/automation/overview/save","payload":{},"operation":"scriptSave","totalMs":420000},
17
+ {"target":"/bridge/automation/compile","payload":{"script":{"scripts":{"lighting":"source"}}},"operation":"scriptValidation","totalMs":205000}
18
+ ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.core.v1",
3
- "contractVersion": "1.41.0",
3
+ "contractVersion": "1.47.0",
4
4
  "protocols": {
5
5
  "agentGateway": {
6
6
  "submitTarget": "/agent/submit",
@@ -30,13 +30,16 @@
30
30
  "routes": {
31
31
  "deviceList": "device/list",
32
32
  "managementSnapshot": "management/snapshot",
33
- "deviceRemove": "device/remove",
34
- "sourceRemove": "source/remove",
35
33
  "deviceSettingsGet": "device/settings/get",
36
- "deviceSettingsUpdate": "device/settings/update"
34
+ "deviceSettingsUpdate": "device/settings/update",
35
+ "providerList": "provider/list",
36
+ "connectionList": "connection/list",
37
+ "connectionDetail": "connection/detail",
38
+ "connectionDisconnect": "connection/disconnect"
37
39
  },
38
40
  "flows": {
39
- "deviceAdd": "device.add"
41
+ "connectionCreate": "connection.create",
42
+ "connectionReconfigure": "connection.reconfigure"
40
43
  }
41
44
  },
42
45
  "audioBridgePlugin": {
@@ -49,7 +52,6 @@
49
52
  "deviceRefresh": "device/refresh",
50
53
  "devicePair": "device/pair",
51
54
  "deviceUnpair": "device/unpair",
52
- "deviceTest": "device/test",
53
55
  "deviceListenSet": "device/listen",
54
56
  "smartSpeakerSnapshot": "smart-speaker/snapshot",
55
57
  "smartSpeakerCreate": "smart-speaker/create",
@@ -66,7 +68,7 @@
66
68
  "version": "storage.v2"
67
69
  },
68
70
  "hostDelivery": {
69
- "externalProtocolVersion": 21,
71
+ "externalProtocolVersion": 23,
70
72
  "requestKind": "deliveryRequested",
71
73
  "connectionControlKind": "connectionControlRequested",
72
74
  "acceptanceBoundaries": [
@@ -0,0 +1,27 @@
1
+ {
2
+ "manifest": "mhome.operation-timeouts.v1",
3
+ "operations": {
4
+ "coreRequest": { "totalMs": 60000, "description": "Default Core request execution." },
5
+ "harnessSessionOpen": { "totalMs": 150000, "description": "Harness session initialization." },
6
+ "scriptValidation": { "totalMs": 205000, "description": "Companion Rust build and validation." },
7
+ "scriptSave": { "totalMs": 420000, "description": "Companion Rust build, installation and save." },
8
+ "coreBridgeRequest": { "totalMs": 35000, "description": "Core outgoing cloud request, including sending." },
9
+ "coreBridgeInbound": { "totalMs": 55000, "description": "Ordinary incoming Core Bridge request, including queueing." },
10
+ "nodeRequest": { "totalMs": 60000, "description": "Core outgoing Node request, including sending." },
11
+ "lionBridgeRequest": { "totalMs": 60000, "description": "Default Lion outgoing Hub request." },
12
+ "llmCloudCall": { "totalMs": 180000, "description": "Cloud model call preparation, execution and accounting." }
13
+ },
14
+ "waitProfiles": {
15
+ "hostIpc": { "graceMs": 5000 },
16
+ "coreBridge": { "graceMs": 10000 },
17
+ "client": { "graceMs": 35000 },
18
+ "lionForward": { "graceMs": 0, "overrides": { "scriptValidation": 25000, "scriptSave": 30000 } }
19
+ },
20
+ "selectors": {
21
+ "forwardPrefixes": ["/invoke", "/dispatch/agent", "/dispatch"],
22
+ "storedScriptSave": ["/automation/snapshot/rollback", "/app/automation/snapshot/rollback", "/automation/overview/save", "/automation/copy", "/automation/enroll"],
23
+ "sourceScriptSave": ["/automation/save", "/app/automation/save"],
24
+ "sourceScriptValidation": ["/app/automation/verify", "/bridge/automation/parse", "/bridge/automation/format", "/bridge/automation/verify", "/automation/format", "/bridge/automation/compile", "/bridge/agent/automation/compile"],
25
+ "ordinaryOverrides": { "/harness/test/session/open": "harnessSessionOpen" }
26
+ }
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/core-protocol",
3
- "version": "1.41.0",
3
+ "version": "1.47.0",
4
4
  "description": "Build-time protocol artifacts for mhome.core",
5
5
  "license": "MIT",
6
6
  "repository": {
package/protocol.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocol": "mhome.core",
3
- "contractVersion": "1.41.0",
3
+ "contractVersion": "1.47.0",
4
4
  "manifest": "manifest/core.v1.json",
5
5
  "schemas": {
6
6
  "hostManagementRequest": "schema/host-management-request.v1.schema.json",
@@ -19,7 +19,7 @@
19
19
  "$ref": "#/$defs/deviceListResponse"
20
20
  },
21
21
  {
22
- "$ref": "#/$defs/deviceMutationResponse"
22
+ "$ref": "#/$defs/connectionMutationResponse"
23
23
  },
24
24
  {
25
25
  "$ref": "#/$defs/deviceSettingsResponse"
@@ -28,7 +28,19 @@
28
28
  "$ref": "#/$defs/errorResponse"
29
29
  },
30
30
  {
31
- "$ref": "#/$defs/sourceIdRequest"
31
+ "$ref": "#/$defs/connectionIdRequest"
32
+ },
33
+ {
34
+ "$ref": "#/$defs/connectionDisconnectRequest"
35
+ },
36
+ {
37
+ "$ref": "#/$defs/connectionListResponse"
38
+ },
39
+ {
40
+ "$ref": "#/$defs/providerListResponse"
41
+ },
42
+ {
43
+ "$ref": "#/$defs/connectionDetailResponse"
32
44
  }
33
45
  ],
34
46
  "$defs": {
@@ -82,7 +94,7 @@
82
94
  "name",
83
95
  "capabilities",
84
96
  "health",
85
- "sourceId",
97
+ "connectionId",
86
98
  "resolutionStatus"
87
99
  ],
88
100
  "properties": {
@@ -118,11 +130,8 @@
118
130
  "type": "string",
119
131
  "minLength": 1
120
132
  },
121
- "sourceId": {
122
- "type": [
123
- "string",
124
- "null"
125
- ],
133
+ "connectionId": {
134
+ "type": "string",
126
135
  "minLength": 1
127
136
  },
128
137
  "resolutionStatus": {
@@ -173,7 +182,7 @@
173
182
  "devices",
174
183
  "onlineStates",
175
184
  "cameraSettings",
176
- "sources"
185
+ "connections"
177
186
  ],
178
187
  "properties": {
179
188
  "providers": {
@@ -194,10 +203,10 @@
194
203
  "$ref": "#/$defs/onlineStateObservation"
195
204
  }
196
205
  },
197
- "sources": {
206
+ "connections": {
198
207
  "type": "array",
199
208
  "items": {
200
- "$ref": "#/$defs/homeAssistantSource"
209
+ "$ref": "#/$defs/cameraConnection"
201
210
  }
202
211
  },
203
212
  "cameraSettings": {
@@ -231,11 +240,12 @@
231
240
  },
232
241
  "additionalProperties": false
233
242
  },
234
- "deviceMutationResponse": {
243
+ "connectionMutationResponse": {
235
244
  "type": "object",
236
245
  "required": [
237
246
  "deviceIds",
238
- "changed"
247
+ "changed",
248
+ "connectionId"
239
249
  ],
240
250
  "properties": {
241
251
  "deviceIds": {
@@ -248,6 +258,10 @@
248
258
  },
249
259
  "changed": {
250
260
  "type": "boolean"
261
+ },
262
+ "connectionId": {
263
+ "type": "string",
264
+ "minLength": 1
251
265
  }
252
266
  },
253
267
  "additionalProperties": false
@@ -274,25 +288,27 @@
274
288
  },
275
289
  "additionalProperties": false
276
290
  },
277
- "sourceIdRequest": {
291
+ "connectionIdRequest": {
278
292
  "type": "object",
279
293
  "required": [
280
- "sourceId"
294
+ "connectionId"
281
295
  ],
282
296
  "properties": {
283
- "sourceId": {
297
+ "connectionId": {
284
298
  "type": "string",
285
299
  "minLength": 1
286
300
  }
287
301
  },
288
302
  "additionalProperties": false
289
303
  },
290
- "homeAssistantSource": {
304
+ "cameraConnection": {
291
305
  "type": "object",
292
306
  "required": [
293
307
  "id",
294
308
  "name",
295
- "deviceCount"
309
+ "deviceCount",
310
+ "provider",
311
+ "revision"
296
312
  ],
297
313
  "properties": {
298
314
  "id": {
@@ -318,6 +334,14 @@
318
334
  "string",
319
335
  "null"
320
336
  ]
337
+ },
338
+ "provider": {
339
+ "type": "string",
340
+ "minLength": 1
341
+ },
342
+ "revision": {
343
+ "type": "integer",
344
+ "minimum": 1
321
345
  }
322
346
  },
323
347
  "additionalProperties": false
@@ -348,6 +372,16 @@
348
372
  "off",
349
373
  "event"
350
374
  ]
375
+ },
376
+ "preRollSeconds": {
377
+ "type": "integer",
378
+ "minimum": 0,
379
+ "maximum": 120
380
+ },
381
+ "postRollSeconds": {
382
+ "type": "integer",
383
+ "minimum": 0,
384
+ "maximum": 300
351
385
  }
352
386
  },
353
387
  "additionalProperties": false,
@@ -355,7 +389,9 @@
355
389
  "watchMode",
356
390
  "objectDetectionMode",
357
391
  "personDetectionMode",
358
- "recordMode"
392
+ "recordMode",
393
+ "preRollSeconds",
394
+ "postRollSeconds"
359
395
  ]
360
396
  },
361
397
  "cameraSettingsPatch": {
@@ -384,6 +420,16 @@
384
420
  "off",
385
421
  "event"
386
422
  ]
423
+ },
424
+ "preRollSeconds": {
425
+ "type": "integer",
426
+ "minimum": 0,
427
+ "maximum": 120
428
+ },
429
+ "postRollSeconds": {
430
+ "type": "integer",
431
+ "minimum": 0,
432
+ "maximum": 300
387
433
  }
388
434
  },
389
435
  "additionalProperties": false,
@@ -427,6 +473,87 @@
427
473
  }
428
474
  },
429
475
  "additionalProperties": false
476
+ },
477
+ "connectionDisconnectRequest": {
478
+ "type": "object",
479
+ "required": [
480
+ "connectionId",
481
+ "expectedRevision"
482
+ ],
483
+ "properties": {
484
+ "connectionId": {
485
+ "type": "string",
486
+ "minLength": 1
487
+ },
488
+ "expectedRevision": {
489
+ "type": "integer",
490
+ "minimum": 1
491
+ },
492
+ "expectedDeviceIds": {
493
+ "type": "array",
494
+ "items": {
495
+ "type": "string"
496
+ },
497
+ "uniqueItems": true
498
+ }
499
+ },
500
+ "additionalProperties": false
501
+ },
502
+ "connectionListResponse": {
503
+ "type": "object",
504
+ "required": [
505
+ "connections"
506
+ ],
507
+ "properties": {
508
+ "connections": {
509
+ "type": "array",
510
+ "items": {
511
+ "$ref": "#/$defs/cameraConnection"
512
+ }
513
+ }
514
+ },
515
+ "additionalProperties": false
516
+ },
517
+ "providerListResponse": {
518
+ "type": "object",
519
+ "required": [
520
+ "providers"
521
+ ],
522
+ "properties": {
523
+ "providers": {
524
+ "type": "array",
525
+ "items": {
526
+ "$ref": "#/$defs/providerCatalogItem"
527
+ }
528
+ }
529
+ },
530
+ "additionalProperties": false
531
+ },
532
+ "connectionDetailResponse": {
533
+ "type": "object",
534
+ "required": [
535
+ "connection",
536
+ "devices"
537
+ ],
538
+ "properties": {
539
+ "connection": {
540
+ "anyOf": [
541
+ {
542
+ "$ref": "#/$defs/cameraConnection"
543
+ },
544
+ {
545
+ "type": "null"
546
+ }
547
+ ]
548
+ },
549
+ "devices": {
550
+ "type": "array",
551
+ "items": {
552
+ "$ref": "#/$defs/cameraDevice"
553
+ }
554
+ }
555
+ },
556
+ "additionalProperties": false
430
557
  }
431
558
  }
432
559
  }