homebridge-config-ui-x 5.0.0-beta.6 → 5.0.0-beta.60

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 (316) hide show
  1. package/CHANGELOG.md +384 -20
  2. package/CONTRIBUTING.md +5 -4
  3. package/LICENSE +1 -1
  4. package/config.schema.json +34 -144
  5. package/dist/bin/hb-service.d.ts +2 -2
  6. package/dist/bin/hb-service.js +51 -50
  7. package/dist/bin/hb-service.js.map +1 -1
  8. package/dist/bin/platforms/darwin.js +2 -2
  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 +2 -2
  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 +4 -6
  22. package/dist/core/config/config.service.js +9 -9
  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 +1 -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.d.ts +1 -0
  39. package/dist/modules/backup/backup.service.js +57 -89
  40. package/dist/modules/backup/backup.service.js.map +1 -1
  41. package/dist/modules/child-bridges/child-bridges.service.js +0 -7
  42. package/dist/modules/child-bridges/child-bridges.service.js.map +1 -1
  43. package/dist/modules/config-editor/config-editor.controller.d.ts +1 -1
  44. package/dist/modules/config-editor/config-editor.controller.js +8 -8
  45. package/dist/modules/config-editor/config-editor.controller.js.map +1 -1
  46. package/dist/modules/config-editor/config-editor.service.d.ts +1 -1
  47. package/dist/modules/config-editor/config-editor.service.js +62 -64
  48. package/dist/modules/config-editor/config-editor.service.js.map +1 -1
  49. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.d.ts +1 -1
  50. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js +13 -11
  51. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js.map +1 -1
  52. package/dist/modules/log/log.gateway.d.ts +2 -1
  53. package/dist/modules/log/log.gateway.js.map +1 -1
  54. package/dist/modules/log/log.service.js +3 -3
  55. package/dist/modules/log/log.service.js.map +1 -1
  56. package/dist/modules/platform-tools/docker/docker.controller.js +3 -3
  57. package/dist/modules/platform-tools/docker/docker.controller.js.map +1 -1
  58. package/dist/modules/platform-tools/docker/docker.service.js +1 -1
  59. package/dist/modules/platform-tools/docker/docker.service.js.map +1 -1
  60. package/dist/modules/platform-tools/hb-service/hb-service.controller.js +3 -3
  61. package/dist/modules/platform-tools/hb-service/hb-service.controller.js.map +1 -1
  62. package/dist/modules/platform-tools/hb-service/hb-service.service.js +6 -6
  63. package/dist/modules/platform-tools/hb-service/hb-service.service.js.map +1 -1
  64. package/dist/modules/platform-tools/linux/linux.controller.js +2 -2
  65. package/dist/modules/platform-tools/linux/linux.controller.js.map +1 -1
  66. package/dist/modules/platform-tools/linux/linux.service.js +2 -2
  67. package/dist/modules/platform-tools/linux/linux.service.js.map +1 -1
  68. package/dist/modules/platform-tools/terminal/terminal.gateway.d.ts +2 -1
  69. package/dist/modules/platform-tools/terminal/terminal.gateway.js.map +1 -1
  70. package/dist/modules/platform-tools/terminal/terminal.service.js +2 -2
  71. package/dist/modules/platform-tools/terminal/terminal.service.js.map +1 -1
  72. package/dist/modules/plugins/plugins.controller.js +4 -4
  73. package/dist/modules/plugins/plugins.controller.js.map +1 -1
  74. package/dist/modules/plugins/plugins.service.d.ts +4 -1
  75. package/dist/modules/plugins/plugins.service.js +174 -103
  76. package/dist/modules/plugins/plugins.service.js.map +1 -1
  77. package/dist/modules/server/server.controller.d.ts +22 -2
  78. package/dist/modules/server/server.controller.js +86 -18
  79. package/dist/modules/server/server.controller.js.map +1 -1
  80. package/dist/modules/server/server.service.d.ts +24 -6
  81. package/dist/modules/server/server.service.js +186 -67
  82. package/dist/modules/server/server.service.js.map +1 -1
  83. package/dist/modules/status/status.controller.d.ts +0 -1
  84. package/dist/modules/status/status.controller.js +3 -4
  85. package/dist/modules/status/status.controller.js.map +1 -1
  86. package/dist/modules/status/status.gateway.d.ts +1 -1
  87. package/dist/modules/status/status.service.d.ts +1 -1
  88. package/dist/modules/status/status.service.js +17 -41
  89. package/dist/modules/status/status.service.js.map +1 -1
  90. package/dist/modules/users/users.controller.js +7 -7
  91. package/dist/modules/users/users.controller.js.map +1 -1
  92. package/dist/self-check.js +6 -6
  93. package/dist/self-check.js.map +1 -1
  94. package/package.json +45 -43
  95. package/public/3rdpartylicenses.txt +119 -62
  96. package/public/assets/hap-icons/airpurifier.svg +49 -16
  97. package/public/assets/hap-icons/airquality.svg +24 -13
  98. package/public/assets/hap-icons/co-sensor.svg +72 -0
  99. package/public/assets/hap-icons/co2-sensor.svg +72 -0
  100. package/public/assets/hap-icons/contactsensor-closed.svg +35 -2
  101. package/public/assets/hap-icons/contactsensor-open.svg +80 -2
  102. package/public/assets/hap-icons/door-closed.svg +32 -2
  103. package/public/assets/hap-icons/door-open.svg +48 -2
  104. package/public/assets/hap-icons/fan-off.svg +24 -13
  105. package/public/assets/hap-icons/fan-on.svg +24 -13
  106. package/public/assets/hap-icons/garagedoor.svg +24 -13
  107. package/public/assets/hap-icons/humidity.svg +24 -13
  108. package/public/assets/hap-icons/irrigation-system.svg +47 -18
  109. package/public/assets/hap-icons/leaksensor.svg +52 -2
  110. package/public/assets/hap-icons/light.svg +47 -28
  111. package/public/assets/hap-icons/lightbulb.svg +24 -13
  112. package/public/assets/hap-icons/lock-locked.svg +24 -13
  113. package/public/assets/hap-icons/lock-unlocked.svg +24 -13
  114. package/public/assets/hap-icons/motionsensor.svg +100 -2
  115. package/public/assets/hap-icons/occupancysensor.svg +97 -2
  116. package/public/assets/hap-icons/outlet.svg +24 -13
  117. package/public/assets/hap-icons/securitysystem-active.svg +102 -2
  118. package/public/assets/hap-icons/securitysystem-off.svg +68 -2
  119. package/public/assets/hap-icons/smokesensor.svg +42 -9
  120. package/public/assets/hap-icons/speaker.svg +29 -13
  121. package/public/assets/hap-icons/statelessprogrammableswitch.svg +51 -2
  122. package/public/assets/hap-icons/switch.svg +24 -13
  123. package/public/assets/hap-icons/television.svg +15 -4
  124. package/public/assets/hap-icons/temperature.svg +24 -13
  125. package/public/assets/hap-icons/unknown.svg +24 -13
  126. package/public/assets/hap-icons/valve-generic.svg +27 -16
  127. package/public/assets/hap-icons/valve-irrigation.svg +37 -21
  128. package/public/assets/hap-icons/valve-showerhead.svg +52 -0
  129. package/public/assets/hap-icons/valve-waterfaucet.svg +21 -0
  130. package/public/assets/hap-icons/window-closed.svg +85 -2
  131. package/public/assets/hap-icons/window-open.svg +136 -2
  132. package/public/assets/hap-icons/windowcovering-closed.svg +45 -49
  133. package/public/assets/hap-icons/windowcovering-open.svg +40 -44
  134. package/public/assets/homebridge-color-round.svg +36 -1
  135. package/public/assets/homebridge-logo.svg +11 -1
  136. package/public/assets/mask-icon.svg +5 -1
  137. package/public/chunk-2HDJYV7P.js +1 -0
  138. package/public/chunk-2PYGXL3S.js +1 -0
  139. package/public/chunk-3JURJEIZ.js +1 -0
  140. package/public/chunk-3MYZ74PN.js +1 -0
  141. package/public/{chunk-CCUID66K.js → chunk-3NAAMMFZ.js} +1 -1
  142. package/public/chunk-3R3PVZG7.js +1 -0
  143. package/public/{chunk-BKUGARB4.js → chunk-44K5XVZH.js} +1 -1
  144. package/public/chunk-4UVD743J.js +1 -0
  145. package/public/chunk-5HMI3475.js +1 -0
  146. package/public/{chunk-NW6AFAD7.js → chunk-6OMIETCH.js} +1 -1
  147. package/public/chunk-6ZN4TBLM.js +7 -0
  148. package/public/chunk-7C3VZ3TZ.js +1 -0
  149. package/public/chunk-7IAYSGNN.js +1 -0
  150. package/public/{chunk-NWD4LL6Q.js → chunk-7X5P6T37.js} +1 -1
  151. package/public/chunk-ABI2LUSA.js +1 -0
  152. package/public/{chunk-QE7DO6J3.js → chunk-AFY7IWPI.js} +2 -2
  153. package/public/chunk-B75MYCJS.js +1 -0
  154. package/public/chunk-BC4ZZQWV.js +1 -0
  155. package/public/chunk-BS6X2RZL.js +1 -0
  156. package/public/chunk-BTB3JIDJ.js +1 -0
  157. package/public/chunk-CLRF6AUJ.js +6 -0
  158. package/public/{chunk-WNWWUCCZ.js → chunk-CUKPSGYS.js} +1 -1
  159. package/public/chunk-CYW4JA4J.js +1 -0
  160. package/public/{chunk-WHJSVGC7.js → chunk-D33FRRVF.js} +1 -1
  161. package/public/chunk-D6DBYUFZ.js +1 -0
  162. package/public/chunk-DBUCH6BG.js +1 -0
  163. package/public/chunk-DNQFYNG6.js +1 -0
  164. package/public/chunk-F5ICJKYI.js +8 -0
  165. package/public/chunk-FETTFNJ2.js +1 -0
  166. package/public/{chunk-6TCHCTXZ.js → chunk-G5LGUOIC.js} +1 -1
  167. package/public/chunk-GDFC3M3E.js +2 -0
  168. package/public/chunk-GLHOQ5NF.js +1 -0
  169. package/public/chunk-GSGK2SKG.js +1 -0
  170. package/public/chunk-H2GFXLCD.js +1 -0
  171. package/public/chunk-H2OOFTLR.js +1 -0
  172. package/public/chunk-HEMNUWDO.js +1 -0
  173. package/public/{chunk-3KDOQQBM.js → chunk-HGUYWCTI.js} +2 -2
  174. package/public/chunk-I275JECR.js +1 -0
  175. package/public/{chunk-JZZQRLNW.js → chunk-I6OD3CV4.js} +1 -1
  176. package/public/chunk-IGYTH5AX.js +14 -0
  177. package/public/chunk-IPT5RIJX.js +1 -0
  178. package/public/chunk-J4CB3RFZ.js +1 -0
  179. package/public/chunk-KFR3ZGJZ.js +1 -0
  180. package/public/chunk-KKIRFTQL.js +5 -0
  181. package/public/{chunk-7EUQWCP5.js → chunk-KPW2P5T4.js} +1 -1
  182. package/public/chunk-L3LMIZ4T.js +1 -0
  183. package/public/chunk-L4DBDSHB.js +1 -0
  184. package/public/chunk-LCRNBCRA.js +1 -0
  185. package/public/chunk-LZSQNNXJ.js +1 -0
  186. package/public/chunk-MMD3VZMJ.js +20 -0
  187. package/public/chunk-MOCPJJF3.js +1 -0
  188. package/public/chunk-NAFR5P6T.js +1 -0
  189. package/public/chunk-NRSBQVRN.js +1 -0
  190. package/public/chunk-O5XYQE2Q.js +23 -0
  191. package/public/chunk-OBOD4WBI.js +1 -0
  192. package/public/chunk-OI57SN2B.js +1 -0
  193. package/public/{chunk-4IKE4OKZ.js → chunk-OJE7KDWZ.js} +1 -1
  194. package/public/chunk-OXOBBTSH.js +1 -0
  195. package/public/chunk-PCYP2KRX.js +1 -0
  196. package/public/{chunk-EA5J2VEJ.js → chunk-QCVDY2FW.js} +1 -1
  197. package/public/chunk-QGLICWXR.js +1 -0
  198. package/public/chunk-QJ75M7UB.js +1 -0
  199. package/public/chunk-RFWUHIJ5.js +1 -0
  200. package/public/chunk-T2YC5S4T.js +1 -0
  201. package/public/chunk-TCXWRDYD.js +1 -0
  202. package/public/chunk-TEFE7EK6.js +1 -0
  203. package/public/chunk-TWDTF4UX.js +1 -0
  204. package/public/{chunk-QXT6R24L.js → chunk-TYEF6KNJ.js} +1 -1
  205. package/public/chunk-UCQRW7SR.js +1 -0
  206. package/public/chunk-VDSVEHQG.js +1 -0
  207. package/public/chunk-W2INV3O5.js +5 -0
  208. package/public/chunk-WACROBL3.js +1 -0
  209. package/public/chunk-WGP32ABK.js +1 -0
  210. package/public/chunk-XSS6G7LC.js +1 -0
  211. package/public/chunk-YCBC3C3V.js +1 -0
  212. package/public/chunk-YET3EKNI.js +1 -0
  213. package/public/chunk-YTVVSAM2.js +1 -0
  214. package/public/chunk-YU3C2YTO.js +32 -0
  215. package/public/chunk-YWMHIHWJ.js +1 -0
  216. package/public/chunk-Z7QXZUQS.js +1 -0
  217. package/public/chunk-ZB3BYZDQ.js +1 -0
  218. package/public/chunk-ZIXTTJ4W.js +1 -0
  219. package/public/chunk-ZZQJ3DGD.js +1 -0
  220. package/public/index.html +2 -2
  221. package/public/main-26CAD2XH.js +1 -0
  222. package/public/media/fa-brands-400-Q3XCMWHQ.woff2 +0 -0
  223. package/public/media/{fa-brands-400-KOKGDU7E.ttf → fa-brands-400-R2XQZCET.ttf} +0 -0
  224. package/public/media/fa-regular-400-QSNYFYRT.woff2 +0 -0
  225. package/public/media/{fa-regular-400-IPMAEX5Y.ttf → fa-regular-400-XUOPSR7E.ttf} +0 -0
  226. package/public/media/fa-solid-900-5ZUYHGA7.woff2 +0 -0
  227. package/public/media/{fa-solid-900-SRFFQLRM.ttf → fa-solid-900-PJNKLK6W.ttf} +0 -0
  228. package/public/polyfills-JFMQVUE2.js +2 -0
  229. package/public/styles-6IKEC5B4.css +1 -0
  230. package/scripts/upgrade-install-plugin.sh +1 -1
  231. package/public/chunk-2J6XFH3T.js +0 -1
  232. package/public/chunk-2KNNQ3N3.js +0 -1
  233. package/public/chunk-3FKHRKWG.js +0 -1
  234. package/public/chunk-3IX3CLER.js +0 -1
  235. package/public/chunk-3YHN3VHA.js +0 -1
  236. package/public/chunk-4AVEC3ZU.js +0 -1
  237. package/public/chunk-5BHGCTWL.js +0 -5
  238. package/public/chunk-62ADZGBJ.js +0 -6
  239. package/public/chunk-6GHV2TKM.js +0 -1
  240. package/public/chunk-6SC3OCKT.js +0 -8
  241. package/public/chunk-BCHM64F4.js +0 -1
  242. package/public/chunk-BPMSJ2VF.js +0 -1
  243. package/public/chunk-C2ERQ3FX.js +0 -1
  244. package/public/chunk-C6NH5RUO.js +0 -1
  245. package/public/chunk-CUPS2NE5.js +0 -1
  246. package/public/chunk-D6EUS4KM.js +0 -1
  247. package/public/chunk-DNZYDCPW.js +0 -1
  248. package/public/chunk-E3OHYYGW.js +0 -23
  249. package/public/chunk-EMKXES77.js +0 -14
  250. package/public/chunk-G7AVJCRN.js +0 -1
  251. package/public/chunk-HSJSWZHD.js +0 -1
  252. package/public/chunk-HYBTNEG3.js +0 -1
  253. package/public/chunk-J34SI644.js +0 -1
  254. package/public/chunk-JD7YSN6P.js +0 -1
  255. package/public/chunk-JL3HH376.js +0 -1
  256. package/public/chunk-JN46TW24.js +0 -1
  257. package/public/chunk-JPWCYJWM.js +0 -1
  258. package/public/chunk-JQVXXNK7.js +0 -1
  259. package/public/chunk-JW6PX64L.js +0 -1
  260. package/public/chunk-JXEJU7V3.js +0 -1
  261. package/public/chunk-K53Q52AX.js +0 -1
  262. package/public/chunk-KD3Z24TB.js +0 -1
  263. package/public/chunk-KPCZLNW7.js +0 -1
  264. package/public/chunk-KQJ7ONUG.js +0 -1
  265. package/public/chunk-KUOCPYMN.js +0 -1
  266. package/public/chunk-KXMGIDLQ.js +0 -1
  267. package/public/chunk-KZLHFU5K.js +0 -1
  268. package/public/chunk-LJHUPCL3.js +0 -32
  269. package/public/chunk-MTMEA6JC.js +0 -1
  270. package/public/chunk-N5BZ3IQD.js +0 -1
  271. package/public/chunk-N5UE2QRB.js +0 -1
  272. package/public/chunk-NGAJQJ5T.js +0 -1
  273. package/public/chunk-NNATGODS.js +0 -1
  274. package/public/chunk-NZNNTHFQ.js +0 -1
  275. package/public/chunk-ORPWYWCL.js +0 -5
  276. package/public/chunk-OSDZN4BY.js +0 -1
  277. package/public/chunk-PREJ7FYU.js +0 -1
  278. package/public/chunk-QHPDGSZ6.js +0 -1
  279. package/public/chunk-RBHJOIVB.js +0 -1
  280. package/public/chunk-RRHDXAZE.js +0 -1
  281. package/public/chunk-T7MCUROV.js +0 -1
  282. package/public/chunk-TCSXGQNF.js +0 -1
  283. package/public/chunk-TTENUNEO.js +0 -1
  284. package/public/chunk-TXOB7R5K.js +0 -7
  285. package/public/chunk-UEX6RTZJ.js +0 -1
  286. package/public/chunk-UG5DK2RQ.js +0 -2
  287. package/public/chunk-UG6ZOHXY.js +0 -1
  288. package/public/chunk-UN5422JY.js +0 -1
  289. package/public/chunk-VAJZ7KCO.js +0 -1
  290. package/public/chunk-VBCYNIMT.js +0 -1
  291. package/public/chunk-WHJOLAED.js +0 -1
  292. package/public/chunk-WJFAG3CM.js +0 -20
  293. package/public/chunk-YN5SJ37A.js +0 -1
  294. package/public/chunk-YR3ETJZY.js +0 -1
  295. package/public/chunk-YRD5IXFM.js +0 -1
  296. package/public/chunk-Z4A7KLCA.js +0 -1
  297. package/public/chunk-ZT23DWNL.js +0 -1
  298. package/public/main-LYJIIXIC.js +0 -1
  299. package/public/media/01-RQ3S2L53.png +0 -0
  300. package/public/media/02-VNCG2I2A.png +0 -0
  301. package/public/media/03-HI42L4ZG.png +0 -0
  302. package/public/media/04-FJLL55LZ.png +0 -0
  303. package/public/media/05-V3EO6SPT.png +0 -0
  304. package/public/media/06-EOJZCQZN.png +0 -0
  305. package/public/media/07-KMKB5PBD.png +0 -0
  306. package/public/media/08-UQJRF6B2.png +0 -0
  307. package/public/media/09-2DJQFRHH.png +0 -0
  308. package/public/media/arrow_left-CQT7FZM7.svg +0 -4
  309. package/public/media/arrow_right-SBUDRR2G.svg +0 -4
  310. package/public/media/fa-brands-400-6PJPV6JM.woff2 +0 -0
  311. package/public/media/fa-regular-400-OHB6J4OK.woff2 +0 -0
  312. package/public/media/fa-solid-900-ABTK6BNK.woff2 +0 -0
  313. package/public/polyfills-C6JHVXJJ.js +0 -2
  314. package/public/scripts-6GVLYD7F.js +0 -62
  315. package/public/styles-EG5MFQEM.css +0 -1
  316. /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();
