@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,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time range validation for timeseries queries
|
|
3
|
+
* Prevents excessive data queries that could timeout or overwhelm the API
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validate time range based on interval
|
|
7
|
+
* @param interval Aggregation interval ('1m', '1h', '1d')
|
|
8
|
+
* @param start Start time (ISO-8601 or relative like '-24h')
|
|
9
|
+
* @param stop Stop time (ISO-8601 or relative like 'now')
|
|
10
|
+
* @throws Error if time range exceeds limits for the given interval
|
|
11
|
+
*/
|
|
12
|
+
export function validateTimeRange(interval, start, stop) {
|
|
13
|
+
// Parse times - handle relative times by converting to absolute
|
|
14
|
+
const startTime = parseTime(start);
|
|
15
|
+
const stopTime = parseTime(stop);
|
|
16
|
+
// Calculate duration in hours
|
|
17
|
+
const durationMs = stopTime.getTime() - startTime.getTime();
|
|
18
|
+
const durationHours = durationMs / (1000 * 60 * 60);
|
|
19
|
+
const durationDays = durationHours / 24;
|
|
20
|
+
// Validate based on interval
|
|
21
|
+
if (interval === "1m" && durationHours > 1) {
|
|
22
|
+
throw new Error(`Time range too large for 1m interval. Maximum: 1 hour. Requested: ${durationHours.toFixed(1)} hours.`);
|
|
23
|
+
}
|
|
24
|
+
if (interval === "1h" && durationHours > 48) {
|
|
25
|
+
throw new Error(`Time range too large for 1h interval. Maximum: 48 hours. Requested: ${durationHours.toFixed(1)} hours.`);
|
|
26
|
+
}
|
|
27
|
+
if (interval === "1d" && durationDays > 60) {
|
|
28
|
+
throw new Error(`Time range too large for 1d interval. Maximum: 60 days. Requested: ${durationDays.toFixed(1)} days.`);
|
|
29
|
+
}
|
|
30
|
+
// Validate start is before stop
|
|
31
|
+
if (startTime >= stopTime) {
|
|
32
|
+
throw new Error(`Start time must be before stop time. Start: ${start}, Stop: ${stop}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parse time string (ISO-8601 or relative) to Date
|
|
37
|
+
*/
|
|
38
|
+
function parseTime(timeStr) {
|
|
39
|
+
// Handle relative times like '-24h', '-1d', 'now'
|
|
40
|
+
if (timeStr === "now") {
|
|
41
|
+
return new Date();
|
|
42
|
+
}
|
|
43
|
+
// Relative time pattern: -<number><unit>
|
|
44
|
+
const relativeMatch = timeStr.match(/^-(\d+)(m|h|d)$/);
|
|
45
|
+
if (relativeMatch) {
|
|
46
|
+
const amount = parseInt(relativeMatch[1], 10);
|
|
47
|
+
const unit = relativeMatch[2];
|
|
48
|
+
const now = new Date();
|
|
49
|
+
switch (unit) {
|
|
50
|
+
case "m":
|
|
51
|
+
return new Date(now.getTime() - amount * 60 * 1000);
|
|
52
|
+
case "h":
|
|
53
|
+
return new Date(now.getTime() - amount * 60 * 60 * 1000);
|
|
54
|
+
case "d":
|
|
55
|
+
return new Date(now.getTime() - amount * 24 * 60 * 60 * 1000);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Try parsing as ISO-8601
|
|
59
|
+
const parsed = new Date(timeStr);
|
|
60
|
+
if (isNaN(parsed.getTime())) {
|
|
61
|
+
throw new Error(`Invalid time format: ${timeStr}. Use ISO-8601 or relative format like '-24h'.`);
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=time-range-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-range-validator.js","sourceRoot":"","sources":["../../src/utils/time-range-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,KAAa,EAAE,IAAY;IAC7E,gEAAgE;IAChE,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAEjC,8BAA8B;IAC9B,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;IAC5D,MAAM,aAAa,GAAG,UAAU,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,aAAa,GAAG,EAAE,CAAC;IAExC,6BAA6B;IAC7B,IAAI,QAAQ,KAAK,IAAI,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,qEAAqE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACvG,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,KAAK,IAAI,IAAI,aAAa,GAAG,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,uEAAuE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACzG,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,KAAK,IAAI,IAAI,YAAY,GAAG,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,sEAAsE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CACtG,CAAC;IACJ,CAAC;IAED,gCAAgC;IAChC,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,+CAA+C,KAAK,WAAW,IAAI,EAAE,CAAC,CAAC;IACzF,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,OAAe;IAChC,kDAAkD;IAClD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,OAAO,IAAI,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,yCAAyC;IACzC,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACvD,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,GAAG;gBACN,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YACtD,KAAK,GAAG;gBACN,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAC3D,KAAK,GAAG;gBACN,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,wBAAwB,OAAO,gDAAgD,CAChF,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timezone utilities for converting between UTC and site-specific timezones
|
|
3
|
+
* All Butlr timestamps are UTC; timezones are stored at Site level
|
|
4
|
+
*/
|
|
5
|
+
import type { Site, Building, Floor } from "../clients/types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Get the site timezone for any asset (floor, room, zone)
|
|
8
|
+
* Traverses hierarchy to find parent site
|
|
9
|
+
*/
|
|
10
|
+
export declare function getTimezoneForAsset(assetId: string, assetType: "floor" | "room" | "zone", floors: Floor[], buildings: Building[], sites: Site[]): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Get timezone abbreviation (PST, PDT, CST, EST, etc.)
|
|
13
|
+
* Uses Intl.DateTimeFormat to get the correct abbreviation for the given date
|
|
14
|
+
*/
|
|
15
|
+
export declare function getTimezoneAbbreviation(timezone: string, date?: Date): string;
|
|
16
|
+
/**
|
|
17
|
+
* Get UTC offset for a timezone at a specific date
|
|
18
|
+
* Returns format like "UTC-5" or "UTC+9"
|
|
19
|
+
*/
|
|
20
|
+
export declare function getUTCOffset(timezone: string, date?: Date): string;
|
|
21
|
+
/**
|
|
22
|
+
* Check if daylight saving time is currently active for a timezone
|
|
23
|
+
*/
|
|
24
|
+
export declare function isDSTActive(timezone: string, date?: Date): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Format current local time for display
|
|
27
|
+
* Returns: "2:35 PM PST on Oct 15, 2025"
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCurrentLocalTime(timezone: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Get midnight (start of day) in a specific timezone
|
|
32
|
+
* Returns UTC Date object representing midnight in the given timezone
|
|
33
|
+
*
|
|
34
|
+
* Example: getLocalMidnight(new Date("2025-10-15T10:00:00Z"), "Asia/Kolkata")
|
|
35
|
+
* Returns: Date object for "2025-10-14T18:30:00Z" (which is Oct 15 midnight IST)
|
|
36
|
+
*/
|
|
37
|
+
export declare function getLocalMidnight(date: Date, timezone: string): Date;
|
|
38
|
+
/**
|
|
39
|
+
* Build rich timezone metadata for an asset
|
|
40
|
+
*/
|
|
41
|
+
export interface TimezoneMetadata {
|
|
42
|
+
site_timezone: string;
|
|
43
|
+
timezone_offset: string;
|
|
44
|
+
timezone_abbr: string;
|
|
45
|
+
current_local_time: string;
|
|
46
|
+
dst_active: boolean;
|
|
47
|
+
}
|
|
48
|
+
export declare function buildTimezoneMetadata(timezone: string): TimezoneMetadata;
|
|
49
|
+
//# sourceMappingURL=timezone-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/timezone-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,EACpC,MAAM,EAAE,KAAK,EAAE,EACf,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,GACZ,MAAM,GAAG,IAAI,CAqCf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,CAgBzF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,CAsB9E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,OAAO,CAqB9E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuB5D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsDnE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CASxE"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timezone utilities for converting between UTC and site-specific timezones
|
|
3
|
+
* All Butlr timestamps are UTC; timezones are stored at Site level
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Get the site timezone for any asset (floor, room, zone)
|
|
7
|
+
* Traverses hierarchy to find parent site
|
|
8
|
+
*/
|
|
9
|
+
export function getTimezoneForAsset(assetId, assetType, floors, buildings, sites) {
|
|
10
|
+
if (assetType === "floor") {
|
|
11
|
+
const floor = floors.find((f) => f.id === assetId);
|
|
12
|
+
if (!floor)
|
|
13
|
+
return null;
|
|
14
|
+
const building = buildings.find((b) => b.id === floor.building_id);
|
|
15
|
+
if (!building)
|
|
16
|
+
return null;
|
|
17
|
+
const site = sites.find((s) => s.id === building.site_id);
|
|
18
|
+
return site?.timezone || null;
|
|
19
|
+
}
|
|
20
|
+
if (assetType === "room") {
|
|
21
|
+
// Find floor for this room
|
|
22
|
+
const floor = floors.find((f) => f.rooms?.some((r) => r.id === assetId));
|
|
23
|
+
if (!floor)
|
|
24
|
+
return null;
|
|
25
|
+
const building = buildings.find((b) => b.id === floor.building_id);
|
|
26
|
+
if (!building)
|
|
27
|
+
return null;
|
|
28
|
+
const site = sites.find((s) => s.id === building.site_id);
|
|
29
|
+
return site?.timezone || null;
|
|
30
|
+
}
|
|
31
|
+
if (assetType === "zone") {
|
|
32
|
+
// Find floor for this zone
|
|
33
|
+
const floor = floors.find((f) => f.zones?.some((z) => z.id === assetId));
|
|
34
|
+
if (!floor)
|
|
35
|
+
return null;
|
|
36
|
+
const building = buildings.find((b) => b.id === floor.building_id);
|
|
37
|
+
if (!building)
|
|
38
|
+
return null;
|
|
39
|
+
const site = sites.find((s) => s.id === building.site_id);
|
|
40
|
+
return site?.timezone || null;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get timezone abbreviation (PST, PDT, CST, EST, etc.)
|
|
46
|
+
* Uses Intl.DateTimeFormat to get the correct abbreviation for the given date
|
|
47
|
+
*/
|
|
48
|
+
export function getTimezoneAbbreviation(timezone, date = new Date()) {
|
|
49
|
+
try {
|
|
50
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
51
|
+
timeZone: timezone,
|
|
52
|
+
timeZoneName: "short",
|
|
53
|
+
});
|
|
54
|
+
const parts = formatter.formatToParts(date);
|
|
55
|
+
const tzPart = parts.find((p) => p.type === "timeZoneName");
|
|
56
|
+
return tzPart?.value || "UTC";
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (process.env.DEBUG) {
|
|
60
|
+
console.error(`[timezone-helpers] Failed to get abbreviation for ${timezone}:`, error);
|
|
61
|
+
}
|
|
62
|
+
return "UTC";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get UTC offset for a timezone at a specific date
|
|
67
|
+
* Returns format like "UTC-5" or "UTC+9"
|
|
68
|
+
*/
|
|
69
|
+
export function getUTCOffset(timezone, date = new Date()) {
|
|
70
|
+
try {
|
|
71
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
72
|
+
timeZone: timezone,
|
|
73
|
+
timeZoneName: "longOffset",
|
|
74
|
+
});
|
|
75
|
+
const parts = formatter.formatToParts(date);
|
|
76
|
+
const offsetPart = parts.find((p) => p.type === "timeZoneName");
|
|
77
|
+
if (offsetPart?.value) {
|
|
78
|
+
// Convert "GMT-5" to "UTC-5"
|
|
79
|
+
return offsetPart.value.replace("GMT", "UTC");
|
|
80
|
+
}
|
|
81
|
+
return "UTC";
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (process.env.DEBUG) {
|
|
85
|
+
console.error(`[timezone-helpers] Failed to get offset for ${timezone}:`, error);
|
|
86
|
+
}
|
|
87
|
+
return "UTC";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if daylight saving time is currently active for a timezone
|
|
92
|
+
*/
|
|
93
|
+
export function isDSTActive(timezone, date = new Date()) {
|
|
94
|
+
try {
|
|
95
|
+
const jan = new Date(date.getFullYear(), 0, 1);
|
|
96
|
+
const jul = new Date(date.getFullYear(), 6, 1);
|
|
97
|
+
const janOffset = getUTCOffset(timezone, jan);
|
|
98
|
+
const julOffset = getUTCOffset(timezone, jul);
|
|
99
|
+
const currentOffset = getUTCOffset(timezone, date);
|
|
100
|
+
// If Jan and Jul offsets are the same, timezone has no DST
|
|
101
|
+
if (janOffset === julOffset)
|
|
102
|
+
return false;
|
|
103
|
+
// Standard time has the smaller (more negative) UTC offset string.
|
|
104
|
+
// This works for both hemispheres:
|
|
105
|
+
// - Northern: Jan is standard (e.g., "-08:00"), Jul is DST ("-07:00")
|
|
106
|
+
// - Southern: Jul is standard (e.g., "+10:00"), Jan is DST ("+11:00")
|
|
107
|
+
const standardOffset = janOffset < julOffset ? janOffset : julOffset;
|
|
108
|
+
return currentOffset !== standardOffset;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Format current local time for display
|
|
116
|
+
* Returns: "2:35 PM PST on Oct 15, 2025"
|
|
117
|
+
*/
|
|
118
|
+
export function getCurrentLocalTime(timezone) {
|
|
119
|
+
try {
|
|
120
|
+
const now = new Date();
|
|
121
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
122
|
+
timeZone: timezone,
|
|
123
|
+
month: "short",
|
|
124
|
+
day: "numeric",
|
|
125
|
+
year: "numeric",
|
|
126
|
+
hour: "numeric",
|
|
127
|
+
minute: "2-digit",
|
|
128
|
+
hour12: true,
|
|
129
|
+
});
|
|
130
|
+
const tzAbbr = getTimezoneAbbreviation(timezone, now);
|
|
131
|
+
const dateStr = formatter.format(now);
|
|
132
|
+
return `${dateStr} ${tzAbbr}`;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (process.env.DEBUG) {
|
|
136
|
+
console.error(`[timezone-helpers] Failed to format time for ${timezone}:`, error);
|
|
137
|
+
}
|
|
138
|
+
return new Date().toISOString();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get midnight (start of day) in a specific timezone
|
|
143
|
+
* Returns UTC Date object representing midnight in the given timezone
|
|
144
|
+
*
|
|
145
|
+
* Example: getLocalMidnight(new Date("2025-10-15T10:00:00Z"), "Asia/Kolkata")
|
|
146
|
+
* Returns: Date object for "2025-10-14T18:30:00Z" (which is Oct 15 midnight IST)
|
|
147
|
+
*/
|
|
148
|
+
export function getLocalMidnight(date, timezone) {
|
|
149
|
+
try {
|
|
150
|
+
// Get the local date in the target timezone using Intl.DateTimeFormat
|
|
151
|
+
const localDate = new Intl.DateTimeFormat("en-CA", {
|
|
152
|
+
timeZone: timezone,
|
|
153
|
+
year: "numeric",
|
|
154
|
+
month: "2-digit",
|
|
155
|
+
day: "2-digit",
|
|
156
|
+
}).format(date);
|
|
157
|
+
// localDate is "YYYY-MM-DD" in the target timezone
|
|
158
|
+
const [year, month, day] = localDate.split("-").map(Number);
|
|
159
|
+
// Start with a rough estimate: UTC midnight on that date
|
|
160
|
+
let estimate = new Date(Date.UTC(year, month - 1, day, 0, 0, 0));
|
|
161
|
+
// Iteratively adjust to find the UTC time that corresponds to midnight local time
|
|
162
|
+
for (let i = 0; i < 3; i++) {
|
|
163
|
+
const localAtEstimate = new Intl.DateTimeFormat("en-CA", {
|
|
164
|
+
timeZone: timezone,
|
|
165
|
+
year: "numeric",
|
|
166
|
+
month: "2-digit",
|
|
167
|
+
day: "2-digit",
|
|
168
|
+
hour: "2-digit",
|
|
169
|
+
minute: "2-digit",
|
|
170
|
+
second: "2-digit",
|
|
171
|
+
hour12: false,
|
|
172
|
+
}).formatToParts(estimate);
|
|
173
|
+
const h = parseInt(localAtEstimate.find((p) => p.type === "hour")?.value || "0");
|
|
174
|
+
const m = parseInt(localAtEstimate.find((p) => p.type === "minute")?.value || "0");
|
|
175
|
+
const d = parseInt(localAtEstimate.find((p) => p.type === "day")?.value || "0");
|
|
176
|
+
if (h === 0 && m === 0 && d === day)
|
|
177
|
+
break; // Found it
|
|
178
|
+
// Adjust: we want local to be 00:00 on `day`
|
|
179
|
+
let diffMinutes = h * 60 + m;
|
|
180
|
+
if (d !== day) {
|
|
181
|
+
// Day is wrong, big adjustment needed
|
|
182
|
+
if (d < day)
|
|
183
|
+
diffMinutes -= 24 * 60; // We're a day behind
|
|
184
|
+
else
|
|
185
|
+
diffMinutes += 24 * 60; // We're a day ahead
|
|
186
|
+
}
|
|
187
|
+
estimate = new Date(estimate.getTime() - diffMinutes * 60 * 1000);
|
|
188
|
+
}
|
|
189
|
+
return estimate;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console.error(`[timezone] Failed to calculate local midnight for ${timezone}:`, error);
|
|
193
|
+
// Fallback to UTC midnight
|
|
194
|
+
const utcMidnight = new Date(date);
|
|
195
|
+
utcMidnight.setUTCHours(0, 0, 0, 0);
|
|
196
|
+
return utcMidnight;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export function buildTimezoneMetadata(timezone) {
|
|
200
|
+
const now = new Date();
|
|
201
|
+
return {
|
|
202
|
+
site_timezone: timezone,
|
|
203
|
+
timezone_offset: getUTCOffset(timezone, now),
|
|
204
|
+
timezone_abbr: getTimezoneAbbreviation(timezone, now),
|
|
205
|
+
current_local_time: getCurrentLocalTime(timezone),
|
|
206
|
+
dst_active: isDSTActive(timezone, now),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=timezone-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone-helpers.js","sourceRoot":"","sources":["../../src/utils/timezone-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAe,EACf,SAAoC,EACpC,MAAe,EACf,SAAqB,EACrB,KAAa;IAEb,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;IAChC,CAAC;IAED,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,2BAA2B;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;IAChC,CAAC;IAED,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,2BAA2B;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB,EAAE,OAAa,IAAI,IAAI,EAAE;IAC/E,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC;QAC5D,OAAO,MAAM,EAAE,KAAK,IAAI,KAAK,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,qDAAqD,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,OAAa,IAAI,IAAI,EAAE;IACpE,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC;QAEhE,IAAI,UAAU,EAAE,KAAK,EAAE,CAAC;YACtB,6BAA6B;YAC7B,OAAO,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,+CAA+C,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,OAAa,IAAI,IAAI,EAAE;IACnE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAE/C,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC9C,MAAM,aAAa,GAAG,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEnD,2DAA2D;QAC3D,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAE1C,mEAAmE;QACnE,mCAAmC;QACnC,sEAAsE;QACtE,sEAAsE;QACtE,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,OAAO,aAAa,KAAK,cAAc,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,OAAO;YACd,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAEtC,OAAO,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,gDAAgD,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACpF,CAAC;QACD,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAU,EAAE,QAAgB;IAC3D,IAAI,CAAC;QACH,sEAAsE;QACtE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,SAAS;SACf,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChB,mDAAmD;QAEnD,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAE5D,yDAAyD;QACzD,IAAI,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAEjE,kFAAkF;QAClF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,eAAe,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;gBACvD,QAAQ,EAAE,QAAQ;gBAClB,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,SAAS;gBAChB,GAAG,EAAE,SAAS;gBACd,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,KAAK;aACd,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAE3B,MAAM,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;YACjF,MAAM,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;YACnF,MAAM,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;YAEhF,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG;gBAAE,MAAM,CAAC,WAAW;YAEvD,6CAA6C;YAC7C,IAAI,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC7B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACd,sCAAsC;gBACtC,IAAI,CAAC,GAAG,GAAG;oBACT,WAAW,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,qBAAqB;;oBAC1C,WAAW,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,oBAAoB;YACnD,CAAC;YACD,QAAQ,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qDAAqD,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACvF,2BAA2B;QAC3B,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACpC,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAaD,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,OAAO;QACL,aAAa,EAAE,QAAQ;QACvB,eAAe,EAAE,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC;QAC5C,aAAa,EAAE,uBAAuB,CAAC,QAAQ,EAAE,GAAG,CAAC;QACrD,kBAAkB,EAAE,mBAAmB,CAAC,QAAQ,CAAC;QACjD,UAAU,EAAE,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC;KACvC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree formatter for topology visualization
|
|
3
|
+
* Formats organizational hierarchy in a tree structure with depth control
|
|
4
|
+
*
|
|
5
|
+
* ULTRA-COMPACT FORMAT: Positional arrays [id, display_identifier, children?]
|
|
6
|
+
* - Position 0: Asset ID
|
|
7
|
+
* - Position 1: Display identifier (name for sites/buildings/floors/rooms/zones, serialNumber for hives, mac_address for sensors)
|
|
8
|
+
* - Position 2: Children array (optional, only if has children)
|
|
9
|
+
*/
|
|
10
|
+
import type { Site } from "../clients/types.js";
|
|
11
|
+
import type { TopologyNode } from "../types/responses.js";
|
|
12
|
+
/**
|
|
13
|
+
* Format topology as a tree structure with depth controls
|
|
14
|
+
* Hierarchy: Sites(0) -> Buildings(1) -> Floors(2) -> Rooms/Zones(3) -> Hives(4) -> Sensors(5)
|
|
15
|
+
* Format: [id, display_name, children?]
|
|
16
|
+
*
|
|
17
|
+
* @param sites Array of sites with full topology
|
|
18
|
+
* @param startingDepth Depth level to start showing (0=sites, 1=buildings, etc.)
|
|
19
|
+
* @param traversalDepth How many levels below starting_depth to traverse (0=starting level only)
|
|
20
|
+
* @returns Ultra-compact array tree structure
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatTopologyTree(sites: Site[], startingDepth?: number, traversalDepth?: number): TopologyNode[];
|
|
23
|
+
//# sourceMappingURL=tree-formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-formatter.d.ts","sourceRoot":"","sources":["../../src/utils/tree-formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA6C,MAAM,qBAAqB,CAAC;AAC3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAyB1D;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,IAAI,EAAE,EACb,aAAa,GAAE,MAAU,EACzB,cAAc,GAAE,MAAU,GACzB,YAAY,EAAE,CAUhB"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree formatter for topology visualization
|
|
3
|
+
* Formats organizational hierarchy in a tree structure with depth control
|
|
4
|
+
*
|
|
5
|
+
* ULTRA-COMPACT FORMAT: Positional arrays [id, display_identifier, children?]
|
|
6
|
+
* - Position 0: Asset ID
|
|
7
|
+
* - Position 1: Display identifier (name for sites/buildings/floors/rooms/zones, serialNumber for hives, mac_address for sensors)
|
|
8
|
+
* - Position 2: Children array (optional, only if has children)
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Depth level mappings
|
|
12
|
+
*/
|
|
13
|
+
const DEPTH_LEVELS = {
|
|
14
|
+
SITE: 0,
|
|
15
|
+
BUILDING: 1,
|
|
16
|
+
FLOOR: 2,
|
|
17
|
+
ROOM_ZONE: 3,
|
|
18
|
+
HIVE: 4,
|
|
19
|
+
SENSOR: 5,
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Determine whether children should be included at the current depth level.
|
|
23
|
+
*/
|
|
24
|
+
function shouldTraverse(currentDepth, startingDepth, traversalDepth) {
|
|
25
|
+
return currentDepth >= startingDepth && currentDepth - startingDepth < traversalDepth;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Format topology as a tree structure with depth controls
|
|
29
|
+
* Hierarchy: Sites(0) -> Buildings(1) -> Floors(2) -> Rooms/Zones(3) -> Hives(4) -> Sensors(5)
|
|
30
|
+
* Format: [id, display_name, children?]
|
|
31
|
+
*
|
|
32
|
+
* @param sites Array of sites with full topology
|
|
33
|
+
* @param startingDepth Depth level to start showing (0=sites, 1=buildings, etc.)
|
|
34
|
+
* @param traversalDepth How many levels below starting_depth to traverse (0=starting level only)
|
|
35
|
+
* @returns Ultra-compact array tree structure
|
|
36
|
+
*/
|
|
37
|
+
export function formatTopologyTree(sites, startingDepth = 0, traversalDepth = 0) {
|
|
38
|
+
const currentDepth = DEPTH_LEVELS.SITE;
|
|
39
|
+
// If starting depth is 0 (sites), format sites
|
|
40
|
+
if (startingDepth === 0) {
|
|
41
|
+
return sites.map((site) => formatSite(site, currentDepth, startingDepth, traversalDepth));
|
|
42
|
+
}
|
|
43
|
+
// Otherwise, collect assets at starting depth from all sites
|
|
44
|
+
return collectAssetsAtDepth(sites, startingDepth, traversalDepth);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Collect all assets at a specific depth level
|
|
48
|
+
*/
|
|
49
|
+
function collectAssetsAtDepth(sites, targetDepth, traversalDepth) {
|
|
50
|
+
const results = [];
|
|
51
|
+
for (const site of sites) {
|
|
52
|
+
if (targetDepth === DEPTH_LEVELS.BUILDING) {
|
|
53
|
+
// Collect buildings
|
|
54
|
+
site.buildings?.forEach((building) => {
|
|
55
|
+
results.push(formatBuilding(building, DEPTH_LEVELS.BUILDING, targetDepth, traversalDepth));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else if (targetDepth === DEPTH_LEVELS.FLOOR) {
|
|
59
|
+
// Collect floors
|
|
60
|
+
site.buildings?.forEach((building) => {
|
|
61
|
+
building.floors?.forEach((floor) => {
|
|
62
|
+
results.push(formatFloor(floor, DEPTH_LEVELS.FLOOR, targetDepth, traversalDepth));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else if (targetDepth === DEPTH_LEVELS.ROOM_ZONE) {
|
|
67
|
+
// Collect rooms and zones
|
|
68
|
+
site.buildings?.forEach((building) => {
|
|
69
|
+
building.floors?.forEach((floor) => {
|
|
70
|
+
floor.rooms?.forEach((room) => {
|
|
71
|
+
results.push(formatRoom(room, floor, DEPTH_LEVELS.ROOM_ZONE, targetDepth, traversalDepth));
|
|
72
|
+
});
|
|
73
|
+
floor.zones?.forEach((zone) => {
|
|
74
|
+
results.push(formatZone(zone));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else if (targetDepth === DEPTH_LEVELS.HIVE) {
|
|
80
|
+
// Collect hives
|
|
81
|
+
site.buildings?.forEach((building) => {
|
|
82
|
+
building.floors?.forEach((floor) => {
|
|
83
|
+
floor.hives?.forEach((hive) => {
|
|
84
|
+
results.push(formatHive(hive, floor, DEPTH_LEVELS.HIVE, targetDepth, traversalDepth));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else if (targetDepth === DEPTH_LEVELS.SENSOR) {
|
|
90
|
+
// Collect sensors
|
|
91
|
+
site.buildings?.forEach((building) => {
|
|
92
|
+
building.floors?.forEach((floor) => {
|
|
93
|
+
floor.sensors?.forEach((sensor) => {
|
|
94
|
+
results.push(formatSensor(sensor));
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Format a single site
|
|
104
|
+
* Returns: [id, name, children?]
|
|
105
|
+
*/
|
|
106
|
+
function formatSite(site, currentDepth, startingDepth, traversalDepth) {
|
|
107
|
+
const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
|
|
108
|
+
if (includeChildren && site.buildings && site.buildings.length > 0) {
|
|
109
|
+
const children = site.buildings.map((building) => formatBuilding(building, DEPTH_LEVELS.BUILDING, startingDepth, traversalDepth));
|
|
110
|
+
return [site.id, site.name, children];
|
|
111
|
+
}
|
|
112
|
+
return [site.id, site.name];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Format a single building
|
|
116
|
+
* Returns: [id, name, children?]
|
|
117
|
+
*/
|
|
118
|
+
function formatBuilding(building, currentDepth, startingDepth, traversalDepth) {
|
|
119
|
+
const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
|
|
120
|
+
if (includeChildren && building.floors && building.floors.length > 0) {
|
|
121
|
+
const children = building.floors.map((floor) => formatFloor(floor, DEPTH_LEVELS.FLOOR, startingDepth, traversalDepth));
|
|
122
|
+
return [building.id, building.name, children];
|
|
123
|
+
}
|
|
124
|
+
return [building.id, building.name];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Format a single floor
|
|
128
|
+
* Returns: [id, name, children?]
|
|
129
|
+
*/
|
|
130
|
+
function formatFloor(floor, currentDepth, startingDepth, traversalDepth) {
|
|
131
|
+
const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
|
|
132
|
+
if (!includeChildren) {
|
|
133
|
+
return [floor.id, floor.name];
|
|
134
|
+
}
|
|
135
|
+
const children = [];
|
|
136
|
+
// Add rooms
|
|
137
|
+
if (floor.rooms && floor.rooms.length > 0) {
|
|
138
|
+
children.push(...floor.rooms.map((room) => formatRoom(room, floor, DEPTH_LEVELS.ROOM_ZONE, startingDepth, traversalDepth)));
|
|
139
|
+
}
|
|
140
|
+
// Add floor-level zones (no room assignment)
|
|
141
|
+
if (floor.zones && floor.zones.length > 0) {
|
|
142
|
+
const floorLevelZones = floor.zones.filter((zone) => !(zone.roomID || zone.room_id));
|
|
143
|
+
children.push(...floorLevelZones.map((zone) => formatZone(zone)));
|
|
144
|
+
}
|
|
145
|
+
// Add floor-level hives (no room assignment)
|
|
146
|
+
if (floor.hives && floor.hives.length > 0) {
|
|
147
|
+
const floorLevelHives = floor.hives.filter((hive) => !(hive.roomID || hive.room_id));
|
|
148
|
+
children.push(...floorLevelHives.map((hive) => formatHive(hive, floor, DEPTH_LEVELS.HIVE, startingDepth, traversalDepth)));
|
|
149
|
+
}
|
|
150
|
+
// Add floor-level orphan sensors (only if depth allows)
|
|
151
|
+
const shouldIncludeSensors = DEPTH_LEVELS.SENSOR - startingDepth <= traversalDepth;
|
|
152
|
+
if (shouldIncludeSensors) {
|
|
153
|
+
const floorOrphans = getOrphanSensors(floor, null);
|
|
154
|
+
if (floorOrphans.length > 0) {
|
|
155
|
+
// Orphan group: ["orphan", "Orphan (no parent hive)", [sensors]]
|
|
156
|
+
children.push([
|
|
157
|
+
"orphan",
|
|
158
|
+
"Orphan (no parent hive)",
|
|
159
|
+
floorOrphans.map((sensor) => formatSensor(sensor)),
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (children.length > 0) {
|
|
164
|
+
return [floor.id, floor.name, children];
|
|
165
|
+
}
|
|
166
|
+
return [floor.id, floor.name];
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Format a single room
|
|
170
|
+
* Returns: [id, name, children?]
|
|
171
|
+
*/
|
|
172
|
+
function formatRoom(room, floor, currentDepth, startingDepth, traversalDepth) {
|
|
173
|
+
const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
|
|
174
|
+
if (!includeChildren) {
|
|
175
|
+
return [room.id, room.name];
|
|
176
|
+
}
|
|
177
|
+
const children = [];
|
|
178
|
+
// Add zones belonging to this room (zones at room level, not separate depth)
|
|
179
|
+
if (floor.zones && floor.zones.length > 0) {
|
|
180
|
+
const roomZones = floor.zones.filter((zone) => (zone.roomID || zone.room_id) === room.id);
|
|
181
|
+
children.push(...roomZones.map((zone) => formatZone(zone)));
|
|
182
|
+
}
|
|
183
|
+
// Add hives belonging to this room
|
|
184
|
+
if (floor.hives && floor.hives.length > 0) {
|
|
185
|
+
const roomHives = floor.hives.filter((hive) => (hive.roomID || hive.room_id) === room.id);
|
|
186
|
+
children.push(...roomHives.map((hive) => formatHive(hive, floor, DEPTH_LEVELS.HIVE, startingDepth, traversalDepth)));
|
|
187
|
+
}
|
|
188
|
+
// Add orphan sensors belonging to this room (only if depth allows)
|
|
189
|
+
const shouldIncludeSensors = DEPTH_LEVELS.SENSOR - startingDepth <= traversalDepth;
|
|
190
|
+
if (shouldIncludeSensors) {
|
|
191
|
+
const roomOrphans = getOrphanSensors(floor, room.id);
|
|
192
|
+
if (roomOrphans.length > 0) {
|
|
193
|
+
// Orphan group: ["orphan", "Orphan (no parent hive)", [sensors]]
|
|
194
|
+
children.push([
|
|
195
|
+
"orphan",
|
|
196
|
+
"Orphan (no parent hive)",
|
|
197
|
+
roomOrphans.map((sensor) => formatSensor(sensor)),
|
|
198
|
+
]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (children.length > 0) {
|
|
202
|
+
return [room.id, room.name, children];
|
|
203
|
+
}
|
|
204
|
+
return [room.id, room.name];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Format a single zone
|
|
208
|
+
* Returns: [id, name]
|
|
209
|
+
*/
|
|
210
|
+
function formatZone(zone) {
|
|
211
|
+
return [zone.id, zone.name];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Format a single hive (with its sensors)
|
|
215
|
+
* Returns: [id, serialNumber, children?]
|
|
216
|
+
* NOTE: Uses serialNumber, NOT name (name is optional metadata)
|
|
217
|
+
*/
|
|
218
|
+
function formatHive(hive, floor, currentDepth, startingDepth, traversalDepth) {
|
|
219
|
+
const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
|
|
220
|
+
// Find sensors belonging to this hive
|
|
221
|
+
if (includeChildren && floor.sensors && floor.sensors.length > 0) {
|
|
222
|
+
const hiveSensors = floor.sensors.filter((sensor) => sensor.hive_serial === hive.serialNumber);
|
|
223
|
+
if (hiveSensors.length > 0) {
|
|
224
|
+
const children = hiveSensors.map((sensor) => formatSensor(sensor));
|
|
225
|
+
return [hive.id, hive.serialNumber, children];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return [hive.id, hive.serialNumber];
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Format a single sensor
|
|
232
|
+
* Returns: [id, mac_address]
|
|
233
|
+
* NOTE: Uses mac_address, NOT name (name is optional metadata)
|
|
234
|
+
*/
|
|
235
|
+
function formatSensor(sensor) {
|
|
236
|
+
return [sensor.id, sensor.mac_address];
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get orphan sensors (sensors without a parent hive) for a room or floor
|
|
240
|
+
* @param floor Floor object containing sensors and hives
|
|
241
|
+
* @param roomID Room ID to filter by (null for floor-level)
|
|
242
|
+
* @returns Array of orphan sensors
|
|
243
|
+
*/
|
|
244
|
+
function getOrphanSensors(floor, roomID) {
|
|
245
|
+
if (!floor.sensors || floor.sensors.length === 0) {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
// Get all hive serial numbers
|
|
249
|
+
const hiveSerials = new Set(floor.hives?.map((hive) => hive.serialNumber) || []);
|
|
250
|
+
// Find sensors without a parent hive that belong to this room/floor
|
|
251
|
+
return floor.sensors.filter((sensor) => {
|
|
252
|
+
const hasNoHive = !sensor.hive_serial || !hiveSerials.has(sensor.hive_serial);
|
|
253
|
+
const sensorRoomID = sensor.roomID || sensor.room_id || null;
|
|
254
|
+
const matchesLocation = sensorRoomID === roomID;
|
|
255
|
+
return hasNoHive && matchesLocation;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=tree-formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-formatter.js","sourceRoot":"","sources":["../../src/utils/tree-formatter.ts"],"names":[],"mappings":"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"}
|