@butlr/butlr-mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/cache/occupancy-cache.d.ts +72 -0
  4. package/dist/cache/occupancy-cache.d.ts.map +1 -0
  5. package/dist/cache/occupancy-cache.js +166 -0
  6. package/dist/cache/occupancy-cache.js.map +1 -0
  7. package/dist/cache/topology-cache.d.ts +36 -0
  8. package/dist/cache/topology-cache.d.ts.map +1 -0
  9. package/dist/cache/topology-cache.js +74 -0
  10. package/dist/cache/topology-cache.js.map +1 -0
  11. package/dist/clients/auth-client.d.ts +22 -0
  12. package/dist/clients/auth-client.d.ts.map +1 -0
  13. package/dist/clients/auth-client.js +82 -0
  14. package/dist/clients/auth-client.js.map +1 -0
  15. package/dist/clients/graphql-client.d.ts +6 -0
  16. package/dist/clients/graphql-client.d.ts.map +1 -0
  17. package/dist/clients/graphql-client.js +106 -0
  18. package/dist/clients/graphql-client.js.map +1 -0
  19. package/dist/clients/queries/topology.d.ts +36 -0
  20. package/dist/clients/queries/topology.d.ts.map +1 -0
  21. package/dist/clients/queries/topology.js +252 -0
  22. package/dist/clients/queries/topology.js.map +1 -0
  23. package/dist/clients/reporting-client.d.ts +191 -0
  24. package/dist/clients/reporting-client.d.ts.map +1 -0
  25. package/dist/clients/reporting-client.js +353 -0
  26. package/dist/clients/reporting-client.js.map +1 -0
  27. package/dist/clients/stats-client.d.ts +119 -0
  28. package/dist/clients/stats-client.d.ts.map +1 -0
  29. package/dist/clients/stats-client.js +238 -0
  30. package/dist/clients/stats-client.js.map +1 -0
  31. package/dist/clients/types.d.ts +215 -0
  32. package/dist/clients/types.d.ts.map +1 -0
  33. package/dist/clients/types.js +6 -0
  34. package/dist/clients/types.js.map +1 -0
  35. package/dist/constants.d.ts +3 -0
  36. package/dist/constants.d.ts.map +1 -0
  37. package/dist/constants.js +3 -0
  38. package/dist/constants.js.map +1 -0
  39. package/dist/errors/mcp-errors.d.ts +63 -0
  40. package/dist/errors/mcp-errors.d.ts.map +1 -0
  41. package/dist/errors/mcp-errors.js +144 -0
  42. package/dist/errors/mcp-errors.js.map +1 -0
  43. package/dist/index.d.ts +3 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +43 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tools/butlr-available-rooms.d.ts +49 -0
  48. package/dist/tools/butlr-available-rooms.d.ts.map +1 -0
  49. package/dist/tools/butlr-available-rooms.js +492 -0
  50. package/dist/tools/butlr-available-rooms.js.map +1 -0
  51. package/dist/tools/butlr-fetch-entity-details.d.ts +42 -0
  52. package/dist/tools/butlr-fetch-entity-details.d.ts.map +1 -0
  53. package/dist/tools/butlr-fetch-entity-details.js +276 -0
  54. package/dist/tools/butlr-fetch-entity-details.js.map +1 -0
  55. package/dist/tools/butlr-get-asset-details.d.ts +51 -0
  56. package/dist/tools/butlr-get-asset-details.d.ts.map +1 -0
  57. package/dist/tools/butlr-get-asset-details.js +391 -0
  58. package/dist/tools/butlr-get-asset-details.js.map +1 -0
  59. package/dist/tools/butlr-get-current-occupancy.d.ts +21 -0
  60. package/dist/tools/butlr-get-current-occupancy.d.ts.map +1 -0
  61. package/dist/tools/butlr-get-current-occupancy.js +126 -0
  62. package/dist/tools/butlr-get-current-occupancy.js.map +1 -0
  63. package/dist/tools/butlr-get-occupancy-timeseries.d.ts +31 -0
  64. package/dist/tools/butlr-get-occupancy-timeseries.d.ts.map +1 -0
  65. package/dist/tools/butlr-get-occupancy-timeseries.js +145 -0
  66. package/dist/tools/butlr-get-occupancy-timeseries.js.map +1 -0
  67. package/dist/tools/butlr-hardware-snapshot.d.ts +55 -0
  68. package/dist/tools/butlr-hardware-snapshot.d.ts.map +1 -0
  69. package/dist/tools/butlr-hardware-snapshot.js +556 -0
  70. package/dist/tools/butlr-hardware-snapshot.js.map +1 -0
  71. package/dist/tools/butlr-list-topology.d.ts +27 -0
  72. package/dist/tools/butlr-list-topology.d.ts.map +1 -0
  73. package/dist/tools/butlr-list-topology.js +241 -0
  74. package/dist/tools/butlr-list-topology.js.map +1 -0
  75. package/dist/tools/butlr-search-assets.d.ts +53 -0
  76. package/dist/tools/butlr-search-assets.d.ts.map +1 -0
  77. package/dist/tools/butlr-search-assets.js +206 -0
  78. package/dist/tools/butlr-search-assets.js.map +1 -0
  79. package/dist/tools/butlr-space-busyness.d.ts +23 -0
  80. package/dist/tools/butlr-space-busyness.d.ts.map +1 -0
  81. package/dist/tools/butlr-space-busyness.js +304 -0
  82. package/dist/tools/butlr-space-busyness.js.map +1 -0
  83. package/dist/tools/butlr-traffic-flow.d.ts +39 -0
  84. package/dist/tools/butlr-traffic-flow.d.ts.map +1 -0
  85. package/dist/tools/butlr-traffic-flow.js +369 -0
  86. package/dist/tools/butlr-traffic-flow.js.map +1 -0
  87. package/dist/types/responses.d.ts +253 -0
  88. package/dist/types/responses.d.ts.map +1 -0
  89. package/dist/types/responses.js +8 -0
  90. package/dist/types/responses.js.map +1 -0
  91. package/dist/utils/asset-flattener.d.ts +50 -0
  92. package/dist/utils/asset-flattener.d.ts.map +1 -0
  93. package/dist/utils/asset-flattener.js +131 -0
  94. package/dist/utils/asset-flattener.js.map +1 -0
  95. package/dist/utils/asset-helpers.d.ts +8 -0
  96. package/dist/utils/asset-helpers.d.ts.map +1 -0
  97. package/dist/utils/asset-helpers.js +24 -0
  98. package/dist/utils/asset-helpers.js.map +1 -0
  99. package/dist/utils/field-validator.d.ts +29 -0
  100. package/dist/utils/field-validator.d.ts.map +1 -0
  101. package/dist/utils/field-validator.js +197 -0
  102. package/dist/utils/field-validator.js.map +1 -0
  103. package/dist/utils/fuzzy-match.d.ts +29 -0
  104. package/dist/utils/fuzzy-match.d.ts.map +1 -0
  105. package/dist/utils/fuzzy-match.js +70 -0
  106. package/dist/utils/fuzzy-match.js.map +1 -0
  107. package/dist/utils/graphql-helpers.d.ts +29 -0
  108. package/dist/utils/graphql-helpers.d.ts.map +1 -0
  109. package/dist/utils/graphql-helpers.js +43 -0
  110. package/dist/utils/graphql-helpers.js.map +1 -0
  111. package/dist/utils/natural-language.d.ts +73 -0
  112. package/dist/utils/natural-language.d.ts.map +1 -0
  113. package/dist/utils/natural-language.js +172 -0
  114. package/dist/utils/natural-language.js.map +1 -0
  115. package/dist/utils/occupancy-helpers.d.ts +63 -0
  116. package/dist/utils/occupancy-helpers.d.ts.map +1 -0
  117. package/dist/utils/occupancy-helpers.js +203 -0
  118. package/dist/utils/occupancy-helpers.js.map +1 -0
  119. package/dist/utils/path-builder.d.ts +10 -0
  120. package/dist/utils/path-builder.d.ts.map +1 -0
  121. package/dist/utils/path-builder.js +34 -0
  122. package/dist/utils/path-builder.js.map +1 -0
  123. package/dist/utils/time-range-validator.d.ts +13 -0
  124. package/dist/utils/time-range-validator.d.ts.map +1 -0
  125. package/dist/utils/time-range-validator.js +65 -0
  126. package/dist/utils/time-range-validator.js.map +1 -0
  127. package/dist/utils/timezone-helpers.d.ts +49 -0
  128. package/dist/utils/timezone-helpers.d.ts.map +1 -0
  129. package/dist/utils/timezone-helpers.js +209 -0
  130. package/dist/utils/timezone-helpers.js.map +1 -0
  131. package/dist/utils/tree-formatter.d.ts +23 -0
  132. package/dist/utils/tree-formatter.d.ts.map +1 -0
  133. package/dist/utils/tree-formatter.js +258 -0
  134. package/dist/utils/tree-formatter.js.map +1 -0
  135. package/package.json +93 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporting-client.d.ts","sourceRoot":"","sources":["../../src/clients/reporting-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,353 @@
