@mhome/app-facade-protocol 1.40.1 → 1.42.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.
@@ -1,15 +1,18 @@
1
1
  # Pod commissioning v1
2
2
 
3
- A pod is a MeowLink end device that acts as an App client of one user. This
3
+ A pod is a MeowLink end device that acts as a User Client or a Space Client. This
4
4
  contract is implementation-neutral: ESP-IDF on BLE-capable ESP32 chips is the
5
5
  first implementation, not a requirement. Commissioners are the native Clients:
6
6
  desktop/CLI Clientd, Android and iOS. The shared UI and CLI never talk BLE; they
7
7
  call the `/local/pod/*` targets of their native Client.
8
8
 
9
- Commissioning order is fixed: secure BLE session, Wi-Fi joined, user
10
- authorization, credential issue, delivery, activation. A credential is issued only
11
- after the pod reports a joined network. Failed setup releases the connection;
12
- pending cloud credentials expire, and a fresh successful claim cleans up old credentials.
9
+ Network provisioning and activation are separate transactions. BLE provisions
10
+ Wi-Fi; Ethernet and already-connected Wi-Fi skip it. Every initialization then
11
+ uses a direct native Client ↔ Pod LAN security-2 session. No Core relay is used.
12
+ The commissioner explicitly chooses `clientMode: "user" | "space"` during
13
+ `/local/pod/commission/authorize`. User mode uses the personal Pod credential.
14
+ Space mode uses a local Core credential signed by Lion; it carries no user
15
+ identity and has no cloud access/refresh token. Changing mode requires reset.
13
16
 
14
17
  ## 1. Device identity
15
18
 
@@ -47,7 +50,7 @@ credential invalidation, or inside a re-provision window. A commissioned pod doe
47
50
 
48
51
  ## 3. Secure session
49
52
 
50
- Transport is ESP protocomm over BLE GATT. Endpoints are GATT characteristics
53
+ Wi-Fi provisioning uses ESP protocomm over BLE GATT. Endpoints are GATT characteristics
51
54
  named by their Characteristic User Description descriptor (`0x2901`). A
52
55
  commissioner that cannot read descriptors falls back to fixed UUIDs: the service
53
56
  UUID with bytes 2–3 replaced by `prov-ctrl` `0xFF4F`, `prov-scan` `0xFF50`,
