UNPKG

homebridge-config-ui-x

Version:

A web based management, configuration and control platform for Homebridge

323 lines • 13.2 kB
#!/usr/bin/env node "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const os = require("os"); const path = require("path"); const request = require("request"); const commander = require("commander"); const child_process = require("child_process"); const fs = require("fs-extra"); class HomebridgeServiceHelper { constructor() { this.serviceName = 'Homebridge'; this.storagePath = path.resolve(os.homedir(), '.homebridge'); this.uiPort = 8080; commander .allowUnknownOption() .option('-U, --user-storage-path [path]', '', (p) => this.storagePath = p) .option('-P, --plugin-path [path]', '', (p) => process.env.UIX_CUSTOM_PLUGIN_PATH = p) .option('-I, --insecure', '', () => process.env.UIX_INSECURE_MODE = '1') .option('-T, --no-timestamp', '', () => process.env.UIX_LOG_NO_TIMESTAMPS = '1') .option('-S, --service-name [service name]', '', (p) => this.serviceName = p) .arguments('<install|uninstall|start|stop|restart|run>') .action((cmd) => { this.action = cmd; }) .parse(process.argv); this.osCheck(); this.setEnv(); switch (this.action) { case 'install': { this.logger(`Installing ${this.serviceName} Service`); this.install(); break; } case 'uninstall': { this.logger(`Removing ${this.serviceName} Service`); this.uninstall(); break; } case 'start': { this.logger(`Starting ${this.serviceName} Service`); this.start(); break; } case 'stop': { this.logger(`Stopping ${this.serviceName} Service`); this.stop(); break; } case 'restart': { this.logger(`Restart ${this.serviceName} Service`); this.restart(); break; } case 'run': { this.launch(); break; } default: { commander.outputHelp(); console.log('\nThe hb-service command is provided by homebridge-config-ui-x\n'); console.log('Please provide a command:'); console.log(' install install homebridge as a service'); console.log(' uninstall remove the homebridge service'); console.log(' start start the homebridge service'); console.log(' stop stop the homebridge service'); console.log(' restart restart the homebridge service'); console.log(' run run homebridge daemon'); process.exit(1); } } } logger(msg) { msg = `\x1b[37m[${new Date().toLocaleString()}]\x1b[0m ` + '\x1b[36m[HB Supervisor]\x1b[0m ' + msg; if (this.log) { this.log.write(msg + '\n'); } else { console.log(msg); } } osCheck() { if (os.platform() !== 'win32') { this.logger('ERROR: This command is only supported on Windows 10.'); process.exit(1); } } setEnv() { process.env.UIX_STORAGE_PATH = this.storagePath; process.env.UIX_CONFIG_PATH = path.resolve(this.storagePath, 'config.json'); process.env.UIX_BASE_PATH = path.resolve(__dirname, '../../'); process.env.UIX_SERVICE_MODE = '1'; } launch() { return __awaiter(this, void 0, void 0, function* () { yield this.storagePathCheck(); this.logPath = path.resolve(this.storagePath, 'homebridge.log'); this.logger(`Logging to ${this.logPath}`); this.log = fs.createWriteStream(this.logPath, { flags: 'a' }); process.stdout.write = process.stderr.write = this.log.write.bind(this.log); yield this.configCheck(); const node_modules = path.resolve(process.env.UIX_BASE_PATH, '..'); this.homebridgeBinary = path.resolve(node_modules, 'homebridge', 'bin', 'homebridge'); this.logger(`Homebridge Path: ${this.homebridgeBinary}`); this.uiBinary = path.resolve(process.env.UIX_BASE_PATH, 'dist', 'bin', 'standalone.js'); this.logger(`UI Path: ${this.uiBinary}`); this.runHomebridge(); this.runUi(); }); } runHomebridge() { this.homebridge = child_process.spawn(process.execPath, [ this.homebridgeBinary, '-I', '-C', '-U', this.storagePath, ], { env: process.env, windowsHide: true, }); this.logger(`Started Homebridge with PID: ${this.homebridge.pid}`); this.homebridge.stdout.on('data', (data) => { this.log.write(data); }); this.homebridge.stderr.on('data', (data) => { this.log.write(data); }); this.homebridge.on('close', (code, signal) => { this.handleHomebridgeClose(code, signal); }); } handleHomebridgeClose(code, signal) { this.logger(`Homebridge Process Ended. Code: ${code}, Signal: ${signal}`); setTimeout(() => { this.logger('Restarting Homebridge...'); this.runHomebridge(); }, 5000); } runUi() { return __awaiter(this, void 0, void 0, function* () { yield Promise.resolve().then(() => require('../main')); }); } install() { return __awaiter(this, void 0, void 0, function* () { yield this.storagePathCheck(); yield this.configCheck(); const nssmPath = yield this.downloadNssm(); const installCmd = `${nssmPath} install ${this.serviceName} ` + `"${process.execPath}" "${__filename}" run -I -U ${this.storagePath}`; const setUserDirCmd = `${nssmPath} set ${this.serviceName} AppEnvironmentExtra ":UIX_STORAGE_PATH=${this.storagePath}"`; try { child_process.execSync(installCmd); child_process.execSync(setUserDirCmd); yield this.start(); console.log(`\nManage Homebridge by going to http://localhost:${this.uiPort} in your browser`); console.log(`Default Username: admin`); console.log(`Default Password: admin\n`); } catch (e) { console.error(e.toString()); this.logger(`ERROR: Failed Operation`); } }); } uninstall() { return __awaiter(this, void 0, void 0, function* () { const nssmPath = yield this.downloadNssm(); const uninstallCmd = `${nssmPath} remove ${this.serviceName} confirm`; yield this.stop(); try { child_process.execSync(uninstallCmd); this.logger(`Removed ${this.serviceName} Service.`); } catch (e) { console.error(e.toString()); this.logger(`ERROR: Failed Operation`); } }); } start() { return __awaiter(this, void 0, void 0, function* () { const nssmPath = yield this.downloadNssm(); const stopCmd = `${nssmPath} start ${this.serviceName}`; try { this.logger(`Starting ${this.serviceName} Service...`); child_process.execSync(stopCmd); this.logger(`${this.serviceName} Started`); } catch (e) { this.logger(`Failed to start ${this.serviceName}`); } }); } stop() { return __awaiter(this, void 0, void 0, function* () { const nssmPath = yield this.downloadNssm(); const stopCmd = `${nssmPath} stop ${this.serviceName}`; try { this.logger(`Stopping ${this.serviceName} Service...`); child_process.execSync(stopCmd); this.logger(`${this.serviceName} Stopped`); } catch (e) { this.logger(`Failed to stop ${this.serviceName}`); } }); } restart() { return __awaiter(this, void 0, void 0, function* () { yield this.stop(); setTimeout(() => __awaiter(this, void 0, void 0, function* () { yield this.start(); }), 3000); }); } downloadNssm() { return __awaiter(this, void 0, void 0, function* () { const downloadUrl = `https://github.com/oznu/nssm/releases/download/2.24-101-g897c7ad/nssm_${os.arch()}.exe`; const nssmPath = path.resolve(this.storagePath, 'nssm.exe'); if (yield fs.pathExists(nssmPath)) { return nssmPath; } const nssmFile = fs.createWriteStream(nssmPath); this.logger(`Downloading NSSM from ${downloadUrl}`); return new Promise((resolve, reject) => { request({ url: downloadUrl, method: 'GET', encoding: null, }).pipe(nssmFile) .on('finish', () => { return resolve(nssmPath); }) .on('error', (err) => { return reject(err); }); }); }); } storagePathCheck() { return __awaiter(this, void 0, void 0, function* () { if (!(yield fs.pathExists(this.storagePath))) { this.logger(`Creating Homebridge directory: ${this.storagePath}`); yield fs.mkdirp(this.storagePath); } }); } configCheck() { return __awaiter(this, void 0, void 0, function* () { if (!(yield fs.pathExists(process.env.UIX_CONFIG_PATH))) { this.logger(`Creating default config.json: ${process.env.UIX_CONFIG_PATH}`); return yield this.createDefaultConfig(); } try { yield fs.readJson(process.env.UIX_CONFIG_PATH); } catch (e) { const backupFile = path.resolve(this.storagePath, 'config.json.invalid.' + new Date().getTime().toString()); this.logger(`${process.env.UIX_CONFIG_PATH} does not contain valid JSON.`); this.logger(`Invalid config.json file has been backed up to ${backupFile}.`); yield fs.rename(process.env.UIX_CONFIG_PATH, backupFile); yield this.createDefaultConfig(); } }); } createDefaultConfig() { return __awaiter(this, void 0, void 0, function* () { yield fs.writeJson(process.env.UIX_CONFIG_PATH, { bridge: { name: this.serviceName, username: this.generateUsername(), port: Math.floor(Math.random() * (52000 - 51000 + 1) + 51000), pin: this.generatePin(), }, accessories: [], platforms: [ { name: 'Config', port: this.uiPort, platform: 'config', }, ], }, { spaces: 4 }); }); } generatePin() { let code = Math.floor(10000000 + Math.random() * 90000000) + ''; code = code.split(''); code.splice(3, 0, '-'); code.splice(6, 0, '-'); code = code.join(''); return code; } generateUsername() { const hexDigits = '0123456789ABCDEF'; let username = '0E:'; for (let i = 0; i < 5; i++) { username += hexDigits.charAt(Math.round(Math.random() * 15)); username += hexDigits.charAt(Math.round(Math.random() * 15)); if (i !== 4) { username += ':'; } } return username; } } function bootstrap() { return new HomebridgeServiceHelper(); } bootstrap(); //# sourceMappingURL=hb-service.js.map