amaran-light-cli
Version:
Command line tool for controlling Aputure Amaran lights via WebSocket to a local Amaran desktop app.
137 lines • 7.05 kB
JavaScript
import chalk from 'chalk';
import { CURVE_METADATA, SPECIAL_TIME_CONFIG } from './constants.js';
import { formatCoordinate, formatSource } from './privacyUtil.js';
// Helper function to get color and emoji for a special time
function getSpecialTimeStyling(currentTime, times) {
const isSpecialTime = (specialTime) => {
if (!specialTime || Number.isNaN(specialTime.getTime()))
return false;
return Math.abs(currentTime.getTime() - specialTime.getTime()) < 30000; // 30 seconds
};
for (const config of SPECIAL_TIME_CONFIG) {
if (isSpecialTime(times[config.key])) {
return { color: config.color, emoji: config.emoji };
}
}
return { color: chalk.white, emoji: '' };
}
const formatTitle = (key) => {
return key
.replace(/([A-Z])/g, ' $1')
.replace(/^([a-z])/, (c) => c.toUpperCase())
.trim();
};
const stripAnsiCodes = (message) => {
// biome-ignore lint/suspicious/noControlCharactersInRegex: intentional use for ANSI stripping
return message.replace(/\u001b\[\d+m/g, '');
};
export function textSchedule(schedule, options = {}) {
const lines = [];
const privacyOff = options.privacyOff === true;
const { csv, interval, stripAnsi } = options;
const push = (message) => {
lines.push(stripAnsi ? stripAnsiCodes(message) : message);
};
if (csv) {
// CSV Output
const header = ['DateTime', 'Location', 'Event'];
schedule.curves.forEach((c) => {
header.push(`${c}_CCT`, `${c}_Intensity`, `${c}_Lux`);
});
push(header.join(','));
for (const point of schedule.points) {
const dateTimeStr = point.time.toISOString();
const locationStr = `${schedule.lat},${schedule.lon}`;
const eventName = point.eventName ? formatTitle(point.eventName) : '';
const row = [dateTimeStr, `"${locationStr}"`, eventName];
schedule.curves.forEach((curve) => {
const val = point.values.get(curve);
row.push(val?.cct.toString() ?? '', val ? (val.intensity / 10).toFixed(1) : '', val?.lightOutput?.toString() ?? '');
});
push(row.join(','));
}
}
else {
// Standard Output
push(chalk.blue('\n═══════════════════════════════════════════════════════════'));
push(chalk.blue(' Auto-CCT Schedule Preview'));
push(chalk.blue('═══════════════════════════════════════════════════════════\n'));
push(chalk.cyan(`Location: ${formatCoordinate(schedule.lat, privacyOff)}°, ${formatCoordinate(schedule.lon, privacyOff)}° (${formatSource(schedule.source, privacyOff)})`));
push(chalk.cyan(`Date: ${schedule.date.toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}`));
push(chalk.cyan(`Interval: Every ${interval ?? '30'} minute${interval !== '1' ? 's' : ''}`));
push(chalk.cyan(schedule.curves.length > 1
? `Curve: Multiple curves selected\n`
: `Curve: ${schedule.curves[0].toLowerCase()}\n`));
const allSpecialTimes = [
...SPECIAL_TIME_CONFIG.map((conf) => ({
key: conf.key,
emoji: conf.emoji,
color: conf.color,
time: schedule.times[conf.key],
})),
{ key: 'nadir', emoji: 'NA', color: chalk.blue, time: schedule.times.nadir },
].filter((item) => item.time && !Number.isNaN(item.time.getTime()));
const halfLength = Math.ceil(allSpecialTimes.length / 2);
for (let i = 0; i < halfLength; i++) {
const left = allSpecialTimes[i];
const right = allSpecialTimes[i + halfLength];
let line = '';
if (left?.time) {
const timeStr = left.time.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
line += left.color(`${`${left.emoji} ${formatTitle(left.key)}`.padEnd(20)}: ${timeStr}`);
}
if (right?.time) {
const timeStr = right.time.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
line += ` ${right.color(`${`${right.emoji} ${formatTitle(right.key)}`.padEnd(20)}: ${timeStr}`)}`;
}
push(line);
}
push(chalk.blue('\n'));
if (schedule.curves.length > 1) {
const headers = [
'Time',
...schedule.curves.map((c) => {
const metadata = CURVE_METADATA[c];
return metadata ? metadata.shortName : c.replace(/_/g, ' ');
}),
];
const colWidths = [13, ...schedule.curves.map(() => 12)];
const totalWidth = colWidths.reduce((a, b) => a + b, 0);
let headerLine = '';
headers.forEach((h, i) => {
headerLine += h.padEnd(colWidths[i]);
});
push(chalk.blue('─'.repeat(totalWidth)));
push(chalk.blue(headerLine));
push(chalk.blue('─'.repeat(totalWidth)));
for (const point of schedule.points) {
const timeStr = point.time.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
const { color, emoji } = getSpecialTimeStyling(point.time, schedule.times);
let rowLine = color(`${timeStr} ${emoji || ' '} `);
schedule.curves.forEach((curve, index) => {
const val = point.values.get(curve);
const valStr = val ? `${val.cct}K/${(val.intensity / 10).toFixed(0)}%` : '';
rowLine += color(valStr.padEnd(colWidths[index + 1]));
});
push(rowLine);
}
push(chalk.blue(`${'─'.repeat(totalWidth)}\n`));
}
else {
const singleCurveWidth = 31;
push(chalk.blue('─'.repeat(singleCurveWidth)));
push(chalk.blue('Time CCT/Intensity'));
push(chalk.blue('─'.repeat(singleCurveWidth)));
for (const point of schedule.points) {
const val = point.values.get(schedule.curves[0]);
const timeStr = point.time.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
const valStr = val ? `${val.cct}K/${(val.intensity / 10).toFixed(0)}%`.padEnd(18) : '';
const { color, emoji } = getSpecialTimeStyling(point.time, schedule.times);
push(color(`${timeStr} ${emoji || ' '} ${valStr}`));
}
push(chalk.blue(`${'─'.repeat(singleCurveWidth)}\n`));
}
}
return lines.join('\n');
}
//# sourceMappingURL=textSchedule.js.map