rhombus-node-mcp 0.1.21 → 0.1.23

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.
Files changed (82) hide show
  1. package/README.md +8 -0
  2. package/dist/api/access-control-tool-api.js +256 -0
  3. package/dist/api/alarm-monitoring-tool-api.js +65 -0
  4. package/dist/api/camera-tool-api.js +35 -15
  5. package/dist/api/camera-uptime-tool-api.js +112 -0
  6. package/dist/api/clips-tool-api.js +110 -21
  7. package/dist/api/create-tool-api.js +143 -16
  8. package/dist/api/door-tool-api.js +66 -0
  9. package/dist/api/events-tool-api.js +151 -28
  10. package/dist/api/faces-tool-api.js +118 -91
  11. package/dist/api/get-entity-tool-api.js +2 -0
  12. package/dist/api/guest-management-tool-api.js +75 -0
  13. package/dist/api/location-tool-api.js +48 -1
  14. package/dist/api/lpr-tool-api.js +26 -0
  15. package/dist/api/policy-alerts-tool-api.js +54 -0
  16. package/dist/api/report-tool-api.js +345 -23
  17. package/dist/api/rules-tool-api.js +78 -0
  18. package/dist/api/search-tool-api.js +93 -0
  19. package/dist/api/time-tool-api.js +3 -1
  20. package/dist/api/update-tool-api.js +204 -0
  21. package/dist/api/user-access-trail-tool-api.js +45 -0
  22. package/dist/api/user-audit-tool-api.js +47 -0
  23. package/dist/api/user-tool-api.js +77 -0
  24. package/dist/createServer.js +3 -1
  25. package/dist/filtering-utils.js +298 -0
  26. package/dist/index.js +0 -9
  27. package/dist/logger.js +6 -5
  28. package/dist/network.js +18 -4
  29. package/dist/tools/access-control-tool.js +97 -0
  30. package/dist/tools/alarm-monitoring-tool.js +50 -0
  31. package/dist/tools/analytics-tool.js +383 -0
  32. package/dist/tools/camera-tool.js +27 -9
  33. package/dist/tools/camera-uptime-tool.js +50 -0
  34. package/dist/tools/clips-tool.js +46 -16
  35. package/dist/tools/door-tool.js +66 -0
  36. package/dist/tools/events-tool.js +103 -81
  37. package/dist/tools/faces-tool.js +175 -32
  38. package/dist/tools/get-entity-tool.js +6 -1
  39. package/dist/tools/guest-management-tool.js +50 -0
  40. package/dist/tools/location-tool.js +23 -4
  41. package/dist/tools/lpr-tool.js +26 -20
  42. package/dist/tools/policy-alerts-tool.js +34 -4
  43. package/dist/tools/report-tool.js +183 -46
  44. package/dist/tools/rules-tool.js +76 -0
  45. package/dist/tools/search-tool.js +69 -0
  46. package/dist/tools/update-tool.js +63 -2
  47. package/dist/tools/user-access-trail-tool.js +64 -0
  48. package/dist/tools/user-audit-tool.js +53 -0
  49. package/dist/tools/user-tool.js +55 -0
  50. package/dist/tools/video-walls-tool.js +41 -0
  51. package/dist/transports/streamable-http.js +25 -15
  52. package/dist/types/access-control-tool-types.js +120 -0
  53. package/dist/types/alarm-monitoring-tool-types.js +60 -0
  54. package/dist/types/analytics-tool-types.js +190 -0
  55. package/dist/types/camera-tool-types.js +3 -2
  56. package/dist/types/camera-uptime-tool-types.js +52 -0
  57. package/dist/types/clips-tool-types.js +60 -11
  58. package/dist/types/door-tool-types.js +64 -0
  59. package/dist/types/events-tools-types.js +71 -3
  60. package/dist/types/faces-tools-types.js +36 -0
  61. package/dist/types/guest-management-tool-types.js +57 -0
  62. package/dist/types/location-tool-types.js +10 -1
  63. package/dist/types/lpr-tool-types.js +14 -0
  64. package/dist/types/policy-alerts-tool-types.js +38 -3
  65. package/dist/types/report-tool-types.js +284 -42
  66. package/dist/types/rules-tool-types.js +58 -0
  67. package/dist/types/schema.js +3123 -1
  68. package/dist/types/search-tool-types.js +82 -0
  69. package/dist/types/update-tool-types.js +4 -1
  70. package/dist/types/user-access-trail-tool-types.js +54 -0
  71. package/dist/types/user-audit-tool-types.js +48 -0
  72. package/dist/types/user-tool-types.js +61 -0
  73. package/dist/types/video-walls-tool-types.js +59 -0
  74. package/dist/types/zod-schemas.js +3237 -986
  75. package/dist/types.js +0 -21
  76. package/dist/util.js +34 -57
  77. package/package.json +7 -5
  78. package/dist/disabled-tools/semantic-search-tool.js +0 -90
  79. package/dist/tools/create-tool.js +0 -30
  80. package/dist/types/create-tool-types.js +0 -8
  81. package/dist/types/schema-components.js +0 -7211
  82. package/dist/types/semantic-search-tool-types.js +0 -5
