@mhome/app-facade-protocol 1.90.1 → 1.91.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/contract/now-attention-v1.md +17 -4
- package/contract/resource-read-v1.md +29 -3
- package/fixtures/attention/device-observations.json +335 -0
- package/fixtures/entity-presentation.conformance.json +1 -1
- package/fixtures/group.conformance.json +1 -1
- package/fixtures/presentation/cases.json +4 -4
- package/manifest/app-facade.v1.json +1 -1
- package/manifest/authorization.v1.json +1 -1
- package/manifest/host-provision-targets.v1.json +1 -1
- package/manifest/hub-targets.v1.json +1 -1
- package/manifest/pod-targets.v1.json +1 -1
- package/manifest/routing.v1.json +1 -1
- package/manifest/targets.v1.json +1 -1
- package/package.json +1 -1
- package/protocol.json +1 -1
|
@@ -44,10 +44,23 @@ Standard device facts currently cover `state.water` (`wet` / `dry`) and
|
|
|
44
44
|
`state.smoke` (`detected` / `clear`). Other readings, including unknown/testing,
|
|
45
45
|
do not create an incident and do not resolve an existing incident. The small
|
|
46
46
|
retained device projection preserves the episode across restarts and Space
|
|
47
|
-
execution-mode migration. Its revision is owned by Attention, independent of
|
|
48
|
-
|
|
49
|
-
Twin
|
|
50
|
-
|
|
47
|
+
execution-mode migration. Its CAS revision is owned by Attention, independent of rebuildable Twin cache
|
|
48
|
+
counters. Source ingestion persists a bounded set of ordered facts before
|
|
49
|
+
replacing the Twin cache: latest report, latest detection and latest explicit
|
|
50
|
+
clear. Their sequence comes from the existing durable `nowAttention` increment
|
|
51
|
+
namespace. A delayed detection survives a newer unknown, but cannot supersede a
|
|
52
|
+
newer explicit clear. A delayed clear cannot remove a newer detection. Unknown
|
|
53
|
+
changes verification only. The incident ID remains stable until another clear
|
|
54
|
+
boundary is observed. Delayed notification delivery does not replay source facts.
|
|
55
|
+
|
|
56
|
+
List reconciliation only bootstraps an absent source record from canonical Twin
|
|
57
|
+
state, including retained-history recovery; a live source record wins a racing
|
|
58
|
+
bootstrap. Afterwards the durable source facts own lifecycle state. Device
|
|
59
|
+
metadata and retained episodes cover cold/missing caches. The portable Attention
|
|
60
|
+
payload contains `entityId`, `capabilityId`, and nullable `latest`, `detected`,
|
|
61
|
+
`cleared` observations (`sequence`, `timestamp`, `value`). Imports preserve the
|
|
62
|
+
source sequence floor as well as the separate CAS revision. This keeps source
|
|
63
|
+
ordering valid after a restart, a cache rebuild and execution-mode migration.
|
|
51
64
|
|
|
52
65
|
All timestamps are epoch milliseconds. `sourceUpdatedAt` is the underlying
|
|
53
66
|
state-change timestamp, not a heartbeat or proof of current connectivity.
|
|
@@ -56,17 +56,43 @@ observation and graph reads preserve their bounded business fields in Essential.
|
|
|
56
56
|
|
|
57
57
|
## UI-only reads
|
|
58
58
|
|
|
59
|
-
- `/app/device/view/get`, `{deviceId}`: `{deviceId, primaryViewId, views, widgets}`.
|
|
59
|
+
- `/app/device/view/get`, `{deviceId}`: `{deviceId, primaryViewId, views, widgets, twin}`.
|
|
60
60
|
Reads the unique hidden Device render definition, preserving exact Widget/Twin
|
|
61
61
|
bindings. Disabled devices can have empty definitions. Multiple anchors fail rather
|
|
62
|
-
than choosing an arbitrary visible Card. Widgets contain configuration, never values.
|
|
62
|
+
than choosing an arbitrary visible Card. Widgets contain configuration, never values. The optional `twin` metadata is
|
|
63
|
+
returned even when a known device has no render anchor.
|
|
63
64
|
- `/app/device/history/series/get`, `{deviceId, capabilityIds?: []}`:
|
|
64
65
|
`{deviceId, series}` with per-capability decoded `states`, `events`, `logs`. Empty
|
|
65
66
|
capability selection reads all series. This preserves chart structure; the shared
|
|
66
67
|
Device history Facade remains a bounded, merged business timeline.
|
|
67
68
|
- `/app/dashboard/canvas/content/get`, `{canvasId}`: Canvas metadata plus Cards' static
|
|
68
69
|
titles, view bindings, layouts, settings, metadata and selected Widget definitions.
|
|
69
|
-
|
|
70
|
+
Each entity Card includes the same `twin` metadata, loaded once per entity in the
|
|
71
|
+
batch. No observations, history, related automations or suggestions are loaded.
|
|
72
|
+
|
|
73
|
+
`twin` contains `entityId`, `entityType`, `integration`, `capabilities`,
|
|
74
|
+
`blueprintIds`, `attributes`, `available`, and optional `problem`. Attributes use canonical data capability IDs as
|
|
75
|
+
keys and preserve typed JSON values, including units, false, zero and empty arrays.
|
|
76
|
+
Only immutable entity-only data is cached here; mutable or parameterized data is
|
|
77
|
+
invoked explicitly when needed. These reads never fetch providers. A missing key
|
|
78
|
+
means no cached value is available, not a null/empty result. Group `available` is
|
|
79
|
+
true only for a ready definition whose runtime revision matches its Twin projection.
|
|
80
|
+
Applying/failed/mismatched projections return empty `attributes` and a `problem`,
|
|
81
|
+
while retaining identity and capability/Blueprint IDs for rendering. Clients disable
|
|
82
|
+
actions while unavailable, including retained Widget controls. This availability is
|
|
83
|
+
projection usability, not device reachability or on/off state. Retained observations
|
|
84
|
+
and history are not deleted by projection failure.
|
|
85
|
+
|
|
86
|
+
Ordinary source synchronization preserves immutable caches for unchanged entity
|
|
87
|
+
identity/connection/capabilities. Schema publication does not expire them. Explicit
|
|
88
|
+
device refresh rereads attributes and reconciles related Groups. These are write-side
|
|
89
|
+
operations; definition reads remain cache-only.
|
|
90
|
+
|
|
91
|
+
`/twin/entityUpdate` pushes `{scopeId, entityIds}` after cached metadata changes.
|
|
92
|
+
Open views refresh their definition batches; other Spaces and unrelated entities
|
|
93
|
+
are ignored. A new definition owns its capability membership: old observations
|
|
94
|
+
for removed capabilities must not be merged back into the current view. History
|
|
95
|
+
remains available through its separate read.
|
|
70
96
|
|
|
71
97
|
The frontend batches selected observations after reading definitions. Automation Cards
|
|
72
98
|
also read selected Automation definitions for their name and type. Canvas IDs are
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "unknown alone does not create water incident",
|
|
4
|
+
"capabilityId": "basic.device.state.water",
|
|
5
|
+
"observations": [
|
|
6
|
+
{
|
|
7
|
+
"sequence": 1,
|
|
8
|
+
"timestamp": 1000,
|
|
9
|
+
"value": "unknown"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"active": false,
|
|
13
|
+
"verificationRequired": false,
|
|
14
|
+
"episodeClearSequence": 0,
|
|
15
|
+
"sourceUpdatedAt": 1000,
|
|
16
|
+
"persisted": {
|
|
17
|
+
"entityId": "sensor",
|
|
18
|
+
"capabilityId": "basic.device.state.water",
|
|
19
|
+
"latest": {
|
|
20
|
+
"sequence": 1,
|
|
21
|
+
"timestamp": 1000,
|
|
22
|
+
"value": "unknown"
|
|
23
|
+
},
|
|
24
|
+
"detected": null,
|
|
25
|
+
"cleared": null
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "delayed first wet survives unknown",
|
|
30
|
+
"capabilityId": "basic.device.state.water",
|
|
31
|
+
"observations": [
|
|
32
|
+
{
|
|
33
|
+
"sequence": 2,
|
|
34
|
+
"timestamp": 2000,
|
|
35
|
+
"value": "unknown"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"sequence": 1,
|
|
39
|
+
"timestamp": 1000,
|
|
40
|
+
"value": "wet"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"active": true,
|
|
44
|
+
"verificationRequired": true,
|
|
45
|
+
"episodeClearSequence": 0,
|
|
46
|
+
"sourceUpdatedAt": 2000,
|
|
47
|
+
"persisted": {
|
|
48
|
+
"entityId": "sensor",
|
|
49
|
+
"capabilityId": "basic.device.state.water",
|
|
50
|
+
"latest": {
|
|
51
|
+
"sequence": 2,
|
|
52
|
+
"timestamp": 2000,
|
|
53
|
+
"value": "unknown"
|
|
54
|
+
},
|
|
55
|
+
"detected": {
|
|
56
|
+
"sequence": 1,
|
|
57
|
+
"timestamp": 1000,
|
|
58
|
+
"value": "wet"
|
|
59
|
+
},
|
|
60
|
+
"cleared": null
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "delayed wet cannot revive cleared incident",
|
|
65
|
+
"capabilityId": "basic.device.state.water",
|
|
66
|
+
"observations": [
|
|
67
|
+
{
|
|
68
|
+
"sequence": 3,
|
|
69
|
+
"timestamp": 3000,
|
|
70
|
+
"value": "unknown"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"sequence": 2,
|
|
74
|
+
"timestamp": 2000,
|
|
75
|
+
"value": "dry"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"sequence": 1,
|
|
79
|
+
"timestamp": 1000,
|
|
80
|
+
"value": "wet"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"active": false,
|
|
84
|
+
"verificationRequired": false,
|
|
85
|
+
"episodeClearSequence": 0,
|
|
86
|
+
"sourceUpdatedAt": 1000,
|
|
87
|
+
"persisted": {
|
|
88
|
+
"entityId": "sensor",
|
|
89
|
+
"capabilityId": "basic.device.state.water",
|
|
90
|
+
"latest": {
|
|
91
|
+
"sequence": 3,
|
|
92
|
+
"timestamp": 3000,
|
|
93
|
+
"value": "unknown"
|
|
94
|
+
},
|
|
95
|
+
"detected": {
|
|
96
|
+
"sequence": 1,
|
|
97
|
+
"timestamp": 1000,
|
|
98
|
+
"value": "wet"
|
|
99
|
+
},
|
|
100
|
+
"cleared": {
|
|
101
|
+
"sequence": 2,
|
|
102
|
+
"timestamp": 2000,
|
|
103
|
+
"value": "dry"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "delayed clear cannot remove newer wet",
|
|
109
|
+
"capabilityId": "basic.device.state.water",
|
|
110
|
+
"observations": [
|
|
111
|
+
{
|
|
112
|
+
"sequence": 3,
|
|
113
|
+
"timestamp": 3000,
|
|
114
|
+
"value": "wet"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"sequence": 1,
|
|
118
|
+
"timestamp": 1000,
|
|
119
|
+
"value": "wet"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"sequence": 2,
|
|
123
|
+
"timestamp": 2000,
|
|
124
|
+
"value": "dry"
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"active": true,
|
|
128
|
+
"verificationRequired": false,
|
|
129
|
+
"episodeClearSequence": 2,
|
|
130
|
+
"sourceUpdatedAt": 3000,
|
|
131
|
+
"persisted": {
|
|
132
|
+
"entityId": "sensor",
|
|
133
|
+
"capabilityId": "basic.device.state.water",
|
|
134
|
+
"latest": {
|
|
135
|
+
"sequence": 3,
|
|
136
|
+
"timestamp": 3000,
|
|
137
|
+
"value": "wet"
|
|
138
|
+
},
|
|
139
|
+
"detected": {
|
|
140
|
+
"sequence": 3,
|
|
141
|
+
"timestamp": 3000,
|
|
142
|
+
"value": "wet"
|
|
143
|
+
},
|
|
144
|
+
"cleared": {
|
|
145
|
+
"sequence": 2,
|
|
146
|
+
"timestamp": 2000,
|
|
147
|
+
"value": "dry"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "new episode remains under verification",
|
|
153
|
+
"capabilityId": "basic.device.state.water",
|
|
154
|
+
"observations": [
|
|
155
|
+
{
|
|
156
|
+
"sequence": 3,
|
|
157
|
+
"timestamp": 3000,
|
|
158
|
+
"value": "unknown"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"sequence": 2,
|
|
162
|
+
"timestamp": 2000,
|
|
163
|
+
"value": "wet"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"sequence": 1,
|
|
167
|
+
"timestamp": 1000,
|
|
168
|
+
"value": "dry"
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
"active": true,
|
|
172
|
+
"verificationRequired": true,
|
|
173
|
+
"episodeClearSequence": 1,
|
|
174
|
+
"sourceUpdatedAt": 3000,
|
|
175
|
+
"persisted": {
|
|
176
|
+
"entityId": "sensor",
|
|
177
|
+
"capabilityId": "basic.device.state.water",
|
|
178
|
+
"latest": {
|
|
179
|
+
"sequence": 3,
|
|
180
|
+
"timestamp": 3000,
|
|
181
|
+
"value": "unknown"
|
|
182
|
+
},
|
|
183
|
+
"detected": {
|
|
184
|
+
"sequence": 2,
|
|
185
|
+
"timestamp": 2000,
|
|
186
|
+
"value": "wet"
|
|
187
|
+
},
|
|
188
|
+
"cleared": {
|
|
189
|
+
"sequence": 1,
|
|
190
|
+
"timestamp": 1000,
|
|
191
|
+
"value": "dry"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"name": "duplicate wet is not proof of freshness",
|
|
197
|
+
"capabilityId": "basic.device.state.water",
|
|
198
|
+
"observations": [
|
|
199
|
+
{
|
|
200
|
+
"sequence": 1,
|
|
201
|
+
"timestamp": 1000,
|
|
202
|
+
"value": "wet"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"sequence": 2,
|
|
206
|
+
"timestamp": 2000,
|
|
207
|
+
"value": "wet"
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"active": true,
|
|
211
|
+
"verificationRequired": false,
|
|
212
|
+
"episodeClearSequence": 0,
|
|
213
|
+
"sourceUpdatedAt": 1000,
|
|
214
|
+
"persisted": {
|
|
215
|
+
"entityId": "sensor",
|
|
216
|
+
"capabilityId": "basic.device.state.water",
|
|
217
|
+
"latest": {
|
|
218
|
+
"sequence": 2,
|
|
219
|
+
"timestamp": 1000,
|
|
220
|
+
"value": "wet"
|
|
221
|
+
},
|
|
222
|
+
"detected": {
|
|
223
|
+
"sequence": 2,
|
|
224
|
+
"timestamp": 1000,
|
|
225
|
+
"value": "wet"
|
|
226
|
+
},
|
|
227
|
+
"cleared": null
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"name": "testing alone does not create smoke incident",
|
|
232
|
+
"capabilityId": "basic.device.state.smoke",
|
|
233
|
+
"observations": [
|
|
234
|
+
{
|
|
235
|
+
"sequence": 1,
|
|
236
|
+
"timestamp": 1000,
|
|
237
|
+
"value": "testing"
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"active": false,
|
|
241
|
+
"verificationRequired": false,
|
|
242
|
+
"episodeClearSequence": 0,
|
|
243
|
+
"sourceUpdatedAt": 1000,
|
|
244
|
+
"persisted": {
|
|
245
|
+
"entityId": "sensor",
|
|
246
|
+
"capabilityId": "basic.device.state.smoke",
|
|
247
|
+
"latest": {
|
|
248
|
+
"sequence": 1,
|
|
249
|
+
"timestamp": 1000,
|
|
250
|
+
"value": "testing"
|
|
251
|
+
},
|
|
252
|
+
"detected": null,
|
|
253
|
+
"cleared": null
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"name": "smoke testing preserves detected incident",
|
|
258
|
+
"capabilityId": "basic.device.state.smoke",
|
|
259
|
+
"observations": [
|
|
260
|
+
{
|
|
261
|
+
"sequence": 1,
|
|
262
|
+
"timestamp": 1000,
|
|
263
|
+
"value": "detected"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"sequence": 2,
|
|
267
|
+
"timestamp": 2000,
|
|
268
|
+
"value": "testing"
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
"active": true,
|
|
272
|
+
"verificationRequired": true,
|
|
273
|
+
"episodeClearSequence": 0,
|
|
274
|
+
"sourceUpdatedAt": 2000,
|
|
275
|
+
"persisted": {
|
|
276
|
+
"entityId": "sensor",
|
|
277
|
+
"capabilityId": "basic.device.state.smoke",
|
|
278
|
+
"latest": {
|
|
279
|
+
"sequence": 2,
|
|
280
|
+
"timestamp": 2000,
|
|
281
|
+
"value": "testing"
|
|
282
|
+
},
|
|
283
|
+
"detected": {
|
|
284
|
+
"sequence": 1,
|
|
285
|
+
"timestamp": 1000,
|
|
286
|
+
"value": "detected"
|
|
287
|
+
},
|
|
288
|
+
"cleared": null
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"name": "clear ends smoke incident",
|
|
293
|
+
"capabilityId": "basic.device.state.smoke",
|
|
294
|
+
"observations": [
|
|
295
|
+
{
|
|
296
|
+
"sequence": 1,
|
|
297
|
+
"timestamp": 1000,
|
|
298
|
+
"value": "detected"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"sequence": 2,
|
|
302
|
+
"timestamp": 2000,
|
|
303
|
+
"value": "clear"
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
"sequence": 3,
|
|
307
|
+
"timestamp": 3000,
|
|
308
|
+
"value": "unknown"
|
|
309
|
+
}
|
|
310
|
+
],
|
|
311
|
+
"active": false,
|
|
312
|
+
"verificationRequired": false,
|
|
313
|
+
"episodeClearSequence": 0,
|
|
314
|
+
"sourceUpdatedAt": 1000,
|
|
315
|
+
"persisted": {
|
|
316
|
+
"entityId": "sensor",
|
|
317
|
+
"capabilityId": "basic.device.state.smoke",
|
|
318
|
+
"latest": {
|
|
319
|
+
"sequence": 3,
|
|
320
|
+
"timestamp": 3000,
|
|
321
|
+
"value": "unknown"
|
|
322
|
+
},
|
|
323
|
+
"detected": {
|
|
324
|
+
"sequence": 1,
|
|
325
|
+
"timestamp": 1000,
|
|
326
|
+
"value": "detected"
|
|
327
|
+
},
|
|
328
|
+
"cleared": {
|
|
329
|
+
"sequence": 2,
|
|
330
|
+
"timestamp": 2000,
|
|
331
|
+
"value": "clear"
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
]
|
|
@@ -458,7 +458,7 @@
|
|
|
458
458
|
"manufacturerId": "demo",
|
|
459
459
|
"manufacturerName": "Example maker",
|
|
460
460
|
"availabilityStatus": "unavailable",
|
|
461
|
-
"
|
|
461
|
+
"blueprintIds": [
|
|
462
462
|
"light"
|
|
463
463
|
]
|
|
464
464
|
},
|
|
@@ -492,7 +492,7 @@
|
|
|
492
492
|
"group": false,
|
|
493
493
|
"integration": "demo.device",
|
|
494
494
|
"availabilityStatus": "unavailable",
|
|
495
|
-
"
|
|
495
|
+
"blueprintIds": [
|
|
496
496
|
"light"
|
|
497
497
|
]
|
|
498
498
|
},
|
|
@@ -546,7 +546,7 @@
|
|
|
546
546
|
"manufacturerId": "demo",
|
|
547
547
|
"manufacturerName": "Example maker",
|
|
548
548
|
"availabilityStatus": "unavailable",
|
|
549
|
-
"
|
|
549
|
+
"blueprintIds": [
|
|
550
550
|
"light"
|
|
551
551
|
],
|
|
552
552
|
"entityIds": []
|
|
@@ -600,7 +600,7 @@
|
|
|
600
600
|
"manufacturerId": "demo",
|
|
601
601
|
"manufacturerName": "Example maker",
|
|
602
602
|
"availabilityStatus": "unavailable",
|
|
603
|
-
"
|
|
603
|
+
"blueprintIds": [
|
|
604
604
|
"light"
|
|
605
605
|
],
|
|
606
606
|
"entityIds": []
|
package/manifest/routing.v1.json
CHANGED
package/manifest/targets.v1.json
CHANGED
package/package.json
CHANGED