homebridge 2.0.0-alpha.2 → 2.0.0-alpha.4

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.
Files changed (79) hide show
  1. package/README.md +16 -17
  2. package/bin/homebridge +8 -6
  3. package/config-sample.json +3 -3
  4. package/dist/childBridgeFork.js +2 -0
  5. package/dist/childBridgeFork.js.map +7 -0
  6. package/dist/cli.js +2 -0
  7. package/dist/cli.js.map +7 -0
  8. package/dist/index.d.ts +1099 -0
  9. package/dist/index.js +2 -0
  10. package/dist/index.js.map +7 -0
  11. package/package.json +50 -47
  12. package/lib/api.d.ts +0 -210
  13. package/lib/api.d.ts.map +0 -1
  14. package/lib/api.js +0 -155
  15. package/lib/api.js.map +0 -1
  16. package/lib/bridgeService.d.ts +0 -105
  17. package/lib/bridgeService.d.ts.map +0 -1
  18. package/lib/bridgeService.js +0 -428
  19. package/lib/bridgeService.js.map +0 -1
  20. package/lib/childBridgeFork.d.ts +0 -37
  21. package/lib/childBridgeFork.d.ts.map +0 -1
  22. package/lib/childBridgeFork.js +0 -244
  23. package/lib/childBridgeFork.js.map +0 -1
  24. package/lib/childBridgeService.d.ts +0 -199
  25. package/lib/childBridgeService.d.ts.map +0 -1
  26. package/lib/childBridgeService.js +0 -428
  27. package/lib/childBridgeService.js.map +0 -1
  28. package/lib/cli.d.ts +0 -4
  29. package/lib/cli.d.ts.map +0 -1
  30. package/lib/cli.js +0 -115
  31. package/lib/cli.js.map +0 -1
  32. package/lib/externalPortService.d.ts +0 -33
  33. package/lib/externalPortService.d.ts.map +0 -1
  34. package/lib/externalPortService.js +0 -64
  35. package/lib/externalPortService.js.map +0 -1
  36. package/lib/index.d.ts +0 -76
  37. package/lib/index.d.ts.map +0 -1
  38. package/lib/index.js +0 -72
  39. package/lib/index.js.map +0 -1
  40. package/lib/ipcService.d.ts +0 -33
  41. package/lib/ipcService.d.ts.map +0 -1
  42. package/lib/ipcService.js +0 -49
  43. package/lib/ipcService.js.map +0 -1
  44. package/lib/logger.d.ts +0 -78
  45. package/lib/logger.d.ts.map +0 -1
  46. package/lib/logger.js +0 -147
  47. package/lib/logger.js.map +0 -1
  48. package/lib/platformAccessory.d.ts +0 -54
  49. package/lib/platformAccessory.d.ts.map +0 -1
  50. package/lib/platformAccessory.js +0 -102
  51. package/lib/platformAccessory.js.map +0 -1
  52. package/lib/plugin.d.ts +0 -31
  53. package/lib/plugin.d.ts.map +0 -1
  54. package/lib/plugin.js +0 -194
  55. package/lib/plugin.js.map +0 -1
  56. package/lib/pluginManager.d.ts +0 -77
  57. package/lib/pluginManager.d.ts.map +0 -1
  58. package/lib/pluginManager.js +0 -380
  59. package/lib/pluginManager.js.map +0 -1
  60. package/lib/server.d.ts +0 -58
  61. package/lib/server.d.ts.map +0 -1
  62. package/lib/server.js +0 -457
  63. package/lib/server.js.map +0 -1
  64. package/lib/storageService.d.ts +0 -13
  65. package/lib/storageService.d.ts.map +0 -1
  66. package/lib/storageService.js +0 -70
  67. package/lib/storageService.js.map +0 -1
  68. package/lib/user.d.ts +0 -13
  69. package/lib/user.d.ts.map +0 -1
  70. package/lib/user.js +0 -36
  71. package/lib/user.js.map +0 -1
  72. package/lib/util/mac.d.ts +0 -4
  73. package/lib/util/mac.d.ts.map +0 -1
  74. package/lib/util/mac.js +0 -20
  75. package/lib/util/mac.js.map +0 -1
  76. package/lib/version.d.ts +0 -3
  77. package/lib/version.d.ts.map +0 -1
  78. package/lib/version.js +0 -21
  79. package/lib/version.js.map +0 -1
package/README.md CHANGED
@@ -16,19 +16,19 @@
16
16
 
17
17
  <img src="https://media.giphy.com/media/10l79ICohTu4iQ/giphy.gif" align="right" alt="Unlocking Door">
18
18
 
19
- **Homebridge** is a lightweight Node.js server you can run on your home network that emulates the iOS HomeKit API. It supports Plugins, which are community-contributed modules that provide a basic bridge from HomeKit to various 3rd-party APIs provided by manufacturers of "smart home" devices.
19
+ **Homebridge** is a lightweight Node.js server you can run on your home network that emulates the iOS HomeKit API. It supports Plugins, which are community-contributed modules that provide a basic bridge from HomeKit to various 3rd-party APIs provided by manufacturers of "smart home" devices.
20
20
 
21
21
  Since Siri supports devices added through HomeKit, this means that with Homebridge you can ask Siri to control devices that don't have any support for HomeKit at all. For instance, using just some of the available plugins, you can say:
22
22
 
23
- * _Siri, unlock the back door._ [pictured to the right]
24
- * _Siri, open the garage door._
25
- * _Siri, turn on the coffee maker._
26
- * _Siri, turn on the living room lights._
27
- * _Siri, good morning!_
23
+ - _Siri, unlock the back door._ [pictured to the right]
24
+ - _Siri, open the garage door._
25
+ - _Siri, turn on the coffee maker._
26
+ - _Siri, turn on the living room lights._
27
+ - _Siri, good morning!_
28
28
 
