homebridge-config-ui-x 5.0.0-beta.11 → 5.0.0-beta.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +479 -31
- package/CONTRIBUTING.md +5 -4
- package/LICENSE +1 -1
- package/config.schema.json +55 -252
- package/dist/bin/hb-service.d.ts +2 -2
- package/dist/bin/hb-service.js +52 -51
- package/dist/bin/hb-service.js.map +1 -1
- package/dist/bin/platforms/darwin.js +3 -11
- package/dist/bin/platforms/darwin.js.map +1 -1
- package/dist/bin/platforms/win32.js +4 -2
- package/dist/bin/platforms/win32.js.map +1 -1
- package/dist/bin/standalone.js +1 -0
- package/dist/bin/standalone.js.map +1 -1
- package/dist/core/auth/auth.controller.d.ts +31 -3
- package/dist/core/auth/auth.controller.js.map +1 -1
- package/dist/core/auth/auth.module.js +1 -1
- package/dist/core/auth/auth.module.js.map +1 -1
- package/dist/core/auth/auth.service.js +10 -10
- package/dist/core/auth/auth.service.js.map +1 -1
- package/dist/core/auth/jwt.strategy.d.ts +3 -1
- package/dist/core/config/config.service.d.ts +46 -11
- package/dist/core/config/config.service.js +57 -20
- package/dist/core/config/config.service.js.map +1 -1
- package/dist/core/config/config.startup.js +2 -2
- package/dist/core/config/config.startup.js.map +1 -1
- package/dist/core/homebridge-ipc/homebridge-ipc.service.js +3 -3
- package/dist/core/homebridge-ipc/homebridge-ipc.service.js.map +1 -1
- package/dist/index.js +6 -77
- package/dist/index.js.map +1 -1
- package/dist/main.js +2 -1
- package/dist/main.js.map +1 -1
- package/dist/modules/accessories/accessories.controller.js +5 -5
- package/dist/modules/accessories/accessories.controller.js.map +1 -1
- package/dist/modules/accessories/accessories.service.js +8 -7
- package/dist/modules/accessories/accessories.service.js.map +1 -1
- package/dist/modules/backup/backup.controller.js +7 -7
- package/dist/modules/backup/backup.controller.js.map +1 -1
- package/dist/modules/backup/backup.service.js +19 -79
- package/dist/modules/backup/backup.service.js.map +1 -1
- package/dist/modules/child-bridges/child-bridges.service.js +0 -7
- package/dist/modules/child-bridges/child-bridges.service.js.map +1 -1
- package/dist/modules/config-editor/config-editor.controller.d.ts +7 -1
- package/dist/modules/config-editor/config-editor.controller.js +36 -8
- package/dist/modules/config-editor/config-editor.controller.js.map +1 -1
- package/dist/modules/config-editor/config-editor.service.d.ts +5 -1
- package/dist/modules/config-editor/config-editor.service.js +133 -69
- package/dist/modules/config-editor/config-editor.service.js.map +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.d.ts +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js +13 -11
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js.map +1 -1
- package/dist/modules/log/log.gateway.d.ts +2 -1
- package/dist/modules/log/log.gateway.js.map +1 -1
- package/dist/modules/log/log.service.js +3 -3
- package/dist/modules/log/log.service.js.map +1 -1
- package/dist/modules/platform-tools/docker/docker.controller.js +3 -3
- package/dist/modules/platform-tools/docker/docker.controller.js.map +1 -1
- package/dist/modules/platform-tools/docker/docker.service.js +1 -1
- package/dist/modules/platform-tools/docker/docker.service.js.map +1 -1
- package/dist/modules/platform-tools/hb-service/hb-service.controller.js +3 -3
- package/dist/modules/platform-tools/hb-service/hb-service.controller.js.map +1 -1
- package/dist/modules/platform-tools/hb-service/hb-service.service.js +6 -6
- package/dist/modules/platform-tools/hb-service/hb-service.service.js.map +1 -1
- package/dist/modules/platform-tools/linux/linux.controller.js +2 -2
- package/dist/modules/platform-tools/linux/linux.controller.js.map +1 -1
- package/dist/modules/platform-tools/linux/linux.service.js +2 -2
- package/dist/modules/platform-tools/linux/linux.service.js.map +1 -1
- package/dist/modules/platform-tools/terminal/terminal.gateway.d.ts +2 -1
- package/dist/modules/platform-tools/terminal/terminal.gateway.js.map +1 -1
- package/dist/modules/platform-tools/terminal/terminal.service.js +2 -2
- package/dist/modules/platform-tools/terminal/terminal.service.js.map +1 -1
- package/dist/modules/plugins/plugins.controller.d.ts +2 -0
- package/dist/modules/plugins/plugins.controller.js +5 -5
- package/dist/modules/plugins/plugins.controller.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +10 -1
- package/dist/modules/plugins/plugins.service.js +285 -168
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/server/server.controller.d.ts +33 -3
- package/dist/modules/server/server.controller.js +162 -20
- package/dist/modules/server/server.controller.js.map +1 -1
- package/dist/modules/server/server.service.d.ts +35 -6
- package/dist/modules/server/server.service.js +286 -66
- package/dist/modules/server/server.service.js.map +1 -1
- package/dist/modules/status/status.controller.d.ts +0 -1
- package/dist/modules/status/status.controller.js +3 -4
- package/dist/modules/status/status.controller.js.map +1 -1
- package/dist/modules/status/status.gateway.d.ts +1 -1
- package/dist/modules/status/status.service.d.ts +1 -1
- package/dist/modules/status/status.service.js +25 -41
- package/dist/modules/status/status.service.js.map +1 -1
- package/dist/modules/users/users.controller.js +7 -7
- package/dist/modules/users/users.controller.js.map +1 -1
- package/dist/self-check.js +6 -6
- package/dist/self-check.js.map +1 -1
- package/package.json +50 -43
- package/public/3rdpartylicenses.txt +218 -108
- package/public/assets/hap-icons/airpurifier.svg +49 -16
- package/public/assets/hap-icons/airquality.svg +24 -13
- package/public/assets/hap-icons/co-sensor.svg +72 -0
- package/public/assets/hap-icons/co2-sensor.svg +72 -0
- package/public/assets/hap-icons/contactsensor-closed.svg +35 -2
- package/public/assets/hap-icons/contactsensor-open.svg +80 -2
- package/public/assets/hap-icons/door-closed.svg +32 -2
- package/public/assets/hap-icons/door-open.svg +48 -2
- package/public/assets/hap-icons/fan-off.svg +24 -13
- package/public/assets/hap-icons/fan-on.svg +24 -13
- package/public/assets/hap-icons/filtermaintenance.svg +26 -0
- package/public/assets/hap-icons/garagedoor.svg +24 -13
- package/public/assets/hap-icons/humidity.svg +24 -13
- package/public/assets/hap-icons/irrigation-system.svg +47 -18
- package/public/assets/hap-icons/leaksensor.svg +52 -2
- package/public/assets/hap-icons/light.svg +47 -28
- package/public/assets/hap-icons/lightbulb.svg +24 -13
- package/public/assets/hap-icons/lock-locked.svg +24 -13
- package/public/assets/hap-icons/lock-unlocked.svg +24 -13
- package/public/assets/hap-icons/motionsensor.svg +100 -2
- package/public/assets/hap-icons/occupancysensor.svg +97 -2
- package/public/assets/hap-icons/outlet.svg +24 -13
- package/public/assets/hap-icons/securitysystem-active.svg +102 -2
- package/public/assets/hap-icons/securitysystem-off.svg +68 -2
- package/public/assets/hap-icons/securitysystem-triggered.svg +103 -0
- package/public/assets/hap-icons/smokesensor.svg +42 -9
- package/public/assets/hap-icons/speaker.svg +29 -13
- package/public/assets/hap-icons/statelessprogrammableswitch.svg +51 -2
- package/public/assets/hap-icons/switch.svg +24 -13
- package/public/assets/hap-icons/television.svg +15 -4
- package/public/assets/hap-icons/temperature.svg +24 -13
- package/public/assets/hap-icons/unknown.svg +24 -13
- package/public/assets/hap-icons/valve-generic.svg +27 -16
- package/public/assets/hap-icons/valve-irrigation.svg +37 -21
- package/public/assets/hap-icons/valve-showerhead.svg +52 -0
- package/public/assets/hap-icons/valve-waterfaucet.svg +21 -0
- package/public/assets/hap-icons/window-closed.svg +85 -2
- package/public/assets/hap-icons/window-open.svg +136 -2
- package/public/assets/hap-icons/windowcovering-closed.svg +45 -49
- package/public/assets/hap-icons/windowcovering-open.svg +40 -44
- package/public/assets/homebridge-color-round.svg +36 -1
- package/public/assets/homebridge-logo.svg +11 -1
- package/public/assets/mask-icon.svg +5 -1
- package/public/assets/plugin-ui-utils/ui.js +3 -0
- package/public/assets/plugin-ui-utils/ui.js.map +1 -1
- package/public/chunk-2FVPRLQG.js +1 -0
- package/public/{chunk-MGBLPFXT.js → chunk-2GSALGGZ.js} +1 -1
- package/public/chunk-2IJPNUW7.js +29 -0
- package/public/chunk-2NQ5FYSH.js +5 -0
- package/public/chunk-3FQVTGEO.js +1 -0
- package/public/chunk-3Z2VETLJ.js +1 -0
- package/public/{chunk-3TUBDO76.js → chunk-4G5TRQV5.js} +1 -1
- package/public/chunk-4I6LLOMI.js +1 -0
- package/public/chunk-4N5XER24.js +1 -0
- package/public/chunk-555LSHXI.js +1 -0
- package/public/{chunk-CCUID66K.js → chunk-5CEP4QNR.js} +1 -1
- package/public/chunk-5NG7YGKN.js +1 -0
- package/public/chunk-6TUL34DU.js +1 -0
- package/public/{chunk-3RNRIJ74.js → chunk-6XANA56B.js} +11 -11
- package/public/chunk-BOWJ3GNE.js +1 -0
- package/public/chunk-BY2CUGGW.js +1 -0
- package/public/{chunk-LM6S4A72.js → chunk-CI7ES5EB.js} +1 -1
- package/public/chunk-CXAHE4UR.js +1 -0
- package/public/chunk-CYTL54HA.js +1 -0
- package/public/chunk-D35WUZO6.js +1 -0
- package/public/chunk-DM5SJAXR.js +1 -0
- package/public/chunk-DX3J7WR4.js +20 -0
- package/public/chunk-ELHQENNT.js +1 -0
- package/public/chunk-EM4PY2IW.js +2 -0
- package/public/chunk-FGR2Q3QC.js +1 -0
- package/public/chunk-FQM6JWF4.js +1 -0
- package/public/chunk-GD7WE5NM.js +1 -0
- package/public/{chunk-6TCHCTXZ.js → chunk-GMQEHLXV.js} +1 -1
- package/public/chunk-GSH6XPH5.js +1 -0
- package/public/chunk-HTBBDMIM.js +1 -0
- package/public/chunk-IMPGSXGE.js +1 -0
- package/public/chunk-IZCQX62P.js +1 -0
- package/public/chunk-IZENZ6C7.js +1 -0
- package/public/{chunk-QE7DO6J3.js → chunk-J764EFYE.js} +2 -2
- package/public/chunk-JBQX4JBH.js +1 -0
- package/public/chunk-JROQHF7T.js +1 -0
- package/public/chunk-JVERWDTV.js +8 -0
- package/public/chunk-KAHHKQN4.js +1 -0
- package/public/chunk-KFUUWA7X.js +1 -0
- package/public/chunk-LSWNZUER.js +1 -0
- package/public/chunk-M5VWLNY3.js +7 -0
- package/public/chunk-MDSOQRIW.js +1 -0
- package/public/chunk-MFBVZAEQ.js +2 -0
- package/public/chunk-MGITCDRJ.js +1 -0
- package/public/chunk-NQ5I37UV.js +1 -0
- package/public/chunk-NQE2RXH6.js +1 -0
- package/public/chunk-O3VYSP26.js +1 -0
- package/public/chunk-O7GTAWDZ.js +1 -0
- package/public/chunk-OR2P7NW7.js +6 -0
- package/public/chunk-OT72EUBX.js +1 -0
- package/public/chunk-OUNNY3LQ.js +1 -0
- package/public/chunk-P5VEHFPQ.js +1 -0
- package/public/chunk-QHLEZFHN.js +1 -0
- package/public/{chunk-WNWWUCCZ.js → chunk-QJQSHTGR.js} +3 -3
- package/public/chunk-QZ7YTQCT.js +1 -0
- package/public/chunk-R4C7YVNE.js +1 -0
- package/public/chunk-RPX42C27.js +1 -0
- package/public/{chunk-JZZQRLNW.js → chunk-S6UXXPGF.js} +1 -1
- package/public/chunk-SCRWY2OT.js +1 -0
- package/public/chunk-SVBSGSWA.js +1 -0
- package/public/chunk-SWUH5RAT.js +1 -0
- package/public/chunk-SYDH2EUJ.js +1 -0
- package/public/{chunk-EA5J2VEJ.js → chunk-TJJCM4OW.js} +1 -1
- package/public/chunk-TNXIENZS.js +1 -0
- package/public/{chunk-UPYKPJUD.js → chunk-TY327ZWV.js} +2 -2
- package/public/chunk-U3GYVCZ5.js +1 -0
- package/public/chunk-UC3VLQ36.js +23 -0
- package/public/chunk-UTDF2KPP.js +1 -0
- package/public/chunk-UXMF7TL3.js +1 -0
- package/public/chunk-VFFTVCSV.js +1 -0
- package/public/chunk-VP6YRYDX.js +1 -0
- package/public/chunk-W6X46MTW.js +7 -0
- package/public/chunk-WP6HJUZ6.js +1 -0
- package/public/{chunk-7EUQWCP5.js → chunk-WTTWP6KF.js} +2 -2
- package/public/chunk-WUXDJZ6X.js +1 -0
- package/public/chunk-WXHERXH5.js +1 -0
- package/public/chunk-YLZEJ2HL.js +5 -0
- package/public/chunk-YRRMQRGZ.js +1 -0
- package/public/chunk-YSWBG5VY.js +8 -0
- package/public/chunk-Z7NIAJEB.js +1 -0
- package/public/chunk-ZI5BUHBS.js +1 -0
- package/public/index.html +2 -2
- package/public/main-EWBCXJ6H.js +1 -0
- package/public/media/fa-brands-400-Q3XCMWHQ.woff2 +0 -0
- package/public/media/{fa-brands-400-KOKGDU7E.ttf → fa-brands-400-R2XQZCET.ttf} +0 -0
- package/public/media/fa-regular-400-QSNYFYRT.woff2 +0 -0
- package/public/media/{fa-regular-400-IPMAEX5Y.ttf → fa-regular-400-XUOPSR7E.ttf} +0 -0
- package/public/media/fa-solid-900-5ZUYHGA7.woff2 +0 -0
- package/public/media/{fa-solid-900-SRFFQLRM.ttf → fa-solid-900-PJNKLK6W.ttf} +0 -0
- package/public/polyfills-WE4HA5DL.js +2 -0
- package/public/styles-SK4HZ2RX.css +1 -0
- package/scripts/upgrade-install-plugin.sh +1 -1
- package/public/chunk-23E7VL2P.js +0 -1
- package/public/chunk-33LJ3YIR.js +0 -1
- package/public/chunk-34W4KCBV.js +0 -1
- package/public/chunk-3IX3CLER.js +0 -1
- package/public/chunk-3UBYWHRR.js +0 -1
- package/public/chunk-52HTAWOT.js +0 -1
- package/public/chunk-535DPMCW.js +0 -1
- package/public/chunk-56BOPXKC.js +0 -1
- package/public/chunk-5M6KOARX.js +0 -1
- package/public/chunk-6IAVZXBU.js +0 -20
- package/public/chunk-6T27YQ2D.js +0 -1
- package/public/chunk-7WLTVXXL.js +0 -1
- package/public/chunk-AS2OC4NZ.js +0 -1
- package/public/chunk-BKUGARB4.js +0 -7
- package/public/chunk-BLUX2UMQ.js +0 -1
- package/public/chunk-BPMSJ2VF.js +0 -1
- package/public/chunk-BW6NZ6VD.js +0 -6
- package/public/chunk-C4ENUEJD.js +0 -1
- package/public/chunk-C536FAQ7.js +0 -5
- package/public/chunk-CL4YMJZW.js +0 -1
- package/public/chunk-D7HF5OGR.js +0 -1
- package/public/chunk-DD3R2DFS.js +0 -1
- package/public/chunk-DHAYGFW2.js +0 -1
- package/public/chunk-DVTSZQXS.js +0 -1
- package/public/chunk-EIBIFGLP.js +0 -1
- package/public/chunk-F4CW25FV.js +0 -1
- package/public/chunk-HL7XXKHI.js +0 -1
- package/public/chunk-HSJSWZHD.js +0 -1
- package/public/chunk-HUV3VJSE.js +0 -1
- package/public/chunk-INJABMER.js +0 -23
- package/public/chunk-IU27XSWW.js +0 -1
- package/public/chunk-JVAHZDVJ.js +0 -1
- package/public/chunk-KQJ7ONUG.js +0 -1
- package/public/chunk-KS3FIR4R.js +0 -1
- package/public/chunk-KXWJIWYD.js +0 -1
- package/public/chunk-LDGB6S2N.js +0 -1
- package/public/chunk-M45D5CCT.js +0 -1
- package/public/chunk-MIMQGXTO.js +0 -1
- package/public/chunk-NKN62APE.js +0 -1
- package/public/chunk-NW6AFAD7.js +0 -1
- package/public/chunk-NZNNTHFQ.js +0 -1
- package/public/chunk-ORPWYWCL.js +0 -5
- package/public/chunk-Q2YQ4J5L.js +0 -1
- package/public/chunk-QHPDGSZ6.js +0 -1
- package/public/chunk-QXHB5Q7H.js +0 -1
- package/public/chunk-R2QZPN2M.js +0 -1
- package/public/chunk-RDOXIKK6.js +0 -1
- package/public/chunk-RHBPQJLQ.js +0 -1
- package/public/chunk-RSGTDKO6.js +0 -1
- package/public/chunk-RTVXC3Y7.js +0 -1
- package/public/chunk-SFK7OSIU.js +0 -1
- package/public/chunk-SFLEKDGL.js +0 -1
- package/public/chunk-T5R3UG6Z.js +0 -1
- package/public/chunk-TB7FJ5XX.js +0 -1
- package/public/chunk-TCSXGQNF.js +0 -1
- package/public/chunk-TRTZHJGG.js +0 -1
- package/public/chunk-TWWDGSRW.js +0 -1
- package/public/chunk-TXOB7R5K.js +0 -7
- package/public/chunk-U2WFZQTC.js +0 -32
- package/public/chunk-UG5DK2RQ.js +0 -2
- package/public/chunk-UVNEKL77.js +0 -1
- package/public/chunk-VHLIKZYD.js +0 -1
- package/public/chunk-WHJOLAED.js +0 -1
- package/public/chunk-WHJSVGC7.js +0 -1
- package/public/chunk-Y62756RF.js +0 -1
- package/public/chunk-YA4WNIBX.js +0 -1
- package/public/chunk-ZGUIFA2B.js +0 -1
- package/public/chunk-ZLOETFRA.js +0 -8
- package/public/chunk-ZT23DWNL.js +0 -1
- package/public/main-TLKQDE7H.js +0 -1
- package/public/media/01-RQ3S2L53.png +0 -0
- package/public/media/02-VNCG2I2A.png +0 -0
- package/public/media/03-HI42L4ZG.png +0 -0
- package/public/media/04-FJLL55LZ.png +0 -0
- package/public/media/05-V3EO6SPT.png +0 -0
- package/public/media/06-EOJZCQZN.png +0 -0
- package/public/media/07-KMKB5PBD.png +0 -0
- package/public/media/08-UQJRF6B2.png +0 -0
- package/public/media/09-2DJQFRHH.png +0 -0
- package/public/media/arrow_left-CQT7FZM7.svg +0 -4
- package/public/media/arrow_right-SBUDRR2G.svg +0 -4
- package/public/media/fa-brands-400-6PJPV6JM.woff2 +0 -0
- package/public/media/fa-regular-400-OHB6J4OK.woff2 +0 -0
- package/public/media/fa-solid-900-ABTK6BNK.woff2 +0 -0
- package/public/polyfills-C6JHVXJJ.js +0 -2
- package/public/scripts-6GVLYD7F.js +0 -62
- package/public/styles-EG5MFQEM.css +0 -1
- /package/public/assets/{bootstrap-4 → bootstrap-5}/cssframework/assets.json +0 -0
|
@@ -40,11 +40,13 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
40
40
|
this.npm = this.getNpmPath();
|
|
41
41
|
this.paths = this.getBasePaths();
|
|
42
42
|
this.pluginListUrl = 'https://raw.githubusercontent.com/homebridge/plugins/latest/';
|
|
43
|
-
this.pluginListFile = `${this.pluginListUrl}assets/plugins.min.json`;
|
|
43
|
+
this.pluginListFile = `${this.pluginListUrl}assets/plugins-v2.min.json`;
|
|
44
44
|
this.hiddenPlugins = [];
|
|
45
45
|
this.maintainedPlugins = [];
|
|
46
46
|
this.pluginIcons = {};
|
|
47
|
-
this.
|
|
47
|
+
this.pluginAuthors = {};
|
|
48
|
+
this.pluginNames = {};
|
|
49
|
+
this.pluginChangelogs = {};
|
|
48
50
|
this.newScopePlugins = {};
|
|
49
51
|
this.verifiedPlugins = [];
|
|
50
52
|
this.verifiedPlusPlugins = [];
|
|
@@ -67,6 +69,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
67
69
|
this.loadPluginList();
|
|
68
70
|
setInterval(this.loadPluginList.bind(this), 60000 * 60 * 12);
|
|
69
71
|
}
|
|
72
|
+
fixDisplayName(plugin) {
|
|
73
|
+
plugin.displayName = plugin.displayName || (plugin.name.charAt(0) === '@' ? plugin.name.split('/')[1] : plugin.name)
|
|
74
|
+
.replace(/-/g, ' ')
|
|
75
|
+
.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase());
|
|
76
|
+
return plugin;
|
|
77
|
+
}
|
|
70
78
|
async getInstalledPlugins() {
|
|
71
79
|
const plugins = [];
|
|
72
80
|
const modules = await this.getInstalledModules();
|
|
@@ -92,12 +100,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
catch (e) {
|
|
95
|
-
this.logger.error(`Failed to parse plugin
|
|
103
|
+
this.logger.error(`Failed to parse plugin ${pkg.name} as ${e.message}.`);
|
|
96
104
|
}
|
|
97
105
|
});
|
|
98
106
|
}));
|
|
99
|
-
this.installedPlugins = plugins;
|
|
100
|
-
return
|
|
107
|
+
this.installedPlugins = plugins.map(plugin => this.fixDisplayName(plugin));
|
|
108
|
+
return this.installedPlugins;
|
|
101
109
|
}
|
|
102
110
|
async getOutOfDatePlugins() {
|
|
103
111
|
const plugins = await this.getInstalledPlugins();
|
|
@@ -142,58 +150,111 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
142
150
|
throw new common_1.NotFoundException();
|
|
143
151
|
}
|
|
144
152
|
}
|
|
153
|
+
extractTerms(query, separator) {
|
|
154
|
+
return query
|
|
155
|
+
.toLowerCase()
|
|
156
|
+
.split(separator)
|
|
157
|
+
.map(term => term.trim())
|
|
158
|
+
.filter(term => term && term !== 'homebridge' && term !== 'plugin');
|
|
159
|
+
}
|
|
160
|
+
getPluginKeywords(plugin) {
|
|
161
|
+
return Array.isArray(plugin.keywords)
|
|
162
|
+
? plugin.keywords.map(k => k.toLowerCase())
|
|
163
|
+
: [];
|
|
164
|
+
}
|
|
165
|
+
matchesPlugin(plugin, searchTerms) {
|
|
166
|
+
const pluginName = plugin.name.toLowerCase();
|
|
167
|
+
const pluginKeywords = this.getPluginKeywords(plugin);
|
|
168
|
+
const pluginDescription = (plugin.description || '').toLowerCase();
|
|
169
|
+
const nameTerms = this.extractTerms(pluginName.substring(pluginName.lastIndexOf('/') + 1), /-/);
|
|
170
|
+
if (nameTerms.every(term => searchTerms.includes(term))) {
|
|
171
|
+
return 'exactName';
|
|
172
|
+
}
|
|
173
|
+
if (searchTerms.every(term => pluginKeywords.includes(term))
|
|
174
|
+
|| searchTerms.every(term => nameTerms.includes(term))) {
|
|
175
|
+
return 'exactKeyword';
|
|
176
|
+
}
|
|
177
|
+
if (searchTerms.some(term => pluginName.includes(term))
|
|
178
|
+
|| searchTerms.some(term => pluginKeywords.some(k => k.includes(term)))
|
|
179
|
+
|| searchTerms.some(term => pluginDescription.includes(term))) {
|
|
180
|
+
return 'partial';
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
145
184
|
async searchNpmRegistry(query) {
|
|
146
185
|
if (!this.installedPlugins) {
|
|
147
186
|
await this.getInstalledPlugins();
|
|
148
187
|
}
|
|
149
|
-
const
|
|
188
|
+
const searchTerms = this.extractTerms(query, /\s+/);
|
|
189
|
+
const normalizedQuery = searchTerms.length > 0 ? searchTerms.join(' ') : 'homebridge';
|
|
190
|
+
if ((normalizedQuery.startsWith('homebridge-') || this.isScopedPlugin(normalizedQuery))
|
|
191
|
+
&& !this.hiddenPlugins.includes(normalizedQuery)) {
|
|
192
|
+
if (!this.installedPlugins.find(x => x.name === normalizedQuery)
|
|
193
|
+
&& Object.keys(this.newScopePlugins).includes(normalizedQuery)) {
|
|
194
|
+
return await this.searchNpmRegistrySingle(`@homebridge-plugins/${normalizedQuery}`);
|
|
195
|
+
}
|
|
196
|
+
return await this.searchNpmRegistrySingle(normalizedQuery);
|
|
197
|
+
}
|
|
198
|
+
const q = `${normalizedQuery.substring(0, 15)}+keywords:homebridge-plugin+not:deprecated&size=99`;
|
|
150
199
|
let searchResults;
|
|
151
200
|
try {
|
|
152
201
|
searchResults = (await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://registry.npmjs.org/-/v1/search?text=${q}`))).data;
|
|
153
202
|
}
|
|
154
203
|
catch (e) {
|
|
155
|
-
this.logger.error(`Failed to search the npm registry
|
|
156
|
-
throw new common_1.InternalServerErrorException(`Failed to search the npm registry
|
|
204
|
+
this.logger.error(`Failed to search the npm registry (see https://homebridge.io/w/JJSz6 for help) as ${e.message}.`);
|
|
205
|
+
throw new common_1.InternalServerErrorException(`Failed to search the npm registry as ${e.message}, see logs.`);
|
|
157
206
|
}
|
|
158
|
-
const
|
|
159
|
-
.filter(x => x.package.name.
|
|
207
|
+
const plugins = searchResults.objects
|
|
208
|
+
.filter(x => x.package.name.startsWith('homebridge-') || this.isScopedPlugin(x.package.name))
|
|
160
209
|
.filter(x => !this.hiddenPlugins.includes(x.package.name))
|
|
161
210
|
.map((pkg) => {
|
|
162
|
-
|
|
211
|
+
const isInstalled = this.installedPlugins.find(x => x.name === pkg.package.name);
|
|
212
|
+
if (isInstalled) {
|
|
213
|
+
return {
|
|
214
|
+
...isInstalled,
|
|
215
|
+
lastUpdated: pkg.package.date,
|
|
216
|
+
keywords: pkg.package.keywords || [],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
163
220
|
name: pkg.package.name,
|
|
221
|
+
displayName: this.pluginNames[pkg.package.name],
|
|
164
222
|
private: false,
|
|
223
|
+
publicPackage: true,
|
|
224
|
+
installedVersion: null,
|
|
225
|
+
latestVersion: pkg.package.version,
|
|
226
|
+
lastUpdated: pkg.package.date,
|
|
227
|
+
description: (pkg.package.description || pkg.package.name).replace(/\(?(?:https?|ftp):\/\/[\n\S]+/g, '').trim(),
|
|
228
|
+
keywords: pkg.package.keywords || [],
|
|
229
|
+
links: pkg.package.links,
|
|
230
|
+
author: this.pluginAuthors[pkg.package.name] || (pkg.package.publisher ? pkg.package.publisher.username : null),
|
|
231
|
+
verifiedPlugin: this.verifiedPlugins.includes(pkg.package.name),
|
|
232
|
+
verifiedPlusPlugin: this.verifiedPlusPlugins.includes(pkg.package.name),
|
|
233
|
+
icon: this.pluginIcons[pkg.package.name] ? `${this.pluginListUrl}${this.pluginIcons[pkg.package.name]}` : null,
|
|
234
|
+
isHbScoped: pkg.package.name.startsWith('@homebridge-plugins/'),
|
|
235
|
+
newHbScope: this.newScopePlugins[pkg.package.name],
|
|
236
|
+
isHbMaintained: this.maintainedPlugins.includes(pkg.package.name),
|
|
165
237
|
};
|
|
166
|
-
const isInstalled = this.installedPlugins.find(x => x.name === plugin.name);
|
|
167
|
-
if (isInstalled) {
|
|
168
|
-
plugin = isInstalled;
|
|
169
|
-
plugin.lastUpdated = pkg.package.date;
|
|
170
|
-
return plugin;
|
|
171
|
-
}
|
|
172
|
-
plugin.publicPackage = true;
|
|
173
|
-
plugin.installedVersion = null;
|
|
174
|
-
plugin.latestVersion = pkg.package.version;
|
|
175
|
-
plugin.lastUpdated = pkg.package.date;
|
|
176
|
-
plugin.description = (pkg.package.description)
|
|
177
|
-
? pkg.package.description.replace(/\(?(?:https?|ftp):\/\/[\n\S]+/g, '').trim()
|
|
178
|
-
: pkg.package.name;
|
|
179
|
-
plugin.links = pkg.package.links;
|
|
180
|
-
plugin.author = this.scopedPlugins[pkg.package.name] || ((pkg.package.publisher) ? pkg.package.publisher.username : null);
|
|
181
|
-
plugin.verifiedPlugin = this.verifiedPlugins.includes(pkg.package.name);
|
|
182
|
-
plugin.verifiedPlusPlugin = this.verifiedPlusPlugins.includes(pkg.package.name);
|
|
183
|
-
plugin.icon = this.pluginIcons[pkg.package.name]
|
|
184
|
-
? `${this.pluginListUrl}${this.pluginIcons[pkg.package.name]}`
|
|
185
|
-
: null;
|
|
186
|
-
plugin.isHbScoped = !!this.scopedPlugins[pkg.package.name];
|
|
187
|
-
plugin.newHbScope = this.newScopePlugins[pkg.package.name];
|
|
188
|
-
plugin.isHbMaintained = this.maintainedPlugins.includes(pkg.package.name);
|
|
189
|
-
return plugin;
|
|
190
238
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
239
|
+
const matchGroups = {
|
|
240
|
+
exactName: [],
|
|
241
|
+
exactKeyword: [],
|
|
242
|
+
partial: [],
|
|
243
|
+
};
|
|
244
|
+
for (const plugin of plugins) {
|
|
245
|
+
const matchType = this.matchesPlugin(plugin, searchTerms);
|
|
246
|
+
if (matchType) {
|
|
247
|
+
matchGroups[matchType].push(plugin);
|
|
248
|
+
}
|
|
195
249
|
}
|
|
196
|
-
|
|
250
|
+
const orderPlugins = (arr) => (0, lodash_1.orderBy)(arr, ['isHbScoped', 'verifiedPlusPlugin', 'verifiedPlugin', 'lastUpdated'], ['desc', 'desc', 'desc']);
|
|
251
|
+
return [
|
|
252
|
+
...orderPlugins(matchGroups.exactName),
|
|
253
|
+
...orderPlugins(matchGroups.exactKeyword),
|
|
254
|
+
...orderPlugins(matchGroups.partial),
|
|
255
|
+
]
|
|
256
|
+
.slice(0, 30)
|
|
257
|
+
.map(plugin => this.fixDisplayName(plugin));
|
|
197
258
|
}
|
|
198
259
|
async searchNpmRegistrySingle(query) {
|
|
199
260
|
try {
|
|
@@ -224,10 +285,11 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
224
285
|
verifiedPlugin: this.verifiedPlugins.includes(pkg.name),
|
|
225
286
|
verifiedPlusPlugin: this.verifiedPlusPlugins.includes(pkg.name),
|
|
226
287
|
icon: this.pluginIcons[pkg.name],
|
|
227
|
-
isHbScoped:
|
|
288
|
+
isHbScoped: pkg.name.startsWith('@homebridge-plugins/'),
|
|
228
289
|
newHbScope: this.newScopePlugins[pkg.name],
|
|
229
290
|
isHbMaintained: this.maintainedPlugins.includes(pkg.name),
|
|
230
291
|
};
|
|
292
|
+
plugin.displayName = this.pluginNames[pkg.name];
|
|
231
293
|
plugin.publicPackage = true;
|
|
232
294
|
plugin.latestVersion = pkg['dist-tags'] ? pkg['dist-tags'].latest : undefined;
|
|
233
295
|
plugin.lastUpdated = pkg.time.modified;
|
|
@@ -238,38 +300,38 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
238
300
|
homepage: pkg.homepage,
|
|
239
301
|
bugs: typeof pkg.bugs === 'object' && pkg.bugs?.url ? pkg.bugs.url : null,
|
|
240
302
|
};
|
|
241
|
-
plugin.author = this.
|
|
303
|
+
plugin.author = this.pluginAuthors[pkg.name]
|
|
242
304
|
|| ((pkg.maintainers && pkg.maintainers.length) ? pkg.maintainers[0].name : null);
|
|
243
305
|
plugin.verifiedPlugin = this.verifiedPlugins.includes(pkg.name);
|
|
244
306
|
plugin.verifiedPlusPlugin = this.verifiedPlusPlugins.includes(pkg.name);
|
|
245
307
|
plugin.icon = this.pluginIcons[pkg.name]
|
|
246
308
|
? `${this.pluginListUrl}${this.pluginIcons[pkg.name]}`
|
|
247
309
|
: null;
|
|
248
|
-
plugin.isHbScoped =
|
|
310
|
+
plugin.isHbScoped = pkg.name.startsWith('@homebridge-plugins/');
|
|
249
311
|
plugin.newHbScope = this.newScopePlugins[pkg.name];
|
|
250
312
|
plugin.isHbMaintained = this.maintainedPlugins.includes(pkg.name);
|
|
251
|
-
return [plugin];
|
|
313
|
+
return [this.fixDisplayName(plugin)];
|
|
252
314
|
}
|
|
253
315
|
catch (e) {
|
|
254
316
|
if (e.response?.status !== 404) {
|
|
255
|
-
this.logger.error(`Failed to search the npm registry
|
|
317
|
+
this.logger.error(`Failed to search the npm registry (see https://homebridge.io/w/JJSz6 for help) as ${e.message}.`);
|
|
256
318
|
}
|
|
257
319
|
return [];
|
|
258
320
|
}
|
|
259
321
|
}
|
|
260
|
-
async
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
throw new Error(`Cannot uninstall ${pluginAction.name} from ${this.configService.name}.`);
|
|
322
|
+
async manageUi(action, pluginAction, client) {
|
|
323
|
+
if (action === 'uninstall') {
|
|
324
|
+
throw new Error('Cannot uninstall the Homebridge UI.');
|
|
264
325
|
}
|
|
265
|
-
if (
|
|
326
|
+
if (this.configService.dockerOfflineUpdate && pluginAction.version === 'latest') {
|
|
266
327
|
await this.updateSelfOffline(client);
|
|
267
328
|
return true;
|
|
268
329
|
}
|
|
269
330
|
if (action === 'install' && pluginAction.version === 'latest') {
|
|
270
331
|
pluginAction.version = await this.getNpmModuleLatestVersion(pluginAction.name);
|
|
271
332
|
}
|
|
272
|
-
|
|
333
|
+
const userPlatform = (0, node_os_1.platform)();
|
|
334
|
+
let installPath = this.configService.customPluginPath
|
|
273
335
|
? this.configService.customPluginPath
|
|
274
336
|
: this.installedPlugins.find(x => x.name === this.configService.name).installPath;
|
|
275
337
|
await this.getInstalledPlugins();
|
|
@@ -277,24 +339,53 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
277
339
|
if (existingPlugin) {
|
|
278
340
|
installPath = existingPlugin.installPath;
|
|
279
341
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return true;
|
|
286
|
-
}
|
|
287
|
-
catch (e) {
|
|
288
|
-
client.emit('stdout', (0, bash_color_1.yellow)('\r\nBundled update failed. Trying regular update using npm.\r\n\r\n'));
|
|
289
|
-
}
|
|
342
|
+
const githubReleaseName = await this.isUiUpdateBundleAvailable(pluginAction);
|
|
343
|
+
if (githubReleaseName) {
|
|
344
|
+
try {
|
|
345
|
+
await this.doUiBundleUpdate(pluginAction, client, githubReleaseName);
|
|
346
|
+
return true;
|
|
290
347
|
}
|
|
291
|
-
|
|
292
|
-
client.emit('stdout', (0, bash_color_1.yellow)('
|
|
293
|
-
client.emit('stdout', (0, bash_color_1.yellow)(`Please be patient while ${this.configService.name} updates.\r\n`));
|
|
294
|
-
client.emit('stdout', (0, bash_color_1.yellow)('This process may take 5-15 minutes to complete on your device.\r\n'));
|
|
295
|
-
client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n\r\n'));
|
|
348
|
+
catch (e) {
|
|
349
|
+
client.emit('stdout', (0, bash_color_1.yellow)('\r\nBundled update failed. Trying regular update using npm.\r\n\r\n'));
|
|
296
350
|
}
|
|
297
351
|
}
|
|
352
|
+
if ((0, node_os_1.cpus)().length === 1 && (0, node_os_1.arch)() === 'arm') {
|
|
353
|
+
client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n'));
|
|
354
|
+
client.emit('stdout', (0, bash_color_1.yellow)(`Please be patient while ${this.configService.name} updates.\r\n`));
|
|
355
|
+
client.emit('stdout', (0, bash_color_1.yellow)('This process may take 5-15 minutes to complete on your device.\r\n'));
|
|
356
|
+
client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n\r\n'));
|
|
357
|
+
}
|
|
358
|
+
const installOptions = [];
|
|
359
|
+
if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
|
|
360
|
+
installOptions.push('--save');
|
|
361
|
+
}
|
|
362
|
+
installPath = (0, node_path_1.resolve)(installPath, '../');
|
|
363
|
+
if (!this.configService.customPluginPath || userPlatform === 'win32' || existingPlugin?.globalInstall === true) {
|
|
364
|
+
installOptions.push('-g');
|
|
365
|
+
}
|
|
366
|
+
installOptions.push('--omit=dev');
|
|
367
|
+
const npmPluginLabel = `${pluginAction.name}@${pluginAction.version}`;
|
|
368
|
+
await this.cleanNpmCache();
|
|
369
|
+
await this.runNpmCommand([...this.npm, action, ...installOptions, npmPluginLabel], installPath, client, pluginAction.termCols, pluginAction.termRows);
|
|
370
|
+
await this.ensureCustomPluginDirExists();
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
async managePlugin(action, pluginAction, client) {
|
|
374
|
+
pluginAction.version = pluginAction.version || 'latest';
|
|
375
|
+
if (pluginAction.name === this.configService.name) {
|
|
376
|
+
return await this.manageUi(action, pluginAction, client);
|
|
377
|
+
}
|
|
378
|
+
if (action === 'install' && pluginAction.version === 'latest') {
|
|
379
|
+
pluginAction.version = await this.getNpmModuleLatestVersion(pluginAction.name);
|
|
380
|
+
}
|
|
381
|
+
let installPath = this.configService.customPluginPath
|
|
382
|
+
? this.configService.customPluginPath
|
|
383
|
+
: this.installedPlugins.find(x => x.name === this.configService.name).installPath;
|
|
384
|
+
await this.getInstalledPlugins();
|
|
385
|
+
const existingPlugin = this.installedPlugins.find(x => x.name === pluginAction.name);
|
|
386
|
+
if (existingPlugin) {
|
|
387
|
+
installPath = existingPlugin.installPath;
|
|
388
|
+
}
|
|
298
389
|
if (action === 'install' && await this.isPluginBundleAvailable(pluginAction)) {
|
|
299
390
|
try {
|
|
300
391
|
await this.doPluginBundleUpdate(pluginAction, client);
|
|
@@ -305,6 +396,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
305
396
|
}
|
|
306
397
|
}
|
|
307
398
|
const installOptions = [];
|
|
399
|
+
let npmPluginLabel = pluginAction.name;
|
|
308
400
|
if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
|
|
309
401
|
installOptions.push('--save');
|
|
310
402
|
}
|
|
@@ -312,20 +404,14 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
312
404
|
if (!this.configService.customPluginPath || (0, node_os_1.platform)() === 'win32' || existingPlugin?.globalInstall === true) {
|
|
313
405
|
installOptions.push('-g');
|
|
314
406
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
await this.ensureCustomPluginDirExists();
|
|
319
|
-
return true;
|
|
320
|
-
}
|
|
321
|
-
catch (e) {
|
|
322
|
-
if (pluginAction.name === this.configService.name) {
|
|
323
|
-
client.emit('stdout', (0, bash_color_1.yellow)('\r\nCleaning up npm cache, please wait...\r\n'));
|
|
324
|
-
await this.cleanNpmCache();
|
|
325
|
-
client.emit('stdout', (0, bash_color_1.yellow)(`npm cache cleared, please try updating ${this.configService.name} again.\r\n`));
|
|
326
|
-
}
|
|
327
|
-
throw e;
|
|
407
|
+
if (action === 'install') {
|
|
408
|
+
installOptions.push('--omit=dev');
|
|
409
|
+
npmPluginLabel = `${pluginAction.name}@${pluginAction.version}`;
|
|
328
410
|
}
|
|
411
|
+
await this.cleanNpmCache();
|
|
412
|
+
await this.runNpmCommand([...this.npm, action, ...installOptions, npmPluginLabel], installPath, client, pluginAction.termCols, pluginAction.termRows);
|
|
413
|
+
await this.ensureCustomPluginDirExists();
|
|
414
|
+
return true;
|
|
329
415
|
}
|
|
330
416
|
async getHomebridgePackage() {
|
|
331
417
|
if (this.configService.ui.homebridgePackagePath) {
|
|
@@ -334,21 +420,21 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
334
420
|
return await this.parsePackageJson(await (0, fs_extra_1.readJson)(pkgJsonPath), this.configService.ui.homebridgePackagePath);
|
|
335
421
|
}
|
|
336
422
|
else {
|
|
337
|
-
this.logger.error(`
|
|
423
|
+
this.logger.error(`The Homebridge path ${this.configService.ui.homebridgePackagePath} does not exist.`);
|
|
338
424
|
}
|
|
339
425
|
}
|
|
340
426
|
const modules = await this.getInstalledModules();
|
|
341
427
|
const homebridgeInstalls = modules.filter(x => x.name === 'homebridge');
|
|
342
428
|
if (homebridgeInstalls.length > 1) {
|
|
343
|
-
this.logger.warn('Multiple
|
|
429
|
+
this.logger.warn('Multiple instances of Homebridge were found, see https://homebridge.io/w/JJSgm for help.');
|
|
344
430
|
homebridgeInstalls.forEach((instance) => {
|
|
345
431
|
this.logger.warn(instance.installPath);
|
|
346
432
|
});
|
|
347
433
|
}
|
|
348
434
|
if (!homebridgeInstalls.length) {
|
|
349
435
|
this.configService.hbServiceUiRestartRequired = true;
|
|
350
|
-
this.logger.error('Unable
|
|
351
|
-
throw new Error('Unable To Find Homebridge Installation');
|
|
436
|
+
this.logger.error('Unable to find Homebridge installation, see https://homebridge.io/w/JJSgZ for help.');
|
|
437
|
+
throw new Error('Unable To Find Homebridge Installation.');
|
|
352
438
|
}
|
|
353
439
|
const homebridgeModule = homebridgeInstalls[0];
|
|
354
440
|
const pkgJson = await (0, fs_extra_1.readJson)((0, node_path_1.join)(homebridgeModule.installPath, 'package.json'));
|
|
@@ -378,6 +464,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
378
464
|
}
|
|
379
465
|
let installPath = homebridge.installPath;
|
|
380
466
|
const installOptions = [];
|
|
467
|
+
installOptions.push('--omit=dev');
|
|
381
468
|
if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
|
|
382
469
|
installOptions.push('--save');
|
|
383
470
|
}
|
|
@@ -416,7 +503,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
416
503
|
&& pluginAction.name !== this.configService.name
|
|
417
504
|
&& pluginAction.version !== 'latest') {
|
|
418
505
|
try {
|
|
419
|
-
await (0, rxjs_1.firstValueFrom)(this.httpService.head(`https://github.com/homebridge/
|
|
506
|
+
await (0, rxjs_1.firstValueFrom)(this.httpService.head(`https://github.com/homebridge/plugins/releases/download/v1.0.0/${pluginAction.name.replace('/', '@')}-${pluginAction.version}.sha256`));
|
|
420
507
|
return true;
|
|
421
508
|
}
|
|
422
509
|
catch (e) {
|
|
@@ -440,7 +527,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
440
527
|
'/var/packages/homebridge/target/app/lib/node_modules',
|
|
441
528
|
].includes((0, node_path_1.dirname)(node_process_1.default.env.UIX_BASE_PATH))
|
|
442
529
|
&& pluginAction.name === this.configService.name
|
|
443
|
-
&&
|
|
530
|
+
&& !['latest', 'alpha', 'beta'].includes(pluginAction.version)) {
|
|
444
531
|
try {
|
|
445
532
|
try {
|
|
446
533
|
const withV = `v${pluginAction.version}`;
|
|
@@ -454,7 +541,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
454
541
|
}
|
|
455
542
|
}
|
|
456
543
|
catch (e) {
|
|
457
|
-
this.logger.error(`Failed to check for bundled update: ${e.message}
|
|
544
|
+
this.logger.error(`Failed to check for bundled update: ${e.message}.`);
|
|
458
545
|
return '';
|
|
459
546
|
}
|
|
460
547
|
}
|
|
@@ -494,27 +581,19 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
494
581
|
let configSchema = await (0, fs_extra_1.readJson)(schemaPath);
|
|
495
582
|
if (configSchema.dynamicSchemaVersion) {
|
|
496
583
|
const dynamicSchemaPath = (0, node_path_1.resolve)(this.configService.storagePath, `.${pluginName}-v${configSchema.dynamicSchemaVersion}.schema.json`);
|
|
497
|
-
this.logger.log(`[${pluginName}] dynamic schema path: ${dynamicSchemaPath}
|
|
584
|
+
this.logger.log(`[${pluginName}] dynamic schema path: ${dynamicSchemaPath}.`);
|
|
498
585
|
if ((0, fs_extra_1.existsSync)(dynamicSchemaPath)) {
|
|
499
586
|
try {
|
|
500
587
|
configSchema = await (0, fs_extra_1.readJson)(dynamicSchemaPath);
|
|
501
|
-
this.logger.log(`[${pluginName}] dynamic schema loaded from
|
|
588
|
+
this.logger.log(`[${pluginName}] dynamic schema loaded from ${dynamicSchemaPath}.`);
|
|
502
589
|
}
|
|
503
590
|
catch (e) {
|
|
504
|
-
this.logger.error(`[${pluginName}]
|
|
591
|
+
this.logger.error(`[${pluginName}] failed to load dynamic schema from ${dynamicSchemaPath} as ${e.message}.`);
|
|
505
592
|
}
|
|
506
593
|
}
|
|
507
594
|
}
|
|
508
595
|
if (pluginName === this.configService.name) {
|
|
509
596
|
configSchema.schema.properties.port.default = this.configService.ui.port;
|
|
510
|
-
if (this.configService.serviceMode) {
|
|
511
|
-
configSchema.layout = configSchema.layout.filter((x) => {
|
|
512
|
-
return x.ref !== 'log';
|
|
513
|
-
});
|
|
514
|
-
configSchema.layout = configSchema.layout.filter((x) => {
|
|
515
|
-
return !(x === 'sudo' || x.key === 'restart');
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
597
|
}
|
|
519
598
|
if (pluginName === 'homebridge-alexa') {
|
|
520
599
|
configSchema.schema.properties.pin.default = this.configService.homebridgeConfig.bridge.pin;
|
|
@@ -595,57 +674,81 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
595
674
|
}
|
|
596
675
|
}
|
|
597
676
|
async getPluginRelease(pluginName) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
if (!plugin) {
|
|
603
|
-
throw new common_1.NotFoundException();
|
|
677
|
+
let latestVersion = null;
|
|
678
|
+
try {
|
|
679
|
+
const pkg = (await (0, rxjs_1.firstValueFrom)((this.httpService.get(`https://registry.npmjs.org/${encodeURIComponent(pluginName).replace(/%40/g, '@')}`)))).data;
|
|
680
|
+
latestVersion = pkg['dist-tags'] ? pkg['dist-tags'].latest : null;
|
|
604
681
|
}
|
|
605
|
-
|
|
682
|
+
catch (e) {
|
|
606
683
|
throw new common_1.NotFoundException();
|
|
607
684
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
685
|
+
switch (pluginName) {
|
|
686
|
+
case 'homebridge':
|
|
687
|
+
case 'homebridge-config-ui-x': {
|
|
688
|
+
try {
|
|
689
|
+
const release = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://api.github.com/repos/homebridge/${pluginName}/releases/latest`));
|
|
690
|
+
const changelog = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://raw.githubusercontent.com/homebridge/${pluginName}/refs/tags/${release.data.tag_name}/CHANGELOG.md`));
|
|
691
|
+
return {
|
|
692
|
+
name: release.data.name,
|
|
693
|
+
notes: release.data.body,
|
|
694
|
+
changelog: changelog.data,
|
|
695
|
+
latestVersion,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
catch {
|
|
699
|
+
return {
|
|
700
|
+
name: null,
|
|
701
|
+
notes: null,
|
|
702
|
+
changelog: null,
|
|
703
|
+
latestVersion,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
614
706
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
707
|
+
default: {
|
|
708
|
+
await this.getInstalledPlugins();
|
|
709
|
+
const plugin = this.installedPlugins.find(x => x.name === pluginName);
|
|
710
|
+
if (!plugin) {
|
|
711
|
+
throw new common_1.NotFoundException();
|
|
712
|
+
}
|
|
713
|
+
if (!plugin.links.homepage && !plugin.links.bugs) {
|
|
714
|
+
throw new common_1.NotFoundException();
|
|
715
|
+
}
|
|
716
|
+
const repoMatch = plugin.links.homepage?.match(/https:\/\/github.com\/([^/]+)\/([^/#]+)/);
|
|
717
|
+
const bugsMatch = plugin.links.bugs?.match(/https:\/\/github.com\/([^/]+)\/([^/#]+)/);
|
|
718
|
+
let match = repoMatch;
|
|
719
|
+
if (!repoMatch) {
|
|
720
|
+
if (!bugsMatch) {
|
|
721
|
+
throw new common_1.NotFoundException();
|
|
722
|
+
}
|
|
723
|
+
match = bugsMatch;
|
|
724
|
+
}
|
|
622
725
|
try {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
726
|
+
const release = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://api.github.com/repos/${match[1]}/${match[2]}/releases/latest`));
|
|
727
|
+
const latestTag = release.data.tag_name;
|
|
728
|
+
const changelogPath = this.pluginChangelogs[pluginName] || '';
|
|
729
|
+
let changelogData = null;
|
|
730
|
+
try {
|
|
731
|
+
const changelog = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://raw.githubusercontent.com/${match[1]}/${match[2]}/refs/tags/${latestTag}/${changelogPath}CHANGELOG.md`));
|
|
732
|
+
changelogData = changelog.data;
|
|
733
|
+
}
|
|
734
|
+
catch {
|
|
735
|
+
try {
|
|
736
|
+
const changelog = (await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://raw.githubusercontent.com/${match[1]}/${match[2]}/refs/tags/${latestTag}/${changelogPath}changelog.md`))).data;
|
|
737
|
+
changelogData = changelog.data;
|
|
738
|
+
}
|
|
739
|
+
catch { }
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
name: release.data.name || null,
|
|
743
|
+
notes: release.data.body || null,
|
|
744
|
+
changelog: changelogData,
|
|
745
|
+
latestVersion,
|
|
746
|
+
};
|
|
627
747
|
}
|
|
628
748
|
catch (e) {
|
|
629
|
-
|
|
749
|
+
throw new common_1.NotFoundException();
|
|
630
750
|
}
|
|
631
751
|
}
|
|
632
|
-
return {
|
|
633
|
-
name: `v${plugin.latestVersion}`,
|
|
634
|
-
changelog: `Thank you for helping improve ${plugin.displayName || `\`${plugin.name}\``} by testing a beta version.\n\n`
|
|
635
|
-
+ 'You can use the Homebridge UI at any time to revert back to the stable version.\n\n'
|
|
636
|
-
+ `Please remember this **${tag}** version is a pre-release, and report any issues to the GitHub repository page:\n`
|
|
637
|
-
+ `- https://github.com/${repoMatch[1]}/${repoMatch[2]}/issues${branch ? `\n\nSee the commit history for recent changes:\n- https://github.com/${repoMatch[1]}/${repoMatch[2]}/commits/${branch}` : ''}`,
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
try {
|
|
641
|
-
const release = (await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://api.github.com/repos/${match[1]}/${match[2]}/releases/latest`))).data;
|
|
642
|
-
return {
|
|
643
|
-
name: release.name,
|
|
644
|
-
changelog: release.body,
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
catch (e) {
|
|
648
|
-
throw new common_1.NotFoundException();
|
|
649
752
|
}
|
|
650
753
|
}
|
|
651
754
|
async getPluginAlias(pluginName) {
|
|
@@ -696,7 +799,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
696
799
|
});
|
|
697
800
|
}
|
|
698
801
|
catch (e) {
|
|
699
|
-
this.logger.debug(
|
|
802
|
+
this.logger.debug(`Failed to extract ${pluginName} plugin alias as ${e.message}.`);
|
|
700
803
|
if (this.pluginAliasHints[pluginName]) {
|
|
701
804
|
output.pluginAlias = this.pluginAliasHints[pluginName].pluginAlias;
|
|
702
805
|
output.pluginType = this.pluginAliasHints[pluginName].pluginType;
|
|
@@ -788,7 +891,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
788
891
|
}
|
|
789
892
|
}
|
|
790
893
|
catch (e) {
|
|
791
|
-
this.logger.log(`Failed to parse
|
|
894
|
+
this.logger.log(`Failed to parse ${module} in ${requiredPath} as ${e.message}.`);
|
|
792
895
|
}
|
|
793
896
|
}
|
|
794
897
|
}
|
|
@@ -824,8 +927,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
824
927
|
return [windowsNpmPath[0]];
|
|
825
928
|
}
|
|
826
929
|
else {
|
|
827
|
-
this.logger.error('
|
|
828
|
-
this.logger.error('
|
|
930
|
+
this.logger.error('Cannot find npm binary, you will not be able to manage plugins or update Homebridge. You might be able to fix this problem by running:');
|
|
931
|
+
this.logger.error('npm install -g npm');
|
|
829
932
|
}
|
|
830
933
|
}
|
|
831
934
|
return ['npm'];
|
|
@@ -876,8 +979,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
876
979
|
async parsePackageJson(pkgJson, installPath) {
|
|
877
980
|
const plugin = {
|
|
878
981
|
name: pkgJson.name,
|
|
982
|
+
displayName: pkgJson.displayName || this.pluginNames[pkgJson.name],
|
|
879
983
|
private: pkgJson.private || false,
|
|
880
|
-
displayName: pkgJson.displayName,
|
|
881
984
|
description: (pkgJson.description)
|
|
882
985
|
? pkgJson.description.replace(/(?:https?|ftp):\/\/[\n\S]+/g, '').trim()
|
|
883
986
|
: pkgJson.name,
|
|
@@ -886,7 +989,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
886
989
|
icon: this.pluginIcons[pkgJson.name]
|
|
887
990
|
? `${this.pluginListUrl}${this.pluginIcons[pkgJson.name]}`
|
|
888
991
|
: null,
|
|
889
|
-
isHbScoped:
|
|
992
|
+
isHbScoped: pkgJson.name.startsWith('@homebridge-plugins/'),
|
|
890
993
|
newHbScope: this.newScopePlugins[pkgJson.name],
|
|
891
994
|
isHbMaintained: this.maintainedPlugins.includes(pkgJson.name),
|
|
892
995
|
installedVersion: installPath ? (pkgJson.version || '0.0.1') : null,
|
|
@@ -938,12 +1041,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
938
1041
|
homepage: pkg.homepage,
|
|
939
1042
|
bugs: typeof pkg.bugs === 'object' && pkg.bugs?.url ? pkg.bugs.url : null,
|
|
940
1043
|
};
|
|
941
|
-
plugin.author = this.
|
|
1044
|
+
plugin.author = this.pluginAuthors[pkg.name]
|
|
942
1045
|
|| ((pkg.maintainers && pkg.maintainers.length) ? pkg.maintainers[0].name : null);
|
|
943
1046
|
}
|
|
944
1047
|
catch (e) {
|
|
945
1048
|
if (e.response?.status !== 404) {
|
|
946
|
-
this.logger.log(`[${plugin.name}]
|
|
1049
|
+
this.logger.log(`[${plugin.name}] failed to check registry.npmjs.org for updates (see https://homebridge.io/w/JJSz6 for help) as ${e.message}.`);
|
|
947
1050
|
}
|
|
948
1051
|
plugin.publicPackage = false;
|
|
949
1052
|
plugin.latestVersion = null;
|
|
@@ -970,18 +1073,25 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
970
1073
|
command.unshift('sudo', '-E', '-n');
|
|
971
1074
|
}
|
|
972
1075
|
else {
|
|
1076
|
+
let npmInstallPath;
|
|
973
1077
|
try {
|
|
974
|
-
|
|
1078
|
+
npmInstallPath = (0, node_child_process_1.execSync)('npm root -g').toString().trim();
|
|
1079
|
+
}
|
|
1080
|
+
catch (e) {
|
|
1081
|
+
npmInstallPath = (0, node_path_1.resolve)(cwd, 'node_modules');
|
|
1082
|
+
}
|
|
1083
|
+
try {
|
|
1084
|
+
await (0, fs_extra_1.access)(npmInstallPath, fs_extra_1.constants.W_OK);
|
|
975
1085
|
}
|
|
976
1086
|
catch (e) {
|
|
977
1087
|
client.emit('stdout', (0, bash_color_1.yellow)(`The user "${(0, node_os_1.userInfo)().username}" does not have write access to the target directory:\n\r\n\r`));
|
|
978
|
-
client.emit('stdout', `${
|
|
1088
|
+
client.emit('stdout', `${npmInstallPath}\n\r\n\r`);
|
|
979
1089
|
client.emit('stdout', (0, bash_color_1.yellow)('This may cause the operation to fail.\n\r'));
|
|
980
1090
|
client.emit('stdout', (0, bash_color_1.yellow)('See the docs for details on how to enable sudo mode:\n\r'));
|
|
981
1091
|
client.emit('stdout', (0, bash_color_1.yellow)('https://github.com/homebridge/homebridge-config-ui-x/wiki/Manual-Configuration#sudo-mode\n\r\n\r'));
|
|
982
1092
|
}
|
|
983
1093
|
}
|
|
984
|
-
this.logger.log(`Running
|
|
1094
|
+
this.logger.log(`Running command ${command.join(' ')}.`);
|
|
985
1095
|
if (!(0, semver_1.satisfies)(node_process_1.default.version, `>=${this.configService.minimumNodeVersion}`)) {
|
|
986
1096
|
client.emit('stdout', (0, bash_color_1.yellow)(`Node.js v${this.configService.minimumNodeVersion} higher is required for ${this.configService.name}.\n\r`));
|
|
987
1097
|
client.emit('stdout', (0, bash_color_1.yellow)(`You may experience issues while running on Node.js ${node_process_1.default.version}.\n\r\n\r`));
|
|
@@ -1042,13 +1152,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1042
1152
|
return;
|
|
1043
1153
|
}
|
|
1044
1154
|
if (!await (0, fs_extra_1.pathExists)(this.configService.customPluginPath)) {
|
|
1045
|
-
this.logger.warn(`Custom plugin directory was removed
|
|
1155
|
+
this.logger.warn(`Custom plugin directory was removed, re-creating ${this.configService.customPluginPath}.`);
|
|
1046
1156
|
try {
|
|
1047
1157
|
await (0, fs_extra_1.ensureDir)(this.configService.customPluginPath);
|
|
1048
1158
|
}
|
|
1049
1159
|
catch (e) {
|
|
1050
|
-
this.logger.error(
|
|
1051
|
-
this.logger.error(e.message);
|
|
1160
|
+
this.logger.error(`Failed to re-create custom plugin directory as ${e.message}.`);
|
|
1052
1161
|
}
|
|
1053
1162
|
}
|
|
1054
1163
|
}
|
|
@@ -1063,7 +1172,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1063
1172
|
}
|
|
1064
1173
|
}
|
|
1065
1174
|
catch (e) {
|
|
1066
|
-
this.logger.error(`Failed to remove ${offendingPath}
|
|
1175
|
+
this.logger.error(`Failed to remove ${offendingPath} as ${e.message}.`);
|
|
1067
1176
|
}
|
|
1068
1177
|
}
|
|
1069
1178
|
async cleanNpmCache() {
|
|
@@ -1072,9 +1181,9 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1072
1181
|
command.unshift('sudo', '-E', '-n');
|
|
1073
1182
|
}
|
|
1074
1183
|
return new Promise((res) => {
|
|
1075
|
-
const child = (0, node_child_process_1.spawn)(command.shift(), command);
|
|
1184
|
+
const child = (0, node_child_process_1.spawn)(command.shift(), command, { shell: true });
|
|
1076
1185
|
child.on('exit', (code) => {
|
|
1077
|
-
this.logger.log(
|
|
1186
|
+
this.logger.log(`Executed npm cache clear command with exit code ${code}.`);
|
|
1078
1187
|
res(null);
|
|
1079
1188
|
});
|
|
1080
1189
|
child.on('error', () => {
|
|
@@ -1093,36 +1202,44 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1093
1202
|
this.pluginIcons = {};
|
|
1094
1203
|
this.hiddenPlugins = [];
|
|
1095
1204
|
this.maintainedPlugins = [];
|
|
1096
|
-
this.
|
|
1205
|
+
this.pluginAuthors = {};
|
|
1206
|
+
this.pluginNames = {};
|
|
1207
|
+
this.pluginChangelogs = {};
|
|
1097
1208
|
this.newScopePlugins = {};
|
|
1098
1209
|
Object.keys(pluginListData).forEach((key) => {
|
|
1099
1210
|
const plugin = pluginListData[key];
|
|
1100
|
-
if (plugin.
|
|
1101
|
-
this.pluginIcons[key] = `icons/${plugin.
|
|
1211
|
+
if (plugin.i) {
|
|
1212
|
+
this.pluginIcons[key] = `icons/${plugin.i}.png`;
|
|
1102
1213
|
}
|
|
1103
|
-
if (plugin.
|
|
1214
|
+
if (plugin.h) {
|
|
1104
1215
|
this.hiddenPlugins.push(key);
|
|
1105
1216
|
}
|
|
1106
|
-
if (plugin.
|
|
1217
|
+
if (plugin.m) {
|
|
1107
1218
|
this.maintainedPlugins.push(key);
|
|
1108
1219
|
}
|
|
1109
|
-
if (plugin.
|
|
1110
|
-
this.
|
|
1220
|
+
if (plugin.a) {
|
|
1221
|
+
this.pluginAuthors[key] = plugin.a;
|
|
1222
|
+
}
|
|
1223
|
+
if (plugin.n) {
|
|
1224
|
+
this.pluginNames[key] = plugin.n;
|
|
1111
1225
|
}
|
|
1112
|
-
if (plugin.
|
|
1113
|
-
this.newScopePlugins[key] = plugin.
|
|
1226
|
+
if (plugin.s) {
|
|
1227
|
+
this.newScopePlugins[key] = plugin.s;
|
|
1114
1228
|
}
|
|
1115
|
-
if (plugin.
|
|
1229
|
+
if (plugin.v) {
|
|
1116
1230
|
this.verifiedPlugins.push(key);
|
|
1117
1231
|
}
|
|
1118
|
-
if (plugin.
|
|
1232
|
+
if (plugin.p) {
|
|
1119
1233
|
this.verifiedPlusPlugins.push(key);
|
|
1120
1234
|
}
|
|
1235
|
+
if (plugin.c) {
|
|
1236
|
+
this.pluginChangelogs[key] = plugin.c;
|
|
1237
|
+
}
|
|
1121
1238
|
});
|
|
1122
1239
|
}
|
|
1123
1240
|
catch (e) {
|
|
1124
1241
|
this.pluginListRetryTimeout = setTimeout(() => this.loadPluginList(), 60000);
|
|
1125
|
-
this.logger.debug(
|
|
1242
|
+
this.logger.debug(`Could not obtain plugin list from plugins repo as ${e.message}.`);
|
|
1126
1243
|
}
|
|
1127
1244
|
}
|
|
1128
1245
|
};
|