homebridge-config-ui-x 5.0.0-beta.11 → 5.0.0-beta.111
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 +526 -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 +35 -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 +50 -11
- package/dist/core/config/config.service.js +59 -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 +3 -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.d.ts +1 -0
- package/dist/modules/backup/backup.controller.js +18 -7
- package/dist/modules/backup/backup.controller.js.map +1 -1
- package/dist/modules/backup/backup.service.d.ts +1 -0
- package/dist/modules/backup/backup.service.js +37 -83
- 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 +287 -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 +288 -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 +34 -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 +246 -136
- package/public/assets/hap-icons/air-purifier.svg +50 -0
- package/public/assets/hap-icons/air-quality-sensor.svg +25 -0
- package/public/assets/hap-icons/carbon-dioxide-sensor.svg +72 -0
- package/public/assets/hap-icons/carbon-monoxide-sensor.svg +72 -0
- package/public/assets/hap-icons/contact-sensor-closed.svg +36 -0
- package/public/assets/hap-icons/contact-sensor-open.svg +81 -0
- 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/filter-maintenance.svg +26 -0
- package/public/assets/hap-icons/garage-door.svg +25 -0
- package/public/assets/hap-icons/humidity-sensor.svg +25 -0
- package/public/assets/hap-icons/irrigation-system.svg +47 -18
- package/public/assets/hap-icons/leak-sensor.svg +53 -0
- package/public/assets/hap-icons/light-bulb.svg +25 -0
- package/public/assets/hap-icons/light-sensor.svg +50 -0
- 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/motion-sensor.svg +101 -0
- package/public/assets/hap-icons/occupancy-sensor.svg +98 -0
- package/public/assets/hap-icons/outlet.svg +24 -13
- package/public/assets/hap-icons/security-system-active.svg +103 -0
- package/public/assets/hap-icons/security-system-off.svg +69 -0
- package/public/assets/hap-icons/security-system-triggered.svg +103 -0
- package/public/assets/hap-icons/smoke-sensor.svg +59 -0
- package/public/assets/hap-icons/speaker.svg +29 -13
- package/public/assets/hap-icons/stateless-programmable-switch.svg +52 -0
- 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-faucet.svg +21 -0
- 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-shower-head.svg +52 -0
- package/public/assets/hap-icons/window-closed.svg +85 -2
- package/public/assets/hap-icons/window-covering-closed.svg +49 -0
- package/public/assets/hap-icons/window-covering-open.svg +44 -0
- package/public/assets/hap-icons/window-open.svg +136 -2
- 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-2B6Z42D5.js +2 -0
- package/public/chunk-3KEVK4CU.js +1 -0
- package/public/{chunk-T5R3UG6Z.js → chunk-3TXUNOXT.js} +1 -1
- package/public/chunk-4G7W6MRL.js +1 -0
- package/public/chunk-4N7ZDOUO.js +1 -0
- package/public/chunk-5CIDMZGS.js +1 -0
- package/public/chunk-66VSEU25.js +5 -0
- package/public/chunk-7DSQ7XWE.js +1 -0
- package/public/{chunk-QE7DO6J3.js → chunk-7EQ4BJZB.js} +2 -2
- package/public/chunk-7GGXZ7OU.js +1 -0
- package/public/chunk-7QRMEOTO.js +8 -0
- package/public/chunk-7RKBTRXY.js +1 -0
- package/public/chunk-ABBL3RO7.js +1 -0
- package/public/chunk-BCMP25TC.js +1 -0
- package/public/chunk-BP5LQRNS.js +4 -0
- package/public/chunk-CF2ZN7LY.js +5 -0
- package/public/chunk-DSKSIOUV.js +1 -0
- package/public/chunk-EGCUYKRH.js +1 -0
- package/public/chunk-EL5XLLBH.js +1 -0
- package/public/chunk-EPA4YSLT.js +1 -0
- package/public/chunk-F3ERQJVG.js +1 -0
- package/public/{chunk-LM6S4A72.js → chunk-FFFA4Z4P.js} +1 -1
- package/public/chunk-FNE4UWIK.js +1 -0
- package/public/chunk-FXYQSGFB.js +1 -0
- package/public/{chunk-7EUQWCP5.js → chunk-GDFV4BMO.js} +2 -2
- package/public/chunk-GW2DVJRQ.js +1 -0
- package/public/chunk-HTQJEJW6.js +23 -0
- package/public/chunk-IN7NZAFJ.js +1 -0
- package/public/chunk-J6YDFLQQ.js +1 -0
- package/public/chunk-K6X6GPJI.js +1 -0
- package/public/chunk-KKNYWOLQ.js +20 -0
- package/public/chunk-L3KYADWL.js +1 -0
- package/public/chunk-LUNR4YXI.js +1 -0
- package/public/chunk-LXXELGX4.js +1 -0
- package/public/chunk-LZTR2JMZ.js +1 -0
- package/public/{chunk-JZZQRLNW.js → chunk-NGNQPAAK.js} +1 -1
- package/public/chunk-NH3ELE5E.js +4 -0
- package/public/chunk-OKTBHUEL.js +1 -0
- package/public/{chunk-5M6KOARX.js → chunk-ORZLF5HZ.js} +1 -1
- package/public/chunk-Q3UFAHWJ.js +1 -0
- package/public/{chunk-WNWWUCCZ.js → chunk-QDCKP2LU.js} +3 -3
- package/public/chunk-QEBH3NJ4.js +1 -0
- package/public/chunk-QTCNRXCY.js +29 -0
- package/public/chunk-R33U7ULB.js +1 -0
- package/public/chunk-RMY3PS3V.js +1 -0
- package/public/chunk-S5TTYLQD.js +1 -0
- package/public/chunk-SKZUTSHE.js +7 -0
- package/public/chunk-SRP6QX5M.js +2 -0
- package/public/chunk-SSMWAG33.js +1 -0
- package/public/chunk-T252YJXA.js +1 -0
- package/public/chunk-TFSYGCLN.js +1 -0
- package/public/chunk-TKTM3Z2T.js +3 -0
- package/public/chunk-TQNKRJOX.js +1 -0
- package/public/chunk-U3DAE6KH.js +1 -0
- package/public/chunk-UD6SVFW2.js +7 -0
- package/public/chunk-UO2HQVMO.js +1 -0
- package/public/{chunk-3RNRIJ74.js → chunk-UQYCFN3T.js} +11 -11
- package/public/chunk-V6SZXPWT.js +1 -0
- package/public/chunk-VGEV4FQW.js +1 -0
- package/public/{chunk-EA5J2VEJ.js → chunk-X2SHRXXY.js} +1 -1
- package/public/chunk-X7PVYZAE.js +1 -0
- package/public/{chunk-3TUBDO76.js → chunk-XBWIDIPO.js} +1 -1
- package/public/chunk-ZDJGHFIU.js +12 -0
- package/public/chunk-ZNACRWKA.js +1 -0
- package/public/chunk-ZVUGVJDR.js +1 -0
- package/public/index.html +2 -2
- package/public/main-YVALKUM3.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-YDIK2EDN.css +1 -0
- package/scripts/upgrade-install-plugin.sh +1 -1
- package/public/assets/hap-icons/airpurifier.svg +0 -17
- package/public/assets/hap-icons/airquality.svg +0 -14
- package/public/assets/hap-icons/contactsensor-closed.svg +0 -3
- package/public/assets/hap-icons/contactsensor-open.svg +0 -3
- package/public/assets/hap-icons/garagedoor.svg +0 -14
- package/public/assets/hap-icons/humidity.svg +0 -14
- package/public/assets/hap-icons/leaksensor.svg +0 -3
- package/public/assets/hap-icons/light.svg +0 -31
- package/public/assets/hap-icons/lightbulb.svg +0 -14
- package/public/assets/hap-icons/motionsensor.svg +0 -3
- package/public/assets/hap-icons/occupancysensor.svg +0 -3
- package/public/assets/hap-icons/securitysystem-active.svg +0 -3
- package/public/assets/hap-icons/securitysystem-off.svg +0 -3
- package/public/assets/hap-icons/smokesensor.svg +0 -13
- package/public/assets/hap-icons/statelessprogrammableswitch.svg +0 -3
- package/public/assets/hap-icons/windowcovering-closed.svg +0 -53
- package/public/assets/hap-icons/windowcovering-open.svg +0 -48
- 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-6IAVZXBU.js +0 -20
- package/public/chunk-6T27YQ2D.js +0 -1
- package/public/chunk-6TCHCTXZ.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-CCUID66K.js +0 -1
- 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-MGBLPFXT.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-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-UPYKPJUD.js +0 -4
- 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,83 @@ 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 tags = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://api.github.com/repos/homebridge/${pluginName}/tags`));
|
|
691
|
+
const changelog = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://raw.githubusercontent.com/homebridge/${pluginName}/refs/tags/${tags.data[0].name}/CHANGELOG.md`));
|
|
692
|
+
return {
|
|
693
|
+
name: release.data.name,
|
|
694
|
+
notes: release.data.body,
|
|
695
|
+
changelog: changelog.data,
|
|
696
|
+
latestVersion,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
return {
|
|
701
|
+
name: null,
|
|
702
|
+
notes: null,
|
|
703
|
+
changelog: null,
|
|
704
|
+
latestVersion,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
614
707
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
708
|
+
default: {
|
|
709
|
+
await this.getInstalledPlugins();
|
|
710
|
+
const plugin = this.installedPlugins.find(x => x.name === pluginName);
|
|
711
|
+
if (!plugin) {
|
|
712
|
+
throw new common_1.NotFoundException();
|
|
713
|
+
}
|
|
714
|
+
if (!plugin.links.homepage && !plugin.links.bugs) {
|
|
715
|
+
throw new common_1.NotFoundException();
|
|
716
|
+
}
|
|
717
|
+
const repoMatch = plugin.links.homepage?.match(/https:\/\/github.com\/([^/]+)\/([^/#]+)/);
|
|
718
|
+
const bugsMatch = plugin.links.bugs?.match(/https:\/\/github.com\/([^/]+)\/([^/#]+)/);
|
|
719
|
+
let match = repoMatch;
|
|
720
|
+
if (!repoMatch) {
|
|
721
|
+
if (!bugsMatch) {
|
|
722
|
+
throw new common_1.NotFoundException();
|
|
723
|
+
}
|
|
724
|
+
match = bugsMatch;
|
|
725
|
+
}
|
|
622
726
|
try {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
727
|
+
const release = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://api.github.com/repos/${match[1]}/${match[2]}/releases/latest`));
|
|
728
|
+
const latestTag = release.data.tag_name;
|
|
729
|
+
const isReleaseMatch = latestVersion?.replace(/[^0-9.]/g, '').includes(release.data.tag_name?.replace(/[^0-9.]/g, ''));
|
|
730
|
+
const changelogPath = this.pluginChangelogs[pluginName] || '';
|
|
731
|
+
let changelogData = null;
|
|
732
|
+
try {
|
|
733
|
+
const changelog = await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://raw.githubusercontent.com/${match[1]}/${match[2]}/refs/tags/${latestTag}/${changelogPath}CHANGELOG.md`));
|
|
734
|
+
changelogData = changelog.data;
|
|
735
|
+
}
|
|
736
|
+
catch {
|
|
737
|
+
try {
|
|
738
|
+
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;
|
|
739
|
+
changelogData = changelog.data;
|
|
740
|
+
}
|
|
741
|
+
catch { }
|
|
742
|
+
}
|
|
743
|
+
return {
|
|
744
|
+
name: isReleaseMatch && release.data.tag_name ? release.data.tag_name : null,
|
|
745
|
+
notes: isReleaseMatch && release.data.body ? release.data.body : null,
|
|
746
|
+
changelog: changelogData,
|
|
747
|
+
latestVersion,
|
|
748
|
+
};
|
|
627
749
|
}
|
|
628
750
|
catch (e) {
|
|
629
|
-
|
|
751
|
+
throw new common_1.NotFoundException();
|
|
630
752
|
}
|
|
631
753
|
}
|
|
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
754
|
}
|
|
650
755
|
}
|
|
651
756
|
async getPluginAlias(pluginName) {
|
|
@@ -696,7 +801,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
696
801
|
});
|
|
697
802
|
}
|
|
698
803
|
catch (e) {
|
|
699
|
-
this.logger.debug(
|
|
804
|
+
this.logger.debug(`Failed to extract ${pluginName} plugin alias as ${e.message}.`);
|
|
700
805
|
if (this.pluginAliasHints[pluginName]) {
|
|
701
806
|
output.pluginAlias = this.pluginAliasHints[pluginName].pluginAlias;
|
|
702
807
|
output.pluginType = this.pluginAliasHints[pluginName].pluginType;
|
|
@@ -788,7 +893,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
788
893
|
}
|
|
789
894
|
}
|
|
790
895
|
catch (e) {
|
|
791
|
-
this.logger.log(`Failed to parse
|
|
896
|
+
this.logger.log(`Failed to parse ${module} in ${requiredPath} as ${e.message}.`);
|
|
792
897
|
}
|
|
793
898
|
}
|
|
794
899
|
}
|
|
@@ -824,8 +929,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
824
929
|
return [windowsNpmPath[0]];
|
|
825
930
|
}
|
|
826
931
|
else {
|
|
827
|
-
this.logger.error('
|
|
828
|
-
this.logger.error('
|
|
932
|
+
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:');
|
|
933
|
+
this.logger.error('npm install -g npm');
|
|
829
934
|
}
|
|
830
935
|
}
|
|
831
936
|
return ['npm'];
|
|
@@ -876,8 +981,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
876
981
|
async parsePackageJson(pkgJson, installPath) {
|
|
877
982
|
const plugin = {
|
|
878
983
|
name: pkgJson.name,
|
|
984
|
+
displayName: pkgJson.displayName || this.pluginNames[pkgJson.name],
|
|
879
985
|
private: pkgJson.private || false,
|
|
880
|
-
displayName: pkgJson.displayName,
|
|
881
986
|
description: (pkgJson.description)
|
|
882
987
|
? pkgJson.description.replace(/(?:https?|ftp):\/\/[\n\S]+/g, '').trim()
|
|
883
988
|
: pkgJson.name,
|
|
@@ -886,7 +991,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
886
991
|
icon: this.pluginIcons[pkgJson.name]
|
|
887
992
|
? `${this.pluginListUrl}${this.pluginIcons[pkgJson.name]}`
|
|
888
993
|
: null,
|
|
889
|
-
isHbScoped:
|
|
994
|
+
isHbScoped: pkgJson.name.startsWith('@homebridge-plugins/'),
|
|
890
995
|
newHbScope: this.newScopePlugins[pkgJson.name],
|
|
891
996
|
isHbMaintained: this.maintainedPlugins.includes(pkgJson.name),
|
|
892
997
|
installedVersion: installPath ? (pkgJson.version || '0.0.1') : null,
|
|
@@ -938,12 +1043,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
938
1043
|
homepage: pkg.homepage,
|
|
939
1044
|
bugs: typeof pkg.bugs === 'object' && pkg.bugs?.url ? pkg.bugs.url : null,
|
|
940
1045
|
};
|
|
941
|
-
plugin.author = this.
|
|
1046
|
+
plugin.author = this.pluginAuthors[pkg.name]
|
|
942
1047
|
|| ((pkg.maintainers && pkg.maintainers.length) ? pkg.maintainers[0].name : null);
|
|
943
1048
|
}
|
|
944
1049
|
catch (e) {
|
|
945
1050
|
if (e.response?.status !== 404) {
|
|
946
|
-
this.logger.log(`[${plugin.name}]
|
|
1051
|
+
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
1052
|
}
|
|
948
1053
|
plugin.publicPackage = false;
|
|
949
1054
|
plugin.latestVersion = null;
|
|
@@ -970,18 +1075,25 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
970
1075
|
command.unshift('sudo', '-E', '-n');
|
|
971
1076
|
}
|
|
972
1077
|
else {
|
|
1078
|
+
let npmInstallPath;
|
|
973
1079
|
try {
|
|
974
|
-
|
|
1080
|
+
npmInstallPath = (0, node_child_process_1.execSync)('npm root -g').toString().trim();
|
|
1081
|
+
}
|
|
1082
|
+
catch (e) {
|
|
1083
|
+
npmInstallPath = (0, node_path_1.resolve)(cwd, 'node_modules');
|
|
1084
|
+
}
|
|
1085
|
+
try {
|
|
1086
|
+
await (0, fs_extra_1.access)(npmInstallPath, fs_extra_1.constants.W_OK);
|
|
975
1087
|
}
|
|
976
1088
|
catch (e) {
|
|
977
1089
|
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', `${
|
|
1090
|
+
client.emit('stdout', `${npmInstallPath}\n\r\n\r`);
|
|
979
1091
|
client.emit('stdout', (0, bash_color_1.yellow)('This may cause the operation to fail.\n\r'));
|
|
980
1092
|
client.emit('stdout', (0, bash_color_1.yellow)('See the docs for details on how to enable sudo mode:\n\r'));
|
|
981
1093
|
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
1094
|
}
|
|
983
1095
|
}
|
|
984
|
-
this.logger.log(`Running
|
|
1096
|
+
this.logger.log(`Running command ${command.join(' ')}.`);
|
|
985
1097
|
if (!(0, semver_1.satisfies)(node_process_1.default.version, `>=${this.configService.minimumNodeVersion}`)) {
|
|
986
1098
|
client.emit('stdout', (0, bash_color_1.yellow)(`Node.js v${this.configService.minimumNodeVersion} higher is required for ${this.configService.name}.\n\r`));
|
|
987
1099
|
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 +1154,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1042
1154
|
return;
|
|
1043
1155
|
}
|
|
1044
1156
|
if (!await (0, fs_extra_1.pathExists)(this.configService.customPluginPath)) {
|
|
1045
|
-
this.logger.warn(`Custom plugin directory was removed
|
|
1157
|
+
this.logger.warn(`Custom plugin directory was removed, re-creating ${this.configService.customPluginPath}.`);
|
|
1046
1158
|
try {
|
|
1047
1159
|
await (0, fs_extra_1.ensureDir)(this.configService.customPluginPath);
|
|
1048
1160
|
}
|
|
1049
1161
|
catch (e) {
|
|
1050
|
-
this.logger.error(
|
|
1051
|
-
this.logger.error(e.message);
|
|
1162
|
+
this.logger.error(`Failed to re-create custom plugin directory as ${e.message}.`);
|
|
1052
1163
|
}
|
|
1053
1164
|
}
|
|
1054
1165
|
}
|
|
@@ -1063,7 +1174,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1063
1174
|
}
|
|
1064
1175
|
}
|
|
1065
1176
|
catch (e) {
|
|
1066
|
-
this.logger.error(`Failed to remove ${offendingPath}
|
|
1177
|
+
this.logger.error(`Failed to remove ${offendingPath} as ${e.message}.`);
|
|
1067
1178
|
}
|
|
1068
1179
|
}
|
|
1069
1180
|
async cleanNpmCache() {
|
|
@@ -1072,9 +1183,9 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1072
1183
|
command.unshift('sudo', '-E', '-n');
|
|
1073
1184
|
}
|
|
1074
1185
|
return new Promise((res) => {
|
|
1075
|
-
const child = (0, node_child_process_1.spawn)(command.shift(), command);
|
|
1186
|
+
const child = (0, node_child_process_1.spawn)(command.shift(), command, { shell: true });
|
|
1076
1187
|
child.on('exit', (code) => {
|
|
1077
|
-
this.logger.log(
|
|
1188
|
+
this.logger.log(`Executed npm cache clear command with exit code ${code}.`);
|
|
1078
1189
|
res(null);
|
|
1079
1190
|
});
|
|
1080
1191
|
child.on('error', () => {
|
|
@@ -1093,36 +1204,44 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1093
1204
|
this.pluginIcons = {};
|
|
1094
1205
|
this.hiddenPlugins = [];
|
|
1095
1206
|
this.maintainedPlugins = [];
|
|
1096
|
-
this.
|
|
1207
|
+
this.pluginAuthors = {};
|
|
1208
|
+
this.pluginNames = {};
|
|
1209
|
+
this.pluginChangelogs = {};
|
|
1097
1210
|
this.newScopePlugins = {};
|
|
1098
1211
|
Object.keys(pluginListData).forEach((key) => {
|
|
1099
1212
|
const plugin = pluginListData[key];
|
|
1100
|
-
if (plugin.
|
|
1101
|
-
this.pluginIcons[key] = `icons/${plugin.
|
|
1213
|
+
if (plugin.i) {
|
|
1214
|
+
this.pluginIcons[key] = `icons/${plugin.i}.png`;
|
|
1102
1215
|
}
|
|
1103
|
-
if (plugin.
|
|
1216
|
+
if (plugin.h) {
|
|
1104
1217
|
this.hiddenPlugins.push(key);
|
|
1105
1218
|
}
|
|
1106
|
-
if (plugin.
|
|
1219
|
+
if (plugin.m) {
|
|
1107
1220
|
this.maintainedPlugins.push(key);
|
|
1108
1221
|
}
|
|
1109
|
-
if (plugin.
|
|
1110
|
-
this.
|
|
1222
|
+
if (plugin.a) {
|
|
1223
|
+
this.pluginAuthors[key] = plugin.a;
|
|
1224
|
+
}
|
|
1225
|
+
if (plugin.n) {
|
|
1226
|
+
this.pluginNames[key] = plugin.n;
|
|
1111
1227
|
}
|
|
1112
|
-
if (plugin.
|
|
1113
|
-
this.newScopePlugins[key] = plugin.
|
|
1228
|
+
if (plugin.s) {
|
|
1229
|
+
this.newScopePlugins[key] = plugin.s;
|
|
1114
1230
|
}
|
|
1115
|
-
if (plugin.
|
|
1231
|
+
if (plugin.v) {
|
|
1116
1232
|
this.verifiedPlugins.push(key);
|
|
1117
1233
|
}
|
|
1118
|
-
if (plugin.
|
|
1234
|
+
if (plugin.p) {
|
|
1119
1235
|
this.verifiedPlusPlugins.push(key);
|
|
1120
1236
|
}
|
|
1237
|
+
if (plugin.c) {
|
|
1238
|
+
this.pluginChangelogs[key] = plugin.c;
|
|
1239
|
+
}
|
|
1121
1240
|
});
|
|
1122
1241
|
}
|
|
1123
1242
|
catch (e) {
|
|
1124
1243
|
this.pluginListRetryTimeout = setTimeout(() => this.loadPluginList(), 60000);
|
|
1125
|
-
this.logger.debug(
|
|
1244
|
+
this.logger.debug(`Could not obtain plugin list from plugins repo as ${e.message}.`);
|
|
1126
1245
|
}
|
|
1127
1246
|
}
|
|
1128
1247
|
};
|