@@ -146,14 +153,21 @@ let PluginsService = PluginsService_1 = class PluginsService {
146
153
  if (!this.installedPlugins) {
147
154
  await this.getInstalledPlugins();
148
155
  }
149
- const q = `${(!query || !query.length) ? '' : `${query}+`}keywords:homebridge-plugin+not:deprecated&size=30`;
156
+ query = query.trim().toLowerCase();
157
+ if ((query.startsWith('homebridge-') || this.isScopedPlugin(query)) && !this.hiddenPlugins.includes(query)) {
158
+ if (!this.installedPlugins.find(x => x.name === query) && Object.keys(this.newScopePlugins).includes(query)) {
159
+ query = `@homebridge-plugins/${query}`;
160
+ }
161
+ return await this.searchNpmRegistrySingle(query);
162
+ }
163
+ const q = `${(!query || !query.length) ? '' : `${query.substring(0, 15)}+`}keywords:homebridge-plugin+not:deprecated&size=99`;
150
164
  let searchResults;
151
165
  try {
152
166
  searchResults = (await (0, rxjs_1.firstValueFrom)(this.httpService.get(`https://registry.npmjs.org/-/v1/search?text=${q}`))).data;
153
167
  }
154
168
  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.`);
169
+ this.logger.error(`Failed to search the npm registry (see https://homebridge.io/w/JJSz6 for help) as ${e.message}.`);
170
+ throw new common_1.InternalServerErrorException(`Failed to search the npm registry as ${e.message}, see logs.`);
157
171
  }
158
172
  const result = searchResults.objects
159
173
  .filter(x => x.package.name.indexOf('homebridge-') === 0 || this.isScopedPlugin(x.package.name))
@@ -161,12 +175,14 @@ let PluginsService = PluginsService_1 = class PluginsService {
161
175
  .map((pkg) => {
162
176
  let plugin = {
163
177
  name: pkg.package.name,
178
+ displayName: this.pluginNames[pkg.package.name],
164
179
  private: false,
165
180
  };
166
181
  const isInstalled = this.installedPlugins.find(x => x.name === plugin.name);
167
182
  if (isInstalled) {
168
183
  plugin = isInstalled;
169
184
  plugin.lastUpdated = pkg.package.date;
185
+ plugin.keywords = pkg.package.keywords;
170
186
  return plugin;
171
187
  }
172
188
  plugin.publicPackage = true;
@@ -176,24 +192,45 @@ let PluginsService = PluginsService_1 = class PluginsService {
176
192
  plugin.description = (pkg.package.description)
177
193
  ? pkg.package.description.replace(/\(?(?:https?|ftp):\/\/[\n\S]+/g, '').trim()
178
194
  : pkg.package.name;
195
+ plugin.keywords = pkg.package.keywords;
179
196
  plugin.links = pkg.package.links;
180
- plugin.author = this.scopedPlugins[pkg.package.name] || ((pkg.package.publisher) ? pkg.package.publisher.username : null);
197
+ plugin.author = this.pluginAuthors[pkg.package.name] || ((pkg.package.publisher) ? pkg.package.publisher.username : null);
181
198
  plugin.verifiedPlugin = this.verifiedPlugins.includes(pkg.package.name);
182
199
  plugin.verifiedPlusPlugin = this.verifiedPlusPlugins.includes(pkg.package.name);
183
200
  plugin.icon = this.pluginIcons[pkg.package.name]
184
201
  ? `${this.pluginListUrl}${this.pluginIcons[pkg.package.name]}`
185
202
  : null;
186
- plugin.isHbScoped = !!this.scopedPlugins[pkg.package.name];
203
+ plugin.isHbScoped = pkg.package.name.startsWith('@homebridge-plugins/');
187
204
  plugin.newHbScope = this.newScopePlugins[pkg.package.name];
188
205
  plugin.isHbMaintained = this.maintainedPlugins.includes(pkg.package.name);
189
206
  return plugin;
190
207
  });
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());
195
- }
196
- return (0, lodash_1.orderBy)(result, ['verifiedPlusPlugin', 'verifiedPlugin'], ['desc', 'desc']);
208
+ const searchTerm = query
209
+ .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, '');
210
+ const searchTerms = searchTerm
211
+ .split(/\s+/)
212
+ .filter(term => term.length > 0);
213
+ const exactMatchPlugins = [];
214
+ const partialMatchPlugins = [];
215
+ result.forEach((plugin) => {
216
+ const pluginKeywords = plugin.keywords.map(keyword => keyword.toLowerCase());
217
+ const isExactMatch = pluginKeywords.includes(searchTerm);
218
+ if (isExactMatch) {
219
+ exactMatchPlugins.push(plugin);
220
+ return;
221
+ }
222
+ const pluginName = plugin.name.toLowerCase();
223
+ const pluginDescription = plugin.description.toLowerCase();
224
+ const isPartialMatch = searchTerms.some(term => pluginName.includes(term))
225
+ || searchTerms.some(term => pluginKeywords.some(keyword => keyword.includes(term)))
226
+ || searchTerms.some(term => pluginDescription.includes(term));
227
+ if (isPartialMatch) {
228
+ partialMatchPlugins.push(plugin);
229
+ }
230
+ });
231
+ return (0, lodash_1.orderBy)([...exactMatchPlugins, ...partialMatchPlugins], ['verifiedPlusPlugin', 'verifiedPlugin'], ['desc', 'desc'])
232
+ .slice(0, 30)
233
+ .map(plugin => this.fixDisplayName(plugin));
197
234
  }
