UNPKG

amaran-light-cli

Version:

Command line tool for controlling Aputure Amaran lights via WebSocket to a local Amaran desktop app.

266 lines 12.2 kB
import * as fs from 'node:fs'; import * as path from 'node:path'; import chalk from 'chalk'; import { CURVE_HELP_TEXT } from '../../daylightSimulation/constants.js'; import { formatCoordinate } from '../../daylightSimulation/privacyUtil.js'; export default function registerConfig(program, deps) { const { asyncCommand } = deps; program .command('config') .description('Configure WebSocket URL and other settings') .option('-u, --url <url>', 'WebSocket URL (default: ws://localhost:60124)') .option('-c, --client-id <id>', 'Client ID (default: amaran-cli)') .option('-d, --debug <boolean>', 'Enable debug mode') .option('--lat <latitude>', 'Default latitude for auto-cct (overrides geoip)') .option('--lon <longitude>', 'Default longitude for auto-cct (overrides geoip)') .option('--cct-min <kelvin>', 'Minimum CCT for auto-cct in Kelvin (default: 2000)') .option('--cct-max <kelvin>', 'Maximum CCT for auto-cct in Kelvin (default: 6500)') .option('--intensity-min <percent>', 'Minimum intensity for auto-cct in percent (default: 5)') .option('--intensity-max <percent>', 'Maximum intensity for auto-cct in percent (default: 100)') .option('--default-curve <curve>', CURVE_HELP_TEXT) .option('--auto-start-app <boolean>', 'Automatically start Amaran desktop app on connection failure (default: true)') .option('--max-lux <number>', 'Maximum lux output of the setup (for auto-cct scaling)') .option('--weather <boolean>', 'Enable automatic weather fetching for auto-cct (default: false)') .option('--privacy-off', 'Show full coordinates and sensitive data', false) .option('--show', 'Show current configuration') .action(asyncCommand(handleConfig(deps))); } function handleConfig(deps) { const { loadConfig, saveConfig } = deps; if (!loadConfig) { throw new Error('loadConfig dependency is required'); } return async (options) => { const hasSetOptions = options.url !== undefined || options.clientId !== undefined || options.debug !== undefined || options.lat !== undefined || options.lon !== undefined || options.cctMin !== undefined || options.cctMax !== undefined || options.intensityMin !== undefined || options.intensityMax !== undefined || options.defaultCurve !== undefined || options.autoStartApp !== undefined || options.maxLux !== undefined || options.weather !== undefined; if (options.show || !hasSetOptions) { const config = loadConfig() || {}; const privacyOff = options.privacyOff === true; console.log(chalk.blue('Current configuration:')); const displayConfig = { ...config }; if (typeof displayConfig.latitude === 'number') { displayConfig.latitude = formatCoordinate(displayConfig.latitude, privacyOff); } if (typeof displayConfig.longitude === 'number') { displayConfig.longitude = formatCoordinate(displayConfig.longitude, privacyOff); } console.log(JSON.stringify(displayConfig, null, 2)); return; } const config = loadConfig() || {}; const changes = []; if (options.url) { config.wsUrl = options.url; changes.push(`WebSocket URL: ${options.url}`); } if (options.clientId) { config.clientId = options.clientId; changes.push(`Client ID: ${options.clientId}`); } if (options.debug !== undefined) { const value = options.debug; if (typeof value === 'boolean') { config.debug = value; } else { const lowerValue = value.toLowerCase().trim(); if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes' || lowerValue === 'on') { config.debug = true; } else if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no' || lowerValue === 'off') { config.debug = false; } else { console.error(chalk.red('debug must be true or false')); process.exit(1); } } changes.push(`Debug mode: ${config.debug ? 'enabled' : 'disabled'}`); } if (options.lat !== undefined) { const lat = parseFloat(options.lat); if (Number.isNaN(lat) || lat < -90 || lat > 90) { console.error(chalk.red('Latitude must be between -90 and 90')); process.exit(1); } config.latitude = lat; changes.push(`Latitude: ${lat}`); } if (options.lon !== undefined) { const lon = parseFloat(options.lon); if (Number.isNaN(lon) || lon < -180 || lon > 180) { console.error(chalk.red('Longitude must be between -180 and 180')); process.exit(1); } config.longitude = lon; changes.push(`Longitude: ${lon}`); } // Bounds validation helpers const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v)); if (options.cctMin !== undefined) { const k = parseInt(options.cctMin, 10); if (Number.isNaN(k)) { console.error(chalk.red('cct-min must be a number (Kelvin)')); process.exit(1); } config.cctMin = clamp(k, 1000, 20000); changes.push(`CCT minimum: ${config.cctMin}K`); } if (options.cctMax !== undefined) { const k = parseInt(options.cctMax, 10); if (Number.isNaN(k)) { console.error(chalk.red('cct-max must be a number (Kelvin)')); process.exit(1); } config.cctMax = clamp(k, 1000, 20000); changes.push(`CCT maximum: ${config.cctMax}K`); } if (options.intensityMin !== undefined) { const p = parseFloat(options.intensityMin); if (Number.isNaN(p)) { console.error(chalk.red('intensity-min must be a number (percent)')); process.exit(1); } config.intensityMin = clamp(p, 0, 100); changes.push(`Intensity minimum: ${config.intensityMin}%`); } if (options.intensityMax !== undefined) { const p = parseFloat(options.intensityMax); if (Number.isNaN(p)) { console.error(chalk.red('intensity-max must be a number (percent)')); process.exit(1); } config.intensityMax = clamp(p, 0, 100); changes.push(`Intensity maximum: ${config.intensityMax}%`); } // Handle default curve option if (options.defaultCurve !== undefined) { // dynamic import for ESM const { parseCurveType } = await import('../../daylightSimulation/cctUtil.js'); try { parseCurveType(options.defaultCurve); config.defaultCurve = options.defaultCurve; changes.push(`Default curve: ${config.defaultCurve}`); } catch (error) { console.error(chalk.red(error.message)); process.exit(1); } } // Handle auto-start-app option if (options.autoStartApp !== undefined) { const value = options.autoStartApp; if (typeof value === 'boolean') { config.autoStartApp = value; } else { const lowerValue = value.toLowerCase().trim(); if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes' || lowerValue === 'on') { config.autoStartApp = true; } else if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no' || lowerValue === 'off') { config.autoStartApp = false; } else { console.error(chalk.red('auto-start-app must be true or false')); process.exit(1); } } changes.push(`Auto-start app: ${config.autoStartApp ? 'enabled' : 'disabled'}`); } // Handle max-lux option if (options.maxLux !== undefined) { let valid = false; const luxVal = options.maxLux; // Try as simple number const luxNum = parseFloat(luxVal); if (!Number.isNaN(luxNum) && luxNum > 0 && !luxVal.includes(':')) { config.maxLux = luxNum; changes.push(`Max Lux: ${luxNum}`); valid = true; } else { // Try as map const { parseMaxLuxMap } = await import('../../daylightSimulation/mathUtil.js'); const map = parseMaxLuxMap(luxVal); if (map) { config.maxLux = map; changes.push(`Max Lux Map: ${JSON.stringify(map)}`); valid = true; } } if (!valid) { console.error(chalk.red('max-lux must be a positive number OR a map string like "2700:8000,5600:10000"')); process.exit(1); } } // Handle weather option if (options.weather !== undefined) { const value = options.weather; if (typeof value === 'boolean') { config.weather = value.toString() === 'true'; // Commander might pass boolean if it was a flag, but we defined it with <boolean> } else { const lowerValue = value.toLowerCase().trim(); if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes' || lowerValue === 'on') { config.weather = true; } else if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no' || lowerValue === 'off') { config.weather = false; } else { console.error(chalk.red('weather must be true or false')); process.exit(1); } } changes.push(`Default weather enabled: ${config.weather ? 'enabled' : 'disabled'}`); } // Ensure logical ordering if both sides provided if (config.cctMin !== undefined && config.cctMax !== undefined && config.cctMin !== null && config.cctMax !== null && config.cctMin > config.cctMax) { console.error(chalk.red('cct-min must be <= cct-max')); process.exit(1); } if (config.intensityMin !== undefined && config.intensityMin !== null && config.intensityMax !== undefined && config.intensityMax !== null && config.intensityMin > config.intensityMax) { console.error(chalk.red('intensity-min must be <= intensity-max')); process.exit(1); } // Save the config using provided deps if available, otherwise fallback to local write if (typeof saveConfig === 'function') { saveConfig(config, changes); } else { // Filesystem and Path imports are handled at top level const configPath = path.join(process.env.HOME || '', '.amaran-cli.json'); fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); if (changes && changes.length > 0) { console.log(chalk.green('Configuration saved successfully:')); changes.forEach((change) => { console.log(chalk.green(` • ${change}`)); }); } else { console.log(chalk.green('Configuration saved successfully')); } } }; } //# sourceMappingURL=config.js.map