mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-06 14:11:35 +02:00
125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
/**
|
|
* AMS (Automatic Material System) helper utilities for Bambu Lab printers.
|
|
* These functions handle color normalization, slot labeling, and tray ID calculations
|
|
* for AMS, AMS-HT, and external spool configurations.
|
|
*/
|
|
|
|
/**
|
|
* Normalize color format from various sources.
|
|
* API returns "RRGGBBAA" (8-char), 3MF uses "#RRGGBB" (7-char with hash).
|
|
* This normalizes to "#RRGGBB" format.
|
|
*/
|
|
export function normalizeColor(color: string | null | undefined): string {
|
|
if (!color) return '#808080';
|
|
// Remove alpha channel if present (8-char hex to 6-char)
|
|
const hex = color.replace('#', '').substring(0, 6);
|
|
return `#${hex}`;
|
|
}
|
|
|
|
/**
|
|
* Normalize color for comparison (case-insensitive, strip hash and alpha).
|
|
*/
|
|
export function normalizeColorForCompare(color: string | undefined): string {
|
|
if (!color) return '';
|
|
return color.replace('#', '').toLowerCase().substring(0, 6);
|
|
}
|
|
|
|
/**
|
|
* Check if two colors are visually similar within a threshold.
|
|
* Uses RGB component comparison with configurable tolerance.
|
|
* @param color1 - First hex color
|
|
* @param color2 - Second hex color
|
|
* @param threshold - Maximum difference per RGB component (default: 40)
|
|
*/
|
|
export function colorsAreSimilar(
|
|
color1: string | undefined,
|
|
color2: string | undefined,
|
|
threshold = 40
|
|
): boolean {
|
|
const hex1 = normalizeColorForCompare(color1);
|
|
const hex2 = normalizeColorForCompare(color2);
|
|
if (!hex1 || !hex2 || hex1.length < 6 || hex2.length < 6) return false;
|
|
|
|
const r1 = parseInt(hex1.substring(0, 2), 16);
|
|
const g1 = parseInt(hex1.substring(2, 4), 16);
|
|
const b1 = parseInt(hex1.substring(4, 6), 16);
|
|
const r2 = parseInt(hex2.substring(0, 2), 16);
|
|
const g2 = parseInt(hex2.substring(2, 4), 16);
|
|
const b2 = parseInt(hex2.substring(4, 6), 16);
|
|
|
|
return (
|
|
Math.abs(r1 - r2) <= threshold &&
|
|
Math.abs(g1 - g2) <= threshold &&
|
|
Math.abs(b1 - b2) <= threshold
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Format slot label for display in the UI.
|
|
* @param amsId - AMS unit ID (0-3 for regular AMS, 128+ for AMS-HT)
|
|
* @param trayId - Tray/slot ID within the AMS unit (0-3)
|
|
* @param isHt - Whether this is an AMS-HT unit (single tray)
|
|
* @param isExternal - Whether this is the external spool holder
|
|
*/
|
|
export function formatSlotLabel(
|
|
amsId: number,
|
|
trayId: number,
|
|
isHt: boolean,
|
|
isExternal: boolean
|
|
): string {
|
|
if (isExternal) return 'External';
|
|
// Convert AMS ID to letter (A, B, C, D)
|
|
// AMS-HT uses IDs starting at 128
|
|
const letter = String.fromCharCode(65 + (amsId >= 128 ? amsId - 128 : amsId));
|
|
if (isHt) return `HT-${letter}`;
|
|
return `AMS-${letter} Slot ${trayId + 1}`;
|
|
}
|
|
|
|
/**
|
|
* Calculate global tray ID for MQTT command.
|
|
* Used in the ams_mapping array sent to the printer.
|
|
* @param amsId - AMS unit ID
|
|
* @param trayId - Tray/slot ID within the AMS unit
|
|
* @param isExternal - Whether this is the external spool holder
|
|
* @returns Global tray ID (0-15 for AMS, 254 for external)
|
|
*/
|
|
export function getGlobalTrayId(
|
|
amsId: number,
|
|
trayId: number,
|
|
isExternal: boolean
|
|
): number {
|
|
if (isExternal) return 254;
|
|
return amsId * 4 + trayId;
|
|
}
|
|
|
|
/**
|
|
* Format seconds to human readable time string.
|
|
*/
|
|
export function formatTime(seconds: number | null | undefined): string {
|
|
if (!seconds) return '';
|
|
const hours = Math.floor(seconds / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
return `${minutes}m`;
|
|
}
|
|
|
|
/**
|
|
* Get minimum datetime for scheduling (now + 1 minute).
|
|
* Returns ISO string format for datetime-local input.
|
|
*/
|
|
export function getMinDateTime(): string {
|
|
const now = new Date();
|
|
now.setMinutes(now.getMinutes() + 1);
|
|
return now.toISOString().slice(0, 16);
|
|
}
|
|
|
|
/**
|
|
* Check if a scheduled time is a placeholder far-future date.
|
|
* Placeholder dates (more than 6 months out) are treated as ASAP.
|
|
*/
|
|
export function isPlaceholderDate(scheduledTime: string | null | undefined): boolean {
|
|
if (!scheduledTime) return false;
|
|
const sixMonthsFromNow = Date.now() + 180 * 24 * 60 * 60 * 1000;
|
|
return new Date(scheduledTime).getTime() > sixMonthsFromNow;
|
|
}
|