@@ -0,0 +1,75 @@
1
+ import { postApi } from "../network.js";
2
+ export async function getAllGuestsByOrg(requestModifiers, sessionId) {
3
+ const res = await postApi({
4
+ route: "/guestmanagement/getAllGuestsByOrg",
5
+ body: {},
6
+ modifiers: requestModifiers,
7
+ sessionId,
8
+ });
9
+ if (res.error) {
10
+ throw new Error(res.errorMsg ?? "Failed to get guests");
11
+ }
12
+ return (res.allGuests?.map(guest => ({
13
+ firstName: guest.firstName ?? undefined,
14
+ lastName: guest.lastName ?? undefined,
15
+ email: guest.email ?? undefined,
16
+ companyName: guest.companyName ?? undefined,
17
+ locationUuid: guest.locationUuid ?? undefined,
18
+ hostUserUuid: guest.hostUserUuid ?? undefined,
19
+ guestType: guest.guestType ?? undefined,
20
+ checkedInStatus: guest.checkedInEnum ?? undefined,
21
+ lastCheckedInMs: guest.lastCheckedInMs ?? undefined,
22
+ lastCheckedOutMs: guest.lastCheckedOutMs ?? undefined,
23
+ phoneNumber: guest.phoneNumber ?? undefined,
24
+ })) ?? []);
25
+ }
26
+ export async function getGuestActivityLogs(startTimeMs, endTimeMs, requestModifiers, sessionId) {
27
+ const body = {};
28
+ if (startTimeMs)
29
+ body.createdAfterMs = startTimeMs;
30
+ if (endTimeMs)
31
+ body.createdBeforeMs = endTimeMs;
32
+ const res = await postApi({
33
+ route: "/guestmanagement/getGuestActivityLogs",
34
+ body,
35
+ modifiers: requestModifiers,
36
+ sessionId,
37
+ });
38
+ if (res.error) {
39
+ throw new Error(res.errorMsg ?? "Failed to get guest activity logs");
40
+ }
41
+ return (res.guestActivities?.map(a => ({
42
+ activity: a.activity ?? undefined,
43
+ email: a.email ?? undefined,
44
+ guestType: a.guestType ?? undefined,
45
+ locationUuid: a.locationUuid ?? undefined,
46
+ hostUserUuid: a.hostUserUuid ?? undefined,
47
+ timestampMs: a.timestampMs ?? undefined,
48
+ })) ?? []);
49
+ }
50
+ export async function getActivitiesForLocation(locationUuid, startTimeMs, endTimeMs, requestModifiers, sessionId) {
51
+ const body = {
52
+ locationUuid,
53
+ };
54
+ if (startTimeMs)
55
+ body.createdAfterMs = startTimeMs;
56
+ if (endTimeMs)
57
+ body.createdBeforeMs = endTimeMs;
58
+ const res = await postApi({
59
+ route: "/guestmanagement/getActivitiesForLocation",
60
+ body,
61
+ modifiers: requestModifiers,
62
+ sessionId,
63
+ });
64
+ if (res.error) {
65
+ throw new Error(res.errorMsg ?? "Failed to get activities for location");
66
+ }
67
+ return (res.guestActivities?.map(a => ({
68
+ activity: a.activity ?? undefined,
69
+ email: a.email ?? undefined,
70
+ guestType: a.guestType ?? undefined,
71
+ locationUuid: a.locationUuid ?? undefined,
72
+ hostUserUuid: a.hostUserUuid ?? undefined,
73
+ timestampMs: a.timestampMs ?? undefined,
74
+ })) ?? []);
75
+ }
@@ -1,9 +1,56 @@
1
1
  import { postApi } from "../network.js";
