homebridge-config-ui-x
Version:
A web based management, configuration and control platform for Homebridge
323 lines • 13.2 kB
JavaScript
"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