198
235
  async searchNpmRegistrySingle(query) {
199
236
  try {
@@ -224,10 +261,11 @@ let PluginsService = PluginsService_1 = class PluginsService {
224
261
  verifiedPlugin: this.verifiedPlugins.includes(pkg.name),
225
262
  verifiedPlusPlugin: this.verifiedPlusPlugins.includes(pkg.name),
226
263
  icon: this.pluginIcons[pkg.name],
227
- isHbScoped: !!this.scopedPlugins[pkg.name],
264
+ isHbScoped: pkg.name.startsWith('@homebridge-plugins/'),
228
265
  newHbScope: this.newScopePlugins[pkg.name],
229
266
  isHbMaintained: this.maintainedPlugins.includes(pkg.name),
230
267
  };
268
+ plugin.displayName = this.pluginNames[pkg.name];
231
269
  plugin.publicPackage = true;
232
270
  plugin.latestVersion = pkg['dist-tags'] ? pkg['dist-tags'].latest : undefined;
233
271
  plugin.lastUpdated = pkg.time.modified;
@@ -238,38 +276,38 @@ let PluginsService = PluginsService_1 = class PluginsService {
238
276
  homepage: pkg.homepage,
239
277
  bugs: typeof pkg.bugs === 'object' && pkg.bugs?.url ? pkg.bugs.url : null,
240
278
  };
241
- plugin.author = this.scopedPlugins[pkg.name]
279
+ plugin.author = this.pluginAuthors[pkg.name]
242
280
  || ((pkg.maintainers && pkg.maintainers.length) ? pkg.maintainers[0].name : null);
243
281
  plugin.verifiedPlugin = this.verifiedPlugins.includes(pkg.name);
244
282
  plugin.verifiedPlusPlugin = this.verifiedPlusPlugins.includes(pkg.name);
245
283
  plugin.icon = this.pluginIcons[pkg.name]
246
284
  ? `${this.pluginListUrl}${this.pluginIcons[pkg.name]}`
247
285
  : null;
248
- plugin.isHbScoped = !!this.scopedPlugins[pkg.name];
286
+ plugin.isHbScoped = pkg.name.startsWith('@homebridge-plugins/');
249
287
  plugin.newHbScope = this.newScopePlugins[pkg.name];
250
288
  plugin.isHbMaintained = this.maintainedPlugins.includes(pkg.name);
251
- return [plugin];
289
+ return [this.fixDisplayName(plugin)];
252
290
  }
253
291
  catch (e) {
254
292
  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.`);
293
+ this.logger.error(`Failed to search the npm registry (see https://homebridge.io/w/JJSz6 for help) as ${e.message}.`);
256
294
  }
257
295
  return [];
258
296
  }
259
297
  }
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}.`);
298
+ async manageUi(action, pluginAction, client) {
299
+ if (action === 'uninstall') {
300
+ throw new Error('Cannot uninstall the Homebridge UI.');
264
301
  }
265
- if (pluginAction.name === this.configService.name && this.configService.dockerOfflineUpdate && pluginAction.version === 'latest') {
302
+ if (this.configService.dockerOfflineUpdate && pluginAction.version === 'latest') {
266
303
  await this.updateSelfOffline(client);
267
304
  return true;
268
305
  }
269
306
  if (action === 'install' && pluginAction.version === 'latest') {
270
307
  pluginAction.version = await this.getNpmModuleLatestVersion(pluginAction.name);
271
308
  }
272
- let installPath = (this.configService.customPluginPath)
309
+ const userPlatform = (0, node_os_1.platform)();
310
+ let installPath = this.configService.customPluginPath
273
311
  ? this.configService.customPluginPath
274
312
  : this.installedPlugins.find(x => x.name === this.configService.name).installPath;
275
313
  await this.getInstalledPlugins();
@@ -277,24 +315,53 @@ let PluginsService = PluginsService_1 = class PluginsService {
277
315
  if (existingPlugin) {
278
316
  installPath = existingPlugin.installPath;
279
317
  }
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
- }
318
+ const githubReleaseName = await this.isUiUpdateBundleAvailable(pluginAction);
319
+ if (githubReleaseName) {
320
+ try {
321
+ await this.doUiBundleUpdate(pluginAction, client, githubReleaseName);
322
+ return true;
290
323
  }
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'));
324
+ catch (e) {
325
+ client.emit('stdout', (0, bash_color_1.yellow)('\r\nBundled update failed. Trying regular update using npm.\r\n\r\n'));
296
326
  }
297
327
  }
328
+ if ((0, node_os_1.cpus)().length === 1 && (0, node_os_1.arch)() === 'arm') {
329
+ client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n'));
330
+ client.emit('stdout', (0, bash_color_1.yellow)(`Please be patient while ${this.configService.name} updates.\r\n`));
331
+ client.emit('stdout', (0, bash_color_1.yellow)('This process may take 5-15 minutes to complete on your device.\r\n'));
332
+ client.emit('stdout', (0, bash_color_1.yellow)('***************************************************************\r\n\r\n'));
333
+ }
334
+ const installOptions = [];
335
+ if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
336
+ installOptions.push('--save');
337
+ }
338
+ installPath = (0, node_path_1.resolve)(installPath, '../');
339
+ if (!this.configService.customPluginPath || userPlatform === 'win32' || existingPlugin?.globalInstall === true) {
340
+ installOptions.push('-g');
341
+ }
342
+ installOptions.push('--omit=dev');
343
+ const npmPluginLabel = `${pluginAction.name}@${pluginAction.version}`;
344
+ await this.cleanNpmCache();
345
+ await this.runNpmCommand([...this.npm, action, ...installOptions, npmPluginLabel], installPath, client, pluginAction.termCols, pluginAction.termRows);
346
+ await this.ensureCustomPluginDirExists();
347
+ return true;
348
+ }
349
+ async managePlugin(action, pluginAction, client) {
350
+ pluginAction.version = pluginAction.version || 'latest';
351
+ if (pluginAction.name === this.configService.name) {
352
+ return await this.manageUi(action, pluginAction, client);
353
+ }
354
+ if (action === 'install' && pluginAction.version === 'latest') {
355
+ pluginAction.version = await this.getNpmModuleLatestVersion(pluginAction.name);
356
+ }
357
+ let installPath = this.configService.customPluginPath
358
+ ? this.configService.customPluginPath
359
+ : this.installedPlugins.find(x => x.name === this.configService.name).installPath;
360
+ await this.getInstalledPlugins();
361
+ const existingPlugin = this.installedPlugins.find(x => x.name === pluginAction.name);
362
+ if (existingPlugin) {
363
+ installPath = existingPlugin.installPath;
364
+ }
298
365
  if (action === 'install' && await this.isPluginBundleAvailable(pluginAction)) {
299
366
  try {
300
367
  await this.doPluginBundleUpdate(pluginAction, client);
@@ -305,6 +372,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
305
372
  }
306
373
  }
307
374
  const installOptions = [];
375
+ let npmPluginLabel = pluginAction.name;
308
376
  if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
309
377
  installOptions.push('--save');
310
378
  }
@@ -312,20 +380,14 @@ let PluginsService = PluginsService_1 = class PluginsService {
312
380
  if (!this.configService.customPluginPath || (0, node_os_1.platform)() === 'win32' || existingPlugin?.globalInstall === true) {
313
381
  installOptions.push('-g');
314
382
  }
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;
383
+ if (action === 'install') {
384
+ installOptions.push('--omit=dev');
385
+ npmPluginLabel = `${pluginAction.name}@${pluginAction.version}`;
328
386
  }
387
+ await this.cleanNpmCache();
388
+ await this.runNpmCommand([...this.npm, action, ...installOptions, npmPluginLabel], installPath, client, pluginAction.termCols, pluginAction.termRows);
389
+ await this.ensureCustomPluginDirExists();
390
+ return true;
329
391
  }
330
392
  async getHomebridgePackage() {
331
393
  if (this.configService.ui.homebridgePackagePath) {
@@ -334,21 +396,21 @@ let PluginsService = PluginsService_1 = class PluginsService {
334
396
  return await this.parsePackageJson(await (0, fs_extra_1.readJson)(pkgJsonPath), this.configService.ui.homebridgePackagePath);
335
397
  }
336
398
  else {
337
- this.logger.error(`"homebridgePath" (${this.configService.ui.homebridgePackagePath}) does not exist`);
399
+ this.logger.error(`The Homebridge path ${this.configService.ui.homebridgePackagePath} does not exist.`);
338
400
  }
339
401
  }
340
402
  const modules = await this.getInstalledModules();
341
403
  const homebridgeInstalls = modules.filter(x => x.name === 'homebridge');
342
404
  if (homebridgeInstalls.length > 1) {
343
- this.logger.warn('Multiple Instances Of Homebridge Found Installed - see https://homebridge.io/w/JJSgm for help.');
405
+ this.logger.warn('Multiple instances of Homebridge were found, see https://homebridge.io/w/JJSgm for help.');
344
406
  homebridgeInstalls.forEach((instance) => {
345
407
  this.logger.warn(instance.installPath);
346
408
  });
347
409
  }
348
410
  if (!homebridgeInstalls.length) {
349
411
  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');
412
+ this.logger.error('Unable to find Homebridge installation, see https://homebridge.io/w/JJSgZ for help.');
413
+ throw new Error('Unable To Find Homebridge Installation.');
352
414
  }
353
415
  const homebridgeModule = homebridgeInstalls[0];
354
416
  const pkgJson = await (0, fs_extra_1.readJson)((0, node_path_1.join)(homebridgeModule.installPath, 'package.json'));
@@ -378,6 +440,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
378
440
  }
379
441
  let installPath = homebridge.installPath;
380
442
  const installOptions = [];
443
+ installOptions.push('--omit=dev');
381
444
  if (installPath === this.configService.customPluginPath && await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(installPath, '../package.json'))) {
382
445
  installOptions.push('--save');
383
446
  }
@@ -416,7 +479,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
416
479
  && pluginAction.name !== this.configService.name
417
480
  && pluginAction.version !== 'latest') {
418
481
  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`));