2
2
  export async function getLocations(requestModifiers, sessionId) {
3
- return await postApi({
3
+ const res = await postApi({
4
4
  route: "/location/getLocationsV2",
5
5
  body: {},
6
6
  modifiers: requestModifiers,
7
7
  sessionId,
8
8
  });
9
+ return {
10
+ locations: (res.locations || []).map((loc) => ({
11
+ uuid: loc.uuid ?? undefined,
12
+ name: loc.name ?? undefined,
13
+ })),
14
+ };
15
+ }
16
+ export async function createLocation(name, address, requestModifiers, sessionId) {
17
+ const res = await postApi({
18
+ route: "/location/createLocation",
19
+ body: { name, address: address || undefined },
20
+ modifiers: requestModifiers,
21
+ sessionId,
22
+ });
23
+ if (res.error)
24
+ throw new Error(JSON.stringify(res));
25
+ return { uuid: res.locationUuid ?? res.uuid ?? undefined, success: true };
26
+ }
27
+ export async function updateLocation(locationUuid, name, address, requestModifiers, sessionId) {
28
+ const body = { locationUuid };
29
+ if (name)
30
+ body.name = name;
31
+ if (address)
32
+ body.address = address;
33
+ const res = await postApi({
34
+ route: "/location/updateLocation",
35
+ body,
36
+ modifiers: requestModifiers,
37
+ sessionId,
38
+ });
39
+ if (res.error)
40
+ throw new Error(JSON.stringify(res));
41
+ return { success: true };
42
+ }
43
+ export async function getLocationLabels(requestModifiers, sessionId) {
44
+ const res = await postApi({
45
+ route: "/location/getLocationLabelsForOrg",
46
+ body: {},
47
+ modifiers: requestModifiers,
48
+ sessionId,
49
+ });
50
+ if (res.error)
51
+ throw new Error(JSON.stringify(res));
52
+ return (res.locationLabels || []).map((label) => ({
53
+ uuid: label.uuid ?? undefined,
54
+ name: label.name ?? undefined,
55
+ }));
9
56
  }
@@ -66,3 +66,29 @@ export async function getVehicleLabels(requestModifiers, sessionId) {
66
66
  }
67
67
  return processedVehicleLabels;
68
68
  }
69
+ export async function searchLicensePlates(query, timeZone, requestModifiers, sessionId) {
70
+ const res = await postApi({
71
+ route: "/search/searchLicensePlates",
72
+ body: { licensePlateQuery: query },
73
+ modifiers: requestModifiers,
74
+ sessionId,
75
+ });
76
+ if (res.error)
77
+ throw new Error(JSON.stringify(res));
78
+ return (res.licensePlateEvents || []).map((event) => ({
79
+ licensePlate: event.vehicleLicensePlate ?? undefined,
80
+ deviceUuid: event.deviceUuid ?? undefined,
81
+ timestampMs: event.eventTimestamp ?? undefined,
82
+ }));
83
+ }
84
+ export async function saveVehicle(name, licensePlate, description, requestModifiers, sessionId) {
85
+ const res = await postApi({
86
+ route: "/vehicle/saveVehicle",
87
+ body: { name, licensePlate, description: description || "" },
88
+ modifiers: requestModifiers,
89
+ sessionId,
90
+ });
91
+ if (res.error)
92
+ throw new Error(JSON.stringify(res));
93
+ return { success: true };
94
+ }
@@ -40,3 +40,57 @@ export async function getExpiringPolicyAlerts(args, requestModifiers, sessionId)
40
40
  };
41
41
  });
42
42
  }
