rhombus-node-mcp 0.1.56 → 0.1.58
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/dist/api/access-control-tool-api.js +231 -32
- package/dist/api/alarm-monitoring-tool-api.js +50 -1
- package/dist/api/automated-prompts-tool-api.js +9 -17
- package/dist/api/camera-policy-tool-api.js +122 -0
- package/dist/api/camera-tool-api.js +3 -5
- package/dist/api/camera-uptime-tool-api.js +62 -25
- package/dist/api/clips-tool-api.js +45 -9
- package/dist/api/create-tool-api.js +55 -7
- package/dist/api/door-tool-api.js +74 -13
- package/dist/api/entity-lookup-tool-api.js +8 -1
- package/dist/api/events-tool-api.js +79 -6
- package/dist/api/faces-tool-api.js +112 -7
- package/dist/api/get-accessible-apps.js +68 -6
- package/dist/api/get-entity-tool-api.js +12 -12
- package/dist/api/get-org-information-tool-api.js +2 -2
- package/dist/api/guest-management-tool-api.js +75 -1
- package/dist/api/location-tool-api.js +120 -28
- package/dist/api/lpr-tool-api.js +71 -16
- package/dist/api/policy-alerts-tool-api.js +7 -3
- package/dist/api/report-tool-api.js +4 -1
- package/dist/api/rules-tool-api.js +6 -16
- package/dist/api/schedule-tool-api.js +107 -0
- package/dist/api/search-tool-api.js +5 -13
- package/dist/api/update-tool-api.js +96 -1
- package/dist/api/user-tool-api.js +58 -13
- package/dist/createServer.js +18 -6
- package/dist/filtering-utils.js +421 -29
- package/dist/index.js +1 -1
- package/dist/network/network.js +122 -10
- package/dist/network/org-reference-cache.js +74 -0
- package/dist/telemetry/tracingProxy.js +4 -0
- package/dist/tools/entity-lookup-tool.js +6 -4
- package/dist/tools/get-entity-tool.js +40 -20
- package/dist/tools/get-org-information-tool.js +30 -2
- package/dist/tools/time-conversion-tool.js +59 -36
- package/dist/tools/time-tool.js +12 -3
- package/dist/tools/user-tool.js +100 -2
- package/dist/tools-console/access-anomaly-tool.js +2 -19
- package/dist/tools-console/access-control-tool.js +328 -2
- package/dist/tools-console/alarm-monitoring-tool.js +72 -2
- package/dist/tools-console/analytics-tool.js +2 -2
- package/dist/tools-console/badge-timeline-tool.js +2 -21
- package/dist/tools-console/camera-policy-tool.js +252 -0
- package/dist/tools-console/camera-tool.js +111 -89
- package/dist/tools-console/clips-tool.js +21 -31
- package/dist/tools-console/count-tool.js +8 -0
- package/dist/tools-console/create-camera-policy-tool.js +302 -122
- package/dist/tools-console/door-schedule-exception-tool.js +2 -33
- package/dist/tools-console/door-tool.js +130 -2
- package/dist/tools-console/elements-access-anomaly-tool.js +2 -19
- package/dist/tools-console/elements-badge-timeline-tool.js +2 -21
- package/dist/tools-console/elements-lost-badge-tool.js +2 -17
- package/dist/tools-console/elements-tool.js +2 -29
- package/dist/tools-console/events-tool.js +22 -101
- package/dist/tools-console/faces-tool.js +254 -28
- package/dist/tools-console/guest-management-tool.js +127 -2
- package/dist/tools-console/location-tool.js +156 -35
- package/dist/tools-console/lost-badge-tool.js +2 -17
- package/dist/tools-console/lpr-tool.js +89 -23
- package/dist/tools-console/netbox-access-anomaly-tool.js +2 -19
- package/dist/tools-console/netbox-badge-timeline-tool.js +2 -21
- package/dist/tools-console/netbox-lost-badge-tool.js +2 -17
- package/dist/tools-console/netbox-tool.js +2 -29
- package/dist/tools-console/onguard-tool.js +2 -30
- package/dist/tools-console/person-tracking-tool.js +2 -2
- package/dist/tools-console/policy-alerts-tool.js +5 -29
- package/dist/tools-console/reboot-cameras-tool.js +17 -2
- package/dist/tools-console/report-tool.js +11 -41
- package/dist/tools-console/schedule-tool.js +190 -0
- package/dist/tools-console/update-tool.js +298 -168
- package/dist/tools-console/video-walls-tool.js +93 -21
- package/dist/types/access-anomaly-tool-types.js +22 -2
- package/dist/types/access-control-tool-types.js +116 -1
- package/dist/types/alarm-monitoring-tool-types.js +42 -1
- package/dist/types/badge-timeline-tool-types.js +18 -3
- package/dist/types/camera-policy-tool-types.js +104 -0
- package/dist/types/camera-tool-types.js +80 -1
- package/dist/types/clips-tool-types.js +27 -3
- package/dist/types/create-camera-policy-tool-types.js +275 -26
- package/dist/types/door-schedule-exception-tool-types.js +20 -6
- package/dist/types/door-tool-types.js +36 -4
- package/dist/types/entity-lookup-tool-types.js +6 -1
- package/dist/types/events-tools-types.js +24 -16
- package/dist/types/faces-tools-types.js +106 -3
- package/dist/types/get-entity-tool-types.js +76 -12
- package/dist/types/guest-management-tool-types.js +103 -1
- package/dist/types/location-tool-types.js +100 -14
- package/dist/types/lost-badge-tool-types.js +17 -2
- package/dist/types/lpr-tool-types.js +47 -8
- package/dist/types/onguard-tool-types.js +21 -1
- package/dist/types/policy-alerts-tool-types.js +1 -1
- package/dist/types/report-tool-types.js +29 -1
- package/dist/types/schedule-tool-types.js +370 -0
- package/dist/types/schema.js +479 -9
- package/dist/types/time-conversion-tool-types.js +3 -4
- package/dist/types/update-tool-types.js +40 -18
- package/dist/types/user-tool-types.js +44 -1
- package/dist/types/video-walls-tool-types.js +30 -2
- package/dist/types/zod-schemas.js +5128 -996
- package/dist/util.js +62 -3
- package/dist/utils/entity-name-match.js +11 -0
- package/package.json +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { postApi } from "../network/network.js";
|
|
1
|
+
import { apiWarning, postApi, throwIfApiError } from "../network/network.js";
|
|
2
2
|
import { formatTimestamp } from "../util.js";
|
|
3
3
|
import { removeNulls } from "../utils/remove-nulls.js";
|
|
4
4
|
export async function getFaceEvents(args, timeZone, requestModifiers, sessionId) {
|
|
@@ -78,8 +78,7 @@ export async function searchSimilarFaces(faceEventUuid, timeZone, requestModifie
|
|
|
78
78
|
modifiers: requestModifiers,
|
|
79
79
|
sessionId,
|
|
80
80
|
});
|
|
81
|
-
|
|
82
|
-
throw new Error(JSON.stringify(res));
|
|
81
|
+
throwIfApiError(res);
|
|
83
82
|
return (res.faceEvents || []).map((event) => ({
|
|
84
83
|
uuid: event.uuid ?? undefined,
|
|
85
84
|
deviceUuid: event.deviceUuid ?? undefined,
|
|
@@ -98,8 +97,7 @@ export async function getFaceMatchmakers(requestModifiers, sessionId) {
|
|
|
98
97
|
modifiers: requestModifiers,
|
|
99
98
|
sessionId,
|
|
100
99
|
});
|
|
101
|
-
|
|
102
|
-
throw new Error(JSON.stringify(res));
|
|
100
|
+
throwIfApiError(res);
|
|
103
101
|
return (res.faceMatchmakers || []).map((m) => ({
|
|
104
102
|
uuid: m.uuid ?? undefined,
|
|
105
103
|
personUuid: m.personUuid ?? undefined,
|
|
@@ -122,8 +120,7 @@ export async function getFaceEventsByPerson(personUuid, timeZone, requestModifie
|
|
|
122
120
|
modifiers: requestModifiers,
|
|
123
121
|
sessionId,
|
|
124
122
|
});
|
|
125
|
-
|
|
126
|
-
throw new Error(JSON.stringify(res));
|
|
123
|
+
throwIfApiError(res);
|
|
127
124
|
const faceEvents = (res.faceEvents || []).map((event) => ({
|
|
128
125
|
uuid: event.uuid ?? undefined,
|
|
129
126
|
personUuid: event.personUuid ?? undefined,
|
|
@@ -137,3 +134,111 @@ export async function getFaceEventsByPerson(personUuid, timeZone, requestModifie
|
|
|
137
134
|
lastEvaluatedKey: res.lastEvaluatedKey,
|
|
138
135
|
};
|
|
139
136
|
}
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Person write paths
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
export async function findPerson(personUuid, requestModifiers, sessionId) {
|
|
141
|
+
const res = await postApi({
|
|
142
|
+
route: "/faceRecognition/person/findPeopleByOrg",
|
|
143
|
+
body: {},
|
|
144
|
+
modifiers: requestModifiers,
|
|
145
|
+
sessionId,
|
|
146
|
+
});
|
|
147
|
+
throwIfApiError(res);
|
|
148
|
+
return res.people?.find(person => person?.uuid === personUuid) ?? undefined;
|
|
149
|
+
}
|
|
150
|
+
export async function createPerson(name, requestModifiers, sessionId) {
|
|
151
|
+
const res = await postApi({
|
|
152
|
+
route: "/faceRecognition/person/createPerson",
|
|
153
|
+
body: { name },
|
|
154
|
+
modifiers: requestModifiers,
|
|
155
|
+
sessionId,
|
|
156
|
+
});
|
|
157
|
+
throwIfApiError(res);
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
uuid: res.person?.uuid ?? undefined,
|
|
161
|
+
warningMsg: apiWarning(res),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Selective person update.
|
|
166
|
+
*
|
|
167
|
+
* api2 marks which fields were deliberately set via `updatedSetMethodMap`
|
|
168
|
+
* rather than inferring it from presence — so a field omitted from that map is
|
|
169
|
+
* left alone, and a field IN the map with no value is cleared. Only the fields
|
|
170
|
+
* the caller actually changed are flagged.
|
|
171
|
+
*
|
|
172
|
+
* The keys are SETTER names (`setName`, `setEmail`), NOT field names. The spec
|
|
173
|
+
* declares the map as an open `{string: boolean}`, so nothing enforces this.
|
|
174
|
+
* Sending `{name: true}` returns `error: false` and bumps the record's
|
|
175
|
+
* `updatedOn` while leaving the name unchanged — a silent no-op that reports
|
|
176
|
+
* success. Verified against api2.itg on 2026-08-05; pinned by
|
|
177
|
+
* `test/api/faces-tool-api.test.ts`.
|
|
178
|
+
*/
|
|
179
|
+
export async function updatePerson(personUuid, changes, requestModifiers, sessionId) {
|
|
180
|
+
const updatedSetMethodMap = {};
|
|
181
|
+
if (changes.name !== undefined)
|
|
182
|
+
updatedSetMethodMap.setName = true;
|
|
183
|
+
if (changes.email !== undefined)
|
|
184
|
+
updatedSetMethodMap.setEmail = true;
|
|
185
|
+
const res = await postApi({
|
|
186
|
+
route: "/faceRecognition/person/updatePerson",
|
|
187
|
+
body: {
|
|
188
|
+
personSelectiveUpdate: {
|
|
189
|
+
uuid: personUuid,
|
|
190
|
+
name: changes.name,
|
|
191
|
+
email: changes.email,
|
|
192
|
+
updatedSetMethodMap,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
modifiers: requestModifiers,
|
|
196
|
+
sessionId,
|
|
197
|
+
});
|
|
198
|
+
throwIfApiError(res);
|
|
199
|
+
return { success: true, uuid: personUuid, warningMsg: apiWarning(res) };
|
|
200
|
+
}
|
|
201
|
+
export async function deletePerson(personUuid, requestModifiers, sessionId) {
|
|
202
|
+
const res = await postApi({
|
|
203
|
+
route: "/faceRecognition/person/deletePerson",
|
|
204
|
+
body: {
|
|
205
|
+
personUuid,
|
|
206
|
+
},
|
|
207
|
+
modifiers: requestModifiers,
|
|
208
|
+
sessionId,
|
|
209
|
+
});
|
|
210
|
+
throwIfApiError(res);
|
|
211
|
+
return { success: true, uuid: personUuid, warningMsg: apiWarning(res) };
|
|
212
|
+
}
|
|
213
|
+
export async function changePersonLabel(personUuid, label, action, requestModifiers, sessionId) {
|
|
214
|
+
const res = await postApi({
|
|
215
|
+
route: action === "add"
|
|
216
|
+
? "/faceRecognition/person/addPersonLabel"
|
|
217
|
+
: "/faceRecognition/person/removePersonLabel",
|
|
218
|
+
body: {
|
|
219
|
+
personUuid,
|
|
220
|
+
label,
|
|
221
|
+
},
|
|
222
|
+
modifiers: requestModifiers,
|
|
223
|
+
sessionId,
|
|
224
|
+
});
|
|
225
|
+
throwIfApiError(res);
|
|
226
|
+
return { success: true, uuid: personUuid, label, warningMsg: apiWarning(res) };
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Delete one enrolled face image. Keyed on `faceId`, NOT on a person uuid — a
|
|
230
|
+
* person can have several matchmakers, and deleting one only removes that
|
|
231
|
+
* enrolled image, leaving the person and their other faces intact.
|
|
232
|
+
*/
|
|
233
|
+
export async function deleteFaceMatchmaker(faceId, requestModifiers, sessionId) {
|
|
234
|
+
const res = await postApi({
|
|
235
|
+
route: "/faceRecognition/matchmaker/deleteFaceMatchmaker",
|
|
236
|
+
body: {
|
|
237
|
+
faceId,
|
|
238
|
+
},
|
|
239
|
+
modifiers: requestModifiers,
|
|
240
|
+
sessionId,
|
|
241
|
+
});
|
|
242
|
+
throwIfApiError(res);
|
|
243
|
+
return { success: true, faceId, warningMsg: apiWarning(res) };
|
|
244
|
+
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { logger } from "../logger.js";
|
|
2
2
|
import { postApi } from "../network/network.js";
|
|
3
|
+
import { RhombusAppEnum } from "../types/schema.js";
|
|
4
|
+
/**
|
|
5
|
+
* `enduser.id` for a support session whose payload carries no `supportSession`
|
|
6
|
+
* block (webservice older than the additive `sessionType`/`supportSession` fields).
|
|
7
|
+
*/
|
|
8
|
+
export const SUPPORT_SESSION_MARKER = "support-session";
|
|
3
9
|
/**
|
|
4
10
|
* Single cached `getCurrentUser` fetch per session. Both `resolveAccessibleApps`
|
|
5
11
|
* and `resolveSessionIdentity` read from this cache, so identity for tracing
|
|
@@ -7,6 +13,38 @@ import { postApi } from "../network/network.js";
|
|
|
7
13
|
* the session; failures are NOT cached so transient errors don't poison it.
|
|
8
14
|
*/
|
|
9
15
|
const cache = new Map();
|
|
16
|
+
function optionalString(value) {
|
|
17
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* `sessionType` and `supportSession` are ADDITIVE fields on
|
|
21
|
+
* GetCurrentUserWSResponse (webservice, SUPPORT_AUTHORITY_MIND C.4). They are
|
|
22
|
+
* not in the generated client type until the openapi is regenerated, so they
|
|
23
|
+
* are read off the raw payload and tolerated when absent.
|
|
24
|
+
*/
|
|
25
|
+
function readSupportFields(res) {
|
|
26
|
+
if (!res || typeof res !== "object")
|
|
27
|
+
return {};
|
|
28
|
+
const raw = res;
|
|
29
|
+
const sessionType = optionalString(raw.sessionType);
|
|
30
|
+
const ss = raw.supportSession;
|
|
31
|
+
if (!ss || typeof ss !== "object")
|
|
32
|
+
return { sessionType };
|
|
33
|
+
const s = ss;
|
|
34
|
+
return {
|
|
35
|
+
sessionType,
|
|
36
|
+
supportSession: {
|
|
37
|
+
authorityUuid: optionalString(s.authorityUuid),
|
|
38
|
+
name: optionalString(s.name),
|
|
39
|
+
email: optionalString(s.email),
|
|
40
|
+
orgUuid: optionalString(s.orgUuid),
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function describeIdentity(identity) {
|
|
45
|
+
const who = [identity.name, identity.email].filter(Boolean).join(" ");
|
|
46
|
+
return `${who ? `${who} ` : ""}(${identity.userId ?? "no id"})`;
|
|
47
|
+
}
|
|
10
48
|
async function fetchSession(sessionId) {
|
|
11
49
|
const cached = cache.get(sessionId);
|
|
12
50
|
if (cached !== undefined)
|
|
@@ -22,11 +60,31 @@ async function fetchSession(sessionId) {
|
|
|
22
60
|
return null;
|
|
23
61
|
}
|
|
24
62
|
const user = res.user;
|
|
25
|
-
const
|
|
63
|
+
const { sessionType, supportSession } = readSupportFields(res);
|
|
64
|
+
// A successful getCurrentUser with no `user` is a support-authority session
|
|
65
|
+
// (the webservice never populates `user` for a SupportUserPrincipal). Such a
|
|
66
|
+
// session acts inside the customer's console, so it gets exactly the
|
|
67
|
+
// CONSOLE tool set — never the permissive console+partner union that an
|
|
68
|
+
// empty accessibleRhombusApps list used to fall into.
|
|
69
|
+
if (sessionType === "SUPPORT" || user === null || user === undefined) {
|
|
70
|
+
const identity = {
|
|
71
|
+
userId: supportSession?.authorityUuid ?? SUPPORT_SESSION_MARKER,
|
|
72
|
+
orgUuid: supportSession?.orgUuid,
|
|
73
|
+
email: supportSession?.email,
|
|
74
|
+
name: supportSession?.name,
|
|
75
|
+
sessionType: "SUPPORT",
|
|
76
|
+
};
|
|
77
|
+
const entry = { apps: [RhombusAppEnum.CONSOLE], identity };
|
|
78
|
+
cache.set(sessionId, entry);
|
|
79
|
+
logger.info(`resolveAccessibleApps: session ${sessionId} -> support session ${describeIdentity(identity)}; tool set=CONSOLE`);
|
|
80
|
+
return entry;
|
|
81
|
+
}
|
|
82
|
+
const apps = (user.accessibleRhombusApps ?? []).filter((a) => a !== null && a !== undefined);
|
|
26
83
|
const identity = {
|
|
27
|
-
userId: user
|
|
28
|
-
orgUuid: user
|
|
29
|
-
email: user
|
|
84
|
+
userId: user.uuid ?? user.rhombusUserUuid ?? undefined,
|
|
85
|
+
orgUuid: user.orgUuid ?? undefined,
|
|
86
|
+
email: user.email ?? undefined,
|
|
87
|
+
sessionType: "USER",
|
|
30
88
|
};
|
|
31
89
|
const entry = { apps, identity };
|
|
32
90
|
cache.set(sessionId, entry);
|
|
@@ -40,6 +98,7 @@ async function fetchSession(sessionId) {
|
|
|
40
98
|
}
|
|
41
99
|
/**
|
|
42
100
|
* Fetches `user.accessibleRhombusApps` from getCurrentUser for the given session.
|
|
101
|
+
* Support-authority sessions resolve to exactly `[CONSOLE]`.
|
|
43
102
|
* Returns null on error or missing session; callers should fall back to a
|
|
44
103
|
* permissive default.
|
|
45
104
|
*/
|
|
@@ -49,8 +108,11 @@ export async function resolveAccessibleApps(sessionId) {
|
|
|
49
108
|
return (await fetchSession(sessionId))?.apps ?? null;
|
|
50
109
|
}
|
|
51
110
|
/**
|
|
52
|
-
* Fetches identity (user uuid, org uuid, email) from
|
|
53
|
-
* given session, reusing the same cached response as
|
|
111
|
+
* Fetches identity (user uuid, org uuid, email, session type) from
|
|
112
|
+
* getCurrentUser for the given session, reusing the same cached response as
|
|
113
|
+
* `resolveAccessibleApps`. For support sessions the identity comes from
|
|
114
|
+
* `supportSession` (authorityUuid / name / email) when the webservice sends it,
|
|
115
|
+
* else `userId` is the fixed {@link SUPPORT_SESSION_MARKER}.
|
|
54
116
|
* Returns null on error or missing session.
|
|
55
117
|
*/
|
|
56
118
|
export async function resolveSessionIdentity(sessionId) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { logger } from "../logger.js";
|
|
2
|
-
import {
|
|
2
|
+
import { cachedPostApi } from "../network/org-reference-cache.js";
|
|
3
3
|
import { formatTimestamp } from "../util.js";
|
|
4
4
|
import { tempFunc } from "../utils/temp.js";
|
|
5
5
|
export async function getCameraList(requestModifiers, sessionId) {
|
|
6
|
-
const body = await
|
|
6
|
+
const body = await cachedPostApi({
|
|
7
7
|
route: "/camera/getMinimalCameraStateList",
|
|
8
8
|
body: {},
|
|
9
9
|
modifiers: requestModifiers,
|
|
@@ -14,7 +14,7 @@ export async function getCameraList(requestModifiers, sessionId) {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
export async function getDoorbellCameras(requestModifiers, sessionId) {
|
|
17
|
-
const body = await
|
|
17
|
+
const body = await cachedPostApi({
|
|
18
18
|
route: "/doorbellcamera/getMinimalStateList",
|
|
19
19
|
body: {},
|
|
20
20
|
modifiers: requestModifiers,
|
|
@@ -25,7 +25,7 @@ export async function getDoorbellCameras(requestModifiers, sessionId) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
export async function getBadgeReaders(requestModifiers, sessionId) {
|
|
28
|
-
return await
|
|
28
|
+
return await cachedPostApi({
|
|
29
29
|
route: "/badgereader/getMinimalStateList",
|
|
30
30
|
body: {},
|
|
31
31
|
modifiers: requestModifiers,
|
|
@@ -37,7 +37,7 @@ export async function getBadgeReaders(requestModifiers, sessionId) {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
export async function getAccessControlledDoors(requestModifiers, sessionId) {
|
|
40
|
-
return await
|
|
40
|
+
return await cachedPostApi({
|
|
41
41
|
route: "/component/findAccessControlledDoors",
|
|
42
42
|
body: {},
|
|
43
43
|
modifiers: requestModifiers,
|
|
@@ -60,7 +60,7 @@ export async function getAccessControlledDoors(requestModifiers, sessionId) {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
export async function getAudioGateways(timeZone, requestModifiers, sessionId) {
|
|
63
|
-
return await
|
|
63
|
+
return await cachedPostApi({
|
|
64
64
|
route: "/audiogateway/getMinimalAudioGatewayStateList",
|
|
65
65
|
body: {},
|
|
66
66
|
modifiers: requestModifiers,
|
|
@@ -79,7 +79,7 @@ export async function getAudioGateways(timeZone, requestModifiers, sessionId) {
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
export async function getDoorSensors(requestModifiers, sessionId) {
|
|
82
|
-
return await
|
|
82
|
+
return await cachedPostApi({
|
|
83
83
|
route: "/door/getMinimalDoorStateList",
|
|
84
84
|
body: {},
|
|
85
85
|
modifiers: requestModifiers,
|
|
@@ -91,7 +91,7 @@ export async function getDoorSensors(requestModifiers, sessionId) {
|
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
export async function getEnvironmentalSensors(timeZone, tempUnit, requestModifiers, sessionId) {
|
|
94
|
-
return await
|
|
94
|
+
return await cachedPostApi({
|
|
95
95
|
route: "/climate/getMinimalClimateStateList",
|
|
96
96
|
body: {},
|
|
97
97
|
modifiers: requestModifiers,
|
|
@@ -115,7 +115,7 @@ export async function getEnvironmentalSensors(timeZone, tempUnit, requestModifie
|
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
117
|
export async function getMotionSensors(requestModifiers, sessionId) {
|
|
118
|
-
return await
|
|
118
|
+
return await cachedPostApi({
|
|
119
119
|
route: "/occupancy/getMinimalOccupancySensorStateList",
|
|
120
120
|
body: {},
|
|
121
121
|
modifiers: requestModifiers,
|
|
@@ -127,7 +127,7 @@ export async function getMotionSensors(requestModifiers, sessionId) {
|
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
export async function getButtons(timeZone, requestModifiers, sessionId) {
|
|
130
|
-
return await
|
|
130
|
+
return await cachedPostApi({
|
|
131
131
|
route: "/button/getMinimalButtonStateList",
|
|
132
132
|
body: {},
|
|
133
133
|
modifiers: requestModifiers,
|
|
@@ -146,7 +146,7 @@ export async function getButtons(timeZone, requestModifiers, sessionId) {
|
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
export async function getKeypads(requestModifiers, sessionId) {
|
|
149
|
-
return await
|
|
149
|
+
return await cachedPostApi({
|
|
150
150
|
route: "/keypad/getKeypadsForOrg",
|
|
151
151
|
body: {},
|
|
152
152
|
modifiers: requestModifiers,
|
|
@@ -158,7 +158,7 @@ export async function getKeypads(requestModifiers, sessionId) {
|
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
160
|
export async function getEnvironmentalGateways(timeZone, requestModifiers, sessionId) {
|
|
161
|
-
return await
|
|
161
|
+
return await cachedPostApi({
|
|
162
162
|
route: "/climate/getMinimalEnvironmentalGatewayStates",
|
|
163
163
|
body: {},
|
|
164
164
|
modifiers: requestModifiers,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cachedPostApi } from "../network/org-reference-cache.js";
|
|
2
2
|
export async function getOrg(requestModifiers, sessionId) {
|
|
3
|
-
return await
|
|
3
|
+
return await cachedPostApi({
|
|
4
4
|
route: "/org/getOrgV2",
|
|
5
5
|
body: {},
|
|
6
6
|
modifiers: requestModifiers,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { postApi } from "../network/network.js";
|
|
1
|
+
import { apiWarning, postApi, throwIfApiError } from "../network/network.js";
|
|
2
2
|
export async function getAllGuestsByOrg(requestModifiers, sessionId) {
|
|
3
3
|
const res = await postApi({
|
|
4
4
|
route: "/guestmanagement/getAllGuestsByOrg",
|
|
@@ -73,3 +73,77 @@ export async function getActivitiesForLocation(locationUuid, startTimeMs, endTim
|
|
|
73
73
|
timestampMs: a.timestampMs ?? undefined,
|
|
74
74
|
})) ?? []);
|
|
75
75
|
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Guest write paths
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
export async function createGuest(guest, requestModifiers, sessionId) {
|
|
80
|
+
const res = await postApi({
|
|
81
|
+
route: "/guestmanagement/createGuest",
|
|
82
|
+
body: { guest },
|
|
83
|
+
modifiers: requestModifiers,
|
|
84
|
+
sessionId,
|
|
85
|
+
});
|
|
86
|
+
throwIfApiError(res);
|
|
87
|
+
return { success: true, email: guest.email, warningMsg: apiWarning(res) };
|
|
88
|
+
}
|
|
89
|
+
/** Guests are addressed by EMAIL, not a uuid — there is no delete-by-uuid. */
|
|
90
|
+
export async function deleteGuest(email, requestModifiers, sessionId) {
|
|
91
|
+
const res = await postApi({
|
|
92
|
+
route: "/guestmanagement/deleteGuest",
|
|
93
|
+
body: { email },
|
|
94
|
+
modifiers: requestModifiers,
|
|
95
|
+
sessionId,
|
|
96
|
+
});
|
|
97
|
+
throwIfApiError(res);
|
|
98
|
+
return { success: true, email, warningMsg: apiWarning(res) };
|
|
99
|
+
}
|
|
100
|
+
export async function getGuestPasses(range, requestModifiers, sessionId) {
|
|
101
|
+
const res = await postApi({
|
|
102
|
+
route: "/accesscontrol/guestpass/findGuestPasses",
|
|
103
|
+
body: {
|
|
104
|
+
startAfterMs: range.startAfterMs,
|
|
105
|
+
startBeforeMs: range.startBeforeMs,
|
|
106
|
+
},
|
|
107
|
+
modifiers: requestModifiers,
|
|
108
|
+
sessionId,
|
|
109
|
+
});
|
|
110
|
+
throwIfApiError(res);
|
|
111
|
+
return {
|
|
112
|
+
guestPasses: res.guestPasses?.flatMap(pass => pass
|
|
113
|
+
? [
|
|
114
|
+
{
|
|
115
|
+
uuid: pass.uuid ?? undefined,
|
|
116
|
+
locationUuid: pass.locationUuid ?? undefined,
|
|
117
|
+
lifecycle: pass.lifecycle ?? undefined,
|
|
118
|
+
note: pass.note ?? undefined,
|
|
119
|
+
passStartTimeMs: pass.passStartTimeMs ?? undefined,
|
|
120
|
+
principalType: pass.principalType ?? undefined,
|
|
121
|
+
},
|
|
122
|
+
]
|
|
123
|
+
: []) ?? [],
|
|
124
|
+
lastEvaluatedKey: res.lastEvaluatedKey ?? undefined,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Guest pass state changes.
|
|
129
|
+
*
|
|
130
|
+
* They are not interchangeable: suspend is reversible via unsuspend, expire ends
|
|
131
|
+
* the pass but keeps its record and history, and delete removes the record
|
|
132
|
+
* outright.
|
|
133
|
+
*/
|
|
134
|
+
export async function changeGuestPassState(guestPassUuid, action, requestModifiers, sessionId) {
|
|
135
|
+
const routes = {
|
|
136
|
+
expire: "/accesscontrol/guestpass/expireGuestPass",
|
|
137
|
+
suspend: "/accesscontrol/guestpass/suspendGuestPass",
|
|
138
|
+
unsuspend: "/accesscontrol/guestpass/unsuspendGuestPass",
|
|
139
|
+
delete: "/accesscontrol/guestpass/deleteGuestPass",
|
|
140
|
+
};
|
|
141
|
+
const res = await postApi({
|
|
142
|
+
route: routes[action],
|
|
143
|
+
body: { guestPassUuid },
|
|
144
|
+
modifiers: requestModifiers,
|
|
145
|
+
sessionId,
|
|
146
|
+
});
|
|
147
|
+
throwIfApiError(res);
|
|
148
|
+
return { success: true, uuid: guestPassUuid, action, warningMsg: apiWarning(res) };
|
|
149
|
+
}
|
|
@@ -1,18 +1,43 @@
|
|
|
1
|
-
import { postApi } from "../network/network.js";
|
|
1
|
+
import { postApi, throwIfApiError } from "../network/network.js";
|
|
2
|
+
import { cachedPostApi, clearOrgReferenceCache } from "../network/org-reference-cache.js";
|
|
3
|
+
function mapLocation(raw) {
|
|
4
|
+
const addressParts = [raw.address1, raw.address2, raw.postalCode, raw.countryCode]
|
|
5
|
+
.map(part => part?.trim())
|
|
6
|
+
.filter((part) => !!part);
|
|
7
|
+
return {
|
|
8
|
+
uuid: raw.uuid ?? undefined,
|
|
9
|
+
name: raw.name ?? undefined,
|
|
10
|
+
address1: raw.address1 ?? undefined,
|
|
11
|
+
address2: raw.address2 ?? undefined,
|
|
12
|
+
postalCode: raw.postalCode ?? undefined,
|
|
13
|
+
countryCode: raw.countryCode ?? undefined,
|
|
14
|
+
// Pre-joined so callers never have to reassemble it (and so the model
|
|
15
|
+
// doesn't invent a format).
|
|
16
|
+
address: addressParts.length > 0 ? addressParts.join(", ") : undefined,
|
|
17
|
+
timezone: raw.tz ?? undefined,
|
|
18
|
+
labels: raw.labels?.filter((label) => !!label) ?? [],
|
|
19
|
+
latitude: raw.latitude ?? undefined,
|
|
20
|
+
longitude: raw.longitude ?? undefined,
|
|
21
|
+
policyUuid: raw.policyUuid ?? undefined,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
2
24
|
export async function getLocations(requestModifiers, sessionId) {
|
|
3
|
-
const res = await
|
|
25
|
+
const res = await cachedPostApi({
|
|
4
26
|
route: "/location/getLocationsV2",
|
|
5
27
|
body: {},
|
|
6
28
|
modifiers: requestModifiers,
|
|
7
29
|
sessionId,
|
|
8
30
|
});
|
|
31
|
+
throwIfApiError(res);
|
|
9
32
|
return {
|
|
10
|
-
locations:
|
|
11
|
-
|
|
12
|
-
name: loc.name ?? undefined,
|
|
13
|
-
})),
|
|
33
|
+
locations: res.locations?.flatMap(raw => (raw ? [mapLocation(raw)] : [])) ?? [],
|
|
34
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
14
35
|
};
|
|
15
36
|
}
|
|
37
|
+
export async function findLocation(locationUuid, requestModifiers, sessionId) {
|
|
38
|
+
const { locations } = await getLocations(requestModifiers, sessionId);
|
|
39
|
+
return locations.find(location => location.uuid === locationUuid);
|
|
40
|
+
}
|
|
16
41
|
export async function createLocation(name, address, requestModifiers, sessionId) {
|
|
17
42
|
const res = await postApi({
|
|
18
43
|
route: "/location/createLocation",
|
|
@@ -20,37 +45,104 @@ export async function createLocation(name, address, requestModifiers, sessionId)
|
|
|
20
45
|
modifiers: requestModifiers,
|
|
21
46
|
sessionId,
|
|
22
47
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
throwIfApiError(res);
|
|
49
|
+
// The org-wide location list is cached for up to 60s; without this, a read
|
|
50
|
+
// straight after the write still reports the old list.
|
|
51
|
+
clearOrgReferenceCache();
|
|
52
|
+
return {
|
|
53
|
+
uuid: res.locationUuid ?? res.uuid ?? undefined,
|
|
54
|
+
success: true,
|
|
55
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* `selectiveUpdateLocation` leaves out-of-body fields alone, unlike
|
|
60
|
+
* `updateLocation` which replaces the record — so a rename can't blank the
|
|
61
|
+
* address, and setting a timezone can't blank the labels.
|
|
62
|
+
*/
|
|
63
|
+
export async function updateLocation(update, requestModifiers, sessionId) {
|
|
33
64
|
const res = await postApi({
|
|
34
|
-
route: "/location/
|
|
35
|
-
body,
|
|
65
|
+
route: "/location/selectiveUpdateLocation",
|
|
66
|
+
body: update,
|
|
36
67
|
modifiers: requestModifiers,
|
|
37
68
|
sessionId,
|
|
38
69
|
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return {
|
|
70
|
+
throwIfApiError(res);
|
|
71
|
+
clearOrgReferenceCache();
|
|
72
|
+
return {
|
|
73
|
+
updated: { success: true, locationUuid: update.locationUuid },
|
|
74
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
75
|
+
};
|
|
42
76
|
}
|
|
43
|
-
export async function
|
|
77
|
+
export async function deleteLocation(locationUuid, requestModifiers, sessionId) {
|
|
44
78
|
const res = await postApi({
|
|
79
|
+
route: "/location/deleteLocation",
|
|
80
|
+
body: { uuid: locationUuid },
|
|
81
|
+
modifiers: requestModifiers,
|
|
82
|
+
sessionId,
|
|
83
|
+
});
|
|
84
|
+
throwIfApiError(res);
|
|
85
|
+
clearOrgReferenceCache();
|
|
86
|
+
return {
|
|
87
|
+
deleted: { success: true, locationUuid },
|
|
88
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export async function getLocationLabels(requestModifiers, sessionId) {
|
|
92
|
+
const res = await cachedPostApi({
|
|
45
93
|
route: "/location/getLocationLabelsForOrg",
|
|
46
94
|
body: {},
|
|
47
95
|
modifiers: requestModifiers,
|
|
48
96
|
sessionId,
|
|
49
97
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
98
|
+
throwIfApiError(res);
|
|
99
|
+
// api2 returns location labels as bare strings on some orgs and objects on
|
|
100
|
+
// others; normalise both to {name}.
|
|
101
|
+
const raw = res.locationLabels ?? [];
|
|
102
|
+
const labels = [];
|
|
103
|
+
for (const label of raw) {
|
|
104
|
+
if (typeof label === "string") {
|
|
105
|
+
if (label.trim())
|
|
106
|
+
labels.push({ name: label.trim() });
|
|
107
|
+
}
|
|
108
|
+
else if (label && typeof label === "object") {
|
|
109
|
+
const record = label;
|
|
110
|
+
labels.push({ uuid: record.uuid ?? undefined, name: record.name ?? undefined });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return labels;
|
|
114
|
+
}
|
|
115
|
+
export async function addLocationLabel(locationUuid, label, requestModifiers, sessionId) {
|
|
116
|
+
const res = await postApi({
|
|
117
|
+
route: "/location/addLocationLabel",
|
|
118
|
+
body: {
|
|
119
|
+
locationIdentifier: locationUuid,
|
|
120
|
+
label,
|
|
121
|
+
},
|
|
122
|
+
modifiers: requestModifiers,
|
|
123
|
+
sessionId,
|
|
124
|
+
});
|
|
125
|
+
throwIfApiError(res);
|
|
126
|
+
clearOrgReferenceCache();
|
|
127
|
+
return {
|
|
128
|
+
labelChanged: { success: true, locationUuid, label },
|
|
129
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
export async function removeLocationLabel(locationUuid, label, requestModifiers, sessionId) {
|
|
133
|
+
const res = await postApi({
|
|
134
|
+
route: "/location/removeLocationLabel",
|
|
135
|
+
body: {
|
|
136
|
+
locationIdentifier: locationUuid,
|
|
137
|
+
label,
|
|
138
|
+
},
|
|
139
|
+
modifiers: requestModifiers,
|
|
140
|
+
sessionId,
|
|
141
|
+
});
|
|
142
|
+
throwIfApiError(res);
|
|
143
|
+
clearOrgReferenceCache();
|
|
144
|
+
return {
|
|
145
|
+
labelChanged: { success: true, locationUuid, label },
|
|
146
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
147
|
+
};
|
|
56
148
|
}
|