homebridge-kasa-python
Version:
Plugin that uses Python-Kasa API to communicate with Kasa Devices.
133 lines • 4.85 kB
JavaScript
import { spawn } from 'node:child_process';
import { writeFile } from 'node:fs/promises';
export function deferAndCombine(fn, timeout, runNowFn) {
let requests = [];
let timer = null;
const processRequests = () => {
const currentRequests = requests;
requests = [];
fn(currentRequests.length)
.then(value => currentRequests.forEach(req => req.resolve(value)))
.catch(error => currentRequests.forEach(req => req.reject(error)))
.finally(() => timer = null);
};
return (arg) => {
if (runNowFn && arg !== undefined) {
runNowFn(arg);
}
return new Promise((resolve, reject) => {
requests.push({ resolve, reject });
if (!timer) {
timer = setTimeout(processRequests, timeout);
}
});
};
}
export function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
export function isObjectLike(candidate) {
return typeof candidate === 'object' && candidate !== null || typeof candidate === 'function';
}
export function lookup(object, compareFn, value) {
const compare = compareFn ?? ((objectProp, search) => objectProp === search);
if (isObjectLike(object)) {
return Object.keys(object).find(key => compare(object[key], value));
}
return undefined;
}
export function lookupCharacteristicNameByUUID(characteristic, uuid) {
return Object.keys(characteristic).find(key => (characteristic[key].UUID === uuid));
}
export function prefixLogger(logger, prefix) {
const methods = ['info', 'warn', 'error', 'debug', 'log'];
const clonedLogger = methods.reduce((acc, method) => {
acc[method] = (...args) => {
const prefixString = typeof prefix === 'function' ? prefix() : prefix;
if (method === 'log') {
const [level, message, ...parameters] = args;
logger[method](level, `${prefixString} ${message}`, ...parameters);
}
else {
const [message, ...parameters] = args;
logger[method](`${prefixString} ${message}`, ...parameters);
}
};
return acc;
}, {});
clonedLogger.prefix = typeof logger.prefix === 'string' ? `${prefix} ${logger.prefix}` : prefix;
return clonedLogger;
}
export async function runCommand(logger, command, args = [], options, hideStdout = false, hideStderr = false, returnProcess = false, suppressErrors = []) {
const MAX_BUFFER_SIZE = 1024 * 1024;
let stdout = '';
let stderr = '';
let outputFile = null;
const filteredArgs = args.filter(arg => {
if (arg.startsWith('>')) {
outputFile = arg.substring(1).trim();
return false;
}
return true;
});
logger.debug(`Running command: ${command} ${filteredArgs.join(' ')}`);
const p = spawn(command, filteredArgs, {
...options,
env: process.env,
});
logger.debug(`Command PID: ${p.pid}`);
p.stdout.setEncoding('utf8').on('data', data => {
stdout += data;
if (stdout.length > MAX_BUFFER_SIZE) {
stdout = stdout.slice(-MAX_BUFFER_SIZE);
}
if (!hideStdout) {
logger.debug(`STDOUT: ${data.trim()}`);
}
});
p.stderr.setEncoding('utf8').on('data', data => {
stderr += data;
if (stderr.length > MAX_BUFFER_SIZE) {
stderr = stderr.slice(-MAX_BUFFER_SIZE);
}
if (!hideStderr) {
logger.error(`STDERR: ${data.trim()}`);
}
});
if (returnProcess) {
logger.debug('Command started and returning process.');
const stderrReady = new Promise((resolve) => {
p.stderr.once('data', () => {
logger.debug('Process data received.');
resolve();
});
});
await stderrReady;
return [stdout, stderr, null, p];
}
const exitCode = await new Promise((resolve, reject) => {
p.on('close', (code) => {
logger.debug(`Command closed with exit code: ${code}`);
resolve(code);
}).on('error', (error) => {
const errorMessage = error.message.toLowerCase();
const shouldSuppress = suppressErrors.some(err => errorMessage.includes(err));
if (!shouldSuppress) {
logger.error('Command encountered an error:', error);
}
reject(error);
});
});
p.stdout.destroy();
p.stderr.destroy();
p.kill();
if (outputFile) {
logger.debug(`Writing command output to file: ${outputFile}`);
await writeFile(outputFile, stdout);
}
logger.debug('Command finished.');
return [stdout, stderr, exitCode];
}
//# sourceMappingURL=utils.js.map