43
+ export async function getPolicyAlertDetails(alertUuid, requestModifiers, sessionId) {
44
+ const response = await postApi({
45
+ route: "/event/getPolicyAlertDetails",
46
+ body: { policyAlertUuid: alertUuid },
47
+ modifiers: requestModifiers,
48
+ sessionId,
49
+ });
50
+ return { policyAlerts: response.policyAlert ? [response.policyAlert] : [] };
51
+ }
52
+ export async function dismissPolicyAlert(alertUuid, requestModifiers, sessionId) {
53
+ const response = await postApi({
54
+ route: "/event/dismissPolicyAlertV2",
55
+ body: { policyAlertUuid: alertUuid },
56
+ modifiers: requestModifiers,
57
+ sessionId,
58
+ });
59
+ return { policyAlerts: [], dismissed: true };
60
+ }
61
+ export async function getUnhealthyDeviceAlerts(requestModifiers, sessionId) {
62
+ const response = await postApi({
63
+ route: "/event/getUnhealthyDeviceAlerts",
64
+ body: {},
65
+ modifiers: requestModifiers,
66
+ sessionId,
67
+ });
68
+ return { policyAlerts: response.unhealthyDeviceAlerts || [] };
69
+ }
70
+ export async function getPolicyAlertGroups(payload, requestModifiers, sessionId) {
71
+ const body = {};
72
+ if (payload.afterTimestampMs)
73
+ body.afterTimestampMs = payload.afterTimestampMs;
74
+ if (payload.beforeTimestampMs)
75
+ body.beforeTimestampMs = payload.beforeTimestampMs;
76
+ if (payload.deviceFilter)
77
+ body.deviceUuids = payload.deviceFilter;
78
+ if (payload.locationFilter)
79
+ body.locationUuids = payload.locationFilter;
80
+ if (payload.maxResults)
81
+ body.maxResults = payload.maxResults;
82
+ if (payload.lastTimestampMs != null)
83
+ body.lastTimestampMs = payload.lastTimestampMs;
84
+ if (payload.lastUuid)
85
+ body.lastUuid = payload.lastUuid;
86
+ const response = await postApi({
87
+ route: "/event/getPolicyAlertGroupsV2",
88
+ body,
89
+ modifiers: requestModifiers,
90
+ sessionId,
91
+ });
92
+ return {
93
+ ...response,
94
+ policyAlerts: response.policyAlertGroups || [],
95
+ };
96
+ }
@@ -1,13 +1,12 @@
1
+ import { DateTime } from "luxon";
1
2
  import { getLogger } from "../logger.js";
2
3
  import { postApi } from "../network.js";
4
+ import { ReportTypeEnum } from "../types/schema.js";
3
5
  import { formatTimestamp } from "../util.js";
4
- import { GetCountReportV2WSRequestTypesEnum } from "../types/schema-components.js";
5
- import { DateTime } from "luxon";
6
6
  const REPORT_TYPES_THAT_RETURN_UTC = new Set([
7
- GetCountReportV2WSRequestTypesEnum.BANDWIDTH,
8
- GetCountReportV2WSRequestTypesEnum.VEHICLES,
9
- GetCountReportV2WSRequestTypesEnum.MOTION,
10
- GetCountReportV2WSRequestTypesEnum.DWELL,
7
+ ReportTypeEnum.BANDWIDTH,
8
+ ReportTypeEnum.LICENSEPLATES,
9
+ ReportTypeEnum.DWELL,
11
10
  ]);
12
11
  const logger = getLogger();
13
12
  function getUtcTime(datetime, reportType, timeZone) {
@@ -15,8 +14,8 @@ function getUtcTime(datetime, reportType, timeZone) {
15
14
  return datetime;
16
15
  }
17
16
  else {
18
- logger.info("timeZone", datetime);
19
- return DateTime.fromISO(datetime, { zone: timeZone }).toUTC().toISO();
17
+ logger.trace("timeZone", datetime);
18
+ return DateTime.fromISO(datetime, { zone: timeZone }).toUTC().toISO() ?? datetime;
20
19
  }
21
20
  }
