@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.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/cache/occupancy-cache.d.ts +72 -0
- package/dist/cache/occupancy-cache.d.ts.map +1 -0
- package/dist/cache/occupancy-cache.js +166 -0
- package/dist/cache/occupancy-cache.js.map +1 -0
- package/dist/cache/topology-cache.d.ts +36 -0
- package/dist/cache/topology-cache.d.ts.map +1 -0
- package/dist/cache/topology-cache.js +74 -0
- package/dist/cache/topology-cache.js.map +1 -0
- package/dist/clients/auth-client.d.ts +22 -0
- package/dist/clients/auth-client.d.ts.map +1 -0
- package/dist/clients/auth-client.js +82 -0
- package/dist/clients/auth-client.js.map +1 -0
- package/dist/clients/graphql-client.d.ts +6 -0
- package/dist/clients/graphql-client.d.ts.map +1 -0
- package/dist/clients/graphql-client.js +106 -0
- package/dist/clients/graphql-client.js.map +1 -0
- package/dist/clients/queries/topology.d.ts +36 -0
- package/dist/clients/queries/topology.d.ts.map +1 -0
- package/dist/clients/queries/topology.js +252 -0
- package/dist/clients/queries/topology.js.map +1 -0
- package/dist/clients/reporting-client.d.ts +191 -0
- package/dist/clients/reporting-client.d.ts.map +1 -0
- package/dist/clients/reporting-client.js +353 -0
- package/dist/clients/reporting-client.js.map +1 -0
- package/dist/clients/stats-client.d.ts +119 -0
- package/dist/clients/stats-client.d.ts.map +1 -0
- package/dist/clients/stats-client.js +238 -0
- package/dist/clients/stats-client.js.map +1 -0
- package/dist/clients/types.d.ts +215 -0
- package/dist/clients/types.d.ts.map +1 -0
- package/dist/clients/types.js +6 -0
- package/dist/clients/types.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors/mcp-errors.d.ts +63 -0
- package/dist/errors/mcp-errors.d.ts.map +1 -0
- package/dist/errors/mcp-errors.js +144 -0
- package/dist/errors/mcp-errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/butlr-available-rooms.d.ts +49 -0
- package/dist/tools/butlr-available-rooms.d.ts.map +1 -0
- package/dist/tools/butlr-available-rooms.js +492 -0
- package/dist/tools/butlr-available-rooms.js.map +1 -0
- package/dist/tools/butlr-fetch-entity-details.d.ts +42 -0
- package/dist/tools/butlr-fetch-entity-details.d.ts.map +1 -0
- package/dist/tools/butlr-fetch-entity-details.js +276 -0
- package/dist/tools/butlr-fetch-entity-details.js.map +1 -0
- package/dist/tools/butlr-get-asset-details.d.ts +51 -0
- package/dist/tools/butlr-get-asset-details.d.ts.map +1 -0
- package/dist/tools/butlr-get-asset-details.js +391 -0
- package/dist/tools/butlr-get-asset-details.js.map +1 -0
- package/dist/tools/butlr-get-current-occupancy.d.ts +21 -0
- package/dist/tools/butlr-get-current-occupancy.d.ts.map +1 -0
- package/dist/tools/butlr-get-current-occupancy.js +126 -0
- package/dist/tools/butlr-get-current-occupancy.js.map +1 -0
- package/dist/tools/butlr-get-occupancy-timeseries.d.ts +31 -0
- package/dist/tools/butlr-get-occupancy-timeseries.d.ts.map +1 -0
- package/dist/tools/butlr-get-occupancy-timeseries.js +145 -0
- package/dist/tools/butlr-get-occupancy-timeseries.js.map +1 -0
- package/dist/tools/butlr-hardware-snapshot.d.ts +55 -0
- package/dist/tools/butlr-hardware-snapshot.d.ts.map +1 -0
- package/dist/tools/butlr-hardware-snapshot.js +556 -0
- package/dist/tools/butlr-hardware-snapshot.js.map +1 -0
- package/dist/tools/butlr-list-topology.d.ts +27 -0
- package/dist/tools/butlr-list-topology.d.ts.map +1 -0
- package/dist/tools/butlr-list-topology.js +241 -0
- package/dist/tools/butlr-list-topology.js.map +1 -0
- package/dist/tools/butlr-search-assets.d.ts +53 -0
- package/dist/tools/butlr-search-assets.d.ts.map +1 -0
- package/dist/tools/butlr-search-assets.js +206 -0
- package/dist/tools/butlr-search-assets.js.map +1 -0
- package/dist/tools/butlr-space-busyness.d.ts +23 -0
- package/dist/tools/butlr-space-busyness.d.ts.map +1 -0
- package/dist/tools/butlr-space-busyness.js +304 -0
- package/dist/tools/butlr-space-busyness.js.map +1 -0
- package/dist/tools/butlr-traffic-flow.d.ts +39 -0
- package/dist/tools/butlr-traffic-flow.d.ts.map +1 -0
- package/dist/tools/butlr-traffic-flow.js +369 -0
- package/dist/tools/butlr-traffic-flow.js.map +1 -0
- package/dist/types/responses.d.ts +253 -0
- package/dist/types/responses.d.ts.map +1 -0
- package/dist/types/responses.js +8 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/utils/asset-flattener.d.ts +50 -0
- package/dist/utils/asset-flattener.d.ts.map +1 -0
- package/dist/utils/asset-flattener.js +131 -0
- package/dist/utils/asset-flattener.js.map +1 -0
- package/dist/utils/asset-helpers.d.ts +8 -0
- package/dist/utils/asset-helpers.d.ts.map +1 -0
- package/dist/utils/asset-helpers.js +24 -0
- package/dist/utils/asset-helpers.js.map +1 -0
- package/dist/utils/field-validator.d.ts +29 -0
- package/dist/utils/field-validator.d.ts.map +1 -0
- package/dist/utils/field-validator.js +197 -0
- package/dist/utils/field-validator.js.map +1 -0
- package/dist/utils/fuzzy-match.d.ts +29 -0
- package/dist/utils/fuzzy-match.d.ts.map +1 -0
- package/dist/utils/fuzzy-match.js +70 -0
- package/dist/utils/fuzzy-match.js.map +1 -0
- package/dist/utils/graphql-helpers.d.ts +29 -0
- package/dist/utils/graphql-helpers.d.ts.map +1 -0
- package/dist/utils/graphql-helpers.js +43 -0
- package/dist/utils/graphql-helpers.js.map +1 -0
- package/dist/utils/natural-language.d.ts +73 -0
- package/dist/utils/natural-language.d.ts.map +1 -0
- package/dist/utils/natural-language.js +172 -0
- package/dist/utils/natural-language.js.map +1 -0
- package/dist/utils/occupancy-helpers.d.ts +63 -0
- package/dist/utils/occupancy-helpers.d.ts.map +1 -0
- package/dist/utils/occupancy-helpers.js +203 -0
- package/dist/utils/occupancy-helpers.js.map +1 -0
- package/dist/utils/path-builder.d.ts +10 -0
- package/dist/utils/path-builder.d.ts.map +1 -0
- package/dist/utils/path-builder.js +34 -0
- package/dist/utils/path-builder.js.map +1 -0
- package/dist/utils/time-range-validator.d.ts +13 -0
- package/dist/utils/time-range-validator.d.ts.map +1 -0
- package/dist/utils/time-range-validator.js +65 -0
- package/dist/utils/time-range-validator.js.map +1 -0
- package/dist/utils/timezone-helpers.d.ts +49 -0
- package/dist/utils/timezone-helpers.d.ts.map +1 -0
- package/dist/utils/timezone-helpers.js +209 -0
- package/dist/utils/timezone-helpers.js.map +1 -0
- package/dist/utils/tree-formatter.d.ts +23 -0
- package/dist/utils/tree-formatter.d.ts.map +1 -0
- package/dist/utils/tree-formatter.js +258 -0
- package/dist/utils/tree-formatter.js.map +1 -0
- 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"}
|