homebridge 2.0.0-alpha.4 → 2.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/homebridge +1 -1
- package/dist/api.d.ts +193 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +129 -0
- package/dist/api.js.map +1 -0
- package/dist/bridgeService.d.ts +106 -0
- package/dist/bridgeService.d.ts.map +1 -0
- package/dist/bridgeService.js +390 -0
- package/dist/bridgeService.js.map +1 -0
- package/dist/childBridgeFork.d.ts +38 -0
- package/dist/childBridgeFork.d.ts.map +1 -0
- package/dist/childBridgeFork.js +241 -2
- package/dist/childBridgeFork.js.map +1 -7
- package/dist/childBridgeService.d.ts +200 -0
- package/dist/childBridgeService.d.ts.map +1 -0
- package/dist/childBridgeService.js +427 -0
- package/dist/childBridgeService.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +89 -2
- package/dist/cli.js.map +1 -7
- package/dist/externalPortService.d.ts +33 -0
- package/dist/externalPortService.d.ts.map +1 -0
- package/dist/externalPortService.js +59 -0
- package/dist/externalPortService.js.map +1 -0
- package/dist/index.d.ts +76 -1099
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -7
- package/dist/ipcService.d.ts +30 -0
- package/dist/ipcService.d.ts.map +1 -0
- package/dist/ipcService.js +49 -0
- package/dist/ipcService.js.map +1 -0
- package/dist/logger.d.ts +78 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +138 -0
- package/dist/logger.js.map +1 -0
- package/dist/platformAccessory.d.ts +55 -0
- package/dist/platformAccessory.d.ts.map +1 -0
- package/dist/platformAccessory.js +98 -0
- package/dist/platformAccessory.js.map +1 -0
- package/dist/plugin.d.ts +31 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +185 -0
- package/dist/plugin.js.map +1 -0
- package/dist/pluginManager.d.ts +77 -0
- package/dist/pluginManager.d.ts.map +1 -0
- package/dist/pluginManager.js +374 -0
- package/dist/pluginManager.js.map +1 -0
- package/dist/server.d.ts +58 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +430 -0
- package/dist/server.js.map +1 -0
- package/dist/storageService.d.ts +13 -0
- package/dist/storageService.d.ts.map +1 -0
- package/dist/storageService.js +41 -0
- package/dist/storageService.js.map +1 -0
- package/dist/user.d.ts +13 -0
- package/dist/user.d.ts.map +1 -0
- package/dist/user.js +29 -0
- package/dist/user.js.map +1 -0
- package/dist/util/mac.d.ts +5 -0
- package/dist/util/mac.d.ts.map +1 -0
- package/dist/util/mac.js +14 -0
- package/dist/util/mac.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/package.json +7 -10
package/dist/cli.js
CHANGED
|
@@ -1,2 +1,89 @@
|
|
|
1
|
-
import E from"node:process";import{Command as Xe}from"commander";import{HAPStorage as Ze}from"hap-nodejs";import{satisfies as ei}from"semver";import ue from"node:util";import N from"chalk";var g=class o{static internal=new o;static loggerCache=new Map;static debugEnabled=!1;static timestampEnabled=!0;prefix;constructor(e){this.prefix=e}static withPrefix(e){let i=o.loggerCache.get(e);if(i)return i;{let r=new o(e),t=r.info.bind(r);t.info=r.info,t.success=r.success,t.warn=r.warn,t.error=r.error,t.debug=r.debug,t.log=r.log,t.prefix=r.prefix;let s=t;return o.loggerCache.set(e,s),s}}static setDebugEnabled(e=!0){o.debugEnabled=e}static setTimestampEnabled(e=!0){o.timestampEnabled=e}static forceColor(){N.level=1}info(e,...i){this.log("info",e,...i)}success(e,...i){this.log("success",e,...i)}warn(e,...i){this.log("warn",e,...i)}error(e,...i){this.log("error",e,...i)}debug(e,...i){this.log("debug",e,...i)}log(e,i,...r){if(e==="debug"&&!o.debugEnabled)return;i=ue.format(i,...r);let t=console.log;switch(e){case"success":i=N.green(i);break;case"warn":i=N.yellow(i),t=console.error;break;case"error":i=N.red(i),t=console.error;break;case"debug":i=N.gray(i);break}if(this.prefix&&(i=`${y(this.prefix)} ${i}`),o.timestampEnabled){let s=new Date;i=N.white(`[${s.toLocaleString()}] `)+i}t(i)}};function y(o){return N.cyan(`[${o}]`)}import le from"node:fs";import Ke from"node:process";import H from"chalk";import{AccessoryEventTypes as J,MDNSAdvertiser as W}from"hap-nodejs";import de from"qrcode-terminal";import{EventEmitter as xe}from"node:events";import ne from"hap-nodejs";import De from"semver";import{EventEmitter as he}from"node:events";import{Accessory as V,AccessoryEventTypes as pe,Categories as fe}from"hap-nodejs";var A=class o extends he{static injectedAccessory;_associatedPlugin;_associatedPlatform;_associatedHAPAccessory;displayName;UUID;category;services=[];context={};constructor(e,i,r){super(),this._associatedHAPAccessory=o.injectedAccessory?o.injectedAccessory:new V(e,i),r&&(this._associatedHAPAccessory.category=r),this.displayName=this._associatedHAPAccessory.displayName,this.UUID=this._associatedHAPAccessory.UUID,this.category=r||fe.OTHER,this.services=this._associatedHAPAccessory.services,this._associatedHAPAccessory.on(pe.IDENTIFY,(t,s)=>{this.emit("identify",t,()=>{}),s()})}addService(e,...i){return this._associatedHAPAccessory.addService(e,...i)}removeService(e){this._associatedHAPAccessory.removeService(e)}getService(e){return this._associatedHAPAccessory.getService(e)}getServiceById(e,i){return this._associatedHAPAccessory.getServiceById(e,i)}configureController(e){this._associatedHAPAccessory.configureController(e)}removeController(e){this._associatedHAPAccessory.removeController(e)}static serialize(e){return{plugin:e._associatedPlugin,platform:e._associatedPlatform,context:e.context,...V.serialize(e._associatedHAPAccessory)}}static deserialize(e){let i=V.deserialize(e);o.injectedAccessory=i;let r=new o(i.displayName,i.UUID);return o.injectedAccessory=void 0,r._associatedPlugin=e.plugin,r._associatedPlatform=e.platform,r.context=e.context,r.category=e.category,r}};import{execSync as Ce}from"node:child_process";import R from"node:fs";import{createRequire as Re}from"node:module";import b from"node:path";import x from"node:process";import Ie from"node:assert";import Se from"node:path";import te from"node:process";import{pathToFileURL as Ee}from"node:url";import{satisfies as se}from"semver";import me from"node:fs";import{dirname as Pe,join as ye}from"node:path";import{fileURLToPath as ve}from"node:url";var be=ve(import.meta.url),Ae=Pe(be);function ie(){let o=ye(Ae,"../package.json");return JSON.parse(me.readFileSync(o,{encoding:"utf8"}))}function v(){return ie().version}function re(){return ie().engines.node}var L=g.internal,U=class{pluginName;scope;pluginPath;isESM;disabled=!1;version;main;loadContext;pluginInitializer;registeredAccessories=new Map;registeredPlatforms=new Map;activeDynamicPlatforms=new Map;constructor(e,i,r,t){if(this.pluginName=e,this.scope=t,this.pluginPath=i,this.version=r.version||"0.0.0",this.main="",r.exports)if(typeof r.exports=="string")this.main=r.exports;else{let s=r.exports.import||r.exports.require||r.exports.node||r.exports.default||r.exports["."];typeof s!="string"?s.import?this.main=s.import:this.main=s.require||s.node||s.default:this.main=s}this.main||(this.main=r.main||"./index.js"),this.isESM=this.main.endsWith(".mjs")||this.main.endsWith(".js")&&r.type==="module",r.peerDependencies&&(!r.engines||!r.engines.homebridge)&&(r.engines=r.engines||{},r.engines.homebridge=r.peerDependencies.homebridge),this.loadContext={engines:r.engines,dependencies:r.dependencies}}getPluginIdentifier(){return(this.scope?`${this.scope}/`:"")+this.pluginName}getPluginPath(){return this.pluginPath}registerAccessory(e,i){if(this.registeredAccessories.has(e))throw new Error(`Plugin '${this.getPluginIdentifier()}' tried to register an accessory '${e}' which has already been registered!`);this.disabled||L.info("Registering accessory '%s'",`${this.getPluginIdentifier()}.${e}`),this.registeredAccessories.set(e,i)}registerPlatform(e,i){if(this.registeredPlatforms.has(e))throw new Error(`Plugin '${this.getPluginIdentifier()}' tried to register a platform '${e}' which has already been registered!`);this.disabled||L.info("Registering platform '%s'",`${this.getPluginIdentifier()}.${e}`),this.registeredPlatforms.set(e,i)}getAccessoryConstructor(e){let i=P.getAccessoryName(e),r=this.registeredAccessories.get(i);if(!r)throw new Error(`The requested accessory '${i}' was not registered by the plugin '${this.getPluginIdentifier()}'.`);return r}getPlatformConstructor(e){let i=P.getPlatformName(e),r=this.registeredPlatforms.get(i);if(!r)throw new Error(`The requested platform '${i}' was not registered by the plugin '${this.getPluginIdentifier()}'.`);if(this.activeDynamicPlatforms.has(i))throw new Error(`The dynamic platform ${i} from the plugin ${this.getPluginIdentifier()} is configured times in your config.json.`);return r}assignDynamicPlatform(e,i){let r=P.getPlatformName(e),t=this.activeDynamicPlatforms.get(r);t||(t=[],this.activeDynamicPlatforms.set(r,t)),t.unshift(i)}getActiveDynamicPlatform(e){let i=this.activeDynamicPlatforms.get(e);return i&&i[0]}async load(){let e=this.loadContext;if(Ie(e,"Reached illegal state. Plugin state is undefined!"),this.loadContext=void 0,!e.engines||!e.engines.homebridge)throw new Error(`Plugin ${this.pluginPath} does not contain the 'homebridge' package in 'engines'.`);let i=e.engines.homebridge,r=e.engines.node;se(v(),i,{includePrerelease:!0})||L.error(`The plugin "${this.pluginName}" requires a Homebridge version of ${i} which does not satisfy the current Homebridge version of ${v()}. You may need to update this plugin (or Homebridge) to a newer version. You may face unexpected issues or stability problems running this plugin.`),r&&!se(te.version,r)&&L.warn(`The plugin "${this.pluginName}" requires Node.js version of ${r} which does not satisfy the current Node.js version of ${te.version}. You may need to upgrade your installation of Node.js - see https://homebridge.io/w/JTKEF`);let t=e.dependencies||{};(t.homebridge||t["hap-nodejs"])&&L.error(`The plugin "${this.pluginName}" defines 'homebridge' and/or 'hap-nodejs' in their 'dependencies' section, meaning they carry an additional copy of homebridge and hap-nodejs. This not only wastes disk space, but also can cause major incompatibility issues and thus is considered bad practice. Please inform the developer to update their plugin!`);let s=Se.join(this.pluginPath,this.main),n=await import(Ee(s).href);if(typeof n=="function")this.pluginInitializer=n;else if(n&&typeof n.default=="function")this.pluginInitializer=n.default;else throw new Error(`Plugin ${this.pluginPath} does not export a initializer function from main.`)}initialize(e){if(!this.pluginInitializer)throw new Error("Tried to initialize a plugin which hasn't been loaded yet!");return this.pluginInitializer(e)}};var f=g.internal,we=Re(import.meta.url),oe=we.resolve.paths(""),P=class o{static PLUGIN_IDENTIFIER_PATTERN=/^((@[\w-]*)\/)?(homebridge-[\w-]*)$/;api;searchPaths=new Set;strictPluginResolution=!1;activePlugins;disabledPlugins;plugins=new Map;pluginIdentifierTranslation=new Map;accessoryToPluginMap=new Map;platformToPluginMap=new Map;currentInitializingPlugin;constructor(e,i){this.api=e,i&&(i.customPluginPath&&this.searchPaths.add(b.resolve(x.cwd(),i.customPluginPath)),this.strictPluginResolution=i.strictPluginResolution||!1,this.activePlugins=i.activePlugins,this.disabledPlugins=Array.isArray(i.disabledPlugins)?i.disabledPlugins:void 0),this.api.on("registerAccessory",this.handleRegisterAccessory.bind(this)),this.api.on("registerPlatform",this.handleRegisterPlatform.bind(this))}static isQualifiedPluginIdentifier(e){return o.PLUGIN_IDENTIFIER_PATTERN.test(e)}static extractPluginName(e){return e.match(o.PLUGIN_IDENTIFIER_PATTERN)[3]}static extractPluginScope(e){return e.match(o.PLUGIN_IDENTIFIER_PATTERN)[2]}static getAccessoryName(e){return e.includes(".")?e.split(".")[1]:e}static getPlatformName(e){return e.includes(".")?e.split(".")[1]:e}static getPluginIdentifier(e){return e.split(".")[0]}async initializeInstalledPlugins(){f.info("---"),this.loadInstalledPlugins();for(let[e,i]of this.plugins){try{await i.load()}catch(r){f.error("===================="),f.error(`ERROR LOADING PLUGIN ${e}:`),f.error(r.stack),f.error("===================="),this.plugins.delete(e);continue}this.disabledPlugins&&this.disabledPlugins.includes(i.getPluginIdentifier())&&(i.disabled=!0),i.disabled?f.warn(`Disabled plugin: ${e}@${i.version}`):f.info(`Loaded plugin: ${e}@${i.version}`),await this.initializePlugin(i,e),f.info("---")}this.currentInitializingPlugin=void 0}async initializePlugin(e,i){try{this.currentInitializingPlugin=e,await e.initialize(this.api)}catch(r){f.error("===================="),f.error(`ERROR INITIALIZING PLUGIN ${i}:`),f.error(r.stack),f.error("===================="),this.plugins.delete(i)}}handleRegisterAccessory(e,i,r){if(!this.currentInitializingPlugin)throw new Error(`Unexpected accessory registration. Plugin ${r?`'${r}' `:""}tried to register outside the initializer function!`);r&&r!==this.currentInitializingPlugin.getPluginIdentifier()&&(f.info(`Plugin '${this.currentInitializingPlugin.getPluginIdentifier()}' tried to register with an incorrect plugin identifier: '${r}'. Please report this to the developer!`),this.pluginIdentifierTranslation.set(r,this.currentInitializingPlugin.getPluginIdentifier())),this.currentInitializingPlugin.registerAccessory(e,i);let t=this.accessoryToPluginMap.get(e);t||(t=[],this.accessoryToPluginMap.set(e,t)),t.push(this.currentInitializingPlugin)}handleRegisterPlatform(e,i,r){if(!this.currentInitializingPlugin)throw new Error(`Unexpected platform registration. Plugin ${r?`'${r}' `:""}tried to register outside the initializer function!`);r&&r!==this.currentInitializingPlugin.getPluginIdentifier()&&(f.debug(`Plugin '${this.currentInitializingPlugin.getPluginIdentifier()}' tried to register with an incorrect plugin identifier: '${r}'. Please report this to the developer!`),this.pluginIdentifierTranslation.set(r,this.currentInitializingPlugin.getPluginIdentifier())),this.currentInitializingPlugin.registerPlatform(e,i);let t=this.platformToPluginMap.get(e);t||(t=[],this.platformToPluginMap.set(e,t)),t.push(this.currentInitializingPlugin)}getPluginForAccessory(e){let i;if(e.includes(".")){let r=o.getPluginIdentifier(e);if(!this.hasPluginRegistered(r))throw new Error(`The requested plugin '${r}' was not registered.`);i=this.getPlugin(r)}else{let r=this.accessoryToPluginMap.get(e);if(!r)throw new Error(`No plugin was found for the accessory "${e}" in your config.json. Please make sure the corresponding plugin is installed correctly.`);if(r.length>1){let t=r.map(s=>`${s.getPluginIdentifier()}.${e}`).join(", ");if(r=r.filter(s=>!s.disabled),r.length!==1)throw new Error(`The requested accessory '${e}' has been registered multiple times. Please be more specific by writing one of: ${t}`)}i=r[0],e=`${i.getPluginIdentifier()}.${e}`}return i}getPluginForPlatform(e){let i;if(e.includes(".")){let r=o.getPluginIdentifier(e);if(!this.hasPluginRegistered(r))throw new Error(`The requested plugin '${r}' was not registered.`);i=this.getPlugin(r)}else{let r=this.platformToPluginMap.get(e);if(!r)throw new Error(`No plugin was found for the platform "${e}" in your config.json. Please make sure the corresponding plugin is installed correctly.`);if(r.length>1){let t=r.map(s=>`${s.getPluginIdentifier()}.${e}`).join(", ");if(r=r.filter(s=>!s.disabled),r.length!==1)throw new Error(`The requested platform '${e}' has been registered multiple times. Please be more specific by writing one of: ${t}`)}i=r[0],e=`${i.getPluginIdentifier()}.${e}`}return i}hasPluginRegistered(e){return this.plugins.has(e)||this.pluginIdentifierTranslation.has(e)}getPlugin(e){let i=this.plugins.get(e);if(i)return i;{let r=this.pluginIdentifierTranslation.get(e);if(r)return this.plugins.get(r)}}getPluginByActiveDynamicPlatform(e){let i=(this.platformToPluginMap.get(e)||[]).filter(r=>!!r.getActiveDynamicPlatform(e));if(i.length!==0)if(i.length>1){let r=i.map(t=>t.getPluginIdentifier()).join(", ");throw new Error(`'${e}' is an ambiguous platform name. It was registered by multiple plugins: ${r}`)}else return i[0]}loadInstalledPlugins(){this.loadDefaultPaths(),this.searchPaths.forEach(e=>{if(R.existsSync(e))if(R.existsSync(b.join(e,"package.json")))try{this.loadPlugin(e)}catch(i){f.warn(i.message)}else{let i=R.readdirSync(e).filter(r=>{try{return R.statSync(b.resolve(e,r)).isDirectory()}catch(t){return f.debug(`Ignoring path ${b.resolve(e,r)} - ${t.message}`),!1}});i.slice().filter(r=>r.charAt(0)==="@").forEach(r=>{let t=i.indexOf(r);i.splice(t,1);let s=b.join(e,r);R.readdirSync(s).filter(n=>o.isQualifiedPluginIdentifier(n)).filter(n=>{try{return R.statSync(b.resolve(s,n)).isDirectory()}catch(c){return f.debug(`Ignoring path ${b.resolve(s,n)} - ${c.message}`),!1}}).forEach(n=>i.push(`${r}/${n}`))}),i.filter(r=>o.isQualifiedPluginIdentifier(r)&&(!this.activePlugins||this.activePlugins.includes(r))).forEach(r=>{try{let t=b.resolve(e,r);this.loadPlugin(t)}catch(t){f.warn(t.message)}})}}),this.plugins.size===0&&f.warn("No plugins found.")}loadPlugin(e){let i=o.loadPackageJSON(e),r=i.name,t=o.extractPluginName(r),s=o.extractPluginScope(r),n=this.plugins.get(r);if(n)throw new Error(`Warning: skipping plugin found at '${e}' since we already loaded the same plugin from '${n.getPluginPath()}'.`);let c=new U(t,e,i,s);return this.plugins.set(r,c),c}static loadPackageJSON(e){let i=b.join(e,"package.json"),r;if(!R.existsSync(i))throw new Error(`Plugin ${e} does not contain a package.json.`);try{r=JSON.parse(R.readFileSync(i,{encoding:"utf8"}))}catch(t){throw new Error(`Plugin ${e} contains an invalid package.json. Error: ${t}`)}if(!r.name||!o.isQualifiedPluginIdentifier(r.name))throw new Error(`Plugin ${e} does not have a package name that begins with 'homebridge-' or '@scope/homebridge-.`);if(!r.keywords||!r.keywords.includes("homebridge-plugin"))throw new Error(`Plugin ${e} package.json does not contain the keyword 'homebridge-plugin'.`);return r}loadDefaultPaths(){if(this.strictPluginResolution){this.searchPaths.size===0&&this.addNpmPrefixToSearchPaths();return}oe&&oe.forEach(e=>this.searchPaths.add(e)),x.env.NODE_PATH?x.env.NODE_PATH.split(b.delimiter).filter(e=>!!e).forEach(e=>this.searchPaths.add(e)):(x.platform!=="win32"&&(this.searchPaths.add("/usr/local/lib/node_modules"),this.searchPaths.add("/usr/lib/node_modules")),this.addNpmPrefixToSearchPaths())}addNpmPrefixToSearchPaths(){x.platform==="win32"?this.searchPaths.add(b.join(x.env.APPDATA,"npm/node_modules")):this.searchPaths.add(Ce('/bin/echo -n "$(npm -g prefix)/lib/node_modules"',{env:Object.assign({npm_config_loglevel:"silent",npm_update_notifier:"false"},x.env)}).toString("utf8"))}};import Ne from"node:os";import M from"node:path";var m=class o{static customStoragePath;static storageAccessed=!1;static configPath(){return M.join(o.storagePath(),"config.json")}static persistPath(){return M.join(o.storagePath(),"persist")}static cachedAccessoryPath(){return M.join(o.storagePath(),"accessories")}static storagePath(){return o.storageAccessed=!0,o.customStoragePath?o.customStoragePath:M.join(Ne.homedir(),".homebridge")}static setStoragePath(...e){if(o.storageAccessed)throw new Error("Storage path was already accessed and cannot be changed anymore. Try initializing your custom storage path earlier!");o.customStoragePath=M.resolve(...e)}};var _e=g.internal;var O=class extends xe{version=2.7;serverVersion=v();user=m;hap=ne;hapLegacyTypes=ne.LegacyTypes;platformAccessory=A;constructor(){super()}versionGreaterOrEqual(e){return De.gte(this.serverVersion,e)}static isDynamicPlatformPlugin(e){return"configureAccessory"in e}static isStaticPlatformPlugin(e){return"accessories"in e}signalFinished(){this.emit("didFinishLaunching")}signalShutdown(){this.emit("shutdown")}registerAccessory(e,i,r){typeof i=="function"?(r=i,i=e,this.emit("registerAccessory",i,r)):this.emit("registerAccessory",i,r,e)}registerPlatform(e,i,r){typeof i=="function"?(r=i,i=e,this.emit("registerPlatform",i,r)):this.emit("registerPlatform",i,r,e)}publishCameraAccessories(e,i){this.publishExternalAccessories(e,i)}publishExternalAccessories(e,i){P.isQualifiedPluginIdentifier(e)||_e.info(`One of your plugins incorrectly registered an external accessory using the platform name (${e}) and not the plugin identifier. Please report this to the developer!`),i.forEach(r=>{if(!(r instanceof A))throw new TypeError(`${e} attempt to register an accessory that isn't PlatformAccessory!`);r._associatedPlugin=e}),this.emit("publishExternalAccessories",i)}registerPlatformAccessories(e,i,r){r.forEach(t=>{if(!(t instanceof A))throw new TypeError(`${e} - ${i} attempt to register an accessory that isn't PlatformAccessory!`);t._associatedPlugin=e,t._associatedPlatform=i}),this.emit("registerPlatformAccessories",r)}updatePlatformAccessories(e){this.emit("updatePlatformAccessories",e)}unregisterPlatformAccessories(e,i,r){r.forEach(t=>{if(!(t instanceof A))throw new TypeError(`${e} - ${i} attempt to unregister an accessory that isn't PlatformAccessory!`)}),this.emit("unregisterPlatformAccessories",r)}};import{Accessory as Me,AccessoryEventTypes as w,Bridge as $e,Categories as Be,Characteristic as D,CharacteristicEventTypes as He,CharacteristicWarningType as _,HAPLibraryVersion as Ue,once as ke,Service as j,uuid as ce}from"hap-nodejs";import S from"node:path";import I from"fs-extra";var k=class{constructor(e){this.baseDirectory=e}initSync(){return I.ensureDirSync(this.baseDirectory)}getItemSync(e){let i=S.resolve(this.baseDirectory,e);return I.pathExistsSync(i)?I.readJsonSync(i):null}async getItem(e){let i=S.resolve(this.baseDirectory,e);return await I.pathExists(i)?await I.readJson(i):null}setItemSync(e,i){return I.writeJsonSync(S.resolve(this.baseDirectory,e),i)}setItem(e,i){return I.writeJson(S.resolve(this.baseDirectory,e),i)}copyItem(e,i){return I.copyFile(S.resolve(this.baseDirectory,e),S.resolve(this.baseDirectory,i))}copyItemSync(e,i){return I.copyFileSync(S.resolve(this.baseDirectory,e),S.resolve(this.baseDirectory,i))}removeItemSync(e){return I.removeSync(S.resolve(this.baseDirectory,e))}};import Oe from"node:crypto";var Le=/^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$/;function Y(o){return Le.test(o)}function ae(o){let e=Oe.createHash("sha1");e.update(o);let i=e.digest("hex"),r=0;return"xx:xx:xx:xx:xx:xx".replace(/x/g,()=>i[r++]).toUpperCase()}var a=g.internal,F=class o{constructor(e,i,r,t,s,n){this.api=e;this.pluginManager=i;this.externalPortService=r;this.bridgeOptions=t;this.bridgeConfig=s;this.config=n;this.storageService=new k(this.bridgeOptions.cachedAccessoriesDir),this.storageService.initSync(),this.allowInsecureAccess=this.bridgeOptions.insecureAccess||!1,this.api.on("registerPlatformAccessories",this.handleRegisterPlatformAccessories.bind(this)),this.api.on("updatePlatformAccessories",this.handleUpdatePlatformAccessories.bind(this)),this.api.on("unregisterPlatformAccessories",this.handleUnregisterPlatformAccessories.bind(this)),this.api.on("publishExternalAccessories",this.handlePublishExternalAccessories.bind(this)),this.bridge=new $e(s.name,ce.generate("HomeBridge")),this.bridge.on(w.CHARACTERISTIC_WARNING,()=>{})}bridge;storageService;allowInsecureAccess;cachedPlatformAccessories=[];cachedAccessoriesFileLoaded=!1;publishedExternalAccessories=new Map;static printCharacteristicWriteWarning(e,i,r,t){let s="See https://homebridge.io/w/JtMGR for more info.";switch(t.type){case _.SLOW_READ:case _.SLOW_WRITE:r.ignoreSlow||a.info(y(e.getPluginIdentifier()),"This plugin slows down Homebridge.",t.message,s);break;case _.TIMEOUT_READ:case _.TIMEOUT_WRITE:a.error(y(e.getPluginIdentifier()),"This plugin slows down Homebridge.",t.message,s);break;case _.WARN_MESSAGE:a.info(y(e.getPluginIdentifier()),`This plugin generated a warning from the characteristic '${t.characteristic.displayName}':`,`${t.message}.`,s);break;case _.ERROR_MESSAGE:a.error(y(e.getPluginIdentifier()),`This plugin threw an error from the characteristic '${t.characteristic.displayName}':`,`${t.message}.`,s);break;case _.DEBUG_MESSAGE:a.debug(y(e.getPluginIdentifier()),`Characteristic '${t.characteristic.displayName}':`,`${t.message}.`,s);break;default:a.info(y(e.getPluginIdentifier()),`This plugin generated a warning from the characteristic '${t.characteristic.displayName}':`,`${t.message}.`,s);break}t.stack&&a.debug(y(e.getPluginIdentifier()),t.stack)}publishBridge(){let e=this.bridgeConfig,i=this.bridge.getService(j.AccessoryInformation);i.setCharacteristic(D.Manufacturer,e.manufacturer||"homebridge.io"),i.setCharacteristic(D.Model,e.model||"homebridge"),i.setCharacteristic(D.SerialNumber,e.username),i.setCharacteristic(D.FirmwareRevision,e.firmwareRevision||v()),this.bridge.on(w.LISTENING,t=>{a.success("Homebridge v%s (HAP v%s) (%s) is running on port %s.",v(),Ue(),e.name,t)});let r={username:e.username,port:e.port,pincode:e.pin,category:Be.BRIDGE,bind:e.bind,addIdentifyingMaterial:!0,advertiser:e.advertiser};e.setupID&&e.setupID.length===4&&(r.setupID=e.setupID),a.debug("Publishing bridge accessory (name: %s, publishInfo: %o).",this.bridge.displayName,o.strippingPinCode(r)),this.bridge.publish(r,this.allowInsecureAccess)}async loadCachedPlatformAccessoriesFromDisk(){let e=null;try{e=await this.storageService.getItem(this.bridgeOptions.cachedAccessoriesItemName)}catch(i){a.error("Failed to load cached accessories from disk:",i.message),i instanceof SyntaxError?e=await this.restoreCachedAccessoriesBackup():a.error("Not restoring cached accessories - some accessories may be reset.")}e&&(a.info(`Loaded ${e.length} cached accessories from ${this.bridgeOptions.cachedAccessoriesItemName}.`),this.cachedPlatformAccessories=e.map(i=>A.deserialize(i)),e.length&&await this.createCachedAccessoriesBackup()),this.cachedAccessoriesFileLoaded=!0}get backupCacheFileName(){return`.${this.bridgeOptions.cachedAccessoriesItemName}.bak`}async createCachedAccessoriesBackup(){try{await this.storageService.copyItem(this.bridgeOptions.cachedAccessoriesItemName,this.backupCacheFileName)}catch(e){a.warn(`Failed to create a backup of the ${this.bridgeOptions.cachedAccessoriesItemName} cached accessories file:`,e.message)}}async restoreCachedAccessoriesBackup(){try{let e=await this.storageService.getItem(this.backupCacheFileName);return e&&e.length&&a.warn(`Recovered ${e.length} accessories from ${this.bridgeOptions.cachedAccessoriesItemName} cache backup.`),e}catch{return null}}restoreCachedPlatformAccessories(){this.cachedPlatformAccessories=this.cachedPlatformAccessories.filter(e=>{let i=this.pluginManager.getPlugin(e._associatedPlugin);if(!i)try{i=this.pluginManager.getPluginByActiveDynamicPlatform(e._associatedPlatform),i&&(a.info(`When searching for the associated plugin of the accessory '${e.displayName}' it seems like the plugin name changed from '${e._associatedPlugin}' to '${i.getPluginIdentifier()}'. Plugin association is now being transformed!`),e._associatedPlugin=i.getPluginIdentifier())}catch(t){a.info(`Could not find the associated plugin for the accessory '${e.displayName}'. Tried to find the plugin by the platform name but ${t.message}`)}let r=i&&i.getActiveDynamicPlatform(e._associatedPlatform);if(i&&e._associatedHAPAccessory.on(w.CHARACTERISTIC_WARNING,o.printCharacteristicWriteWarning.bind(this,i,e._associatedHAPAccessory,{})),r)e.getService(j.AccessoryInformation)?.setCharacteristic(D.FirmwareRevision,"0"),r.configureAccessory(e);else if(a.info(`Failed to find plugin to handle accessory ${e._associatedHAPAccessory.displayName}`),!this.bridgeOptions.keepOrphanedCachedAccessories)return a.info(`Removing orphaned accessory ${e._associatedHAPAccessory.displayName}`),!1;try{this.bridge.addBridgedAccessory(e._associatedHAPAccessory)}catch(t){return a.warn(`${e._associatedPlugin?y(e._associatedPlugin):""} Could not restore cached accessory '${e._associatedHAPAccessory.displayName}':`,t.message),!1}return!0})}saveCachedPlatformAccessoriesOnDisk(){try{if(this.cachedAccessoriesFileLoaded){let e=this.cachedPlatformAccessories.map(i=>A.serialize(i));this.storageService.setItemSync(this.bridgeOptions.cachedAccessoriesItemName,e)}}catch(e){a.error("Failed to save cached accessories to disk:",e.message),a.error("Your accessories will not persist between restarts until this issue is resolved.")}}handleRegisterPlatformAccessories(e){let i=e.map(r=>{this.cachedPlatformAccessories.push(r);let t=this.pluginManager.getPlugin(r._associatedPlugin);return t?(t.getActiveDynamicPlatform(r._associatedPlatform)||a.warn("The plugin '%s' registered a new accessory for the platform '%s'. The platform couldn't be found though!",r._associatedPlugin,r._associatedPlatform),r._associatedHAPAccessory.on(w.CHARACTERISTIC_WARNING,o.printCharacteristicWriteWarning.bind(this,t,r._associatedHAPAccessory,{}))):a.warn("A platform configured a new accessory under the plugin name '%s'. However no loaded plugin could be found for the name!",r._associatedPlugin),r._associatedHAPAccessory});this.bridge.addBridgedAccessories(i),this.saveCachedPlatformAccessoriesOnDisk()}handleUpdatePlatformAccessories(){this.saveCachedPlatformAccessoriesOnDisk()}handleUnregisterPlatformAccessories(e){let i=e.map(r=>{let t=this.cachedPlatformAccessories.indexOf(r);return t>=0&&this.cachedPlatformAccessories.splice(t,1),r._associatedHAPAccessory});this.bridge.removeBridgedAccessories(i),this.saveCachedPlatformAccessoriesOnDisk()}async handlePublishExternalAccessories(e){let i=this.bridgeConfig.pin;for(let r of e){let t=r._associatedHAPAccessory,s=ae(t.UUID),n=await this.externalPortService.requestPort(s);if(this.publishedExternalAccessories.has(s))throw new Error(`Accessory ${t.displayName} experienced an address collision.`);this.publishedExternalAccessories.set(s,r);let c=this.pluginManager.getPlugin(r._associatedPlugin);c?t.on(w.CHARACTERISTIC_WARNING,o.printCharacteristicWriteWarning.bind(this,c,t,{ignoreSlow:!0})):P.isQualifiedPluginIdentifier(r._associatedPlugin)&&a.warn("A platform configured a external accessory under the plugin name '%s'. However no loaded plugin could be found for the name!",r._associatedPlugin),t.on(w.LISTENING,d=>{a.success("%s is running on port %s.",t.displayName,d),a.info("Please add [%s] manually in Home app. Setup Code: %s",t.displayName,i)});let h={username:s,pincode:i,category:r.category,port:n,bind:this.bridgeConfig.bind,addIdentifyingMaterial:!0,advertiser:this.bridgeConfig.advertiser};a.debug("Publishing external accessory (name: %s, publishInfo: %o).",t.displayName,o.strippingPinCode(h)),t.publish(h,this.allowInsecureAccess)}}createHAPAccessory(e,i,r,t,s){let n=(i.getServices()||[]).filter(p=>!!p),c=(i.getControllers&&i.getControllers()||[]).filter(p=>!!p);if(n.length===0&&c.length===0)return;let h=ce.generate(`${t}:${s||r}`),d=new Me(r,h);i.identify&&d.on(w.IDENTIFY,(p,T)=>{i.identify(()=>{}),T()});let u=d.getService(j.AccessoryInformation);return n.forEach(p=>{p instanceof j.AccessoryInformation?(p.setCharacteristic(D.Name,r),p.getCharacteristic(D.Identify).removeAllListeners(He.SET),u.replaceCharacteristicsFromService(p)):d.addService(p)}),d.on(w.CHARACTERISTIC_WARNING,o.printCharacteristicWriteWarning.bind(this,e,d,{})),c.forEach(p=>{d.configureController(p)}),d}async loadPlatformAccessories(e,i,r,t){return new Promise(s=>{let n=setInterval(()=>{a.warn(y(e.getPluginIdentifier()),"This plugin is taking long time to load and preventing Homebridge from starting. See https://homebridge.io/w/JtMGR for more info.")},2e4);i.accessories(ke(c=>{clearInterval(n),c.forEach((h,d)=>{let u=h.name,p=h.uuid_base;a.info("Initializing platform accessory '%s'...",u);let T=this.createHAPAccessory(e,h,u,r,p);T?this.bridge.addBridgedAccessory(T):t("Platform %s returned an accessory at index %d with an empty set of services. Won't adding it to the bridge!",r,d)}),s()}))})}teardown(){this.bridge.unpublish();for(let e of this.publishedExternalAccessories.values())e._associatedHAPAccessory.unpublish();this.saveCachedPlatformAccessoriesOnDisk(),this.api.signalShutdown()}static strippingPinCode(e){let i={...e};return i.pincode="***-**-***",i}};import ze from"node:child_process";import We,{dirname as qe}from"node:path";import $ from"node:process";import{fileURLToPath as Ve}from"node:url";import Ye from"fs-extra";import{EventEmitter as je}from"node:events";import Q from"node:process";var G=class extends je{constructor(){super()}start(){Q.on("message",e=>{!e||typeof e!="object"||!e.id||this.emit(e.id,e.data)})}sendMessage(e,i){Q.send&&Q.send({id:e,data:i})}};var Qe=Ve(import.meta.url),Je=qe(Qe);var B=class{constructor(e,i,r,t,s,n,c,h,d){this.type=e;this.identifier=i;this.plugin=r;this.bridgeConfig=t;this.homebridgeConfig=s;this.homebridgeOptions=n;this.api=c;this.ipcService=h;this.externalPortService=d;this.log=g.withPrefix(this.plugin.getPluginIdentifier()),this.api.on("shutdown",()=>{this.shuttingDown=!0,this.teardown()}),this.api.setMaxListeners(this.api.getMaxListeners()+1)}child;args=[];processEnv={};shuttingDown=!1;lastBridgeStatus="pending";pairedStatus=null;manuallyStopped=!1;setupUri=null;pluginConfig=[];log;displayName;start(){this.setProcessFlags(),this.setProcessEnv(),this.startChildProcess(),this.pluginConfig.length>1||this.pluginConfig.length===0?this.displayName=this.plugin.getPluginIdentifier():this.displayName=this.pluginConfig[0]?.name||this.plugin.getPluginIdentifier(),this.log=g.withPrefix(this.displayName)}addConfig(e){this.pluginConfig.push(e)}get bridgeStatus(){return this.lastBridgeStatus}set bridgeStatus(e){this.lastBridgeStatus=e,this.sendStatusUpdate()}startChildProcess(){this.bridgeStatus="pending",this.child=ze.fork(We.resolve(Je,"childBridgeFork.js"),this.args,this.processEnv),this.child.stdout?.on("data",e=>{$.stdout.write(e)}),this.child.stderr?.on("data",e=>{$.stderr.write(e)}),this.child.on("exit",()=>{this.log.warn("Child bridge process ended")}),this.child.on("error",e=>{this.bridgeStatus="down",this.log.error("Child process error",e)}),this.child.once("close",(e,i)=>{this.bridgeStatus="down",this.handleProcessClose(e,i)}),this.child.on("message",e=>{if(!(typeof e!="object"||!e.id))switch(e.id){case"ready":{this.log(`Launched child bridge with PID ${this.child?.pid}`),this.loadPlugin();break}case"loaded":{let i=e.data.version;this.pluginConfig.length>1?this.log(`Loaded ${this.plugin.getPluginIdentifier()} v${i} child bridge successfully with ${this.pluginConfig.length} accessories`):this.log(`Loaded ${this.plugin.getPluginIdentifier()} v${i} child bridge successfully`),this.startBridge();break}case"online":{this.bridgeStatus="ok";break}case"portRequest":{this.handlePortRequest(e.data);break}case"status":{this.pairedStatus=e.data.paired,this.setupUri=e.data.setupUri,this.sendStatusUpdate();break}}})}handleProcessClose(e,i){this.log(`Process Ended. Code: ${e}, Signal: ${i}`),setTimeout(()=>{this.shuttingDown||(this.log("Restarting Process..."),this.startChildProcess())},7e3)}sendMessage(e,i){this.child&&this.child.connected&&this.child.send({id:e,data:i})}setProcessFlags(){this.homebridgeOptions.debugModeEnabled&&this.args.push("-D"),this.homebridgeOptions.forceColourLogging&&this.args.push("-C"),this.homebridgeOptions.insecureAccess&&this.args.push("-I"),this.homebridgeOptions.noLogTimestamps&&this.args.push("-T"),this.homebridgeOptions.keepOrphanedCachedAccessories&&this.args.push("-K"),this.homebridgeOptions.customStoragePath&&this.args.push("-U",this.homebridgeOptions.customStoragePath),this.homebridgeOptions.customPluginPath&&this.args.push("-P",this.homebridgeOptions.customPluginPath)}setProcessEnv(){this.processEnv={env:{...$.env,DEBUG:`${$.env.DEBUG||""} ${this.bridgeConfig.env?.DEBUG||""}`.trim(),NODE_OPTIONS:`${$.env.NODE_OPTIONS||""} ${this.bridgeConfig.env?.NODE_OPTIONS||""}`.trim()},silent:!0}}loadPlugin(){let e={name:this.bridgeConfig.name||this.displayName||this.plugin.getPluginIdentifier(),port:this.bridgeConfig.port,username:this.bridgeConfig.username,advertiser:this.homebridgeConfig.bridge.advertiser,pin:this.bridgeConfig.pin||this.homebridgeConfig.bridge.pin,bind:this.homebridgeConfig.bridge.bind,setupID:this.bridgeConfig.setupID,manufacturer:this.bridgeConfig.manufacturer||this.homebridgeConfig.bridge.manufacturer,model:this.bridgeConfig.model||this.homebridgeConfig.bridge.model,firmwareRevision:this.bridgeConfig.firmwareRevision||this.homebridgeConfig.bridge.firmwareRevision},i={cachedAccessoriesDir:m.cachedAccessoryPath(),cachedAccessoriesItemName:`cachedAccessories.${this.bridgeConfig.username.replace(/:/g,"").toUpperCase()}`};Object.assign(i,this.homebridgeOptions),this.sendMessage("load",{type:this.type,identifier:this.identifier,pluginPath:this.plugin.getPluginPath(),pluginConfig:this.pluginConfig,bridgeConfig:e,bridgeOptions:i,homebridgeConfig:{bridge:this.homebridgeConfig.bridge,ports:this.homebridgeConfig.ports,disabledPlugins:[],accessories:[],platforms:[]}})}startBridge(){this.sendMessage("start")}async handlePortRequest(e){let i=await this.externalPortService.requestPort(e.username);this.sendMessage("portAllocated",{username:e.username,port:i})}teardown(){this.child&&this.child.connected&&(this.bridgeStatus="down",this.child.kill("SIGTERM"))}sendStatusUpdate(){this.ipcService.sendMessage("childBridgeStatusUpdate",this.getMetadata())}restartChildBridge(){this.manuallyStopped?this.startChildBridge():(this.log.warn("Restarting child bridge..."),this.refreshConfig(),this.teardown())}stopChildBridge(){this.shuttingDown?this.log.warn("Bridge already shutting down or stopped."):(this.log.warn("Stopping child bridge (will not restart)..."),this.shuttingDown=!0,this.manuallyStopped=!0,this.child?.removeAllListeners("close"),this.teardown())}startChildBridge(){this.manuallyStopped&&this.bridgeStatus==="down"&&(!this.child||!this.child.connected)?(this.log.warn("Starting child bridge..."),this.refreshConfig(),this.startChildProcess(),this.shuttingDown=!1,this.manuallyStopped=!1):this.log.warn("Cannot start child bridge, it is still running or was not manually stopped")}async refreshConfig(){try{let e=await Ye.readJson(m.configPath());if(this.type==="platform"){let i=e.platforms?.filter(r=>r.platform===this.identifier&&r._bridge?.username===this.bridgeConfig.username);i.length?(this.pluginConfig=i,this.bridgeConfig=this.pluginConfig[0]._bridge||this.bridgeConfig):this.log.warn("Platform config could not be found, using existing config.")}else if(this.type==="accessory"){let i=e.accessories?.filter(r=>r.accessory===this.identifier&&r._bridge?.username===this.bridgeConfig.username);i.length?(this.pluginConfig=i,this.bridgeConfig=this.pluginConfig[0]._bridge||this.bridgeConfig):this.log.warn("Accessory config could not be found, using existing config.")}}catch(e){this.log.error("Failed to refresh plugin config:",e.message)}}getMetadata(){return{status:this.bridgeStatus,paired:this.pairedStatus,setupUri:this.setupUri,username:this.bridgeConfig.username,pin:this.bridgeConfig.pin||this.homebridgeConfig.bridge.pin,name:this.bridgeConfig.name||this.displayName||this.plugin.getPluginIdentifier(),plugin:this.plugin.getPluginIdentifier(),identifier:this.identifier,pid:this.child?.pid,manuallyStopped:this.manuallyStopped}}};var z=class{constructor(e){this.externalPorts=e}nextExternalPort;allocatedPorts=new Map;async requestPort(e){let i=this.allocatedPorts.get(e);if(i)return i;let r=this.getNextFreePort();return this.allocatedPorts.set(e,r),r}getNextFreePort(){if(this.externalPorts){if(this.nextExternalPort===void 0)return this.nextExternalPort=this.externalPorts.start,this.nextExternalPort;if(this.nextExternalPort++,this.nextExternalPort<=this.externalPorts.end)return this.nextExternalPort;g.internal.warn("External port pool ran out of ports. Falling back to random port assignment.")}}};var l=g.internal;var q=class o{constructor(e={}){this.options=e;this.config=o.loadConfig(),this.api=new O,this.ipcService=new G,this.externalPortService=new z(this.config.ports),this.setServerStatus("pending");let i={activePlugins:this.config.plugins,disabledPlugins:this.config.disabledPlugins,customPluginPath:e.customPluginPath,strictPluginResolution:e.strictPluginResolution};this.pluginManager=new P(this.api,i);let r={cachedAccessoriesDir:m.cachedAccessoryPath(),cachedAccessoriesItemName:"cachedAccessories"};Object.assign(r,this.options),this.bridgeService=new F(this.api,this.pluginManager,this.externalPortService,r,this.config.bridge,this.config),this.bridgeService.bridge.on(J.ADVERTISED,()=>{this.setServerStatus("ok")}),this.bridgeService.bridge.on(J.PAIRED,()=>{this.setServerStatus(this.serverStatus)}),this.bridgeService.bridge.on(J.UNPAIRED,()=>{this.setServerStatus(this.serverStatus)})}api;pluginManager;bridgeService;ipcService;externalPortService;config;childBridges=new Map;serverStatus="pending";setServerStatus(e){this.serverStatus=e,this.ipcService.sendMessage("serverStatusUpdate",{status:this.serverStatus,paired:this.bridgeService?.bridge?._accessoryInfo?.paired()??null,setupUri:this.bridgeService?.bridge?.setupURI()??null,name:this.bridgeService?.bridge?.displayName||this.config.bridge.name,username:this.config.bridge.username,pin:this.config.bridge.pin})}async start(){this.config.bridge.disableIpc!==!0&&this.initializeIpcEventHandlers();let e=[];await this.bridgeService.loadCachedPlatformAccessoriesFromDisk(),await this.pluginManager.initializeInstalledPlugins(),this.config.platforms.length>0&&e.push(...this.loadPlatforms()),this.config.accessories.length>0&&this.loadAccessories();for(let i of this.childBridges.values())i.start();this.bridgeService.restoreCachedPlatformAccessories(),this.api.signalFinished(),await Promise.all(e).then(()=>this.publishBridge())}teardown(){this.bridgeService.teardown(),this.setServerStatus("down")}publishBridge(){this.bridgeService.publishBridge(),this.printSetupInfo(this.config.bridge.pin)}static loadConfig(){let e=m.configPath(),i={name:"Homebridge",username:"CC:22:3D:E3:CE:30",pin:"031-45-154"};if(!le.existsSync(e))return l.warn("config.json (%s) not found.",e),{bridge:i,accessories:[],platforms:[]};let r;try{r=JSON.parse(le.readFileSync(e,{encoding:"utf8"}))}catch(n){throw l.error("There was a problem reading your config.json file."),l.error("Please try pasting your config.json file here to validate it: https://jsonlint.com"),l.error(""),n}r.ports!==void 0&&(r.ports.start&&r.ports.end?r.ports.start>r.ports.end&&(l.error("Invalid port pool configuration. End should be greater than or equal to start."),r.ports=void 0):(l.error("Invalid configuration for 'ports'. Missing 'start' and 'end' properties! Ignoring it!"),r.ports=void 0));let t=r.bridge||i;t.name=t.name||i.name,t.username=t.username||i.username,t.pin=t.pin||i.pin,r.bridge=t;let s=r.bridge.username;if(!Y(s))throw new Error(`Not a valid username: ${s}. Must be 6 pairs of colon-separated hexadecimal chars (A-F 0-9), like a MAC address.`);return r.accessories=r.accessories||[],r.platforms=r.platforms||[],Array.isArray(r.accessories)||(l.error("Value provided for accessories must be an array[]"),r.accessories=[]),Array.isArray(r.platforms)||(l.error("Value provided for platforms must be an array[]"),r.platforms=[]),l.info("Loaded config.json with %s accessories and %s platforms.",r.accessories.length,r.platforms.length),r.bridge.advertiser?[W.BONJOUR,W.CIAO,W.AVAHI,W.RESOLVED].includes(r.bridge.advertiser)||(r.bridge.advertiser=void 0,l.error("Value provided in bridge.advertiser is not valid, reverting to platform default.")):r.bridge.advertiser=void 0,r}loadAccessories(){l.info(`Loading ${this.config.accessories.length} accessories...`),this.config.accessories.forEach((e,i)=>{if(!e.accessory){l.warn("Your config.json contains an illegal accessory configuration object at position %d. Missing property 'accessory'. Skipping entry...",i+1);return}let r=e.accessory,t=e.name;if(!t){l.warn("Could not load accessory %s at position %d as it is missing the required 'name' property!",r,i+1);return}let s,n;try{s=this.pluginManager.getPluginForAccessory(r)}catch(u){l.error(u.message);return}if(s.disabled){l.warn(`Ignoring config for the accessory "${r}" in your config.json as the plugin "${s.getPluginIdentifier()}" has been disabled.`);return}try{n=s.getAccessoryConstructor(r)}catch(u){l.error(`Error loading the accessory "${r}" requested in your config.json at position ${i+1} - this is likely an issue with the "${s.getPluginIdentifier()}" plugin.`),l.error(u);return}let c=g.withPrefix(t);if(c("Initializing %s accessory...",r),e._bridge){e._bridge.username=e._bridge.username.toUpperCase();try{this.validateChildBridgeConfig("accessory",r,e._bridge)}catch(p){l.error(p.message);return}let u;this.childBridges.has(e._bridge.username)?(u=this.childBridges.get(e._bridge.username),c(`Adding to existing child bridge ${e._bridge.username}`)):(c(`Initializing child bridge ${e._bridge.username}`),u=new B("accessory",r,s,e._bridge,this.config,this.options,this.api,this.ipcService,this.externalPortService),this.childBridges.set(e._bridge.username,u)),u.addConfig(e);return}let h=new n(c,e,this.api),d=this.bridgeService.createHAPAccessory(s,h,t,r,e.uuid_base);if(d)try{this.bridgeService.bridge.addBridgedAccessory(d)}catch(u){c.error(`Error loading the accessory "${r}" from "${s.getPluginIdentifier()}" requested in your config.json:`,u.message)}else c.info("Accessory %s returned empty set of services; not adding it to the bridge.",r)})}loadPlatforms(){l.info(`Loading ${this.config.platforms.length} platforms...`);let e=[];return this.config.platforms.forEach((i,r)=>{if(!i.platform){l.warn("Your config.json contains an illegal platform configuration object at position %d. Missing property 'platform'. Skipping entry...",r+1);return}let t=i.platform,s=i.name||t,n,c;if(t==="config"&&Ke.env.UIX_SERVICE_MODE==="1")return;try{n=this.pluginManager.getPluginForPlatform(t)}catch(u){l.error(u.message);return}if(n.disabled){l.warn(`Ignoring config for the platform "${t}" in your config.json as the plugin "${n.getPluginIdentifier()}" has been disabled.`);return}try{c=n.getPlatformConstructor(t)}catch(u){l.error(`Error loading the platform "${t}" requested in your config.json at position ${r+1} - this is likely an issue with the "${n.getPluginIdentifier()}" plugin.`),l.error(u);return}let h=g.withPrefix(s);if(h("Initializing %s platform...",t),i._bridge){i._bridge.username=i._bridge.username.toUpperCase();try{this.validateChildBridgeConfig("platform",t,i._bridge)}catch(p){l.error(p.message);return}h(`Initializing child bridge ${i._bridge.username}`);let u=new B("platform",t,n,i._bridge,this.config,this.options,this.api,this.ipcService,this.externalPortService);this.childBridges.set(i._bridge.username,u),u.addConfig(i);return}let d=new c(h,i,this.api);O.isDynamicPlatformPlugin(d)?n.assignDynamicPlatform(t,d):O.isStaticPlatformPlugin(d)&&e.push(this.bridgeService.loadPlatformAccessories(n,d,t,h))}),e}validateChildBridgeConfig(e,i,r){if(!Y(r.username))throw new Error(`Error loading the ${e} "${i}" requested in your config.json - not a valid username in _bridge.username: "${r.username}". Must be 6 pairs of colon-separated hexadecimal chars (A-F 0-9), like a MAC address.`);if(this.childBridges.has(r.username)){let t=this.childBridges.get(r.username);if(e==="platform")throw new Error(`Error loading the ${e} "${i}" requested in your config.json - Duplicate username found in _bridge.username: "${r.username}". Each platform child bridge must have it's own unique username.`);if(t?.identifier!==i)throw new Error(`Error loading the ${e} "${i}" requested in your config.json - Duplicate username found in _bridge.username: "${r.username}". You can only group accessories of the same type in a child bridge.`)}if(r.username===this.config.bridge.username.toUpperCase())throw new Error(`Error loading the ${e} "${i}" requested in your config.json - Username found in _bridge.username: "${r.username}" is the same as the main bridge. Each child bridge platform/accessory must have it's own unique username.`)}initializeIpcEventHandlers(){this.ipcService.start(),this.ipcService.on("restartChildBridge",e=>{typeof e=="string"&&this.childBridges.get(e.toUpperCase())?.restartChildBridge()}),this.ipcService.on("stopChildBridge",e=>{typeof e=="string"&&this.childBridges.get(e.toUpperCase())?.stopChildBridge()}),this.ipcService.on("startChildBridge",e=>{typeof e=="string"&&this.childBridges.get(e.toUpperCase())?.startChildBridge()}),this.ipcService.on("childBridgeMetadataRequest",()=>{this.ipcService.sendMessage("childBridgeMetadataResponse",Array.from(this.childBridges.values()).map(e=>e.getMetadata()))})}printSetupInfo(e){console.log("Setup Payload:"),console.log(this.bridgeService.bridge.setupURI()),this.options.hideQRCode?console.log("Enter this code with your HomeKit app on your iOS device to pair with Homebridge:"):(console.log("Scan this code with your HomeKit app on your iOS device to pair with Homebridge:"),de.setErrorLevel("M"),de.generate(this.bridgeService.bridge.setupURI()),console.log("Or enter this code with your HomeKit app on your iOS device to pair with Homebridge:")),console.log(H.black.bgWhite(" ")),console.log(H.black.bgWhite(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 ")),console.log(H.black.bgWhite(` \u2502 ${e} \u2502 `)),console.log(H.black.bgWhite(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 ")),console.log(H.black.bgWhite(" "))}};var X=g.internal,K=re();K&&!ei(E.version,K)&&X.warn(`Homebridge requires Node.js version of ${K} which does not satisfy the current Node.js version of ${E.version}. You may need to upgrade your installation of Node.js - see https://homebridge.io/w/JTKEF`);function ii(){let o=!1,e=!1,i=!1,r,t=!1,s=!1,n=!1,c=!1,h,d=!1;new Xe().version(v()).option("-C, --color","force color in logging",()=>c=!0).option("-D, --debug","turn on debug level logging",()=>n=!0).option("-I, --insecure","allow unauthenticated requests (for easier hacking)",()=>o=!0).option("-P, --plugin-path [path]","look for plugins installed at [path] as well as the default locations ([path] can also point to a single plugin)",C=>r=C).option("-Q, --no-qrcode","do not issue QRcode in logging",()=>e=!0).option("-K, --keep-orphans","keep cached accessories for which the associated plugin is not loaded",()=>i=!0).option("-T, --no-timestamp","do not issue timestamps in logging",()=>s=!0).option("-U, --user-storage-path [path]","look for homebridge user files at [path] instead of the default location (~/.homebridge)",C=>h=C).option("--strict-plugin-resolution","only load plugins from the --plugin-path if set, otherwise from the primary global node_modules",()=>t=!0).parse(E.argv),s&&g.setTimestampEnabled(!1),n&&g.setDebugEnabled(!0),c&&g.forceColor(),h&&m.setStoragePath(h),Ze.setCustomStoragePath(m.persistPath());let p={keepOrphanedCachedAccessories:i,insecureAccess:o,hideQRCode:e,customPluginPath:r,noLogTimestamps:s,debugModeEnabled:n,forceColourLogging:c,customStoragePath:h,strictPluginResolution:t},T=new q(p),Z=(C,ge)=>{d||(d=!0,X.info("Got %s, shutting down Homebridge...",C),setTimeout(()=>E.exit(128+ge),5e3),T.teardown())};E.on("SIGINT",Z.bind(void 0,"SIGINT",2)),E.on("SIGTERM",Z.bind(void 0,"SIGTERM",15));let ee=C=>{C.stack&&X.error(C.stack),d||E.kill(E.pid,"SIGTERM")};E.on("uncaughtException",ee),T.start().catch(ee)}export{ii as default};
|
|
2
|
-
|
|
1
|
+
/* global NodeJS */
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { HAPStorage } from 'hap-nodejs';
|
|
5
|
+
import { satisfies } from 'semver';
|
|
6
|
+
import 'source-map-support/register.js';
|
|
7
|
+
import { Logger } from './logger.js';
|
|
8
|
+
import { Server } from './server.js';
|
|
9
|
+
import { User } from './user.js';
|
|
10
|
+
import getVersion, { getRequiredNodeVersion } from './version.js';
|
|
11
|
+
const log = Logger.internal;
|
|
12
|
+
const requiredNodeVersion = getRequiredNodeVersion();
|
|
13
|
+
if (requiredNodeVersion && !satisfies(process.version, requiredNodeVersion)) {
|
|
14
|
+
log.warn(`Homebridge requires Node.js version of ${requiredNodeVersion} which does \
|
|
15
|
+
not satisfy the current Node.js version of ${process.version}. You may need to upgrade your installation of Node.js - see https://homebridge.io/w/JTKEF`);
|
|
16
|
+
}
|
|
17
|
+
export default function cli() {
|
|
18
|
+
let insecureAccess = false;
|
|
19
|
+
let hideQRCode = false;
|
|
20
|
+
let keepOrphans = false;
|
|
21
|
+
let customPluginPath;
|
|
22
|
+
let strictPluginResolution = false;
|
|
23
|
+
let noLogTimestamps = false;
|
|
24
|
+
let debugModeEnabled = false;
|
|
25
|
+
let forceColourLogging = false;
|
|
26
|
+
let customStoragePath;
|
|
27
|
+
let shuttingDown = false;
|
|
28
|
+
const program = new Command();
|
|
29
|
+
program
|
|
30
|
+
.version(getVersion())
|
|
31
|
+
.option('-C, --color', 'force color in logging', () => forceColourLogging = true)
|
|
32
|
+
.option('-D, --debug', 'turn on debug level logging', () => debugModeEnabled = true)
|
|
33
|
+
.option('-I, --insecure', 'allow unauthenticated requests (for easier hacking)', () => insecureAccess = true)
|
|
34
|
+
.option('-P, --plugin-path [path]', 'look for plugins installed at [path] as well as the default locations ([path] can also point to a single plugin)', path => customPluginPath = path)
|
|
35
|
+
.option('-Q, --no-qrcode', 'do not issue QRcode in logging', () => hideQRCode = true)
|
|
36
|
+
.option('-K, --keep-orphans', 'keep cached accessories for which the associated plugin is not loaded', () => keepOrphans = true)
|
|
37
|
+
.option('-T, --no-timestamp', 'do not issue timestamps in logging', () => noLogTimestamps = true)
|
|
38
|
+
.option('-U, --user-storage-path [path]', 'look for homebridge user files at [path] instead of the default location (~/.homebridge)', path => customStoragePath = path)
|
|
39
|
+
.option('--strict-plugin-resolution', 'only load plugins from the --plugin-path if set, otherwise from the primary global node_modules', () => strictPluginResolution = true)
|
|
40
|
+
.parse(process.argv);
|
|
41
|
+
if (noLogTimestamps) {
|
|
42
|
+
Logger.setTimestampEnabled(false);
|
|
43
|
+
}
|
|
44
|
+
if (debugModeEnabled) {
|
|
45
|
+
Logger.setDebugEnabled(true);
|
|
46
|
+
}
|
|
47
|
+
if (forceColourLogging) {
|
|
48
|
+
Logger.forceColor();
|
|
49
|
+
}
|
|
50
|
+
if (customStoragePath) {
|
|
51
|
+
User.setStoragePath(customStoragePath);
|
|
52
|
+
}
|
|
53
|
+
// Initialize HAP-NodeJS with a custom persist directory
|
|
54
|
+
HAPStorage.setCustomStoragePath(User.persistPath());
|
|
55
|
+
const options = {
|
|
56
|
+
keepOrphanedCachedAccessories: keepOrphans,
|
|
57
|
+
insecureAccess,
|
|
58
|
+
hideQRCode,
|
|
59
|
+
customPluginPath,
|
|
60
|
+
noLogTimestamps,
|
|
61
|
+
debugModeEnabled,
|
|
62
|
+
forceColourLogging,
|
|
63
|
+
customStoragePath,
|
|
64
|
+
strictPluginResolution,
|
|
65
|
+
};
|
|
66
|
+
const server = new Server(options);
|
|
67
|
+
const signalHandler = (signal, signalNum) => {
|
|
68
|
+
if (shuttingDown) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
shuttingDown = true;
|
|
72
|
+
log.info('Got %s, shutting down Homebridge...', signal);
|
|
73
|
+
setTimeout(() => process.exit(128 + signalNum), 5000);
|
|
74
|
+
server.teardown();
|
|
75
|
+
};
|
|
76
|
+
process.on('SIGINT', signalHandler.bind(undefined, 'SIGINT', 2));
|
|
77
|
+
process.on('SIGTERM', signalHandler.bind(undefined, 'SIGTERM', 15));
|
|
78
|
+
const errorHandler = (error) => {
|
|
79
|
+
if (error.stack) {
|
|
80
|
+
log.error(error.stack);
|
|
81
|
+
}
|
|
82
|
+
if (!shuttingDown) {
|
|
83
|
+
process.kill(process.pid, 'SIGTERM');
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
process.on('uncaughtException', errorHandler);
|
|
87
|
+
server.start().catch(errorHandler);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=cli.js.map
|