22
21
  export async function getOccupancyCountReport(deviceUuid, startTimeMs, endTimeMs, interval, requestModifiers, sessionId) {
@@ -59,7 +58,9 @@ export async function getSummaryCountReport(interval, scope, types, uuid, endTim
59
58
  scope,
60
59
  startTimeMs,
61
60
  types,
62
- ...(uuid ? { uuid } : {}),
61
+ // omit for org
62
+ ...(uuid && scope !== "ORG" ? { uuid } : {}),
63
+ ...(timeZone ? { timeZone } : {}),
63
64
  };
64
65
  const response = await postApi({
65
66
  route: "/report/getCountReportV2",
@@ -68,8 +69,8 @@ export async function getSummaryCountReport(interval, scope, types, uuid, endTim
68
69
  sessionId,
69
70
  });
70
71
  // Process response to convert date strings to UTC milliseconds timestamps
71
- let newTimeSeriesDataPoints = undefined;
72
- if (response && response.timeSeriesDataPoints && Array.isArray(response.timeSeriesDataPoints)) {
72
+ let newTimeSeriesDataPoints;
73
+ if (response?.timeSeriesDataPoints && Array.isArray(response.timeSeriesDataPoints)) {
73
74
  newTimeSeriesDataPoints = response.timeSeriesDataPoints.map((dataPoint) => {
74
75
  const sanitizedDataPoint = {
75
76
  eventCountMap: dataPoint.eventCountMap,
@@ -162,7 +163,7 @@ export async function getThresholdCrossingCountReport(deviceUuid, startTimeMs, e
162
163
  }))
163
164
  : undefined;
164
165
  // Calculate metrics if we have crossing counts and the bucket size is appropriate
165
- let metrics = undefined;
166
+ let metrics;
166
167
  if (crossingCounts && crossingCounts.length > 0) {
167
168
  // Calculate total entries and exits
168
169
  let totalEntries = 0;
@@ -244,17 +245,17 @@ export async function getThresholdCrossingCountReport(deviceUuid, startTimeMs, e
244
245
  averageExitsPerHour: totalExits / hoursInPeriod,
245
246
  mostEntriesInHour: {
246
247
  count: maxEntries,
247
- timestamp: maxEntriesTimestamp ? DateTime.fromMillis(maxEntriesTimestamp).toISO() : "",
248
+ timestamp: maxEntriesTimestamp ? DateTime.fromMillis(maxEntriesTimestamp).toISO() ?? "" : "",
248
249
  hourLabel: formatHourLabel(maxEntriesTimestamp),
249
250
  },
250
251
  mostExitsInHour: {
251
252
  count: maxExits,
252
- timestamp: maxExitsTimestamp ? DateTime.fromMillis(maxExitsTimestamp).toISO() : "",
253
+ timestamp: maxExitsTimestamp ? DateTime.fromMillis(maxExitsTimestamp).toISO() ?? "" : "",
253
254
  hourLabel: formatHourLabel(maxExitsTimestamp),
254
255
  },
255
256
  busiestHour: {
256
257
  totalCount: maxTotal,
257
- timestamp: maxTotalTimestamp ? DateTime.fromMillis(maxTotalTimestamp).toISO() : "",
258
+ timestamp: maxTotalTimestamp ? DateTime.fromMillis(maxTotalTimestamp).toISO() ?? "" : "",
258
259
  hourLabel: formatHourLabel(maxTotalTimestamp),
259
260
  entries: maxTotalEntries,
260
261
  exits: maxTotalExits,
@@ -307,13 +308,10 @@ export async function getCustomLLMReport(promptUuid, promptType, startTimeMs, en
307
308
  endTimeMs,
308
309
  interval,
309
310
  }));
310
- const body = {
311
- promptUuid,
312
- startTimeMs,
313
- endTimeMs,
314
- interval,
315
- };
316
- // Determine the correct endpoint based on promptType
311
+ // PERCENT endpoint uses a different request schema that doesn't accept interval
312
+ const body = promptType === "PERCENT"
313
+ ? { promptUuid, startTimeMs, endTimeMs }
314
+ : { promptUuid, startTimeMs, endTimeMs, interval };
317
315
  let route;
318
316
  switch (promptType) {
319
317
  case "COUNT":
@@ -348,7 +346,7 @@ export async function getCustomLLMReport(promptUuid, promptType, startTimeMs, en
348
346
  logger.info(`📊 ${promptType} response structure:`, JSON.stringify({ reportEntries: reportEntries.length }));
349
347
  // Aggregate data from all devices
350
348
  const aggregatedData = {};
351
- reportEntries.forEach(([deviceId, reportData]) => {
349
+ reportEntries.forEach(([_, reportData]) => {
352
350
  if (Array.isArray(reportData)) {
353
351
  reportData.forEach((item) => {
354
352
  const dateKey = item.localDate || new Date().toISOString();
@@ -424,3 +422,327 @@ export async function getCustomLLMReport(promptUuid, promptType, startTimeMs, en
424
422
  }));
425
423
  return result;
426
424
  }
425
+ export async function getAuditFeed(startTimeMs, endTimeMs, requestModifiers, sessionId) {
426
+ const body = { startTimeMs, endTimeMs };
427
+ const response = await postApi({
428
+ route: "/report/getAuditFeed",
429
+ body,
430
+ modifiers: requestModifiers,
431
+ sessionId,
432
+ });
433
+ return {
434
+ error: response.error ?? undefined,
435
+ errorMsg: response.errorMsg ?? undefined,
436
+ auditEvents: (response.auditEvents || []).map((event) => ({
437
+ timestamp: event.timestamp != null ? String(event.timestamp) : undefined,
438
+ action: event.action ?? undefined,
439
+ displayText: event.displayText ?? undefined,
440
+ principalName: event.principalName ?? undefined,
441
+ principalUuid: event.principalUuid ?? undefined,
442
+ principalType: event.principalType ?? undefined,
443
+ targetName: event.targetName ?? undefined,
444
+ targetUuid: event.targetUuid ?? undefined,
445
+ })),
446
+ };
447
+ }
448
+ export async function getDiagnosticFeed(startTimeMs, endTimeMs, requestModifiers, sessionId) {
449
+ const body = { startTimeMs, endTimeMs };
450
+ const response = await postApi({
451
+ route: "/report/getDiagnosticFeed",
452
+ body,
453
+ modifiers: requestModifiers,
454
+ sessionId,
455
+ });
456
+ return {
457
+ error: response.error ?? undefined,
458
+ errorMsg: response.errorMsg ?? undefined,
459
+ diagnosticEvents: (response.diagnosticEvents || []).map((event) => ({
460
+ timestampMs: event.timestamp ?? undefined,
461
+ deviceUuid: event.deviceUuid ?? undefined,
462
+ eventType: event.activity ?? undefined,
463
+ description: event.description ?? undefined,
464
+ })),
465
+ };
466
+ }
467
+ export async function getThresholdCrossingEvents(deviceUuid, startTimeMs, endTimeMs, requestModifiers, sessionId) {
468
+ const body = { deviceUuid, startTimeMs, endTimeMs };
469
+ const response = await postApi({
470
+ route: "/report/getThresholdCrossingEvents",
471
+ body,
472
+ modifiers: requestModifiers,
473
+ sessionId,
474
+ });
475
+ return {
476
+ error: response.error ?? undefined,
477
+ errorMsg: response.errorMsg ?? undefined,
478
+ events: (response.thresholdEvents || response.events || []).map((event) => ({
479
+ timestampMs: event.timestampMs ?? undefined,
480
+ direction: event.direction ?? undefined,
481
+ objectType: event.objectType ?? undefined,
482
+ })),
483
+ };
484
+ }
485
+ export async function getCustomEventsReport(promptUuid, startTimeMs, endTimeMs, interval, requestModifiers, sessionId) {
486
+ const body = { promptUuid, startTimeMs, endTimeMs, interval };
487
+ const response = await postApi({
488
+ route: "/report/getCustomEventsReport",
489
+ body,
490
+ modifiers: requestModifiers,
491
+ sessionId,
492
+ });
493
+ return {
494
+ error: response.error ?? undefined,
495
+ errorMsg: response.errorMsg ?? undefined,
496
+ timeSeriesDataPoints: response.timeSeriesDataPoints?.map((dp) => ({
497
+ dateLocal: dp.dateLocal ?? undefined,
498
+ dateUtc: dp.dateUtc ?? undefined,
499
+ eventCountMap: dp.eventCountMap ?? undefined,
500
+ })),
501
+ };
502
+ }
503
+ export async function getPeopleCountEvents(deviceUuids, requestModifiers, sessionId) {
504
+ const body = { deviceUuids };
505
+ const response = await postApi({
506
+ route: "/report/getMostRecentPeopleCountEvents",
507
+ body,
508
+ modifiers: requestModifiers,
509
+ sessionId,
510
+ });
511
+ return {
512
+ error: response.error ?? undefined,
513
+ errorMsg: response.errorMsg ?? undefined,
514
+ events: (response.events || []).map((event) => ({
515
+ deviceUuid: event.deviceUuid ?? undefined,
516
+ timestampMs: event.timestampMs ?? undefined,
517
+ count: event.count ?? undefined,
518
+ })),
519
+ };
520
+ }
521
+ export async function getBatchThresholdCrossingCountReport(deviceUuids, startTimeMs, endTimeMs, bucketSize, crossingObject, dedupe, timeZone, requestModifiers, sessionId) {
522
+ const body = {
523
+ deviceUuids,
524
+ startTimeMs,
525
+ endTimeMs,
526
+ bucketSize,
527
+ crossingObject,
528
+ dedupe,
529
+ ...(timeZone ? { timeZone } : {}),
530
+ };
531
+ const response = await postApi({
532
+ route: "/report/getBatchThresholdCrossingCountReport",
533
+ body,
534
+ modifiers: requestModifiers,
535
+ sessionId,
536
+ });
537
+ if (response.error) {
538
+ throw new Error(response.errorMsg ?? "Failed to get batch threshold crossing report");
539
+ }
540
+ const deviceCrossingCounts = response.deviceCrossingCounts ?? response.crossingCounts ?? {};
541
+ const result = {};
542
+ for (const [deviceUuid, counts] of Object.entries(deviceCrossingCounts)) {
543
+ result[deviceUuid] = (counts || []).map((c) => ({
544
+ timestampMs: c.timestampMs ?? undefined,
545
+ ingressCount: c.ingressCount ?? undefined,
546
+ egressCount: c.egressCount ?? undefined,
547
+ }));
548
+ }
549
+ return result;
550
+ }
551
+ export async function getThresholdCrossingCountReportForOrg(startTimeMs, endTimeMs, bucketSize, crossingObject, dedupe, timeZone, requestModifiers, sessionId) {
552
+ const body = {
553
+ startTimeMs,
554
+ endTimeMs,
555
+ bucketSize,
556
+ crossingObject,
557
+ dedupe,
558
+ ...(timeZone ? { timeZone } : {}),
559
+ };
560
+ const response = await postApi({
561
+ route: "/report/getThresholdCrossingCountReportForOrg",
562
+ body,
563
+ modifiers: requestModifiers,
564
+ sessionId,
565
+ });
566
+ if (response.error) {
567
+ throw new Error(response.errorMsg ?? "Failed to get org threshold crossing report");
568
+ }
569
+ return (response.crossingCounts ?? []).map(c => ({
570
+ timestampMs: c.timestampMs ?? undefined,
571
+ ingressCount: c.ingressCount ?? undefined,
572
+ egressCount: c.egressCount ?? undefined,
573
+ }));
574
+ }
575
+ export async function getThresholdCrossingCounts(devices, startTimeMs, endTimeMs, crossingObject, dailyResetTimeMinute, requestModifiers, sessionId) {
576
+ const body = {
577
+ devices,
578
+ startTimeMs,
579
+ endTimeMs,
580
+ crossingObject,
581
+ };
582
+ if (dailyResetTimeMinute != null)
583
+ body.dailyResetTimeMinute = dailyResetTimeMinute;
584
+ const response = await postApi({
585
+ route: "/report/getThresholdCrossingCounts",
586
+ body,
587
+ modifiers: requestModifiers,
588
+ sessionId,
589
+ });
590
+ if (response.error) {
591
+ throw new Error(response.errorMsg ?? "Failed to get threshold crossing counts");
592
+ }
593
+ return (response.counts ?? []).map((c) => ({
594
+ count: c.count ?? undefined,
595
+ timestampMs: c.timestampMs ?? undefined,
596
+ }));
597
+ }
598
+ export async function getCountReportsForDevicesAtLocation(locationUuid, startTimeMs, endTimeMs, interval, type, requestModifiers, sessionId) {
599
+ const body = {
600
+ locationUuid,
601
+ startTimeMs,
602
+ endTimeMs,
603
+ interval,
604
+ type,
605
+ };
606
+ const response = await postApi({
607
+ route: "/report/getCountReportsForDevicesAtLocation",
608
+ body,
609
+ modifiers: requestModifiers,
610
+ sessionId,
611
+ });
612
+ if (response.error) {
613
+ throw new Error(response.errorMsg ?? "Failed to get count reports for devices at location");
614
+ }
615
+ const deviceDataMap = {};
616
+ const rawMap = response.timeSeriesDataPointsMap ?? {};
617
+ for (const [deviceUuid, dataPoints] of Object.entries(rawMap)) {
618
+ deviceDataMap[deviceUuid] = (dataPoints || []).map((dp) => ({
619
+ dateUtcString: dp.dateLocal ?? dp.dateUtc ?? undefined,
620
+ eventCountMap: dp.eventCountMap ?? undefined,
621
+ }));
622
+ }
623
+ return deviceDataMap;
624
+ }
625
+ export async function getRunningAverage(uuid, startTimeMs, endTimeMs, interval, scope, timeZone, requestModifiers, sessionId) {
626
+ const body = {
627
+ startTimeMs,
628
+ endTimeMs,
629
+ interval,
630
+ scope,
631
+ };
632
+ if (uuid)
633
+ body.uuid = uuid;
634
+ if (timeZone)
635
+ body.timeZone = timeZone;
636
+ const response = await postApi({
637
+ route: "/report/getRunningAverage",
638
+ body,
639
+ modifiers: requestModifiers,
640
+ sessionId,
641
+ });
642
+ if (response.error) {
643
+ throw new Error(response.errorMsg ?? "Failed to get running average");
644
+ }
645
+ return (response.statsDataPoints ?? []).map((dp) => ({
646
+ date: dp.date ?? undefined,
647
+ stats: dp.stats ?? undefined,
648
+ }));
649
+ }
650
+ export async function triggerScenePrompt(deviceFacetUuid, prompt, promptType, timestampMs, requestModifiers, sessionId) {
651
+ const body = {
652
+ deviceFacetUuid,
653
+ prompt,
654
+ promptType,
655
+ };
656
+ if (timestampMs != null)
657
+ body.timestampMs = timestampMs;
658
+ const response = await postApi({
659
+ route: "/scenequery/triggerPrompt",
660
+ body,
661
+ modifiers: requestModifiers,
662
+ sessionId,
663
+ });
664
+ if (response.error) {
665
+ throw new Error(response.errorMsg ?? "Failed to trigger scene prompt");
666
+ }
667
+ const event = response.event;
668
+ return {
669
+ value: event?.value ?? undefined,
670
+ prompt: event?.prompt ?? undefined,
671
+ timestampMs: event?.timestampMs ?? undefined,
672
+ checkCondition: event?.checkCondition ?? undefined,
673
+ image: event?.image ?? undefined,
674
+ };
675
+ }
676
+ export async function adHocScenePrompt(deviceFacetUuid, promptUuid, timestampMs, requestModifiers, sessionId) {
677
+ const body = {
678
+ deviceFacetUuid,
679
+ promptUuid,
680
+ };
681
+ if (timestampMs != null)
682
+ body.timestampMs = timestampMs;
683
+ const response = await postApi({
684
+ route: "/scenequery/adHocPrompt",
685
+ body,
686
+ modifiers: requestModifiers,
687
+ sessionId,
688
+ });
689
+ if (response.error) {
690
+ throw new Error(response.errorMsg ?? "Failed to execute ad-hoc scene prompt");
691
+ }
692
+ const event = response.event;
693
+ return {
694
+ value: event?.value ?? undefined,
695
+ prompt: event?.prompt ?? undefined,
696
+ timestampMs: event?.timestampMs ?? undefined,
697
+ checkCondition: event?.checkCondition ?? undefined,
698
+ image: event?.image ?? undefined,
699
+ };
700
+ }
701
+ const MAX_FACE_COUNT_PAGES = 5;
702
+ const FACE_COUNT_PAGE_SIZE = 200;
703
+ export async function getUniqueFaceCount(deviceUuid, startTimeMs, endTimeMs, requestModifiers, sessionId) {
704
+ const uniquePersonUuids = new Set();
705
+ let totalFaceEvents = 0;
706
+ let lastEvaluatedKey;
707
+ for (let page = 0; page < MAX_FACE_COUNT_PAGES; page++) {
708
+ const body = {
709
+ searchFilter: {
710
+ deviceUuids: [deviceUuid],
711
+ timestampFilter: {
712
+ rangeStart: new Date(startTimeMs).toISOString(),
713
+ rangeEnd: new Date(endTimeMs).toISOString(),
714
+ },
715
+ },
716
+ pageRequest: {
717
+ maxPageSize: FACE_COUNT_PAGE_SIZE,
718
+ ...(lastEvaluatedKey ? { lastEvaluatedKey } : {}),
719
+ },
720
+ };
721
+ try {
722
+ const response = await postApi({
723
+ route: "/faceRecognition/faceEvent/findFaceEventsByOrg",
724
+ body,
725
+ modifiers: requestModifiers,
726
+ sessionId,
727
+ });
728
+ if (response.error || !response.faceEvents) {
729
+ break;
730
+ }
731
+ for (const event of response.faceEvents) {
732
+ totalFaceEvents++;
733
+ if (event.personUuid) {
734
+ uniquePersonUuids.add(event.personUuid);
735
+ }
736
+ }
737
+ if (response.faceEvents.length < FACE_COUNT_PAGE_SIZE || !response.lastEvaluatedKey) {
738
+ break;
739
+ }
740
+ lastEvaluatedKey = response.lastEvaluatedKey;
741
+ }
742
+ catch (err) {
743
+ logger.error("Error fetching face events for enrichment", err);
744
+ break;
745
+ }
746
+ }
747
+ return { uniqueFaceCount: uniquePersonUuids.size, totalFaceEvents };
748
+ }