482
+ 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
483
  return true;
421
484
  }
422
485
  catch (e) {
@@ -440,7 +503,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
440
503
  '/var/packages/homebridge/target/app/lib/node_modules',
441
504
  ].includes((0, node_path_1.dirname)(node_process_1.default.env.UIX_BASE_PATH))
442
505
  && pluginAction.name === this.configService.name
443
- && pluginAction.version !== 'latest') {
506
+ && !['latest', 'alpha', 'beta'].includes(pluginAction.version)) {
444
507
  try {
445
508
  try {
446
509
  const withV = `v${pluginAction.version}`;
@@ -454,7 +517,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
454
517
  }
455
518
  }
456
519
  catch (e) {
457
- this.logger.error(`Failed to check for bundled update: ${e.message}`);
520
+ this.logger.error(`Failed to check for bundled update: ${e.message}.`);
458
521
  return '';
459
522
  }
460
523
  }
@@ -491,30 +554,28 @@ let PluginsService = PluginsService_1 = class PluginsService {
491
554
  throw new common_1.NotFoundException();
492
555
  }
493
556
  const schemaPath = (0, node_path_1.resolve)(plugin.installPath, pluginName, 'config.schema.json');
557
+ if (!schemaPath.startsWith(plugin.installPath)) {
558
+ throw new common_1.BadRequestException('Invalid plugin path');
559
+ }
494
560
  let configSchema = await (0, fs_extra_1.readJson)(schemaPath);
