matterbridge 2.2.7 → 3.0.0-edge.1

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 (148) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README-DEV.md +24 -12
  3. package/dist/cli.js +2 -37
  4. package/dist/cluster/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -23
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/frontend.js +19 -325
  8. package/dist/index.js +1 -28
  9. package/dist/logger/export.js +0 -1
  10. package/dist/matter/behaviors.js +0 -2
  11. package/dist/matter/clusters.js +0 -2
  12. package/dist/matter/devices.js +0 -2
  13. package/dist/matter/endpoints.js +0 -2
  14. package/dist/matter/export.js +0 -2
  15. package/dist/matter/types.js +0 -2
  16. package/dist/matterbridge.js +89 -760
  17. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  18. package/dist/matterbridgeBehaviors.js +15 -41
  19. package/dist/matterbridgeDeviceTypes.js +151 -228
  20. package/dist/matterbridgeDynamicPlatform.js +0 -33
  21. package/dist/matterbridgeEndpoint.js +87 -732
  22. package/dist/matterbridgeEndpointHelpers.js +30 -136
  23. package/dist/matterbridgePlatform.js +7 -216
  24. package/dist/matterbridgeTypes.js +0 -24
  25. package/dist/pluginManager.js +36 -305
  26. package/dist/shelly.js +6 -146
  27. package/dist/storage/export.js +0 -1
  28. package/dist/update.js +0 -45
  29. package/dist/utils/colorUtils.js +2 -205
  30. package/dist/utils/copyDirectory.js +1 -37
  31. package/dist/utils/createZip.js +2 -42
  32. package/dist/utils/deepCopy.js +0 -40
  33. package/dist/utils/deepEqual.js +1 -65
  34. package/dist/utils/export.js +0 -1
  35. package/dist/utils/isvalid.js +0 -86
  36. package/dist/utils/network.js +5 -76
  37. package/dist/utils/parameter.js +0 -41
  38. package/dist/utils/wait.js +5 -48
  39. package/npm-shrinkwrap.json +44 -44
  40. package/package.json +2 -3
  41. package/dist/cli.d.ts +0 -29
  42. package/dist/cli.d.ts.map +0 -1
  43. package/dist/cli.js.map +0 -1
  44. package/dist/cluster/export.d.ts +0 -2
  45. package/dist/cluster/export.d.ts.map +0 -1
  46. package/dist/cluster/export.js.map +0 -1
  47. package/dist/defaultConfigSchema.d.ts +0 -27
  48. package/dist/defaultConfigSchema.d.ts.map +0 -1
  49. package/dist/defaultConfigSchema.js.map +0 -1
  50. package/dist/deviceManager.d.ts +0 -114
  51. package/dist/deviceManager.d.ts.map +0 -1
  52. package/dist/deviceManager.js.map +0 -1
  53. package/dist/frontend.d.ts +0 -221
  54. package/dist/frontend.d.ts.map +0 -1
  55. package/dist/frontend.js.map +0 -1
  56. package/dist/index.d.ts +0 -35
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/index.js.map +0 -1
  59. package/dist/logger/export.d.ts +0 -2
  60. package/dist/logger/export.d.ts.map +0 -1
  61. package/dist/logger/export.js.map +0 -1
  62. package/dist/matter/behaviors.d.ts +0 -2
  63. package/dist/matter/behaviors.d.ts.map +0 -1
  64. package/dist/matter/behaviors.js.map +0 -1
  65. package/dist/matter/clusters.d.ts +0 -2
  66. package/dist/matter/clusters.d.ts.map +0 -1
  67. package/dist/matter/clusters.js.map +0 -1
  68. package/dist/matter/devices.d.ts +0 -2
  69. package/dist/matter/devices.d.ts.map +0 -1
  70. package/dist/matter/devices.js.map +0 -1
  71. package/dist/matter/endpoints.d.ts +0 -2
  72. package/dist/matter/endpoints.d.ts.map +0 -1
  73. package/dist/matter/endpoints.js.map +0 -1
  74. package/dist/matter/export.d.ts +0 -5
  75. package/dist/matter/export.d.ts.map +0 -1
  76. package/dist/matter/export.js.map +0 -1
  77. package/dist/matter/types.d.ts +0 -3
  78. package/dist/matter/types.d.ts.map +0 -1
  79. package/dist/matter/types.js.map +0 -1
  80. package/dist/matterbridge.d.ts +0 -425
  81. package/dist/matterbridge.d.ts.map +0 -1
  82. package/dist/matterbridge.js.map +0 -1
  83. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
  84. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  85. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  86. package/dist/matterbridgeBehaviors.d.ts +0 -1056
  87. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  88. package/dist/matterbridgeBehaviors.js.map +0 -1
  89. package/dist/matterbridgeDeviceTypes.d.ts +0 -177
  90. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  91. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  92. package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
  93. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  94. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  95. package/dist/matterbridgeEndpoint.d.ts +0 -852
  96. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  97. package/dist/matterbridgeEndpoint.js.map +0 -1
  98. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
  99. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  100. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  101. package/dist/matterbridgePlatform.d.ts +0 -285
  102. package/dist/matterbridgePlatform.d.ts.map +0 -1
  103. package/dist/matterbridgePlatform.js.map +0 -1
  104. package/dist/matterbridgeTypes.d.ts +0 -183
  105. package/dist/matterbridgeTypes.d.ts.map +0 -1
  106. package/dist/matterbridgeTypes.js.map +0 -1
  107. package/dist/pluginManager.d.ts +0 -271
  108. package/dist/pluginManager.d.ts.map +0 -1
  109. package/dist/pluginManager.js.map +0 -1
  110. package/dist/shelly.d.ts +0 -92
  111. package/dist/shelly.d.ts.map +0 -1
  112. package/dist/shelly.js.map +0 -1
  113. package/dist/storage/export.d.ts +0 -2
  114. package/dist/storage/export.d.ts.map +0 -1
  115. package/dist/storage/export.js.map +0 -1
  116. package/dist/update.d.ts +0 -32
  117. package/dist/update.d.ts.map +0 -1
  118. package/dist/update.js.map +0 -1
  119. package/dist/utils/colorUtils.d.ts +0 -61
  120. package/dist/utils/colorUtils.d.ts.map +0 -1
  121. package/dist/utils/colorUtils.js.map +0 -1
  122. package/dist/utils/copyDirectory.d.ts +0 -32
  123. package/dist/utils/copyDirectory.d.ts.map +0 -1
  124. package/dist/utils/copyDirectory.js.map +0 -1
  125. package/dist/utils/createZip.d.ts +0 -38
  126. package/dist/utils/createZip.d.ts.map +0 -1
  127. package/dist/utils/createZip.js.map +0 -1
  128. package/dist/utils/deepCopy.d.ts +0 -31
  129. package/dist/utils/deepCopy.d.ts.map +0 -1
  130. package/dist/utils/deepCopy.js.map +0 -1
  131. package/dist/utils/deepEqual.d.ts +0 -53
  132. package/dist/utils/deepEqual.d.ts.map +0 -1
  133. package/dist/utils/deepEqual.js.map +0 -1
  134. package/dist/utils/export.d.ts +0 -10
  135. package/dist/utils/export.d.ts.map +0 -1
  136. package/dist/utils/export.js.map +0 -1
  137. package/dist/utils/isvalid.d.ts +0 -87
  138. package/dist/utils/isvalid.d.ts.map +0 -1
  139. package/dist/utils/isvalid.js.map +0 -1
  140. package/dist/utils/network.d.ts +0 -69
  141. package/dist/utils/network.d.ts.map +0 -1
  142. package/dist/utils/network.js.map +0 -1
  143. package/dist/utils/parameter.d.ts +0 -44
  144. package/dist/utils/parameter.d.ts.map +0 -1
  145. package/dist/utils/parameter.js.map +0 -1
  146. package/dist/utils/wait.d.ts +0 -43
  147. package/dist/utils/wait.d.ts.map +0 -1
  148. package/dist/utils/wait.js.map +0 -1
