@kusinta/iot-schema 0.2.0-beta.5 → 0.2.0-beta.6
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/package.json +2 -1
- package/src/kusinta/iot/common/v1/pairing_pb.d.ts +199 -0
- package/src/kusinta/iot/common/v1/pairing_pb.js +50 -0
- package/src/kusinta/iot/connector/v1/connector_pb.d.ts +161 -0
- package/src/kusinta/iot/connector/v1/connector_pb.js +25 -3
- package/src/kusinta/iot/webrtc/v1/envelope_pb.d.ts +172 -1
- package/src/kusinta/iot/webrtc/v1/envelope_pb.js +26 -3
- package/src/kusinta/iot/webrtc/v1/management_pb.d.ts +12 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kusinta/iot-schema",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": ["src"],
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
"./common": "./src/kusinta/iot/common/v1/types_pb.js",
|
|
13
|
+
"./common/pairing": "./src/kusinta/iot/common/v1/pairing_pb.js",
|
|
13
14
|
"./identity": "./src/kusinta/iot/identity/v1/identity_pb.js",
|
|
14
15
|
"./device": "./src/kusinta/iot/device/v1/device_pb.js",
|
|
15
16
|
"./device/descriptor": "./src/kusinta/iot/device/v1/descriptor_pb.js",
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// @generated by protoc-gen-es v2.12.0 with parameter "target=js+dts,import_extension=js"
|
|
2
|
+
// @generated from file kusinta/iot/common/v1/pairing.proto (package kusinta.iot.common.v1, syntax proto3)
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
|
|
5
|
+
import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
|
6
|
+
import type { Message } from "@bufbuild/protobuf";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Describes the file kusinta/iot/common/v1/pairing.proto.
|
|
10
|
+
*/
|
|
11
|
+
export declare const file_kusinta_iot_common_v1_pairing: GenFile;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A pairing failure and what can be said about it, the pairing counterpart of CommandError.
|
|
15
|
+
*
|
|
16
|
+
* The message matters more here than elsewhere: DEVICE_UNUSABLE is the code a user is most
|
|
17
|
+
* likely to see and least able to act on, and naming the device that joined is the whole
|
|
18
|
+
* difference between "something went wrong" and "this hub does not support that model yet".
|
|
19
|
+
*
|
|
20
|
+
* @generated from message kusinta.iot.common.v1.PairingErrorDetail
|
|
21
|
+
*/
|
|
22
|
+
export declare type PairingErrorDetail = Message<"kusinta.iot.common.v1.PairingErrorDetail"> & {
|
|
23
|
+
/**
|
|
24
|
+
* @generated from field: kusinta.iot.common.v1.PairingError code = 1;
|
|
25
|
+
*/
|
|
26
|
+
code: PairingError;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* human-readable; may name the device, its model, or the fault
|
|
30
|
+
*
|
|
31
|
+
* @generated from field: string message = 2;
|
|
32
|
+
*/
|
|
33
|
+
message: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Describes the message kusinta.iot.common.v1.PairingErrorDetail.
|
|
38
|
+
* Use `create(PairingErrorDetailSchema)` to create a new message.
|
|
39
|
+
*/
|
|
40
|
+
export declare const PairingErrorDetailSchema: GenMessage<PairingErrorDetail>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* How long a hub accepts new devices, and which it will take.
|
|
44
|
+
*
|
|
45
|
+
* One message on both legs rather than the same three fields declared twice: the clamp rule
|
|
46
|
+
* and the meaning of a hint are the sort of thing that is written once and then diverges,
|
|
47
|
+
* and the two copies had already begun to.
|
|
48
|
+
*
|
|
49
|
+
* @generated from message kusinta.iot.common.v1.PairingWindow
|
|
50
|
+
*/
|
|
51
|
+
export declare type PairingWindow = Message<"kusinta.iot.common.v1.PairingWindow"> & {
|
|
52
|
+
/**
|
|
53
|
+
* How long to accept arrivals. Whoever forwards this clamps it, and so does whoever acts
|
|
54
|
+
* on it: a window is an interval during which anything nearby can join, so its length is a
|
|
55
|
+
* security parameter rather than purely the caller's to choose.
|
|
56
|
+
*
|
|
57
|
+
* Absent means "no preference, use the default" — distinct from a caller asking for zero.
|
|
58
|
+
*
|
|
59
|
+
* @generated from field: optional uint32 duration_seconds = 1;
|
|
60
|
+
*/
|
|
61
|
+
durationSeconds?: number | undefined;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The identifier printed on the device being paired, where the technology has one that is
|
|
65
|
+
* legible and the person is holding it. Free-form: what is printed differs per technology,
|
|
66
|
+
* and nothing between the app and the hub interprets it.
|
|
67
|
+
*
|
|
68
|
+
* Setting it changes the mechanism, not just the accuracy. The hub is restricted to that
|
|
69
|
+
* device and the arrival is verified against this value, so an arrival is attributed on
|
|
70
|
+
* identity. Leaving it empty opens the window to anything nearby, and attribution falls
|
|
71
|
+
* back to the arrival being the only one — which is why one window may be open at a time.
|
|
72
|
+
*
|
|
73
|
+
* Verification is not optional where this is set. A restriction can silently fail to take,
|
|
74
|
+
* and an arrival attributed on a restriction that did not hold is the wrong device in
|
|
75
|
+
* someone's home.
|
|
76
|
+
*
|
|
77
|
+
* @generated from field: string device_hint = 2;
|
|
78
|
+
*/
|
|
79
|
+
deviceHint: string;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* How many arrivals this one window may attribute. Absent means one.
|
|
83
|
+
*
|
|
84
|
+
* A batch suits commissioning, where devices are paired in a row and become a building's
|
|
85
|
+
* rather than any person's. It does not suit a caller whose pairing mints personal
|
|
86
|
+
* ownership.
|
|
87
|
+
*
|
|
88
|
+
* A hint and a batch cannot be combined, and that is a contradiction rather than a
|
|
89
|
+
* limitation: device_hint names one device, so a window restricted to it can never produce
|
|
90
|
+
* a second arrival to attribute.
|
|
91
|
+
*
|
|
92
|
+
* @generated from field: optional uint32 max_devices = 3;
|
|
93
|
+
*/
|
|
94
|
+
maxDevices?: number | undefined;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Describes the message kusinta.iot.common.v1.PairingWindow.
|
|
99
|
+
* Use `create(PairingWindowSchema)` to create a new message.
|
|
100
|
+
*/
|
|
101
|
+
export declare const PairingWindowSchema: GenMessage<PairingWindow>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Why a pairing attempt did not produce a device, in one vocabulary both the app and a
|
|
105
|
+
* connector speak.
|
|
106
|
+
*
|
|
107
|
+
* Shared rather than mirrored, for the reason CommandError's comment gives: mirrored enums
|
|
108
|
+
* leave every connector inventing its own spellings and something in the middle keeping a
|
|
109
|
+
* translation table that silently falls behind. A connector produces the subset it can
|
|
110
|
+
* observe; the gateway produces the rest and passes a connector's value through unchanged.
|
|
111
|
+
*
|
|
112
|
+
* Every value answers one question: *what should the client do now?* A code that does not
|
|
113
|
+
* change the answer does not need to exist, which is why "the device is on the hub and
|
|
114
|
+
* unusable" is one value and not two.
|
|
115
|
+
*
|
|
116
|
+
* @generated from enum kusinta.iot.common.v1.PairingError
|
|
117
|
+
*/
|
|
118
|
+
export enum PairingError {
|
|
119
|
+
/**
|
|
120
|
+
* Sender set no code, or sent one this client's schema does not know.
|
|
121
|
+
*
|
|
122
|
+
* This must stay the safe reading. A client decodes every value added after its build as
|
|
123
|
+
* UNSPECIFIED, so the sensible handling is "it did not work, offering a retry is
|
|
124
|
+
* reasonable" — and no value below is so dangerous that reading it this way misleads.
|
|
125
|
+
*
|
|
126
|
+
* @generated from enum value: PAIRING_ERROR_UNSPECIFIED = 0;
|
|
127
|
+
*/
|
|
128
|
+
UNSPECIFIED = 0,
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The caller may not pair here. Permanent for this caller: stop, and say so.
|
|
132
|
+
*
|
|
133
|
+
* @generated from enum value: PAIRING_ERROR_NOT_ENTITLED = 1;
|
|
134
|
+
*/
|
|
135
|
+
NOT_ENTITLED = 1,
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* A pairing window is already open, and only one may be. Retrying after it closes is the
|
|
139
|
+
* right response; retrying at once is not.
|
|
140
|
+
*
|
|
141
|
+
* The exclusivity is not incidental. Where a request names no device, the only thing
|
|
142
|
+
* attributing an arrival to a requester is that theirs was the one window open.
|
|
143
|
+
*
|
|
144
|
+
* @generated from enum value: PAIRING_ERROR_ALREADY_IN_PROGRESS = 2;
|
|
145
|
+
*/
|
|
146
|
+
ALREADY_IN_PROGRESS = 2,
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The connector could not be reached, or refused to open its window. Transient.
|
|
150
|
+
*
|
|
151
|
+
* @generated from enum value: PAIRING_ERROR_CONNECTOR_UNAVAILABLE = 3;
|
|
152
|
+
*/
|
|
153
|
+
CONNECTOR_UNAVAILABLE = 3,
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The window closed with nothing paired: no device was put into pairing mode, or it was
|
|
157
|
+
* out of range, or its batteries are flat. The ordinary outcome of a user who walked away,
|
|
158
|
+
* so the useful response is to say what to do and offer another go, not to report a fault.
|
|
159
|
+
*
|
|
160
|
+
* @generated from enum value: PAIRING_ERROR_NO_DEVICE_APPEARED = 4;
|
|
161
|
+
*/
|
|
162
|
+
NO_DEVICE_APPEARED = 4,
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A device joined the hub and cannot be presented — the connector has no model for it, or
|
|
166
|
+
* could not assemble a complete description of it.
|
|
167
|
+
*
|
|
168
|
+
* One value rather than two, because the client does the same thing either way: stop, do
|
|
169
|
+
* not retry, and tell the user there is now an unusable device on their hub. Which of the
|
|
170
|
+
* two it was belongs in PairingErrorDetail.message, where it can name the device.
|
|
171
|
+
*
|
|
172
|
+
* It stays joined deliberately. A later connector release that models it adopts it with
|
|
173
|
+
* nobody returning to the hardware, where removing it would need someone standing there.
|
|
174
|
+
*
|
|
175
|
+
* @generated from enum value: PAIRING_ERROR_DEVICE_UNUSABLE = 5;
|
|
176
|
+
*/
|
|
177
|
+
DEVICE_UNUSABLE = 5,
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* A device joined that is not the one the request named. The connector checked the arrival
|
|
181
|
+
* against the identifier it was given and they did not match, so it was not attributed.
|
|
182
|
+
*
|
|
183
|
+
* @generated from enum value: PAIRING_ERROR_WRONG_DEVICE = 6;
|
|
184
|
+
*/
|
|
185
|
+
WRONG_DEVICE = 6,
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A fault on the gateway's or the connector's own side. Not the caller's doing.
|
|
189
|
+
*
|
|
190
|
+
* @generated from enum value: PAIRING_ERROR_INTERNAL = 7;
|
|
191
|
+
*/
|
|
192
|
+
INTERNAL = 7,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Describes the enum kusinta.iot.common.v1.PairingError.
|
|
197
|
+
*/
|
|
198
|
+
export declare const PairingErrorSchema: GenEnum<PairingError>;
|
|
199
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @generated by protoc-gen-es v2.12.0 with parameter "target=js+dts,import_extension=js"
|
|
2
|
+
// @generated from file kusinta/iot/common/v1/pairing.proto (package kusinta.iot.common.v1, syntax proto3)
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
|
|
5
|
+
import { enumDesc, fileDesc, messageDesc, tsEnum } from "@bufbuild/protobuf/codegenv2";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Describes the file kusinta/iot/common/v1/pairing.proto.
|
|
9
|
+
*/
|
|
10
|
+
export const file_kusinta_iot_common_v1_pairing = /*@__PURE__*/
|
|
11
|
+
fileDesc("CiNrdXNpbnRhL2lvdC9jb21tb24vdjEvcGFpcmluZy5wcm90bxIVa3VzaW50YS5pb3QuY29tbW9uLnYxIlgKElBhaXJpbmdFcnJvckRldGFpbBIxCgRjb2RlGAEgASgOMiMua3VzaW50YS5pb3QuY29tbW9uLnYxLlBhaXJpbmdFcnJvchIPCgdtZXNzYWdlGAIgASgJIoIBCg1QYWlyaW5nV2luZG93Eh0KEGR1cmF0aW9uX3NlY29uZHMYASABKA1IAIgBARITCgtkZXZpY2VfaGludBgCIAEoCRIYCgttYXhfZGV2aWNlcxgDIAEoDUgBiAEBQhMKEV9kdXJhdGlvbl9zZWNvbmRzQg4KDF9tYXhfZGV2aWNlcyqiAgoMUGFpcmluZ0Vycm9yEh0KGVBBSVJJTkdfRVJST1JfVU5TUEVDSUZJRUQQABIeChpQQUlSSU5HX0VSUk9SX05PVF9FTlRJVExFRBABEiUKIVBBSVJJTkdfRVJST1JfQUxSRUFEWV9JTl9QUk9HUkVTUxACEicKI1BBSVJJTkdfRVJST1JfQ09OTkVDVE9SX1VOQVZBSUxBQkxFEAMSJAogUEFJUklOR19FUlJPUl9OT19ERVZJQ0VfQVBQRUFSRUQQBBIhCh1QQUlSSU5HX0VSUk9SX0RFVklDRV9VTlVTQUJMRRAFEh4KGlBBSVJJTkdfRVJST1JfV1JPTkdfREVWSUNFEAYSGgoWUEFJUklOR19FUlJPUl9JTlRFUk5BTBAHQgJIAWIGcHJvdG8z");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Describes the message kusinta.iot.common.v1.PairingErrorDetail.
|
|
15
|
+
* Use `create(PairingErrorDetailSchema)` to create a new message.
|
|
16
|
+
*/
|
|
17
|
+
export const PairingErrorDetailSchema = /*@__PURE__*/
|
|
18
|
+
messageDesc(file_kusinta_iot_common_v1_pairing, 0);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Describes the message kusinta.iot.common.v1.PairingWindow.
|
|
22
|
+
* Use `create(PairingWindowSchema)` to create a new message.
|
|
23
|
+
*/
|
|
24
|
+
export const PairingWindowSchema = /*@__PURE__*/
|
|
25
|
+
messageDesc(file_kusinta_iot_common_v1_pairing, 1);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Describes the enum kusinta.iot.common.v1.PairingError.
|
|
29
|
+
*/
|
|
30
|
+
export const PairingErrorSchema = /*@__PURE__*/
|
|
31
|
+
enumDesc(file_kusinta_iot_common_v1_pairing, 0);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Why a pairing attempt did not produce a device, in one vocabulary both the app and a
|
|
35
|
+
* connector speak.
|
|
36
|
+
*
|
|
37
|
+
* Shared rather than mirrored, for the reason CommandError's comment gives: mirrored enums
|
|
38
|
+
* leave every connector inventing its own spellings and something in the middle keeping a
|
|
39
|
+
* translation table that silently falls behind. A connector produces the subset it can
|
|
40
|
+
* observe; the gateway produces the rest and passes a connector's value through unchanged.
|
|
41
|
+
*
|
|
42
|
+
* Every value answers one question: *what should the client do now?* A code that does not
|
|
43
|
+
* change the answer does not need to exist, which is why "the device is on the hub and
|
|
44
|
+
* unusable" is one value and not two.
|
|
45
|
+
*
|
|
46
|
+
* @generated from enum kusinta.iot.common.v1.PairingError
|
|
47
|
+
*/
|
|
48
|
+
export const PairingError = /*@__PURE__*/
|
|
49
|
+
tsEnum(PairingErrorSchema);
|
|
50
|
+
|
|
@@ -9,6 +9,7 @@ import type { ConnectorTransport } from "../../common/v1/types_pb.js";
|
|
|
9
9
|
import type { Device } from "../../device/v1/device_pb.js";
|
|
10
10
|
import type { Timestamp } from "@bufbuild/protobuf/wkt";
|
|
11
11
|
import type { AttributeWriteRequest, CommandError, DeviceCommand } from "../../webrtc/v1/command_pb.js";
|
|
12
|
+
import type { PairingErrorDetail, PairingWindow } from "../../common/v1/pairing_pb.js";
|
|
12
13
|
import type { PropertyUpdateBatch } from "../../device/v1/property_update_pb.js";
|
|
13
14
|
import type { DeviceEventBatch } from "../../device/v1/device_event_pb.js";
|
|
14
15
|
|
|
@@ -122,6 +123,25 @@ export declare type DeviceAnnouncement = Message<"kusinta.iot.connector.v1.Devic
|
|
|
122
123
|
* @generated from field: kusinta.iot.device.v1.Device device = 2;
|
|
123
124
|
*/
|
|
124
125
|
device?: Device | undefined;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The pairing request this device arrived for, empty for a device that arrived on its own.
|
|
129
|
+
*
|
|
130
|
+
* This field is the whole attribution mechanism. A connector is the only party that knows
|
|
131
|
+
* when its own hub was accepting devices, so it decides whether an arrival belongs to a
|
|
132
|
+
* request and says so here.
|
|
133
|
+
*
|
|
134
|
+
* Set it only when the arrival was actually checked. An arrival that fails the check is
|
|
135
|
+
* still announced — the device exists and has joined — but with this empty, which files it
|
|
136
|
+
* as belonging to nobody rather than to the wrong person.
|
|
137
|
+
*
|
|
138
|
+
* This is EnterPairingMode.request_id, minted per connector, and NOT an app message id:
|
|
139
|
+
* whoever forwards the outcome to the app has to map it back. The name avoids `in_reply_to`
|
|
140
|
+
* for that reason, which everywhere else in this schema means the app's own message id.
|
|
141
|
+
*
|
|
142
|
+
* @generated from field: string pairing_request_id = 3;
|
|
143
|
+
*/
|
|
144
|
+
pairingRequestId: string;
|
|
125
145
|
};
|
|
126
146
|
|
|
127
147
|
/**
|
|
@@ -309,6 +329,127 @@ export declare type ConnectorCommandResult = Message<"kusinta.iot.connector.v1.C
|
|
|
309
329
|
*/
|
|
310
330
|
export declare const ConnectorCommandResultSchema: GenMessage<ConnectorCommandResult>;
|
|
311
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Tells a connector to accept new devices for a while, gateway → connector.
|
|
334
|
+
*
|
|
335
|
+
* Named for what the connector does rather than for what the app asked. The two legs are
|
|
336
|
+
* named differently on purpose: the app starts one pairing, and the gateway may put several
|
|
337
|
+
* connectors into pairing mode to serve it.
|
|
338
|
+
*
|
|
339
|
+
* @generated from message kusinta.iot.connector.v1.EnterPairingMode
|
|
340
|
+
*/
|
|
341
|
+
export declare type EnterPairingMode = Message<"kusinta.iot.connector.v1.EnterPairingMode"> & {
|
|
342
|
+
/**
|
|
343
|
+
* echoed on PairingModeResult, PairingModeEnded and the announcement
|
|
344
|
+
*
|
|
345
|
+
* @generated from field: string request_id = 1;
|
|
346
|
+
*/
|
|
347
|
+
requestId: string;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* How long, which device, how many. See PairingWindow — and clamp the duration again here.
|
|
351
|
+
* A connector is the last thing standing between a number on the wire and a radio that
|
|
352
|
+
* accepts anything nearby for that long.
|
|
353
|
+
*
|
|
354
|
+
* @generated from field: kusinta.iot.common.v1.PairingWindow window = 2;
|
|
355
|
+
*/
|
|
356
|
+
window?: PairingWindow | undefined;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Describes the message kusinta.iot.connector.v1.EnterPairingMode.
|
|
361
|
+
* Use `create(EnterPairingModeSchema)` to create a new message.
|
|
362
|
+
*/
|
|
363
|
+
export declare const EnterPairingModeSchema: GenMessage<EnterPairingMode>;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* A connector's answer to EnterPairingMode: whether the window opened, and nothing more.
|
|
367
|
+
*
|
|
368
|
+
* The same accept-now-report-later split as ConnectorCommandResult, for the same reason — a
|
|
369
|
+
* connector that waited for a device to wake before answering would blow every deadline
|
|
370
|
+
* above it. What the window produced arrives later, as PairingModeEnded.
|
|
371
|
+
*
|
|
372
|
+
* @generated from message kusinta.iot.connector.v1.PairingModeResult
|
|
373
|
+
*/
|
|
374
|
+
export declare type PairingModeResult = Message<"kusinta.iot.connector.v1.PairingModeResult"> & {
|
|
375
|
+
/**
|
|
376
|
+
* @generated from field: string request_id = 1;
|
|
377
|
+
*/
|
|
378
|
+
requestId: string;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @generated from field: bool accepted = 2;
|
|
382
|
+
*/
|
|
383
|
+
accepted: boolean;
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Why the window did not open. Only what a connector can observe at this point:
|
|
387
|
+
* CONNECTOR_UNAVAILABLE, INTERNAL. NOT_ENTITLED and ALREADY_IN_PROGRESS are the gateway's,
|
|
388
|
+
* decided before this message is sent, and a connector must not claim them.
|
|
389
|
+
*
|
|
390
|
+
* @generated from field: kusinta.iot.common.v1.PairingErrorDetail error = 3;
|
|
391
|
+
*/
|
|
392
|
+
error?: PairingErrorDetail | undefined;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* When the window closes, if it opened. Lets the gateway expire its own record against the
|
|
396
|
+
* connector's clock rather than a guess — the connector is the party that clamped it.
|
|
397
|
+
*
|
|
398
|
+
* @generated from field: google.protobuf.Timestamp expires_at = 4;
|
|
399
|
+
*/
|
|
400
|
+
expiresAt?: Timestamp | undefined;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Describes the message kusinta.iot.connector.v1.PairingModeResult.
|
|
405
|
+
* Use `create(PairingModeResultSchema)` to create a new message.
|
|
406
|
+
*/
|
|
407
|
+
export declare const PairingModeResultSchema: GenMessage<PairingModeResult>;
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* What the window produced, connector → gateway, sent once when it closes.
|
|
411
|
+
*
|
|
412
|
+
* Without this the connector has no way to report anything it learned after the window
|
|
413
|
+
* opened: a device that joined and cannot be modelled, or one that joined and was not the
|
|
414
|
+
* device the hint named. Neither can travel on a DeviceAnnouncement — the first has nothing
|
|
415
|
+
* announceable, and the second is announced but unattributed, which says a device arrived
|
|
416
|
+
* and not why it was refused.
|
|
417
|
+
*
|
|
418
|
+
* It also gives a batch window a definite end. Counting announcements cannot distinguish
|
|
419
|
+
* "three of five so far" from "three of five, and that is all".
|
|
420
|
+
*
|
|
421
|
+
* @generated from message kusinta.iot.connector.v1.PairingModeEnded
|
|
422
|
+
*/
|
|
423
|
+
export declare type PairingModeEnded = Message<"kusinta.iot.connector.v1.PairingModeEnded"> & {
|
|
424
|
+
/**
|
|
425
|
+
* @generated from field: string request_id = 1;
|
|
426
|
+
*/
|
|
427
|
+
requestId: string;
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* How many arrivals this connector attributed to the request. The gateway can count the
|
|
431
|
+
* announcements it received, but only the connector knows it has stopped.
|
|
432
|
+
*
|
|
433
|
+
* @generated from field: uint32 devices_attributed = 2;
|
|
434
|
+
*/
|
|
435
|
+
devicesAttributed: number;
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Why it produced fewer than asked for, absent when it produced them all. DEVICE_UNUSABLE
|
|
439
|
+
* and WRONG_DEVICE are reported here and should name the device in the message, since that
|
|
440
|
+
* is the only place a user learns which device is now sitting on their hub.
|
|
441
|
+
*
|
|
442
|
+
* @generated from field: kusinta.iot.common.v1.PairingErrorDetail error = 3;
|
|
443
|
+
*/
|
|
444
|
+
error?: PairingErrorDetail | undefined;
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Describes the message kusinta.iot.connector.v1.PairingModeEnded.
|
|
449
|
+
* Use `create(PairingModeEndedSchema)` to create a new message.
|
|
450
|
+
*/
|
|
451
|
+
export declare const PairingModeEndedSchema: GenMessage<PairingModeEnded>;
|
|
452
|
+
|
|
312
453
|
/**
|
|
313
454
|
* @generated from message kusinta.iot.connector.v1.SessionRequest
|
|
314
455
|
*/
|
|
@@ -368,6 +509,18 @@ export declare type SessionRequest = Message<"kusinta.iot.connector.v1.SessionRe
|
|
|
368
509
|
*/
|
|
369
510
|
value: DeviceEventBatch;
|
|
370
511
|
case: "deviceEvents";
|
|
512
|
+
} | {
|
|
513
|
+
/**
|
|
514
|
+
* @generated from field: kusinta.iot.connector.v1.PairingModeResult pairing_mode_result = 10;
|
|
515
|
+
*/
|
|
516
|
+
value: PairingModeResult;
|
|
517
|
+
case: "pairingModeResult";
|
|
518
|
+
} | {
|
|
519
|
+
/**
|
|
520
|
+
* @generated from field: kusinta.iot.connector.v1.PairingModeEnded pairing_mode_ended = 11;
|
|
521
|
+
*/
|
|
522
|
+
value: PairingModeEnded;
|
|
523
|
+
case: "pairingModeEnded";
|
|
371
524
|
} | { case: undefined; value?: undefined };
|
|
372
525
|
};
|
|
373
526
|
|
|
@@ -433,6 +586,14 @@ export declare type SessionResponse = Message<"kusinta.iot.connector.v1.SessionR
|
|
|
433
586
|
*/
|
|
434
587
|
value: AttributeWriteRequest;
|
|
435
588
|
case: "executeAttributeWrite";
|
|
589
|
+
} | {
|
|
590
|
+
/**
|
|
591
|
+
* The connector-leg half of the pairing flow. See EnterPairingMode.
|
|
592
|
+
*
|
|
593
|
+
* @generated from field: kusinta.iot.connector.v1.EnterPairingMode enter_pairing_mode = 10;
|
|
594
|
+
*/
|
|
595
|
+
value: EnterPairingMode;
|
|
596
|
+
case: "enterPairingMode";
|
|
436
597
|
} | { case: undefined; value?: undefined };
|
|
437
598
|
};
|
|
438
599
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
|
6
6
|
import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";
|
|
7
|
+
import { file_kusinta_iot_common_v1_pairing } from "../../common/v1/pairing_pb.js";
|
|
7
8
|
import { file_kusinta_iot_common_v1_types } from "../../common/v1/types_pb.js";
|
|
8
9
|
import { file_kusinta_iot_device_v1_device } from "../../device/v1/device_pb.js";
|
|
9
10
|
import { file_kusinta_iot_device_v1_device_event } from "../../device/v1/device_event_pb.js";
|
|
@@ -15,7 +16,7 @@ import { file_kusinta_iot_webrtc_v1_command } from "../../webrtc/v1/command_pb.j
|
|
|
15
16
|
* Describes the file kusinta/iot/connector/v1/connector.proto.
|
|
16
17
|
*/
|
|
17
18
|
export const file_kusinta_iot_connector_v1_connector = /*@__PURE__*/
|
|
18
|
-
fileDesc("
|
|
19
|
+
fileDesc("CihrdXNpbnRhL2lvdC9jb25uZWN0b3IvdjEvY29ubmVjdG9yLnByb3RvEhhrdXNpbnRhLmlvdC5jb25uZWN0b3IudjEi5QEKDUNvbm5lY3RvckluZm8SOgoMY29ubmVjdG9yX2lkGAEgASgLMiQua3VzaW50YS5pb3QuaWRlbnRpdHkudjEuQ29ubmVjdG9ySWQSFAoMZGlzcGxheV9uYW1lGAIgASgJEg8KB3ZlcnNpb24YAyABKAkSPAoJdHJhbnNwb3J0GAQgASgOMikua3VzaW50YS5pb3QuY29tbW9uLnYxLkNvbm5lY3RvclRyYW5zcG9ydBIQCghlbmRwb2ludBgFIAEoCRIhChlzdXBwb3J0ZWRfZGV2aWNlX3R5cGVfaWRzGAYgAygNIoEBChJDb25uZWN0b3JIYW5kc2hha2USNQoEaW5mbxgBIAEoCzInLmt1c2ludGEuaW90LmNvbm5lY3Rvci52MS5Db25uZWN0b3JJbmZvEjQKDWtub3duX2RldmljZXMYAiADKAsyHS5rdXNpbnRhLmlvdC5kZXZpY2UudjEuRGV2aWNlImgKDEhhbmRzaGFrZUFjaxIQCghhY2NlcHRlZBgBIAEoCBIOCgZyZWFzb24YAiABKAkSNgoKZ2F0ZXdheV9pZBgDIAEoCzIiLmt1c2ludGEuaW90LmlkZW50aXR5LnYxLkdhdGV3YXlJZCJxChJEZXZpY2VBbm5vdW5jZW1lbnQSLQoGZGV2aWNlGAIgASgLMh0ua3VzaW50YS5pb3QuZGV2aWNlLnYxLkRldmljZRIaChJwYWlyaW5nX3JlcXVlc3RfaWQYAyABKAlKBAgBEAJSCmRlc2NyaXB0b3IiVQoNRGV2aWNlUmVtb3ZhbBI0CglkZXZpY2VfaWQYASABKAsyIS5rdXNpbnRhLmlvdC5pZGVudGl0eS52MS5EZXZpY2VJZBIOCgZyZWFzb24YAiABKAkiCwoJSGVhcnRCZWF0IkcKD1N1YnNjcmliZURldmljZRI0CglkZXZpY2VfaWQYASABKAsyIS5rdXNpbnRhLmlvdC5pZGVudGl0eS52MS5EZXZpY2VJZCJJChFVbnN1YnNjcmliZURldmljZRI0CglkZXZpY2VfaWQYASABKAsyIS5rdXNpbnRhLmlvdC5pZGVudGl0eS52MS5EZXZpY2VJZCJBCgxHYXRld2F5RXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEhIKCnJlcXVlc3RfaWQYAyABKAki7QEKFkNvbm5lY3RvckNvbW1hbmRSZXN1bHQSEgoKcmVxdWVzdF9pZBgBIAEoCRIPCgdzdWNjZXNzGAIgASgIEjAKDGNvbXBsZXRlZF9hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASMgoFZXJyb3IYBSABKAsyIy5rdXNpbnRhLmlvdC53ZWJydGMudjEuQ29tbWFuZEVycm9yEjMKCnNldHRsZXNfYnkYBiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSACIAQFCDQoLX3NldHRsZXNfYnlKBAgDEAQiXAoQRW50ZXJQYWlyaW5nTW9kZRISCgpyZXF1ZXN0X2lkGAEgASgJEjQKBndpbmRvdxgCIAEoCzIkLmt1c2ludGEuaW90LmNvbW1vbi52MS5QYWlyaW5nV2luZG93IqMBChFQYWlyaW5nTW9kZVJlc3VsdBISCgpyZXF1ZXN0X2lkGAEgASgJEhAKCGFjY2VwdGVkGAIgASgIEjgKBWVycm9yGAMgASgLMikua3VzaW50YS5pb3QuY29tbW9uLnYxLlBhaXJpbmdFcnJvckRldGFpbBIuCgpleHBpcmVzX2F0GAQgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCJ8ChBQYWlyaW5nTW9kZUVuZGVkEhIKCnJlcXVlc3RfaWQYASABKAkSGgoSZGV2aWNlc19hdHRyaWJ1dGVkGAIgASgNEjgKBWVycm9yGAMgASgLMikua3VzaW50YS5pb3QuY29tbW9uLnYxLlBhaXJpbmdFcnJvckRldGFpbCLRBQoOU2Vzc2lvblJlcXVlc3QSEgoKbWVzc2FnZV9pZBgBIAEoCRIrCgdzZW50X2F0GAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBJBCgloYW5kc2hha2UYAyABKAsyLC5rdXNpbnRhLmlvdC5jb25uZWN0b3IudjEuQ29ubmVjdG9ySGFuZHNoYWtlSAASRQoPcHJvcGVydHlfdXBkYXRlGAQgASgLMioua3VzaW50YS5pb3QuZGV2aWNlLnYxLlByb3BlcnR5VXBkYXRlQmF0Y2hIABJIChBkZXZpY2VfYW5ub3VuY2VkGAUgASgLMiwua3VzaW50YS5pb3QuY29ubmVjdG9yLnYxLkRldmljZUFubm91bmNlbWVudEgAEkEKDmRldmljZV9yZW1vdmVkGAYgASgLMicua3VzaW50YS5pb3QuY29ubmVjdG9yLnYxLkRldmljZVJlbW92YWxIABJKCg5jb21tYW5kX3Jlc3VsdBgHIAEoCzIwLmt1c2ludGEuaW90LmNvbm5lY3Rvci52MS5Db25uZWN0b3JDb21tYW5kUmVzdWx0SAASOAoJaGVhcnRiZWF0GAggASgLMiMua3VzaW50YS5pb3QuY29ubmVjdG9yLnYxLkhlYXJ0QmVhdEgAEkAKDWRldmljZV9ldmVudHMYCSABKAsyJy5rdXNpbnRhLmlvdC5kZXZpY2UudjEuRGV2aWNlRXZlbnRCYXRjaEgAEkoKE3BhaXJpbmdfbW9kZV9yZXN1bHQYCiABKAsyKy5rdXNpbnRhLmlvdC5jb25uZWN0b3IudjEuUGFpcmluZ01vZGVSZXN1bHRIABJIChJwYWlyaW5nX21vZGVfZW5kZWQYCyABKAsyKi5rdXNpbnRhLmlvdC5jb25uZWN0b3IudjEuUGFpcmluZ01vZGVFbmRlZEgAQgkKB3BheWxvYWQivQQKD1Nlc3Npb25SZXNwb25zZRISCgptZXNzYWdlX2lkGAEgASgJEisKB3NlbnRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEj8KDWhhbmRzaGFrZV9hY2sYAyABKAsyJi5rdXNpbnRhLmlvdC5jb25uZWN0b3IudjEuSGFuZHNoYWtlQWNrSAASPgoJc3Vic2NyaWJlGAUgASgLMikua3VzaW50YS5pb3QuY29ubmVjdG9yLnYxLlN1YnNjcmliZURldmljZUgAEkIKC3Vuc3Vic2NyaWJlGAYgASgLMisua3VzaW50YS5pb3QuY29ubmVjdG9yLnYxLlVuc3Vic2NyaWJlRGV2aWNlSAASNwoFZXJyb3IYByABKAsyJi5rdXNpbnRhLmlvdC5jb25uZWN0b3IudjEuR2F0ZXdheUVycm9ySAASPwoPZXhlY3V0ZV9jb21tYW5kGAggASgLMiQua3VzaW50YS5pb3Qud2VicnRjLnYxLkRldmljZUNvbW1hbmRIABJPChdleGVjdXRlX2F0dHJpYnV0ZV93cml0ZRgJIAEoCzIsLmt1c2ludGEuaW90LndlYnJ0Yy52MS5BdHRyaWJ1dGVXcml0ZVJlcXVlc3RIABJIChJlbnRlcl9wYWlyaW5nX21vZGUYCiABKAsyKi5rdXNpbnRhLmlvdC5jb25uZWN0b3IudjEuRW50ZXJQYWlyaW5nTW9kZUgAQgkKB3BheWxvYWRKBAgEEAVCAkgBYgZwcm90bzM", [file_google_protobuf_timestamp, file_kusinta_iot_common_v1_pairing, file_kusinta_iot_common_v1_types, file_kusinta_iot_device_v1_device, file_kusinta_iot_device_v1_device_event, file_kusinta_iot_device_v1_property_update, file_kusinta_iot_identity_v1_identity, file_kusinta_iot_webrtc_v1_command]);
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Describes the message kusinta.iot.connector.v1.ConnectorInfo.
|
|
@@ -87,17 +88,38 @@ export const GatewayErrorSchema = /*@__PURE__*/
|
|
|
87
88
|
export const ConnectorCommandResultSchema = /*@__PURE__*/
|
|
88
89
|
messageDesc(file_kusinta_iot_connector_v1_connector, 9);
|
|
89
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Describes the message kusinta.iot.connector.v1.EnterPairingMode.
|
|
93
|
+
* Use `create(EnterPairingModeSchema)` to create a new message.
|
|
94
|
+
*/
|
|
95
|
+
export const EnterPairingModeSchema = /*@__PURE__*/
|
|
96
|
+
messageDesc(file_kusinta_iot_connector_v1_connector, 10);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Describes the message kusinta.iot.connector.v1.PairingModeResult.
|
|
100
|
+
* Use `create(PairingModeResultSchema)` to create a new message.
|
|
101
|
+
*/
|
|
102
|
+
export const PairingModeResultSchema = /*@__PURE__*/
|
|
103
|
+
messageDesc(file_kusinta_iot_connector_v1_connector, 11);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Describes the message kusinta.iot.connector.v1.PairingModeEnded.
|
|
107
|
+
* Use `create(PairingModeEndedSchema)` to create a new message.
|
|
108
|
+
*/
|
|
109
|
+
export const PairingModeEndedSchema = /*@__PURE__*/
|
|
110
|
+
messageDesc(file_kusinta_iot_connector_v1_connector, 12);
|
|
111
|
+
|
|
90
112
|
/**
|
|
91
113
|
* Describes the message kusinta.iot.connector.v1.SessionRequest.
|
|
92
114
|
* Use `create(SessionRequestSchema)` to create a new message.
|
|
93
115
|
*/
|
|
94
116
|
export const SessionRequestSchema = /*@__PURE__*/
|
|
95
|
-
messageDesc(file_kusinta_iot_connector_v1_connector,
|
|
117
|
+
messageDesc(file_kusinta_iot_connector_v1_connector, 13);
|
|
96
118
|
|
|
97
119
|
/**
|
|
98
120
|
* Describes the message kusinta.iot.connector.v1.SessionResponse.
|
|
99
121
|
* Use `create(SessionResponseSchema)` to create a new message.
|
|
100
122
|
*/
|
|
101
123
|
export const SessionResponseSchema = /*@__PURE__*/
|
|
102
|
-
messageDesc(file_kusinta_iot_connector_v1_connector,
|
|
124
|
+
messageDesc(file_kusinta_iot_connector_v1_connector, 14);
|
|
103
125
|
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
|
6
6
|
import type { Message } from "@bufbuild/protobuf";
|
|
7
|
-
import type { DeviceId } from "../../identity/v1/identity_pb.js";
|
|
7
|
+
import type { ConnectorId, DeviceId, SpaceId } from "../../identity/v1/identity_pb.js";
|
|
8
8
|
import type { AttributeRef } from "../../access/v1/acl_pb.js";
|
|
9
9
|
import type { Space } from "../../space/v1/space_pb.js";
|
|
10
10
|
import type { ManagementAck, ManagementRequest, SpaceTree } from "./management_pb.js";
|
|
11
|
+
import type { PairingErrorDetail, PairingWindow } from "../../common/v1/pairing_pb.js";
|
|
12
|
+
import type { DeviceOwnershipType } from "../../common/v1/types_pb.js";
|
|
11
13
|
import type { Timestamp } from "@bufbuild/protobuf/wkt";
|
|
12
14
|
import type { DeviceAdded, DeviceRemoved, DeviceStateSnapshot, PropertyReport } from "./device_state_pb.js";
|
|
13
15
|
import type { LivePermissionUpdate } from "./permission_push_pb.js";
|
|
@@ -325,6 +327,157 @@ export declare type ManagementResult = Message<"kusinta.iot.webrtc.v1.Management
|
|
|
325
327
|
*/
|
|
326
328
|
export declare const ManagementResultSchema: GenMessage<ManagementResult>;
|
|
327
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Asks the gateway to put a connector into pairing mode, so a device joined during the
|
|
332
|
+
* window becomes the caller's rather than belonging to nobody.
|
|
333
|
+
*
|
|
334
|
+
* A payload case of its own rather than a ManagementRequest. Those are gated on a target —
|
|
335
|
+
* a space, a device — and that is the justification for keeping them behind one wrapper.
|
|
336
|
+
* This one names no target: connector_id is optional, and unset means every connector, so
|
|
337
|
+
* it is authorized against the caller's role and nothing else.
|
|
338
|
+
*
|
|
339
|
+
* Answered twice, and the two answers say different things at different times.
|
|
340
|
+
* PairingStarted reports whether the window opened; PairingFinished reports what came of
|
|
341
|
+
* it, seconds or minutes later, once someone has pressed a button on hardware that may be
|
|
342
|
+
* asleep or out of range.
|
|
343
|
+
*
|
|
344
|
+
* @generated from message kusinta.iot.webrtc.v1.StartPairing
|
|
345
|
+
*/
|
|
346
|
+
export declare type StartPairing = Message<"kusinta.iot.webrtc.v1.StartPairing"> & {
|
|
347
|
+
/**
|
|
348
|
+
* Which connector to open. Unset means every connector the gateway holds, which is the
|
|
349
|
+
* sensible default: a device picks its hub by radio range, and asking a user to choose is
|
|
350
|
+
* asking them to guess at something they cannot observe.
|
|
351
|
+
*
|
|
352
|
+
* Name one where the answer is not a guess — a device that will be linked to devices on a
|
|
353
|
+
* particular hub has to join that hub, because links are held by the hub that brokered
|
|
354
|
+
* them.
|
|
355
|
+
*
|
|
356
|
+
* @generated from field: kusinta.iot.identity.v1.ConnectorId connector_id = 1;
|
|
357
|
+
*/
|
|
358
|
+
connectorId?: ConnectorId | undefined;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* How long, which device, how many. See PairingWindow.
|
|
362
|
+
*
|
|
363
|
+
* @generated from field: kusinta.iot.common.v1.PairingWindow window = 2;
|
|
364
|
+
*/
|
|
365
|
+
window?: PairingWindow | undefined;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Where to file the device once it arrives.
|
|
369
|
+
*
|
|
370
|
+
* Optional, and the two cases differ. A caller pairing a device of their own must name a
|
|
371
|
+
* space they reach, so it is theirs and placed on arrival rather than sitting unfiled and
|
|
372
|
+
* invisible to them. A caller commissioning for a building may leave it unset: a building
|
|
373
|
+
* is commissioned before its spaces are recorded, and an unfiled device is visible to the
|
|
374
|
+
* administrator, who is the person doing the pairing.
|
|
375
|
+
*
|
|
376
|
+
* @generated from field: kusinta.iot.identity.v1.SpaceId initial_space_id = 3;
|
|
377
|
+
*/
|
|
378
|
+
initialSpaceId?: SpaceId | undefined;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* What the device should become. Unset lets the gateway decide from the caller's role,
|
|
382
|
+
* which is the reading that scales: someone commissioning fifty devices for a building
|
|
383
|
+
* should not come to own fifty devices because a field defaulted.
|
|
384
|
+
*
|
|
385
|
+
* The gateway refuses an ownership the caller may not confer, and never mints ownership
|
|
386
|
+
* for a third party — handing a device to someone else is a transfer, a separate operation
|
|
387
|
+
* with its own authority.
|
|
388
|
+
*
|
|
389
|
+
* @generated from field: kusinta.iot.common.v1.DeviceOwnershipType ownership = 4;
|
|
390
|
+
*/
|
|
391
|
+
ownership: DeviceOwnershipType;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Describes the message kusinta.iot.webrtc.v1.StartPairing.
|
|
396
|
+
* Use `create(StartPairingSchema)` to create a new message.
|
|
397
|
+
*/
|
|
398
|
+
export declare const StartPairingSchema: GenMessage<StartPairing>;
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Whether the pairing window opened, gateway → app. The first of two answers to a
|
|
402
|
+
* StartPairing.
|
|
403
|
+
*
|
|
404
|
+
* @generated from message kusinta.iot.webrtc.v1.PairingStarted
|
|
405
|
+
*/
|
|
406
|
+
export declare type PairingStarted = Message<"kusinta.iot.webrtc.v1.PairingStarted"> & {
|
|
407
|
+
/**
|
|
408
|
+
* AppMessage.message_id of the StartPairing
|
|
409
|
+
*
|
|
410
|
+
* @generated from field: string in_reply_to = 1;
|
|
411
|
+
*/
|
|
412
|
+
inReplyTo: string;
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* When the window closes. Present when it opened, and this is the value to count down
|
|
416
|
+
* from — not the duration that was asked for, which both the gateway and the connector
|
|
417
|
+
* clamp. A countdown run on the request rather than on this one disagrees with the hub.
|
|
418
|
+
*
|
|
419
|
+
* @generated from field: google.protobuf.Timestamp expires_at = 2;
|
|
420
|
+
*/
|
|
421
|
+
expiresAt?: Timestamp | undefined;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Why it did not open. Present instead of expires_at.
|
|
425
|
+
*
|
|
426
|
+
* @generated from field: kusinta.iot.common.v1.PairingErrorDetail error = 3;
|
|
427
|
+
*/
|
|
428
|
+
error?: PairingErrorDetail | undefined;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Describes the message kusinta.iot.webrtc.v1.PairingStarted.
|
|
433
|
+
* Use `create(PairingStartedSchema)` to create a new message.
|
|
434
|
+
*/
|
|
435
|
+
export declare const PairingStartedSchema: GenMessage<PairingStarted>;
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* What the pairing window produced, gateway → app. The second and last answer, sent once
|
|
439
|
+
* when the window closes.
|
|
440
|
+
*
|
|
441
|
+
* One message per request, not per device: a batch window attributes several, and a client
|
|
442
|
+
* given one message each has nothing telling it the window is over and no defined moment to
|
|
443
|
+
* stop waiting. The devices are listed here; each also arrives as an ordinary DeviceAdded
|
|
444
|
+
* as it appears, so a client may show them as they come and use this to finish.
|
|
445
|
+
*
|
|
446
|
+
* An error and a non-empty device list are not exclusive. A batch of five that attributed
|
|
447
|
+
* three and then expired reports both — three devices and NO_DEVICE_APPEARED — because
|
|
448
|
+
* "some worked" and "the window ran out" are both true and a user needs to be told both.
|
|
449
|
+
*
|
|
450
|
+
* @generated from message kusinta.iot.webrtc.v1.PairingFinished
|
|
451
|
+
*/
|
|
452
|
+
export declare type PairingFinished = Message<"kusinta.iot.webrtc.v1.PairingFinished"> & {
|
|
453
|
+
/**
|
|
454
|
+
* AppMessage.message_id of the StartPairing
|
|
455
|
+
*
|
|
456
|
+
* @generated from field: string in_reply_to = 1;
|
|
457
|
+
*/
|
|
458
|
+
inReplyTo: string;
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* @generated from field: repeated kusinta.iot.identity.v1.DeviceId device_ids = 2;
|
|
462
|
+
*/
|
|
463
|
+
deviceIds: DeviceId[];
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Why the window produced fewer devices than asked for, absent when it produced them all.
|
|
467
|
+
* Note this is not always a failure of the request: a window that simply expired unused
|
|
468
|
+
* ends here too, and is the ordinary result of a user changing their mind.
|
|
469
|
+
*
|
|
470
|
+
* @generated from field: kusinta.iot.common.v1.PairingErrorDetail error = 3;
|
|
471
|
+
*/
|
|
472
|
+
error?: PairingErrorDetail | undefined;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Describes the message kusinta.iot.webrtc.v1.PairingFinished.
|
|
477
|
+
* Use `create(PairingFinishedSchema)` to create a new message.
|
|
478
|
+
*/
|
|
479
|
+
export declare const PairingFinishedSchema: GenMessage<PairingFinished>;
|
|
480
|
+
|
|
328
481
|
/**
|
|
329
482
|
* GatewayMessage: gateway → app
|
|
330
483
|
*
|
|
@@ -421,6 +574,18 @@ export declare type GatewayMessage = Message<"kusinta.iot.webrtc.v1.GatewayMessa
|
|
|
421
574
|
*/
|
|
422
575
|
value: DeviceEventBatch;
|
|
423
576
|
case: "deviceEvents";
|
|
577
|
+
} | {
|
|
578
|
+
/**
|
|
579
|
+
* @generated from field: kusinta.iot.webrtc.v1.PairingStarted pairing_started = 17;
|
|
580
|
+
*/
|
|
581
|
+
value: PairingStarted;
|
|
582
|
+
case: "pairingStarted";
|
|
583
|
+
} | {
|
|
584
|
+
/**
|
|
585
|
+
* @generated from field: kusinta.iot.webrtc.v1.PairingFinished pairing_finished = 18;
|
|
586
|
+
*/
|
|
587
|
+
value: PairingFinished;
|
|
588
|
+
case: "pairingFinished";
|
|
424
589
|
} | { case: undefined; value?: undefined };
|
|
425
590
|
};
|
|
426
591
|
|
|
@@ -497,6 +662,12 @@ export declare type AppMessage = Message<"kusinta.iot.webrtc.v1.AppMessage"> & {
|
|
|
497
662
|
*/
|
|
498
663
|
value: AttributeWriteRequest;
|
|
499
664
|
case: "attributeWrite";
|
|
665
|
+
} | {
|
|
666
|
+
/**
|
|
667
|
+
* @generated from field: kusinta.iot.webrtc.v1.StartPairing start_pairing = 11;
|
|
668
|
+
*/
|
|
669
|
+
value: StartPairing;
|
|
670
|
+
case: "startPairing";
|
|
500
671
|
} | { case: undefined; value?: undefined };
|
|
501
672
|
};
|
|
502
673
|
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import { enumDesc, fileDesc, messageDesc, tsEnum } from "@bufbuild/protobuf/codegenv2";
|
|
6
6
|
import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";
|
|
7
7
|
import { file_kusinta_iot_access_v1_acl } from "../../access/v1/acl_pb.js";
|
|
8
|
+
import { file_kusinta_iot_common_v1_pairing } from "../../common/v1/pairing_pb.js";
|
|
9
|
+
import { file_kusinta_iot_common_v1_types } from "../../common/v1/types_pb.js";
|
|
8
10
|
import { file_kusinta_iot_device_v1_device_event } from "../../device/v1/device_event_pb.js";
|
|
9
11
|
import { file_kusinta_iot_identity_v1_identity } from "../../identity/v1/identity_pb.js";
|
|
10
12
|
import { file_kusinta_iot_space_v1_space } from "../../space/v1/space_pb.js";
|
|
@@ -17,7 +19,7 @@ import { file_kusinta_iot_webrtc_v1_permission_push } from "./permission_push_pb
|
|
|
17
19
|
* Describes the file kusinta/iot/webrtc/v1/envelope.proto.
|
|
18
20
|
*/
|
|
19
21
|
export const file_kusinta_iot_webrtc_v1_envelope = /*@__PURE__*/
|
|
20
|
-
fileDesc("
|
|
22
|
+
fileDesc("CiRrdXNpbnRhL2lvdC93ZWJydGMvdjEvZW52ZWxvcGUucHJvdG8SFWt1c2ludGEuaW90LndlYnJ0Yy52MSIGCgRQaW5nIgYKBFBvbmciIwoRSGFuZHNoYWtlUmVqZWN0ZWQSDgoGcmVhc29uGAEgASgJIlwKDEFwcEhhbmRzaGFrZRILCgNqd3QYASABKAkSPwoUc3Vic2NyaWJlX2RldmljZV9pZHMYAiADKAsyIS5rdXNpbnRhLmlvdC5pZGVudGl0eS52MS5EZXZpY2VJZCJJChBTdWJzY3JpYmVEZXZpY2VzEjUKCmRldmljZV9pZHMYASADKAsyIS5rdXNpbnRhLmlvdC5pZGVudGl0eS52MS5EZXZpY2VJZCJLChJVbnN1YnNjcmliZURldmljZXMSNQoKZGV2aWNlX2lkcxgBIAMoCzIhLmt1c2ludGEuaW90LmlkZW50aXR5LnYxLkRldmljZUlkIpMBChNSZWZ1c2VkU3Vic2NyaXB0aW9uEjQKCWRldmljZV9pZBgBIAEoCzIhLmt1c2ludGEuaW90LmlkZW50aXR5LnYxLkRldmljZUlkEjUKBGNvZGUYAiABKA4yJy5rdXNpbnRhLmlvdC53ZWJydGMudjEuR2F0ZXdheUVycm9yQ29kZRIPCgdtZXNzYWdlGAMgASgJIpoBCg9TdWJzY3JpcHRpb25BY2sSEwoLaW5fcmVwbHlfdG8YASABKAkSNQoKc3Vic2NyaWJlZBgCIAMoCzIhLmt1c2ludGEuaW90LmlkZW50aXR5LnYxLkRldmljZUlkEjsKB3JlZnVzZWQYAyADKAsyKi5rdXNpbnRhLmlvdC53ZWJydGMudjEuUmVmdXNlZFN1YnNjcmlwdGlvbiLRAQoTUHJvcGVydHlSZWFkUmVxdWVzdBI0CglkZXZpY2VfaWQYASABKAsyIS5rdXNpbnRhLmlvdC5pZGVudGl0eS52MS5EZXZpY2VJZBIzCgZ0YXJnZXQYByABKAsyIy5rdXNpbnRhLmlvdC5hY2Nlc3MudjEuQXR0cmlidXRlUmVmSgQIAhAHUg5hdHRyaWJ1dGVfbmFtZVIOY2x1c3Rlcl9pZF9oZXhSCmNsdXN0ZXJfaWRSC2VuZHBvaW50X2lkUhB2ZW5kb3JfZXh0ZW5zaW9uIswBCgxHYXRld2F5RXJyb3ISNQoEY29kZRgBIAEoDjInLmt1c2ludGEuaW90LndlYnJ0Yy52MS5HYXRld2F5RXJyb3JDb2RlEg8KB21lc3NhZ2UYAiABKAkSQwoIbWV0YWRhdGEYAyADKAsyMS5rdXNpbnRhLmlvdC53ZWJydGMudjEuR2F0ZXdheUVycm9yLk1ldGFkYXRhRW50cnkaLwoNTWV0YWRhdGFFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBIoICChBNYW5hZ2VtZW50UmVzdWx0EhMKC2luX3JlcGx5X3RvGAEgASgJEjQKBWVycm9yGAIgASgLMiMua3VzaW50YS5pb3Qud2VicnRjLnYxLkdhdGV3YXlFcnJvckgAEiwKBXNwYWNlGAMgASgLMhsua3VzaW50YS5pb3Quc3BhY2UudjEuU3BhY2VIABI2CgpzcGFjZV90cmVlGAQgASgLMiAua3VzaW50YS5pb3Qud2VicnRjLnYxLlNwYWNlVHJlZUgAEjMKA2FjaxgFIAEoCzIkLmt1c2ludGEuaW90LndlYnJ0Yy52MS5NYW5hZ2VtZW50QWNrSABCCAoGcmVzdWx0IvsBCgxTdGFydFBhaXJpbmcSOgoMY29ubmVjdG9yX2lkGAEgASgLMiQua3VzaW50YS5pb3QuaWRlbnRpdHkudjEuQ29ubmVjdG9ySWQSNAoGd2luZG93GAIgASgLMiQua3VzaW50YS5pb3QuY29tbW9uLnYxLlBhaXJpbmdXaW5kb3cSOgoQaW5pdGlhbF9zcGFjZV9pZBgDIAEoCzIgLmt1c2ludGEuaW90LmlkZW50aXR5LnYxLlNwYWNlSWQSPQoJb3duZXJzaGlwGAQgASgOMioua3VzaW50YS5pb3QuY29tbW9uLnYxLkRldmljZU93bmVyc2hpcFR5cGUijwEKDlBhaXJpbmdTdGFydGVkEhMKC2luX3JlcGx5X3RvGAEgASgJEi4KCmV4cGlyZXNfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEjgKBWVycm9yGAMgASgLMikua3VzaW50YS5pb3QuY29tbW9uLnYxLlBhaXJpbmdFcnJvckRldGFpbCKXAQoPUGFpcmluZ0ZpbmlzaGVkEhMKC2luX3JlcGx5X3RvGAEgASgJEjUKCmRldmljZV9pZHMYAiADKAsyIS5rdXNpbnRhLmlvdC5pZGVudGl0eS52MS5EZXZpY2VJZBI4CgVlcnJvchgDIAEoCzIpLmt1c2ludGEuaW90LmNvbW1vbi52MS5QYWlyaW5nRXJyb3JEZXRhaWwimwgKDkdhdGV3YXlNZXNzYWdlEhIKCm1lc3NhZ2VfaWQYASABKAkSKwoHc2VudF9hdBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASRAoOc3RhdGVfc25hcHNob3QYAyABKAsyKi5rdXNpbnRhLmlvdC53ZWJydGMudjEuRGV2aWNlU3RhdGVTbmFwc2hvdEgAEkAKD3Byb3BlcnR5X3JlcG9ydBgEIAEoCzIlLmt1c2ludGEuaW90LndlYnJ0Yy52MS5Qcm9wZXJ0eVJlcG9ydEgAEkgKEXBlcm1pc3Npb25fdXBkYXRlGAUgASgLMisua3VzaW50YS5pb3Qud2VicnRjLnYxLkxpdmVQZXJtaXNzaW9uVXBkYXRlSAASPgoOY29tbWFuZF9yZXN1bHQYBiABKAsyJC5rdXNpbnRhLmlvdC53ZWJydGMudjEuQ29tbWFuZFJlc3VsdEgAEisKBHBvbmcYCCABKAsyGy5rdXNpbnRhLmlvdC53ZWJydGMudjEuUG9uZ0gAEkYKEmhhbmRzaGFrZV9yZWplY3RlZBgJIAEoCzIoLmt1c2ludGEuaW90LndlYnJ0Yy52MS5IYW5kc2hha2VSZWplY3RlZEgAEjQKBWVycm9yGAogASgLMiMua3VzaW50YS5pb3Qud2VicnRjLnYxLkdhdGV3YXlFcnJvckgAEkIKEHN1YnNjcmlwdGlvbl9hY2sYCyABKAsyJi5rdXNpbnRhLmlvdC53ZWJydGMudjEuU3Vic2NyaXB0aW9uQWNrSAASOgoMZGV2aWNlX2FkZGVkGAwgASgLMiIua3VzaW50YS5pb3Qud2VicnRjLnYxLkRldmljZUFkZGVkSAASPgoOZGV2aWNlX3JlbW92ZWQYDSABKAsyJC5rdXNpbnRhLmlvdC53ZWJydGMudjEuRGV2aWNlUmVtb3ZlZEgAEkQKEW1hbmFnZW1lbnRfcmVzdWx0GA4gASgLMicua3VzaW50YS5pb3Qud2VicnRjLnYxLk1hbmFnZW1lbnRSZXN1bHRIABJACg1kZXZpY2VfZXZlbnRzGBAgASgLMicua3VzaW50YS5pb3QuZGV2aWNlLnYxLkRldmljZUV2ZW50QmF0Y2hIABJACg9wYWlyaW5nX3N0YXJ0ZWQYESABKAsyJS5rdXNpbnRhLmlvdC53ZWJydGMudjEuUGFpcmluZ1N0YXJ0ZWRIABJCChBwYWlyaW5nX2ZpbmlzaGVkGBIgASgLMiYua3VzaW50YS5pb3Qud2VicnRjLnYxLlBhaXJpbmdGaW5pc2hlZEgAQgkKB3BheWxvYWRKBAgPEBBKBAgHEAhSFmF0dHJpYnV0ZV93cml0ZV9yZXN1bHRSDnByb3BlcnR5X2V2ZW50IoMFCgpBcHBNZXNzYWdlEhIKCm1lc3NhZ2VfaWQYASABKAkSKwoHc2VudF9hdBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASOAoJaGFuZHNoYWtlGAMgASgLMiMua3VzaW50YS5pb3Qud2VicnRjLnYxLkFwcEhhbmRzaGFrZUgAEjcKB2NvbW1hbmQYBCABKAsyJC5rdXNpbnRhLmlvdC53ZWJydGMudjEuRGV2aWNlQ29tbWFuZEgAEkIKDHJlYWRfcmVxdWVzdBgFIAEoCzIqLmt1c2ludGEuaW90LndlYnJ0Yy52MS5Qcm9wZXJ0eVJlYWRSZXF1ZXN0SAASKwoEcGluZxgGIAEoCzIbLmt1c2ludGEuaW90LndlYnJ0Yy52MS5QaW5nSAASPAoJc3Vic2NyaWJlGAcgASgLMicua3VzaW50YS5pb3Qud2VicnRjLnYxLlN1YnNjcmliZURldmljZXNIABJACgt1bnN1YnNjcmliZRgIIAEoCzIpLmt1c2ludGEuaW90LndlYnJ0Yy52MS5VbnN1YnNjcmliZURldmljZXNIABI+CgptYW5hZ2VtZW50GAkgASgLMigua3VzaW50YS5pb3Qud2VicnRjLnYxLk1hbmFnZW1lbnRSZXF1ZXN0SAASRwoPYXR0cmlidXRlX3dyaXRlGAogASgLMiwua3VzaW50YS5pb3Qud2VicnRjLnYxLkF0dHJpYnV0ZVdyaXRlUmVxdWVzdEgAEjwKDXN0YXJ0X3BhaXJpbmcYCyABKAsyIy5rdXNpbnRhLmlvdC53ZWJydGMudjEuU3RhcnRQYWlyaW5nSABCCQoHcGF5bG9hZCr2AQoQR2F0ZXdheUVycm9yQ29kZRIiCh5HQVRFV0FZX0VSUk9SX0NPREVfVU5TUEVDSUZJRUQQABIjCh9HQVRFV0FZX0VSUk9SX0NPREVfTk9UX0VOVElUTEVEEAESJgoiR0FURVdBWV9FUlJPUl9DT0RFX0lOVkFMSURfUkVRVUVTVBACEiIKHkdBVEVXQVlfRVJST1JfQ09ERV9VTkFWQUlMQUJMRRADEh8KG0dBVEVXQVlfRVJST1JfQ09ERV9JTlRFUk5BTBAEEiwKKEdBVEVXQVlfRVJST1JfQ09ERV9TRVNTSU9OX0xJTUlUX1JFQUNIRUQQBUICSAFiBnByb3RvMw", [file_google_protobuf_timestamp, file_kusinta_iot_access_v1_acl, file_kusinta_iot_common_v1_pairing, file_kusinta_iot_common_v1_types, file_kusinta_iot_device_v1_device_event, file_kusinta_iot_identity_v1_identity, file_kusinta_iot_space_v1_space, file_kusinta_iot_webrtc_v1_command, file_kusinta_iot_webrtc_v1_device_state, file_kusinta_iot_webrtc_v1_management, file_kusinta_iot_webrtc_v1_permission_push]);
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Describes the message kusinta.iot.webrtc.v1.Ping.
|
|
@@ -96,19 +98,40 @@ export const GatewayErrorSchema = /*@__PURE__*/
|
|
|
96
98
|
export const ManagementResultSchema = /*@__PURE__*/
|
|
97
99
|
messageDesc(file_kusinta_iot_webrtc_v1_envelope, 10);
|
|
98
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Describes the message kusinta.iot.webrtc.v1.StartPairing.
|
|
103
|
+
* Use `create(StartPairingSchema)` to create a new message.
|
|
104
|
+
*/
|
|
105
|
+
export const StartPairingSchema = /*@__PURE__*/
|
|
106
|
+
messageDesc(file_kusinta_iot_webrtc_v1_envelope, 11);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Describes the message kusinta.iot.webrtc.v1.PairingStarted.
|
|
110
|
+
* Use `create(PairingStartedSchema)` to create a new message.
|
|
111
|
+
*/
|
|
112
|
+
export const PairingStartedSchema = /*@__PURE__*/
|
|
113
|
+
messageDesc(file_kusinta_iot_webrtc_v1_envelope, 12);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Describes the message kusinta.iot.webrtc.v1.PairingFinished.
|
|
117
|
+
* Use `create(PairingFinishedSchema)` to create a new message.
|
|
118
|
+
*/
|
|
119
|
+
export const PairingFinishedSchema = /*@__PURE__*/
|
|
120
|
+
messageDesc(file_kusinta_iot_webrtc_v1_envelope, 13);
|
|
121
|
+
|
|
99
122
|
/**
|
|
100
123
|
* Describes the message kusinta.iot.webrtc.v1.GatewayMessage.
|
|
101
124
|
* Use `create(GatewayMessageSchema)` to create a new message.
|
|
102
125
|
*/
|
|
103
126
|
export const GatewayMessageSchema = /*@__PURE__*/
|
|
104
|
-
messageDesc(file_kusinta_iot_webrtc_v1_envelope,
|
|
127
|
+
messageDesc(file_kusinta_iot_webrtc_v1_envelope, 14);
|
|
105
128
|
|
|
106
129
|
/**
|
|
107
130
|
* Describes the message kusinta.iot.webrtc.v1.AppMessage.
|
|
108
131
|
* Use `create(AppMessageSchema)` to create a new message.
|
|
109
132
|
*/
|
|
110
133
|
export const AppMessageSchema = /*@__PURE__*/
|
|
111
|
-
messageDesc(file_kusinta_iot_webrtc_v1_envelope,
|
|
134
|
+
messageDesc(file_kusinta_iot_webrtc_v1_envelope, 15);
|
|
112
135
|
|
|
113
136
|
/**
|
|
114
137
|
* Describes the enum kusinta.iot.webrtc.v1.GatewayErrorCode.
|
|
@@ -265,8 +265,18 @@ export declare type ClaimDevice = Message<"kusinta.iot.webrtc.v1.ClaimDevice"> &
|
|
|
265
265
|
initialSpaceId?: SpaceId | undefined;
|
|
266
266
|
|
|
267
267
|
/**
|
|
268
|
-
* Evidence the claimant is standing at the device: the
|
|
269
|
-
* matched against DeviceDescriptor.serial_number.
|
|
268
|
+
* Evidence the claimant is standing at the device: the identifier printed on it,
|
|
269
|
+
* matched against DeviceDescriptor.serial_number.
|
|
270
|
+
*
|
|
271
|
+
* The match is by SUFFIX, because what a connector announces is not always the whole
|
|
272
|
+
* of what is printed and the announced value is commonly the tail of the printed one.
|
|
273
|
+
* Both sides are normalised first — lowercased, with whitespace and ASCII hyphens
|
|
274
|
+
* removed, since a label prints the value in groups and a scan does not — and the proof
|
|
275
|
+
* is accepted when what remains ends with the announced serial. Spelled out because two
|
|
276
|
+
* gateways normalising differently would accept different proofs for the same device.
|
|
277
|
+
*
|
|
278
|
+
* A serial shorter than eight characters cannot be proved at all: under a suffix rule a
|
|
279
|
+
* short one is proved by almost anything ending with it. Required precisely when the
|
|
270
280
|
* caller cannot already reach the device, which is the ordinary case for a
|
|
271
281
|
* resident claiming something they just bought — an unfiled device is invisible
|
|
272
282
|
* to them, so possession is the only thing left that can distinguish them from
|