495
561
  if (configSchema.dynamicSchemaVersion) {
496
562
  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}`);
563
+ if (!dynamicSchemaPath.startsWith(this.configService.storagePath)) {
564
+ throw new common_1.BadRequestException('Invalid dynamic schema path');
565
+ }
566
+ this.logger.log(`[${pluginName}] dynamic schema path: ${dynamicSchemaPath}.`);
498
567
  if ((0, fs_extra_1.existsSync)(dynamicSchemaPath)) {
499
568
  try {
500
569
  configSchema = await (0, fs_extra_1.readJson)(dynamicSchemaPath);
501
- this.logger.log(`[${pluginName}] dynamic schema loaded from: ${dynamicSchemaPath}`);
570
+ this.logger.log(`[${pluginName}] dynamic schema loaded from ${dynamicSchemaPath}.`);
502
571
  }
503
572
  catch (e) {
504
- this.logger.error(`[${pluginName}] Failed to load dynamic schema at ${dynamicSchemaPath}: ${e.message}`);
573
+ this.logger.error(`[${pluginName}] failed to load dynamic schema from ${dynamicSchemaPath} as ${e.message}.`);
505
574
  }
506
575
  }
507
576
  }
508
577
  if (pluginName === this.configService.name) {
509
578
  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
579
  }
519
580
  if (pluginName === 'homebridge-alexa') {
520
581
  configSchema.schema.properties.pin.default = this.configService.homebridgeConfig.bridge.pin;
@@ -626,7 +687,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
626
687
  ?.name;
627
688
  }
628
689
  catch (e) {
629
- this.logger.error(`Failed to get list of branches from GitHub: ${e.message}`);
690
+ this.logger.error(`Failed to get list of branches from GitHub as ${e.message}.`);
630
691
  }
631
692
  }
632
693
  return {
@@ -696,7 +757,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
696
757
  });
697
758
  }
698
759
  catch (e) {
699
- this.logger.debug('Failed to extract plugin alias:', e);
760
+ this.logger.debug(`Failed to extract ${pluginName} plugin alias as ${e.message}.`);
700
761
  if (this.pluginAliasHints[pluginName]) {
701
762
  output.pluginAlias = this.pluginAliasHints[pluginName].pluginAlias;
702
763
  output.pluginType = this.pluginAliasHints[pluginName].pluginType;
@@ -788,7 +849,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
788
849
  }
789
850
  }
790
851
  catch (e) {
791
- this.logger.log(`Failed to parse item "${module}" in ${requiredPath}: ${e.message}`);
852
+ this.logger.log(`Failed to parse ${module} in ${requiredPath} as ${e.message}.`);
792
853
  }
793
854
  }
794
855
  }
@@ -824,8 +885,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
824
885
  return [windowsNpmPath[0]];
825
886
  }
826
887
  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');
888
+ 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:');
889
+ this.logger.error('npm install -g npm');
829
890
  }
830
891
  }
831
892
  return ['npm'];
@@ -876,8 +937,8 @@ let PluginsService = PluginsService_1 = class PluginsService {
876
937
  async parsePackageJson(pkgJson, installPath) {
877
938
  const plugin = {
878
939
  name: pkgJson.name,
940
+ displayName: pkgJson.displayName || this.pluginNames[pkgJson.name],
879
941
  private: pkgJson.private || false,
880
- displayName: pkgJson.displayName,
881
942
  description: (pkgJson.description)
882
943
  ? pkgJson.description.replace(/(?:https?|ftp):\/\/[\n\S]+/g, '').trim()
883
944
  : pkgJson.name,
@@ -886,7 +947,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
886
947
  icon: this.pluginIcons[pkgJson.name]
887
948
  ? `${this.pluginListUrl}${this.pluginIcons[pkgJson.name]}`
888
949
  : null,
889
- isHbScoped: !!this.scopedPlugins[pkgJson.name],
950
+ isHbScoped: pkgJson.name.startsWith('@homebridge-plugins/'),
890
951
  newHbScope: this.newScopePlugins[pkgJson.name],
891
952
  isHbMaintained: this.maintainedPlugins.includes(pkgJson.name),
892
953
  installedVersion: installPath ? (pkgJson.version || '0.0.1') : null,
@@ -938,12 +999,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
938
999
  homepage: pkg.homepage,
939
1000
  bugs: typeof pkg.bugs === 'object' && pkg.bugs?.url ? pkg.bugs.url : null,
940
1001
  };
941
- plugin.author = this.scopedPlugins[pkg.name]
1002
+ plugin.author = this.pluginAuthors[pkg.name]
942
1003
  || ((pkg.maintainers && pkg.maintainers.length) ? pkg.maintainers[0].name : null);
943
1004
  }
944
1005
  catch (e) {
945
1006
  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.`);