@@ -1,26 +1,3 @@
1
- /**
2
- * This file contains the Plugins class.
3
- *
4
- * @file plugins.ts
5
- * @author Luca Liguori
6
- * @date 2024-07-14
7
- * @version 1.1.1
8
- *
9
- * Copyright 2024, 2025, 2026 Luca Liguori.
10
- *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
14
- *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License. *
22
- */
23
- // AnsiLogger module
24
1
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, BLUE, db, er, nf, nt, rs, wr } from './logger/export.js';
25
2
  import { plg, typ } from './matterbridgeTypes.js';
26
3
  export class PluginManager {
@@ -30,9 +7,8 @@ export class PluginManager {
30
7
  log;
31
8
  constructor(matterbridge) {
32
9
  this.matterbridge = matterbridge;
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
10
  this.nodeContext = matterbridge.nodeContext;
35
- this.log = new AnsiLogger({ logName: 'PluginManager', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: matterbridge.log.logLevel });
11
+ this.log = new AnsiLogger({ logName: 'PluginManager', logTimestampFormat: 4, logLevel: matterbridge.log.logLevel });
36
12
  this.log.debug('Matterbridge plugin manager starting...');
37
13
  }
38
14
  get length() {
@@ -67,9 +43,8 @@ export class PluginManager {
67
43
  try {
68
44
  await callback(plugin);
69
45
  }
70
- catch (error) {
71
- this.log.error(`Error processing forEach plugin ${plg}${plugin.name}${er}:`, error);
72
- // throw error;
46
+ catch (err) {
47
+ this.log.error(`Error processing forEach plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
73
48
  }
74
49
  });
75
50
  await Promise.all(tasks);
@@ -77,31 +52,13 @@ export class PluginManager {
77
52
  set logLevel(logLevel) {
78
53
  this.log.logLevel = logLevel;
79
54
  }
80
- /**
81
- * Loads registered plugins from storage.
82
- *
83
- * This method retrieves an array of registered plugins from the storage and converts it
84
- * into a map where the plugin names are the keys and the plugin objects are the values.
85
- *
86
- * @returns {Promise<RegisteredPlugin[]>} A promise that resolves to an array of registered plugins.
87
- */
88
55
  async loadFromStorage() {
89
- // Load the array from storage and convert it to a map
90
56
  const pluginsArray = await this.nodeContext.get('plugins', []);
91
57
  for (const plugin of pluginsArray)
92
58
  this._plugins.set(plugin.name, plugin);
93
59
  return pluginsArray;
94
60
  }
95
- /**
96
- * Loads registered plugins from storage.
97
- *
98
- * This method retrieves an array of registered plugins from the storage and converts it
99
- * into a map where the plugin names are the keys and the plugin objects are the values.
100
- *
101
- * @returns {Promise<RegisteredPlugin[]>} A promise that resolves to an array of registered plugins.
102
- */
103
61
  async saveToStorage() {
104
- // Convert the map to an array
105
62
  const plugins = [];
106
63
  const pluginArrayFromMap = Array.from(this._plugins.values());
107
64
  for (const plugin of pluginArrayFromMap) {
@@ -121,20 +78,13 @@ export class PluginManager {
121
78
  this.log.debug(`Saved ${BLUE}${plugins.length}${db} plugins to storage`);
122
79
  return plugins.length;
123
80
  }
124
- /**
125
- * Resolves the name of a plugin by loading and parsing its package.json file.
126
- * @param pluginPath - The path to the plugin or the path to the plugin's package.json file.
127
- * @returns The path to the resolved package.json file, or null if the package.json file is not found or does not contain a name.
128
- */
129
81
  async resolve(pluginPath) {
130
82
  const { default: path } = await import('node:path');
131
83
  const { promises } = await import('node:fs');
132
84
  if (!pluginPath.endsWith('package.json'))
133
85
  pluginPath = path.join(pluginPath, 'package.json');
134
- // Resolve the package.json of the plugin
135
86
  let packageJsonPath = path.resolve(pluginPath);
136
87
  this.log.debug(`Resolving plugin path ${plg}${packageJsonPath}${db}`);
137
- // Check if the package.json file exists
138
88
  try {
139
89
  await promises.access(packageJsonPath);
140
90
  }
@@ -144,9 +94,7 @@ export class PluginManager {
144
94
  this.log.debug(`Trying at ${plg}${packageJsonPath}${db}`);
145
95
  }
146
96
  try {
147
- // Load the package.json of the plugin
148
97
  const packageJson = JSON.parse(await promises.readFile(packageJsonPath, 'utf8'));
149
- // Check for main issues
150
98
  if (!packageJson.name) {
151
99
  this.log.error(`Package.json name not found at ${packageJsonPath}`);
152
100
  return null;
@@ -159,7 +107,6 @@ export class PluginManager {
159
107
  this.log.error(`Plugin at ${packageJsonPath} has no main entrypoint in package.json`);
160
108
  return null;
161
109
  }
162
- // Check for @project-chip and @matter packages in dependencies and devDependencies
163
110
  const checkForProjectChipPackages = (dependencies) => {
164
111
  return Object.keys(dependencies).filter((pkg) => pkg.startsWith('@project-chip') || pkg.startsWith('@matter'));
165
112
  };
@@ -181,7 +128,6 @@ export class PluginManager {
181
128
  this.log.error(`Please open an issue on the plugin repository to remove them.`);
182
129
  return null;
183
130
  }
184
- // Check for matterbridge package in dependencies and devDependencies
185
131
  const checkForMatterbridgePackage = (dependencies) => {
186
132
  return Object.keys(dependencies).filter((pkg) => pkg === 'matterbridge');
187
133
  };
@@ -207,93 +153,47 @@ export class PluginManager {
207
153
  return packageJsonPath;
208
154
  }
209
155
  catch (err) {
210
- this.log.error(`Failed to resolve plugin path ${plg}${pluginPath}${er}: ${err}`);
156
+ this.log.error(`Failed to resolve plugin path ${plg}${pluginPath}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
211
157
  return null;
212
158
  }
213
159
  }
214
- /**
215
- * Get the author of a plugin from its package.json.
216
- *
217
- * @param {Record<string, string | number | Record<string, string | number | object>>} packageJson - The package.json object of the plugin.
218
- * @returns {string} The author of the plugin, or 'Unknown author' if not found.
219
- */
220
160
  getAuthor(packageJson) {
221
161
  if (packageJson.author && typeof packageJson.author === 'string')
222
162
  return packageJson.author;
223
- if (packageJson.author && typeof packageJson.author === 'object' && packageJson.author.name && typeof packageJson.author.name === 'string')
163
+ else if (packageJson.author && typeof packageJson.author === 'object' && packageJson.author.name && typeof packageJson.author.name === 'string')
224
164
  return packageJson.author.name;
225
165
  return 'Unknown author';
226
166
  }
227
- /**
228
- * Get the homepage of a plugin from its package.json.
229
- *
230
- * @param {Record<string, string | number | Record<string, string | number | object>>} packageJson - The package.json object of the plugin.
231
- * @returns {string | undefined} The homepage of the plugin, or undefined if not found.
232
- */
233
167
  getHomepage(packageJson) {
234
- if (packageJson.homepage && typeof packageJson.homepage === 'string') {
168
+ if (packageJson.homepage && typeof packageJson.homepage === 'string' && packageJson.homepage.includes('http')) {
235
169
  return packageJson.homepage.replace('git+', '').replace('.git', '');
236
170
  }
237
- if (packageJson.repository && typeof packageJson.repository === 'object' && packageJson.repository.url && typeof packageJson.repository.url === 'string') {
171
+ else if (packageJson.repository && typeof packageJson.repository === 'object' && packageJson.repository.url && typeof packageJson.repository.url === 'string' && packageJson.repository.url.includes('http')) {
238
172
  return packageJson.repository.url.replace('git+', '').replace('.git', '');
239
173
  }
240
174
  }
241
- /**
242
- * Get the help URL of a plugin from its package.json.
243
- *
244
- * @param {Record<string, string | number | Record<string, string | number | object>>} packageJson - The package.json object of the plugin.
245
- * @returns {string | undefined} The URL to the help page or to the README file, or undefined if not found.
246
- */
247
175
  getHelp(packageJson) {
248
- // If there's a help field that looks like a URL, return it.
249
176
  if (packageJson.help && typeof packageJson.help === 'string' && packageJson.help.startsWith('http')) {
250
177
  return packageJson.help;
251
178
  }
252
- // Derive a base URL from homepage or repository.
253
- let baseUrl;
254
- if (packageJson.homepage && typeof packageJson.homepage === 'string') {
255
- // Remove a trailing "/README.md" if present.
256
- baseUrl = packageJson.homepage
257
- .replace(/\/README\.md$/i, '')
258
- .replace('git+', '')
259
- .replace('.git', '');
179
+ else if (packageJson.repository && typeof packageJson.repository === 'object' && packageJson.repository.url && typeof packageJson.repository.url === 'string' && packageJson.repository.url.includes('http')) {
180
+ return packageJson.repository.url.replace('git+', '').replace('.git', '') + '/blob/main/README.md';
260
181
  }
261
- else if (packageJson.repository && typeof packageJson.repository === 'object' && packageJson.repository.url && typeof packageJson.repository.url === 'string') {
262
- baseUrl = packageJson.repository.url.replace('git+', '').replace('.git', '');
182
+ else if (packageJson.homepage && typeof packageJson.homepage === 'string' && packageJson.homepage.includes('http')) {
183
+ return packageJson.homepage.replace('git+', '').replace('.git', '');
263
184
  }
264
- return baseUrl ? `${baseUrl}/blob/main/README.md` : undefined;
265
185
  }
266
- /**
267
- * Get the changelog URL of a plugin from its package.json.
268
- *
269
- * @param {Record<string, string | number | Record<string, string | number | object>>} packageJson - The package.json object of the plugin.
270
- * @returns {string | undefined} The URL to the CHANGELOG file, or undefined if not found.
271
- */
272
186
  getChangelog(packageJson) {
273
- // If there's a changelog field that looks like a URL, return it.
274
187
  if (packageJson.changelog && typeof packageJson.changelog === 'string' && packageJson.changelog.startsWith('http')) {
275
188
  return packageJson.changelog;
276
189
  }
277
- // Derive a base URL from homepage or repository.
278
- let baseUrl;
279
- if (packageJson.homepage && typeof packageJson.homepage === 'string') {
280
- baseUrl = packageJson.homepage
281
- .replace(/\/README\.md$/i, '')
282
- .replace('git+', '')
283
- .replace('.git', '');
190
+ else if (packageJson.repository && typeof packageJson.repository === 'object' && packageJson.repository.url && typeof packageJson.repository.url === 'string' && packageJson.repository.url.includes('http')) {
191
+ return packageJson.repository.url.replace('git+', '').replace('.git', '') + '/blob/main/CHANGELOG.md';
284
192
  }
285
- else if (packageJson.repository && typeof packageJson.repository === 'object' && packageJson.repository.url && typeof packageJson.repository.url === 'string') {
286
- baseUrl = packageJson.repository.url.replace('git+', '').replace('.git', '');
193
+ else if (packageJson.homepage && typeof packageJson.homepage === 'string' && packageJson.homepage.includes('http')) {
194
+ return packageJson.homepage.replace('git+', '').replace('.git', '');
287
195
  }
288
- return baseUrl ? `${baseUrl}/blob/main/CHANGELOG.md` : undefined;
289
196
  }
290
- /**
291
- * Get the first funding URL(s) of a plugin from its package.json.
292
- *
293
- * @param {Record<string, any>} packageJson - The package.json object of the plugin.
294
- * @returns {string | undefined} The first funding URLs, or undefined if not found.
295
- */
296
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
297
197
  getFunding(packageJson) {
298
198
  const funding = packageJson.funding;
299
199
  if (!funding)
@@ -302,24 +202,16 @@ export class PluginManager {
302
202
  return;
303
203
  if (typeof funding === 'string' && funding.startsWith('http'))
304
204
  return funding;
305
- // Normalize funding into an array.
306
205
  const fundingEntries = Array.isArray(funding) ? funding : [funding];
307
206
  for (const entry of fundingEntries) {
308
207
  if (entry && typeof entry === 'string' && entry.startsWith('http')) {
309
- // If the funding entry is a string, assume it is a URL.
310
208
  return entry;
311
209
  }
312
210
  else if (entry && typeof entry === 'object' && typeof entry.url === 'string' && entry.url.startsWith('http')) {
313
- // If it's an object with a 'url' property, use that.
314
211
  return entry.url;
315
212
  }
316
213
  }
317
214
  }
318
- /**
319
- * Loads and parse the plugin package.json and returns it.
320
- * @param plugin - The plugin to load the package from.
321
- * @returns A Promise that resolves to the package.json object or undefined if the package.json could not be loaded.
322
- */
323
215
  async parse(plugin) {
324
216
  const { promises } = await import('node:fs');
325
217
  try {
@@ -351,7 +243,6 @@ export class PluginManager {
351
243
  this.log.warn(`Plugin ${plg}${plugin.name}${wr} has no path`);
352
244
  if (!plugin.type)
353
245
  this.log.warn(`Plugin ${plg}${plugin.name}${wr} has no type`);
354
- // Check for @project-chip and @matter packages in dependencies and devDependencies
355
246
  const checkForProjectChipPackages = (dependencies) => {
356
247
  return Object.keys(dependencies).filter((pkg) => pkg.startsWith('@project-chip') || pkg.startsWith('@matter'));
357
248
  };
@@ -373,7 +264,6 @@ export class PluginManager {
373
264
  this.log.error(`Please open an issue on the plugin repository to remove them.`);
374
265
  return null;
375
266
  }
376
- // Check for matterbridge package in dependencies and devDependencies
377
267
  const checkForMatterbridgePackage = (dependencies) => {
378
268
  return Object.keys(dependencies).filter((pkg) => pkg === 'matterbridge');
379
269
  };
@@ -395,25 +285,14 @@ export class PluginManager {
395
285
  this.log.error(`Please open an issue on the plugin repository to remove them.`);
396
286
  return null;
397
287
  }
398
- // await this.saveToStorage(); // No need to save the plugin to storage
399
288
  return packageJson;
400
289
  }
401
290
  catch (err) {
402
- this.log.error(`Failed to parse package.json of plugin ${plg}${plugin.name}${er}: ${err}`);
291
+ this.log.error(`Failed to parse package.json of plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
403
292
  plugin.error = true;
404
293
  return null;
405
294
  }
406
295
  }
407
- /**
408
- * Enables a plugin by its name or path.
409
- *
410
- * This method enables a plugin by setting its `enabled` property to `true` and saving the updated
411
- * plugin information to storage. It first checks if the plugin is already registered in the `_plugins` map.
412
- * If not, it attempts to resolve the plugin's `package.json` file to retrieve its name and enable it.
413
- *
414
- * @param {string} nameOrPath - The name or path of the plugin to enable.
415
- * @returns {Promise<RegisteredPlugin | null>} A promise that resolves to the enabled plugin object, or null if the plugin could not be enabled.
416
- */
417
296
  async enable(nameOrPath) {
418
297
  const { promises } = await import('node:fs');
419
298
  if (!nameOrPath || nameOrPath === '')
@@ -443,20 +322,10 @@ export class PluginManager {
443
322
  return plugin;
444
323
  }
445
324
  catch (err) {
446
- this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err}`);
325
+ this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
447
326
  return null;
448
327
  }
449
328
  }
450
- /**
451
- * Enables a plugin by its name or path.
452
- *
453
- * This method enables a plugin by setting its `enabled` property to `true` and saving the updated
454
- * plugin information to storage. It first checks if the plugin is already registered in the `_plugins` map.
455
- * If not, it attempts to resolve the plugin's `package.json` file to retrieve its name and enable it.
456
- *
457
- * @param {string} nameOrPath - The name or path of the plugin to enable.
458
- * @returns {Promise<RegisteredPlugin | null>} A promise that resolves to the enabled plugin object, or null if the plugin could not be enabled.
459
- */
460
329
  async disable(nameOrPath) {
461
330
  const { promises } = await import('node:fs');
462
331
  if (!nameOrPath || nameOrPath === '')
@@ -486,20 +355,10 @@ export class PluginManager {
486
355
  return plugin;
487
356
  }
488
357
  catch (err) {
489
- this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err}`);
358
+ this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
490
359
  return null;
491
360
  }
492
361
  }
493
- /**
494
- * Removes a plugin by its name or path.
495
- *
496
- * This method removes a plugin from the `_plugins` map and saves the updated plugin information to storage.
497
- * It first checks if the plugin is already registered in the `_plugins` map. If not, it attempts to resolve
498
- * the plugin's `package.json` file to retrieve its name and remove it.
499
- *
500
- * @param {string} nameOrPath - The name or path of the plugin to remove.
501
- * @returns {Promise<RegisteredPlugin | null>} A promise that resolves to the removed plugin object, or null if the plugin could not be removed.
502
- */
503
362
  async remove(nameOrPath) {
504
363
  const { promises } = await import('node:fs');
505
364
  if (!nameOrPath || nameOrPath === '')
@@ -529,21 +388,10 @@ export class PluginManager {
529
388
  return plugin;
530
389
  }
531
390
  catch (err) {
532
- this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err}`);
391
+ this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
533
392
  return null;
534
393
  }
535
394
  }
536
- /**
537
- * Adds a plugin by its name or path.
538
- *
539
- * This method adds a plugin to the `_plugins` map and saves the updated plugin information to storage.
540
- * It first resolves the plugin's `package.json` file to retrieve its details. If the plugin is already
541
- * registered, it logs an info message and returns null. Otherwise, it registers the plugin, enables it,
542
- * and saves the updated plugin information to storage.
543
- *
544
- * @param {string} nameOrPath - The name or path of the plugin to add.
545
- * @returns {Promise<RegisteredPlugin | null>} A promise that resolves to the added plugin object, or null if the plugin could not be added.
546
- */
547
395
  async add(nameOrPath) {
548
396
  const { promises } = await import('node:fs');
549
397
  if (!nameOrPath || nameOrPath === '')
@@ -574,19 +422,10 @@ export class PluginManager {
574
422
  return plugin || null;
575
423
  }
576
424
  catch (err) {
577
- this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err instanceof Error ? err.message : err}`);
425
+ this.log.error(`Failed to parse package.json of plugin ${plg}${nameOrPath}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
578
426
  return null;
579
427
  }
580
428
  }
581
- /**
582
- * Installs a plugin by its name.
583
- *
584
- * This method first uninstalls any existing version of the plugin, then installs the plugin globally using npm.
585
- * It logs the installation process and retrieves the installed version of the plugin.
586
- *
587
- * @param {string} name - The name of the plugin to install.
588
- * @returns {Promise<string | undefined>} A promise that resolves to the installed version of the plugin, or undefined if the installation failed.
589
- */
590
429
  async install(name) {
591
430
  const { exec } = await import('node:child_process');
592
431
  await this.uninstall(name);
@@ -601,14 +440,11 @@ export class PluginManager {
601
440
  else {
602
441
  this.log.info(`Installed plugin ${plg}${name}${nf}`);
603
442
  this.log.debug(`Installed plugin ${plg}${name}${db}: ${stdout}`);
604
- // Get the installed version
605
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
606
443
  exec(`npm list -g ${name} --depth=0`, (listError, listStdout, listStderr) => {
607
444
  if (listError) {
608
445
  this.log.error(`List error: ${listError}`);
609
446
  resolve(undefined);
610
447
  }
611
- // Clean the output to get only the package name and version
612
448
  const lines = listStdout.split('\n');
613
449
  const versionLine = lines.find((line) => line.includes(`${name}@`));
614
450
  if (versionLine) {
@@ -624,15 +460,6 @@ export class PluginManager {
624
460
  });
625
461
  });
626
462
  }
627
- /**
628
- * Uninstalls a plugin by its name.
629
- *
630
- * This method uninstalls a globally installed plugin using npm. It logs the uninstallation process
631
- * and returns the name of the uninstalled plugin if successful, or undefined if the uninstallation failed.
632
- *
633
- * @param {string} name - The name of the plugin to uninstall.
634
- * @returns {Promise<string | undefined>} A promise that resolves to the name of the uninstalled plugin, or undefined if the uninstallation failed.
635
- */
636
463
  async uninstall(name) {
637
464
  const { exec } = await import('node:child_process');
638
465
  this.log.info(`Uninstalling plugin ${plg}${name}${nf}`);
@@ -651,14 +478,6 @@ export class PluginManager {
651
478
  });
652
479
  });
653
480
  }
654
- /**
655
- * Loads a plugin and returns the corresponding MatterbridgePlatform instance.
656
- * @param plugin - The plugin to load.
657
- * @param start - Optional flag indicating whether to start the plugin after loading. Default is false.
658
- * @param message - Optional message to pass to the plugin when starting.
659
- * @returns A Promise that resolves to the loaded MatterbridgePlatform instance.
660
- * @throws An error if the plugin is not enabled, already loaded, or fails to load.
661
- */
662
481
  async load(plugin, start = false, message = '', configure = false) {
663
482
  const { promises } = await import('node:fs');
664
483
  const { default: path } = await import('node:path');
@@ -672,20 +491,15 @@ export class PluginManager {
672
491
  }
673
492
  this.log.info(`Loading plugin ${plg}${plugin.name}${nf} type ${typ}${plugin.type}${nf}`);
674
493
  try {
675
- // Load the package.json of the plugin
676
494
  const packageJson = JSON.parse(await promises.readFile(plugin.path, 'utf8'));
677
- // Resolve the main module path relative to package.json
678
495
  const pluginEntry = path.resolve(path.dirname(plugin.path), packageJson.main);
679
- // Dynamically import the plugin
680
496
  const { pathToFileURL } = await import('node:url');
681
497
  const pluginUrl = pathToFileURL(pluginEntry);
682
498
  this.log.debug(`Importing plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
683
499
  const pluginInstance = await import(pluginUrl.href);
684
500
  this.log.debug(`Imported plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
685
- // Call the default export function of the plugin, passing this MatterBridge instance, the log and the config
686
501
  if (pluginInstance.default) {
687
502
  const config = await this.loadConfig(plugin);
688
- // Preset the plugin properties here in case the plugin throws an error during loading. In this case the user can change the config and restart the plugin.
689
503
  plugin.name = packageJson.name;
690
504
  plugin.description = packageJson.description ?? 'No description';
691
505
  plugin.version = packageJson.version;
@@ -694,7 +508,7 @@ export class PluginManager {
694
508
  plugin.schemaJson = await this.loadSchema(plugin);
695
509
  config.name = plugin.name;
696
510
  config.version = packageJson.version;
697
- const log = new AnsiLogger({ logName: plugin.description ?? 'No description', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: config.debug ? "debug" /* LogLevel.DEBUG */ : this.matterbridge.log.logLevel });
511
+ const log = new AnsiLogger({ logName: plugin.description ?? 'No description', logTimestampFormat: 4, logLevel: config.debug ? "debug" : this.matterbridge.log.logLevel });
698
512
  const platform = pluginInstance.default(this.matterbridge, log, config);
699
513
  config.type = platform.type;
700
514
  platform.name = packageJson.name;
@@ -704,12 +518,16 @@ export class PluginManager {
704
518
  plugin.description = packageJson.description ?? 'No description';
705
519
  plugin.version = packageJson.version;
706
520
  plugin.author = this.getAuthor(packageJson);
521
+ plugin.homepage = this.getHomepage(packageJson);
522
+ plugin.help = this.getHelp(packageJson);
523
+ plugin.changelog = this.getChangelog(packageJson);
524
+ plugin.funding = this.getFunding(packageJson);
707
525
  plugin.type = platform.type;
708
526
  plugin.platform = platform;
709
527
  plugin.loaded = true;
710
528
  plugin.registeredDevices = 0;
711
529
  plugin.addedDevices = 0;
712
- await this.saveToStorage(); // Save the plugin to storage
530
+ await this.saveToStorage();
713
531
  this.log.notice(`Loaded plugin ${plg}${plugin.name}${nt} type ${typ}${platform.type}${nt} (entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
714
532
  if (start)
715
533
  await this.start(plugin, message, false);
@@ -723,19 +541,11 @@ export class PluginManager {
723
541
  }
724
542
  }
725
543
  catch (err) {
726
- this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
544
+ this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
727
545
  plugin.error = true;
728
546
  }
729
547
  return undefined;
730
548
  }
731
- /**
732
- * Starts a plugin.
733
- *
734
- * @param {RegisteredPlugin} plugin - The plugin to start.
735
- * @param {string} [message] - Optional message to pass to the plugin's onStart method.
736
- * @param {boolean} [configure] - Indicates whether to configure the plugin after starting (default false).
737
- * @returns {Promise<RegisteredPlugin | undefined>} A promise that resolves when the plugin is started successfully, or rejects with an error if starting the plugin fails.
738
- */
739
549
  async start(plugin, message, configure = false) {
740
550
  if (!plugin.loaded) {
741
551
  this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded`);
@@ -761,16 +571,10 @@ export class PluginManager {
761
571
  }
762
572
  catch (err) {
763
573
  plugin.error = true;
764
- this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
574
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
765
575
  }
766
576
  return undefined;
767
577
  }
768
- /**
769
- * Configures a plugin.
770
- *
771
- * @param {RegisteredPlugin} plugin - The plugin to configure.
772
- * @returns {Promise<void>} A promise that resolves when the plugin is configured successfully, or rejects with an error if configuration fails.
773
- */
774
578
  async configure(plugin) {
775
579
  if (!plugin.loaded) {
776
580
  this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded`);
@@ -797,22 +601,10 @@ export class PluginManager {
797
601
  }
798
602
  catch (err) {
799
603
  plugin.error = true;
800
- this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
604
+ this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
801
605
  }
802
606
  return undefined;
803
607
  }
804
- /**
805
- * Shuts down a plugin.
806
- *
807
- * This method shuts down a plugin by calling its `onShutdown` method and resetting its state.
808
- * It logs the shutdown process and optionally removes all devices associated with the plugin.
809
- *
810
- * @param {RegisteredPlugin} plugin - The plugin to shut down.
811
- * @param {string} [reason] - The reason for shutting down the plugin.
812
- * @param {boolean} [removeAllDevices=false] - Whether to remove all devices associated with the plugin.
813
- * @param {boolean} [force=false] - Whether to force the shutdown even if the plugin is not loaded or started.
814
- * @returns {Promise<RegisteredPlugin | undefined>} A promise that resolves to the shut down plugin object, or undefined if the shutdown failed.
815
- */
816
608
  async shutdown(plugin, reason, removeAllDevices = false, force = false) {
817
609
  this.log.debug(`Shutting down plugin ${plg}${plugin.name}${db}`);
818
610
  if (!plugin.loaded) {
@@ -851,19 +643,10 @@ export class PluginManager {
851
643
  return plugin;
852
644
  }
853
645
  catch (err) {
854
- this.log.error(`Failed to shut down plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
646
+ this.log.error(`Failed to shut down plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
855
647
  }
856
648
  return undefined;
857
649
  }
858
- /**
859
- * Loads the configuration for a plugin.
860
- * If the configuration file exists, it reads the file and returns the parsed JSON data.
861
- * If the configuration file does not exist, it creates a new file with default configuration and returns it.
862
- * If any error occurs during file access or creation, it logs an error and return un empty config.
863
- *
864
- * @param plugin - The plugin for which to load the configuration.
865
- * @returns A promise that resolves to the loaded or created configuration.
866
- */
867
650
  async loadConfig(plugin) {
868
651
  const { default: path } = await import('node:path');
869
652
  const { promises } = await import('node:fs');
@@ -874,8 +657,6 @@ export class PluginManager {
874
657
  const data = await promises.readFile(configFile, 'utf8');
875
658
  const config = JSON.parse(data);
876
659
  this.log.debug(`Loaded config file ${configFile} for plugin ${plg}${plugin.name}${db}.`);
877
- // this.log.debug(`Loaded config file ${configFile} for plugin ${plg}${plugin.name}${db}.\nConfig:${rs}\n`, config);
878
- // The first time a plugin is added to the system, the config file is created with the plugin name and type "AnyPlatform".
879
660
  config.name = plugin.name;
880
661
  config.type = plugin.type;
881
662
  if (config.debug === undefined)
@@ -899,32 +680,19 @@ export class PluginManager {
899
680
  try {
900
681
  await promises.writeFile(configFile, JSON.stringify(config, null, 2), 'utf8');
901
682
  this.log.debug(`Created config file ${configFile} for plugin ${plg}${plugin.name}${db}.`);
902
- // this.log.debug(`Created config file ${configFile} for plugin ${plg}${plugin.name}${db}.\nConfig:${rs}\n`, config);
903
683
  return config;
904
684
  }
905
685
  catch (err) {
906
- this.log.error(`Error creating config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
686
+ this.log.error(`Error creating config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
907
687
  return config;
908
688
  }
909
689
  }
910
690
  else {
911
- this.log.error(`Error accessing config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
691
+ this.log.error(`Error accessing config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
912
692
  return { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
913
693
  }
914
694
  }
915
695
  }
916
- /**
917
- * Saves the configuration of a plugin to a file.
918
- *
919
- * This method saves the configuration of the specified plugin to a JSON file in the matterbridge directory.
920
- * If the plugin's configuration is not found, it logs an error and rejects the promise. If the configuration
921
- * is successfully saved, it logs a debug message. If an error occurs during the file write operation, it logs
922
- * the error and rejects the promise.
923
- *
924
- * @param {RegisteredPlugin} plugin - The plugin whose configuration is to be saved.
925
- * @returns {Promise<void>} A promise that resolves when the configuration is successfully saved, or rejects if an error occurs.
926
- * @throws {Error} If the plugin's configuration is not found.
927
- */
928
696
  async saveConfigFromPlugin(plugin) {
929
697
  const { default: path } = await import('node:path');
930
698
  const { promises } = await import('node:fs');
@@ -937,27 +705,13 @@ export class PluginManager {
937
705
  await promises.writeFile(configFile, JSON.stringify(plugin.platform.config, null, 2), 'utf8');
938
706
  plugin.configJson = plugin.platform.config;
939
707
  this.log.debug(`Saved config file ${configFile} for plugin ${plg}${plugin.name}${db}`);
940
- // this.log.debug(`Saved config file ${configFile} for plugin ${plg}${plugin.name}${db}.\nConfig:${rs}\n`, plugin.platform.config);
941
708
  return Promise.resolve();
942
709
  }
943
710
  catch (err) {
944
- this.log.error(`Error saving config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err}`);
711
+ this.log.error(`Error saving config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
945
712
  return Promise.reject(err);
946
713
  }
947
714
  }
948
- /**
949
- * Saves the configuration of a plugin from a JSON object to a file.
950
- *
951
- * This method saves the provided configuration of the specified plugin to a JSON file in the matterbridge directory.
952
- * It first checks if the configuration data is valid by ensuring it contains the correct name and type, and matches
953
- * the plugin's name. If the configuration data is invalid, it logs an error and returns. If the configuration is
954
- * successfully saved, it updates the plugin's `configJson` property and logs a debug message. If an error occurs
955
- * during the file write operation, it logs the error and returns.
956
- *
957
- * @param {RegisteredPlugin} plugin - The plugin whose configuration is to be saved.
958
- * @param {PlatformConfig} config - The configuration data to be saved.
959
- * @returns {Promise<void>} A promise that resolves when the configuration is successfully saved, or returns if an error occurs.
960
- */
961
715
  async saveConfigFromJson(plugin, config) {
962
716
  const { default: path } = await import('node:path');
963
717
  const { promises } = await import('node:fs');
@@ -975,23 +729,12 @@ export class PluginManager {
975
729
  plugin.platform.onConfigChanged(config).catch((err) => this.log.error(`Error calling onConfigChanged for plugin ${plg}${plugin.name}${er}: ${err}`));
976
730
  }
977
731
  this.log.debug(`Saved config file ${configFile} for plugin ${plg}${plugin.name}${db}`);
978
- // this.log.debug(`Saved config file ${configFile} for plugin ${plg}${plugin.name}${db}.\nConfig:${rs}\n`, config);
979
732
  }
980
733
  catch (err) {
981
- this.log.error(`Error saving config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err}`);
734
+ this.log.error(`Error saving config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
982
735
  return;
983
736
  }
984
737
  }
985
- /**
986
- * Loads the schema for a plugin.
987
- *
988
- * This method attempts to load the schema file for the specified plugin. If the schema file is found,
989
- * it reads and parses the file, updates the schema's title and description, and logs the process.
990
- * If the schema file is not found, it logs the event and loads a default schema for the plugin.
991
- *
992
- * @param {RegisteredPlugin} plugin - The plugin whose schema is to be loaded.
993
- * @returns {Promise<PlatformSchema>} A promise that resolves to the loaded schema object, or the default schema if the schema file is not found.
994
- */
995
738
  async loadSchema(plugin) {
996
739
  const { promises } = await import('node:fs');
997
740
  const schemaFile = plugin.path.replace('package.json', `${plugin.name}.schema.json`);
@@ -1002,24 +745,13 @@ export class PluginManager {
1002
745
  schema.title = plugin.description;
1003
746
  schema.description = plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author;
1004
747
  this.log.debug(`Loaded schema file ${schemaFile} for plugin ${plg}${plugin.name}${db}.`);
1005
- // this.log.debug(`Loaded schema file ${schemaFile} for plugin ${plg}${plugin.name}${db}.\nSchema:${rs}\n`, schema);
1006
748
  return schema;
1007
749
  }
1008
- catch (error) {
1009
- this.log.debug(`Schema file ${schemaFile} for plugin ${plg}${plugin.name}${db} not found. Loading default schema. Error: ${error instanceof Error ? error.message : error}`);
750
+ catch (err) {
751
+ this.log.debug(`Schema file ${schemaFile} for plugin ${plg}${plugin.name}${db} not found. Loading default schema. Error: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
1010
752
  return this.getDefaultSchema(plugin);
1011
753
  }
1012
754
  }
1013
- /**
1014
- * Returns the default schema for a plugin.
1015
- *
1016
- * This method generates a default schema object for the specified plugin. The schema includes
1017
- * metadata such as the plugin's title, description, version, and author. It also defines the
1018
- * properties of the schema, including the plugin's name, type, debug flag, and unregisterOnShutdown flag.
1019
- *
1020
- * @param {RegisteredPlugin} plugin - The plugin for which the default schema is to be generated.
1021
- * @returns {PlatformSchema} The default schema object for the plugin.
1022
- */
1023
755
  getDefaultSchema(plugin) {
1024
756
  return {
1025
757
  title: plugin.description,
@@ -1050,4 +782,3 @@ export class PluginManager {
1050
782
  };
1051
783
  }
1052
784
  }
1053
- //# sourceMappingURL=pluginManager.js.map