homebridge-config-ui-x 5.0.0-beta.1 → 5.0.0-beta.100
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 +439 -18
- package/CONTRIBUTING.md +5 -4
- package/LICENSE +1 -1
- package/README.md +6 -8
- package/config.schema.json +55 -252
- package/dist/bin/fork.js +17 -7
- package/dist/bin/fork.js.map +1 -1
- package/dist/bin/hb-service.d.ts +2 -2
- package/dist/bin/hb-service.js +69 -58
- package/dist/bin/hb-service.js.map +1 -1
- package/dist/bin/platforms/darwin.js +2 -2
- 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 +18 -7
- 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 -67
- 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 +132 -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.js +4 -4
- package/dist/modules/plugins/plugins.controller.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +7 -1
- package/dist/modules/plugins/plugins.service.js +215 -127
- 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 +265 -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 -44
- package/public/3rdpartylicenses.txt +573 -463
- 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/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/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 +41 -19
- package/public/assets/plugin-ui-utils/ui.js.map +1 -1
- package/public/chunk-2E6EOZUJ.js +1 -0
- package/public/chunk-2EEZAGZA.js +1 -0
- package/public/chunk-2G7GNBIF.js +1 -0
- package/public/chunk-3GEFBFMQ.js +1 -0
- package/public/chunk-3VYIHHCL.js +1 -0
- package/public/chunk-45NQ4GBG.js +1 -0
- package/public/chunk-4G7RRMI2.js +1 -0
- package/public/chunk-4L2H4MMG.js +29 -0
- package/public/chunk-4LX4LX6P.js +1 -0
- package/public/chunk-4SLRCK6B.js +2 -0
- package/public/chunk-53ILAFUX.js +1 -0
- package/public/chunk-5FYL3TBQ.js +1 -0
- package/public/chunk-5PN6UJOB.js +1 -0
- package/public/chunk-6IOCCVAH.js +1 -0
- package/public/chunk-7REMZHOO.js +1 -0
- package/public/chunk-AH6LOD34.js +1 -0
- package/public/chunk-ALG7CIS3.js +5 -0
- package/public/chunk-AQNNOWXC.js +1 -0
- package/public/chunk-B4X7IOGK.js +1 -0
- package/public/chunk-BALPGJEG.js +1 -0
- package/public/chunk-BIRWL4X3.js +1 -0
- package/public/chunk-BMCQHGMB.js +1 -0
- package/public/chunk-BNYXD34L.js +1 -0
- package/public/chunk-BRW7C4BZ.js +1 -0
- package/public/chunk-C6HQUAQJ.js +1 -0
- package/public/chunk-C6LXQBT3.js +1 -0
- package/public/chunk-CG7LPZUJ.js +1 -0
- package/public/chunk-CMZBGXBL.js +1 -0
- package/public/chunk-CRIZPUI6.js +8 -0
- package/public/chunk-DDFZVHUO.js +1 -0
- package/public/chunk-DQUBVCJE.js +1 -0
- package/public/chunk-EULZTUQA.js +1 -0
- package/public/chunk-F6PRUGSN.js +1 -0
- package/public/chunk-FD3D3FMF.js +1 -0
- package/public/chunk-FJJNRLA7.js +1 -0
- package/public/chunk-FSQ4KA4X.js +1 -0
- package/public/chunk-FXS7MG4Z.js +1 -0
- package/public/chunk-GMY5QOXR.js +7 -0
- package/public/chunk-HE2YZMXJ.js +1 -0
- package/public/chunk-HOGBMPOF.js +1 -0
- package/public/chunk-HOI6GB6V.js +1 -0
- package/public/chunk-IOWBVW62.js +2 -0
- package/public/chunk-LCYRJQ4W.js +1 -0
- package/public/chunk-M2U64RPA.js +1 -0
- package/public/chunk-MAXJEMB2.js +1 -0
- package/public/chunk-MGEQZI52.js +1 -0
- package/public/chunk-MTSNBCD6.js +1 -0
- package/public/chunk-O752QW52.js +8 -0
- package/public/chunk-OHSRCO5R.js +1 -0
- package/public/chunk-OTJCSRHZ.js +1 -0
- package/public/chunk-PAQIQJ5I.js +1 -0
- package/public/chunk-PODF4FUD.js +1 -0
- package/public/chunk-PYWNQR2G.js +1 -0
- package/public/chunk-Q5OGHYTC.js +1 -0
- package/public/chunk-QIH36FRX.js +1 -0
- package/public/chunk-QMB5XE4Y.js +1 -0
- package/public/chunk-R3VPSO5X.js +7 -0
- package/public/chunk-RGUTUTJY.js +20 -0
- package/public/chunk-RJAFS3B2.js +1 -0
- package/public/chunk-RNHMRKPJ.js +8 -0
- package/public/chunk-SHP2ASKV.js +5 -0
- package/public/chunk-SMR5XBWZ.js +1 -0
- package/public/chunk-T5VLECCH.js +23 -0
- package/public/chunk-TFSUXAYX.js +1 -0
- package/public/chunk-UCO62FWN.js +1 -0
- package/public/chunk-UTAVUGTT.js +14 -0
- package/public/chunk-UVOJ3BF7.js +1 -0
- package/public/chunk-UVQC4EUO.js +6 -0
- package/public/chunk-VECPBVSQ.js +1 -0
- package/public/{chunk-GNQUXZMN.js → chunk-VHTQTL2S.js} +1 -1
- package/public/chunk-VJEUBTTK.js +1 -0
- package/public/chunk-VLX322GM.js +1 -0
- package/public/chunk-VUYAK52W.js +2 -0
- package/public/{chunk-6QEO3BTW.js → chunk-WAA5KWIN.js} +14 -14
- package/public/chunk-WGYMALW2.js +1 -0
- package/public/chunk-WU2EPT6Y.js +1 -0
- package/public/chunk-WUMR3VEV.js +1 -0
- package/public/chunk-WZZWFCHX.js +4 -0
- package/public/chunk-XB7KM2X2.js +1 -0
- package/public/chunk-YJ2ZKKJQ.js +1 -0
- package/public/index.html +2 -2
- package/public/main-BQGRDRA6.js +1 -0
- package/public/media/fa-brands-400-Q3XCMWHQ.woff2 +0 -0
- package/public/media/{fa-brands-400-CNBICIQT.ttf → fa-brands-400-R2XQZCET.ttf} +0 -0
- package/public/media/fa-regular-400-QSNYFYRT.woff2 +0 -0
- package/public/media/{fa-regular-400-2FI4ICQD.ttf → fa-regular-400-XUOPSR7E.ttf} +0 -0
- package/public/media/fa-solid-900-5ZUYHGA7.woff2 +0 -0
- package/public/media/{fa-solid-900-XVWCCN5V.ttf → fa-solid-900-PJNKLK6W.ttf} +0 -0
- package/public/polyfills-GCAZ7JAV.js +2 -0
- package/public/prerendered-routes.json +3 -0
- package/public/styles-PKR3EGHP.css +1 -0
- package/scripts/upgrade-install-plugin.sh +1 -1
- package/public/chunk-3KSUXL3K.js +0 -20
- package/public/chunk-422JXES4.js +0 -23
- package/public/chunk-4R6MSI5I.js +0 -14
- package/public/chunk-5UYN5OWH.js +0 -1
- package/public/chunk-7564WJJL.js +0 -1
- package/public/chunk-7R6JSPNH.js +0 -1
- package/public/chunk-B6C32ZRK.js +0 -1
- package/public/chunk-B6G7GTDI.js +0 -1
- package/public/chunk-BQLALGZ5.js +0 -4
- package/public/chunk-BROUJ7PT.js +0 -1
- package/public/chunk-C4OONTE3.js +0 -1
- package/public/chunk-DOLYKWZH.js +0 -1
- package/public/chunk-DRWF7R4E.js +0 -1
- package/public/chunk-F22FMU37.js +0 -1
- package/public/chunk-FBP5C7VG.js +0 -1
- package/public/chunk-FPOZAQLC.js +0 -5
- package/public/chunk-IGUSDGD6.js +0 -8
- package/public/chunk-JM7QCDMZ.js +0 -1
- package/public/chunk-K3YXRBTH.js +0 -1
- package/public/chunk-K6TZNXIW.js +0 -1
- package/public/chunk-LS7T2AVU.js +0 -8
- package/public/chunk-NZPTOWFC.js +0 -20
- package/public/chunk-RID7V4NP.js +0 -1
- package/public/chunk-RIWBPQF3.js +0 -1
- package/public/chunk-S4SOWUAW.js +0 -1
- package/public/chunk-SJTXTMYT.js +0 -2
- package/public/chunk-T5QVI4H2.js +0 -1
- package/public/chunk-TBJQ2Y3M.js +0 -1
- package/public/chunk-THFLPY67.js +0 -7
- package/public/chunk-TS27NWVA.js +0 -20
- package/public/chunk-VROGSJLF.js +0 -1
- package/public/chunk-VVILZWTF.js +0 -1
- package/public/chunk-WCDFKYP6.js +0 -5
- package/public/chunk-WCQIGCXO.js +0 -1
- package/public/chunk-Z47RP4BH.js +0 -1
- package/public/chunk-ZEONO5N6.js +0 -1
- package/public/main-VTRWQ63R.js +0 -6
- 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-MDS4TU4L.woff2 +0 -0
- package/public/media/fa-regular-400-N56QV3DR.woff2 +0 -0
- package/public/media/fa-solid-900-Z3HFQTXF.woff2 +0 -0
- package/public/polyfills-CE2SNIQF.js +0 -2
- package/public/scripts-6GVLYD7F.js +0 -62
- package/public/styles-7ETWN6DQ.css +0 -1
- /package/public/assets/{bootstrap-4 → bootstrap-5}/cssframework/assets.json +0 -0
|
@@ -40,11 +40,12 @@ 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 = {};
|
|
48
49
|
this.newScopePlugins = {};
|
|
49
50
|
this.verifiedPlugins = [];
|
|
50
51
|
this.verifiedPlusPlugins = [];
|
|
@@ -67,6 +68,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
67
68
|
this.loadPluginList();
|
|
68
69
|
setInterval(this.loadPluginList.bind(this), 60000 * 60 * 12);
|
|
69
70
|
}
|
|
71
|
+
fixDisplayName(plugin) {
|
|
72
|
+
plugin.displayName = plugin.displayName || (plugin.name.charAt(0) === '@' ? plugin.name.split('/')[1] : plugin.name)
|
|
73
|
+
.replace(/-/g, ' ')
|
|
74
|
+
.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase());
|
|
75
|
+
return plugin;
|
|
76
|
+
}
|
|
70
77
|
async getInstalledPlugins() {
|
|
71
78
|
const plugins = [];
|
|
72
79
|
const modules = await this.getInstalledModules();
|
|
@@ -92,12 +99,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
101
|
catch (e) {
|
|
95
|
-
this.logger.error(`Failed to parse plugin
|
|
102
|
+
this.logger.error(`Failed to parse plugin ${pkg.name} as ${e.message}.`);
|
|
96
103
|
}
|
|
97
104
|
});
|
|
98
105
|
}));
|
|
99
|
-
this.installedPlugins = plugins;
|
|
100
|
-
return
|
|
106
|
+
this.installedPlugins = plugins.map(plugin => this.fixDisplayName(plugin));
|
|
107
|
+
return this.installedPlugins;
|
|
101
108
|
}
|
|
102
109
|
async getOutOfDatePlugins() {
|
|
103
110
|
const plugins = await this.getInstalledPlugins();
|
|
@@ -142,58 +149,111 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
142
149
|
throw new common_1.NotFoundException();
|
|
143
150
|
}
|
|
144
151
|
}
|
|
152
|
+
extractTerms(query, separator) {
|
|
153
|
+
return query
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.split(separator)
|
|
156
|
+
.map(term => term.trim())
|
|
157
|
+
.filter(term => term && term !== 'homebridge' && term !== 'plugin');
|
|
158
|
+
}
|
|
159
|
+
getPluginKeywords(plugin) {
|
|
160
|
+
return Array.isArray(plugin.keywords)
|
|
161
|
+
? plugin.keywords.map(k => k.toLowerCase())
|
|
162
|
+
: [];
|
|
163
|
+
}
|
|
164
|
+
matchesPlugin(plugin, searchTerms) {
|
|
165
|
+
const pluginName = plugin.name.toLowerCase();
|
|
166
|
+
const pluginKeywords = this.getPluginKeywords(plugin);
|
|
167
|
+
const pluginDescription = (plugin.description || '').toLowerCase();
|
|
168
|
+
const nameTerms = this.extractTerms(pluginName.substring(pluginName.lastIndexOf('/') + 1), /-/);
|
|
169
|
+
if (nameTerms.every(term => searchTerms.includes(term))) {
|
|
170
|
+
return 'exactName';
|
|
171
|
+
}
|
|
172
|
+
if (searchTerms.every(term => pluginKeywords.includes(term))
|
|
173
|
+
|| searchTerms.every(term => nameTerms.includes(term))) {
|
|
174
|
+
return 'exactKeyword';
|
|
175
|
+
}
|
|
176
|
+
if (searchTerms.some(term => pluginName.includes(term))
|
|
177
|
+
|| searchTerms.some(term => pluginKeywords.some(k => k.includes(term)))
|
|
178
|
+
|| searchTerms.some(term => pluginDescription.includes(term))) {
|
|
179
|
+
return 'partial';
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
145
183
|
async searchNpmRegistry(query) {
|
|
146
184
|
if (!this.installedPlugins) {
|
|
147
185
|
await this.getInstalledPlugins();
|
|
148
186
|
}
|
|
149
|
-
const
|
|
187
|
+
const searchTerms = this.extractTerms(query, /\s+/);
|
|
188
|
+
const normalizedQuery = searchTerms.length > 0 ? searchTerms.join(' ') : 'homebridge';
|
|
189
|
+
if ((normalizedQuery.startsWith('homebridge-') || this.isScopedPlugin(normalizedQuery))
|
|
190
|
+
&& !this.hiddenPlugins.includes(normalizedQuery)) {
|
|
191
|
+
if (!this.installedPlugins.find(x => x.name === normalizedQuery)
|
|
192
|
+
&& Object.keys(this.newScopePlugins).includes(normalizedQuery)) {
|
|
193
|
+
return await this.searchNpmRegistrySingle(`@homebridge-plugins/${normalizedQuery}`);
|
|
194
|
+
}
|
|
195
|
+
return await this.searchNpmRegistrySingle(normalizedQuery);
|
|
196
|
+
}
|
|
197
|
+
const q = `${normalizedQuery.substring(0, 15)}+keywords:homebridge-plugin+not:deprecated&size=99`;
|
|
150
198
|
let searchResults;
|
|
151
199
|
try {
|
|
152
200
|
searchResults = (await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://registry.npmjs.org/-/v1/search?text=${q}`))).data;
|
|
153
201
|
}
|
|
154
202
|
catch (e) {
|
|
155
|
-
this.logger.error(`Failed to search the npm registry
|
|
156
|
-
throw new common_1.InternalServerErrorException(`Failed to search the npm registry
|
|
203
|
+
this.logger.error(`Failed to search the npm registry (see https://homebridge.io/w/JJSz6 for help) as ${e.message}.`);
|
|
204
|
+
throw new common_1.InternalServerErrorException(`Failed to search the npm registry as ${e.message}, see logs.`);
|
|
157
205
|
}
|
|
158
|
-
const
|
|
159
|
-
.filter(x => x.package.name.
|
|
206
|
+
const plugins = searchResults.objects
|
|
207
|
+
.filter(x => x.package.name.startsWith('homebridge-') || this.isScopedPlugin(x.package.name))
|
|
160
208
|
.filter(x => !this.hiddenPlugins.includes(x.package.name))
|
|
161
209
|
.map((pkg) => {
|
|
162
|
-
|
|
210
|
+
const isInstalled = this.installedPlugins.find(x => x.name === pkg.package.name);
|
|
211
|
+
if (isInstalled) {
|
|
212
|
+
return {
|
|
213
|
+
...isInstalled,
|
|
214
|
+
lastUpdated: pkg.package.date,
|
|
215
|
+
keywords: pkg.package.keywords || [],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
163
219
|
name: pkg.package.name,
|
|
220
|
+
displayName: this.pluginNames[pkg.package.name],
|
|
164
221
|
private: false,
|
|
222
|
+
publicPackage: true,
|
|
223
|
+
installedVersion: null,
|
|
224
|
+
latestVersion: pkg.package.version,
|
|
225
|
+
lastUpdated: pkg.package.date,
|
|
226
|
+
description: (pkg.package.description || pkg.package.name).replace(/\(?(?:https?|ftp):\/\/[\n\S]+/g, '').trim(),
|
|
227
|
+
keywords: pkg.package.keywords || [],
|
|
228
|
+
links: pkg.package.links,
|
|
229
|
+
author: this.pluginAuthors[pkg.package.name] || (pkg.package.publisher ? pkg.package.publisher.username : null),
|
|
230
|
+
verifiedPlugin: this.verifiedPlugins.includes(pkg.package.name),
|
|
231
|
+
verifiedPlusPlugin: this.verifiedPlusPlugins.includes(pkg.package.name),
|
|
232
|
+
icon: this.pluginIcons[pkg.package.name] ? `${this.pluginListUrl}${this.pluginIcons[pkg.package.name]}` : null,
|
|
233
|
+
isHbScoped: pkg.package.name.startsWith('@homebridge-plugins/'),
|
|
234
|
+
newHbScope: this.newScopePlugins[pkg.package.name],
|
|
235
|
+
isHbMaintained: this.maintainedPlugins.includes(pkg.package.name),
|
|
165
236
|
};
|
|
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
237
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
238
|
+
const matchGroups = {
|
|
239
|
+
exactName: [],
|
|
240
|
+
exactKeyword: [],
|
|
241
|
+
partial: [],
|
|
242
|
+
};
|
|
243
|
+
for (const plugin of plugins) {
|
|
244
|
+
const matchType = this.matchesPlugin(plugin, searchTerms);
|
|
245
|
+
if (matchType) {
|
|
246
|
+
matchGroups[matchType].push(plugin);
|
|
247
|
+
}
|
|
195
248
|
}
|
|
196
|
-
|
|
249
|
+
const orderPlugins = (arr) => (0, lodash_1.orderBy)(arr, ['isHbScoped', 'verifiedPlusPlugin', 'verifiedPlugin', 'lastUpdated'], ['desc', 'desc', 'desc']);
|
|
250
|
+
return [
|
|
251
|
+
...orderPlugins(matchGroups.exactName),
|
|
252
|
+
...orderPlugins(matchGroups.exactKeyword),
|
|
253
|
+
...orderPlugins(matchGroups.partial),
|
|
254
|
+
]
|
|
255
|
+
.slice(0, 30)
|
|
256
|
+
.map(plugin => this.fixDisplayName(plugin));
|
|
197
257
|
}
|
|
198
258
|
async searchNpmRegistrySingle(query) {
|
|
199
259
|
try {
|
|
@@ -224,10 +284,11 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
224
284
|
verifiedPlugin: this.verifiedPlugins.includes(pkg.name),
|
|
225
285
|
verifiedPlusPlugin: this.verifiedPlusPlugins.includes(pkg.name),
|
|
226
286
|
icon: this.pluginIcons[pkg.name],
|
|
227
|
-
isHbScoped:
|
|
287
|
+
isHbScoped: pkg.name.startsWith('@homebridge-plugins/'),
|
|
228
288
|
newHbScope: this.newScopePlugins[pkg.name],
|
|
229
289
|
isHbMaintained: this.maintainedPlugins.includes(pkg.name),
|
|
230
290
|
};
|
|
291
|
+
plugin.displayName = this.pluginNames[pkg.name];
|
|
231
292
|
plugin.publicPackage = true;
|
|
232
293
|
plugin.latestVersion = pkg['dist-tags'] ? pkg['dist-tags'].latest : undefined;
|
|
233
294
|
plugin.lastUpdated = pkg.time.modified;
|
|
@@ -238,38 +299,38 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
238
299
|
homepage: pkg.homepage,
|
|
239
300
|
bugs: typeof pkg.bugs === 'object' && pkg.bugs?.url ? pkg.bugs.url : null,
|
|
240
301
|
};
|
|
241
|
-
plugin.author = this.
|
|
302
|
+
plugin.author = this.pluginAuthors[pkg.name]
|
|
242
303
|
|| ((pkg.maintainers && pkg.maintainers.length) ? pkg.maintainers[0].name : null);
|
|
243
304
|
plugin.verifiedPlugin = this.verifiedPlugins.includes(pkg.name);
|
|
244
305
|
plugin.verifiedPlusPlugin = this.verifiedPlusPlugins.includes(pkg.name);
|
|
245
306
|
plugin.icon = this.pluginIcons[pkg.name]
|
|
246
307
|
? `${this.pluginListUrl}${this.pluginIcons[pkg.name]}`
|
|
247
308
|
: null;
|
|
248
|
-
plugin.isHbScoped =
|
|
309
|
+
plugin.isHbScoped = pkg.name.startsWith('@homebridge-plugins/');
|
|
249
310
|
plugin.newHbScope = this.newScopePlugins[pkg.name];
|
|
250
311
|
plugin.isHbMaintained = this.maintainedPlugins.includes(pkg.name);
|
|
251
|
-
return [plugin];
|
|
312
|
+
return [this.fixDisplayName(plugin)];
|
|
252
313
|
}
|
|
253
314
|
catch (e) {
|
|
254
315
|
if (e.response?.status !== 404) {
|
|
255
|
-
this.logger.error(`Failed to search the npm registry
|
|
316
|
+
this.logger.error(`Failed to search the npm registry (see https://homebridge.io/w/JJSz6 for help) as ${e.message}.`);
|
|
256
317
|
}
|
|
257
318
|
return [];
|
|
258
319
|
}
|
|
259
320
|
}
|
|
260
|
-
async
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
throw new Error(`Cannot uninstall ${pluginAction.name} from ${this.configService.name}.`);
|
|
321
|
+
async manageUi(action, pluginAction, client) {
|
|
322
|
+
if (action === 'uninstall') {
|
|
323
|
+
throw new Error('Cannot uninstall the Homebridge UI.');
|
|
264
324
|
}
|
|
265
|
-
if (
|
|
325
|
+
if (this.configService.dockerOfflineUpdate && pluginAction.version === 'latest') {
|
|
266
326
|
await this.updateSelfOffline(client);
|
|
267
327
|
return true;
|
|
268
328
|
}
|
|
269
329
|
if (action === 'install' && pluginAction.version === 'latest') {
|
|
270
330
|
pluginAction.version = await this.getNpmModuleLatestVersion(pluginAction.name);
|
|
271
331
|
}
|
|
272
|
-
|
|
332
|
+
const userPlatform = (0, node_os_1.platform)();
|
|
333
|
+
let installPath = this.configService.customPluginPath
|
|
273
334
|
? this.configService.customPluginPath
|
|
274
335
|
: this.installedPlugins.find(x => x.name === this.configService.name).installPath;
|
|
275
336
|
await this.getInstalledPlugins();
|
|
@@ -277,24 +338,53 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
277
338
|
if (existingPlugin) {
|
|
278
339
|
installPath = existingPlugin.installPath;
|
|
279
340
|
}
|
|
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
|
-
}
|
|
341
|
+
const githubReleaseName = await this.isUiUpdateBundleAvailable(pluginAction);
|
|
342
|
+
if (githubReleaseName) {
|
|
343
|
+
try {
|
|
344
|
+
await this.doUiBundleUpdate(pluginAction, client, githubReleaseName);
|
|
345
|
+
return true;
|
|
290
346
|
}
|
|
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'));
|
|
347
|
+
catch (e) {
|
|
348
|
+
client.emit('stdout', (0, bash_color_1.yellow)('\r\nBundled update failed. Trying regular update using npm.\r\n\r\n'));
|
|
296
349
|
}
|
|
297
350
|
}
|
|
351
|
+
if ((0, node_os_1.cpus)().length === 1 && (0, node_os_1.arch)() === 'arm') {
|
|
352
|
+
client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n'));
|
|
353
|
+
client.emit('stdout', (0, bash_color_1.yellow)(`Please be patient while ${this.configService.name} updates.\r\n`));
|
|
354
|
+
client.emit('stdout', (0, bash_color_1.yellow)('This process may take 5-15 minutes to complete on your device.\r\n'));
|
|
355
|
+
client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n\r\n'));
|
|
356
|
+
}
|
|
357
|
+
const installOptions = [];
|
|
358
|
+
if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
|
|
359
|
+
installOptions.push('--save');
|
|
360
|
+
}
|
|
361
|
+
installPath = (0, node_path_1.resolve)(installPath, '../');
|
|
362
|
+
if (!this.configService.customPluginPath || userPlatform === 'win32' || existingPlugin?.globalInstall === true) {
|
|
363
|
+
installOptions.push('-g');
|
|
364
|
+
}
|
|
365
|
+
installOptions.push('--omit=dev');
|
|
366
|
+
const npmPluginLabel = `${pluginAction.name}@${pluginAction.version}`;
|
|
367
|
+
await this.cleanNpmCache();
|
|
368
|
+
await this.runNpmCommand([...this.npm, action, ...installOptions, npmPluginLabel], installPath, client, pluginAction.termCols, pluginAction.termRows);
|
|
369
|
+
await this.ensureCustomPluginDirExists();
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
async managePlugin(action, pluginAction, client) {
|
|
373
|
+
pluginAction.version = pluginAction.version || 'latest';
|
|
374
|
+
if (pluginAction.name === this.configService.name) {
|
|
375
|
+
return await this.manageUi(action, pluginAction, client);
|
|
376
|
+
}
|
|
377
|
+
if (action === 'install' && pluginAction.version === 'latest') {
|
|
378
|
+
pluginAction.version = await this.getNpmModuleLatestVersion(pluginAction.name);
|
|
379
|
+
}
|
|
380
|
+
let installPath = this.configService.customPluginPath
|
|
381
|
+
? this.configService.customPluginPath
|
|
382
|
+
: this.installedPlugins.find(x => x.name === this.configService.name).installPath;
|
|
383
|
+
await this.getInstalledPlugins();
|
|
384
|
+
const existingPlugin = this.installedPlugins.find(x => x.name === pluginAction.name);
|
|
385
|
+
if (existingPlugin) {
|
|
386
|
+
installPath = existingPlugin.installPath;
|
|
387
|
+
}
|
|
298
388
|
if (action === 'install' && await this.isPluginBundleAvailable(pluginAction)) {
|
|
299
389
|
try {
|
|
300
390
|
await this.doPluginBundleUpdate(pluginAction, client);
|
|
@@ -305,6 +395,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
305
395
|
}
|
|
306
396
|
}
|
|
307
397
|
const installOptions = [];
|
|
398
|
+
let npmPluginLabel = pluginAction.name;
|
|
308
399
|
if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
|
|
309
400
|
installOptions.push('--save');
|
|
310
401
|
}
|
|
@@ -312,20 +403,14 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
312
403
|
if (!this.configService.customPluginPath || (0, node_os_1.platform)() === 'win32' || existingPlugin?.globalInstall === true) {
|
|
313
404
|
installOptions.push('-g');
|
|
314
405
|
}
|
|
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;
|
|
406
|
+
if (action === 'install') {
|
|
407
|
+
installOptions.push('--omit=dev');
|
|
408
|
+
npmPluginLabel = `${pluginAction.name}@${pluginAction.version}`;
|
|
328
409
|
}
|
|
410
|
+
await this.cleanNpmCache();
|
|
411
|
+
await this.runNpmCommand([...this.npm, action, ...installOptions, npmPluginLabel], installPath, client, pluginAction.termCols, pluginAction.termRows);
|
|
412
|
+
await this.ensureCustomPluginDirExists();
|
|
413
|
+
return true;
|
|
329
414
|
}
|
|
330
415
|
async getHomebridgePackage() {
|
|
331
416
|
if (this.configService.ui.homebridgePackagePath) {
|
|
@@ -334,21 +419,21 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
334
419
|
return await this.parsePackageJson(await (0, fs_extra_1.readJson)(pkgJsonPath), this.configService.ui.homebridgePackagePath);
|
|
335
420
|
}
|
|
336
421
|
else {
|
|
337
|
-
this.logger.error(`
|
|
422
|
+
this.logger.error(`The Homebridge path ${this.configService.ui.homebridgePackagePath} does not exist.`);
|
|
338
423
|
}
|
|
339
424
|
}
|
|
340
425
|
const modules = await this.getInstalledModules();
|
|
341
426
|
const homebridgeInstalls = modules.filter(x => x.name === 'homebridge');
|
|
342
427
|
if (homebridgeInstalls.length > 1) {
|
|
343
|
-
this.logger.warn('Multiple
|
|
428
|
+
this.logger.warn('Multiple instances of Homebridge were found, see https://homebridge.io/w/JJSgm for help.');
|
|
344
429
|
homebridgeInstalls.forEach((instance) => {
|
|
345
430
|
this.logger.warn(instance.installPath);
|
|
346
431
|
});
|
|
347
432
|
}
|
|
348
433
|
if (!homebridgeInstalls.length) {
|
|
349
434
|
this.configService.hbServiceUiRestartRequired = true;
|
|
350
|
-
this.logger.error('Unable
|
|
351
|
-
throw new Error('Unable To Find Homebridge Installation');
|
|
435
|
+
this.logger.error('Unable to find Homebridge installation, see https://homebridge.io/w/JJSgZ for help.');
|
|
436
|
+
throw new Error('Unable To Find Homebridge Installation.');
|
|
352
437
|
}
|
|
353
438
|
const homebridgeModule = homebridgeInstalls[0];
|
|
354
439
|
const pkgJson = await (0, fs_extra_1.readJson)((0, node_path_1.join)(homebridgeModule.installPath, 'package.json'));
|
|
@@ -378,6 +463,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
378
463
|
}
|
|
379
464
|
let installPath = homebridge.installPath;
|
|
380
465
|
const installOptions = [];
|
|
466
|
+
installOptions.push('--omit=dev');
|
|
381
467
|
if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
|
|
382
468
|
installOptions.push('--save');
|
|
383
469
|
}
|
|
@@ -416,7 +502,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
416
502
|
&& pluginAction.name !== this.configService.name
|
|
417
503
|
&& pluginAction.version !== 'latest') {
|
|
418
504
|
try {
|
|
419
|
-
await (0, rxjs_1.firstValueFrom)(this.httpService.head(`https://github.com/homebridge/
|
|
505
|
+
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
506
|
return true;
|
|
421
507
|
}
|
|
422
508
|
catch (e) {
|
|
@@ -440,7 +526,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
440
526
|
'/var/packages/homebridge/target/app/lib/node_modules',
|
|
441
527
|
].includes((0, node_path_1.dirname)(node_process_1.default.env.UIX_BASE_PATH))
|
|
442
528
|
&& pluginAction.name === this.configService.name
|
|
443
|
-
&&
|
|
529
|
+
&& !['latest', 'alpha', 'beta'].includes(pluginAction.version)) {
|
|
444
530
|
try {
|
|
445
531
|
try {
|
|
446
532
|
const withV = `v${pluginAction.version}`;
|
|
@@ -454,7 +540,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
454
540
|
}
|
|
455
541
|
}
|
|
456
542
|
catch (e) {
|
|
457
|
-
this.logger.error(`Failed to check for bundled update: ${e.message}
|
|
543
|
+
this.logger.error(`Failed to check for bundled update: ${e.message}.`);
|
|
458
544
|
return '';
|
|
459
545
|
}
|
|
460
546
|
}
|
|
@@ -494,27 +580,19 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
494
580
|
let configSchema = await (0, fs_extra_1.readJson)(schemaPath);
|
|
495
581
|
if (configSchema.dynamicSchemaVersion) {
|
|
496
582
|
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}
|
|
583
|
+
this.logger.log(`[${pluginName}] dynamic schema path: ${dynamicSchemaPath}.`);
|
|
498
584
|
if ((0, fs_extra_1.existsSync)(dynamicSchemaPath)) {
|
|
499
585
|
try {
|
|
500
586
|
configSchema = await (0, fs_extra_1.readJson)(dynamicSchemaPath);
|
|
501
|
-
this.logger.log(`[${pluginName}] dynamic schema loaded from
|
|
587
|
+
this.logger.log(`[${pluginName}] dynamic schema loaded from ${dynamicSchemaPath}.`);
|
|
502
588
|
}
|
|
503
589
|
catch (e) {
|
|
504
|
-
this.logger.error(`[${pluginName}]
|
|
590
|
+
this.logger.error(`[${pluginName}] failed to load dynamic schema from ${dynamicSchemaPath} as ${e.message}.`);
|
|
505
591
|
}
|
|
506
592
|
}
|
|
507
593
|
}
|
|
508
594
|
if (pluginName === this.configService.name) {
|
|
509
595
|
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
596
|
}
|
|
519
597
|
if (pluginName === 'homebridge-alexa') {
|
|
520
598
|
configSchema.schema.properties.pin.default = this.configService.homebridgeConfig.bridge.pin;
|
|
@@ -626,7 +704,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
626
704
|
?.name;
|
|
627
705
|
}
|
|
628
706
|
catch (e) {
|
|
629
|
-
this.logger.error(`Failed to get list of branches from GitHub
|
|
707
|
+
this.logger.error(`Failed to get list of branches from GitHub as ${e.message}.`);
|
|
630
708
|
}
|
|
631
709
|
}
|
|
632
710
|
return {
|
|
@@ -696,7 +774,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
696
774
|
});
|
|
697
775
|
}
|
|
698
776
|
catch (e) {
|
|
699
|
-
this.logger.debug(
|
|
777
|
+
this.logger.debug(`Failed to extract ${pluginName} plugin alias as ${e.message}.`);
|
|
700
778
|
if (this.pluginAliasHints[pluginName]) {
|
|
701
779
|
output.pluginAlias = this.pluginAliasHints[pluginName].pluginAlias;
|
|
702
780
|
output.pluginType = this.pluginAliasHints[pluginName].pluginType;
|
|
@@ -788,7 +866,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
788
866
|
}
|
|
789
867
|
}
|
|
790
868
|
catch (e) {
|
|
791
|
-
this.logger.log(`Failed to parse
|
|
869
|
+
this.logger.log(`Failed to parse ${module} in ${requiredPath} as ${e.message}.`);
|
|
792
870
|
}
|
|
793
871
|
}
|
|
794
872
|
}
|
|
@@ -824,8 +902,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
824
902
|
return [windowsNpmPath[0]];
|
|
825
903
|
}
|
|
826
904
|
else {
|
|
827
|
-
this.logger.error('
|
|
828
|
-
this.logger.error('
|
|
905
|
+
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:');
|
|
906
|
+
this.logger.error('npm install -g npm');
|
|
829
907
|
}
|
|
830
908
|
}
|
|
831
909
|
return ['npm'];
|
|
@@ -876,8 +954,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
876
954
|
async parsePackageJson(pkgJson, installPath) {
|
|
877
955
|
const plugin = {
|
|
878
956
|
name: pkgJson.name,
|
|
957
|
+
displayName: pkgJson.displayName || this.pluginNames[pkgJson.name],
|
|
879
958
|
private: pkgJson.private || false,
|
|
880
|
-
displayName: pkgJson.displayName,
|
|
881
959
|
description: (pkgJson.description)
|
|
882
960
|
? pkgJson.description.replace(/(?:https?|ftp):\/\/[\n\S]+/g, '').trim()
|
|
883
961
|
: pkgJson.name,
|
|
@@ -886,7 +964,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
886
964
|
icon: this.pluginIcons[pkgJson.name]
|
|
887
965
|
? `${this.pluginListUrl}${this.pluginIcons[pkgJson.name]}`
|
|
888
966
|
: null,
|
|
889
|
-
isHbScoped:
|
|
967
|
+
isHbScoped: pkgJson.name.startsWith('@homebridge-plugins/'),
|
|
890
968
|
newHbScope: this.newScopePlugins[pkgJson.name],
|
|
891
969
|
isHbMaintained: this.maintainedPlugins.includes(pkgJson.name),
|
|
892
970
|
installedVersion: installPath ? (pkgJson.version || '0.0.1') : null,
|
|
@@ -938,12 +1016,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
938
1016
|
homepage: pkg.homepage,
|
|
939
1017
|
bugs: typeof pkg.bugs === 'object' && pkg.bugs?.url ? pkg.bugs.url : null,
|
|
940
1018
|
};
|
|
941
|
-
plugin.author = this.
|
|
1019
|
+
plugin.author = this.pluginAuthors[pkg.name]
|
|
942
1020
|
|| ((pkg.maintainers && pkg.maintainers.length) ? pkg.maintainers[0].name : null);
|
|
943
1021
|
}
|
|
944
1022
|
catch (e) {
|
|
945
1023
|
if (e.response?.status !== 404) {
|
|
946
|
-
this.logger.log(`[${plugin.name}]
|
|
1024
|
+
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
1025
|
}
|
|
948
1026
|
plugin.publicPackage = false;
|
|
949
1027
|
plugin.latestVersion = null;
|
|
@@ -970,18 +1048,25 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
970
1048
|
command.unshift('sudo', '-E', '-n');
|
|
971
1049
|
}
|
|
972
1050
|
else {
|
|
1051
|
+
let npmInstallPath;
|
|
1052
|
+
try {
|
|
1053
|
+
npmInstallPath = (0, node_child_process_1.execSync)('npm root -g').toString().trim();
|
|
1054
|
+
}
|
|
1055
|
+
catch (e) {
|
|
1056
|
+
npmInstallPath = (0, node_path_1.resolve)(cwd, 'node_modules');
|
|
1057
|
+
}
|
|
973
1058
|
try {
|
|
974
|
-
await (0, fs_extra_1.access)(
|
|
1059
|
+
await (0, fs_extra_1.access)(npmInstallPath, fs_extra_1.constants.W_OK);
|
|
975
1060
|
}
|
|
976
1061
|
catch (e) {
|
|
977
1062
|
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', `${
|
|
1063
|
+
client.emit('stdout', `${npmInstallPath}\n\r\n\r`);
|
|
979
1064
|
client.emit('stdout', (0, bash_color_1.yellow)('This may cause the operation to fail.\n\r'));
|
|
980
1065
|
client.emit('stdout', (0, bash_color_1.yellow)('See the docs for details on how to enable sudo mode:\n\r'));
|
|
981
1066
|
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
1067
|
}
|
|
983
1068
|
}
|
|
984
|
-
this.logger.log(`Running
|
|
1069
|
+
this.logger.log(`Running command ${command.join(' ')}.`);
|
|
985
1070
|
if (!(0, semver_1.satisfies)(node_process_1.default.version, `>=${this.configService.minimumNodeVersion}`)) {
|
|
986
1071
|
client.emit('stdout', (0, bash_color_1.yellow)(`Node.js v${this.configService.minimumNodeVersion} higher is required for ${this.configService.name}.\n\r`));
|
|
987
1072
|
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 +1127,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1042
1127
|
return;
|
|
1043
1128
|
}
|
|
1044
1129
|
if (!await (0, fs_extra_1.pathExists)(this.configService.customPluginPath)) {
|
|
1045
|
-
this.logger.warn(`Custom plugin directory was removed
|
|
1130
|
+
this.logger.warn(`Custom plugin directory was removed, re-creating ${this.configService.customPluginPath}.`);
|
|
1046
1131
|
try {
|
|
1047
1132
|
await (0, fs_extra_1.ensureDir)(this.configService.customPluginPath);
|
|
1048
1133
|
}
|
|
1049
1134
|
catch (e) {
|
|
1050
|
-
this.logger.error(
|
|
1051
|
-
this.logger.error(e.message);
|
|
1135
|
+
this.logger.error(`Failed to re-create custom plugin directory as ${e.message}.`);
|
|
1052
1136
|
}
|
|
1053
1137
|
}
|
|
1054
1138
|
}
|
|
@@ -1063,7 +1147,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1063
1147
|
}
|
|
1064
1148
|
}
|
|
1065
1149
|
catch (e) {
|
|
1066
|
-
this.logger.error(`Failed to remove ${offendingPath}
|
|
1150
|
+
this.logger.error(`Failed to remove ${offendingPath} as ${e.message}.`);
|
|
1067
1151
|
}
|
|
1068
1152
|
}
|
|
1069
1153
|
async cleanNpmCache() {
|
|
@@ -1072,9 +1156,9 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1072
1156
|
command.unshift('sudo', '-E', '-n');
|
|
1073
1157
|
}
|
|
1074
1158
|
return new Promise((res) => {
|
|
1075
|
-
const child = (0, node_child_process_1.spawn)(command.shift(), command);
|
|
1159
|
+
const child = (0, node_child_process_1.spawn)(command.shift(), command, { shell: true });
|
|
1076
1160
|
child.on('exit', (code) => {
|
|
1077
|
-
this.logger.log(
|
|
1161
|
+
this.logger.log(`Executed npm cache clear command with exit code ${code}.`);
|
|
1078
1162
|
res(null);
|
|
1079
1163
|
});
|
|
1080
1164
|
child.on('error', () => {
|
|
@@ -1093,36 +1177,40 @@ let PluginsService = PluginsService_1 = class PluginsService {
|
|
|
1093
1177
|
this.pluginIcons = {};
|
|
1094
1178
|
this.hiddenPlugins = [];
|
|
1095
1179
|
this.maintainedPlugins = [];
|
|
1096
|
-
this.
|
|
1180
|
+
this.pluginAuthors = {};
|
|
1181
|
+
this.pluginNames = {};
|
|
1097
1182
|
this.newScopePlugins = {};
|
|
1098
1183
|
Object.keys(pluginListData).forEach((key) => {
|
|
1099
1184
|
const plugin = pluginListData[key];
|
|
1100
|
-
if (plugin.
|
|
1101
|
-
this.pluginIcons[key] = `icons/${plugin.
|
|
1185
|
+
if (plugin.i) {
|
|
1186
|
+
this.pluginIcons[key] = `icons/${plugin.i}.png`;
|
|
1102
1187
|
}
|
|
1103
|
-
if (plugin.
|
|
1188
|
+
if (plugin.h) {
|
|
1104
1189
|
this.hiddenPlugins.push(key);
|
|
1105
1190
|
}
|
|
1106
|
-
if (plugin.
|
|
1191
|
+
if (plugin.m) {
|
|
1107
1192
|
this.maintainedPlugins.push(key);
|
|
1108
1193
|
}
|
|
1109
|
-
if (plugin.
|
|
1110
|
-
this.
|
|
1194
|
+
if (plugin.a) {
|
|
1195
|
+
this.pluginAuthors[key] = plugin.a;
|
|
1196
|
+
}
|
|
1197
|
+
if (plugin.n) {
|
|
1198
|
+
this.pluginNames[key] = plugin.n;
|
|
1111
1199
|
}
|
|
1112
|
-
if (plugin.
|
|
1113
|
-
this.newScopePlugins[key] = plugin.
|
|
1200
|
+
if (plugin.s) {
|
|
1201
|
+
this.newScopePlugins[key] = plugin.s;
|
|
1114
1202
|
}
|
|
1115
|
-
if (plugin.
|
|
1203
|
+
if (plugin.v) {
|
|
1116
1204
|
this.verifiedPlugins.push(key);
|
|
1117
1205
|
}
|
|
1118
|
-
if (plugin.
|
|
1206
|
+
if (plugin.p) {
|
|
1119
1207
|
this.verifiedPlusPlugins.push(key);
|
|
1120
1208
|
}
|
|
1121
1209
|
});
|
|
1122
1210
|
}
|
|
1123
1211
|
catch (e) {
|
|
1124
1212
|
this.pluginListRetryTimeout = setTimeout(() => this.loadPluginList(), 60000);
|
|
1125
|
-
this.logger.debug(
|
|
1213
|
+
this.logger.debug(`Could not obtain plugin list from plugins repo as ${e.message}.`);
|
|
1126
1214
|
}
|
|
1127
1215
|
}
|
|
1128
1216
|
};
|