@@ -58,7 +61,7 @@ custom endpoints in table order from `0xFF54` (`pod-info` `0xFF54`,
58
61
  - Security: protocomm security 2 (SRP6a 3072-bit with SHA-512, then AES-256-GCM).
59
62
  Username `meow`. Password: a 6-digit decimal code.
60
63
  - Pods with a display generate a uniformly random code, precompute salt and
61
- verifier at boot, and show the code only while a central is connected. A new
64
+ verifier at boot, and show it while connected over BLE or available for LAN initialization. A new
62
65
  code is generated after a successful commissioning, after 5 failed handshakes,
63
66
  or 10 minutes after it was first shown. Pods without a display are out of scope
64
67
  for v1.
@@ -70,8 +73,8 @@ custom endpoints in table order from `0xFF54` (`pod-info` `0xFF54`,
70
73
  `M` uses it as sent; `u` pads both to 384 bytes.
71
74
  - The record nonce is 8 random bytes followed by a 32-bit big-endian counter
72
75
  starting at 1, advanced after every encrypt and decrypt on both sides
73
- (security 2 patch version 1; patch 0 keeps it fixed). Commissioners accept
74
- patch versions 0 and 1 only. A lost or failed exchange leaves the two counters
76
+ (security 2 patch version 1; patch 0 keeps it fixed). Commissioners require
77
+ patch version 1; fixed-nonce patch 0 is rejected. A lost or failed exchange leaves the two counters
75
78
  unknown, so the commissioner abandons the secure session after any transport or
76
79
  decryption failure. A new `SessionCmd0` restarts the handshake on the same link.
77
80
  - While a device refuses handshakes after repeated wrong codes it clears the
@@ -81,8 +84,8 @@ custom endpoints in table order from `0xFF54` (`pod-info` `0xFF54`,
81
84
  such as Insufficient Authorization), so after every rejected handshake the
82
85
  commissioner reads `proto-ver` again and fails the session with `code_locked`
83
86
  when either field is present. A pod session also fails with `code_locked` after
84
- the fifth rejected code. ESP pods restart with a new code after 5 failed
85
- handshakes instead of reporting a lockout.
87
+ the fifth rejected code. Pods generate a fresh code after five rejected handshakes. Once network
88
+ configuration is saved, code rotation preserves that network.
86
89
  - One BLE connection at a time; a second central receives a disconnect.
87
90
  - Session deadline: 10 minutes from connect, or 2 minutes waiting for a
88
91
  handshake. On expiry the pod discards all unpersisted state.
@@ -95,15 +98,17 @@ custom endpoints in table order from `0xFF54` (`pod-info` `0xFF54`,
95
98
  | `prov-config` | yes | standard Wi-Fi set/apply/status |
96
99
  | `prov-ctrl` | yes | standard reset after a failed join, before a retry |
97
100
  | `pod-info` | yes | device information |
98
- | `pod-credential` | yes | credential delivery and activation status |
101
+ | `pod-credential` | yes | LAN credential delivery/status/finish; BLE accepts only reprovision status/finish |
99
102
 
100
- Custom endpoint bodies are UTF-8 JSON, at most 480 bytes in each direction.
103
+ Custom endpoint bodies are UTF-8 JSON, at most 480 bytes over BLE. LAN requests
104
+ and responses are bounded to 64 KiB including encryption overhead.
101
105
 
102
106
  `pod-info` request `{}`, response:
103
107
 
104
108
  ```json
105
109
  {
106
110
  "protocol": 1,
111
+ "env": "prod",
107
112
  "deviceId": "a0b1c2d3e4f5",
108
113
  "model": "S1",
109
114
  "platform": "esp32c5",
@@ -114,6 +119,9 @@ Custom endpoint bodies are UTF-8 JSON, at most 480 bytes in each direction.
114
119
  }
115
120
  ```
116
121
 
122
+ `env` must match the native Client runtime (`prod`, `dev`, `e2e`).
123
+ `podId` is included when activation is in flight or durably completed, so a
124
+ new session can observe completion without issuing another credential.
117
125
  `platform` is diagnostic only. `wifiConfigured` is true when the pod kept a
118
126
  working network across logout; the commissioner then skips the Wi-Fi steps after
119
127
  confirming connectivity with `prov-config` status. Inside a Wi-Fi change window
@@ -122,7 +130,7 @@ the response also carries `"mode":"reprovision"` and `wifiConfigured` is false
122
130
 
123
131
  `pod-credential` requests:
124
132
 
125
- - `{"op":"deliver","podId","refreshToken","scopeId","cloudApi","cloudWs"}` →
133
+ - `{"op":"deliver","clientMode":"user","podId","refreshToken","scopeId","cloudApi","cloudWs"}` →
126
134
  `{"ok":true,"state":"activating"}`. Accepted only in `wifi_connected` outside a
127
135
  Wi-Fi change window; otherwise `{"ok":false,"state":<current pod state>,"reason"}`
128
136
  with `reason` `invalid_payload` (a missing or malformed field, including a
@@ -143,24 +151,55 @@ the response also carries `"mode":"reprovision"` and `wifiConfigured` is false
143
151
  best-effort: the commissioner records `completed` before sending it.
144
152
 
145
153
  Activation error codes: `time_sync_failed`, `cloud_unreachable`,
146
- `refresh_rejected`, `auth_rejected`, `storage_failed` (the final commit, or in a
154
+ `local_hub_unavailable`, `refresh_rejected`, `auth_rejected`, `storage_failed` (the final commit, or in a
147
155
  Wi-Fi change window saving the new network, failed).
148
156
 
149
- The pod persists Wi-Fi, credential, endpoints and Space in one commit after a
150
- successful activation. Nothing from an abandoned attempt survives it; an
151
- implementation whose Wi-Fi driver caches the network on its own (ESP-IDF
152
- provisioning does) erases that cache when the attempt ends.
157
+ The Pod commits Wi-Fi immediately after a successful join, before any credential
158
+ issuance. Activation commits only identity/binding data. Failed activation,
159
+ disconnection, code expiry and reboot preserve the successfully saved network.
160
+ A failed join rolls back only the incomplete network change.
161
+
162
+ ### LAN discovery and channel
163
+
164
+ - Service: `_meow-pod-init._tcp.local.` while network-ready and uninitialized.
165
+ TXT: full `deviceId`, `v=1`, `env`, optional `model`; SRV/A/AAAA locate the
166
+ endpoint. A failed or expired pairing attempt does not remove this service.
167
+ - The same service works on Ethernet and Wi-Fi. BLE supplies the full device ID
168
+ and identity over its secure session; the short display ID is never the match
169
+ key. Discovery data is untrusted and is checked against encrypted `pod-info`.
170
+ - HTTP POST `/{endpoint}` with `Content-Type: application/octet-stream` and a
171
+ fresh UUID `X-Meow-Session`. Allowed endpoints: `proto-ver`, `prov-session`,
172
+ `pod-info`, `pod-credential`. Wi-Fi endpoints are unavailable over LAN.
173
+ Clients connect to the resolved socket directly, with proxies/redirects off.
174
+ - Run a fresh Security 2 patch-1 handshake on LAN. After BLE, reuse only the
175
+ temporarily held code, never the BLE session key or nonce. Ethernet setup
176
+ requests the code in the App UI. Business messages are AES-GCM protected.
177
+ - Each IP session has separate crypto state. Devices cap simultaneous sessions
178
+ at four; pending handshakes expire after two minutes, sessions after ten.
179
+ An encrypted `pod-credential {"op":"close"}` releases that temporary session.
180
+ - Stop init advertisement on durable completion. The existing session has a
181
+ short finish window for confirmation; normal business disconnection never
182
+ returns a completed device to initialization.
153
183
 
154
184
  ## 4. Pod state machine
155
185
 
156
186
  ```text
157
- unprovisioned ──connect──▶ session_open ──handshake──▶ secured
158
- secured ──prov-config apply──▶ wifi_joining ──▶ wifi_connected | wifi_failed
159
- wifi_failed ──retry──▶ wifi_joining
160
- wifi_connected ──deliver──▶ activating ──time sync, refresh, commit──▶ commissioned | failed
161
- any pre-commit state ──timeout/disconnect──▶ unprovisioned
187
+ no network -> BLE secure -> join -> save Wi-Fi -> network ready
188
+ Ethernet / saved Wi-Fi ------------------------> network ready
189
+ network ready -> advertise init -> LAN secure -> authorize -> activate -> save identity
190
+ join failure -> end attempt; start Wi-Fi setup again
191
+ activation failure -> end attempt; keep network and init advertisement
192
+ pairing expiry -> discard temporary sessions/code; keep network; accept a new code
162
193
  ```
163
194
 
195
+ The App reports `discovering_lan` and `connecting_lan` during BLE handoff.
196
+ Discovery snapshots distinguish `scanning` (BLE), `lanBrowsing`, optional
197
+ `lanError`, and candidate `transport` (`ble` or `lan`). LAN candidates carry the
198
+ full `deviceId`; Bluetooth denial never blocks them. The App retries initialization
199
+ by selecting the LAN candidate, possibly re-entering the current code. It first
200
+ reads actual device state: `commissioned` completes immediately, `activating`
201
+ waits for the existing attempt, and a failed attempt may issue afresh.
202
+
164
203
  Runtime states after commit:
165
204
 
166
205
  - `commissioned`: normal operation.
@@ -316,7 +355,7 @@ and unexpired pending pods, each
316
355
 
317
356
  All targets are handled by the native Client and never forwarded. Payloads are
318
357
  domain input JSON. Clients without a usable Bluetooth adapter report
319
- `adapter.state` and reject commissioning with `bluetooth_unavailable`.
358
+ `adapter.state` and reject BLE commissioning with `bluetooth_unavailable`; LAN candidates remain usable.
320
359
 
321
360
  A rejected request uses the facade error envelope
322
361
  `{"error","message","details":{"reason"}}`: `error` is `BAD_REQUEST` for an
@@ -394,9 +433,10 @@ example `storage_failed`) in `detail`. UIs show such a session as changing the
394
433
  pod's Wi-Fi rather than adding a pod.
395
434
 
396
435
  Session states, in order: `connecting`, `awaiting_code`, `securing`,
397
- `reading_info`, `awaiting_wifi`, `joining_wifi`, `awaiting_authorization`,
436
+ `reading_info`, `awaiting_wifi`, `joining_wifi`, `discovering_lan`,
437
+ `connecting_lan`, `awaiting_authorization`,
398
438
  `issuing`, `delivering`, `activating`, `completed`; terminal `failed`,
399
- `timed_out`. Wi-Fi states are skipped when the pod reports a
439
+ `timed_out`. LAN candidates skip all Wi-Fi states. Wi-Fi states are also skipped when the pod reports a
400
440
  configured network and `prov-config` status reports it connected; while that
401
441
  status is `connecting` the commissioner keeps polling, and only after a failure
402
442
  does it reset Wi-Fi (`prov-ctrl`) and offer new credentials. The commissioner
@@ -407,10 +447,11 @@ scan runs waits for it. `revision` increases on every observable change.
407
447
  `delivering` on it is `activateBefore` plus 30 seconds.
408
448
 
409
449
  Only the device's durable `commissioned` response confirms successful setup.
410
- Lost BLE delivery confirmation or activation status fails the attempt, even
450
+ Lost LAN delivery confirmation or activation status fails the attempt, even
411
451
  when Lion has already activated its credential. Expiry fails or times out the
412
452
  attempt. The commissioner does not recover interrupted setup through Lion's
413
- credential status. The user resets the device and starts a fresh attempt.
453
+ credential status. The user selects the network-ready device again, establishes
454
+ a fresh LAN session and reads its actual activation status.
414
455
 
415
456
  A refused `deliver` fails with `delivery_failed` carrying the pod's `reason`
416
457
  (`invalid_payload` or `wrong_state`) in `detail`. When the pod
@@ -423,14 +464,73 @@ app is in the background (Android foreground service of type
423
464
 
424
465
  Session error codes: `bluetooth_unavailable`, `device_busy`, `disconnected`,
425
466
  `code_rejected` (retry allowed), `code_locked`, `wifi_auth_failed`,
426
- `wifi_not_found`, `wifi_failed`, `issue_failed`, `delivery_failed`,
467
+ `wifi_not_found`, `wifi_failed`, `lan_unavailable`, `device_mismatch`, `issue_failed`, `delivery_failed`,
427
468
  `activation_failed` (with the pod's activation code, or `not_activated`, in
428
469
  `detail`), `session_timeout`. `code_locked` is terminal: the device shows a new
429
470
  code once it accepts handshakes again. `failed` and `timed_out` always carry `error`. A rejected
430
- code returns to `awaiting_code` with `code_rejected`; a failed join returns to
431
- `awaiting_wifi` with the `wifi_*` code. No other state carries `error`. Terminal states release the link and radio
432
- without revoking credentials. Reset the Pod and start over after an uncertain
433
- activation outcome; unused pending cloud credentials expire automatically.
471
+ code returns to `awaiting_code` with `code_rejected`; a failed Pod join ends in
472
+ `failed` with the `wifi_*` code. No other state carries `error`. Terminal states release the link and radio
473
+ without revoking credentials. Retry over LAN after an uncertain activation outcome; unused pending cloud credentials expire automatically.
434
474
 
435
475
  The schemas are `schema/pod-discovery.v1.schema.json` and
436
476
  `schema/pod-commission.v1.schema.json`.
477
+
478
+
479
+ ## 8. Space Client local credential
480
+
481
+ `POST /api/v1/pod/space/credential/issue` uses the assisting App's user
482
+ `Authorization` and `ScopeId`, with the same device identity request as personal
483
+ issuance. Any current member may assist. The Space must be local and have one
484
+ published Hub with business and Host TLS identities. Cloud Spaces must complete
485
+ Hub setup first. The issuer creates a fresh `appClientId` (`a:<random id>`) and
486
+ signs a local JWT using the existing Space signing key and local-client audience.
487
+
488
+ Claims are `token_type: local_space_client_access`, `sub: <scopeId>`,
489
+ `tenant_id`, `scope_id`, `hub_id`, `app_client_id`, `jti`, `iat`, `exp`, `is_test`.
490
+ The expiry follows existing long-lived User local credentials. No `userId`,
491
+ initializer identity, `podId` or `issuedScopeId` grants authority. User local
492
+ credentials retain `local_client_access` and must not contain `app_client_id`.
493
+ Personal Pod issuance no longer stores `issuedScopeId`.
494
+
495
+ Lion sends `{jwtToken, clientSource:"pod"}` to the authenticated Hub Bridge
496
+ `/hub/client/space/register`. Core verifies signature, audience, issuer, current
497
+ Hub, environment, Space and canonical installation ID, and stores a pending
498
+ credential. First authentication must occur within ten minutes of JWT issuance.
499
+ The response is `{credentialId, status:"pending", activateBefore}`. Lion returns
500
+ `{credentialId, appClientId, jwtToken, activateBefore, hubIdentity}` only after
501
+ registration succeeds. The Hub identity includes its business P-256 key and
502
+ Host TLS public key, bound to `tenantId`, `scopeId`, `hubId` and `hostId`.
503
+
504
+ The secured LAN `pod-credential` delivery is:
505
+
506
+ ```json
507
+ {"op":"deliver","clientMode":"space","credentialId":"…","appClientId":"a:…","jwtToken":"…","tenantId":"…","scopeId":"…","hubIdentity":{}}
508
+ ```
509
+
510
+ The actual `hubIdentity` is the complete issuer response. User credential fields
511
+ (`refreshToken`, cloud endpoints, `podId`) are rejected in this variant. Space
512
+ credential fields are rejected in the User variant. LAN credential plaintext is
513
+ bounded to 8192 bytes; BLE credential delivery remains disabled.
514
+
515
+ The Pod discovers the matching runtime environment and Space's Hub, pins TLS,
516
+ verifies the signed Hub proof, and authenticates with the local JWT and signed
517
+ `appClientId`. Core atomically activates the pending credential after all binding
518
+ checks. The Pod requires a Space principal for the same Space, installation and
519
+ credential in the authenticated response, then durably saves the profile. Only
520
+ then does LAN setup report completion. The LAN status field named `podId` is an
521
+ enrollment correlation ID: for Space mode it carries `credentialId`, never the
522
+ installation ID or acting principal.
523
+
524
+ After activation, reconnects do not require Lion or renew a cloud session. The
525
+ same Core verifies the JWT and local registration offline, with its cached Scope
526
+ verification key. Failed activation preserves working Wi-Fi and leaves LAN
527
+ commissioning available. Retry may require the pairing code again. Unused pending
528
+ credentials cannot activate after the deadline. Factory reset deletes the local
529
+ credential and identity key; no server-side revoke workflow is introduced.
530
+
531
+ Core carries `AuthenticatedClient` through WS, dispatch, plugin admission,
532
+ prepared actions, dialogs and presence. A Space Client can READ/WRITE its Space;
533
+ MANAGE, personal resources and Host user administration are rejected. It joins
534
+ the canonical Space-client conversation group, never an initializer's threads.
535
+ The billing owner fallback is not an acting identity. Host management tokens are
536
+ independent and are never issued by this flow.
@@ -5,6 +5,7 @@
5
5
  "candidate": {
6
6
  "candidateId": "ble:5E6F7A8B",
7
7
  "kind": "host",
8
+ "transport": "ble",
8
9
  "name": "Kitchen Host",
9
10
  "shortId": "5E6F7A8B",
10
11
  "rssi": -61,
@@ -5,6 +5,7 @@
5
5
  "candidate": {
6
6
  "candidateId": "ble:1A2B3C4D",
7
7
  "kind": "pod",
8
+ "transport": "ble",
8
9
  "name": "MeowPod S1",
9
10
  "model": "S1",
10
11
  "shortId": "1A2B3C4D",
@@ -23,7 +24,13 @@
23
24
  "authorization": {
24
25
  "userId": "user-1",
25
26
  "userName": "Ada",
26
- "scopes": [{ "scopeId": "scope-1", "name": "Home" }],
27
+ "scopes": [
28
+ {
29
+ "scopeId": "scope-1",
30
+ "name": "Home",
31
+ "local": true
32
+ }
33
+ ],
27
34
  "defaultScopeId": "scope-1"
28
35
  }
29
36
  }
@@ -5,6 +5,7 @@
5
5
  "candidate": {
6
6
  "candidateId": "ble:1A2B3C4D",
7
7
  "kind": "pod",
8
+ "transport": "ble",
8
9
  "name": "MeowPod S1",
9
10
  "model": "S1",
10
11
  "shortId": "1A2B3C4D",
@@ -5,6 +5,7 @@
5
5
  "candidate": {
6
6
  "candidateId": "ble:1A2B3C4D",
7
7
  "kind": "pod",
8
+ "transport": "ble",
8
9
  "name": "MeowPod S1",
9
10
  "model": "S1",
10
11
  "shortId": "1A2B3C4D",
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "revision": 4,
3
+ "lanBrowsing": false,
3
4
  "scanning": true,
4
5
  "adapter": { "state": "ready" },
5
6
  "candidates": [
6
7
  {
7
8
  "candidateId": "ble:1A2B3C4D",
8
9
  "kind": "pod",
10
+ "transport": "ble",
9
11
  "name": "MeowPod S1",
10
12
  "model": "S1",
11
13
  "shortId": "1A2B3C4D",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.app-facade.v1",
3
- "contractVersion": "1.40.1",
3
+ "contractVersion": "1.42.0",
4
4
  "callSchema": "schema/facade-call.v1.schema.json",
5
5
  "routing": "manifest/routing.v1.json",
6
6
  "routingSchema": "schema/routing.v1.schema.json",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.host-provision.targets.v1",
3
- "contractVersion": "1.40.1",
3
+ "contractVersion": "1.42.0",
4
4
  "contract": "contract/host-provisioning-v1.md",
5
5
  "localTargets": [
6
6
  "/local/host/provision/start",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.hub.targets.v1",
3
- "contractVersion": "1.40.1",
3
+ "contractVersion": "1.42.0",
4
4
  "appTargets": [
5
5
  "/app/hub/get",
6
6
  "/app/hub/remove",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.pod.targets.v1",
3
- "contractVersion": "1.40.1",
3
+ "contractVersion": "1.42.0",
4
4
  "contract": "contract/pod-commissioning-v1.md",
5
5
  "localTargets": [
6
6
  "/local/pod/discovery/start",
@@ -67,11 +67,13 @@
67
67
  "cloud": {
68
68
  "issue": "pod/credential/issue",
69
69
  "status": "pod/credential/status",
70
- "statusSchema": "schema/pod-credential-status.v1.schema.json"
70
+ "statusSchema": "schema/pod-credential-status.v1.schema.json",
71
+ "spaceIssue": "pod/space/credential/issue"
71
72
  },
72
73
  "activationErrorCodes": [
73
74
  "time_sync_failed",
74
75
  "cloud_unreachable",
76
+ "local_hub_unavailable",
75
77
  "refresh_rejected",
76
78
  "auth_rejected",
77
79
  "storage_failed"
@@ -96,5 +98,12 @@
96
98
  "scope_not_offered",
97
99
  "cloud_unreachable_for_device",
98
100
  "busy"
101
+ ],
102
+ "lan": {
103
+ "credentialMaxBytes": 8192
104
+ },
105
+ "clientModes": [
106
+ "user",
107
+ "space"
99
108
  ]
100
109
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.app-facade.routing.v1",
3
- "contractVersion": "1.40.1",
3
+ "contractVersion": "1.42.0",
4
4
  "targetPrefix": "/app/",
5
5
  "default": {
6
6
  "execution": "scopeMode",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.messaging.targets.v1",
3
- "contractVersion": "1.40.1",
3
+ "contractVersion": "1.42.0",
4
4
  "requestTargets": [
5
5
  "/app/messaging/provider/list",
6
6
  "/app/messaging/provider-account/list",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/app-facade-protocol",
3
- "version": "1.40.1",
3
+ "version": "1.42.0",
4
4
  "description": "Build-time protocol artifacts for mhome.app-facade",
5
5
  "license": "MIT",
6
6
  "repository": {
package/protocol.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocol": "mhome.app-facade",
3
- "contractVersion": "1.40.1",
3
+ "contractVersion": "1.42.0",
4
4
  "manifest": "manifest/app-facade.v1.json",
5
5
  "schemas": {
6
6
  "capabilityDescriptor": "schema/capability-descriptor.v1.schema.json",
@@ -204,7 +204,7 @@
204
204
  "type": "array",
205
205
  "items": {
206
206
  "type": "string",
207
- "pattern": "^(person|cluster):.+$"
207
+ "pattern": "^h:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
208
208
  }
209
209
  }
210
210
  },
@@ -16,6 +16,8 @@
16
16
  "reading_info",
17
17
  "awaiting_wifi",
18
18
  "joining_wifi",
19
+ "discovering_lan",
20
+ "connecting_lan",
19
21
  "awaiting_authorization",
20
22
  "issuing",
21
23
  "delivering",
@@ -64,10 +66,11 @@
64
66
  "items": {
65
67
  "type": "object",
66
68
  "additionalProperties": false,
67
- "required": ["scopeId", "name"],
69
+ "required": ["scopeId", "name", "local"],
68
70
  "properties": {
69
71
  "scopeId": { "type": "string", "minLength": 1 },
70
- "name": { "type": "string" }
72
+ "name": { "type": "string" },
73
+ "local": { "type": "boolean" }
71
74
  }
72
75
  }
73
76
  },
@@ -86,6 +89,8 @@
86
89
  "bluetooth_unavailable",
87
90
  "device_busy",
88
91
  "disconnected",
92
+ "lan_unavailable",
93
+ "device_mismatch",
89
94
  "code_rejected",
90
95
  "code_locked",
91
96
  "wifi_auth_failed",
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://schemas.mhome.ai/app-facade/pod-discovery.v1.schema.json",
4
- "title": "Native-client BLE discovery snapshot (pods and Hosts)",
4
+ "title": "Native-client BLE and LAN discovery snapshot (pods and Hosts)",
5
5
  "type": "object",
6
6
  "additionalProperties": false,
7
- "required": ["revision", "scanning", "adapter", "candidates"],
7
+ "required": ["revision", "scanning", "lanBrowsing", "adapter", "candidates"],
8
8
  "properties": {
9
9
  "revision": { "type": "integer", "minimum": 0 },
10
10
  "scanning": { "type": "boolean" },
11
+ "lanBrowsing": { "type": "boolean" },
12
+ "lanError": { "type": "string" },
11
13
  "adapter": { "$ref": "#/$defs/adapter" },
12
14
  "candidates": { "type": "array", "items": { "$ref": "#/$defs/candidate" } }
13
15
  },
@@ -27,6 +29,7 @@
27
29
  "required": [
28
30
  "candidateId",
29
31
  "kind",
32
+ "transport",
30
33
  "name",
31
34
  "shortId",
32
35
  "rssi",
@@ -37,9 +40,11 @@
37
40
  "properties": {
38
41
  "candidateId": { "type": "string", "minLength": 1 },
39
42
  "kind": { "enum": ["pod", "host"] },
43
+ "transport": { "enum": ["ble", "lan"] },
44
+ "deviceId": { "type": "string", "minLength": 1, "maxLength": 64 },
40
45
  "name": { "type": "string", "minLength": 1 },
41
46
  "model": { "type": "string" },
42
- "shortId": { "type": "string", "pattern": "^[0-9A-F]{8}$" },
47
+ "shortId": { "type": "string", "minLength": 1, "maxLength": 64 },
43
48
  "rssi": { "type": "integer" },
44
49
  "commissionable": { "type": "boolean" },
45
50
  "wifiConfigured": { "type": "boolean" },