29
29
  You can explore all available plugins at the NPM website by [searching for the keyword `homebridge-plugin`](https://www.npmjs.com/search?q=homebridge-plugin).
30
30
 
31
- ## Community
31
+ ## Community
32
32
 
33
33
  The official Homebridge Discord server and Reddit community are where users can discuss Homebridge and ask for help.
34
34
 
@@ -79,7 +79,7 @@ HomeKit communities can also be found on both [Discord](https://discord.gg/RcV7f
79
79
 
80
80
  ### Docker
81
81
 
82
- [Install Homebridge on Docker](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-Docker) <br> [Synology](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-Synology) | [Unraid](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-Unraid) | [QNAP](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-QNAP) | [TrueNAS Scale](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-TrueNAS-Scale)
82
+ [Install Homebridge on Docker](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-Docker) <br> [Synology](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-Synology) | [Unraid](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-Unraid) | [QNAP](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-QNAP) | [TrueNAS Scale](https://github.com/homebridge/docker-homebridge/wiki/Homebridge-on-TrueNAS-Scale)
83
83
 
84
84
  ---
85
85
 
@@ -97,9 +97,9 @@ HomeKit communities can also be found on both [Discord](https://discord.gg/RcV7f
97
97
 
98
98
  1. Open the Home <img src="https://user-images.githubusercontent.com/3979615/78010622-4ea1d380-738e-11ea-8a17-e6a465eeec35.png" height="16.42px"> app on your device.
99
99
  2. Tap the Home tab, then tap <img src="https://user-images.githubusercontent.com/3979615/78010869-9aed1380-738e-11ea-9644-9f46b3633026.png" height="16.42px">.
100
- 3. Tap *Add Accessory*, then scan the QR code shown in the Homebridge UI or your Homebridge logs.
100
+ 3. Tap _Add Accessory_, then scan the QR code shown in the Homebridge UI or your Homebridge logs.
101
101
 
102
- If the bridge does not have any accessories yet, you may receive a message saying *Additional Set-up Required*, this is ok, as you add plugins they will show up in the Home app without the need to pair again (except for Cameras and TVs).
102
+ If the bridge does not have any accessories yet, you may receive a message saying _Additional Set-up Required_, this is ok, as you add plugins they will show up in the Home app without the need to pair again (except for Cameras and TVs).
103
103
 
104
104
  Cameras and most TV devices are exposed as separate accessories and each needs to be paired separately. See [this wiki article](https://github.com/homebridge/homebridge/wiki/Connecting-Homebridge-To-HomeKit#how-to-add-homebridge-cameras--tvs) for instructions.
105
105
 
@@ -113,18 +113,17 @@ One final thing to remember is that Siri will almost always prefer its default p
113
113
 
114
114
  The https://developers.homebridge.io website contains the Homebridge API reference, available service and characteristic types, and plugin examples.
115
115
 
116
- The [Homebridge Plugin Template](https://github.com/homebridge/homebridge-plugin-template) project provides a base you can use to create your own *platform* plugin.
116
+ The [Homebridge Plugin Template](https://github.com/homebridge/homebridge-plugin-template) project provides a base you can use to create your own _platform_ plugin.
117
117
 
118
118
  There are many existing plugins you can study; you might start with the [Homebridge Example Plugins](https://github.com/homebridge/homebridge-examples) or a plugin that already implements the device type you need.
119
119
 
120
120
  When writing your plugin, you'll want Homebridge to load it from your development directory instead of publishing it to `npm` each time. Run this command inside your plugin project folder so your global installation of Homebridge can discover it:
121
121
 
122
-
123
122
  ```shell
124
123
  npm link
125
124
  ```
126
125
 
127
- *You can undo this using the `npm unlink` command.*
126
+ _You can undo this using the `npm unlink` command._
128
127
 
129
128
  Then start Homebridge in debug mode:
130
129
 
@@ -150,13 +149,13 @@ To fix this, [Reset Homebridge](https://github.com/homebridge/homebridge/wiki/Co
150
149
 
151
150
  Try the following:
152
151
 
153
- 1. Swap between the `Bonjour HAP` and `Ciao` mDNS Advertiser options. See [the wiki](https://github.com/homebridge/homebridge/wiki/mDNS-Options) for more details.
154
- 2. iOS DNS cache has gone stale or gotten misconfigured. To fix this, turn airplane mode on and back off to flush the DNS cache.
152
+ 1. Swap between the `Bonjour HAP` and `Ciao` mDNS Advertiser options. See [the wiki](https://github.com/homebridge/homebridge/wiki/mDNS-Options) for more details.
153
+ 2. iOS DNS cache has gone stale or gotten misconfigured. To fix this, turn airplane mode on and back off to flush the DNS cache.
155
154
 
156
155
  ### Limitations
157
156
 
158
- * One bridge can only expose 150 accessories due to a HomeKit limit. You can however run your plugins as a [Child Bridge](https://github.com/homebridge/homebridge/wiki/Child-Bridges) or run [Multiple Homebridge Instances](https://github.com/homebridge/homebridge-config-ui-x/wiki/Homebridge-Service-Command#multiple-instances) to get around this limitation.
159
- * Once an accessory has been added to the Home app, changing its name via Homebridge won't be automatically reflected in iOS. You must change it via the Home app as well.
157
+ - One bridge can only expose 150 accessories due to a HomeKit limit. You can however run your plugins as a [Child Bridge](https://github.com/homebridge/homebridge/wiki/Child-Bridges) or run [Multiple Homebridge Instances](https://github.com/homebridge/homebridge-config-ui-x/wiki/Homebridge-Service-Command#multiple-instances) to get around this limitation.
158
+ - Once an accessory has been added to the Home app, changing its name via Homebridge won't be automatically reflected in iOS. You must change it via the Home app as well.
160
159
 
161
160
  ## Why Homebridge?
162
161
 
package/bin/homebridge CHANGED
@@ -4,14 +4,16 @@
4
4
  // This executable sets up the environment and runs the HomeBridge CLI.
5
5
  //
6
6
 
7
- "use strict";
7
+ import { realpathSync } from 'node:fs'
8
+ import { dirname, join } from 'node:path'
9
+ import process from 'node:process'
10
+ import { fileURLToPath } from 'node:url'
8
11
 
9
- process.title = "homebridge";
12
+ process.title = 'homebridge'
10
13
 
11
14
  // Find the HomeBridge lib
12
- const path = require("path");
13
- const fs = require("fs");
14
- const lib = path.join(path.dirname(fs.realpathSync(__filename)), "../lib");
15
+ const __filename = fileURLToPath(import.meta.url)
16
+ const lib = join(dirname(realpathSync(__filename)), '../dist')
15
17
 
16
18
  // Run HomeBridge
17
- require(lib + '/cli')();
19
+ import(`${lib}/cli.js`).then(module => module.default())
@@ -7,7 +7,7 @@
7
7
  "port": 51826,
8
8
  "pin": "031-45-154"
9
9
  },
10
-
10
+
11
11
  "description": "This is an example configuration file with one fake accessory and one fake platform. You can use this as a template for creating your own configuration file containing devices you actually own.",
12
12
  "ports": {
13
13
  "start": 52100,
@@ -23,8 +23,8 @@
23
23
 
24
24
  "platforms": [
25
25
  {
26
- "platform" : "PhilipsHue",
27
- "name" : "Hue"
26
+ "platform": "PhilipsHue",
27
+ "name": "Hue"
28
28
  }
29
29
  ]
30
30
  }
@@ -0,0 +1,2 @@
1
+ import y from"node:process";import{AccessoryEventTypes as G,HAPStorage as De}from"hap-nodejs";import{EventEmitter as Pe}from"node:events";import Y from"hap-nodejs";import ye from"semver";import K from"node:util";import S from"chalk";var c=class s{static internal=new s;static loggerCache=new Map;static debugEnabled=!1;static timestampEnabled=!0;prefix;constructor(e){this.prefix=e}static withPrefix(e){let i=s.loggerCache.get(e);if(i)return i;{let t=new s(e),r=t.info.bind(t);r.info=t.info,r.success=t.success,r.warn=t.warn,r.error=t.error,r.debug=t.debug,r.log=t.log,r.prefix=t.prefix;let n=r;return s.loggerCache.set(e,n),n}}static setDebugEnabled(e=!0){s.debugEnabled=e}static setTimestampEnabled(e=!0){s.timestampEnabled=e}static forceColor(){S.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,...t){if(e==="debug"&&!s.debugEnabled)return;i=K.format(i,...t);let r=console.log;switch(e){case"success":i=S.green(i);break;case"warn":i=S.yellow(i),r=console.error;break;case"error":i=S.red(i),r=console.error;break;case"debug":i=S.gray(i);break}if(this.prefix&&(i=`${p(this.prefix)} ${i}`),s.timestampEnabled){let n=new Date;i=S.white(`[${n.toLocaleString()}] `)+i}r(i)}};function p(s){return S.cyan(`[${s}]`)}import{EventEmitter as Z}from"node:events";import{Accessory as k,AccessoryEventTypes as ee,Categories as ie}from"hap-nodejs";var m=class s extends Z{static injectedAccessory;_associatedPlugin;_associatedPlatform;_associatedHAPAccessory;displayName;UUID;category;services=[];context={};constructor(e,i,t){super(),this._associatedHAPAccessory=s.injectedAccessory?s.injectedAccessory:new k(e,i),t&&(this._associatedHAPAccessory.category=t),this.displayName=this._associatedHAPAccessory.displayName,this.UUID=this._associatedHAPAccessory.UUID,this.category=t||ie.OTHER,this.services=this._associatedHAPAccessory.services,this._associatedHAPAccessory.on(ee.IDENTIFY,(r,n)=>{this.emit("identify",r,()=>{}),n()})}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,...k.serialize(e._associatedHAPAccessory)}}static deserialize(e){let i=k.deserialize(e);s.injectedAccessory=i;let t=new s(i.displayName,i.UUID);return s.injectedAccessory=void 0,t._associatedPlugin=e.plugin,t._associatedPlatform=e.platform,t.context=e.context,t.category=e.category,t}};import{execSync as ue}from"node:child_process";import C from"node:fs";import{createRequire as he}from"node:module";import f from"node:path";import R from"node:process";import le from"node:assert";import de from"node:path";import z from"node:process";import{pathToFileURL as ge}from"node:url";import{satisfies as W}from"semver";import te from"node:fs";import{dirname as re,join as se}from"node:path";import{fileURLToPath as ne}from"node:url";var oe=ne(import.meta.url),ae=re(oe);function ce(){let s=se(ae,"../package.json");return JSON.parse(te.readFileSync(s,{encoding:"utf8"}))}function A(){return ce().version}var N=c.internal,L=class{pluginName;scope;pluginPath;isESM;disabled=!1;version;main;loadContext;pluginInitializer;registeredAccessories=new Map;registeredPlatforms=new Map;activeDynamicPlatforms=new Map;constructor(e,i,t,r){if(this.pluginName=e,this.scope=r,this.pluginPath=i,this.version=t.version||"0.0.0",this.main="",t.exports)if(typeof t.exports=="string")this.main=t.exports;else{let n=t.exports.import||t.exports.require||t.exports.node||t.exports.default||t.exports["."];typeof n!="string"?n.import?this.main=n.import:this.main=n.require||n.node||n.default:this.main=n}this.main||(this.main=t.main||"./index.js"),this.isESM=this.main.endsWith(".mjs")||this.main.endsWith(".js")&&t.type==="module",t.peerDependencies&&(!t.engines||!t.engines.homebridge)&&(t.engines=t.engines||{},t.engines.homebridge=t.peerDependencies.homebridge),this.loadContext={engines:t.engines,dependencies:t.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||N.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||N.info("Registering platform '%s'",`${this.getPluginIdentifier()}.${e}`),this.registeredPlatforms.set(e,i)}getAccessoryConstructor(e){let i=u.getAccessoryName(e),t=this.registeredAccessories.get(i);if(!t)throw new Error(`The requested accessory '${i}' was not registered by the plugin '${this.getPluginIdentifier()}'.`);return t}getPlatformConstructor(e){let i=u.getPlatformName(e),t=this.registeredPlatforms.get(i);if(!t)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 t}assignDynamicPlatform(e,i){let t=u.getPlatformName(e),r=this.activeDynamicPlatforms.get(t);r||(r=[],this.activeDynamicPlatforms.set(t,r)),r.unshift(i)}getActiveDynamicPlatform(e){let i=this.activeDynamicPlatforms.get(e);return i&&i[0]}async load(){let e=this.loadContext;if(le(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,t=e.engines.node;W(A(),i,{includePrerelease:!0})||N.error(`The plugin "${this.pluginName}" requires a Homebridge version of ${i} which does not satisfy the current Homebridge version of ${A()}. You may need to update this plugin (or Homebridge) to a newer version. You may face unexpected issues or stability problems running this plugin.`),t&&!W(z.version,t)&&N.warn(`The plugin "${this.pluginName}" requires Node.js version of ${t} which does not satisfy the current Node.js version of ${z.version}. You may need to upgrade your installation of Node.js - see https://homebridge.io/w/JTKEF`);let r=e.dependencies||{};(r.homebridge||r["hap-nodejs"])&&N.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 n=de.join(this.pluginPath,this.main),o=await import(ge(n).href);if(typeof o=="function")this.pluginInitializer=o;else if(o&&typeof o.default=="function")this.pluginInitializer=o.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 l=c.internal,pe=he(import.meta.url),q=pe.resolve.paths(""),u=class s{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(f.resolve(R.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 s.PLUGIN_IDENTIFIER_PATTERN.test(e)}static extractPluginName(e){return e.match(s.PLUGIN_IDENTIFIER_PATTERN)[3]}static extractPluginScope(e){return e.match(s.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(){l.info("---"),this.loadInstalledPlugins();for(let[e,i]of this.plugins){try{await i.load()}catch(t){l.error("===================="),l.error(`ERROR LOADING PLUGIN ${e}:`),l.error(t.stack),l.error("===================="),this.plugins.delete(e);continue}this.disabledPlugins&&this.disabledPlugins.includes(i.getPluginIdentifier())&&(i.disabled=!0),i.disabled?l.warn(`Disabled plugin: ${e}@${i.version}`):l.info(`Loaded plugin: ${e}@${i.version}`),await this.initializePlugin(i,e),l.info("---")}this.currentInitializingPlugin=void 0}async initializePlugin(e,i){try{this.currentInitializingPlugin=e,await e.initialize(this.api)}catch(t){l.error("===================="),l.error(`ERROR INITIALIZING PLUGIN ${i}:`),l.error(t.stack),l.error("===================="),this.plugins.delete(i)}}handleRegisterAccessory(e,i,t){if(!this.currentInitializingPlugin)throw new Error(`Unexpected accessory registration. Plugin ${t?`'${t}' `:""}tried to register outside the initializer function!`);t&&t!==this.currentInitializingPlugin.getPluginIdentifier()&&(l.info(`Plugin '${this.currentInitializingPlugin.getPluginIdentifier()}' tried to register with an incorrect plugin identifier: '${t}'. Please report this to the developer!`),this.pluginIdentifierTranslation.set(t,this.currentInitializingPlugin.getPluginIdentifier())),this.currentInitializingPlugin.registerAccessory(e,i);let r=this.accessoryToPluginMap.get(e);r||(r=[],this.accessoryToPluginMap.set(e,r)),r.push(this.currentInitializingPlugin)}handleRegisterPlatform(e,i,t){if(!this.currentInitializingPlugin)throw new Error(`Unexpected platform registration. Plugin ${t?`'${t}' `:""}tried to register outside the initializer function!`);t&&t!==this.currentInitializingPlugin.getPluginIdentifier()&&(l.debug(`Plugin '${this.currentInitializingPlugin.getPluginIdentifier()}' tried to register with an incorrect plugin identifier: '${t}'. Please report this to the developer!`),this.pluginIdentifierTranslation.set(t,this.currentInitializingPlugin.getPluginIdentifier())),this.currentInitializingPlugin.registerPlatform(e,i);let r=this.platformToPluginMap.get(e);r||(r=[],this.platformToPluginMap.set(e,r)),r.push(this.currentInitializingPlugin)}getPluginForAccessory(e){let i;if(e.includes(".")){let t=s.getPluginIdentifier(e);if(!this.hasPluginRegistered(t))throw new Error(`The requested plugin '${t}' was not registered.`);i=this.getPlugin(t)}else{let t=this.accessoryToPluginMap.get(e);if(!t)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(t.length>1){let r=t.map(n=>`${n.getPluginIdentifier()}.${e}`).join(", ");if(t=t.filter(n=>!n.disabled),t.length!==1)throw new Error(`The requested accessory '${e}' has been registered multiple times. Please be more specific by writing one of: ${r}`)}i=t[0],e=`${i.getPluginIdentifier()}.${e}`}return i}getPluginForPlatform(e){let i;if(e.includes(".")){let t=s.getPluginIdentifier(e);if(!this.hasPluginRegistered(t))throw new Error(`The requested plugin '${t}' was not registered.`);i=this.getPlugin(t)}else{let t=this.platformToPluginMap.get(e);if(!t)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(t.length>1){let r=t.map(n=>`${n.getPluginIdentifier()}.${e}`).join(", ");if(t=t.filter(n=>!n.disabled),t.length!==1)throw new Error(`The requested platform '${e}' has been registered multiple times. Please be more specific by writing one of: ${r}`)}i=t[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 t=this.pluginIdentifierTranslation.get(e);if(t)return this.plugins.get(t)}}getPluginByActiveDynamicPlatform(e){let i=(this.platformToPluginMap.get(e)||[]).filter(t=>!!t.getActiveDynamicPlatform(e));if(i.length!==0)if(i.length>1){let t=i.map(r=>r.getPluginIdentifier()).join(", ");throw new Error(`'${e}' is an ambiguous platform name. It was registered by multiple plugins: ${t}`)}else return i[0]}loadInstalledPlugins(){this.loadDefaultPaths(),this.searchPaths.forEach(e=>{if(C.existsSync(e))if(C.existsSync(f.join(e,"package.json")))try{this.loadPlugin(e)}catch(i){l.warn(i.message)}else{let i=C.readdirSync(e).filter(t=>{try{return C.statSync(f.resolve(e,t)).isDirectory()}catch(r){return l.debug(`Ignoring path ${f.resolve(e,t)} - ${r.message}`),!1}});i.slice().filter(t=>t.charAt(0)==="@").forEach(t=>{let r=i.indexOf(t);i.splice(r,1);let n=f.join(e,t);C.readdirSync(n).filter(o=>s.isQualifiedPluginIdentifier(o)).filter(o=>{try{return C.statSync(f.resolve(n,o)).isDirectory()}catch(d){return l.debug(`Ignoring path ${f.resolve(n,o)} - ${d.message}`),!1}}).forEach(o=>i.push(`${t}/${o}`))}),i.filter(t=>s.isQualifiedPluginIdentifier(t)&&(!this.activePlugins||this.activePlugins.includes(t))).forEach(t=>{try{let r=f.resolve(e,t);this.loadPlugin(r)}catch(r){l.warn(r.message)}})}}),this.plugins.size===0&&l.warn("No plugins found.")}loadPlugin(e){let i=s.loadPackageJSON(e),t=i.name,r=s.extractPluginName(t),n=s.extractPluginScope(t),o=this.plugins.get(t);if(o)throw new Error(`Warning: skipping plugin found at '${e}' since we already loaded the same plugin from '${o.getPluginPath()}'.`);let d=new L(r,e,i,n);return this.plugins.set(t,d),d}static loadPackageJSON(e){let i=f.join(e,"package.json"),t;if(!C.existsSync(i))throw new Error(`Plugin ${e} does not contain a package.json.`);try{t=JSON.parse(C.readFileSync(i,{encoding:"utf8"}))}catch(r){throw new Error(`Plugin ${e} contains an invalid package.json. Error: ${r}`)}if(!t.name||!s.isQualifiedPluginIdentifier(t.name))throw new Error(`Plugin ${e} does not have a package name that begins with 'homebridge-' or '@scope/homebridge-.`);if(!t.keywords||!t.keywords.includes("homebridge-plugin"))throw new Error(`Plugin ${e} package.json does not contain the keyword 'homebridge-plugin'.`);return t}loadDefaultPaths(){if(this.strictPluginResolution){this.searchPaths.size===0&&this.addNpmPrefixToSearchPaths();return}q&&q.forEach(e=>this.searchPaths.add(e)),R.env.NODE_PATH?R.env.NODE_PATH.split(f.delimiter).filter(e=>!!e).forEach(e=>this.searchPaths.add(e)):(R.platform!=="win32"&&(this.searchPaths.add("/usr/local/lib/node_modules"),this.searchPaths.add("/usr/lib/node_modules")),this.addNpmPrefixToSearchPaths())}addNpmPrefixToSearchPaths(){R.platform==="win32"?this.searchPaths.add(f.join(R.env.APPDATA,"npm/node_modules")):this.searchPaths.add(ue('/bin/echo -n "$(npm -g prefix)/lib/node_modules"',{env:Object.assign({npm_config_loglevel:"silent",npm_update_notifier:"false"},R.env)}).toString("utf8"))}};import me from"node:os";import D from"node:path";var I=class s{static customStoragePath;static storageAccessed=!1;static configPath(){return D.join(s.storagePath(),"config.json")}static persistPath(){return D.join(s.storagePath(),"persist")}static cachedAccessoryPath(){return D.join(s.storagePath(),"accessories")}static storagePath(){return s.storageAccessed=!0,s.customStoragePath?s.customStoragePath:D.join(me.homedir(),".homebridge")}static setStoragePath(...e){if(s.storageAccessed)throw new Error("Storage path was already accessed and cannot be changed anymore. Try initializing your custom storage path earlier!");s.customStoragePath=D.resolve(...e)}};var Ae=c.internal;var x=class extends Pe{version=2.7;serverVersion=A();user=I;hap=Y;hapLegacyTypes=Y.LegacyTypes;platformAccessory=m;constructor(){super()}versionGreaterOrEqual(e){return ye.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,t){typeof i=="function"?(t=i,i=e,this.emit("registerAccessory",i,t)):this.emit("registerAccessory",i,t,e)}registerPlatform(e,i,t){typeof i=="function"?(t=i,i=e,this.emit("registerPlatform",i,t)):this.emit("registerPlatform",i,t,e)}publishCameraAccessories(e,i){this.publishExternalAccessories(e,i)}publishExternalAccessories(e,i){u.isQualifiedPluginIdentifier(e)||Ae.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(t=>{if(!(t instanceof m))throw new TypeError(`${e} attempt to register an accessory that isn't PlatformAccessory!`);t._associatedPlugin=e}),this.emit("publishExternalAccessories",i)}registerPlatformAccessories(e,i,t){t.forEach(r=>{if(!(r instanceof m))throw new TypeError(`${e} - ${i} attempt to register an accessory that isn't PlatformAccessory!`);r._associatedPlugin=e,r._associatedPlatform=i}),this.emit("registerPlatformAccessories",t)}updatePlatformAccessories(e){this.emit("updatePlatformAccessories",e)}unregisterPlatformAccessories(e,i,t){t.forEach(r=>{if(!(r instanceof m))throw new TypeError(`${e} - ${i} attempt to unregister an accessory that isn't PlatformAccessory!`)}),this.emit("unregisterPlatformAccessories",t)}};import{Accessory as be,AccessoryEventTypes as E,Bridge as Ce,Categories as Ie,Characteristic as T,CharacteristicEventTypes as Ee,CharacteristicWarningType as w,HAPLibraryVersion as Se,once as Re,Service as $,uuid as Q}from"hap-nodejs";import v from"node:path";import P from"fs-extra";var M=class{constructor(e){this.baseDirectory=e}initSync(){return P.ensureDirSync(this.baseDirectory)}getItemSync(e){let i=v.resolve(this.baseDirectory,e);return P.pathExistsSync(i)?P.readJsonSync(i):null}async getItem(e){let i=v.resolve(this.baseDirectory,e);return await P.pathExists(i)?await P.readJson(i):null}setItemSync(e,i){return P.writeJsonSync(v.resolve(this.baseDirectory,e),i)}setItem(e,i){return P.writeJson(v.resolve(this.baseDirectory,e),i)}copyItem(e,i){return P.copyFile(v.resolve(this.baseDirectory,e),v.resolve(this.baseDirectory,i))}copyItemSync(e,i){return P.copyFileSync(v.resolve(this.baseDirectory,e),v.resolve(this.baseDirectory,i))}removeItemSync(e){return P.removeSync(v.resolve(this.baseDirectory,e))}};import ve from"node:crypto";function V(s){let e=ve.createHash("sha1");e.update(s);let i=e.digest("hex"),t=0;return"xx:xx:xx:xx:xx:xx".replace(/x/g,()=>i[t++]).toUpperCase()}var a=c.internal,U=class s{constructor(e,i,t,r,n,o){this.api=e;this.pluginManager=i;this.externalPortService=t;this.bridgeOptions=r;this.bridgeConfig=n;this.config=o;this.storageService=new M(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 Ce(n.name,Q.generate("HomeBridge")),this.bridge.on(E.CHARACTERISTIC_WARNING,()=>{})}bridge;storageService;allowInsecureAccess;cachedPlatformAccessories=[];cachedAccessoriesFileLoaded=!1;publishedExternalAccessories=new Map;static printCharacteristicWriteWarning(e,i,t,r){let n="See https://homebridge.io/w/JtMGR for more info.";switch(r.type){case w.SLOW_READ:case w.SLOW_WRITE:t.ignoreSlow||a.info(p(e.getPluginIdentifier()),"This plugin slows down Homebridge.",r.message,n);break;case w.TIMEOUT_READ:case w.TIMEOUT_WRITE:a.error(p(e.getPluginIdentifier()),"This plugin slows down Homebridge.",r.message,n);break;case w.WARN_MESSAGE:a.info(p(e.getPluginIdentifier()),`This plugin generated a warning from the characteristic '${r.characteristic.displayName}':`,`${r.message}.`,n);break;case w.ERROR_MESSAGE:a.error(p(e.getPluginIdentifier()),`This plugin threw an error from the characteristic '${r.characteristic.displayName}':`,`${r.message}.`,n);break;case w.DEBUG_MESSAGE:a.debug(p(e.getPluginIdentifier()),`Characteristic '${r.characteristic.displayName}':`,`${r.message}.`,n);break;default:a.info(p(e.getPluginIdentifier()),`This plugin generated a warning from the characteristic '${r.characteristic.displayName}':`,`${r.message}.`,n);break}r.stack&&a.debug(p(e.getPluginIdentifier()),r.stack)}publishBridge(){let e=this.bridgeConfig,i=this.bridge.getService($.AccessoryInformation);i.setCharacteristic(T.Manufacturer,e.manufacturer||"homebridge.io"),i.setCharacteristic(T.Model,e.model||"homebridge"),i.setCharacteristic(T.SerialNumber,e.username),i.setCharacteristic(T.FirmwareRevision,e.firmwareRevision||A()),this.bridge.on(E.LISTENING,r=>{a.success("Homebridge v%s (HAP v%s) (%s) is running on port %s.",A(),Se(),e.name,r)});let t={username:e.username,port:e.port,pincode:e.pin,category:Ie.BRIDGE,bind:e.bind,addIdentifyingMaterial:!0,advertiser:e.advertiser};e.setupID&&e.setupID.length===4&&(t.setupID=e.setupID),a.debug("Publishing bridge accessory (name: %s, publishInfo: %o).",this.bridge.displayName,s.strippingPinCode(t)),this.bridge.publish(t,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=>m.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(r){a.info(`Could not find the associated plugin for the accessory '${e.displayName}'. Tried to find the plugin by the platform name but ${r.message}`)}let t=i&&i.getActiveDynamicPlatform(e._associatedPlatform);if(i&&e._associatedHAPAccessory.on(E.CHARACTERISTIC_WARNING,s.printCharacteristicWriteWarning.bind(this,i,e._associatedHAPAccessory,{})),t)e.getService($.AccessoryInformation)?.setCharacteristic(T.FirmwareRevision,"0"),t.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(r){return a.warn(`${e._associatedPlugin?p(e._associatedPlugin):""} Could not restore cached accessory '${e._associatedHAPAccessory.displayName}':`,r.message),!1}return!0})}saveCachedPlatformAccessoriesOnDisk(){try{if(this.cachedAccessoriesFileLoaded){let e=this.cachedPlatformAccessories.map(i=>m.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(t=>{this.cachedPlatformAccessories.push(t);let r=this.pluginManager.getPlugin(t._associatedPlugin);return r?(r.getActiveDynamicPlatform(t._associatedPlatform)||a.warn("The plugin '%s' registered a new accessory for the platform '%s'. The platform couldn't be found though!",t._associatedPlugin,t._associatedPlatform),t._associatedHAPAccessory.on(E.CHARACTERISTIC_WARNING,s.printCharacteristicWriteWarning.bind(this,r,t._associatedHAPAccessory,{}))):a.warn("A platform configured a new accessory under the plugin name '%s'. However no loaded plugin could be found for the name!",t._associatedPlugin),t._associatedHAPAccessory});this.bridge.addBridgedAccessories(i),this.saveCachedPlatformAccessoriesOnDisk()}handleUpdatePlatformAccessories(){this.saveCachedPlatformAccessoriesOnDisk()}handleUnregisterPlatformAccessories(e){let i=e.map(t=>{let r=this.cachedPlatformAccessories.indexOf(t);return r>=0&&this.cachedPlatformAccessories.splice(r,1),t._associatedHAPAccessory});this.bridge.removeBridgedAccessories(i),this.saveCachedPlatformAccessoriesOnDisk()}async handlePublishExternalAccessories(e){let i=this.bridgeConfig.pin;for(let t of e){let r=t._associatedHAPAccessory,n=V(r.UUID),o=await this.externalPortService.requestPort(n);if(this.publishedExternalAccessories.has(n))throw new Error(`Accessory ${r.displayName} experienced an address collision.`);this.publishedExternalAccessories.set(n,t);let d=this.pluginManager.getPlugin(t._associatedPlugin);d?r.on(E.CHARACTERISTIC_WARNING,s.printCharacteristicWriteWarning.bind(this,d,r,{ignoreSlow:!0})):u.isQualifiedPluginIdentifier(t._associatedPlugin)&&a.warn("A platform configured a external accessory under the plugin name '%s'. However no loaded plugin could be found for the name!",t._associatedPlugin),r.on(E.LISTENING,h=>{a.success("%s is running on port %s.",r.displayName,h),a.info("Please add [%s] manually in Home app. Setup Code: %s",r.displayName,i)});let b={username:n,pincode:i,category:t.category,port:o,bind:this.bridgeConfig.bind,addIdentifyingMaterial:!0,advertiser:this.bridgeConfig.advertiser};a.debug("Publishing external accessory (name: %s, publishInfo: %o).",r.displayName,s.strippingPinCode(b)),r.publish(b,this.allowInsecureAccess)}}createHAPAccessory(e,i,t,r,n){let o=(i.getServices()||[]).filter(g=>!!g),d=(i.getControllers&&i.getControllers()||[]).filter(g=>!!g);if(o.length===0&&d.length===0)return;let b=Q.generate(`${r}:${n||t}`),h=new be(t,b);i.identify&&h.on(E.IDENTIFY,(g,_)=>{i.identify(()=>{}),_()});let O=h.getService($.AccessoryInformation);return o.forEach(g=>{g instanceof $.AccessoryInformation?(g.setCharacteristic(T.Name,t),g.getCharacteristic(T.Identify).removeAllListeners(Ee.SET),O.replaceCharacteristicsFromService(g)):h.addService(g)}),h.on(E.CHARACTERISTIC_WARNING,s.printCharacteristicWriteWarning.bind(this,e,h,{})),d.forEach(g=>{h.configureController(g)}),h}async loadPlatformAccessories(e,i,t,r){return new Promise(n=>{let o=setInterval(()=>{a.warn(p(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(Re(d=>{clearInterval(o),d.forEach((b,h)=>{let O=b.name,g=b.uuid_base;a.info("Initializing platform accessory '%s'...",O);let _=this.createHAPAccessory(e,b,O,t,g);_?this.bridge.addBridgedAccessory(_):r("Platform %s returned an accessory at index %d with an empty set of services. Won't adding it to the bridge!",t,h)}),n()}))})}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 Mi,{dirname as we}from"node:path";import{fileURLToPath as xe}from"node:url";import Bi from"fs-extra";var Ne=xe(import.meta.url),ji=we(Ne);var F=class{constructor(e){this.externalPorts=e}nextExternalPort;allocatedPorts=new Map;async requestPort(e){let i=this.allocatedPorts.get(e);if(i)return i;let t=this.getNextFreePort();return this.allocatedPorts.set(e,t),t}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;c.internal.warn("External port pool ran out of ports. Falling back to random port assignment.")}}},B=class extends F{constructor(i){super();this.childBridge=i}async requestPort(i){return await this.childBridge.requestExternalPort(i)}};y.title="homebridge: child bridge";var j=class{bridgeService;api;pluginManager;externalPortService;type;plugin;identifier;pluginConfig;bridgeConfig;bridgeOptions;homebridgeConfig;portRequestCallback=new Map;constructor(){this.sendMessage("ready")}sendMessage(e,i){y.send&&y.send({id:e,data:i})}async loadPlugin(e){this.type=e.type,this.identifier=e.identifier,this.pluginConfig=e.pluginConfig,this.bridgeConfig=e.bridgeConfig,this.bridgeOptions=e.bridgeOptions,this.homebridgeConfig=e.homebridgeConfig;for(let i of this.pluginConfig)delete i._bridge;this.bridgeOptions.noLogTimestamps&&c.setTimestampEnabled(!1),this.bridgeOptions.debugModeEnabled&&c.setDebugEnabled(!0),this.bridgeOptions.forceColourLogging&&c.forceColor(),this.bridgeOptions.customStoragePath&&I.setStoragePath(this.bridgeOptions.customStoragePath),De.setCustomStoragePath(I.persistPath()),this.api=new x,this.pluginManager=new u(this.api),this.externalPortService=new B(this),this.plugin=this.pluginManager.loadPlugin(e.pluginPath),await this.plugin.load(),await this.pluginManager.initializePlugin(this.plugin,e.identifier),y.title=`homebridge: ${this.plugin.getPluginIdentifier()}`,this.sendMessage("loaded",{version:this.plugin.version})}async startBridge(){this.bridgeService=new U(this.api,this.pluginManager,this.externalPortService,this.bridgeOptions,this.bridgeConfig,this.homebridgeConfig),this.bridgeService.bridge.on(G.ADVERTISED,()=>{this.sendPairedStatusEvent()}),this.bridgeService.bridge.on(G.PAIRED,()=>{this.sendPairedStatusEvent()}),this.bridgeService.bridge.on(G.UNPAIRED,()=>{this.sendPairedStatusEvent()}),await this.bridgeService.loadCachedPlatformAccessoriesFromDisk();for(let e of this.pluginConfig)if(this.type==="platform"){let i=this.pluginManager.getPluginForPlatform(this.identifier),t=e.name||i.getPluginIdentifier(),r=c.withPrefix(t),n=i.getPlatformConstructor(this.identifier),o=new n(r,e,this.api);x.isDynamicPlatformPlugin(o)?i.assignDynamicPlatform(this.identifier,o):x.isStaticPlatformPlugin(o)&&await this.bridgeService.loadPlatformAccessories(i,o,this.identifier,r)}else if(this.type==="accessory"){let i=this.pluginManager.getPluginForAccessory(this.identifier),t=e.name;if(!t){c.internal.warn("Could not load accessory %s as it is missing the required 'name' property!",this.identifier);return}let r=c.withPrefix(t),n=i.getAccessoryConstructor(this.identifier),o=new n(r,e,this.api),d=this.bridgeService.createHAPAccessory(i,o,t,this.identifier,e.uuid_base);d?this.bridgeService.bridge.addBridgedAccessory(d):r("Accessory %s returned empty set of services. Won't adding it to the bridge!",this.identifier)}this.bridgeService.restoreCachedPlatformAccessories(),this.bridgeService.publishBridge(),this.api.signalFinished(),this.sendMessage("online")}async requestExternalPort(e){return new Promise(i=>{let t=setTimeout(()=>{c.internal.warn("Parent process did not respond to port allocation request within 5 seconds - assigning random port."),i(void 0)},5e3),r=n=>{clearTimeout(t),i(n),this.portRequestCallback.delete(e)};this.portRequestCallback.set(e,r),this.sendMessage("portRequest",{username:e})})}handleExternalResponse(e){let i=this.portRequestCallback.get(e.username);i&&i(e.port)}sendPairedStatusEvent(){this.sendMessage("status",{paired:this.bridgeService?.bridge?._accessoryInfo?.paired()??null,setupUri:this.bridgeService?.bridge?.setupURI()??null})}shutdown(){this.bridgeService.teardown()}},H=new j;y.on("message",s=>{if(!(typeof s!="object"||!s.id))switch(s.id){case"load":{H.loadPlugin(s.data);break}case"start":{H.startBridge();break}case"portAllocated":{H.handleExternalResponse(s.data);break}}});var J=!1;function X(s,e){if(!J){J=!0,c.internal.info("Got %s, shutting down child bridge process...",s);try{H.shutdown()}catch{}setTimeout(()=>y.exit(128+e),5e3)}}y.on("SIGINT",X.bind(void 0,"SIGINT",2));y.on("SIGTERM",X.bind(void 0,"SIGTERM",15));setInterval(()=>{y.connected||(c.internal.info("Parent process not connected, terminating process..."),y.exit(1))},5e3);export{j as ChildBridgeFork};
2
+ //# sourceMappingURL=childBridgeFork.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/childBridgeFork.ts", "../src/api.ts", "../src/logger.ts", "../src/platformAccessory.ts", "../src/pluginManager.ts", "../src/plugin.ts", "../src/version.ts", "../src/user.ts", "../src/bridgeService.ts", "../src/storageService.ts", "../src/util/mac.ts", "../src/childBridgeService.ts", "../src/externalPortService.ts"],
4
+ "sourcesContent": ["/* global NodeJS */\n\nimport type { MacAddress } from 'hap-nodejs'\n\nimport type { AccessoryPlugin, PlatformPlugin } from './api.js'\nimport type {\n AccessoryConfig,\n BridgeConfiguration,\n BridgeOptions,\n HomebridgeConfig,\n PlatformConfig,\n} from './bridgeService.js'\nimport type {\n ChildBridgePairedStatusEventData,\n ChildProcessLoadEventData,\n ChildProcessMessageEvent,\n ChildProcessPluginLoadedEventData,\n ChildProcessPortAllocatedEventData,\n ChildProcessPortRequestEventData,\n} from './childBridgeService.js'\nimport type { Plugin } from './plugin.js'\n\nimport process from 'node:process'\n\nimport { AccessoryEventTypes, HAPStorage } from 'hap-nodejs'\n\nimport { HomebridgeAPI, PluginType } from './api.js'\nimport { BridgeService } from './bridgeService.js'\nimport { ChildProcessMessageEventType } from './childBridgeService.js'\nimport { ChildBridgeExternalPortService } from './externalPortService.js'\nimport { Logger } from './logger.js'\nimport { PluginManager } from './pluginManager.js'\nimport { User } from './user.js'\n\n/**\n * This is a standalone script executed as a child process fork\n */\n\nprocess.title = 'homebridge: child bridge'\n\nexport class ChildBridgeFork {\n private bridgeService!: BridgeService\n private api!: HomebridgeAPI\n private pluginManager!: PluginManager\n private externalPortService!: ChildBridgeExternalPortService\n\n private type!: PluginType\n private plugin!: Plugin\n private identifier!: string\n private pluginConfig!: Array<PlatformConfig | AccessoryConfig>\n private bridgeConfig!: BridgeConfiguration\n private bridgeOptions!: BridgeOptions\n private homebridgeConfig!: HomebridgeConfig\n\n private portRequestCallback: Map<MacAddress, (port: number | undefined) => void> = new Map()\n\n constructor() {\n // tell the parent process we are ready to accept plugin config\n this.sendMessage(ChildProcessMessageEventType.READY)\n }\n\n sendMessage<T = unknown>(type: ChildProcessMessageEventType, data?: T): void {\n if (process.send) {\n process.send({\n id: type,\n data,\n })\n }\n }\n\n async loadPlugin(data: ChildProcessLoadEventData): Promise<void> {\n // set data\n this.type = data.type\n this.identifier = data.identifier\n this.pluginConfig = data.pluginConfig\n this.bridgeConfig = data.bridgeConfig\n this.bridgeOptions = data.bridgeOptions\n this.homebridgeConfig = data.homebridgeConfig\n\n // remove the _bridge key (some plugins do not like unknown config)\n for (const config of this.pluginConfig) {\n delete config._bridge\n }\n\n // set bridge settings (inherited from main bridge)\n if (this.bridgeOptions.noLogTimestamps) {\n Logger.setTimestampEnabled(false)\n }\n\n if (this.bridgeOptions.debugModeEnabled) {\n Logger.setDebugEnabled(true)\n }\n\n if (this.bridgeOptions.forceColourLogging) {\n Logger.forceColor()\n }\n\n if (this.bridgeOptions.customStoragePath) {\n User.setStoragePath(this.bridgeOptions.customStoragePath)\n }\n\n // Initialize HAP-NodeJS with a custom persist directory\n HAPStorage.setCustomStoragePath(User.persistPath())\n\n // load api\n this.api = new HomebridgeAPI()\n this.pluginManager = new PluginManager(this.api)\n this.externalPortService = new ChildBridgeExternalPortService(this)\n\n // load plugin\n this.plugin = this.pluginManager.loadPlugin(data.pluginPath)\n await this.plugin.load()\n await this.pluginManager.initializePlugin(this.plugin, data.identifier)\n\n // change process title to include plugin name\n process.title = `homebridge: ${this.plugin.getPluginIdentifier()}`\n\n this.sendMessage<ChildProcessPluginLoadedEventData>(ChildProcessMessageEventType.LOADED, {\n version: this.plugin.version,\n })\n }\n\n async startBridge(): Promise<void> {\n this.bridgeService = new BridgeService(\n this.api,\n this.pluginManager,\n this.externalPortService,\n this.bridgeOptions,\n this.bridgeConfig,\n this.homebridgeConfig,\n )\n\n // watch bridge events to check when server is online\n this.bridgeService.bridge.on(AccessoryEventTypes.ADVERTISED, () => {\n this.sendPairedStatusEvent()\n })\n\n // watch for the paired event to update the server status\n this.bridgeService.bridge.on(AccessoryEventTypes.PAIRED, () => {\n this.sendPairedStatusEvent()\n })\n\n // watch for the unpaired event to update the server status\n this.bridgeService.bridge.on(AccessoryEventTypes.UNPAIRED, () => {\n this.sendPairedStatusEvent()\n })\n\n // load the cached accessories\n await this.bridgeService.loadCachedPlatformAccessoriesFromDisk()\n\n for (const config of this.pluginConfig) {\n if (this.type === PluginType.PLATFORM) {\n const plugin = this.pluginManager.getPluginForPlatform(this.identifier)\n const displayName = config.name || plugin.getPluginIdentifier()\n const logger = Logger.withPrefix(displayName)\n const constructor = plugin.getPlatformConstructor(this.identifier)\n const platform: PlatformPlugin = new constructor(logger, config as PlatformConfig, this.api)\n\n if (HomebridgeAPI.isDynamicPlatformPlugin(platform)) {\n plugin.assignDynamicPlatform(this.identifier, platform)\n } else if (HomebridgeAPI.isStaticPlatformPlugin(platform)) { // Plugin 1.0, load accessories\n await this.bridgeService.loadPlatformAccessories(plugin, platform, this.identifier, logger)\n } else {\n // otherwise it's a IndependentPlatformPlugin which doesn't expose any methods at all.\n // We just call the constructor and let it be enabled.\n }\n } else if (this.type === PluginType.ACCESSORY) {\n const plugin = this.pluginManager.getPluginForAccessory(this.identifier)\n const displayName = config.name\n\n if (!displayName) {\n Logger.internal.warn('Could not load accessory %s as it is missing the required \\'name\\' property!', this.identifier)\n return\n }\n\n const logger = Logger.withPrefix(displayName)\n const constructor = plugin.getAccessoryConstructor(this.identifier)\n const accessoryInstance: AccessoryPlugin = new constructor(logger, config as AccessoryConfig, this.api)\n\n // pass accessoryIdentifier for UUID generation, and optional parameter uuid_base which can be used instead of displayName for UUID generation\n const accessory = this.bridgeService.createHAPAccessory(plugin, accessoryInstance, displayName, this.identifier, config.uuid_base)\n\n if (accessory) {\n this.bridgeService.bridge.addBridgedAccessory(accessory)\n } else {\n logger('Accessory %s returned empty set of services. Won\\'t adding it to the bridge!', this.identifier)\n }\n }\n }\n\n // restore the cached accessories\n this.bridgeService.restoreCachedPlatformAccessories()\n\n this.bridgeService.publishBridge()\n this.api.signalFinished()\n\n // tell the parent we are online\n this.sendMessage(ChildProcessMessageEventType.ONLINE)\n }\n\n /**\n * Request the next available external port from the parent process\n * @param username\n */\n public async requestExternalPort(username: MacAddress): Promise<number | undefined> {\n return new Promise((resolve) => {\n const requestTimeout = setTimeout(() => {\n Logger.internal.warn('Parent process did not respond to port allocation request within 5 seconds - assigning random port.')\n resolve(undefined)\n }, 5000)\n\n // setup callback\n const callback = (port: number | undefined) => {\n clearTimeout(requestTimeout)\n resolve(port)\n this.portRequestCallback.delete(username)\n }\n this.portRequestCallback.set(username, callback)\n\n // send port request\n this.sendMessage<ChildProcessPortRequestEventData>(ChildProcessMessageEventType.PORT_REQUEST, { username })\n })\n }\n\n /**\n * Handles the port allocation response message from the parent process\n * @param data\n */\n public handleExternalResponse(data: ChildProcessPortAllocatedEventData): void {\n const callback = this.portRequestCallback.get(data.username)\n if (callback) {\n callback(data.port)\n }\n }\n\n /**\n * Sends the current pairing status of the child bridge to the parent process\n */\n public sendPairedStatusEvent() {\n this.sendMessage<ChildBridgePairedStatusEventData>(ChildProcessMessageEventType.STATUS_UPDATE, {\n paired: this.bridgeService?.bridge?._accessoryInfo?.paired() ?? null,\n setupUri: this.bridgeService?.bridge?.setupURI() ?? null,\n })\n }\n\n shutdown(): void {\n this.bridgeService.teardown()\n }\n}\n\n/**\n * Start Self\n */\nconst childPluginFork = new ChildBridgeFork()\n\n/**\n * Handle incoming IPC messages from the parent Homebridge process\n */\nprocess.on('message', (message: ChildProcessMessageEvent<unknown>) => {\n if (typeof message !== 'object' || !message.id) {\n return\n }\n\n switch (message.id) {\n case ChildProcessMessageEventType.LOAD: {\n childPluginFork.loadPlugin(message.data as ChildProcessLoadEventData)\n break\n }\n case ChildProcessMessageEventType.START: {\n childPluginFork.startBridge()\n break\n }\n case ChildProcessMessageEventType.PORT_ALLOCATED: {\n childPluginFork.handleExternalResponse(message.data as ChildProcessPortAllocatedEventData)\n break\n }\n }\n})\n\n/**\n * Handle the sigterm shutdown signals\n */\nlet shuttingDown = false\nfunction signalHandler(signal: NodeJS.Signals, signalNum: number): void {\n if (shuttingDown) {\n return\n }\n shuttingDown = true\n\n Logger.internal.info('Got %s, shutting down child bridge process...', signal)\n\n try {\n childPluginFork.shutdown()\n } catch (error: any) {\n // do nothing\n }\n\n setTimeout(() => process.exit(128 + signalNum), 5000)\n}\n\nprocess.on('SIGINT', signalHandler.bind(undefined, 'SIGINT', 2))\nprocess.on('SIGTERM', signalHandler.bind(undefined, 'SIGTERM', 15))\n\n/**\n * Ensure orphaned processes are cleaned up\n */\nsetInterval(() => {\n if (!process.connected) {\n Logger.internal.info('Parent process not connected, terminating process...')\n process.exit(1)\n }\n}, 5000)\n", "import type { Controller, Service } from 'hap-nodejs'\n\nimport type { AccessoryConfig, PlatformConfig } from './bridgeService.js'\nimport type { Logging } from './logger.js'\n\nimport { EventEmitter } from 'node:events'\n\nimport hapNodeJs from 'hap-nodejs'\nimport semver from 'semver'\n\nimport { Logger } from './logger.js'\nimport { PlatformAccessory } from './platformAccessory.js'\nimport { PluginManager } from './pluginManager.js'\nimport { User } from './user.js'\nimport getVersion from './version.js'\n\nconst log = Logger.internal\n\nexport type HAP = typeof hapNodeJs\nexport type HAPLegacyTypes = typeof hapNodeJs.LegacyTypes\n\nexport type PluginIdentifier = PluginName | ScopedPluginName\nexport type PluginName = string // plugin name like \"homebridge-dummy\"\nexport type ScopedPluginName = string // plugin name like \"@scope/homebridge-dummy\"\nexport type AccessoryName = string\nexport type PlatformName = string\n\nexport type AccessoryIdentifier = string // format: \"PluginIdentifier.AccessoryName\"\nexport type PlatformIdentifier = string // format: \"PluginIdentifier.PlatformName\"\n\n// eslint-disable-next-line no-restricted-syntax\nexport const enum PluginType {\n ACCESSORY = 'accessory',\n PLATFORM = 'platform',\n}\n\n/**\n * The {PluginInitializer} is a method which must be the default export for every homebridge plugin.\n * It is called once the plugin is loaded from disk.\n */\nexport interface PluginInitializer {\n\n /**\n * When the initializer is called the plugin must use the provided api instance and call the appropriate\n * register methods - {@link API.registerAccessory} or {@link API.registerPlatform} - in order to\n * correctly register for the following startup sequence.\n *\n * @param {API} api\n */\n (api: API): void | Promise<void>\n\n}\n\nexport interface AccessoryPluginConstructor {\n new(logger: Logging, config: AccessoryConfig, api: API): AccessoryPlugin\n}\n\nexport interface AccessoryPlugin {\n\n /**\n * Optional method which will be called if an 'identify' of an Accessory is requested by HomeKit.\n */\n identify?: () => void\n\n /**\n * This method will be called once on startup, to query all services to be exposed by the Accessory.\n * All event handlers for characteristics should be set up before the array is returned.\n *\n * @returns {Service[]} services - returned services will be added to the Accessory\n */\n getServices: () => Service[]\n\n /**\n * This method will be called once on startup, to query all controllers to be exposed by the Accessory.\n * It is optional to implement.\n *\n * This includes controllers like the RemoteController or the CameraController.\n * Any necessary controller specific setup should have been done when returning the array.\n * In most cases the plugin will only return an array of the size 1.\n *\n * In the case that the Plugin does not add any additional services (returned by {@link getServices}) the\n * method {@link getServices} must be defined in any way and should just return an empty array.\n *\n * @returns {Controller[]} controllers - returned controllers will be configured for the Accessory\n */\n getControllers?: () => Controller[]\n\n}\n\nexport interface PlatformPluginConstructor<Config extends PlatformConfig = PlatformConfig> {\n new(logger: Logging, config: Config, api: API): DynamicPlatformPlugin | StaticPlatformPlugin | IndependentPlatformPlugin\n}\n\nexport interface PlatformPlugin {} // not exported to the public in index.ts\n\n/**\n * Platform that is able to dynamically add or remove accessories.\n * All configured accessories are stored to disk and recreated on startup.\n * Accessories can be added or removed by using {@link API.registerPlatformAccessories} or {@link API.unregisterPlatformAccessories}.\n */\nexport interface DynamicPlatformPlugin extends PlatformPlugin {\n\n /**\n * This method is called for every PlatformAccessory, which is recreated from disk on startup.\n * It should be used to properly initialize the Accessory and setup all event handlers for\n * all services and their characteristics.\n *\n * @param {PlatformAccessory} accessory which needs to be configured\n */\n configureAccessory: (accessory: PlatformAccessory) => void\n\n}\n\n/**\n * Platform that exposes all available characteristics at the start of the plugin.\n * The set of accessories can not change at runtime.\n * The bridge waits for all callbacks to return before it is published and accessible by HomeKit controllers.\n */\nexport interface StaticPlatformPlugin extends PlatformPlugin {\n\n /**\n * This method is called once at startup. The Platform should pass all accessories which need to be created\n * to the callback in form of a {@link AccessoryPlugin}.\n * The Platform must respond in a timely manner as otherwise the startup of the bridge would be unnecessarily delayed.\n *\n * @param {(foundAccessories: AccessoryPlugin[]) => void} callback\n */\n accessories: (callback: (foundAccessories: AccessoryPlugin[]) => void) => void\n\n}\n\n/**\n * Platform that does not aim to add any accessories to the main bridge accessory.\n * This platform should be used if for example a plugin aims to only expose external accessories.\n * It should also be used when the platform doesn't intend to expose any accessories at all, like plugins\n * providing a UI for homebridge.\n */\nexport interface IndependentPlatformPlugin extends PlatformPlugin {\n // does not expose any methods\n}\n\n// eslint-disable-next-line no-restricted-syntax\nexport const enum APIEvent {\n /**\n * Event is fired once homebridge has finished with booting up and initializing all components and plugins.\n * When this event is fired it is possible that the Bridge accessory isn't published yet, if homebridge still needs\n * to wait for some {@see StaticPlatformPlugin | StaticPlatformPlugins} to finish accessory creation.\n */\n DID_FINISH_LAUNCHING = 'didFinishLaunching',\n /**\n * This event is fired when homebridge gets shutdown. This could be a regular shutdown or an unexpected crash.\n * At this stage all Accessories are already unpublished and all PlatformAccessories are already saved to disk!\n */\n SHUTDOWN = 'shutdown',\n}\n\n// eslint-disable-next-line no-restricted-syntax\nexport const enum InternalAPIEvent {\n REGISTER_ACCESSORY = 'registerAccessory',\n REGISTER_PLATFORM = 'registerPlatform',\n\n PUBLISH_EXTERNAL_ACCESSORIES = 'publishExternalAccessories',\n REGISTER_PLATFORM_ACCESSORIES = 'registerPlatformAccessories',\n UPDATE_PLATFORM_ACCESSORIES = 'updatePlatformAccessories',\n UNREGISTER_PLATFORM_ACCESSORIES = 'unregisterPlatformAccessories',\n}\n\nexport interface API {\n\n /**\n * The homebridge API version as a floating point number.\n */\n readonly version: number\n /**\n * The current homebridge semver version.\n */\n readonly serverVersion: string\n\n // ------------------ LEGACY EXPORTS FOR PRE TYPESCRIPT ------------------\n readonly user: typeof User\n readonly hap: HAP\n readonly hapLegacyTypes: HAPLegacyTypes // used for older accessories/platforms\n readonly platformAccessory: typeof PlatformAccessory\n // ------------------------------------------------------------------------\n\n /**\n * Returns true if the current running homebridge version is greater or equal to the\n * passed version string.\n *\n * Example:\n *\n * We assume the homebridge version 1.3.0-beta.12 ({@link serverVersion}) and the following example calls below\n * ```\n * versionGreaterOrEqual(\"1.2.0\"); // will return true\n * versionGreaterOrEqual(\"1.3.0\"); // will return false (the RELEASE version 1.3.0 is bigger than the BETA version 1.3.0-beta.12)\n * versionGreaterOrEqual(\"1.3.0-beta.8); // will return true\n * ```\n *\n * @param version\n */\n versionGreaterOrEqual: (version: string) => boolean\n\n registerAccessory: ((accessoryName: AccessoryName, constructor: AccessoryPluginConstructor) => void) & ((pluginIdentifier: PluginIdentifier, accessoryName: AccessoryName, constructor: AccessoryPluginConstructor) => void)\n\n registerPlatform: (<Config extends PlatformConfig>(platformName: PlatformName, constructor: PlatformPluginConstructor<Config>) => void) & (<Config extends PlatformConfig>(pluginIdentifier: PluginIdentifier, platformName: PlatformName, constructor: PlatformPluginConstructor<Config>) => void)\n registerPlatformAccessories: (pluginIdentifier: PluginIdentifier, platformName: PlatformName, accessories: PlatformAccessory[]) => void\n updatePlatformAccessories: (accessories: PlatformAccessory[]) => void\n unregisterPlatformAccessories: (pluginIdentifier: PluginIdentifier, platformName: PlatformName, accessories: PlatformAccessory[]) => void\n\n publishExternalAccessories: (pluginIdentifier: PluginIdentifier, accessories: PlatformAccessory[]) => void\n\n on: ((event: 'didFinishLaunching', listener: () => void) => this) & ((event: 'shutdown', listener: () => void) => this)\n\n}\n\n// eslint-disable-next-line ts/no-unsafe-declaration-merging\nexport declare interface HomebridgeAPI {\n\n on: ((event: 'didFinishLaunching', listener: () => void) => this) & ((event: 'shutdown', listener: () => void) => this) & ((event: InternalAPIEvent.REGISTER_ACCESSORY, listener: (accessoryName: AccessoryName, accessoryConstructor: AccessoryPluginConstructor, pluginIdentifier?: PluginIdentifier) => void) => this) & ((event: InternalAPIEvent.REGISTER_PLATFORM, listener: (platformName: PlatformName, platformConstructor: PlatformPluginConstructor, pluginIdentifier?: PluginIdentifier) => void) => this) & ((event: InternalAPIEvent.PUBLISH_EXTERNAL_ACCESSORIES, listener: (accessories: PlatformAccessory[]) => void) => this) & ((event: InternalAPIEvent.REGISTER_PLATFORM_ACCESSORIES, listener: (accessories: PlatformAccessory[]) => void) => this) & ((event: InternalAPIEvent.UPDATE_PLATFORM_ACCESSORIES, listener: (accessories: PlatformAccessory[]) => void) => this) & ((event: InternalAPIEvent.UNREGISTER_PLATFORM_ACCESSORIES, listener: (accessories: PlatformAccessory[]) => void) => this)\n\n emit: ((event: 'didFinishLaunching') => boolean) & ((event: 'shutdown') => boolean) & ((event: InternalAPIEvent.REGISTER_ACCESSORY, accessoryName: AccessoryName, accessoryConstructor: AccessoryPluginConstructor, pluginIdentifier?: PluginIdentifier) => boolean) & ((event: InternalAPIEvent.REGISTER_PLATFORM, platformName: PlatformName, platformConstructor: PlatformPluginConstructor, pluginIdentifier?: PluginIdentifier) => boolean) & ((event: InternalAPIEvent.PUBLISH_EXTERNAL_ACCESSORIES, accessories: PlatformAccessory[]) => boolean) & ((event: InternalAPIEvent.REGISTER_PLATFORM_ACCESSORIES, accessories: PlatformAccessory[]) => boolean) & ((event: InternalAPIEvent.UPDATE_PLATFORM_ACCESSORIES, accessories: PlatformAccessory[]) => boolean) & ((event: InternalAPIEvent.UNREGISTER_PLATFORM_ACCESSORIES, accessories: PlatformAccessory[]) => boolean)\n\n}\n\n// eslint-disable-next-line ts/no-unsafe-declaration-merging\nexport class HomebridgeAPI extends EventEmitter implements API {\n public readonly version = 2.7 // homebridge API version\n public readonly serverVersion = getVersion() // homebridge node module version\n\n // ------------------ LEGACY EXPORTS FOR PRE TYPESCRIPT ------------------\n readonly user = User\n readonly hap = hapNodeJs\n readonly hapLegacyTypes = hapNodeJs.LegacyTypes // used for older accessories/platforms\n readonly platformAccessory = PlatformAccessory\n // ------------------------------------------------------------------------\n\n constructor() {\n super()\n }\n\n public versionGreaterOrEqual(version: string): boolean {\n return semver.gte(this.serverVersion, version)\n }\n\n public static isDynamicPlatformPlugin(platformPlugin: PlatformPlugin): platformPlugin is DynamicPlatformPlugin {\n return 'configureAccessory' in platformPlugin\n }\n\n public static isStaticPlatformPlugin(platformPlugin: PlatformPlugin): platformPlugin is StaticPlatformPlugin {\n return 'accessories' in platformPlugin\n }\n\n signalFinished(): void {\n this.emit(APIEvent.DID_FINISH_LAUNCHING)\n }\n\n signalShutdown(): void {\n this.emit(APIEvent.SHUTDOWN)\n }\n\n registerAccessory(accessoryName: AccessoryName, constructor: AccessoryPluginConstructor): void\n registerAccessory(pluginIdentifier: PluginIdentifier, accessoryName: AccessoryName, constructor: AccessoryPluginConstructor): void\n\n registerAccessory(pluginIdentifier: PluginIdentifier | AccessoryName, accessoryName: AccessoryName | AccessoryPluginConstructor, constructor?: AccessoryPluginConstructor): void {\n if (typeof accessoryName === 'function') {\n constructor = accessoryName\n accessoryName = pluginIdentifier\n this.emit(InternalAPIEvent.REGISTER_ACCESSORY, accessoryName, constructor)\n } else {\n this.emit(InternalAPIEvent.REGISTER_ACCESSORY, accessoryName, constructor!, pluginIdentifier)\n }\n }\n\n registerPlatform(platformName: PlatformName, constructor: PlatformPluginConstructor): void\n registerPlatform(pluginIdentifier: PluginIdentifier, platformName: PlatformName, constructor: PlatformPluginConstructor): void\n\n registerPlatform(pluginIdentifier: PluginIdentifier | PlatformName, platformName: PlatformName | PlatformPluginConstructor, constructor?: PlatformPluginConstructor): void {\n if (typeof platformName === 'function') {\n constructor = platformName\n platformName = pluginIdentifier\n this.emit(InternalAPIEvent.REGISTER_PLATFORM, platformName, constructor)\n } else {\n this.emit(InternalAPIEvent.REGISTER_PLATFORM, platformName, constructor!, pluginIdentifier)\n }\n }\n\n publishCameraAccessories(pluginIdentifier: PluginIdentifier, accessories: PlatformAccessory[]): void {\n this.publishExternalAccessories(pluginIdentifier, accessories)\n }\n\n publishExternalAccessories(pluginIdentifier: PluginIdentifier, accessories: PlatformAccessory[]): void {\n if (!PluginManager.isQualifiedPluginIdentifier(pluginIdentifier)) {\n log.info(`One of your plugins incorrectly registered an external accessory using the platform name (${pluginIdentifier}) and not the plugin identifier. Please report this to the developer!`)\n }\n\n accessories.forEach((accessory) => {\n // noinspection SuspiciousTypeOfGuard\n if (!(accessory instanceof PlatformAccessory)) {\n throw new TypeError(`${pluginIdentifier} attempt to register an accessory that isn't PlatformAccessory!`)\n }\n\n accessory._associatedPlugin = pluginIdentifier\n })\n\n this.emit(InternalAPIEvent.PUBLISH_EXTERNAL_ACCESSORIES, accessories)\n }\n\n registerPlatformAccessories(pluginIdentifier: PluginIdentifier, platformName: PlatformName, accessories: PlatformAccessory[]): void {\n accessories.forEach((accessory) => {\n // noinspection SuspiciousTypeOfGuard\n if (!(accessory instanceof PlatformAccessory)) {\n throw new TypeError(`${pluginIdentifier} - ${platformName} attempt to register an accessory that isn't PlatformAccessory!`)\n }\n\n accessory._associatedPlugin = pluginIdentifier\n accessory._associatedPlatform = platformName\n })\n\n this.emit(InternalAPIEvent.REGISTER_PLATFORM_ACCESSORIES, accessories)\n }\n\n updatePlatformAccessories(accessories: PlatformAccessory[]): void {\n this.emit(InternalAPIEvent.UPDATE_PLATFORM_ACCESSORIES, accessories)\n }\n\n unregisterPlatformAccessories(pluginIdentifier: PluginIdentifier, platformName: PlatformName, accessories: PlatformAccessory[]): void {\n accessories.forEach((accessory) => {\n // noinspection SuspiciousTypeOfGuard\n if (!(accessory instanceof PlatformAccessory)) {\n throw new TypeError(`${pluginIdentifier} - ${platformName} attempt to unregister an accessory that isn't PlatformAccessory!`)\n }\n })\n\n this.emit(InternalAPIEvent.UNREGISTER_PLATFORM_ACCESSORIES, accessories)\n }\n}\n", "import util from 'node:util'\n\nimport chalk from 'chalk'\n\n/**\n * Log levels to indicate importance of the logged message.\n * Every level corresponds to a certain color.\n *\n * - INFO: no color\n * - SUCCESS: green\n * - WARN: yellow\n * - ERROR: red\n * - DEBUG: gray\n *\n * Messages with DEBUG level are only displayed if explicitly enabled.\n */\n// eslint-disable-next-line no-restricted-syntax\nexport const enum LogLevel {\n INFO = 'info',\n SUCCESS = 'success',\n WARN = 'warn',\n ERROR = 'error',\n DEBUG = 'debug',\n}\n\n/**\n * Represents a logging device which can be used directly as a function (for INFO logging)\n * but also has dedicated logging functions for respective logging levels.\n */\nexport interface Logging {\n\n prefix: string\n\n (message: string, ...parameters: any[]): void\n\n info: (message: string, ...parameters: any[]) => void\n success: (message: string, ...parameters: any[]) => void\n warn: (message: string, ...parameters: any[]) => void\n error: (message: string, ...parameters: any[]) => void\n debug: (message: string, ...parameters: any[]) => void\n log: (level: LogLevel, message: string, ...parameters: any[]) => void\n\n}\n\ninterface IntermediateLogging { // some auxiliary interface used to correctly type stuff happening in \"withPrefix\"\n\n prefix?: string\n\n (message: string, ...parameters: any[]): void\n\n info?: (message: string, ...parameters: any[]) => void\n success?: (message: string, ...parameters: any[]) => void\n warn?: (message: string, ...parameters: any[]) => void\n error?: (message: string, ...parameters: any[]) => void\n debug?: (message: string, ...parameters: any[]) => void\n log?: (level: LogLevel, message: string, ...parameters: any[]) => void\n\n}\n\n/**\n * Logger class\n */\nexport class Logger {\n public static readonly internal = new Logger()\n\n private static readonly loggerCache = new Map<string, Logging>() // global cache of logger instances by plugin name\n private static debugEnabled = false\n private static timestampEnabled = true\n\n readonly prefix?: string\n\n constructor(prefix?: string) {\n this.prefix = prefix\n }\n\n /**\n * Creates a new Logging device with a specified prefix.\n *\n * @param prefix {string} - the prefix of the logger\n */\n static withPrefix(prefix: string): Logging {\n const loggerStuff = Logger.loggerCache.get(prefix)\n\n if (loggerStuff) {\n return loggerStuff\n } else {\n const logger = new Logger(prefix)\n\n const log: IntermediateLogging = logger.info.bind(logger)\n log.info = logger.info\n log.success = logger.success\n log.warn = logger.warn\n log.error = logger.error\n log.debug = logger.debug\n log.log = logger.log\n\n log.prefix = logger.prefix\n\n // @ts-expect-error: I aimed to not use ts-ignore in this project, but this evil \"thing\" above is hell\n const logging: Logging = log\n Logger.loggerCache.set(prefix, logging)\n return logging\n }\n }\n\n /**\n * Turns on debug level logging. Off by default.\n *\n * @param enabled {boolean}\n */\n public static setDebugEnabled(enabled: boolean = true): void {\n Logger.debugEnabled = enabled\n }\n\n /**\n * Turns on inclusion of timestamps in log messages. On by default.\n *\n * @param enabled {boolean}\n */\n public static setTimestampEnabled(enabled: boolean = true): void {\n Logger.timestampEnabled = enabled\n }\n\n /**\n * Forces color in logging output, even if it seems like color is unsupported.\n */\n public static forceColor(): void {\n chalk.level = 1 // `1` - Basic 16 colors support.\n }\n\n public info(message: string, ...parameters: any[]): void {\n this.log(LogLevel.INFO, message, ...parameters)\n }\n\n public success(message: string, ...parameters: any[]): void {\n this.log(LogLevel.SUCCESS, message, ...parameters)\n }\n\n public warn(message: string, ...parameters: any[]): void {\n this.log(LogLevel.WARN, message, ...parameters)\n }\n\n public error(message: string, ...parameters: any[]): void {\n this.log(LogLevel.ERROR, message, ...parameters)\n }\n\n public debug(message: string, ...parameters: any[]): void {\n this.log(LogLevel.DEBUG, message, ...parameters)\n }\n\n public log(level: LogLevel, message: string, ...parameters: any[]): void {\n if (level === LogLevel.DEBUG && !Logger.debugEnabled) {\n return\n }\n\n message = util.format(message, ...parameters)\n\n let loggingFunction = console.log // eslint-disable-line no-console\n switch (level) {\n case LogLevel.SUCCESS:\n message = chalk.green(message)\n break\n case LogLevel.WARN:\n message = chalk.yellow(message)\n loggingFunction = console.error\n break\n case LogLevel.ERROR:\n message = chalk.red(message)\n loggingFunction = console.error\n break\n case LogLevel.DEBUG:\n message = chalk.gray(message)\n break\n }\n\n if (this.prefix) {\n message = `${getLogPrefix(this.prefix)} ${message}`\n }\n\n if (Logger.timestampEnabled) {\n const date = new Date()\n message = chalk.white(`[${date.toLocaleString()}] `) + message\n }\n\n loggingFunction(message)\n }\n}\n\n/**\n * Gets the prefix\n * @param prefix\n */\nexport function getLogPrefix(prefix: string): string {\n return chalk.cyan(`[${prefix}]`)\n}\n", "import type {\n Controller,\n ControllerConstructor,\n SerializedAccessory,\n Service,\n VoidCallback,\n WithUUID,\n} from 'hap-nodejs'\nimport type { ConstructorArgs } from 'hap-nodejs/dist/types.js'\n\nimport type { PlatformName, PluginIdentifier, PluginName } from './api.js'\n\nimport { EventEmitter } from 'node:events'\n\nimport {\n Accessory,\n AccessoryEventTypes,\n Categories,\n} from 'hap-nodejs'\n\nexport type UnknownContext = Record<string, any>\n\nexport interface SerializedPlatformAccessory<T extends UnknownContext = UnknownContext> extends SerializedAccessory {\n\n plugin: PluginName\n platform: PlatformName\n context: T\n\n}\n\n// eslint-disable-next-line no-restricted-syntax\nexport const enum PlatformAccessoryEvent {\n IDENTIFY = 'identify',\n}\n\n// eslint-disable-next-line ts/no-unsafe-declaration-merging\nexport declare interface PlatformAccessory {\n\n on: (event: 'identify', listener: () => void) => this\n\n emit: (event: 'identify') => boolean\n\n}\n\n// eslint-disable-next-line ts/no-unsafe-declaration-merging\nexport class PlatformAccessory<T extends UnknownContext = UnknownContext> extends EventEmitter {\n // somewhat ugly way to inject custom Accessory object, while not changing the publicly exposed constructor signature\n private static injectedAccessory?: Accessory\n\n _associatedPlugin?: PluginIdentifier // present as soon as it is registered\n _associatedPlatform?: PlatformName // not present for external accessories\n\n _associatedHAPAccessory: Accessory\n\n // ---------------- HAP Accessory mirror ----------------\n displayName: string\n UUID: string\n category: Categories\n services: Service[] = []\n // ------------------------------------------------------\n\n /**\n * This is a way for Plugin developers to store custom data with their accessory\n */\n public context: T = {} as T // providing something to store\n\n constructor(displayName: string, uuid: string, category?: Categories) { // category is only useful for external accessories\n super()\n this._associatedHAPAccessory = PlatformAccessory.injectedAccessory\n ? PlatformAccessory.injectedAccessory\n : new Accessory(displayName, uuid)\n\n if (category) {\n this._associatedHAPAccessory.category = category\n }\n\n this.displayName = this._associatedHAPAccessory.displayName\n this.UUID = this._associatedHAPAccessory.UUID\n this.category = category || Categories.OTHER\n this.services = this._associatedHAPAccessory.services\n\n // forward identify event\n this._associatedHAPAccessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => {\n // @ts-expect-error: empty callback for backwards compatibility\n this.emit(PlatformAccessoryEvent.IDENTIFY, paired, () => {})\n callback()\n })\n }\n\n public addService(service: Service): Service\n public addService<S extends typeof Service>(serviceConstructor: S, ...constructorArgs: ConstructorArgs<S>): Service\n public addService(service: Service | typeof Service, ...constructorArgs: any[]): Service {\n // @ts-expect-error: while the HAP-NodeJS interface was refined, the underlying implementation\n // still only operates on an any[] array. Therefore, do not require any additional checks here\n // we force the parameter unpack with expecting a ts-error.\n return this._associatedHAPAccessory.addService(service, ...constructorArgs)\n }\n\n public removeService(service: Service): void {\n this._associatedHAPAccessory.removeService(service)\n }\n\n public getService<T extends WithUUID<typeof Service>>(name: string | T): Service | undefined {\n return this._associatedHAPAccessory.getService(name)\n }\n\n public getServiceById<T extends WithUUID<typeof Service>>(uuid: string | T, subType: string): Service | undefined {\n return this._associatedHAPAccessory.getServiceById(uuid, subType)\n }\n\n /**\n * Configures a new controller for the given accessory.\n * See {@link https://developers.homebridge.io/HAP-NodeJS/classes/accessory.html#configurecontroller | Accessory.configureController}.\n *\n * @param controller\n */\n public configureController(controller: Controller | ControllerConstructor): void {\n this._associatedHAPAccessory.configureController(controller)\n }\n\n /**\n * Removes a configured controller from the given accessory.\n * See {@link https://developers.homebridge.io/HAP-NodeJS/classes/accessory.html#removecontroller | Accessory.removeController}.\n *\n * @param controller\n */\n public removeController(controller: Controller): void {\n this._associatedHAPAccessory.removeController(controller)\n }\n\n // private\n static serialize(accessory: PlatformAccessory): SerializedPlatformAccessory {\n return {\n plugin: accessory._associatedPlugin!,\n platform: accessory._associatedPlatform!,\n context: accessory.context,\n ...Accessory.serialize(accessory._associatedHAPAccessory),\n }\n }\n\n static deserialize(json: SerializedPlatformAccessory): PlatformAccessory {\n const accessory = Accessory.deserialize(json)\n\n PlatformAccessory.injectedAccessory = accessory\n const platformAccessory = new PlatformAccessory(accessory.displayName, accessory.UUID)\n PlatformAccessory.injectedAccessory = undefined\n\n platformAccessory._associatedPlugin = json.plugin\n platformAccessory._associatedPlatform = json.platform\n platformAccessory.context = json.context\n platformAccessory.category = json.category\n\n return platformAccessory\n }\n}\n", "import type {\n AccessoryIdentifier,\n AccessoryName,\n AccessoryPluginConstructor,\n HomebridgeAPI,\n PlatformIdentifier,\n PlatformName,\n PlatformPluginConstructor,\n PluginIdentifier,\n PluginName,\n} from './api.js'\n\nimport { execSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport {\n InternalAPIEvent,\n} from './api.js'\nimport { Logger } from './logger.js'\nimport { Plugin } from './plugin.js'\n\nconst log = Logger.internal\nconst require = createRequire(import.meta.url)\nconst paths = require.resolve.paths('')\n\nexport interface PackageJSON { // incomplete type for package.json (just stuff we use here)\n name: string\n version: string\n keywords?: string[]\n\n // see https://nodejs.org/dist/latest-v14.x/docs/api/packages.html#packages_package_entry_points\n exports?: string | Record<string, string | Record<string, string>>\n main?: string\n\n /**\n * When set as module, it marks .js file to be treated as ESM.\n * See https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_enabling\n */\n type?: 'module' | 'commonjs'\n\n engines?: Record<string, string>\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n peerDependencies?: Record<string, string>\n}\n\nexport interface PluginManagerOptions {\n /**\n * Additional path to search for plugins in. Specified relative to the current working directory.\n */\n customPluginPath?: string\n /**\n * If set, only load plugins from the customPluginPath, if set, otherwise only from the primary global node_modules.\n */\n strictPluginResolution?: boolean\n /**\n * When defined, only plugins specified here will be initialized.\n */\n activePlugins?: PluginIdentifier[]\n /**\n * Plugins that are marked as disabled and whose corresponding config blocks should be ignored\n */\n disabledPlugins?: PluginIdentifier[]\n}\n\n/**\n * Utility which exposes methods to search for installed Homebridge plugins\n */\nexport class PluginManager {\n // name must be prefixed with 'homebridge-' or '@scope/homebridge-'\n private static readonly PLUGIN_IDENTIFIER_PATTERN = /^((@[\\w-]*)\\/)?(homebridge-[\\w-]*)$/\n\n private readonly api: HomebridgeAPI\n\n private readonly searchPaths: Set<string> = new Set() // unique set of search paths we will use to discover installed plugins\n private readonly strictPluginResolution: boolean = false\n private readonly activePlugins?: PluginIdentifier[]\n private readonly disabledPlugins?: PluginIdentifier[]\n\n private readonly plugins: Map<PluginIdentifier, Plugin> = new Map()\n // we have some plugins which simply pass a wrong or misspelled plugin name to the api calls, this translation tries to mitigate this\n private readonly pluginIdentifierTranslation: Map<PluginIdentifier, PluginIdentifier> = new Map()\n private readonly accessoryToPluginMap: Map<AccessoryName, Plugin[]> = new Map()\n private readonly platformToPluginMap: Map<PlatformName, Plugin[]> = new Map()\n\n private currentInitializingPlugin?: Plugin // used to match registering plugins, see handleRegisterAccessory and handleRegisterPlatform\n\n constructor(api: HomebridgeAPI, options?: PluginManagerOptions) {\n this.api = api\n\n if (options) {\n if (options.customPluginPath) {\n this.searchPaths.add(path.resolve(process.cwd(), options.customPluginPath))\n }\n\n this.strictPluginResolution = options.strictPluginResolution || false\n\n this.activePlugins = options.activePlugins\n this.disabledPlugins = Array.isArray(options.disabledPlugins) ? options.disabledPlugins : undefined\n }\n\n this.api.on(InternalAPIEvent.REGISTER_ACCESSORY, this.handleRegisterAccessory.bind(this))\n this.api.on(InternalAPIEvent.REGISTER_PLATFORM, this.handleRegisterPlatform.bind(this))\n }\n\n public static isQualifiedPluginIdentifier(identifier: string): boolean {\n return PluginManager.PLUGIN_IDENTIFIER_PATTERN.test(identifier)\n }\n\n public static extractPluginName(name: string): PluginName { // extract plugin name without @scope/ prefix\n return name.match(PluginManager.PLUGIN_IDENTIFIER_PATTERN)![3]\n }\n\n public static extractPluginScope(name: string): string { // extract the \"@scope\" of a npm module name\n return name.match(PluginManager.PLUGIN_IDENTIFIER_PATTERN)![2]\n }\n\n public static getAccessoryName(identifier: AccessoryIdentifier): AccessoryName {\n if (!identifier.includes('.')) {\n return identifier\n }\n\n return identifier.split('.')[1]\n }\n\n public static getPlatformName(identifier: PlatformIdentifier): PlatformIdentifier {\n if (!identifier.includes('.')) {\n return identifier\n }\n\n return identifier.split('.')[1]\n }\n\n public static getPluginIdentifier(identifier: AccessoryIdentifier | PlatformIdentifier): PluginIdentifier {\n return identifier.split('.')[0]\n }\n\n public async initializeInstalledPlugins(): Promise<void> {\n log.info('---')\n\n this.loadInstalledPlugins()\n\n for (const [identifier, plugin] of this.plugins) {\n try {\n await plugin.load()\n } catch (error: any) {\n log.error('====================')\n log.error(`ERROR LOADING PLUGIN ${identifier}:`)\n log.error(error.stack)\n log.error('====================')\n\n this.plugins.delete(identifier)\n continue\n }\n\n if (this.disabledPlugins && this.disabledPlugins.includes(plugin.getPluginIdentifier())) {\n plugin.disabled = true\n }\n\n if (plugin.disabled) {\n log.warn(`Disabled plugin: ${identifier}@${plugin.version}`)\n } else {\n log.info(`Loaded plugin: ${identifier}@${plugin.version}`)\n }\n\n await this.initializePlugin(plugin, identifier)\n\n log.info('---')\n }\n\n this.currentInitializingPlugin = undefined\n }\n\n public async initializePlugin(plugin: Plugin, identifier: string): Promise<void> {\n try {\n this.currentInitializingPlugin = plugin\n await plugin.initialize(this.api) // call the plugin's initializer and pass it our API instance\n } catch (error: any) {\n log.error('====================')\n log.error(`ERROR INITIALIZING PLUGIN ${identifier}:`)\n log.error(error.stack)\n log.error('====================')\n\n this.plugins.delete(identifier)\n }\n }\n\n private handleRegisterAccessory(name: AccessoryName, constructor: AccessoryPluginConstructor, pluginIdentifier?: PluginIdentifier): void {\n if (!this.currentInitializingPlugin) {\n throw new Error(`Unexpected accessory registration. Plugin ${pluginIdentifier ? `'${pluginIdentifier}' ` : ''}tried to register outside the initializer function!`)\n }\n\n if (pluginIdentifier && pluginIdentifier !== this.currentInitializingPlugin.getPluginIdentifier()) {\n log.info(`Plugin '${this.currentInitializingPlugin.getPluginIdentifier()}' tried to register with an incorrect plugin identifier: '${pluginIdentifier}'. Please report this to the developer!`)\n this.pluginIdentifierTranslation.set(pluginIdentifier, this.currentInitializingPlugin.getPluginIdentifier())\n }\n\n this.currentInitializingPlugin.registerAccessory(name, constructor)\n\n let plugins = this.accessoryToPluginMap.get(name)\n if (!plugins) {\n plugins = []\n this.accessoryToPluginMap.set(name, plugins)\n }\n plugins.push(this.currentInitializingPlugin)\n }\n\n private handleRegisterPlatform(name: PlatformName, constructor: PlatformPluginConstructor, pluginIdentifier?: PluginIdentifier): void {\n if (!this.currentInitializingPlugin) {\n throw new Error(`Unexpected platform registration. Plugin ${pluginIdentifier ? `'${pluginIdentifier}' ` : ''}tried to register outside the initializer function!`)\n }\n\n if (pluginIdentifier && pluginIdentifier !== this.currentInitializingPlugin.getPluginIdentifier()) {\n log.debug(`Plugin '${this.currentInitializingPlugin.getPluginIdentifier()}' tried to register with an incorrect plugin identifier: '${pluginIdentifier}'. Please report this to the developer!`)\n this.pluginIdentifierTranslation.set(pluginIdentifier, this.currentInitializingPlugin.getPluginIdentifier())\n }\n\n this.currentInitializingPlugin.registerPlatform(name, constructor)\n\n let plugins = this.platformToPluginMap.get(name)\n if (!plugins) {\n plugins = []\n this.platformToPluginMap.set(name, plugins)\n }\n plugins.push(this.currentInitializingPlugin)\n }\n\n public getPluginForAccessory(accessoryIdentifier: AccessoryIdentifier | AccessoryName): Plugin {\n let plugin: Plugin\n if (!accessoryIdentifier.includes('.')) { // see if it matches exactly one accessory\n let found = this.accessoryToPluginMap.get(accessoryIdentifier)\n\n if (!found) {\n throw new Error(`No plugin was found for the accessory \"${accessoryIdentifier}\" in your config.json. Please make sure the corresponding plugin is installed correctly.`)\n }\n\n if (found.length > 1) {\n const options = found.map(plugin => `${plugin.getPluginIdentifier()}.${accessoryIdentifier}`).join(', ')\n // check if only one of the multiple platforms is not disabled\n found = found.filter(plugin => !plugin.disabled)\n if (found.length !== 1) {\n throw new Error(`The requested accessory '${accessoryIdentifier}' has been registered multiple times. Please be more specific by writing one of: ${options}`)\n }\n }\n\n plugin = found[0]\n accessoryIdentifier = `${plugin.getPluginIdentifier()}.${accessoryIdentifier}`\n } else {\n const pluginIdentifier = PluginManager.getPluginIdentifier(accessoryIdentifier)\n if (!this.hasPluginRegistered(pluginIdentifier)) {\n throw new Error(`The requested plugin '${pluginIdentifier}' was not registered.`)\n }\n\n plugin = this.getPlugin(pluginIdentifier)!\n }\n\n return plugin\n }\n\n public getPluginForPlatform(platformIdentifier: PlatformIdentifier | PlatformName): Plugin {\n let plugin: Plugin\n if (!platformIdentifier.includes('.')) { // see if it matches exactly one platform\n let found = this.platformToPluginMap.get(platformIdentifier)\n\n if (!found) {\n throw new Error(`No plugin was found for the platform \"${platformIdentifier}\" in your config.json. Please make sure the corresponding plugin is installed correctly.`)\n }\n\n if (found.length > 1) {\n const options = found.map(plugin => `${plugin.getPluginIdentifier()}.${platformIdentifier}`).join(', ')\n // check if only one of the multiple platforms is not disabled\n found = found.filter(plugin => !plugin.disabled)\n if (found.length !== 1) {\n throw new Error(`The requested platform '${platformIdentifier}' has been registered multiple times. Please be more specific by writing one of: ${options}`)\n }\n }\n\n plugin = found[0]\n platformIdentifier = `${plugin.getPluginIdentifier()}.${platformIdentifier}`\n } else {\n const pluginIdentifier = PluginManager.getPluginIdentifier(platformIdentifier)\n if (!this.hasPluginRegistered(pluginIdentifier)) {\n throw new Error(`The requested plugin '${pluginIdentifier}' was not registered.`)\n }\n\n plugin = this.getPlugin(pluginIdentifier)!\n }\n\n return plugin\n }\n\n public hasPluginRegistered(pluginIdentifier: PluginIdentifier): boolean {\n return this.plugins.has(pluginIdentifier) || this.pluginIdentifierTranslation.has(pluginIdentifier)\n }\n\n public getPlugin(pluginIdentifier: PluginIdentifier): Plugin | undefined {\n const plugin = this.plugins.get(pluginIdentifier)\n if (plugin) {\n return plugin\n } else {\n const translation = this.pluginIdentifierTranslation.get(pluginIdentifier)\n if (translation) {\n return this.plugins.get(translation)\n }\n }\n\n return undefined\n }\n\n public getPluginByActiveDynamicPlatform(platformName: PlatformName): Plugin | undefined {\n const found = (this.platformToPluginMap.get(platformName) || [])\n .filter(plugin => !!plugin.getActiveDynamicPlatform(platformName))\n\n if (found.length === 0) {\n return undefined\n } else if (found.length > 1) {\n const plugins = found.map(plugin => plugin.getPluginIdentifier()).join(', ')\n throw new Error(`'${platformName}' is an ambiguous platform name. It was registered by multiple plugins: ${plugins}`)\n } else {\n return found[0]\n }\n }\n\n /**\n * Gets all plugins installed on the local system\n */\n private loadInstalledPlugins(): void {\n this.loadDefaultPaths()\n\n this.searchPaths.forEach((searchPath) => { // search for plugins among all known paths\n if (!fs.existsSync(searchPath)) { // just because this path is in require.main.paths doesn't mean it necessarily exists!\n return\n }\n\n if (fs.existsSync(path.join(searchPath, 'package.json'))) { // does this path point inside a single plugin and not a directory containing plugins?\n try {\n this.loadPlugin(searchPath)\n } catch (error: any) {\n log.warn(error.message)\n }\n } else { // read through each directory in this node_modules folder\n const relativePluginPaths = fs.readdirSync(searchPath) // search for directories only\n .filter((relativePath) => {\n try {\n return fs.statSync(path.resolve(searchPath, relativePath)).isDirectory()\n } catch (error: any) {\n log.debug(`Ignoring path ${path.resolve(searchPath, relativePath)} - ${error.message}`)\n return false\n }\n })\n\n // expand out @scoped plugins\n relativePluginPaths.slice()\n .filter(path => path.charAt(0) === '@') // is it a scope directory?\n .forEach((scopeDirectory) => {\n // remove scopeDirectory from the path list\n const index = relativePluginPaths.indexOf(scopeDirectory)\n relativePluginPaths.splice(index, 1)\n\n const absolutePath = path.join(searchPath, scopeDirectory)\n fs.readdirSync(absolutePath)\n .filter(name => PluginManager.isQualifiedPluginIdentifier(name))\n .filter((name) => {\n try {\n return fs.statSync(path.resolve(absolutePath, name)).isDirectory()\n } catch (error: any) {\n log.debug(`Ignoring path ${path.resolve(absolutePath, name)} - ${error.message}`)\n return false\n }\n })\n .forEach(name => relativePluginPaths.push(`${scopeDirectory}/${name}`))\n })\n\n relativePluginPaths\n .filter((pluginIdentifier) => {\n return PluginManager.isQualifiedPluginIdentifier(pluginIdentifier) // needs to be a valid homebridge plugin name\n && (!this.activePlugins || this.activePlugins.includes(pluginIdentifier)) // check if activePlugins is restricted and if so is the plugin is contained\n })\n .forEach((pluginIdentifier) => {\n try {\n const absolutePath = path.resolve(searchPath, pluginIdentifier)\n this.loadPlugin(absolutePath)\n } catch (error: any) {\n log.warn(error.message)\n }\n })\n }\n })\n\n if (this.plugins.size === 0) {\n log.warn('No plugins found.')\n }\n }\n\n public loadPlugin(absolutePath: string): Plugin {\n const packageJson: PackageJSON = PluginManager.loadPackageJSON(absolutePath)\n\n const identifier: PluginIdentifier = packageJson.name\n const name: PluginName = PluginManager.extractPluginName(identifier)\n const scope = PluginManager.extractPluginScope(identifier) // possibly undefined\n\n const alreadyInstalled = this.plugins.get(identifier) // check if there is already a plugin with the same Identifier\n if (alreadyInstalled) {\n throw new Error(`Warning: skipping plugin found at '${absolutePath}' since we already loaded the same plugin from '${alreadyInstalled.getPluginPath()}'.`)\n }\n\n const plugin = new Plugin(name, absolutePath, packageJson, scope)\n this.plugins.set(identifier, plugin)\n return plugin\n }\n\n private static loadPackageJSON(pluginPath: string): PackageJSON {\n const packageJsonPath = path.join(pluginPath, 'package.json')\n let packageJson: PackageJSON\n\n if (!fs.existsSync(packageJsonPath)) {\n throw new Error(`Plugin ${pluginPath} does not contain a package.json.`)\n }\n\n try {\n packageJson = JSON.parse(fs.readFileSync(packageJsonPath, { encoding: 'utf8' })) // attempt to parse package.json\n } catch (error: any) {\n throw new Error(`Plugin ${pluginPath} contains an invalid package.json. Error: ${error}`)\n }\n\n if (!packageJson.name || !PluginManager.isQualifiedPluginIdentifier(packageJson.name)) {\n throw new Error(`Plugin ${pluginPath} does not have a package name that begins with 'homebridge-' or '@scope/homebridge-.`)\n }\n\n // verify that it's tagged with the correct keyword\n if (!packageJson.keywords || !packageJson.keywords.includes('homebridge-plugin')) {\n throw new Error(`Plugin ${pluginPath} package.json does not contain the keyword 'homebridge-plugin'.`)\n }\n\n return packageJson\n }\n\n private loadDefaultPaths(): void {\n if (this.strictPluginResolution) {\n // if strict plugin resolution is enabled:\n // * only use custom plugin path, if set;\n // * otherwise add the current npm global prefix (e.g. /usr/local/lib/node_modules)\n if (this.searchPaths.size === 0) {\n this.addNpmPrefixToSearchPaths()\n }\n return\n }\n\n if (paths) {\n // add the paths used by require()\n paths.forEach(path => this.searchPaths.add(path))\n }\n\n // THIS SECTION FROM: https://github.com/yeoman/environment/blob/master/lib/resolver.js\n\n // Adding global npm directories\n // We tried using npm to get the global modules path, but it hasn't work out\n // because of bugs in the parsable implementation of `ls` command and mostly\n // performance issues. So, we go with our best bet for now.\n if (process.env.NODE_PATH) {\n process.env.NODE_PATH\n .split(path.delimiter)\n .filter(path => !!path) // trim out empty values\n .forEach(path => this.searchPaths.add(path))\n } else {\n // Default paths for non-windows systems\n if (process.platform !== 'win32') {\n this.searchPaths.add('/usr/local/lib/node_modules')\n this.searchPaths.add('/usr/lib/node_modules')\n }\n this.addNpmPrefixToSearchPaths()\n }\n }\n\n private addNpmPrefixToSearchPaths(): void {\n if (process.platform === 'win32') {\n this.searchPaths.add(path.join(process.env.APPDATA!, 'npm/node_modules'))\n } else {\n this.searchPaths.add(execSync('/bin/echo -n \"$(npm -g prefix)/lib/node_modules\"', {\n env: Object.assign({\n npm_config_loglevel: 'silent',\n npm_update_notifier: 'false',\n }, process.env),\n }).toString('utf8'))\n }\n }\n}\n", "import type {\n AccessoryIdentifier,\n AccessoryName,\n AccessoryPluginConstructor,\n API,\n DynamicPlatformPlugin,\n PlatformIdentifier,\n PlatformName,\n PlatformPluginConstructor,\n PluginIdentifier,\n PluginInitializer,\n PluginName,\n} from './api.js'\nimport type { PackageJSON } from './pluginManager.js'\n\nimport assert from 'node:assert'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\n\nimport { satisfies } from 'semver'\n\nimport { Logger } from './logger.js'\nimport { PluginManager } from './pluginManager.js'\nimport getVersion from './version.js'\n\nconst log = Logger.internal\n\n/**\n * Represents a loaded Homebridge plugin.\n */\nexport class Plugin {\n private readonly pluginName: PluginName\n private readonly scope?: string // npm package scope\n private readonly pluginPath: string // like \"/usr/local/lib/node_modules/homebridge-lockitron\"\n private readonly isESM: boolean\n\n public disabled = false // mark the plugin as disabled\n\n // ------------------ package.json content ------------------\n readonly version: string\n private readonly main: string\n private loadContext?: { // used to store data for a limited time until the load method is called, will be reset afterward\n engines?: Record<string, string>\n dependencies?: Record<string, string>\n }\n // ----------------------------------------------------------\n\n private pluginInitializer?: PluginInitializer // default exported function from the plugin that initializes it\n\n private readonly registeredAccessories: Map<AccessoryName, AccessoryPluginConstructor> = new Map()\n private readonly registeredPlatforms: Map<PlatformName, PlatformPluginConstructor> = new Map()\n\n private readonly activeDynamicPlatforms: Map<PlatformName, DynamicPlatformPlugin[]> = new Map()\n\n constructor(name: PluginName, path: string, packageJSON: PackageJSON, scope?: string) {\n this.pluginName = name\n this.scope = scope\n this.pluginPath = path\n\n this.version = packageJSON.version || '0.0.0'\n this.main = ''\n\n // figure out the main module\n // exports is available - https://nodejs.org/dist/latest-v14.x/docs/api/packages.html#packages_package_entry_points\n if (packageJSON.exports) {\n // main entrypoint - https://nodejs.org/dist/latest-v14.x/docs/api/packages.html#packages_main_entry_point_export\n if (typeof packageJSON.exports === 'string') {\n this.main = packageJSON.exports\n } else { // subpath export - https://nodejs.org/dist/latest-v14.x/docs/api/packages.html#packages_subpath_exports\n // conditional exports - https://nodejs.org/dist/latest-v14.x/docs/api/packages.html#packages_conditional_exports\n const exports = packageJSON.exports.import || packageJSON.exports.require || packageJSON.exports.node || packageJSON.exports.default || packageJSON.exports['.']\n\n // check if conditional export is nested\n if (typeof exports !== 'string') {\n if (exports.import) {\n this.main = exports.import\n } else {\n this.main = exports.require || exports.node || exports.default\n }\n } else {\n this.main = exports\n }\n }\n }\n\n // exports search was not successful, fallback to package.main, using index.js as fallback\n if (!this.main) {\n this.main = packageJSON.main || './index.js'\n }\n\n // check if it is an ESM module\n this.isESM = this.main.endsWith('.mjs') || (this.main.endsWith('.js') && packageJSON.type === 'module')\n\n // very temporary fix for first wave of plugins\n if (packageJSON.peerDependencies && (!packageJSON.engines || !packageJSON.engines.homebridge)) {\n packageJSON.engines = packageJSON.engines || {}\n packageJSON.engines.homebridge = packageJSON.peerDependencies.homebridge\n }\n\n this.loadContext = {\n engines: packageJSON.engines,\n dependencies: packageJSON.dependencies,\n }\n }\n\n public getPluginIdentifier(): PluginIdentifier { // return full plugin name with scope prefix\n return (this.scope ? `${this.scope}/` : '') + this.pluginName\n }\n\n public getPluginPath(): string {\n return this.pluginPath\n }\n\n public registerAccessory(name: AccessoryName, constructor: AccessoryPluginConstructor): void {\n if (this.registeredAccessories.has(name)) {\n throw new Error(`Plugin '${this.getPluginIdentifier()}' tried to register an accessory '${name}' which has already been registered!`)\n }\n\n if (!this.disabled) {\n log.info('Registering accessory \\'%s\\'', `${this.getPluginIdentifier()}.${name}`)\n }\n\n this.registeredAccessories.set(name, constructor)\n }\n\n public registerPlatform(name: PlatformName, constructor: PlatformPluginConstructor): void {\n if (this.registeredPlatforms.has(name)) {\n throw new Error(`Plugin '${this.getPluginIdentifier()}' tried to register a platform '${name}' which has already been registered!`)\n }\n\n if (!this.disabled) {\n log.info('Registering platform \\'%s\\'', `${this.getPluginIdentifier()}.${name}`)\n }\n\n this.registeredPlatforms.set(name, constructor)\n }\n\n public getAccessoryConstructor(accessoryIdentifier: AccessoryIdentifier | AccessoryName): AccessoryPluginConstructor {\n const name: AccessoryName = PluginManager.getAccessoryName(accessoryIdentifier)\n\n const constructor = this.registeredAccessories.get(name)\n if (!constructor) {\n throw new Error(`The requested accessory '${name}' was not registered by the plugin '${this.getPluginIdentifier()}'.`)\n }\n\n return constructor\n }\n\n public getPlatformConstructor(platformIdentifier: PlatformIdentifier | PlatformName): PlatformPluginConstructor {\n const name: PlatformName = PluginManager.getPlatformName(platformIdentifier)\n\n const constructor = this.registeredPlatforms.get(name)\n if (!constructor) {\n throw new Error(`The requested platform '${name}' was not registered by the plugin '${this.getPluginIdentifier()}'.`)\n }\n\n // If it's a dynamic platform plugin, ensure it's not enabled multiple times.\n if (this.activeDynamicPlatforms.has(name)) {\n throw new Error(`The dynamic platform ${name} from the plugin ${this.getPluginIdentifier()} is configured `\n + `times in your config.json.`)\n }\n\n return constructor\n }\n\n public assignDynamicPlatform(platformIdentifier: PlatformIdentifier | PlatformName, platformPlugin: DynamicPlatformPlugin): void {\n const name: PlatformName = PluginManager.getPlatformName(platformIdentifier)\n\n let platforms = this.activeDynamicPlatforms.get(name)\n if (!platforms) {\n platforms = []\n this.activeDynamicPlatforms.set(name, platforms)\n }\n\n // the last platform published should be at the first position for easy access\n // we just try to mimic pre 1.0.0 behavior\n platforms.unshift(platformPlugin)\n }\n\n public getActiveDynamicPlatform(platformName: PlatformName): DynamicPlatformPlugin | undefined {\n const platforms = this.activeDynamicPlatforms.get(platformName)\n // we always use the last registered\n return platforms && platforms[0]\n }\n\n public async load(): Promise<void> {\n const context = this.loadContext!\n assert(context, 'Reached illegal state. Plugin state is undefined!')\n this.loadContext = undefined // free up memory\n\n // pluck out the HomeBridge version requirement\n if (!context.engines || !context.engines.homebridge) {\n throw new Error(`Plugin ${this.pluginPath} does not contain the 'homebridge' package in 'engines'.`)\n }\n\n const versionRequired = context.engines.homebridge\n const nodeVersionRequired = context.engines.node\n\n // make sure the version is satisfied by the currently running version of HomeBridge\n if (!satisfies(getVersion(), versionRequired, { includePrerelease: true })) {\n // TODO - change this back to an error\n log.error(`The plugin \"${this.pluginName}\" requires a Homebridge version of ${versionRequired} which does \\\nnot satisfy the current Homebridge version of ${getVersion()}. You may need to update this plugin (or Homebridge) to a newer version. \\\nYou may face unexpected issues or stability problems running this plugin.`)\n }\n\n // make sure the version is satisfied by the currently running version of Node\n if (nodeVersionRequired && !satisfies(process.version, nodeVersionRequired)) {\n log.warn(`The plugin \"${this.pluginName}\" requires Node.js version of ${nodeVersionRequired} which does \\\nnot 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`)\n }\n\n const dependencies = context.dependencies || {}\n if (dependencies.homebridge || dependencies['hap-nodejs']) {\n log.error(`The plugin \"${this.pluginName}\" defines 'homebridge' and/or 'hap-nodejs' in their 'dependencies' section, \\\nmeaning they carry an additional copy of homebridge and hap-nodejs. This not only wastes disk space, but also can cause \\\nmajor incompatibility issues and thus is considered bad practice. Please inform the developer to update their plugin!`)\n }\n\n const mainPath = path.join(this.pluginPath, this.main)\n\n // try to import it and grab the exported initialization hook\n // pathToFileURL(specifier).href to turn a path into a \"file url\"\n // see https://github.com/nodejs/node/issues/31710\n\n const pluginModules = await import(pathToFileURL(mainPath).href)\n\n if (typeof pluginModules === 'function') {\n this.pluginInitializer = pluginModules\n } else if (pluginModules && typeof pluginModules.default === 'function') {\n this.pluginInitializer = pluginModules.default\n } else {\n throw new Error(`Plugin ${this.pluginPath} does not export a initializer function from main.`)\n }\n }\n\n public initialize(api: API): void | Promise<void> {\n if (!this.pluginInitializer) {\n throw new Error('Tried to initialize a plugin which hasn\\'t been loaded yet!')\n }\n\n return this.pluginInitializer(api)\n }\n}\n", "import fs from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nfunction loadPackageJson(): any {\n const packageJSONPath = join(__dirname, '../package.json')\n return JSON.parse(fs.readFileSync(packageJSONPath, { encoding: 'utf8' }))\n}\n\nexport default function getVersion(): string {\n return loadPackageJson().version\n}\n\nexport function getRequiredNodeVersion(): string {\n return loadPackageJson().engines.node\n}\n", "import os from 'node:os'\nimport path from 'node:path'\n\n/**\n * Manages user settings and storage locations.\n */\nexport class User {\n private static customStoragePath?: string\n private static storageAccessed = false\n\n static configPath(): string {\n return path.join(User.storagePath(), 'config.json')\n }\n\n static persistPath(): string {\n return path.join(User.storagePath(), 'persist') // hap-nodejs data is stored here\n }\n\n static cachedAccessoryPath(): string {\n return path.join(User.storagePath(), 'accessories')\n }\n\n static storagePath(): string {\n User.storageAccessed = true\n\n return User.customStoragePath ? User.customStoragePath : path.join(os.homedir(), '.homebridge')\n }\n\n public static setStoragePath(...storagePathSegments: string[]): void {\n if (User.storageAccessed) {\n throw new Error('Storage path was already accessed and cannot be changed anymore. Try initializing your custom storage path earlier!')\n }\n\n User.customStoragePath = path.resolve(...storagePathSegments)\n }\n}\n", "import type {\n CharacteristicWarning,\n InterfaceName,\n IPAddress,\n MacAddress,\n MDNSAdvertiser,\n PublishInfo,\n VoidCallback,\n} from 'hap-nodejs'\n\nimport type {\n AccessoryIdentifier,\n AccessoryName,\n AccessoryPlugin,\n HomebridgeAPI,\n PlatformIdentifier,\n PlatformName,\n PluginIdentifier,\n StaticPlatformPlugin,\n} from './api.js'\nimport type { ExternalPortsConfiguration, ExternalPortService } from './externalPortService.js'\nimport type { Logging } from './logger.js'\nimport type { SerializedPlatformAccessory } from './platformAccessory.js'\nimport type { Plugin } from './plugin.js'\nimport type { HomebridgeOptions } from './server.js'\n\nimport {\n Accessory,\n AccessoryEventTypes,\n Bridge,\n Categories,\n Characteristic,\n CharacteristicEventTypes,\n CharacteristicWarningType,\n HAPLibraryVersion,\n once,\n Service,\n uuid,\n} from 'hap-nodejs'\n\nimport { InternalAPIEvent } from './api.js'\nimport { getLogPrefix, Logger } from './logger.js'\nimport { PlatformAccessory } from './platformAccessory.js'\nimport { PluginManager } from './pluginManager.js'\nimport { StorageService } from './storageService.js'\nimport { generate } from './util/mac.js'\nimport getVersion from './version.js'\n\nconst log = Logger.internal\n\nexport interface BridgeConfiguration {\n name: string\n username: MacAddress\n pin: string // format like \"000-00-000\"\n advertiser?: MDNSAdvertiser\n port?: number\n bind?: (InterfaceName | IPAddress) | (InterfaceName | IPAddress)[]\n setupID?: string[4]\n manufacturer?: string\n model?: string\n disableIpc?: boolean\n firmwareRevision?: string\n env?: {\n DEBUG?: string\n NODE_OPTIONS?: string\n }\n}\n\nexport interface AccessoryConfig extends Record<string, any> {\n accessory: AccessoryName | AccessoryIdentifier\n name: string\n uuid_base?: string\n _bridge?: BridgeConfiguration\n}\n\nexport interface PlatformConfig extends Record<string, any> {\n platform: PlatformName | PlatformIdentifier\n name?: string\n _bridge?: BridgeConfiguration\n}\n\nexport interface HomebridgeConfig {\n bridge: BridgeConfiguration\n\n accessories: AccessoryConfig[]\n platforms: PlatformConfig[]\n\n plugins?: PluginIdentifier[] // array to define set of active plugins\n\n /**\n * Array of disabled plugins.\n * Unlike the plugins[] config which prevents plugins from being initialised at all, disabled plugins still have their alias loaded, so\n * we can match config blocks of disabled plugins and show an appropriate message in the logs.\n */\n disabledPlugins?: PluginIdentifier[]\n\n // This section is used to control the range of ports (inclusive) that separate accessory (like camera or television) should be bind to\n ports?: ExternalPortsConfiguration\n}\n\nexport interface BridgeOptions extends HomebridgeOptions {\n cachedAccessoriesDir: string\n cachedAccessoriesItemName: string\n}\n\nexport interface CharacteristicWarningOpts {\n ignoreSlow?: boolean\n}\n\nexport class BridgeService {\n public bridge: Bridge\n private storageService: StorageService\n\n private readonly allowInsecureAccess: boolean\n\n private cachedPlatformAccessories: PlatformAccessory[] = []\n private cachedAccessoriesFileLoaded = false\n private readonly publishedExternalAccessories: Map<MacAddress, PlatformAccessory> = new Map()\n\n constructor(\n private api: HomebridgeAPI,\n private pluginManager: PluginManager,\n private externalPortService: ExternalPortService,\n private bridgeOptions: BridgeOptions,\n private bridgeConfig: BridgeConfiguration,\n private config: HomebridgeConfig,\n ) {\n this.storageService = new StorageService(this.bridgeOptions.cachedAccessoriesDir)\n this.storageService.initSync()\n\n // Server is \"secure by default\", meaning it creates a top-level Bridge accessory that\n // will not allow unauthenticated requests. This matches the behavior of actual HomeKit\n // accessories. However, you can set this to true to allow all requests without authentication,\n // which can be useful for easy hacking. Note that this will expose all functions of your\n // bridged accessories, like changing characteristics (i.e. flipping your lights on and off).\n this.allowInsecureAccess = this.bridgeOptions.insecureAccess || false\n\n this.api.on(InternalAPIEvent.REGISTER_PLATFORM_ACCESSORIES, this.handleRegisterPlatformAccessories.bind(this))\n this.api.on(InternalAPIEvent.UPDATE_PLATFORM_ACCESSORIES, this.handleUpdatePlatformAccessories.bind(this))\n this.api.on(InternalAPIEvent.UNREGISTER_PLATFORM_ACCESSORIES, this.handleUnregisterPlatformAccessories.bind(this))\n this.api.on(InternalAPIEvent.PUBLISH_EXTERNAL_ACCESSORIES, this.handlePublishExternalAccessories.bind(this))\n\n this.bridge = new Bridge(bridgeConfig.name, uuid.generate('HomeBridge'))\n this.bridge.on(AccessoryEventTypes.CHARACTERISTIC_WARNING, () => {\n // We register characteristic warning handlers on every bridged accessory (to have a reference to the plugin).\n // For Bridges the warnings will propagate to the main Bridge accessory, thus we need to silence them here.\n // Otherwise, those would be printed twice (by us and HAP-NodeJS as it detects no handlers on the bridge).\n })\n }\n\n // characteristic warning event has additional parameter originatorChain: string[] which is currently unused\n public static printCharacteristicWriteWarning(plugin: Plugin, accessory: Accessory, opts: CharacteristicWarningOpts, warning: CharacteristicWarning): void {\n const wikiInfo = 'See https://homebridge.io/w/JtMGR for more info.'\n switch (warning.type) {\n case CharacteristicWarningType.SLOW_READ:\n case CharacteristicWarningType.SLOW_WRITE:\n if (!opts.ignoreSlow) {\n log.info(getLogPrefix(plugin.getPluginIdentifier()), 'This plugin slows down Homebridge.', warning.message, wikiInfo)\n }\n break\n case CharacteristicWarningType.TIMEOUT_READ:\n case CharacteristicWarningType.TIMEOUT_WRITE:\n log.error(getLogPrefix(plugin.getPluginIdentifier()), 'This plugin slows down Homebridge.', warning.message, wikiInfo)\n break\n case CharacteristicWarningType.WARN_MESSAGE:\n log.info(getLogPrefix(plugin.getPluginIdentifier()), `This plugin generated a warning from the characteristic '${warning.characteristic.displayName}':`, `${warning.message}.`, wikiInfo)\n break\n case CharacteristicWarningType.ERROR_MESSAGE:\n log.error(getLogPrefix(plugin.getPluginIdentifier()), `This plugin threw an error from the characteristic '${warning.characteristic.displayName}':`, `${warning.message}.`, wikiInfo)\n break\n case CharacteristicWarningType.DEBUG_MESSAGE:\n log.debug(getLogPrefix(plugin.getPluginIdentifier()), `Characteristic '${warning.characteristic.displayName}':`, `${warning.message}.`, wikiInfo)\n break\n default: // generic message for yet unknown types\n log.info(getLogPrefix(plugin.getPluginIdentifier()), `This plugin generated a warning from the characteristic '${warning.characteristic.displayName}':`, `${warning.message}.`, wikiInfo)\n break\n }\n if (warning.stack) {\n log.debug(getLogPrefix(plugin.getPluginIdentifier()), warning.stack)\n }\n }\n\n public publishBridge(): void {\n const bridgeConfig = this.bridgeConfig\n\n const info = this.bridge.getService(Service.AccessoryInformation)!\n info.setCharacteristic(Characteristic.Manufacturer, bridgeConfig.manufacturer || 'homebridge.io')\n info.setCharacteristic(Characteristic.Model, bridgeConfig.model || 'homebridge')\n info.setCharacteristic(Characteristic.SerialNumber, bridgeConfig.username)\n info.setCharacteristic(Characteristic.FirmwareRevision, bridgeConfig.firmwareRevision || getVersion())\n\n this.bridge.on(AccessoryEventTypes.LISTENING, (port: number) => {\n log.success('Homebridge v%s (HAP v%s) (%s) is running on port %s.', getVersion(), HAPLibraryVersion(), bridgeConfig.name, port)\n })\n\n const publishInfo: PublishInfo = {\n username: bridgeConfig.username,\n port: bridgeConfig.port,\n pincode: bridgeConfig.pin,\n category: Categories.BRIDGE,\n bind: bridgeConfig.bind,\n addIdentifyingMaterial: true,\n advertiser: bridgeConfig.advertiser,\n }\n\n if (bridgeConfig.setupID && bridgeConfig.setupID.length === 4) {\n publishInfo.setupID = bridgeConfig.setupID\n }\n\n log.debug('Publishing bridge accessory (name: %s, publishInfo: %o).', this.bridge.displayName, BridgeService.strippingPinCode(publishInfo))\n this.bridge.publish(publishInfo, this.allowInsecureAccess)\n }\n\n /**\n * Attempt to load the cached accessories from disk.\n */\n public async loadCachedPlatformAccessoriesFromDisk(): Promise<void> {\n let cachedAccessories: SerializedPlatformAccessory[] | null = null\n\n try {\n cachedAccessories = await this.storageService.getItem<SerializedPlatformAccessory[]>(this.bridgeOptions.cachedAccessoriesItemName)\n } catch (error: any) {\n log.error('Failed to load cached accessories from disk:', error.message)\n if (error instanceof SyntaxError) {\n // syntax error probably means invalid json / corrupted file; try and restore from backup\n cachedAccessories = await this.restoreCachedAccessoriesBackup()\n } else {\n log.error('Not restoring cached accessories - some accessories may be reset.')\n }\n }\n\n if (cachedAccessories) {\n log.info(`Loaded ${cachedAccessories.length} cached accessories from ${this.bridgeOptions.cachedAccessoriesItemName}.`)\n\n this.cachedPlatformAccessories = cachedAccessories.map((serialized) => {\n return PlatformAccessory.deserialize(serialized)\n })\n\n if (cachedAccessories.length) {\n // create a backup of the cache file\n await this.createCachedAccessoriesBackup()\n }\n }\n\n this.cachedAccessoriesFileLoaded = true\n }\n\n /**\n * Return the name of the backup cache file\n */\n private get backupCacheFileName() {\n return `.${this.bridgeOptions.cachedAccessoriesItemName}.bak`\n }\n\n /**\n * Create a backup of the cached file\n * This is used if we ever have trouble reading the main cache file\n */\n private async createCachedAccessoriesBackup(): Promise<void> {\n try {\n await this.storageService.copyItem(this.bridgeOptions.cachedAccessoriesItemName, this.backupCacheFileName)\n } catch (error: any) {\n log.warn(`Failed to create a backup of the ${this.bridgeOptions.cachedAccessoriesItemName} cached accessories file:`, error.message)\n }\n }\n\n /**\n * Restore a cached accessories backup\n * This is used if the main cache file has a JSON syntax error / is corrupted\n */\n private async restoreCachedAccessoriesBackup(): Promise<SerializedPlatformAccessory[] | null> {\n try {\n const cachedAccessories = await this.storageService.getItem<SerializedPlatformAccessory[]>(this.backupCacheFileName)\n if (cachedAccessories && cachedAccessories.length) {\n log.warn(`Recovered ${cachedAccessories.length} accessories from ${this.bridgeOptions.cachedAccessoriesItemName} cache backup.`)\n }\n return cachedAccessories\n } catch (error: any) {\n return null\n }\n }\n\n public restoreCachedPlatformAccessories(): void {\n this.cachedPlatformAccessories = this.cachedPlatformAccessories.filter((accessory) => {\n let plugin = this.pluginManager.getPlugin(accessory._associatedPlugin!)\n if (!plugin) { // a little explainer here. This section is basically here to resolve plugin name changes of dynamic platform plugins\n try {\n // resolve platform accessories by searching for plugins which registered a dynamic platform for the given name\n plugin = this.pluginManager.getPluginByActiveDynamicPlatform(accessory._associatedPlatform!)\n\n if (plugin) { // if it's undefined the no plugin was found\n // could improve on this by calculating the Levenshtein distance to only allow platform ownership changes\n // when something like a typo happened. Are there other reasons the name could change?\n // And how would we define the threshold?\n\n log.info(`When searching for the associated plugin of the accessory '${accessory.displayName}' `\n + `it seems like the plugin name changed from '${accessory._associatedPlugin}' to '${\n plugin.getPluginIdentifier()}'. Plugin association is now being transformed!`)\n\n accessory._associatedPlugin = plugin.getPluginIdentifier() // update the associated plugin to the new one\n }\n } catch (error: any) { // error is thrown if multiple plugins where found for the given platform name\n log.info(`Could not find the associated plugin for the accessory '${accessory.displayName}'. `\n + `Tried to find the plugin by the platform name but ${error.message}`)\n }\n }\n\n const platformPlugins = plugin && plugin.getActiveDynamicPlatform(accessory._associatedPlatform!)\n if (plugin) {\n accessory._associatedHAPAccessory.on(AccessoryEventTypes.CHARACTERISTIC_WARNING, BridgeService.printCharacteristicWriteWarning.bind(this, plugin, accessory._associatedHAPAccessory, {}))\n }\n\n if (!platformPlugins) {\n log.info(`Failed to find plugin to handle accessory ${accessory._associatedHAPAccessory.displayName}`)\n if (!this.bridgeOptions.keepOrphanedCachedAccessories) {\n log.info(`Removing orphaned accessory ${accessory._associatedHAPAccessory.displayName}`)\n return false // filter it from the list\n }\n } else {\n // We set a placeholder for FirmwareRevision before configureAccessory is called so the plugin has the opportunity to override it.\n accessory.getService(Service.AccessoryInformation)?.setCharacteristic(Characteristic.FirmwareRevision, '0')\n platformPlugins.configureAccessory(accessory)\n }\n\n try {\n this.bridge.addBridgedAccessory(accessory._associatedHAPAccessory)\n } catch (error: any) {\n log.warn(`${accessory._associatedPlugin ? getLogPrefix(accessory._associatedPlugin) : ''} Could not restore cached accessory '${accessory._associatedHAPAccessory.displayName}':`, error.message)\n return false // filter it from the list\n }\n return true // keep it in the list\n })\n }\n\n /**\n * Save the cached accessories back to disk.\n */\n public saveCachedPlatformAccessoriesOnDisk(): void {\n try {\n // only save the cache file back to disk if we have already attempted to load it\n // this should prevent the cache being deleted should homebridge be shutdown before it has finished launching\n if (this.cachedAccessoriesFileLoaded) {\n const serializedAccessories = this.cachedPlatformAccessories.map(accessory => PlatformAccessory.serialize(accessory))\n this.storageService.setItemSync(this.bridgeOptions.cachedAccessoriesItemName, serializedAccessories)\n }\n } catch (error: any) {\n log.error('Failed to save cached accessories to disk:', error.message)\n log.error('Your accessories will not persist between restarts until this issue is resolved.')\n }\n }\n\n handleRegisterPlatformAccessories(accessories: PlatformAccessory[]): void {\n const hapAccessories = accessories.map((accessory) => {\n this.cachedPlatformAccessories.push(accessory)\n\n const plugin = this.pluginManager.getPlugin(accessory._associatedPlugin!)\n if (plugin) {\n const platforms = plugin.getActiveDynamicPlatform(accessory._associatedPlatform!)\n\n if (!platforms) {\n log.warn('The plugin \\'%s\\' registered a new accessory for the platform \\'%s\\'. The platform couldn\\'t be found though!', accessory._associatedPlugin!, accessory._associatedPlatform!)\n }\n\n accessory._associatedHAPAccessory.on(AccessoryEventTypes.CHARACTERISTIC_WARNING, BridgeService.printCharacteristicWriteWarning.bind(this, plugin, accessory._associatedHAPAccessory, {}))\n } else {\n log.warn('A platform configured a new accessory under the plugin name \\'%s\\'. However no loaded plugin could be found for the name!', accessory._associatedPlugin)\n }\n\n return accessory._associatedHAPAccessory\n })\n\n this.bridge.addBridgedAccessories(hapAccessories)\n this.saveCachedPlatformAccessoriesOnDisk()\n }\n\n handleUpdatePlatformAccessories(): void {\n // Update persisted accessories\n this.saveCachedPlatformAccessoriesOnDisk()\n }\n\n handleUnregisterPlatformAccessories(accessories: PlatformAccessory[]): void {\n const hapAccessories = accessories.map((accessory) => {\n const index = this.cachedPlatformAccessories.indexOf(accessory)\n if (index >= 0) {\n this.cachedPlatformAccessories.splice(index, 1)\n }\n\n return accessory._associatedHAPAccessory\n })\n\n this.bridge.removeBridgedAccessories(hapAccessories)\n this.saveCachedPlatformAccessoriesOnDisk()\n }\n\n async handlePublishExternalAccessories(accessories: PlatformAccessory[]): Promise<void> {\n const accessoryPin = this.bridgeConfig.pin\n\n for (const accessory of accessories) {\n const hapAccessory = accessory._associatedHAPAccessory\n const advertiseAddress = generate(hapAccessory.UUID)\n\n // get external port allocation\n const accessoryPort = await this.externalPortService.requestPort(advertiseAddress)\n\n if (this.publishedExternalAccessories.has(advertiseAddress)) {\n throw new Error(`Accessory ${hapAccessory.displayName} experienced an address collision.`)\n } else {\n this.publishedExternalAccessories.set(advertiseAddress, accessory)\n }\n\n const plugin = this.pluginManager.getPlugin(accessory._associatedPlugin!)\n if (plugin) {\n hapAccessory.on(AccessoryEventTypes.CHARACTERISTIC_WARNING, BridgeService.printCharacteristicWriteWarning.bind(this, plugin, hapAccessory, { ignoreSlow: true }))\n } else if (PluginManager.isQualifiedPluginIdentifier(accessory._associatedPlugin!)) {\n // we did already complain in api.ts if it wasn't a qualified name\n log.warn('A platform configured a external accessory under the plugin name \\'%s\\'. However no loaded plugin could be found for the name!', accessory._associatedPlugin)\n }\n\n hapAccessory.on(AccessoryEventTypes.LISTENING, (port: number) => {\n log.success('%s is running on port %s.', hapAccessory.displayName, port)\n log.info('Please add [%s] manually in Home app. Setup Code: %s', hapAccessory.displayName, accessoryPin)\n })\n\n const publishInfo: PublishInfo = {\n username: advertiseAddress,\n pincode: accessoryPin,\n category: accessory.category,\n port: accessoryPort,\n bind: this.bridgeConfig.bind,\n addIdentifyingMaterial: true,\n advertiser: this.bridgeConfig.advertiser,\n }\n\n log.debug('Publishing external accessory (name: %s, publishInfo: %o).', hapAccessory.displayName, BridgeService.strippingPinCode(publishInfo))\n hapAccessory.publish(publishInfo, this.allowInsecureAccess)\n }\n }\n\n public createHAPAccessory(plugin: Plugin, accessoryInstance: AccessoryPlugin, displayName: string, accessoryType: AccessoryName | AccessoryIdentifier, uuidBase?: string): Accessory | undefined {\n const services = (accessoryInstance.getServices() || [])\n .filter(service => !!service) // filter out undefined values; a common mistake\n const controllers = ((accessoryInstance.getControllers && accessoryInstance.getControllers()) || [])\n .filter(controller => !!controller)\n\n if (services.length === 0 && controllers.length === 0) { // check that we only add valid accessory with at least one service\n return undefined\n }\n\n // The returned \"services\" for this accessory are simply an array of new-API-style\n // Service instances which we can add to a created HAP-NodeJS Accessory directly.\n const accessoryUUID = uuid.generate(`${accessoryType}:${uuidBase || displayName}`)\n const accessory = new Accessory(displayName, accessoryUUID)\n\n // listen for the identify event if the accessory instance has defined an identify() method\n if (accessoryInstance.identify) {\n accessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => {\n // @ts-expect-error: empty callback for backwards compatibility\n accessoryInstance.identify!(() => {})\n callback()\n })\n }\n\n const informationService = accessory.getService(Service.AccessoryInformation)!\n services.forEach((service) => {\n // if you returned an AccessoryInformation service, merge its values with ours\n if (service instanceof Service.AccessoryInformation) {\n service.setCharacteristic(Characteristic.Name, displayName) // ensure display name is set\n // ensure the plugin has not hooked already some listeners (some weird ones do).\n // Otherwise, they would override our identify listener registered by the HAP-NodeJS accessory\n service.getCharacteristic(Characteristic.Identify).removeAllListeners(CharacteristicEventTypes.SET)\n\n // pull out any values and listeners (get and set) you may have defined\n informationService.replaceCharacteristicsFromService(service)\n } else {\n accessory.addService(service)\n }\n })\n\n accessory.on(AccessoryEventTypes.CHARACTERISTIC_WARNING, BridgeService.printCharacteristicWriteWarning.bind(this, plugin, accessory, {}))\n\n controllers.forEach((controller) => {\n accessory.configureController(controller)\n })\n\n return accessory\n }\n\n public async loadPlatformAccessories(plugin: Plugin, platformInstance: StaticPlatformPlugin, platformType: PlatformName | PlatformIdentifier, logger: Logging): Promise<void> {\n // Plugin 1.0, load accessories\n return new Promise((resolve) => {\n // warn the user if the static platform is blocking the startup of Homebridge for to long\n const loadDelayWarningInterval = setInterval(() => {\n log.warn(getLogPrefix(plugin.getPluginIdentifier()), 'This plugin is taking long time to load and preventing Homebridge from starting. See https://homebridge.io/w/JtMGR for more info.')\n }, 20000)\n\n platformInstance.accessories(once((accessories: AccessoryPlugin[]) => {\n // clear the load delay warning interval\n clearInterval(loadDelayWarningInterval)\n\n // loop through accessories adding them to the list and registering them\n accessories.forEach((accessoryInstance, index) => {\n // @ts-expect-error: assume this property was set\n const accessoryName = accessoryInstance.name\n\n // @ts-expect-error: optional base uuid\n const uuidBase: string | undefined = accessoryInstance.uuid_base\n\n log.info('Initializing platform accessory \\'%s\\'...', accessoryName)\n\n const accessory = this.createHAPAccessory(plugin, accessoryInstance, accessoryName, platformType, uuidBase)\n\n if (accessory) {\n this.bridge.addBridgedAccessory(accessory)\n } else {\n logger('Platform %s returned an accessory at index %d with an empty set of services. Won\\'t adding it to the bridge!', platformType, index)\n }\n })\n\n resolve()\n }))\n })\n }\n\n teardown(): void {\n this.bridge.unpublish()\n for (const accessory of this.publishedExternalAccessories.values()) {\n accessory._associatedHAPAccessory.unpublish()\n }\n\n this.saveCachedPlatformAccessoriesOnDisk()\n\n this.api.signalShutdown()\n }\n\n private static strippingPinCode(publishInfo: PublishInfo): PublishInfo {\n const info = {\n ...publishInfo,\n }\n info.pincode = '***-**-***'\n return info\n }\n}\n", "import path from 'node:path'\n\nimport fs from 'fs-extra'\n\nexport class StorageService {\n constructor(\n public baseDirectory: string,\n ) {}\n\n public initSync(): void {\n return fs.ensureDirSync(this.baseDirectory)\n }\n\n public getItemSync<T>(itemName: string): T | null {\n const filePath = path.resolve(this.baseDirectory, itemName)\n\n if (!fs.pathExistsSync(filePath)) {\n return null\n }\n\n return fs.readJsonSync(filePath)\n }\n\n public async getItem<T>(itemName: string): Promise<T | null> {\n const filePath = path.resolve(this.baseDirectory, itemName)\n\n if (!await fs.pathExists(filePath)) {\n return null\n }\n\n return await fs.readJson(filePath)\n }\n\n public setItemSync(itemName: string, data: Record<any, any> | Array<any>): void {\n return fs.writeJsonSync(path.resolve(this.baseDirectory, itemName), data)\n }\n\n public setItem(itemName: string, data: Record<any, any> | Array<any>): Promise<void> {\n return fs.writeJson(path.resolve(this.baseDirectory, itemName), data)\n }\n\n public copyItem(srcItemName: string, destItemName: string): Promise<void> {\n return fs.copyFile(path.resolve(this.baseDirectory, srcItemName), path.resolve(this.baseDirectory, destItemName))\n }\n\n public copyItemSync(srcItemName: string, destItemName: string): void {\n return fs.copyFileSync(path.resolve(this.baseDirectory, srcItemName), path.resolve(this.baseDirectory, destItemName))\n }\n\n public removeItemSync(itemName: string): void {\n return fs.removeSync(path.resolve(this.baseDirectory, itemName))\n }\n}\n", "/* global NodeJS */\n\nimport type { Buffer } from 'node:buffer'\n\nimport crypto from 'node:crypto'\n\nconst validMac = /^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$/\n\nexport type MacAddress = string\n\nexport function validMacAddress(address: string): boolean {\n return validMac.test(address)\n}\n\nexport function generate(data: string | Buffer | NodeJS.TypedArray | DataView): MacAddress {\n const sha1sum = crypto.createHash('sha1')\n sha1sum.update(data)\n const s = sha1sum.digest('hex')\n\n let i = 0\n return 'xx:xx:xx:xx:xx:xx'.replace(/x/g, () => s[i++]).toUpperCase()\n}\n", "import type { MacAddress } from 'hap-nodejs'\n\nimport type { HomebridgeAPI } from './api.js'\nimport type {\n AccessoryConfig,\n BridgeConfiguration,\n BridgeOptions,\n HomebridgeConfig,\n PlatformConfig,\n} from './bridgeService.js'\nimport type { ExternalPortService } from './externalPortService.js'\nimport type { IpcService } from './ipcService.js'\nimport type { Logging } from './logger.js'\nimport type { Plugin } from './plugin.js'\nimport type { HomebridgeOptions } from './server.js'\n\nimport child_process from 'node:child_process'\nimport path, { dirname } from 'node:path'\nimport process from 'node:process'\nimport { fileURLToPath } from 'node:url'\n\nimport fs from 'fs-extra'\n\nimport { PluginType } from './api.js'\nimport { IpcOutgoingEvent } from './ipcService.js'\nimport { Logger } from './logger.js'\nimport { User } from './user.js'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n// eslint-disable-next-line no-restricted-syntax\nexport const enum ChildProcessMessageEventType {\n /**\n * Sent from the child process when it is ready to accept config\n */\n READY = 'ready',\n\n /**\n * Sent to the child process with a ChildProcessLoadEventData payload\n */\n LOAD = 'load',\n\n /**\n * Sent from the child process once it has loaded the plugin\n */\n LOADED = 'loaded',\n\n /**\n * Sent to the child process telling it to start\n */\n START = 'start',\n\n /**\n * Sent from the child process when the bridge is online\n */\n ONLINE = 'online',\n\n /**\n * Sent from the child when it wants to request port allocation for an external accessory\n */\n PORT_REQUEST = 'portRequest',\n\n /**\n * Sent from the parent with the port allocation response\n */\n PORT_ALLOCATED = 'portAllocated',\n\n /**\n * Sent from the child to update its current status\n */\n STATUS_UPDATE = 'status',\n}\n\n// eslint-disable-next-line no-restricted-syntax\nexport const enum ChildBridgeStatus {\n /**\n * When the child bridge is loading, or restarting\n */\n PENDING = 'pending',\n\n /**\n * The child bridge is online and has published it's accessory\n */\n OK = 'ok',\n\n /**\n * The bridge is shutting down, or the process ended unexpectedly\n */\n DOWN = 'down',\n}\n\nexport interface ChildProcessMessageEvent<T> {\n id: ChildProcessMessageEventType\n data?: T\n}\n\nexport interface ChildProcessLoadEventData {\n type: PluginType\n identifier: string\n pluginPath: string\n pluginConfig: Array<PlatformConfig | AccessoryConfig>\n bridgeConfig: BridgeConfiguration\n homebridgeConfig: HomebridgeConfig\n bridgeOptions: BridgeOptions\n}\n\nexport interface ChildProcessPluginLoadedEventData {\n version: string\n}\n\nexport interface ChildProcessPortRequestEventData {\n username: MacAddress\n}\n\nexport interface ChildProcessPortAllocatedEventData {\n username: MacAddress\n port?: number\n}\n\nexport interface ChildBridgePairedStatusEventData {\n paired: boolean | null\n setupUri: string | null\n}\n\nexport interface ChildMetadata {\n status: ChildBridgeStatus\n paired?: boolean | null\n setupUri?: string | null\n username: MacAddress\n pin: string\n name: string\n plugin: string\n identifier: string\n manuallyStopped: boolean\n pid?: number\n}\n\n/**\n * Manages the child processes of platforms/accessories being exposed as separate forked bridges.\n * A child bridge runs a single platform or accessory.\n */\nexport class ChildBridgeService {\n private child?: child_process.ChildProcess\n private args: string[] = []\n private processEnv: child_process.ForkOptions = {}\n private shuttingDown = false\n private lastBridgeStatus: ChildBridgeStatus = ChildBridgeStatus.PENDING\n private pairedStatus: boolean | null = null\n private manuallyStopped = false\n private setupUri: string | null = null\n private pluginConfig: Array<PlatformConfig | AccessoryConfig> = []\n private log: Logging\n private displayName?: string\n\n constructor(\n public type: PluginType,\n public identifier: string,\n private plugin: Plugin,\n private bridgeConfig: BridgeConfiguration,\n private homebridgeConfig: HomebridgeConfig,\n private homebridgeOptions: HomebridgeOptions,\n private api: HomebridgeAPI,\n private ipcService: IpcService,\n private externalPortService: ExternalPortService,\n ) {\n this.log = Logger.withPrefix(this.plugin.getPluginIdentifier())\n this.api.on('shutdown', () => {\n this.shuttingDown = true\n this.teardown()\n })\n\n // make sure we don't hit the max listeners limit\n this.api.setMaxListeners(this.api.getMaxListeners() + 1)\n }\n\n /**\n * Start the child bridge service\n */\n public start(): void {\n this.setProcessFlags()\n this.setProcessEnv()\n this.startChildProcess()\n\n // set display name\n if (this.pluginConfig.length > 1 || this.pluginConfig.length === 0) {\n this.displayName = this.plugin.getPluginIdentifier()\n } else {\n this.displayName = this.pluginConfig[0]?.name || this.plugin.getPluginIdentifier()\n }\n\n // re-configured log with display name\n this.log = Logger.withPrefix(this.displayName)\n }\n\n /**\n * Add a config block to a child bridge.\n * Platform child bridges can only contain one config block.\n * @param config\n */\n public addConfig(config: PlatformConfig | AccessoryConfig): void {\n this.pluginConfig.push(config)\n }\n\n private get bridgeStatus(): ChildBridgeStatus {\n return this.lastBridgeStatus\n }\n\n private set bridgeStatus(value: ChildBridgeStatus) {\n this.lastBridgeStatus = value\n this.sendStatusUpdate()\n }\n\n /**\n * Start the child bridge process\n */\n private startChildProcess(): void {\n this.bridgeStatus = ChildBridgeStatus.PENDING\n\n this.child = child_process.fork(path.resolve(__dirname, 'childBridgeFork.js'), this.args, this.processEnv)\n\n this.child.stdout?.on('data', (data) => {\n process.stdout.write(data)\n })\n\n this.child.stderr?.on('data', (data) => {\n process.stderr.write(data)\n })\n\n this.child.on('exit', () => {\n this.log.warn('Child bridge process ended')\n })\n\n this.child.on('error', (e) => {\n this.bridgeStatus = ChildBridgeStatus.DOWN\n this.log.error('Child process error', e)\n })\n\n this.child.once('close', (code, signal) => {\n this.bridgeStatus = ChildBridgeStatus.DOWN\n this.handleProcessClose(code, signal)\n })\n\n // handle incoming ipc messages from the child process\n this.child.on('message', (message: ChildProcessMessageEvent<unknown>) => {\n if (typeof message !== 'object' || !message.id) {\n return\n }\n\n switch (message.id) {\n case ChildProcessMessageEventType.READY: {\n this.log(`Launched child bridge with PID ${this.child?.pid}`)\n this.loadPlugin()\n break\n }\n case ChildProcessMessageEventType.LOADED: {\n const version = (message.data as ChildProcessPluginLoadedEventData).version\n if (this.pluginConfig.length > 1) {\n this.log(`Loaded ${this.plugin.getPluginIdentifier()} v${version} child bridge successfully with ${this.pluginConfig.length} accessories`)\n } else {\n this.log(`Loaded ${this.plugin.getPluginIdentifier()} v${version} child bridge successfully`)\n }\n this.startBridge()\n break\n }\n case ChildProcessMessageEventType.ONLINE: {\n this.bridgeStatus = ChildBridgeStatus.OK\n break\n }\n case ChildProcessMessageEventType.PORT_REQUEST: {\n this.handlePortRequest(message.data as ChildProcessPortRequestEventData)\n break\n }\n case ChildProcessMessageEventType.STATUS_UPDATE: {\n this.pairedStatus = (message.data as ChildBridgePairedStatusEventData).paired\n this.setupUri = (message.data as ChildBridgePairedStatusEventData).setupUri\n this.sendStatusUpdate()\n break\n }\n }\n })\n }\n\n /**\n * Called when the child bridge process exits, if Homebridge is not shutting down, it will restart the process\n * @param code\n * @param signal\n */\n private handleProcessClose(code: number | null, signal: string | null): void {\n this.log(`Process Ended. Code: ${code}, Signal: ${signal}`)\n\n setTimeout(() => {\n if (!this.shuttingDown) {\n this.log('Restarting Process...')\n this.startChildProcess()\n }\n }, 7000)\n }\n\n /**\n * Helper function to send a message to the child process\n * @param type\n * @param data\n */\n private sendMessage<T = unknown>(type: ChildProcessMessageEventType, data?: T): void {\n if (this.child && this.child.connected) {\n this.child.send({\n id: type,\n data,\n })\n }\n }\n\n /**\n * Some plugins may make use of the homebridge process flags\n * These will be passed through to the forked process\n */\n private setProcessFlags(): void {\n if (this.homebridgeOptions.debugModeEnabled) {\n this.args.push('-D')\n }\n\n if (this.homebridgeOptions.forceColourLogging) {\n this.args.push('-C')\n }\n\n if (this.homebridgeOptions.insecureAccess) {\n this.args.push('-I')\n }\n\n if (this.homebridgeOptions.noLogTimestamps) {\n this.args.push('-T')\n }\n\n if (this.homebridgeOptions.keepOrphanedCachedAccessories) {\n this.args.push('-K')\n }\n\n if (this.homebridgeOptions.customStoragePath) {\n this.args.push('-U', this.homebridgeOptions.customStoragePath)\n }\n\n if (this.homebridgeOptions.customPluginPath) {\n this.args.push('-P', this.homebridgeOptions.customPluginPath)\n }\n }\n\n /**\n * Set environment variables for the child process\n */\n private setProcessEnv(): void {\n this.processEnv = {\n env: {\n ...process.env,\n DEBUG: `${process.env.DEBUG || ''} ${this.bridgeConfig.env?.DEBUG || ''}`.trim(),\n NODE_OPTIONS: `${process.env.NODE_OPTIONS || ''} ${this.bridgeConfig.env?.NODE_OPTIONS || ''}`.trim(),\n },\n silent: true,\n }\n }\n\n /**\n * Tell the child process to load the given plugin\n */\n private loadPlugin(): void {\n const bridgeConfig: BridgeConfiguration = {\n name: this.bridgeConfig.name || this.displayName || this.plugin.getPluginIdentifier(),\n port: this.bridgeConfig.port,\n username: this.bridgeConfig.username,\n advertiser: this.homebridgeConfig.bridge.advertiser,\n pin: this.bridgeConfig.pin || this.homebridgeConfig.bridge.pin,\n bind: this.homebridgeConfig.bridge.bind,\n setupID: this.bridgeConfig.setupID,\n manufacturer: this.bridgeConfig.manufacturer || this.homebridgeConfig.bridge.manufacturer,\n model: this.bridgeConfig.model || this.homebridgeConfig.bridge.model,\n firmwareRevision: this.bridgeConfig.firmwareRevision || this.homebridgeConfig.bridge.firmwareRevision,\n }\n\n const bridgeOptions: BridgeOptions = {\n cachedAccessoriesDir: User.cachedAccessoryPath(),\n cachedAccessoriesItemName: `cachedAccessories.${this.bridgeConfig.username.replace(/:/g, '').toUpperCase()}`,\n }\n\n // shallow copy the homebridge options to the bridge options object\n Object.assign(bridgeOptions, this.homebridgeOptions)\n\n this.sendMessage<ChildProcessLoadEventData>(ChildProcessMessageEventType.LOAD, {\n type: this.type,\n identifier: this.identifier,\n pluginPath: this.plugin.getPluginPath(),\n pluginConfig: this.pluginConfig,\n bridgeConfig,\n bridgeOptions,\n homebridgeConfig: { // need to break this out to avoid a circular structure to JSON from other plugins modifying their config at runtime.\n bridge: this.homebridgeConfig.bridge,\n ports: this.homebridgeConfig.ports,\n disabledPlugins: [], // not used by child bridges\n accessories: [], // not used by child bridges\n platforms: [], // not used by child bridges\n },\n })\n }\n\n /**\n * Tell the child bridge to start broadcasting\n */\n private startBridge(): void {\n this.sendMessage(ChildProcessMessageEventType.START)\n }\n\n /**\n * Handle external port requests from child\n */\n private async handlePortRequest(request: ChildProcessPortRequestEventData) {\n const port = await this.externalPortService.requestPort(request.username)\n this.sendMessage<ChildProcessPortAllocatedEventData>(ChildProcessMessageEventType.PORT_ALLOCATED, {\n username: request.username,\n port,\n })\n }\n\n /**\n * Send sigterm to the child bridge\n */\n private teardown(): void {\n if (this.child && this.child.connected) {\n this.bridgeStatus = ChildBridgeStatus.DOWN\n this.child.kill('SIGTERM')\n }\n }\n\n /**\n * Trigger sending child bridge metadata to the process parent via IPC\n */\n private sendStatusUpdate(): void {\n this.ipcService.sendMessage(IpcOutgoingEvent.CHILD_BRIDGE_STATUS_UPDATE, this.getMetadata())\n }\n\n /**\n * Restarts the child bridge process\n */\n public restartChildBridge(): void {\n if (this.manuallyStopped) {\n this.startChildBridge()\n } else {\n this.log.warn('Restarting child bridge...')\n this.refreshConfig()\n this.teardown()\n }\n }\n\n /**\n * Stops the child bridge, not starting it again\n */\n public stopChildBridge(): void {\n if (!this.shuttingDown) {\n this.log.warn('Stopping child bridge (will not restart)...')\n this.shuttingDown = true\n this.manuallyStopped = true\n this.child?.removeAllListeners('close')\n this.teardown()\n } else {\n this.log.warn('Bridge already shutting down or stopped.')\n }\n }\n\n /**\n * Starts the child bridge, only if it was manually stopped and is no longer running\n */\n public startChildBridge(): void {\n if (this.manuallyStopped && this.bridgeStatus === ChildBridgeStatus.DOWN && (!this.child || !this.child.connected)) {\n this.log.warn('Starting child bridge...')\n this.refreshConfig()\n this.startChildProcess()\n this.shuttingDown = false\n this.manuallyStopped = false\n } else {\n this.log.warn('Cannot start child bridge, it is still running or was not manually stopped')\n }\n }\n\n /**\n * Read the config.json file from disk and refresh the plugin config block for just this plugin\n */\n public async refreshConfig(): Promise<void> {\n try {\n const homebridgeConfig: HomebridgeConfig = await fs.readJson(User.configPath())\n\n if (this.type === PluginType.PLATFORM) {\n const config = homebridgeConfig.platforms?.filter(x => x.platform === this.identifier && x._bridge?.username === this.bridgeConfig.username)\n if (config.length) {\n this.pluginConfig = config\n this.bridgeConfig = this.pluginConfig[0]._bridge || this.bridgeConfig\n } else {\n this.log.warn('Platform config could not be found, using existing config.')\n }\n } else if (this.type === PluginType.ACCESSORY) {\n const config = homebridgeConfig.accessories?.filter(x => x.accessory === this.identifier && x._bridge?.username === this.bridgeConfig.username)\n if (config.length) {\n this.pluginConfig = config\n this.bridgeConfig = this.pluginConfig[0]._bridge || this.bridgeConfig\n } else {\n this.log.warn('Accessory config could not be found, using existing config.')\n }\n }\n } catch (error: any) {\n this.log.error('Failed to refresh plugin config:', error.message)\n }\n }\n\n /**\n * Returns metadata about this child bridge\n */\n public getMetadata(): ChildMetadata {\n return {\n status: this.bridgeStatus,\n paired: this.pairedStatus,\n setupUri: this.setupUri,\n username: this.bridgeConfig.username,\n pin: this.bridgeConfig.pin || this.homebridgeConfig.bridge.pin,\n name: this.bridgeConfig.name || this.displayName || this.plugin.getPluginIdentifier(),\n plugin: this.plugin.getPluginIdentifier(),\n identifier: this.identifier,\n pid: this.child?.pid,\n manuallyStopped: this.manuallyStopped,\n }\n }\n}\n", "import type { MacAddress } from 'hap-nodejs'\n\nimport type { ChildBridgeFork } from './childBridgeFork.js'\n\nimport { Logger } from './logger.js'\n\nexport interface ExternalPortsConfiguration {\n start: number\n end: number\n}\n\n/**\n * Allocates ports from the user defined `config.ports` option\n * This service is used to allocate ports for external accessories on the main bridge, and child bridges.\n */\nexport class ExternalPortService {\n private nextExternalPort?: number\n private allocatedPorts: Map<MacAddress, number | undefined> = new Map()\n\n constructor(\n private externalPorts?: ExternalPortsConfiguration,\n ) { }\n\n /**\n * Returns the next available port in the external port config.\n * If the external port is not configured by the user it will return null.\n * If the port range has been exhausted it will return null.\n */\n public async requestPort(username: MacAddress): Promise<number | undefined> {\n // check to see if this device has already requested an external port\n const existingPortAllocation = this.allocatedPorts.get(username)\n if (existingPortAllocation) {\n return existingPortAllocation\n }\n\n // get the next unused port\n const port = this.getNextFreePort()\n this.allocatedPorts.set(username, port)\n return port\n }\n\n private getNextFreePort(): number | undefined {\n if (!this.externalPorts) {\n return undefined\n }\n\n if (this.nextExternalPort === undefined) {\n this.nextExternalPort = this.externalPorts.start\n return this.nextExternalPort\n }\n\n this.nextExternalPort++\n\n if (this.nextExternalPort <= this.externalPorts.end) {\n return this.nextExternalPort\n }\n\n Logger.internal.warn('External port pool ran out of ports. Falling back to random port assignment.')\n\n return undefined\n }\n}\n\n/**\n * This is the child bridge version of the port allocation service.\n * It requests a free port from the main bridge's port service.\n */\nexport class ChildBridgeExternalPortService extends ExternalPortService {\n constructor(\n private childBridge: ChildBridgeFork,\n ) {\n super()\n }\n\n public async requestPort(username: MacAddress): Promise<number | undefined> {\n return await this.childBridge.requestExternalPort(username)\n }\n}\n"],
5
+ "mappings": "AAsBA,OAAOA,MAAa,eAEpB,OAAS,uBAAAC,EAAqB,cAAAC,OAAkB,aCnBhD,OAAS,gBAAAC,OAAoB,cAE7B,OAAOC,MAAe,aACtB,OAAOC,OAAY,SCRnB,OAAOC,MAAU,YAEjB,OAAOC,MAAW,QA4DX,IAAMC,EAAN,MAAMC,CAAO,CAClB,OAAuB,SAAW,IAAIA,EAEtC,OAAwB,YAAc,IAAI,IAC1C,OAAe,aAAe,GAC9B,OAAe,iBAAmB,GAEzB,OAET,YAAYC,EAAiB,CAC3B,KAAK,OAASA,CAChB,CAOA,OAAO,WAAWA,EAAyB,CACzC,IAAMC,EAAcF,EAAO,YAAY,IAAIC,CAAM,EAEjD,GAAIC,EACF,OAAOA,EACF,CACL,IAAMC,EAAS,IAAIH,EAAOC,CAAM,EAE1BG,EAA2BD,EAAO,KAAK,KAAKA,CAAM,EACxDC,EAAI,KAAOD,EAAO,KAClBC,EAAI,QAAUD,EAAO,QACrBC,EAAI,KAAOD,EAAO,KAClBC,EAAI,MAAQD,EAAO,MACnBC,EAAI,MAAQD,EAAO,MACnBC,EAAI,IAAMD,EAAO,IAEjBC,EAAI,OAASD,EAAO,OAGpB,IAAME,EAAmBD,EACzB,OAAAJ,EAAO,YAAY,IAAIC,EAAQI,CAAO,EAC/BA,CACT,CACF,CAOA,OAAc,gBAAgBC,EAAmB,GAAY,CAC3DN,EAAO,aAAeM,CACxB,CAOA,OAAc,oBAAoBA,EAAmB,GAAY,CAC/DN,EAAO,iBAAmBM,CAC5B,CAKA,OAAc,YAAmB,CAC/BC,EAAM,MAAQ,CAChB,CAEO,KAAKC,KAAoBC,EAAyB,CACvD,KAAK,IAAI,OAAeD,EAAS,GAAGC,CAAU,CAChD,CAEO,QAAQD,KAAoBC,EAAyB,CAC1D,KAAK,IAAI,UAAkBD,EAAS,GAAGC,CAAU,CACnD,CAEO,KAAKD,KAAoBC,EAAyB,CACvD,KAAK,IAAI,OAAeD,EAAS,GAAGC,CAAU,CAChD,CAEO,MAAMD,KAAoBC,EAAyB,CACxD,KAAK,IAAI,QAAgBD,EAAS,GAAGC,CAAU,CACjD,CAEO,MAAMD,KAAoBC,EAAyB,CACxD,KAAK,IAAI,QAAgBD,EAAS,GAAGC,CAAU,CACjD,CAEO,IAAIC,EAAiBF,KAAoBC,EAAyB,CACvE,GAAIC,IAAU,SAAkB,CAACV,EAAO,aACtC,OAGFQ,EAAUG,EAAK,OAAOH,EAAS,GAAGC,CAAU,EAE5C,IAAIG,EAAkB,QAAQ,IAC9B,OAAQF,EAAO,CACb,IAAK,UACHF,EAAUD,EAAM,MAAMC,CAAO,EAC7B,MACF,IAAK,OACHA,EAAUD,EAAM,OAAOC,CAAO,EAC9BI,EAAkB,QAAQ,MAC1B,MACF,IAAK,QACHJ,EAAUD,EAAM,IAAIC,CAAO,EAC3BI,EAAkB,QAAQ,MAC1B,MACF,IAAK,QACHJ,EAAUD,EAAM,KAAKC,CAAO,EAC5B,KACJ,CAMA,GAJI,KAAK,SACPA,EAAU,GAAGK,EAAa,KAAK,MAAM,CAAC,IAAIL,CAAO,IAG/CR,EAAO,iBAAkB,CAC3B,IAAMc,EAAO,IAAI,KACjBN,EAAUD,EAAM,MAAM,IAAIO,EAAK,eAAe,CAAC,IAAI,EAAIN,CACzD,CAEAI,EAAgBJ,CAAO,CACzB,CACF,EAMO,SAASK,EAAaZ,EAAwB,CACnD,OAAOM,EAAM,KAAK,IAAIN,CAAM,GAAG,CACjC,CCtLA,OAAS,gBAAAc,MAAoB,cAE7B,OACE,aAAAC,EACA,uBAAAC,GACA,cAAAC,OACK,aA2BA,IAAMC,EAAN,MAAMC,UAAqEC,CAAa,CAE7F,OAAe,kBAEf,kBACA,oBAEA,wBAGA,YACA,KACA,SACA,SAAsB,CAAC,EAMhB,QAAa,CAAC,EAErB,YAAYC,EAAqBC,EAAcC,EAAuB,CACpE,MAAM,EACN,KAAK,wBAA0BJ,EAAkB,kBAC7CA,EAAkB,kBAClB,IAAIK,EAAUH,EAAaC,CAAI,EAE/BC,IACF,KAAK,wBAAwB,SAAWA,GAG1C,KAAK,YAAc,KAAK,wBAAwB,YAChD,KAAK,KAAO,KAAK,wBAAwB,KACzC,KAAK,SAAWA,GAAYE,GAAW,MACvC,KAAK,SAAW,KAAK,wBAAwB,SAG7C,KAAK,wBAAwB,GAAGC,GAAoB,SAAU,CAACC,EAAiBC,IAA2B,CAEzG,KAAK,KAAK,WAAiCD,EAAQ,IAAM,CAAC,CAAC,EAC3DC,EAAS,CACX,CAAC,CACH,CAIO,WAAWC,KAAsCC,EAAiC,CAIvF,OAAO,KAAK,wBAAwB,WAAWD,EAAS,GAAGC,CAAe,CAC5E,CAEO,cAAcD,EAAwB,CAC3C,KAAK,wBAAwB,cAAcA,CAAO,CACpD,CAEO,WAA+CE,EAAuC,CAC3F,OAAO,KAAK,wBAAwB,WAAWA,CAAI,CACrD,CAEO,eAAmDT,EAAkBU,EAAsC,CAChH,OAAO,KAAK,wBAAwB,eAAeV,EAAMU,CAAO,CAClE,CAQO,oBAAoBC,EAAsD,CAC/E,KAAK,wBAAwB,oBAAoBA,CAAU,CAC7D,CAQO,iBAAiBA,EAA8B,CACpD,KAAK,wBAAwB,iBAAiBA,CAAU,CAC1D,CAGA,OAAO,UAAUC,EAA2D,CAC1E,MAAO,CACL,OAAQA,EAAU,kBAClB,SAAUA,EAAU,oBACpB,QAASA,EAAU,QACnB,GAAGV,EAAU,UAAUU,EAAU,uBAAuB,CAC1D,CACF,CAEA,OAAO,YAAYC,EAAsD,CACvE,IAAMD,EAAYV,EAAU,YAAYW,CAAI,EAE5ChB,EAAkB,kBAAoBe,EACtC,IAAME,EAAoB,IAAIjB,EAAkBe,EAAU,YAAaA,EAAU,IAAI,EACrF,OAAAf,EAAkB,kBAAoB,OAEtCiB,EAAkB,kBAAoBD,EAAK,OAC3CC,EAAkB,oBAAsBD,EAAK,SAC7CC,EAAkB,QAAUD,EAAK,QACjCC,EAAkB,SAAWD,EAAK,SAE3BC,CACT,CACF,EC9IA,OAAS,YAAAC,OAAgB,qBACzB,OAAOC,MAAQ,UACf,OAAS,iBAAAC,OAAqB,cAC9B,OAAOC,MAAU,YACjB,OAAOC,MAAa,eCDpB,OAAOC,OAAY,cACnB,OAAOC,OAAU,YACjB,OAAOC,MAAa,eACpB,OAAS,iBAAAC,OAAqB,WAE9B,OAAS,aAAAC,MAAiB,SCpB1B,OAAOC,OAAQ,UACf,OAAS,WAAAC,GAAS,QAAAC,OAAY,YAC9B,OAAS,iBAAAC,OAAqB,WAE9B,IAAMC,GAAaD,GAAc,YAAY,GAAG,EAC1CE,GAAYJ,GAAQG,EAAU,EAEpC,SAASE,IAAuB,CAC9B,IAAMC,EAAkBL,GAAKG,GAAW,iBAAiB,EACzD,OAAO,KAAK,MAAML,GAAG,aAAaO,EAAiB,CAAE,SAAU,MAAO,CAAC,CAAC,CAC1E,CAEe,SAARC,GAAsC,CAC3C,OAAOF,GAAgB,EAAE,OAC3B,CDYA,IAAMG,EAAMC,EAAO,SAKNC,EAAN,KAAa,CACD,WACA,MACA,WACA,MAEV,SAAW,GAGT,QACQ,KACT,YAMA,kBAES,sBAAwE,IAAI,IAC5E,oBAAoE,IAAI,IAExE,uBAAqE,IAAI,IAE1F,YAAYC,EAAkBC,EAAcC,EAA0BC,EAAgB,CAUpF,GATA,KAAK,WAAaH,EAClB,KAAK,MAAQG,EACb,KAAK,WAAaF,EAElB,KAAK,QAAUC,EAAY,SAAW,QACtC,KAAK,KAAO,GAIRA,EAAY,QAEd,GAAI,OAAOA,EAAY,SAAY,SACjC,KAAK,KAAOA,EAAY,YACnB,CAEL,IAAME,EAAUF,EAAY,QAAQ,QAAUA,EAAY,QAAQ,SAAWA,EAAY,QAAQ,MAAQA,EAAY,QAAQ,SAAWA,EAAY,QAAQ,GAAG,EAG3J,OAAOE,GAAY,SACjBA,EAAQ,OACV,KAAK,KAAOA,EAAQ,OAEpB,KAAK,KAAOA,EAAQ,SAAWA,EAAQ,MAAQA,EAAQ,QAGzD,KAAK,KAAOA,CAEhB,CAIG,KAAK,OACR,KAAK,KAAOF,EAAY,MAAQ,cAIlC,KAAK,MAAQ,KAAK,KAAK,SAAS,MAAM,GAAM,KAAK,KAAK,SAAS,KAAK,GAAKA,EAAY,OAAS,SAG1FA,EAAY,mBAAqB,CAACA,EAAY,SAAW,CAACA,EAAY,QAAQ,cAChFA,EAAY,QAAUA,EAAY,SAAW,CAAC,EAC9CA,EAAY,QAAQ,WAAaA,EAAY,iBAAiB,YAGhE,KAAK,YAAc,CACjB,QAASA,EAAY,QACrB,aAAcA,EAAY,YAC5B,CACF,CAEO,qBAAwC,CAC7C,OAAQ,KAAK,MAAQ,GAAG,KAAK,KAAK,IAAM,IAAM,KAAK,UACrD,CAEO,eAAwB,CAC7B,OAAO,KAAK,UACd,CAEO,kBAAkBF,EAAqBK,EAA+C,CAC3F,GAAI,KAAK,sBAAsB,IAAIL,CAAI,EACrC,MAAM,IAAI,MAAM,WAAW,KAAK,oBAAoB,CAAC,qCAAqCA,CAAI,sCAAsC,EAGjI,KAAK,UACRH,EAAI,KAAK,6BAAgC,GAAG,KAAK,oBAAoB,CAAC,IAAIG,CAAI,EAAE,EAGlF,KAAK,sBAAsB,IAAIA,EAAMK,CAAW,CAClD,CAEO,iBAAiBL,EAAoBK,EAA8C,CACxF,GAAI,KAAK,oBAAoB,IAAIL,CAAI,EACnC,MAAM,IAAI,MAAM,WAAW,KAAK,oBAAoB,CAAC,mCAAmCA,CAAI,sCAAsC,EAG/H,KAAK,UACRH,EAAI,KAAK,4BAA+B,GAAG,KAAK,oBAAoB,CAAC,IAAIG,CAAI,EAAE,EAGjF,KAAK,oBAAoB,IAAIA,EAAMK,CAAW,CAChD,CAEO,wBAAwBC,EAAsF,CACnH,IAAMN,EAAsBO,EAAc,iBAAiBD,CAAmB,EAExED,EAAc,KAAK,sBAAsB,IAAIL,CAAI,EACvD,GAAI,CAACK,EACH,MAAM,IAAI,MAAM,4BAA4BL,CAAI,uCAAuC,KAAK,oBAAoB,CAAC,IAAI,EAGvH,OAAOK,CACT,CAEO,uBAAuBG,EAAkF,CAC9G,IAAMR,EAAqBO,EAAc,gBAAgBC,CAAkB,EAErEH,EAAc,KAAK,oBAAoB,IAAIL,CAAI,EACrD,GAAI,CAACK,EACH,MAAM,IAAI,MAAM,2BAA2BL,CAAI,uCAAuC,KAAK,oBAAoB,CAAC,IAAI,EAItH,GAAI,KAAK,uBAAuB,IAAIA,CAAI,EACtC,MAAM,IAAI,MAAM,wBAAwBA,CAAI,oBAAoB,KAAK,oBAAoB,CAAC,2CAC1D,EAGlC,OAAOK,CACT,CAEO,sBAAsBG,EAAuDC,EAA6C,CAC/H,IAAMT,EAAqBO,EAAc,gBAAgBC,CAAkB,EAEvEE,EAAY,KAAK,uBAAuB,IAAIV,CAAI,EAC/CU,IACHA,EAAY,CAAC,EACb,KAAK,uBAAuB,IAAIV,EAAMU,CAAS,GAKjDA,EAAU,QAAQD,CAAc,CAClC,CAEO,yBAAyBE,EAA+D,CAC7F,IAAMD,EAAY,KAAK,uBAAuB,IAAIC,CAAY,EAE9D,OAAOD,GAAaA,EAAU,CAAC,CACjC,CAEA,MAAa,MAAsB,CACjC,IAAME,EAAU,KAAK,YAKrB,GAJAC,GAAOD,EAAS,mDAAmD,EACnE,KAAK,YAAc,OAGf,CAACA,EAAQ,SAAW,CAACA,EAAQ,QAAQ,WACvC,MAAM,IAAI,MAAM,UAAU,KAAK,UAAU,0DAA0D,EAGrG,IAAME,EAAkBF,EAAQ,QAAQ,WAClCG,EAAsBH,EAAQ,QAAQ,KAGvCI,EAAUC,EAAW,EAAGH,EAAiB,CAAE,kBAAmB,EAAK,CAAC,GAEvEjB,EAAI,MAAM,eAAe,KAAK,UAAU,sCAAsCiB,CAAe,6DACnDG,EAAW,CAAC,oJACc,EAIlEF,GAAuB,CAACC,EAAUE,EAAQ,QAASH,CAAmB,GACxElB,EAAI,KAAK,eAAe,KAAK,UAAU,iCAAiCkB,CAAmB,0DACpDG,EAAQ,OAAO,4FAA4F,EAGpJ,IAAMC,EAAeP,EAAQ,cAAgB,CAAC,GAC1CO,EAAa,YAAcA,EAAa,YAAY,IACtDtB,EAAI,MAAM,eAAe,KAAK,UAAU,2TAEwE,EAGlH,IAAMuB,EAAWnB,GAAK,KAAK,KAAK,WAAY,KAAK,IAAI,EAM/CoB,EAAgB,MAAM,OAAOC,GAAcF,CAAQ,EAAE,MAE3D,GAAI,OAAOC,GAAkB,WAC3B,KAAK,kBAAoBA,UAChBA,GAAiB,OAAOA,EAAc,SAAY,WAC3D,KAAK,kBAAoBA,EAAc,YAEvC,OAAM,IAAI,MAAM,UAAU,KAAK,UAAU,oDAAoD,CAEjG,CAEO,WAAWE,EAAgC,CAChD,GAAI,CAAC,KAAK,kBACR,MAAM,IAAI,MAAM,4DAA6D,EAG/E,OAAO,KAAK,kBAAkBA,CAAG,CACnC,CACF,ED5NA,IAAMC,EAAMC,EAAO,SACbC,GAAUC,GAAc,YAAY,GAAG,EACvCC,EAAQF,GAAQ,QAAQ,MAAM,EAAE,EA6CzBG,EAAN,MAAMC,CAAc,CAEzB,OAAwB,0BAA4B,sCAEnC,IAEA,YAA2B,IAAI,IAC/B,uBAAkC,GAClC,cACA,gBAEA,QAAyC,IAAI,IAE7C,4BAAuE,IAAI,IAC3E,qBAAqD,IAAI,IACzD,oBAAmD,IAAI,IAEhE,0BAER,YAAYC,EAAoBC,EAAgC,CAC9D,KAAK,IAAMD,EAEPC,IACEA,EAAQ,kBACV,KAAK,YAAY,IAAIC,EAAK,QAAQC,EAAQ,IAAI,EAAGF,EAAQ,gBAAgB,CAAC,EAG5E,KAAK,uBAAyBA,EAAQ,wBAA0B,GAEhE,KAAK,cAAgBA,EAAQ,cAC7B,KAAK,gBAAkB,MAAM,QAAQA,EAAQ,eAAe,EAAIA,EAAQ,gBAAkB,QAG5F,KAAK,IAAI,uBAAwC,KAAK,wBAAwB,KAAK,IAAI,CAAC,EACxF,KAAK,IAAI,sBAAuC,KAAK,uBAAuB,KAAK,IAAI,CAAC,CACxF,CAEA,OAAc,4BAA4BG,EAA6B,CACrE,OAAOL,EAAc,0BAA0B,KAAKK,CAAU,CAChE,CAEA,OAAc,kBAAkBC,EAA0B,CACxD,OAAOA,EAAK,MAAMN,EAAc,yBAAyB,EAAG,CAAC,CAC/D,CAEA,OAAc,mBAAmBM,EAAsB,CACrD,OAAOA,EAAK,MAAMN,EAAc,yBAAyB,EAAG,CAAC,CAC/D,CAEA,OAAc,iBAAiBK,EAAgD,CAC7E,OAAKA,EAAW,SAAS,GAAG,EAIrBA,EAAW,MAAM,GAAG,EAAE,CAAC,EAHrBA,CAIX,CAEA,OAAc,gBAAgBA,EAAoD,CAChF,OAAKA,EAAW,SAAS,GAAG,EAIrBA,EAAW,MAAM,GAAG,EAAE,CAAC,EAHrBA,CAIX,CAEA,OAAc,oBAAoBA,EAAwE,CACxG,OAAOA,EAAW,MAAM,GAAG,EAAE,CAAC,CAChC,CAEA,MAAa,4BAA4C,CACvDX,EAAI,KAAK,KAAK,EAEd,KAAK,qBAAqB,EAE1B,OAAW,CAACW,EAAYE,CAAM,IAAK,KAAK,QAAS,CAC/C,GAAI,CACF,MAAMA,EAAO,KAAK,CACpB,OAASC,EAAY,CACnBd,EAAI,MAAM,sBAAsB,EAChCA,EAAI,MAAM,wBAAwBW,CAAU,GAAG,EAC/CX,EAAI,MAAMc,EAAM,KAAK,EACrBd,EAAI,MAAM,sBAAsB,EAEhC,KAAK,QAAQ,OAAOW,CAAU,EAC9B,QACF,CAEI,KAAK,iBAAmB,KAAK,gBAAgB,SAASE,EAAO,oBAAoB,CAAC,IACpFA,EAAO,SAAW,IAGhBA,EAAO,SACTb,EAAI,KAAK,oBAAoBW,CAAU,IAAIE,EAAO,OAAO,EAAE,EAE3Db,EAAI,KAAK,kBAAkBW,CAAU,IAAIE,EAAO,OAAO,EAAE,EAG3D,MAAM,KAAK,iBAAiBA,EAAQF,CAAU,EAE9CX,EAAI,KAAK,KAAK,CAChB,CAEA,KAAK,0BAA4B,MACnC,CAEA,MAAa,iBAAiBa,EAAgBF,EAAmC,CAC/E,GAAI,CACF,KAAK,0BAA4BE,EACjC,MAAMA,EAAO,WAAW,KAAK,GAAG,CAClC,OAASC,EAAY,CACnBd,EAAI,MAAM,sBAAsB,EAChCA,EAAI,MAAM,6BAA6BW,CAAU,GAAG,EACpDX,EAAI,MAAMc,EAAM,KAAK,EACrBd,EAAI,MAAM,sBAAsB,EAEhC,KAAK,QAAQ,OAAOW,CAAU,CAChC,CACF,CAEQ,wBAAwBC,EAAqBG,EAAyCC,EAA2C,CACvI,GAAI,CAAC,KAAK,0BACR,MAAM,IAAI,MAAM,6CAA6CA,EAAmB,IAAIA,CAAgB,KAAO,EAAE,qDAAqD,EAGhKA,GAAoBA,IAAqB,KAAK,0BAA0B,oBAAoB,IAC9FhB,EAAI,KAAK,WAAW,KAAK,0BAA0B,oBAAoB,CAAC,6DAA6DgB,CAAgB,yCAAyC,EAC9L,KAAK,4BAA4B,IAAIA,EAAkB,KAAK,0BAA0B,oBAAoB,CAAC,GAG7G,KAAK,0BAA0B,kBAAkBJ,EAAMG,CAAW,EAElE,IAAIE,EAAU,KAAK,qBAAqB,IAAIL,CAAI,EAC3CK,IACHA,EAAU,CAAC,EACX,KAAK,qBAAqB,IAAIL,EAAMK,CAAO,GAE7CA,EAAQ,KAAK,KAAK,yBAAyB,CAC7C,CAEQ,uBAAuBL,EAAoBG,EAAwCC,EAA2C,CACpI,GAAI,CAAC,KAAK,0BACR,MAAM,IAAI,MAAM,4CAA4CA,EAAmB,IAAIA,CAAgB,KAAO,EAAE,qDAAqD,EAG/JA,GAAoBA,IAAqB,KAAK,0BAA0B,oBAAoB,IAC9FhB,EAAI,MAAM,WAAW,KAAK,0BAA0B,oBAAoB,CAAC,6DAA6DgB,CAAgB,yCAAyC,EAC/L,KAAK,4BAA4B,IAAIA,EAAkB,KAAK,0BAA0B,oBAAoB,CAAC,GAG7G,KAAK,0BAA0B,iBAAiBJ,EAAMG,CAAW,EAEjE,IAAIE,EAAU,KAAK,oBAAoB,IAAIL,CAAI,EAC1CK,IACHA,EAAU,CAAC,EACX,KAAK,oBAAoB,IAAIL,EAAMK,CAAO,GAE5CA,EAAQ,KAAK,KAAK,yBAAyB,CAC7C,CAEO,sBAAsBC,EAAkE,CAC7F,IAAIL,EACJ,GAAKK,EAAoB,SAAS,GAAG,EAkB9B,CACL,IAAMF,EAAmBV,EAAc,oBAAoBY,CAAmB,EAC9E,GAAI,CAAC,KAAK,oBAAoBF,CAAgB,EAC5C,MAAM,IAAI,MAAM,yBAAyBA,CAAgB,uBAAuB,EAGlFH,EAAS,KAAK,UAAUG,CAAgB,CAC1C,KAzBwC,CACtC,IAAIG,EAAQ,KAAK,qBAAqB,IAAID,CAAmB,EAE7D,GAAI,CAACC,EACH,MAAM,IAAI,MAAM,0CAA0CD,CAAmB,0FAA0F,EAGzK,GAAIC,EAAM,OAAS,EAAG,CACpB,IAAMX,EAAUW,EAAM,IAAIN,GAAU,GAAGA,EAAO,oBAAoB,CAAC,IAAIK,CAAmB,EAAE,EAAE,KAAK,IAAI,EAGvG,GADAC,EAAQA,EAAM,OAAON,GAAU,CAACA,EAAO,QAAQ,EAC3CM,EAAM,SAAW,EACnB,MAAM,IAAI,MAAM,4BAA4BD,CAAmB,oFAAoFV,CAAO,EAAE,CAEhK,CAEAK,EAASM,EAAM,CAAC,EAChBD,EAAsB,GAAGL,EAAO,oBAAoB,CAAC,IAAIK,CAAmB,EAC9E,CASA,OAAOL,CACT,CAEO,qBAAqBO,EAA+D,CACzF,IAAIP,EACJ,GAAKO,EAAmB,SAAS,GAAG,EAkB7B,CACL,IAAMJ,EAAmBV,EAAc,oBAAoBc,CAAkB,EAC7E,GAAI,CAAC,KAAK,oBAAoBJ,CAAgB,EAC5C,MAAM,IAAI,MAAM,yBAAyBA,CAAgB,uBAAuB,EAGlFH,EAAS,KAAK,UAAUG,CAAgB,CAC1C,KAzBuC,CACrC,IAAIG,EAAQ,KAAK,oBAAoB,IAAIC,CAAkB,EAE3D,GAAI,CAACD,EACH,MAAM,IAAI,MAAM,yCAAyCC,CAAkB,0FAA0F,EAGvK,GAAID,EAAM,OAAS,EAAG,CACpB,IAAMX,EAAUW,EAAM,IAAIN,GAAU,GAAGA,EAAO,oBAAoB,CAAC,IAAIO,CAAkB,EAAE,EAAE,KAAK,IAAI,EAGtG,GADAD,EAAQA,EAAM,OAAON,GAAU,CAACA,EAAO,QAAQ,EAC3CM,EAAM,SAAW,EACnB,MAAM,IAAI,MAAM,2BAA2BC,CAAkB,oFAAoFZ,CAAO,EAAE,CAE9J,CAEAK,EAASM,EAAM,CAAC,EAChBC,EAAqB,GAAGP,EAAO,oBAAoB,CAAC,IAAIO,CAAkB,EAC5E,CASA,OAAOP,CACT,CAEO,oBAAoBG,EAA6C,CACtE,OAAO,KAAK,QAAQ,IAAIA,CAAgB,GAAK,KAAK,4BAA4B,IAAIA,CAAgB,CACpG,CAEO,UAAUA,EAAwD,CACvE,IAAMH,EAAS,KAAK,QAAQ,IAAIG,CAAgB,EAChD,GAAIH,EACF,OAAOA,EACF,CACL,IAAMQ,EAAc,KAAK,4BAA4B,IAAIL,CAAgB,EACzE,GAAIK,EACF,OAAO,KAAK,QAAQ,IAAIA,CAAW,CAEvC,CAGF,CAEO,iCAAiCC,EAAgD,CACtF,IAAMH,GAAS,KAAK,oBAAoB,IAAIG,CAAY,GAAK,CAAC,GAC3D,OAAOT,GAAU,CAAC,CAACA,EAAO,yBAAyBS,CAAY,CAAC,EAEnE,GAAIH,EAAM,SAAW,EAEd,GAAIA,EAAM,OAAS,EAAG,CAC3B,IAAMF,EAAUE,EAAM,IAAIN,GAAUA,EAAO,oBAAoB,CAAC,EAAE,KAAK,IAAI,EAC3E,MAAM,IAAI,MAAM,IAAIS,CAAY,2EAA2EL,CAAO,EAAE,CACtH,KACE,QAAOE,EAAM,CAAC,CAElB,CAKQ,sBAA6B,CACnC,KAAK,iBAAiB,EAEtB,KAAK,YAAY,QAASI,GAAe,CACvC,GAAKC,EAAG,WAAWD,CAAU,EAI7B,GAAIC,EAAG,WAAWf,EAAK,KAAKc,EAAY,cAAc,CAAC,EACrD,GAAI,CACF,KAAK,WAAWA,CAAU,CAC5B,OAAST,EAAY,CACnBd,EAAI,KAAKc,EAAM,OAAO,CACxB,KACK,CACL,IAAMW,EAAsBD,EAAG,YAAYD,CAAU,EAClD,OAAQG,GAAiB,CACxB,GAAI,CACF,OAAOF,EAAG,SAASf,EAAK,QAAQc,EAAYG,CAAY,CAAC,EAAE,YAAY,CACzE,OAASZ,EAAY,CACnB,OAAAd,EAAI,MAAM,iBAAiBS,EAAK,QAAQc,EAAYG,CAAY,CAAC,MAAMZ,EAAM,OAAO,EAAE,EAC/E,EACT,CACF,CAAC,EAGHW,EAAoB,MAAM,EACvB,OAAOhB,GAAQA,EAAK,OAAO,CAAC,IAAM,GAAG,EACrC,QAASkB,GAAmB,CAE3B,IAAMC,EAAQH,EAAoB,QAAQE,CAAc,EACxDF,EAAoB,OAAOG,EAAO,CAAC,EAEnC,IAAMC,EAAepB,EAAK,KAAKc,EAAYI,CAAc,EACzDH,EAAG,YAAYK,CAAY,EACxB,OAAOjB,GAAQN,EAAc,4BAA4BM,CAAI,CAAC,EAC9D,OAAQA,GAAS,CAChB,GAAI,CACF,OAAOY,EAAG,SAASf,EAAK,QAAQoB,EAAcjB,CAAI,CAAC,EAAE,YAAY,CACnE,OAASE,EAAY,CACnB,OAAAd,EAAI,MAAM,iBAAiBS,EAAK,QAAQoB,EAAcjB,CAAI,CAAC,MAAME,EAAM,OAAO,EAAE,EACzE,EACT,CACF,CAAC,EACA,QAAQF,GAAQa,EAAoB,KAAK,GAAGE,CAAc,IAAIf,CAAI,EAAE,CAAC,CAC1E,CAAC,EAEHa,EACG,OAAQT,GACAV,EAAc,4BAA4BU,CAAgB,IAC3D,CAAC,KAAK,eAAiB,KAAK,cAAc,SAASA,CAAgB,EAC1E,EACA,QAASA,GAAqB,CAC7B,GAAI,CACF,IAAMa,EAAepB,EAAK,QAAQc,EAAYP,CAAgB,EAC9D,KAAK,WAAWa,CAAY,CAC9B,OAASf,EAAY,CACnBd,EAAI,KAAKc,EAAM,OAAO,CACxB,CACF,CAAC,CACL,CACF,CAAC,EAEG,KAAK,QAAQ,OAAS,GACxBd,EAAI,KAAK,mBAAmB,CAEhC,CAEO,WAAW6B,EAA8B,CAC9C,IAAMC,EAA2BxB,EAAc,gBAAgBuB,CAAY,EAErElB,EAA+BmB,EAAY,KAC3ClB,EAAmBN,EAAc,kBAAkBK,CAAU,EAC7DoB,EAAQzB,EAAc,mBAAmBK,CAAU,EAEnDqB,EAAmB,KAAK,QAAQ,IAAIrB,CAAU,EACpD,GAAIqB,EACF,MAAM,IAAI,MAAM,sCAAsCH,CAAY,mDAAmDG,EAAiB,cAAc,CAAC,IAAI,EAG3J,IAAMnB,EAAS,IAAIoB,EAAOrB,EAAMiB,EAAcC,EAAaC,CAAK,EAChE,YAAK,QAAQ,IAAIpB,EAAYE,CAAM,EAC5BA,CACT,CAEA,OAAe,gBAAgBqB,EAAiC,CAC9D,IAAMC,EAAkB1B,EAAK,KAAKyB,EAAY,cAAc,EACxDJ,EAEJ,GAAI,CAACN,EAAG,WAAWW,CAAe,EAChC,MAAM,IAAI,MAAM,UAAUD,CAAU,mCAAmC,EAGzE,GAAI,CACFJ,EAAc,KAAK,MAAMN,EAAG,aAAaW,EAAiB,CAAE,SAAU,MAAO,CAAC,CAAC,CACjF,OAASrB,EAAY,CACnB,MAAM,IAAI,MAAM,UAAUoB,CAAU,6CAA6CpB,CAAK,EAAE,CAC1F,CAEA,GAAI,CAACgB,EAAY,MAAQ,CAACxB,EAAc,4BAA4BwB,EAAY,IAAI,EAClF,MAAM,IAAI,MAAM,UAAUI,CAAU,sFAAsF,EAI5H,GAAI,CAACJ,EAAY,UAAY,CAACA,EAAY,SAAS,SAAS,mBAAmB,EAC7E,MAAM,IAAI,MAAM,UAAUI,CAAU,iEAAiE,EAGvG,OAAOJ,CACT,CAEQ,kBAAyB,CAC/B,GAAI,KAAK,uBAAwB,CAI3B,KAAK,YAAY,OAAS,GAC5B,KAAK,0BAA0B,EAEjC,MACF,CAEI1B,GAEFA,EAAM,QAAQK,GAAQ,KAAK,YAAY,IAAIA,CAAI,CAAC,EAS9CC,EAAQ,IAAI,UACdA,EAAQ,IAAI,UACT,MAAMD,EAAK,SAAS,EACpB,OAAOA,GAAQ,CAAC,CAACA,CAAI,EACrB,QAAQA,GAAQ,KAAK,YAAY,IAAIA,CAAI,CAAC,GAGzCC,EAAQ,WAAa,UACvB,KAAK,YAAY,IAAI,6BAA6B,EAClD,KAAK,YAAY,IAAI,uBAAuB,GAE9C,KAAK,0BAA0B,EAEnC,CAEQ,2BAAkC,CACpCA,EAAQ,WAAa,QACvB,KAAK,YAAY,IAAID,EAAK,KAAKC,EAAQ,IAAI,QAAU,kBAAkB,CAAC,EAExE,KAAK,YAAY,IAAI0B,GAAS,mDAAoD,CAChF,IAAK,OAAO,OAAO,CACjB,oBAAqB,SACrB,oBAAqB,OACvB,EAAG1B,EAAQ,GAAG,CAChB,CAAC,EAAE,SAAS,MAAM,CAAC,CAEvB,CACF,EGzeA,OAAO2B,OAAQ,UACf,OAAOC,MAAU,YAKV,IAAMC,EAAN,MAAMC,CAAK,CAChB,OAAe,kBACf,OAAe,gBAAkB,GAEjC,OAAO,YAAqB,CAC1B,OAAOF,EAAK,KAAKE,EAAK,YAAY,EAAG,aAAa,CACpD,CAEA,OAAO,aAAsB,CAC3B,OAAOF,EAAK,KAAKE,EAAK,YAAY,EAAG,SAAS,CAChD,CAEA,OAAO,qBAA8B,CACnC,OAAOF,EAAK,KAAKE,EAAK,YAAY,EAAG,aAAa,CACpD,CAEA,OAAO,aAAsB,CAC3B,OAAAA,EAAK,gBAAkB,GAEhBA,EAAK,kBAAoBA,EAAK,kBAAoBF,EAAK,KAAKD,GAAG,QAAQ,EAAG,aAAa,CAChG,CAEA,OAAc,kBAAkBI,EAAqC,CACnE,GAAID,EAAK,gBACP,MAAM,IAAI,MAAM,qHAAqH,EAGvIA,EAAK,kBAAoBF,EAAK,QAAQ,GAAGG,CAAmB,CAC9D,CACF,ENnBA,IAAMC,GAAMC,EAAO,SAiNZ,IAAMC,EAAN,cAA4BC,EAA4B,CAC7C,QAAU,IACV,cAAgBC,EAAW,EAGlC,KAAOC,EACP,IAAMC,EACN,eAAiBA,EAAU,YAC3B,kBAAoBC,EAG7B,aAAc,CACZ,MAAM,CACR,CAEO,sBAAsBC,EAA0B,CACrD,OAAOC,GAAO,IAAI,KAAK,cAAeD,CAAO,CAC/C,CAEA,OAAc,wBAAwBE,EAAyE,CAC7G,MAAO,uBAAwBA,CACjC,CAEA,OAAc,uBAAuBA,EAAwE,CAC3G,MAAO,gBAAiBA,CAC1B,CAEA,gBAAuB,CACrB,KAAK,KAAK,oBAA6B,CACzC,CAEA,gBAAuB,CACrB,KAAK,KAAK,UAAiB,CAC7B,CAKA,kBAAkBC,EAAoDC,EAA2DC,EAAgD,CAC3K,OAAOD,GAAkB,YAC3BC,EAAcD,EACdA,EAAgBD,EAChB,KAAK,KAAK,oBAAqCC,EAAeC,CAAW,GAEzE,KAAK,KAAK,oBAAqCD,EAAeC,EAAcF,CAAgB,CAEhG,CAKA,iBAAiBA,EAAmDG,EAAwDD,EAA+C,CACrK,OAAOC,GAAiB,YAC1BD,EAAcC,EACdA,EAAeH,EACf,KAAK,KAAK,mBAAoCG,EAAcD,CAAW,GAEvE,KAAK,KAAK,mBAAoCC,EAAcD,EAAcF,CAAgB,CAE9F,CAEA,yBAAyBA,EAAoCI,EAAwC,CACnG,KAAK,2BAA2BJ,EAAkBI,CAAW,CAC/D,CAEA,2BAA2BJ,EAAoCI,EAAwC,CAChGC,EAAc,4BAA4BL,CAAgB,GAC7DM,GAAI,KAAK,6FAA6FN,CAAgB,uEAAuE,EAG/LI,EAAY,QAASG,GAAc,CAEjC,GAAI,EAAEA,aAAqBX,GACzB,MAAM,IAAI,UAAU,GAAGI,CAAgB,iEAAiE,EAG1GO,EAAU,kBAAoBP,CAChC,CAAC,EAED,KAAK,KAAK,6BAA+CI,CAAW,CACtE,CAEA,4BAA4BJ,EAAoCG,EAA4BC,EAAwC,CAClIA,EAAY,QAASG,GAAc,CAEjC,GAAI,EAAEA,aAAqBX,GACzB,MAAM,IAAI,UAAU,GAAGI,CAAgB,MAAMG,CAAY,iEAAiE,EAG5HI,EAAU,kBAAoBP,EAC9BO,EAAU,oBAAsBJ,CAClC,CAAC,EAED,KAAK,KAAK,8BAAgDC,CAAW,CACvE,CAEA,0BAA0BA,EAAwC,CAChE,KAAK,KAAK,4BAA8CA,CAAW,CACrE,CAEA,8BAA8BJ,EAAoCG,EAA4BC,EAAwC,CACpIA,EAAY,QAASG,GAAc,CAEjC,GAAI,EAAEA,aAAqBX,GACzB,MAAM,IAAI,UAAU,GAAGI,CAAgB,MAAMG,CAAY,mEAAmE,CAEhI,CAAC,EAED,KAAK,KAAK,gCAAkDC,CAAW,CACzE,CACF,EOrTA,OACE,aAAAI,GACA,uBAAAC,EACA,UAAAC,GACA,cAAAC,GACA,kBAAAC,EACA,4BAAAC,GACA,6BAAAC,EACA,qBAAAC,GACA,QAAAC,GACA,WAAAC,EACA,QAAAC,MACK,aCtCP,OAAOC,MAAU,YAEjB,OAAOC,MAAQ,WAER,IAAMC,EAAN,KAAqB,CAC1B,YACSC,EACP,CADO,mBAAAA,CACN,CAEI,UAAiB,CACtB,OAAOF,EAAG,cAAc,KAAK,aAAa,CAC5C,CAEO,YAAeG,EAA4B,CAChD,IAAMC,EAAWL,EAAK,QAAQ,KAAK,cAAeI,CAAQ,EAE1D,OAAKH,EAAG,eAAeI,CAAQ,EAIxBJ,EAAG,aAAaI,CAAQ,EAHtB,IAIX,CAEA,MAAa,QAAWD,EAAqC,CAC3D,IAAMC,EAAWL,EAAK,QAAQ,KAAK,cAAeI,CAAQ,EAE1D,OAAK,MAAMH,EAAG,WAAWI,CAAQ,EAI1B,MAAMJ,EAAG,SAASI,CAAQ,EAHxB,IAIX,CAEO,YAAYD,EAAkBE,EAA2C,CAC9E,OAAOL,EAAG,cAAcD,EAAK,QAAQ,KAAK,cAAeI,CAAQ,EAAGE,CAAI,CAC1E,CAEO,QAAQF,EAAkBE,EAAoD,CACnF,OAAOL,EAAG,UAAUD,EAAK,QAAQ,KAAK,cAAeI,CAAQ,EAAGE,CAAI,CACtE,CAEO,SAASC,EAAqBC,EAAqC,CACxE,OAAOP,EAAG,SAASD,EAAK,QAAQ,KAAK,cAAeO,CAAW,EAAGP,EAAK,QAAQ,KAAK,cAAeQ,CAAY,CAAC,CAClH,CAEO,aAAaD,EAAqBC,EAA4B,CACnE,OAAOP,EAAG,aAAaD,EAAK,QAAQ,KAAK,cAAeO,CAAW,EAAGP,EAAK,QAAQ,KAAK,cAAeQ,CAAY,CAAC,CACtH,CAEO,eAAeJ,EAAwB,CAC5C,OAAOH,EAAG,WAAWD,EAAK,QAAQ,KAAK,cAAeI,CAAQ,CAAC,CACjE,CACF,EChDA,OAAOK,OAAY,cAUZ,SAASC,EAASC,EAAkE,CACzF,IAAMC,EAAUC,GAAO,WAAW,MAAM,EACxCD,EAAQ,OAAOD,CAAI,EACnB,IAAMG,EAAIF,EAAQ,OAAO,KAAK,EAE1BG,EAAI,EACR,MAAO,oBAAoB,QAAQ,KAAM,IAAMD,EAAEC,GAAG,CAAC,EAAE,YAAY,CACrE,CF2BA,IAAMC,EAAMC,EAAO,SA6DNC,EAAN,MAAMC,CAAc,CAUzB,YACUC,EACAC,EACAC,EACAC,EACAC,EACAC,EACR,CANQ,SAAAL,EACA,mBAAAC,EACA,yBAAAC,EACA,mBAAAC,EACA,kBAAAC,EACA,YAAAC,EAER,KAAK,eAAiB,IAAIC,EAAe,KAAK,cAAc,oBAAoB,EAChF,KAAK,eAAe,SAAS,EAO7B,KAAK,oBAAsB,KAAK,cAAc,gBAAkB,GAEhE,KAAK,IAAI,iCAAmD,KAAK,kCAAkC,KAAK,IAAI,CAAC,EAC7G,KAAK,IAAI,+BAAiD,KAAK,gCAAgC,KAAK,IAAI,CAAC,EACzG,KAAK,IAAI,mCAAqD,KAAK,oCAAoC,KAAK,IAAI,CAAC,EACjH,KAAK,IAAI,gCAAkD,KAAK,iCAAiC,KAAK,IAAI,CAAC,EAE3G,KAAK,OAAS,IAAIC,GAAOH,EAAa,KAAMI,EAAK,SAAS,YAAY,CAAC,EACvE,KAAK,OAAO,GAAGC,EAAoB,uBAAwB,IAAM,CAIjE,CAAC,CACH,CAtCO,OACC,eAES,oBAET,0BAAiD,CAAC,EAClD,4BAA8B,GACrB,6BAAmE,IAAI,IAkCxF,OAAc,gCAAgCC,EAAgBC,EAAsBC,EAAiCC,EAAsC,CACzJ,IAAMC,EAAW,mDACjB,OAAQD,EAAQ,KAAM,CACpB,KAAKE,EAA0B,UAC/B,KAAKA,EAA0B,WACxBH,EAAK,YACRhB,EAAI,KAAKoB,EAAaN,EAAO,oBAAoB,CAAC,EAAG,qCAAsCG,EAAQ,QAASC,CAAQ,EAEtH,MACF,KAAKC,EAA0B,aAC/B,KAAKA,EAA0B,cAC7BnB,EAAI,MAAMoB,EAAaN,EAAO,oBAAoB,CAAC,EAAG,qCAAsCG,EAAQ,QAASC,CAAQ,EACrH,MACF,KAAKC,EAA0B,aAC7BnB,EAAI,KAAKoB,EAAaN,EAAO,oBAAoB,CAAC,EAAG,4DAA4DG,EAAQ,eAAe,WAAW,KAAM,GAAGA,EAAQ,OAAO,IAAKC,CAAQ,EACxL,MACF,KAAKC,EAA0B,cAC7BnB,EAAI,MAAMoB,EAAaN,EAAO,oBAAoB,CAAC,EAAG,uDAAuDG,EAAQ,eAAe,WAAW,KAAM,GAAGA,EAAQ,OAAO,IAAKC,CAAQ,EACpL,MACF,KAAKC,EAA0B,cAC7BnB,EAAI,MAAMoB,EAAaN,EAAO,oBAAoB,CAAC,EAAG,mBAAmBG,EAAQ,eAAe,WAAW,KAAM,GAAGA,EAAQ,OAAO,IAAKC,CAAQ,EAChJ,MACF,QACElB,EAAI,KAAKoB,EAAaN,EAAO,oBAAoB,CAAC,EAAG,4DAA4DG,EAAQ,eAAe,WAAW,KAAM,GAAGA,EAAQ,OAAO,IAAKC,CAAQ,EACxL,KACJ,CACID,EAAQ,OACVjB,EAAI,MAAMoB,EAAaN,EAAO,oBAAoB,CAAC,EAAGG,EAAQ,KAAK,CAEvE,CAEO,eAAsB,CAC3B,IAAMT,EAAe,KAAK,aAEpBa,EAAO,KAAK,OAAO,WAAWC,EAAQ,oBAAoB,EAChED,EAAK,kBAAkBE,EAAe,aAAcf,EAAa,cAAgB,eAAe,EAChGa,EAAK,kBAAkBE,EAAe,MAAOf,EAAa,OAAS,YAAY,EAC/Ea,EAAK,kBAAkBE,EAAe,aAAcf,EAAa,QAAQ,EACzEa,EAAK,kBAAkBE,EAAe,iBAAkBf,EAAa,kBAAoBgB,EAAW,CAAC,EAErG,KAAK,OAAO,GAAGX,EAAoB,UAAYY,GAAiB,CAC9DzB,EAAI,QAAQ,uDAAwDwB,EAAW,EAAGE,GAAkB,EAAGlB,EAAa,KAAMiB,CAAI,CAChI,CAAC,EAED,IAAME,EAA2B,CAC/B,SAAUnB,EAAa,SACvB,KAAMA,EAAa,KACnB,QAASA,EAAa,IACtB,SAAUoB,GAAW,OACrB,KAAMpB,EAAa,KACnB,uBAAwB,GACxB,WAAYA,EAAa,UAC3B,EAEIA,EAAa,SAAWA,EAAa,QAAQ,SAAW,IAC1DmB,EAAY,QAAUnB,EAAa,SAGrCR,EAAI,MAAM,2DAA4D,KAAK,OAAO,YAAaG,EAAc,iBAAiBwB,CAAW,CAAC,EAC1I,KAAK,OAAO,QAAQA,EAAa,KAAK,mBAAmB,CAC3D,CAKA,MAAa,uCAAuD,CAClE,IAAIE,EAA0D,KAE9D,GAAI,CACFA,EAAoB,MAAM,KAAK,eAAe,QAAuC,KAAK,cAAc,yBAAyB,CACnI,OAASC,EAAY,CACnB9B,EAAI,MAAM,+CAAgD8B,EAAM,OAAO,EACnEA,aAAiB,YAEnBD,EAAoB,MAAM,KAAK,+BAA+B,EAE9D7B,EAAI,MAAM,mEAAmE,CAEjF,CAEI6B,IACF7B,EAAI,KAAK,UAAU6B,EAAkB,MAAM,4BAA4B,KAAK,cAAc,yBAAyB,GAAG,EAEtH,KAAK,0BAA4BA,EAAkB,IAAKE,GAC/CC,EAAkB,YAAYD,CAAU,CAChD,EAEGF,EAAkB,QAEpB,MAAM,KAAK,8BAA8B,GAI7C,KAAK,4BAA8B,EACrC,CAKA,IAAY,qBAAsB,CAChC,MAAO,IAAI,KAAK,cAAc,yBAAyB,MACzD,CAMA,MAAc,+BAA+C,CAC3D,GAAI,CACF,MAAM,KAAK,eAAe,SAAS,KAAK,cAAc,0BAA2B,KAAK,mBAAmB,CAC3G,OAASC,EAAY,CACnB9B,EAAI,KAAK,oCAAoC,KAAK,cAAc,yBAAyB,4BAA6B8B,EAAM,OAAO,CACrI,CACF,CAMA,MAAc,gCAAgF,CAC5F,GAAI,CACF,IAAMD,EAAoB,MAAM,KAAK,eAAe,QAAuC,KAAK,mBAAmB,EACnH,OAAIA,GAAqBA,EAAkB,QACzC7B,EAAI,KAAK,aAAa6B,EAAkB,MAAM,qBAAqB,KAAK,cAAc,yBAAyB,gBAAgB,EAE1HA,CACT,MAAqB,CACnB,OAAO,IACT,CACF,CAEO,kCAAyC,CAC9C,KAAK,0BAA4B,KAAK,0BAA0B,OAAQd,GAAc,CACpF,IAAID,EAAS,KAAK,cAAc,UAAUC,EAAU,iBAAkB,EACtE,GAAI,CAACD,EACH,GAAI,CAEFA,EAAS,KAAK,cAAc,iCAAiCC,EAAU,mBAAoB,EAEvFD,IAKFd,EAAI,KAAK,8DAA8De,EAAU,WAAW,iDAC3CA,EAAU,iBAAiB,SAC1ED,EAAO,oBAAoB,CAAC,iDAAiD,EAE/EC,EAAU,kBAAoBD,EAAO,oBAAoB,EAE7D,OAASgB,EAAY,CACnB9B,EAAI,KAAK,2DAA2De,EAAU,WAAW,wDAClCe,EAAM,OAAO,EAAE,CACxE,CAGF,IAAMG,EAAkBnB,GAAUA,EAAO,yBAAyBC,EAAU,mBAAoB,EAKhG,GAJID,GACFC,EAAU,wBAAwB,GAAGF,EAAoB,uBAAwBV,EAAc,gCAAgC,KAAK,KAAMW,EAAQC,EAAU,wBAAyB,CAAC,CAAC,CAAC,EAGrLkB,EAQHlB,EAAU,WAAWO,EAAQ,oBAAoB,GAAG,kBAAkBC,EAAe,iBAAkB,GAAG,EAC1GU,EAAgB,mBAAmBlB,CAAS,UAR5Cf,EAAI,KAAK,6CAA6Ce,EAAU,wBAAwB,WAAW,EAAE,EACjG,CAAC,KAAK,cAAc,8BACtB,OAAAf,EAAI,KAAK,+BAA+Be,EAAU,wBAAwB,WAAW,EAAE,EAChF,GAQX,GAAI,CACF,KAAK,OAAO,oBAAoBA,EAAU,uBAAuB,CACnE,OAASe,EAAY,CACnB,OAAA9B,EAAI,KAAK,GAAGe,EAAU,kBAAoBK,EAAaL,EAAU,iBAAiB,EAAI,EAAE,wCAAwCA,EAAU,wBAAwB,WAAW,KAAMe,EAAM,OAAO,EACzL,EACT,CACA,MAAO,EACT,CAAC,CACH,CAKO,qCAA4C,CACjD,GAAI,CAGF,GAAI,KAAK,4BAA6B,CACpC,IAAMI,EAAwB,KAAK,0BAA0B,IAAInB,GAAaiB,EAAkB,UAAUjB,CAAS,CAAC,EACpH,KAAK,eAAe,YAAY,KAAK,cAAc,0BAA2BmB,CAAqB,CACrG,CACF,OAASJ,EAAY,CACnB9B,EAAI,MAAM,6CAA8C8B,EAAM,OAAO,EACrE9B,EAAI,MAAM,kFAAkF,CAC9F,CACF,CAEA,kCAAkCmC,EAAwC,CACxE,IAAMC,EAAiBD,EAAY,IAAKpB,GAAc,CACpD,KAAK,0BAA0B,KAAKA,CAAS,EAE7C,IAAMD,EAAS,KAAK,cAAc,UAAUC,EAAU,iBAAkB,EACxE,OAAID,GACgBA,EAAO,yBAAyBC,EAAU,mBAAoB,GAG9Ef,EAAI,KAAK,2GAAiHe,EAAU,kBAAoBA,EAAU,mBAAoB,EAGxLA,EAAU,wBAAwB,GAAGF,EAAoB,uBAAwBV,EAAc,gCAAgC,KAAK,KAAMW,EAAQC,EAAU,wBAAyB,CAAC,CAAC,CAAC,GAExLf,EAAI,KAAK,0HAA6He,EAAU,iBAAiB,EAG5JA,EAAU,uBACnB,CAAC,EAED,KAAK,OAAO,sBAAsBqB,CAAc,EAChD,KAAK,oCAAoC,CAC3C,CAEA,iCAAwC,CAEtC,KAAK,oCAAoC,CAC3C,CAEA,oCAAoCD,EAAwC,CAC1E,IAAMC,EAAiBD,EAAY,IAAKpB,GAAc,CACpD,IAAMsB,EAAQ,KAAK,0BAA0B,QAAQtB,CAAS,EAC9D,OAAIsB,GAAS,GACX,KAAK,0BAA0B,OAAOA,EAAO,CAAC,EAGzCtB,EAAU,uBACnB,CAAC,EAED,KAAK,OAAO,yBAAyBqB,CAAc,EACnD,KAAK,oCAAoC,CAC3C,CAEA,MAAM,iCAAiCD,EAAiD,CACtF,IAAMG,EAAe,KAAK,aAAa,IAEvC,QAAWvB,KAAaoB,EAAa,CACnC,IAAMI,EAAexB,EAAU,wBACzByB,EAAmBC,EAASF,EAAa,IAAI,EAG7CG,EAAgB,MAAM,KAAK,oBAAoB,YAAYF,CAAgB,EAEjF,GAAI,KAAK,6BAA6B,IAAIA,CAAgB,EACxD,MAAM,IAAI,MAAM,aAAaD,EAAa,WAAW,oCAAoC,EAEzF,KAAK,6BAA6B,IAAIC,EAAkBzB,CAAS,EAGnE,IAAMD,EAAS,KAAK,cAAc,UAAUC,EAAU,iBAAkB,EACpED,EACFyB,EAAa,GAAG1B,EAAoB,uBAAwBV,EAAc,gCAAgC,KAAK,KAAMW,EAAQyB,EAAc,CAAE,WAAY,EAAK,CAAC,CAAC,EACvJI,EAAc,4BAA4B5B,EAAU,iBAAkB,GAE/Ef,EAAI,KAAK,+HAAkIe,EAAU,iBAAiB,EAGxKwB,EAAa,GAAG1B,EAAoB,UAAYY,GAAiB,CAC/DzB,EAAI,QAAQ,4BAA6BuC,EAAa,YAAad,CAAI,EACvEzB,EAAI,KAAK,uDAAwDuC,EAAa,YAAaD,CAAY,CACzG,CAAC,EAED,IAAMX,EAA2B,CAC/B,SAAUa,EACV,QAASF,EACT,SAAUvB,EAAU,SACpB,KAAM2B,EACN,KAAM,KAAK,aAAa,KACxB,uBAAwB,GACxB,WAAY,KAAK,aAAa,UAChC,EAEA1C,EAAI,MAAM,6DAA8DuC,EAAa,YAAapC,EAAc,iBAAiBwB,CAAW,CAAC,EAC7IY,EAAa,QAAQZ,EAAa,KAAK,mBAAmB,CAC5D,CACF,CAEO,mBAAmBb,EAAgB8B,EAAoCC,EAAqBC,EAAoDC,EAA0C,CAC/L,IAAMC,GAAYJ,EAAkB,YAAY,GAAK,CAAC,GACnD,OAAOK,GAAW,CAAC,CAACA,CAAO,EACxBC,GAAgBN,EAAkB,gBAAkBA,EAAkB,eAAe,GAAM,CAAC,GAC/F,OAAOO,GAAc,CAAC,CAACA,CAAU,EAEpC,GAAIH,EAAS,SAAW,GAAKE,EAAY,SAAW,EAClD,OAKF,IAAME,EAAgBxC,EAAK,SAAS,GAAGkC,CAAa,IAAIC,GAAYF,CAAW,EAAE,EAC3E9B,EAAY,IAAIsC,GAAUR,EAAaO,CAAa,EAGtDR,EAAkB,UACpB7B,EAAU,GAAGF,EAAoB,SAAU,CAACyC,EAAiBC,IAA2B,CAEtFX,EAAkB,SAAU,IAAM,CAAC,CAAC,EACpCW,EAAS,CACX,CAAC,EAGH,IAAMC,EAAqBzC,EAAU,WAAWO,EAAQ,oBAAoB,EAC5E,OAAA0B,EAAS,QAASC,GAAY,CAExBA,aAAmB3B,EAAQ,sBAC7B2B,EAAQ,kBAAkB1B,EAAe,KAAMsB,CAAW,EAG1DI,EAAQ,kBAAkB1B,EAAe,QAAQ,EAAE,mBAAmBkC,GAAyB,GAAG,EAGlGD,EAAmB,kCAAkCP,CAAO,GAE5DlC,EAAU,WAAWkC,CAAO,CAEhC,CAAC,EAEDlC,EAAU,GAAGF,EAAoB,uBAAwBV,EAAc,gCAAgC,KAAK,KAAMW,EAAQC,EAAW,CAAC,CAAC,CAAC,EAExImC,EAAY,QAASC,GAAe,CAClCpC,EAAU,oBAAoBoC,CAAU,CAC1C,CAAC,EAEMpC,CACT,CAEA,MAAa,wBAAwBD,EAAgB4C,EAAwCC,EAAiDC,EAAgC,CAE5K,OAAO,IAAI,QAASC,GAAY,CAE9B,IAAMC,EAA2B,YAAY,IAAM,CACjD9D,EAAI,KAAKoB,EAAaN,EAAO,oBAAoB,CAAC,EAAG,mIAAmI,CAC1L,EAAG,GAAK,EAER4C,EAAiB,YAAYK,GAAM5B,GAAmC,CAEpE,cAAc2B,CAAwB,EAGtC3B,EAAY,QAAQ,CAACS,EAAmBP,IAAU,CAEhD,IAAM2B,EAAgBpB,EAAkB,KAGlCG,EAA+BH,EAAkB,UAEvD5C,EAAI,KAAK,0CAA6CgE,CAAa,EAEnE,IAAMjD,EAAY,KAAK,mBAAmBD,EAAQ8B,EAAmBoB,EAAeL,EAAcZ,CAAQ,EAEtGhC,EACF,KAAK,OAAO,oBAAoBA,CAAS,EAEzC6C,EAAO,8GAAgHD,EAActB,CAAK,CAE9I,CAAC,EAEDwB,EAAQ,CACV,CAAC,CAAC,CACJ,CAAC,CACH,CAEA,UAAiB,CACf,KAAK,OAAO,UAAU,EACtB,QAAW9C,KAAa,KAAK,6BAA6B,OAAO,EAC/DA,EAAU,wBAAwB,UAAU,EAG9C,KAAK,oCAAoC,EAEzC,KAAK,IAAI,eAAe,CAC1B,CAEA,OAAe,iBAAiBY,EAAuC,CACrE,IAAMN,EAAO,CACX,GAAGM,CACL,EACA,OAAAN,EAAK,QAAU,aACRA,CACT,CACF,EG5gBA,OAAO4C,IAAQ,WAAAC,OAAe,YAE9B,OAAS,iBAAAC,OAAqB,WAE9B,OAAOC,OAAQ,WAOf,IAAMC,GAAaC,GAAc,YAAY,GAAG,EAC1CC,GAAYC,GAAQH,EAAU,ECd7B,IAAMI,EAAN,KAA0B,CAI/B,YACUC,EACR,CADQ,mBAAAA,CACN,CALI,iBACA,eAAsD,IAAI,IAWlE,MAAa,YAAYC,EAAmD,CAE1E,IAAMC,EAAyB,KAAK,eAAe,IAAID,CAAQ,EAC/D,GAAIC,EACF,OAAOA,EAIT,IAAMC,EAAO,KAAK,gBAAgB,EAClC,YAAK,eAAe,IAAIF,EAAUE,CAAI,EAC/BA,CACT,CAEQ,iBAAsC,CAC5C,GAAK,KAAK,cAIV,IAAI,KAAK,mBAAqB,OAC5B,YAAK,iBAAmB,KAAK,cAAc,MACpC,KAAK,iBAKd,GAFA,KAAK,mBAED,KAAK,kBAAoB,KAAK,cAAc,IAC9C,OAAO,KAAK,iBAGdC,EAAO,SAAS,KAAK,8EAA8E,EAGrG,CACF,EAMaC,EAAN,cAA6CN,CAAoB,CACtE,YACUO,EACR,CACA,MAAM,EAFE,iBAAAA,CAGV,CAEA,MAAa,YAAYL,EAAmD,CAC1E,OAAO,MAAM,KAAK,YAAY,oBAAoBA,CAAQ,CAC5D,CACF,EZvCAM,EAAQ,MAAQ,2BAET,IAAMC,EAAN,KAAsB,CACnB,cACA,IACA,cACA,oBAEA,KACA,OACA,WACA,aACA,aACA,cACA,iBAEA,oBAA2E,IAAI,IAEvF,aAAc,CAEZ,KAAK,mBAA8C,CACrD,CAEA,YAAyBC,EAAoCC,EAAgB,CACvEH,EAAQ,MACVA,EAAQ,KAAK,CACX,GAAIE,EACJ,KAAAC,CACF,CAAC,CAEL,CAEA,MAAM,WAAWA,EAAgD,CAE/D,KAAK,KAAOA,EAAK,KACjB,KAAK,WAAaA,EAAK,WACvB,KAAK,aAAeA,EAAK,aACzB,KAAK,aAAeA,EAAK,aACzB,KAAK,cAAgBA,EAAK,cAC1B,KAAK,iBAAmBA,EAAK,iBAG7B,QAAWC,KAAU,KAAK,aACxB,OAAOA,EAAO,QAIZ,KAAK,cAAc,iBACrBC,EAAO,oBAAoB,EAAK,EAG9B,KAAK,cAAc,kBACrBA,EAAO,gBAAgB,EAAI,EAGzB,KAAK,cAAc,oBACrBA,EAAO,WAAW,EAGhB,KAAK,cAAc,mBACrBC,EAAK,eAAe,KAAK,cAAc,iBAAiB,EAI1DC,GAAW,qBAAqBD,EAAK,YAAY,CAAC,EAGlD,KAAK,IAAM,IAAIE,EACf,KAAK,cAAgB,IAAIC,EAAc,KAAK,GAAG,EAC/C,KAAK,oBAAsB,IAAIC,EAA+B,IAAI,EAGlE,KAAK,OAAS,KAAK,cAAc,WAAWP,EAAK,UAAU,EAC3D,MAAM,KAAK,OAAO,KAAK,EACvB,MAAM,KAAK,cAAc,iBAAiB,KAAK,OAAQA,EAAK,UAAU,EAGtEH,EAAQ,MAAQ,eAAe,KAAK,OAAO,oBAAoB,CAAC,GAEhE,KAAK,qBAAoF,CACvF,QAAS,KAAK,OAAO,OACvB,CAAC,CACH,CAEA,MAAM,aAA6B,CACjC,KAAK,cAAgB,IAAIW,EACvB,KAAK,IACL,KAAK,cACL,KAAK,oBACL,KAAK,cACL,KAAK,aACL,KAAK,gBACP,EAGA,KAAK,cAAc,OAAO,GAAGC,EAAoB,WAAY,IAAM,CACjE,KAAK,sBAAsB,CAC7B,CAAC,EAGD,KAAK,cAAc,OAAO,GAAGA,EAAoB,OAAQ,IAAM,CAC7D,KAAK,sBAAsB,CAC7B,CAAC,EAGD,KAAK,cAAc,OAAO,GAAGA,EAAoB,SAAU,IAAM,CAC/D,KAAK,sBAAsB,CAC7B,CAAC,EAGD,MAAM,KAAK,cAAc,sCAAsC,EAE/D,QAAWR,KAAU,KAAK,aACxB,GAAI,KAAK,OAAS,WAAqB,CACrC,IAAMS,EAAS,KAAK,cAAc,qBAAqB,KAAK,UAAU,EAChEC,EAAcV,EAAO,MAAQS,EAAO,oBAAoB,EACxDE,EAASV,EAAO,WAAWS,CAAW,EACtCE,EAAcH,EAAO,uBAAuB,KAAK,UAAU,EAC3DI,EAA2B,IAAID,EAAYD,EAAQX,EAA0B,KAAK,GAAG,EAEvFI,EAAc,wBAAwBS,CAAQ,EAChDJ,EAAO,sBAAsB,KAAK,WAAYI,CAAQ,EAC7CT,EAAc,uBAAuBS,CAAQ,GACtD,MAAM,KAAK,cAAc,wBAAwBJ,EAAQI,EAAU,KAAK,WAAYF,CAAM,CAK9F,SAAW,KAAK,OAAS,YAAsB,CAC7C,IAAMF,EAAS,KAAK,cAAc,sBAAsB,KAAK,UAAU,EACjEC,EAAcV,EAAO,KAE3B,GAAI,CAACU,EAAa,CAChBT,EAAO,SAAS,KAAK,6EAAgF,KAAK,UAAU,EACpH,MACF,CAEA,IAAMU,EAASV,EAAO,WAAWS,CAAW,EACtCE,EAAcH,EAAO,wBAAwB,KAAK,UAAU,EAC5DK,EAAqC,IAAIF,EAAYD,EAAQX,EAA2B,KAAK,GAAG,EAGhGe,EAAY,KAAK,cAAc,mBAAmBN,EAAQK,EAAmBJ,EAAa,KAAK,WAAYV,EAAO,SAAS,EAE7He,EACF,KAAK,cAAc,OAAO,oBAAoBA,CAAS,EAEvDJ,EAAO,8EAAgF,KAAK,UAAU,CAE1G,CAIF,KAAK,cAAc,iCAAiC,EAEpD,KAAK,cAAc,cAAc,EACjC,KAAK,IAAI,eAAe,EAGxB,KAAK,oBAA+C,CACtD,CAMA,MAAa,oBAAoBK,EAAmD,CAClF,OAAO,IAAI,QAASC,GAAY,CAC9B,IAAMC,EAAiB,WAAW,IAAM,CACtCjB,EAAO,SAAS,KAAK,qGAAqG,EAC1HgB,EAAQ,MAAS,CACnB,EAAG,GAAI,EAGDE,EAAYC,GAA6B,CAC7C,aAAaF,CAAc,EAC3BD,EAAQG,CAAI,EACZ,KAAK,oBAAoB,OAAOJ,CAAQ,CAC1C,EACA,KAAK,oBAAoB,IAAIA,EAAUG,CAAQ,EAG/C,KAAK,0BAAyF,CAAE,SAAAH,CAAS,CAAC,CAC5G,CAAC,CACH,CAMO,uBAAuBjB,EAAgD,CAC5E,IAAMoB,EAAW,KAAK,oBAAoB,IAAIpB,EAAK,QAAQ,EACvDoB,GACFA,EAASpB,EAAK,IAAI,CAEtB,CAKO,uBAAwB,CAC7B,KAAK,qBAA0F,CAC7F,OAAQ,KAAK,eAAe,QAAQ,gBAAgB,OAAO,GAAK,KAChE,SAAU,KAAK,eAAe,QAAQ,SAAS,GAAK,IACtD,CAAC,CACH,CAEA,UAAiB,CACf,KAAK,cAAc,SAAS,CAC9B,CACF,EAKMsB,EAAkB,IAAIxB,EAK5BD,EAAQ,GAAG,UAAY0B,GAA+C,CACpE,GAAI,SAAOA,GAAY,UAAY,CAACA,EAAQ,IAI5C,OAAQA,EAAQ,GAAI,CAClB,WAAwC,CACtCD,EAAgB,WAAWC,EAAQ,IAAiC,EACpE,KACF,CACA,YAAyC,CACvCD,EAAgB,YAAY,EAC5B,KACF,CACA,oBAAkD,CAChDA,EAAgB,uBAAuBC,EAAQ,IAA0C,EACzF,KACF,CACF,CACF,CAAC,EAKD,IAAIC,EAAe,GACnB,SAASC,EAAcC,EAAwBC,EAAyB,CACtE,GAAI,CAAAH,EAGJ,CAAAA,EAAe,GAEftB,EAAO,SAAS,KAAK,gDAAiDwB,CAAM,EAE5E,GAAI,CACFJ,EAAgB,SAAS,CAC3B,MAAqB,CAErB,CAEA,WAAW,IAAMzB,EAAQ,KAAK,IAAM8B,CAAS,EAAG,GAAI,EACtD,CAEA9B,EAAQ,GAAG,SAAU4B,EAAc,KAAK,OAAW,SAAU,CAAC,CAAC,EAC/D5B,EAAQ,GAAG,UAAW4B,EAAc,KAAK,OAAW,UAAW,EAAE,CAAC,EAKlE,YAAY,IAAM,CACX5B,EAAQ,YACXK,EAAO,SAAS,KAAK,sDAAsD,EAC3EL,EAAQ,KAAK,CAAC,EAElB,EAAG,GAAI",
6
+ "names": ["process", "AccessoryEventTypes", "HAPStorage", "EventEmitter", "hapNodeJs", "semver", "util", "chalk", "Logger", "_Logger", "prefix", "loggerStuff", "logger", "log", "logging", "enabled", "chalk", "message", "parameters", "level", "util", "loggingFunction", "getLogPrefix", "date", "EventEmitter", "Accessory", "AccessoryEventTypes", "Categories", "PlatformAccessory", "_PlatformAccessory", "EventEmitter", "displayName", "uuid", "category", "Accessory", "Categories", "AccessoryEventTypes", "paired", "callback", "service", "constructorArgs", "name", "subType", "controller", "accessory", "json", "platformAccessory", "execSync", "fs", "createRequire", "path", "process", "assert", "path", "process", "pathToFileURL", "satisfies", "fs", "dirname", "join", "fileURLToPath", "__filename", "__dirname", "loadPackageJson", "packageJSONPath", "getVersion", "log", "Logger", "Plugin", "name", "path", "packageJSON", "scope", "exports", "constructor", "accessoryIdentifier", "PluginManager", "platformIdentifier", "platformPlugin", "platforms", "platformName", "context", "assert", "versionRequired", "nodeVersionRequired", "satisfies", "getVersion", "process", "dependencies", "mainPath", "pluginModules", "pathToFileURL", "api", "log", "Logger", "require", "createRequire", "paths", "PluginManager", "_PluginManager", "api", "options", "path", "process", "identifier", "name", "plugin", "error", "constructor", "pluginIdentifier", "plugins", "accessoryIdentifier", "found", "platformIdentifier", "translation", "platformName", "searchPath", "fs", "relativePluginPaths", "relativePath", "scopeDirectory", "index", "absolutePath", "packageJson", "scope", "alreadyInstalled", "Plugin", "pluginPath", "packageJsonPath", "execSync", "os", "path", "User", "_User", "storagePathSegments", "log", "Logger", "HomebridgeAPI", "EventEmitter", "getVersion", "User", "hapNodeJs", "PlatformAccessory", "version", "semver", "platformPlugin", "pluginIdentifier", "accessoryName", "constructor", "platformName", "accessories", "PluginManager", "log", "accessory", "Accessory", "AccessoryEventTypes", "Bridge", "Categories", "Characteristic", "CharacteristicEventTypes", "CharacteristicWarningType", "HAPLibraryVersion", "once", "Service", "uuid", "path", "fs", "StorageService", "baseDirectory", "itemName", "filePath", "data", "srcItemName", "destItemName", "crypto", "generate", "data", "sha1sum", "crypto", "s", "i", "log", "Logger", "BridgeService", "_BridgeService", "api", "pluginManager", "externalPortService", "bridgeOptions", "bridgeConfig", "config", "StorageService", "Bridge", "uuid", "AccessoryEventTypes", "plugin", "accessory", "opts", "warning", "wikiInfo", "CharacteristicWarningType", "getLogPrefix", "info", "Service", "Characteristic", "getVersion", "port", "HAPLibraryVersion", "publishInfo", "Categories", "cachedAccessories", "error", "serialized", "PlatformAccessory", "platformPlugins", "serializedAccessories", "accessories", "hapAccessories", "index", "accessoryPin", "hapAccessory", "advertiseAddress", "generate", "accessoryPort", "PluginManager", "accessoryInstance", "displayName", "accessoryType", "uuidBase", "services", "service", "controllers", "controller", "accessoryUUID", "Accessory", "paired", "callback", "informationService", "CharacteristicEventTypes", "platformInstance", "platformType", "logger", "resolve", "loadDelayWarningInterval", "once", "accessoryName", "path", "dirname", "fileURLToPath", "fs", "__filename", "fileURLToPath", "__dirname", "dirname", "ExternalPortService", "externalPorts", "username", "existingPortAllocation", "port", "Logger", "ChildBridgeExternalPortService", "childBridge", "process", "ChildBridgeFork", "type", "data", "config", "Logger", "User", "HAPStorage", "HomebridgeAPI", "PluginManager", "ChildBridgeExternalPortService", "BridgeService", "AccessoryEventTypes", "plugin", "displayName", "logger", "constructor", "platform", "accessoryInstance", "accessory", "username", "resolve", "requestTimeout", "callback", "port", "childPluginFork", "message", "shuttingDown", "signalHandler", "signal", "signalNum"]
7
+ }