homebridge-config-ui-x 5.0.0-beta.10 → 5.0.0-beta.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/CHANGELOG.md +438 -31
  2. package/CONTRIBUTING.md +5 -4
  3. package/LICENSE +1 -1
  4. package/config.schema.json +55 -252
  5. package/dist/bin/hb-service.d.ts +2 -2
  6. package/dist/bin/hb-service.js +52 -51
  7. package/dist/bin/hb-service.js.map +1 -1
  8. package/dist/bin/platforms/darwin.js +3 -10
  9. package/dist/bin/platforms/darwin.js.map +1 -1
  10. package/dist/bin/platforms/win32.js +4 -2
  11. package/dist/bin/platforms/win32.js.map +1 -1
  12. package/dist/bin/standalone.js +1 -0
  13. package/dist/bin/standalone.js.map +1 -1
  14. package/dist/core/auth/auth.controller.d.ts +31 -3
  15. package/dist/core/auth/auth.controller.js.map +1 -1
  16. package/dist/core/auth/auth.module.js +1 -1
  17. package/dist/core/auth/auth.module.js.map +1 -1
  18. package/dist/core/auth/auth.service.js +10 -10
  19. package/dist/core/auth/auth.service.js.map +1 -1
  20. package/dist/core/auth/jwt.strategy.d.ts +3 -1
  21. package/dist/core/config/config.service.d.ts +46 -11
  22. package/dist/core/config/config.service.js +57 -20
  23. package/dist/core/config/config.service.js.map +1 -1
  24. package/dist/core/config/config.startup.js +2 -2
  25. package/dist/core/config/config.startup.js.map +1 -1
  26. package/dist/core/homebridge-ipc/homebridge-ipc.service.js +3 -3
  27. package/dist/core/homebridge-ipc/homebridge-ipc.service.js.map +1 -1
  28. package/dist/index.js +6 -77
  29. package/dist/index.js.map +1 -1
  30. package/dist/main.js +2 -1
  31. package/dist/main.js.map +1 -1
  32. package/dist/modules/accessories/accessories.controller.js +5 -5
  33. package/dist/modules/accessories/accessories.controller.js.map +1 -1
  34. package/dist/modules/accessories/accessories.service.js +8 -7
  35. package/dist/modules/accessories/accessories.service.js.map +1 -1
  36. package/dist/modules/backup/backup.controller.js +7 -7
  37. package/dist/modules/backup/backup.controller.js.map +1 -1
  38. package/dist/modules/backup/backup.service.js +19 -79
  39. package/dist/modules/backup/backup.service.js.map +1 -1
  40. package/dist/modules/child-bridges/child-bridges.service.js +0 -7
  41. package/dist/modules/child-bridges/child-bridges.service.js.map +1 -1
  42. package/dist/modules/config-editor/config-editor.controller.d.ts +7 -1
  43. package/dist/modules/config-editor/config-editor.controller.js +36 -8
  44. package/dist/modules/config-editor/config-editor.controller.js.map +1 -1
  45. package/dist/modules/config-editor/config-editor.service.d.ts +5 -1
  46. package/dist/modules/config-editor/config-editor.service.js +132 -69
  47. package/dist/modules/config-editor/config-editor.service.js.map +1 -1
  48. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.d.ts +1 -1
  49. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js +13 -11
  50. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js.map +1 -1
  51. package/dist/modules/log/log.gateway.d.ts +2 -1
  52. package/dist/modules/log/log.gateway.js.map +1 -1
  53. package/dist/modules/log/log.service.js +3 -3
  54. package/dist/modules/log/log.service.js.map +1 -1
  55. package/dist/modules/platform-tools/docker/docker.controller.js +3 -3
  56. package/dist/modules/platform-tools/docker/docker.controller.js.map +1 -1
  57. package/dist/modules/platform-tools/docker/docker.service.js +1 -1
  58. package/dist/modules/platform-tools/docker/docker.service.js.map +1 -1
  59. package/dist/modules/platform-tools/hb-service/hb-service.controller.js +3 -3
  60. package/dist/modules/platform-tools/hb-service/hb-service.controller.js.map +1 -1
  61. package/dist/modules/platform-tools/hb-service/hb-service.service.js +6 -6
  62. package/dist/modules/platform-tools/hb-service/hb-service.service.js.map +1 -1
  63. package/dist/modules/platform-tools/linux/linux.controller.js +2 -2
  64. package/dist/modules/platform-tools/linux/linux.controller.js.map +1 -1
  65. package/dist/modules/platform-tools/linux/linux.service.js +2 -2
  66. package/dist/modules/platform-tools/linux/linux.service.js.map +1 -1
  67. package/dist/modules/platform-tools/terminal/terminal.gateway.d.ts +2 -1
  68. package/dist/modules/platform-tools/terminal/terminal.gateway.js.map +1 -1
  69. package/dist/modules/platform-tools/terminal/terminal.service.js +2 -2
  70. package/dist/modules/platform-tools/terminal/terminal.service.js.map +1 -1
  71. package/dist/modules/plugins/plugins.controller.js +4 -4
  72. package/dist/modules/plugins/plugins.controller.js.map +1 -1
  73. package/dist/modules/plugins/plugins.service.d.ts +7 -1
  74. package/dist/modules/plugins/plugins.service.js +215 -127
  75. package/dist/modules/plugins/plugins.service.js.map +1 -1
  76. package/dist/modules/server/server.controller.d.ts +33 -3
  77. package/dist/modules/server/server.controller.js +162 -20
  78. package/dist/modules/server/server.controller.js.map +1 -1
  79. package/dist/modules/server/server.service.d.ts +35 -6
  80. package/dist/modules/server/server.service.js +265 -66
  81. package/dist/modules/server/server.service.js.map +1 -1
  82. package/dist/modules/status/status.controller.d.ts +0 -1
  83. package/dist/modules/status/status.controller.js +3 -4
  84. package/dist/modules/status/status.controller.js.map +1 -1
  85. package/dist/modules/status/status.gateway.d.ts +1 -1
  86. package/dist/modules/status/status.service.d.ts +1 -1
  87. package/dist/modules/status/status.service.js +25 -41
  88. package/dist/modules/status/status.service.js.map +1 -1
  89. package/dist/modules/users/users.controller.js +7 -7
  90. package/dist/modules/users/users.controller.js.map +1 -1
  91. package/dist/self-check.js +6 -6
  92. package/dist/self-check.js.map +1 -1
  93. package/package.json +50 -43
  94. package/public/3rdpartylicenses.txt +218 -108
  95. package/public/assets/hap-icons/airpurifier.svg +49 -16
  96. package/public/assets/hap-icons/airquality.svg +24 -13
  97. package/public/assets/hap-icons/co-sensor.svg +72 -0
  98. package/public/assets/hap-icons/co2-sensor.svg +72 -0
  99. package/public/assets/hap-icons/contactsensor-closed.svg +35 -2
  100. package/public/assets/hap-icons/contactsensor-open.svg +80 -2
  101. package/public/assets/hap-icons/door-closed.svg +32 -2
  102. package/public/assets/hap-icons/door-open.svg +48 -2
  103. package/public/assets/hap-icons/fan-off.svg +24 -13
  104. package/public/assets/hap-icons/fan-on.svg +24 -13
  105. package/public/assets/hap-icons/garagedoor.svg +24 -13
  106. package/public/assets/hap-icons/humidity.svg +24 -13
  107. package/public/assets/hap-icons/irrigation-system.svg +47 -18
  108. package/public/assets/hap-icons/leaksensor.svg +52 -2
  109. package/public/assets/hap-icons/light.svg +47 -28
  110. package/public/assets/hap-icons/lightbulb.svg +24 -13
  111. package/public/assets/hap-icons/lock-locked.svg +24 -13
  112. package/public/assets/hap-icons/lock-unlocked.svg +24 -13
  113. package/public/assets/hap-icons/motionsensor.svg +100 -2
  114. package/public/assets/hap-icons/occupancysensor.svg +97 -2
  115. package/public/assets/hap-icons/outlet.svg +24 -13
  116. package/public/assets/hap-icons/securitysystem-active.svg +102 -2
  117. package/public/assets/hap-icons/securitysystem-off.svg +68 -2
  118. package/public/assets/hap-icons/smokesensor.svg +42 -9
  119. package/public/assets/hap-icons/speaker.svg +29 -13
  120. package/public/assets/hap-icons/statelessprogrammableswitch.svg +51 -2
  121. package/public/assets/hap-icons/switch.svg +24 -13
  122. package/public/assets/hap-icons/television.svg +15 -4
  123. package/public/assets/hap-icons/temperature.svg +24 -13
  124. package/public/assets/hap-icons/unknown.svg +24 -13
  125. package/public/assets/hap-icons/valve-generic.svg +27 -16
  126. package/public/assets/hap-icons/valve-irrigation.svg +37 -21
  127. package/public/assets/hap-icons/valve-showerhead.svg +52 -0
  128. package/public/assets/hap-icons/valve-waterfaucet.svg +21 -0
  129. package/public/assets/hap-icons/window-closed.svg +85 -2
  130. package/public/assets/hap-icons/window-open.svg +136 -2
  131. package/public/assets/hap-icons/windowcovering-closed.svg +45 -49
  132. package/public/assets/hap-icons/windowcovering-open.svg +40 -44
  133. package/public/assets/homebridge-color-round.svg +36 -1
  134. package/public/assets/homebridge-logo.svg +11 -1
  135. package/public/assets/mask-icon.svg +5 -1
  136. package/public/assets/plugin-ui-utils/ui.js +3 -0
  137. package/public/assets/plugin-ui-utils/ui.js.map +1 -1
  138. package/public/chunk-2G7GNBIF.js +1 -0
  139. package/public/chunk-3KBLYMVM.js +1 -0
  140. package/public/chunk-3NTCV6WP.js +2 -0
  141. package/public/chunk-4A5UMBFX.js +1 -0
  142. package/public/chunk-4KFFPE3X.js +1 -0
  143. package/public/chunk-4RGKWQU6.js +1 -0
  144. package/public/chunk-5NBW7CCV.js +1 -0
  145. package/public/chunk-67FAL7JH.js +1 -0
  146. package/public/chunk-6YYG3OKR.js +1 -0
  147. package/public/chunk-A3KWNY4Q.js +1 -0
  148. package/public/chunk-ALG7CIS3.js +5 -0
  149. package/public/chunk-BIRWL4X3.js +1 -0
  150. package/public/chunk-BKR5NA75.js +1 -0
  151. package/public/chunk-BMCQHGMB.js +1 -0
  152. package/public/{chunk-EA5J2VEJ.js → chunk-BRW7C4BZ.js} +1 -1
  153. package/public/chunk-C6LXQBT3.js +1 -0
  154. package/public/chunk-CG7LPZUJ.js +1 -0
  155. package/public/{chunk-Q5NJBBHF.js → chunk-CNOY6SFC.js} +1 -1
  156. package/public/chunk-CSHEX5NC.js +29 -0
  157. package/public/chunk-DDFZVHUO.js +1 -0
  158. package/public/{chunk-BSD4FVOE.js → chunk-E36YMZY4.js} +2 -2
  159. package/public/chunk-EGRPIFYF.js +1 -0
  160. package/public/chunk-EHYB64DK.js +1 -0
  161. package/public/chunk-EZZ2U6ZO.js +8 -0
  162. package/public/chunk-FNHPLIFV.js +1 -0
  163. package/public/chunk-GMY5QOXR.js +7 -0
  164. package/public/chunk-GYBA35QS.js +1 -0
  165. package/public/chunk-H7HZZEL7.js +1 -0
  166. package/public/chunk-HCJSLSTH.js +1 -0
  167. package/public/chunk-HPVQO6EP.js +1 -0
  168. package/public/chunk-HYNYVUKA.js +6 -0
  169. package/public/chunk-IFJOFLLC.js +1 -0
  170. package/public/chunk-IJR4P4CH.js +1 -0
  171. package/public/{chunk-7EUQWCP5.js → chunk-IOWBVW62.js} +2 -2
  172. package/public/chunk-JE732SOT.js +1 -0
  173. package/public/chunk-JHZXVI3K.js +1 -0
  174. package/public/chunk-JSTVINGW.js +1 -0
  175. package/public/chunk-K4IBXXRR.js +1 -0
  176. package/public/chunk-KGQXFMUX.js +8 -0
  177. package/public/chunk-L6JNO3P5.js +5 -0
  178. package/public/chunk-LBTT254H.js +23 -0
  179. package/public/{chunk-6FLZSYP2.js → chunk-MMEFZVFQ.js} +1 -1
  180. package/public/chunk-NANSDWEP.js +1 -0
  181. package/public/chunk-NPGQTP6K.js +1 -0
  182. package/public/chunk-NQ4J7SSU.js +1 -0
  183. package/public/chunk-OHPCYLXI.js +1 -0
  184. package/public/chunk-OK54UGUG.js +1 -0
  185. package/public/chunk-OR6CUPLP.js +1 -0
  186. package/public/chunk-PCOFQVCM.js +1 -0
  187. package/public/chunk-PFYSR33C.js +1 -0
  188. package/public/chunk-PODF4FUD.js +1 -0
  189. package/public/chunk-PU3TWSJU.js +20 -0
  190. package/public/chunk-PVH3OZPO.js +1 -0
  191. package/public/chunk-QGDQHACV.js +1 -0
  192. package/public/chunk-QHUIV2B5.js +1 -0
  193. package/public/chunk-QIH36FRX.js +1 -0
  194. package/public/chunk-QMB5XE4Y.js +1 -0
  195. package/public/chunk-R3VPSO5X.js +7 -0
  196. package/public/{chunk-QE7DO6J3.js → chunk-RNHMRKPJ.js} +2 -2
  197. package/public/chunk-S46K5JOQ.js +1 -0
  198. package/public/chunk-T4TYPDNX.js +1 -0
  199. package/public/chunk-TXSUD3RL.js +1 -0
  200. package/public/{chunk-PIAD3JIG.js → chunk-UTAVUGTT.js} +11 -11
  201. package/public/{chunk-JZZQRLNW.js → chunk-UVOJ3BF7.js} +1 -1
  202. package/public/chunk-UWKOQJLO.js +1 -0
  203. package/public/chunk-V2ZZMO7J.js +1 -0
  204. package/public/chunk-VEPTLCSB.js +1 -0
  205. package/public/{chunk-CCUID66K.js → chunk-VHTQTL2S.js} +1 -1
  206. package/public/chunk-VJEUBTTK.js +1 -0
  207. package/public/chunk-VLX322GM.js +1 -0
  208. package/public/chunk-VUYAK52W.js +2 -0
  209. package/public/chunk-W3C6RTKX.js +1 -0
  210. package/public/{chunk-WNWWUCCZ.js → chunk-WAA5KWIN.js} +3 -3
  211. package/public/chunk-WALYNNK5.js +1 -0
  212. package/public/chunk-WCYCETUA.js +1 -0
  213. package/public/chunk-WD2RKBDO.js +1 -0
  214. package/public/chunk-XAC7NMU3.js +1 -0
  215. package/public/{chunk-TIVE3PVO.js → chunk-XYAWU2ED.js} +1 -1
  216. package/public/chunk-Z64JVPZ3.js +1 -0
  217. package/public/chunk-ZP5ERU6O.js +1 -0
  218. package/public/index.html +2 -2
  219. package/public/main-ORLCJTBG.js +1 -0
  220. package/public/media/fa-brands-400-Q3XCMWHQ.woff2 +0 -0
  221. package/public/media/{fa-brands-400-KOKGDU7E.ttf → fa-brands-400-R2XQZCET.ttf} +0 -0
  222. package/public/media/fa-regular-400-QSNYFYRT.woff2 +0 -0
  223. package/public/media/{fa-regular-400-IPMAEX5Y.ttf → fa-regular-400-XUOPSR7E.ttf} +0 -0
  224. package/public/media/fa-solid-900-5ZUYHGA7.woff2 +0 -0
  225. package/public/media/{fa-solid-900-SRFFQLRM.ttf → fa-solid-900-PJNKLK6W.ttf} +0 -0
  226. package/public/polyfills-GCAZ7JAV.js +2 -0
  227. package/public/styles-PKR3EGHP.css +1 -0
  228. package/scripts/upgrade-install-plugin.sh +1 -1
  229. package/public/chunk-26EWO6RG.js +0 -1
  230. package/public/chunk-3IX3CLER.js +0 -1
  231. package/public/chunk-3ZJBUXFZ.js +0 -20
  232. package/public/chunk-4DDKOVAZ.js +0 -1
  233. package/public/chunk-4IGKXTSE.js +0 -6
  234. package/public/chunk-4PAHKWW6.js +0 -1
  235. package/public/chunk-4SMDEHVB.js +0 -1
  236. package/public/chunk-4X4WE4X3.js +0 -1
  237. package/public/chunk-57O4WFHV.js +0 -1
  238. package/public/chunk-5LGVFGMP.js +0 -1
  239. package/public/chunk-6HVQYFKM.js +0 -1
  240. package/public/chunk-6I5NOTAK.js +0 -5
  241. package/public/chunk-6TCHCTXZ.js +0 -1
  242. package/public/chunk-7RD3ANA6.js +0 -1
  243. package/public/chunk-AEZNVJNA.js +0 -1
  244. package/public/chunk-BKUGARB4.js +0 -7
  245. package/public/chunk-BPMSJ2VF.js +0 -1
  246. package/public/chunk-C3OLDXDF.js +0 -1
  247. package/public/chunk-CKJ7WSCY.js +0 -1
  248. package/public/chunk-DW3VS3AJ.js +0 -1
  249. package/public/chunk-FBHAKNKK.js +0 -1
  250. package/public/chunk-FTBDA6VV.js +0 -1
  251. package/public/chunk-FX2G7EKQ.js +0 -1
  252. package/public/chunk-GLXDWUP7.js +0 -1
  253. package/public/chunk-GW3B2BTG.js +0 -1
  254. package/public/chunk-GZS6TFES.js +0 -1
  255. package/public/chunk-HFUBGIQF.js +0 -8
  256. package/public/chunk-HJMBSNDE.js +0 -1
  257. package/public/chunk-HSJSWZHD.js +0 -1
  258. package/public/chunk-J2ACJ2SJ.js +0 -1
  259. package/public/chunk-J2GVB2HH.js +0 -1
  260. package/public/chunk-JGA3UJQJ.js +0 -1
  261. package/public/chunk-JLZHU54T.js +0 -23
  262. package/public/chunk-JTBW4DGJ.js +0 -1
  263. package/public/chunk-K2S5XQH3.js +0 -1
  264. package/public/chunk-KQJ7ONUG.js +0 -1
  265. package/public/chunk-L376ASVB.js +0 -1
  266. package/public/chunk-MR5BBXDA.js +0 -1
  267. package/public/chunk-NW6AFAD7.js +0 -1
  268. package/public/chunk-NZNNTHFQ.js +0 -1
  269. package/public/chunk-ORPWYWCL.js +0 -5
  270. package/public/chunk-P7LFHZOY.js +0 -1
  271. package/public/chunk-PLAUCLPN.js +0 -1
  272. package/public/chunk-Q3KYLUJV.js +0 -1
  273. package/public/chunk-Q63MEYUW.js +0 -1
  274. package/public/chunk-QHPDGSZ6.js +0 -1
  275. package/public/chunk-RVIWX6PZ.js +0 -1
  276. package/public/chunk-S522GW34.js +0 -1
  277. package/public/chunk-SCN3MDZ7.js +0 -1
  278. package/public/chunk-SQXGCWUT.js +0 -1
  279. package/public/chunk-TCSXGQNF.js +0 -1
  280. package/public/chunk-TGK4ZIKS.js +0 -1
  281. package/public/chunk-TR6DJBRN.js +0 -1
  282. package/public/chunk-TXOB7R5K.js +0 -7
  283. package/public/chunk-UEDIA63L.js +0 -1
  284. package/public/chunk-UG5DK2RQ.js +0 -2
  285. package/public/chunk-VHDU5NZU.js +0 -1
  286. package/public/chunk-VHO4CUXG.js +0 -1
  287. package/public/chunk-VWTJUCNR.js +0 -1
  288. package/public/chunk-WALRIIUN.js +0 -1
  289. package/public/chunk-WBI25HR2.js +0 -1
  290. package/public/chunk-WHJOLAED.js +0 -1
  291. package/public/chunk-WHJSVGC7.js +0 -1
  292. package/public/chunk-WJD5UZP6.js +0 -32
  293. package/public/chunk-WZ5X6FKY.js +0 -1
  294. package/public/chunk-Y6JL2J5G.js +0 -1
  295. package/public/chunk-YPX7RY6A.js +0 -1
  296. package/public/chunk-YZUUWSDB.js +0 -1
  297. package/public/chunk-YZVODNRC.js +0 -1
  298. package/public/chunk-ZT23DWNL.js +0 -1
  299. package/public/main-T37JUFDS.js +0 -1
  300. package/public/media/01-RQ3S2L53.png +0 -0
  301. package/public/media/02-VNCG2I2A.png +0 -0
  302. package/public/media/03-HI42L4ZG.png +0 -0
  303. package/public/media/04-FJLL55LZ.png +0 -0
  304. package/public/media/05-V3EO6SPT.png +0 -0
  305. package/public/media/06-EOJZCQZN.png +0 -0
  306. package/public/media/07-KMKB5PBD.png +0 -0
  307. package/public/media/08-UQJRF6B2.png +0 -0
  308. package/public/media/09-2DJQFRHH.png +0 -0
  309. package/public/media/arrow_left-CQT7FZM7.svg +0 -4
  310. package/public/media/arrow_right-SBUDRR2G.svg +0 -4
  311. package/public/media/fa-brands-400-6PJPV6JM.woff2 +0 -0
  312. package/public/media/fa-regular-400-OHB6J4OK.woff2 +0 -0
  313. package/public/media/fa-solid-900-ABTK6BNK.woff2 +0 -0
  314. package/public/polyfills-C6JHVXJJ.js +0 -2
  315. package/public/scripts-6GVLYD7F.js +0 -62
  316. package/public/styles-EG5MFQEM.css +0 -1
  317. /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.scopedPlugins = {};
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 "${pkg.name}": ${e.message}`);
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 plugins;
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 q = `${(!query || !query.length) ? '' : `${query}+`}keywords:homebridge-plugin+not:deprecated&size=30`;
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 - "${e.message}" - see https://homebridge.io/w/JJSz6 for help.`);
156
- throw new common_1.InternalServerErrorException(`Failed to search the npm registry - "${e.message}" - see logs.`);
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 result = searchResults.objects
159
- .filter(x => x.package.name.indexOf('homebridge-') === 0 || this.isScopedPlugin(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
- let plugin = {
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
- if (!result.length
192
- && (query.indexOf('homebridge-') === 0 || this.isScopedPlugin(query))
193
- && !this.hiddenPlugins.includes(query.toLowerCase())) {
194
- return await this.searchNpmRegistrySingle(query.toLowerCase());
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
- return (0, lodash_1.orderBy)(result, ['verifiedPlusPlugin', 'verifiedPlugin'], ['desc', 'desc']);
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: !!this.scopedPlugins[pkg.name],
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.scopedPlugins[pkg.name]
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 = !!this.scopedPlugins[pkg.name];
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 - "${e.message}" - see https://homebridge.io/w/JJSz6 for help.`);
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 managePlugin(action, pluginAction, client) {
261
- pluginAction.version = pluginAction.version || 'latest';
262
- if (action === 'uninstall' && pluginAction.name === this.configService.name) {
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 (pluginAction.name === this.configService.name && this.configService.dockerOfflineUpdate && pluginAction.version === 'latest') {
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
- let installPath = (this.configService.customPluginPath)
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
- if (action === 'install' && pluginAction.name === this.configService.name) {
281
- const githubReleaseName = await this.isUiUpdateBundleAvailable(pluginAction);
282
- if (githubReleaseName) {
283
- try {
284
- await this.doUiBundleUpdate(pluginAction, client, githubReleaseName);
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
- if ((0, node_os_1.cpus)().length === 1 && (0, node_os_1.arch)() === 'arm') {
292
- client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n'));
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
- const npmPluginLabel = action === 'uninstall' ? pluginAction.name : `${pluginAction.name}@${pluginAction.version}`;
316
- try {
317
- await this.runNpmCommand([...this.npm, action, ...installOptions, npmPluginLabel], installPath, client, pluginAction.termCols, pluginAction.termRows);
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(`"homebridgePath" (${this.configService.ui.homebridgePackagePath}) does not exist`);
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 Instances Of Homebridge Found Installed - see https://homebridge.io/w/JJSgm for help.');
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 To Find Homebridge Installation - see https://homebridge.io/w/JJSgZ for help.');
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/verified/releases/download/v1.0.0/${pluginAction.name.replace('/', '@')}-${pluginAction.version}.sha256`));
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
- && pluginAction.version !== 'latest') {
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: ${dynamicSchemaPath}`);
587
+ this.logger.log(`[${pluginName}] dynamic schema loaded from ${dynamicSchemaPath}.`);
502
588
  }
503
589
  catch (e) {
504
- this.logger.error(`[${pluginName}] Failed to load dynamic schema at ${dynamicSchemaPath}: ${e.message}`);
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: ${e.message}`);
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('Failed to extract plugin alias:', e);
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 item "${module}" in ${requiredPath}: ${e.message}`);
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('ERROR: Cannot find npm binary. You will not be able to manage plugins or update homebridge.');
828
- this.logger.error('ERROR: You might be able to fix this problem by running: npm install -g npm');
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: !!this.scopedPlugins[pkgJson.name],
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.scopedPlugins[pkg.name]
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}] Failed to check registry.npmjs.org for updates: "${e.message}" - see https://homebridge.io/w/JJSz6 for help.`);
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)((0, node_path_1.resolve)(cwd, 'node_modules'), fs_extra_1.constants.W_OK);
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', `${(0, node_path_1.resolve)(cwd, 'node_modules')}\n\r\n\r`);
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 Command: ${command.join(' ')}`);
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. Re-creating: ${this.configService.customPluginPath}`);
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('Failed to recreate custom plugin directory');
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}`, e.message);
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('npm cache clear command executed with exit code', code);
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.scopedPlugins = {};
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.icon) {
1101
- this.pluginIcons[key] = `icons/${plugin.icon}.png`;
1185
+ if (plugin.i) {
1186
+ this.pluginIcons[key] = `icons/${plugin.i}.png`;
1102
1187
  }
1103
- if (plugin.hidden) {
1188
+ if (plugin.h) {
1104
1189
  this.hiddenPlugins.push(key);
1105
1190
  }
1106
- if (plugin.maintained) {
1191
+ if (plugin.m) {
1107
1192
  this.maintainedPlugins.push(key);
1108
1193
  }
1109
- if (plugin.scoped) {
1110
- this.scopedPlugins[key] = plugin.scoped;
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.newScope) {
1113
- this.newScopePlugins[key] = plugin.newScope;
1200
+ if (plugin.s) {
1201
+ this.newScopePlugins[key] = plugin.s;
1114
1202
  }
1115
- if (plugin.verified) {
1203
+ if (plugin.v) {
1116
1204
  this.verifiedPlugins.push(key);
1117
1205
  }
1118
- if (plugin.verifiedPlus) {
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('Error when trying to get github plugin list:', e.message);
1213
+ this.logger.debug(`Could not obtain plugin list from plugins repo as ${e.message}.`);
1126
1214
  }
1127
1215
  }
1128
1216
  };