1007
+ 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
1008
  }
948
1009
  plugin.publicPackage = false;
949
1010
  plugin.latestVersion = null;
@@ -970,18 +1031,25 @@ let PluginsService = PluginsService_1 = class PluginsService {
970
1031
  command.unshift('sudo', '-E', '-n');
971
1032
  }
972
1033
  else {
1034
+ let npmInstallPath;
973
1035
  try {
974
- await (0, fs_extra_1.access)((0, node_path_1.resolve)(cwd, 'node_modules'), fs_extra_1.constants.W_OK);
1036
+ npmInstallPath = (0, node_child_process_1.execSync)('npm root -g').toString().trim();
1037
+ }
1038
+ catch (e) {
1039
+ npmInstallPath = (0, node_path_1.resolve)(cwd, 'node_modules');
1040
+ }
1041
+ try {
1042
+ await (0, fs_extra_1.access)(npmInstallPath, fs_extra_1.constants.W_OK);
975
1043
  }
976
1044
  catch (e) {
977
1045
  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`);
1046
+ client.emit('stdout', `${npmInstallPath}\n\r\n\r`);
979
1047
  client.emit('stdout', (0, bash_color_1.yellow)('This may cause the operation to fail.\n\r'));
980
1048
  client.emit('stdout', (0, bash_color_1.yellow)('See the docs for details on how to enable sudo mode:\n\r'));
981
1049
  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
1050
  }
983
1051
  }
984
- this.logger.log(`Running Command: ${command.join(' ')}`);
1052
+ this.logger.log(`Running command ${command.join(' ')}.`);
985
1053
  if (!(0, semver_1.satisfies)(node_process_1.default.version, `>=${this.configService.minimumNodeVersion}`)) {
986
1054
  client.emit('stdout', (0, bash_color_1.yellow)(`Node.js v${this.configService.minimumNodeVersion} higher is required for ${this.configService.name}.\n\r`));
987
1055
  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 +1110,12 @@ let PluginsService = PluginsService_1 = class PluginsService {
1042
1110
  return;
1043
1111
  }
1044
1112
  if (!await (0, fs_extra_1.pathExists)(this.configService.customPluginPath)) {
1045
- this.logger.warn(`Custom plugin directory was removed. Re-creating: ${this.configService.customPluginPath}`);
1113
+ this.logger.warn(`Custom plugin directory was removed, re-creating ${this.configService.customPluginPath}.`);
1046
1114
  try {
1047
1115
  await (0, fs_extra_1.ensureDir)(this.configService.customPluginPath);
1048
1116
  }
1049
1117
  catch (e) {
1050
- this.logger.error('Failed to recreate custom plugin directory');
1051
- this.logger.error(e.message);
1118
+ this.logger.error(`Failed to re-create custom plugin directory as ${e.message}.`);
1052
1119
  }
1053
1120
  }
1054
1121
  }
@@ -1063,7 +1130,7 @@ let PluginsService = PluginsService_1 = class PluginsService {
1063
1130
  }
1064
1131
  }
1065
1132
  catch (e) {
1066
- this.logger.error(`Failed to remove ${offendingPath}`, e.message);
1133
+ this.logger.error(`Failed to remove ${offendingPath} as ${e.message}.`);
1067
1134
  }
1068
1135
  }
1069
1136
  async cleanNpmCache() {
@@ -1072,9 +1139,9 @@ let PluginsService = PluginsService_1 = class PluginsService {
1072
1139
  command.unshift('sudo', '-E', '-n');
1073
1140
  }
1074
1141
  return new Promise((res) => {
1075
- const child = (0, node_child_process_1.spawn)(command.shift(), command);
1142
+ const child = (0, node_child_process_1.spawn)(command.shift(), command, { shell: true });
1076
1143
  child.on('exit', (code) => {
1077
- this.logger.log('npm cache clear command executed with exit code', code);
1144
+ this.logger.log(`Executed npm cache clear command with exit code ${code}.`);
1078
1145
  res(null);
1079
1146
  });
1080
1147
  child.on('error', () => {
@@ -1093,36 +1160,40 @@ let PluginsService = PluginsService_1 = class PluginsService {
1093
1160
  this.pluginIcons = {};
1094
1161
  this.hiddenPlugins = [];
1095
1162
  this.maintainedPlugins = [];
1096
- this.scopedPlugins = {};
1163
+ this.pluginAuthors = {};
1164
+ this.pluginNames = {};
1097
1165
  this.newScopePlugins = {};
1098
1166
  Object.keys(pluginListData).forEach((key) => {
1099
1167
  const plugin = pluginListData[key];
1100
- if (plugin.icon) {
1101
- this.pluginIcons[key] = `icons/${plugin.icon}.png`;
1168
+ if (plugin.i) {
1169
+ this.pluginIcons[key] = `icons/${plugin.i}.png`;
1102
1170
  }
1103
- if (plugin.hidden) {
1171
+ if (plugin.h) {
1104
1172
  this.hiddenPlugins.push(key);
1105
1173
  }
1106
- if (plugin.maintained) {
1174
+ if (plugin.m) {
1107
1175
  this.maintainedPlugins.push(key);
1108
1176
  }
1109
- if (plugin.scoped) {
1110
- this.scopedPlugins[key] = plugin.scoped;
1177
+ if (plugin.a) {
1178
+ this.pluginAuthors[key] = plugin.a;
1179
+ }
1180
+ if (plugin.n) {
1181
+ this.pluginNames[key] = plugin.n;
1111
1182
  }
1112
- if (plugin.newScope) {
1113
- this.newScopePlugins[key] = plugin.newScope;
1183
+ if (plugin.s) {
1184
+ this.newScopePlugins[key] = plugin.s;
1114
1185
  }
1115
- if (plugin.verified) {
1186
+ if (plugin.v) {
1116
1187
  this.verifiedPlugins.push(key);
1117
1188
  }
1118
- if (plugin.verifiedPlus) {
1189
+ if (plugin.p) {
1119
1190
  this.verifiedPlusPlugins.push(key);
1120
1191
  }
1121
1192
  });
1122
1193
  }
1123
1194
  catch (e) {
1124
1195
  this.pluginListRetryTimeout = setTimeout(() => this.loadPluginList(), 60000);
1125
- this.logger.debug('Error when trying to get github plugin list:', e.message);
1196
+ this.logger.debug(`Could not obtain plugin list from plugins repo as ${e.message}.`);
1126
1197
  }
1127
1198
  }
1128
1199
  };