1
+ import { request as httpRequest } from "undici";
2
+ import { authClient } from "./auth-client.js";
3
+ /**
4
+ * Structured API error with status code for proper error translation
5
+ */
6
+ export class ApiError extends Error {
7
+ statusCode;
8
+ constructor(statusCode, message) {
9
+ super(message);
10
+ this.statusCode = statusCode;
11
+ this.name = "ApiError";
12
+ }
13
+ }
14
+ const BASE_URL = process.env.BUTLR_BASE_URL || "https://api.butlr.io";
15
+ const REPORTING_ENDPOINT = `${BASE_URL}/api/v3/reporting`;
16
+ /**
17
+ * Field mapping: Asset type → v3 API filter field
18
+ * Corrected based on API_CONSTRAINTS.md analysis
19
+ */
20
+ const FILTER_FIELD_MAP = {
21
+ site: "clients", // Organizations/clients
22
+ building: "buildings",
23
+ floor: "spaces", // Floors are called "spaces" in v3 API
24
+ room: "rooms",
25
+ zone: "zones",
26
+ sensor: "sensors",
27
+ hive: "hives",
28
+ };
29
+ /**
30
+ * Measurement mapping: Asset type → v3 API measurement name
31
+ */
32
+ const MEASUREMENT_MAP = {
33
+ room: "room_occupancy",
34
+ zone: "zone_occupancy",
35
+ floor: "floor_occupancy",
36
+ traffic: "traffic", // For traffic-mode sensors
37
+ };
38
+ /**
39
+ * Helper to get filter field name for asset type
40
+ */
41
+ export function getFilterField(assetType) {
42
+ const field = FILTER_FIELD_MAP[assetType];
43
+ if (!field) {
44
+ throw new Error(`Unknown asset type: ${assetType}. Valid types: ${Object.keys(FILTER_FIELD_MAP).join(", ")}`);
45
+ }
46
+ return field;
47
+ }
48
+ /**
49
+ * Helper to get measurement name for asset type
50
+ */
51
+ export function getMeasurement(assetType) {
52
+ const measurement = MEASUREMENT_MAP[assetType];
53
+ if (!measurement) {
54
+ throw new Error(`No measurement mapping for asset type: ${assetType}. Valid types: ${Object.keys(MEASUREMENT_MAP).join(", ")}`);
55
+ }
56
+ return measurement;
57
+ }
58
+ /**
59
+ * Normalize RFC3339 timestamp to ISO-8601
60
+ */
61
+ export function normalizeTimestamp(rfc3339) {
62
+ if (!rfc3339) {
63
+ console.error(`[reporting-client] Invalid timestamp received: ${rfc3339}`);
64
+ return "";
65
+ }
66
+ try {
67
+ const date = new Date(rfc3339);
68
+ if (isNaN(date.getTime())) {
69
+ console.error(`[reporting-client] Invalid timestamp received: ${rfc3339}`);
70
+ return "";
71
+ }
72
+ return date.toISOString();
73
+ }
74
+ catch (error) {
75
+ console.error(`[reporting-client] Failed to normalize timestamp ${rfc3339}:`, error);
76
+ return "";
77
+ }
78
+ }
79
+ /**
80
+ * Query v3 Reporting API
81
+ */
82
+ export async function queryReporting(requestBody) {
83
+ if (process.env.DEBUG) {
84
+ console.error(`[reporting-client] POST ${REPORTING_ENDPOINT}`, JSON.stringify(requestBody, null, 2));
85
+ }
86
+ try {
87
+ // Get auth token
88
+ const token = await authClient.getToken();
89
+ // Make request
90
+ const response = await httpRequest(REPORTING_ENDPOINT, {
91
+ method: "POST",
92
+ headers: {
93
+ "Content-Type": "application/json",
94
+ Authorization: `Bearer ${token}`,
95
+ },
96
+ body: JSON.stringify(requestBody),
97
+ });
98
+ if (response.statusCode !== 200) {
99
+ const errorBody = await response.body.text();
100
+ if (process.env.DEBUG) {
101
+ console.error(`[reporting-client] API error body: ${errorBody}`);
102
+ }
103
+ throw new ApiError(response.statusCode, `Butlr API error (${response.statusCode}). Enable DEBUG=butlr-mcp for details.`);
104
+ }
105
+ const data = (await response.body.json());
106
+ if (process.env.DEBUG) {
107
+ console.error(`[reporting-client] Response: ${data.data?.length || 0} data points`);
108
+ }
109
+ return data;
110
+ }
111
+ catch (error) {
112
+ console.error(`[reporting-client] Request failed:`, error);
113
+ // Translate common errors using structured ApiError
114
+ if (error instanceof ApiError) {
115
+ if (error.statusCode === 401 || error.statusCode === 403) {
116
+ authClient.clearToken();
117
+ throw new ApiError(error.statusCode, "Authentication failed. Check BUTLR_CLIENT_ID and BUTLR_CLIENT_SECRET.");
118
+ }
119
+ if (error.statusCode === 429) {
120
+ throw new ApiError(429, "Rate limit exceeded. Please retry after a few seconds.");
121
+ }
122
+ if (error.statusCode === 400) {
123
+ throw new ApiError(400, "Invalid request parameters. Check your filter configuration.");
124
+ }
125
+ }
126
+ throw error;
127
+ }
128
+ }
129
+ /**
130
+ * Request builder for common occupancy queries
131
+ */
132
+ export class ReportingRequestBuilder {
133
+ request;
134
+ constructor() {
135
+ this.request = {
136
+ filter: {
137
+ measurements: [],
138
+ start: "-24h", // Default to last 24 hours
139
+ },
140
+ options: {
141
+ format: "json",
142
+ timestamp: "RFC3339",
143
+ },
144
+ };
145
+ }
146
+ /**
147
+ * Set asset IDs to query
148
+ */
149
+ assets(assetType, ids) {
150
+ const filterField = getFilterField(assetType);
151
+ this.request.filter[filterField] = {
152
+ eq: ids,
153
+ };
154
+ return this;
155
+ }
156
+ /**
157
+ * Set measurements (auto-mapped from asset type if not provided)
158
+ */
159
+ measurements(measurements) {
160
+ this.request.filter.measurements = measurements;
161
+ return this;
162
+ }
163
+ /**
164
+ * Auto-set measurement based on asset type
165
+ */
166
+ measurementForAssetType(assetType) {
167
+ const measurement = getMeasurement(assetType);
168
+ this.request.filter.measurements = [measurement];
169
+ return this;
170
+ }
171
+ /**
172
+ * Set time range (ISO-8601 or relative like '-24h')
173
+ */
174
+ timeRange(start, stop) {
175
+ this.request.filter.start = start;
176
+ // Only set stop if it's not "now" (API doesn't accept "now" as stop value)
177
+ if (stop && stop !== "now") {
178
+ this.request.filter.stop = stop;
179
+ }
180
+ return this;
181
+ }
182
+ /**
183
+ * Set window aggregation
184
+ */
185
+ window(every, func, timezone) {
186
+ this.request.window = {
187
+ every,
188
+ function: func,
189
+ timezone: timezone || process.env.BUTLR_TIMEZONE || "UTC",
190
+ };
191
+ return this;
192
+ }
193
+ /**
194
+ * Set grouping
195
+ */
196
+ groupBy(order, raw = true) {
197
+ this.request.group_by = { order, raw };
198
+ return this;
199
+ }
200
+ /**
201
+ * Set pagination
202
+ */
203
+ paginate(page, limit) {
204
+ this.request.paginate = { page, limit };
205
+ return this;
206
+ }
207
+ /**
208
+ * Set tags filter
209
+ */
210
+ tags(tags) {
211
+ this.request.filter.tags = { eq: tags };
212
+ return this;
213
+ }
214
+ /**
215
+ * Build and return the request
216
+ */
217
+ build() {
218
+ // Validation
219
+ if (!this.request.filter.measurements.length) {
220
+ throw new Error("At least one measurement is required");
221
+ }
222
+ return this.request;
223
+ }
224
+ /**
225
+ * Build and execute the query
226
+ */
227
+ async execute() {
228
+ return queryReporting(this.build());
229
+ }
230
+ }
231
+ /**
232
+ * Convenience function: Get current occupancy for assets
233
+ * Implements fallback strategy: presence first, then traffic (matches dashboard behavior)
234
+ */
235
+ export async function getCurrentOccupancy(assetType, assetIds) {
236
+ if (assetType !== "room" && assetType !== "zone" && assetType !== "floor") {
237
+ throw new Error(`getCurrentOccupancy only supports room, zone, or floor. Got: ${assetType}`);
238
+ }
239
+ // Calculate time range: 5 minutes ago to now
240
+ // Matches dashboard approach: recent window with median for noise reduction
241
+ const now = new Date();
242
+ const fiveMinAgo = new Date(now.getTime() - 5 * 60 * 1000);
243
+ const start = fiveMinAgo.toISOString();
244
+ const stop = now.toISOString();
245
+ // Try presence data first (room_occupancy, zone_occupancy, floor_occupancy)
246
+ const presenceMeasurement = getMeasurement(assetType);
247
+ const presenceRequest = new ReportingRequestBuilder()
248
+ .assets(assetType, assetIds)
249
+ .measurements([presenceMeasurement])
250
+ .timeRange(start, stop)
251
+ .window("60s", "median") // Use median to smooth noise (matches dashboard)
252
+ .build();
253
+ // Add value filter and raw grouping (no order field - simpler response)
254
+ presenceRequest.filter.value = { gte: 0 };
255
+ presenceRequest.group_by = { raw: true };
256
+ // Build traffic request in parallel with presence
257
+ const trafficMeasurement = `traffic_${presenceMeasurement}`;
258
+ const trafficRequest = new ReportingRequestBuilder()
259
+ .assets(assetType, assetIds)
260
+ .measurements([trafficMeasurement])
261
+ .timeRange(start, stop)
262
+ .window("60s", "median") // Use median to smooth noise
263
+ .build();
264
+ trafficRequest.filter.calibrated = "true";
265
+ trafficRequest.filter.value = { gte: 0 };
266
+ trafficRequest.group_by = { raw: true };
267
+ // Run both queries in parallel — they're independent
268
+ const [presenceResult, trafficResult] = await Promise.allSettled([
269
+ queryReporting(presenceRequest),
270
+ queryReporting(trafficRequest),
271
+ ]);
272
+ const presenceResponse = presenceResult.status === "fulfilled" ? presenceResult.value : { data: [] };
273
+ const trafficResponse = trafficResult.status === "fulfilled" ? trafficResult.value : { data: [] };
274
+ if (trafficResult.status === "rejected") {
275
+ console.error(`[reporting-client] Traffic query failed (may not have traffic sensors):`, trafficResult.reason);
276
+ }
277
+ // Normalize presence data
278
+ // Response structure: { "room_123": { "2025-10-10T00:00:00Z": { "max": 5 } } }
279
+ const presenceData = {};
280
+ if (presenceResponse.data && typeof presenceResponse.data === "object") {
281
+ for (const [assetId, timeSeriesData] of Object.entries(presenceResponse.data)) {
282
+ if (!timeSeriesData || typeof timeSeriesData !== "object")
283
+ continue;
284
+ const timestamps = Object.keys(timeSeriesData).sort();
285
+ if (timestamps.length === 0)
286
+ continue;
287
+ const latestTimestamp = timestamps[timestamps.length - 1];
288
+ const latestData = timeSeriesData[latestTimestamp];
289
+ if (latestData && typeof latestData === "object") {
290
+ const value = latestData.median ?? latestData.max ?? latestData.mean ?? latestData.last ?? 0;
291
+ presenceData[assetId] = {
292
+ start: normalizeTimestamp(latestTimestamp),
293
+ measurement: presenceMeasurement,
294
+ value: value,
295
+ asset_id: assetId,
296
+ asset_name: undefined,
297
+ };
298
+ }
299
+ }
300
+ }
301
+ // Parse traffic data (different structure - timestamps map to arrays)
302
+ const trafficData = {};
303
+ if (trafficResponse.data && typeof trafficResponse.data === "object") {
304
+ for (const [assetId, timeSeriesData] of Object.entries(trafficResponse.data)) {
305
+ if (!timeSeriesData || typeof timeSeriesData !== "object")
306
+ continue;
307
+ // Get all timestamps for this asset
308
+ const timestamps = Object.keys(timeSeriesData).sort();
309
+ if (timestamps.length === 0)
310
+ continue;
311
+ // Get the latest timestamp
312
+ const latestTimestamp = timestamps[timestamps.length - 1];
313
+ const latestData = timeSeriesData[latestTimestamp];
314
+ // Traffic response can be array or object
315
+ let value = 0;
316
+ if (Array.isArray(latestData) && latestData.length > 0) {
317
+ // Extract value from array
318
+ value = latestData[0]?.value ?? 0;
319
+ }
320
+ else if (latestData && typeof latestData === "object") {
321
+ // Extract median (smoothed value)
322
+ value = latestData.median ?? latestData.max ?? latestData.mean ?? latestData.last ?? 0;
323
+ }
324
+ trafficData[assetId] = {
325
+ start: normalizeTimestamp(latestTimestamp),
326
+ measurement: trafficMeasurement,
327
+ value: value,
328
+ asset_id: assetId,
329
+ asset_name: undefined,
330
+ };
331
+ }
332
+ }
333
+ // Merge results: Use HIGHEST value from either source
334
+ const result = [];
335
+ const allAssetIds = new Set([...Object.keys(presenceData), ...Object.keys(trafficData)]);
336
+ for (const assetId of allAssetIds) {
337
+ const presencePoint = presenceData[assetId];
338
+ const trafficPoint = trafficData[assetId];
339
+ // Use whichever has higher value (or exists if only one does)
340
+ if (presencePoint && trafficPoint) {
341
+ // Both exist - use higher value
342
+ result.push(presencePoint.value >= trafficPoint.value ? presencePoint : trafficPoint);
343
+ }
344
+ else if (presencePoint) {
345
+ result.push(presencePoint);
346
+ }
347
+ else if (trafficPoint) {
348
+ result.push(trafficPoint);
349
+ }
350
+ }
351
+ return result;
352
+ }
353
+ //# sourceMappingURL=reporting-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporting-client.js","sourceRoot":"","sources":["../../src/clients/reporting-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * v4 Stats API Request Structure
3
+ * Based on butlr-api-container/pkg/reporting/stats/handler.go
4
+ *
5
+ * Valid measurements (from query.go lines 30-37):
6
+ * - occupancy_avg_presence: Average occupancy from presence sensors
7
+ * - occupancy_avg_traffic: Average occupancy from traffic sensors
8
+ * - occupancy_median_presence: Median occupancy from presence sensors
9
+ * - occupancy_median_traffic: Median occupancy from traffic sensors
10
+ * - occupancy_used_avg_presence: Average (excluding zeros)
11
+ * - occupancy_used_avg_traffic: Average (excluding zeros)
12
+ * - occupancy_used_median_presence: Median (excluding zeros)
13
+ * - occupancy_used_median_traffic: Median (excluding zeros)
14
+ */
15
+ export interface StatsRequest {
16
+ measurements: string[];
17
+ items: string[];
18
+ start?: string;
19
+ stop?: string;
20
+ filters?: {
21
+ time_ranges?: Array<{
22
+ start: string;
23
+ stop: string;
24
+ }>;
25
+ exclude_days_of_week?: string[];
26
+ };
27
+ }
28
+ /**
29
+ * Statistics for a single asset
30
+ */
31
+ export interface AssetStatistics {
32
+ count: number;
33
+ first: number;
34
+ last: number;
35
+ max: number;
36
+ mean: number;
37
+ median: number;
38
+ min: number;
39
+ stdev: number;
40
+ sum: number;
41
+ }
42
+ /**
43
+ * v4 Stats API Response Structure
44
+ */
45
+ export interface StatsResponse {
46
+ data: {
47
+ [assetId: string]: AssetStatistics;
48
+ };
49
+ }
50
+ /**
51
+ * Query v4 Stats API
52
+ *
53
+ * Note: This endpoint is production-ready but may occasionally return 504 errors
54
+ * under heavy load. Implement fallback to client-side calculation if needed.
55
+ */
56
+ export declare function queryStats(statsRequest: StatsRequest): Promise<StatsResponse>;
57
+ /**
58
+ * Request builder for stats queries
59
+ */
60
+ export declare class StatsRequestBuilder {
61
+ private request;
62
+ constructor();
63
+ /**
64
+ * Set measurements to query
65
+ * Use v4-specific measurement names:
66
+ * - occupancy_avg_presence
67
+ * - occupancy_median_presence
68
+ * - occupancy_avg_traffic
69
+ * - occupancy_median_traffic
70
+ */
71
+ measurements(measurements: string[]): this;
72
+ /**
73
+ * Set asset IDs to query
74
+ */
75
+ assets(assetIds: string[]): this;
76
+ /**
77
+ * Set time range (ISO-8601 timestamp or relative like '-7d')
78
+ * Relative times will be converted to ISO-8601
79
+ */
80
+ timeRange(start: string, stop?: string): this;
81
+ /**
82
+ * Set time-of-day filters
83
+ */
84
+ timeOfDayFilter(ranges: Array<{
85
+ start: string;
86
+ stop: string;
87
+ }>): this;
88
+ /**
89
+ * Exclude specific days of week
90
+ */
91
+ excludeDays(days: string[]): this;
92
+ /**
93
+ * Build and return the request
94
+ */
95
+ build(): StatsRequest;
96
+ /**
97
+ * Build and execute the query
98
+ */
99
+ execute(): Promise<StatsResponse>;
100
+ }
101
+ /**
102
+ * Convenience function: Get statistics for multiple assets
103
+ * @param measurement - Use v4 format: 'occupancy_avg_presence', 'occupancy_median_presence', etc.
104
+ * @param assetIds - Array of asset IDs (rooms, floors, zones)
105
+ * @param start - ISO-8601 or relative time (will be converted)
106
+ * @param stop - ISO-8601 timestamp (optional)
107
+ */
108
+ export declare function getAssetStatistics(measurement: string, assetIds: string[], start?: string, stop?: string): Promise<StatsResponse>;
109
+ /**
110
+ * Convenience function: Get statistics for a single asset
111
+ * @param measurement - Use v4 format: 'occupancy_avg_presence', 'occupancy_median_presence', etc.
112
+ */
113
+ export declare function getSingleAssetStats(measurement: string, assetId: string, start?: string, stop?: string): Promise<AssetStatistics | null>;
114
+ /**
115
+ * Calculate client-side statistics as fallback
116
+ * Useful if v4/stats returns 504 error
117
+ */
118
+ export declare function calculateStatistics(values: number[]): AssetStatistics;
119
+ //# sourceMappingURL=stats-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats-client.d.ts","sourceRoot":"","sources":["../../src/clients/stats-client.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QAER,WAAW,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACrD,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;KACjC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE;QACJ,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC;KACpC,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAyEnF;AAiCD;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,OAAO,CAAe;;IAS9B;;;;;;;OAOG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI;IAK1C;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAKhC;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAc7C;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAQrE;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAQjC;;OAEG;IACH,KAAK,IAAI,YAAY;IAarB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC;CAGxC;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,GAAE,MAAc,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,CAAC,CAMxB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAc,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAGjC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,CAuCrE"}