@aceplus/smartace-api 2.6.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +309 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Guide
|
|
2
|
+
|
|
3
|
+
To use the SmartAce Api in node you will need the following dependencies
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
"script": {
|
|
7
|
+
"dev": "npx tsx index.ts"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@improbable-eng/grpc-web": "^0.15.0",
|
|
11
|
+
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
|
|
12
|
+
"google-protobuf": "^3.21.2"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/google-protobuf": "^3.15.6",
|
|
16
|
+
"ts-node": "^10.9.2",
|
|
17
|
+
"tsx": "^4.19.2"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Auth
|
|
23
|
+
|
|
24
|
+
Authentication happens on the auth cluster which is located on the following URL
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
var AuthAddress = "https://backend-auth.smart-ace.com";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Login
|
|
31
|
+
|
|
32
|
+
To authenticate login with valid credentials
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { grpc } from "@improbable-eng/grpc-web";
|
|
36
|
+
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
|
37
|
+
import { AuthnProto } from "@aceplus/smartace-api/auth.authn_pb";
|
|
38
|
+
import { AuthnGRPC } from "@aceplus/smartace-api/auth.authn_pb_service";
|
|
39
|
+
|
|
40
|
+
function login(username: string, password: string): Promise<AuthnProto.AsObject> {
|
|
41
|
+
return new Promise(async (resolve, reject) => {
|
|
42
|
+
let request = new AuthnProto.LoginRequest();
|
|
43
|
+
|
|
44
|
+
request.setUsername(username);
|
|
45
|
+
request.setPassword(password);
|
|
46
|
+
|
|
47
|
+
grpc.invoke(AuthnGRPC.Login, {
|
|
48
|
+
request,
|
|
49
|
+
transport: NodeHttpTransport(),
|
|
50
|
+
host: AuthAddress,
|
|
51
|
+
onMessage: (res) => {
|
|
52
|
+
resolve(res.toObject() as AuthnProto.AsObject);
|
|
53
|
+
},
|
|
54
|
+
onEnd: (code, message) => {
|
|
55
|
+
if (code === 0) {
|
|
56
|
+
console.log("Ok");
|
|
57
|
+
} else {
|
|
58
|
+
console.log({ code, message });
|
|
59
|
+
reject({ code, message });
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var auth = await login("test@email.com", "password"); // fill in your credentials here
|
|
67
|
+
|
|
68
|
+
console.log(auth.accessToken);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Refresh
|
|
72
|
+
|
|
73
|
+
Logging in gives you a accessToken that is valid for a certain amount of time. (1 hour by default)
|
|
74
|
+
|
|
75
|
+
To keep the session open, use the refresh token
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { grpc } from "@improbable-eng/grpc-web";
|
|
79
|
+
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
|
80
|
+
import { AuthnProto } from "@aceplus/smartace-api/auth.authn_pb";
|
|
81
|
+
import { AuthnGRPC } from "@aceplus/smartace-api/auth.authn_pb_service";
|
|
82
|
+
|
|
83
|
+
function refresh(accessToken: string, refreshToken: string): Promise<AuthnProto.AsObject> {
|
|
84
|
+
return new Promise(async (resolve, reject) => {
|
|
85
|
+
let request = new AuthnProto.RefreshRequest();
|
|
86
|
+
|
|
87
|
+
request.setAccessToken(accessToken);
|
|
88
|
+
request.setRefreshToken(refreshToken);
|
|
89
|
+
|
|
90
|
+
grpc.invoke(AuthnGRPC.Refresh, {
|
|
91
|
+
request,
|
|
92
|
+
transport: NodeHttpTransport(),
|
|
93
|
+
host: AuthAddress,
|
|
94
|
+
onMessage: (res) => {
|
|
95
|
+
resolve(res.toObject() as AuthnProto.AsObject);
|
|
96
|
+
},
|
|
97
|
+
onEnd: (code, message) => {
|
|
98
|
+
if (code === 0) {
|
|
99
|
+
console.log("Ok");
|
|
100
|
+
} else {
|
|
101
|
+
console.log({ code, message });
|
|
102
|
+
reject({ code, message });
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
var auth = await login("test@email.com", "password");
|
|
110
|
+
|
|
111
|
+
auth = await refresh(auth.accessToken, auth.refreshToken);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Spic
|
|
115
|
+
|
|
116
|
+
To access the spic data, we need to access the operator cluster on the following URL
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
var OperatorAddress = "https://backend-dev.smart-ace.com";
|
|
120
|
+
|
|
121
|
+
// grpc call helper function
|
|
122
|
+
function BaseOperatorRequest<TRequest extends grpc.ProtobufMessage, TResponse extends grpc.ProtobufMessage, TMethod extends grpc.MethodDefinition<TRequest, TResponse>>(
|
|
123
|
+
accessToken: string,
|
|
124
|
+
method: TMethod,
|
|
125
|
+
request: TRequest
|
|
126
|
+
): Promise<TResponse> {
|
|
127
|
+
return new Promise(async (resolve, reject) => {
|
|
128
|
+
// set accesssToken in header for authorization
|
|
129
|
+
const metadata = new grpc.Metadata();
|
|
130
|
+
metadata.set("authorization", "Bearer " + accessToken);
|
|
131
|
+
|
|
132
|
+
grpc.invoke(method, {
|
|
133
|
+
metadata,
|
|
134
|
+
request,
|
|
135
|
+
host: OperatorAddress,
|
|
136
|
+
transport: NodeHttpTransport(), // required for Node
|
|
137
|
+
onMessage: (res) => {
|
|
138
|
+
resolve(res as TResponse);
|
|
139
|
+
},
|
|
140
|
+
onEnd: (code, message) => {
|
|
141
|
+
if (code === 0) {
|
|
142
|
+
console.log("Ok");
|
|
143
|
+
} else {
|
|
144
|
+
reject({ code, message });
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Search Cabinets
|
|
153
|
+
|
|
154
|
+
Searching cabinets with pagination
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
function searchCabinets(accessToken: string, name: string, count: number, page: number): Promise<CabinetProto.SearchReply> {
|
|
158
|
+
return new Promise(async (resolve, reject) => {
|
|
159
|
+
const request = new CabinetProto.SearchRequest();
|
|
160
|
+
name && name.length >= 3 && request.setName(name);
|
|
161
|
+
request.setCount(count);
|
|
162
|
+
request.setPage(page);
|
|
163
|
+
|
|
164
|
+
BaseOperatorRequest(accessToken, CabinetGRPC.Search, request)
|
|
165
|
+
.then((res) => {
|
|
166
|
+
resolve(res as CabinetProto.SearchReply);
|
|
167
|
+
})
|
|
168
|
+
.catch((err) => reject(err));
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
var cabinets = await searchCabinets(auth.accessToken, "", 10, 0);
|
|
173
|
+
|
|
174
|
+
console.log(cabinets.toObject());
|
|
175
|
+
let firstCabinet = cabinets.getCabinetsList()[0];
|
|
176
|
+
|
|
177
|
+
console.log(firstCabinet?.toObject());
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Retrieve cabinet
|
|
181
|
+
|
|
182
|
+
Or retrieve cabinet by id or Uid
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
function retrieveCabinetById(accessToken: string, id: number): Promise<CabinetWithLocation> {
|
|
186
|
+
return new Promise(async (resolve, reject) => {
|
|
187
|
+
const request = new CabinetProto.RetrieveRequest();
|
|
188
|
+
request.setId(id);
|
|
189
|
+
|
|
190
|
+
BaseOperatorRequest(accessToken, CabinetGRPC.Retrieve, request)
|
|
191
|
+
.then((res) => {
|
|
192
|
+
resolve(res as CabinetWithLocation);
|
|
193
|
+
})
|
|
194
|
+
.catch((err) => reject(err));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function retrieveCabinetByUid(accessToken: string, uid: string): Promise<CabinetWithLocation> {
|
|
199
|
+
return new Promise(async (resolve, reject) => {
|
|
200
|
+
const request = new CabinetProto.RetrieveRequest();
|
|
201
|
+
request.setUid(uid);
|
|
202
|
+
|
|
203
|
+
BaseOperatorRequest(accessToken, CabinetGRPC.Retrieve, request)
|
|
204
|
+
.then((res) => {
|
|
205
|
+
resolve(res as CabinetWithLocation);
|
|
206
|
+
})
|
|
207
|
+
.catch((err) => reject(err));
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let cabinet1 = await retrieveCabinetById(auth.accessToken, 1);
|
|
212
|
+
let cabinet2 = await retrieveCabinetByUid(auth.accessToken, "5424346F37680424");
|
|
213
|
+
|
|
214
|
+
console.log(cabinet2.toObject());
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Retrieve events
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
function retrieveEvents(accessToken: string, cabinetId: number, count: number, page: number, descendingOrder: boolean): Promise<CabinetProto.Event.SearchReply> {
|
|
221
|
+
return new Promise(async (resolve, reject) => {
|
|
222
|
+
const request = new CabinetProto.Event.SearchRequest();
|
|
223
|
+
|
|
224
|
+
request.setCabinetId(cabinetId);
|
|
225
|
+
request.setCount(count);
|
|
226
|
+
request.setPage(page);
|
|
227
|
+
request.setDescorder(descendingOrder);
|
|
228
|
+
|
|
229
|
+
BaseOperatorRequest(accessToken, CabinetGRPC.SearchEvents, request)
|
|
230
|
+
.then((res) => {
|
|
231
|
+
resolve(res as CabinetProto.Event.SearchReply);
|
|
232
|
+
})
|
|
233
|
+
.catch((err) => reject(err));
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
let events = await retrieveEvents(auth.accessToken, cabinet2.getCabinet()!.getId(), 10, 0, false);
|
|
237
|
+
|
|
238
|
+
console.log(events.toObject());
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Retrieve states
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
function retrieveStates(accessToken: string, uid: string, count: number, page: number, descendingOrder: boolean): Promise<CabinetProto.State.SearchReply> {
|
|
245
|
+
return new Promise(async (resolve, reject) => {
|
|
246
|
+
const request = new CabinetProto.State.SearchRequest();
|
|
247
|
+
|
|
248
|
+
request.setUid(uid);
|
|
249
|
+
request.setCount(count);
|
|
250
|
+
request.setPage(page);
|
|
251
|
+
request.setDescorder(descendingOrder);
|
|
252
|
+
|
|
253
|
+
request.setBatteryTo(4000);
|
|
254
|
+
request.setTemperatureTo(100);
|
|
255
|
+
|
|
256
|
+
BaseOperatorRequest(accessToken, CabinetGRPC.SearchStates, request)
|
|
257
|
+
.then((res) => {
|
|
258
|
+
resolve(res as CabinetProto.State.SearchReply);
|
|
259
|
+
})
|
|
260
|
+
.catch((err) => reject(err));
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
let states = await retrieveStates(auth.accessToken, cabinet2.getCabinet()!.getUid(), 10, 0, false);
|
|
264
|
+
|
|
265
|
+
console.log(states.toObject());
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Opening a cabinet
|
|
269
|
+
|
|
270
|
+
To open a cabinet you must create a openrequest.
|
|
271
|
+
|
|
272
|
+
Here you have option for how long the opening is valid and if it is a single use or can be used multiple times withing the period
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
function Open(accessToken: string, cabinetId: number): Promise<CabinetProto.OpenRequest> {
|
|
276
|
+
return new Promise(async (resolve, reject) => {
|
|
277
|
+
const request = new CabinetProto.OpenRequest.CreateRequest();
|
|
278
|
+
|
|
279
|
+
request.setCabinetId(cabinetId);
|
|
280
|
+
|
|
281
|
+
function DateToTimestamp(date: Date): Timestamp {
|
|
282
|
+
let ts = new Timestamp();
|
|
283
|
+
let seconds = Math.round(date.getTime() / 1000);
|
|
284
|
+
ts.setSeconds(seconds);
|
|
285
|
+
return ts;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
var now = new Date(Date.now());
|
|
289
|
+
request.setRequestTime(DateToTimestamp(now));
|
|
290
|
+
|
|
291
|
+
var expirationDate = new Date(Date.now());
|
|
292
|
+
expirationDate.setTime(expirationDate.getTime() + 60 * 60 * 1000);
|
|
293
|
+
|
|
294
|
+
request.setValidUntil(DateToTimestamp(expirationDate));
|
|
295
|
+
|
|
296
|
+
request.setType(CabinetProto.OpenRequest.OpenRequestType.ADMIN_OPENING);
|
|
297
|
+
|
|
298
|
+
request.setDeactivateafteropening(true);
|
|
299
|
+
|
|
300
|
+
BaseOperatorRequest(accessToken, CabinetGRPC.CreateOpenRequest, request)
|
|
301
|
+
.then((res) => {
|
|
302
|
+
resolve(res as CabinetProto.OpenRequest);
|
|
303
|
+
})
|
|
304
|
+
.catch((err) => reject(err));
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let openRequest = await Open(auth.accessToken, cabinet2.getCabinet()!.getId()).catch((err) => console.log(err));
|
|
309
|
+
```
|