matterbridge 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +33 -14
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/matterbridge.d.ts +14 -6
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +351 -216
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +7 -13
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
- package/dist/matterbridgeAccessoryPlatform.js +9 -18
- package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +10 -0
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +11 -7
- package/dist/matterbridgeDevice.js.map +1 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +7 -13
- package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
- package/dist/matterbridgeDynamicPlatform.js +9 -18
- package/dist/matterbridgeDynamicPlatform.js.map +1 -1
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/manifest.json +2 -2
- package/frontend/build/matterbridge 32x32.png +0 -0
- package/frontend/build/matterbridge 64x64.png +0 -0
- package/frontend/build/static/js/{main.b5a876cf.js → main.b6ca1c6b.js} +3 -3
- package/frontend/build/static/js/main.b6ca1c6b.js.map +1 -0
- package/package.json +4 -3
- package/frontend/build/static/js/main.b5a876cf.js.map +0 -1
- /package/frontend/build/static/js/{main.b5a876cf.js.LICENSE.txt → main.b6ca1c6b.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
* See the License for the specific language governing permissions and
|
|
21
21
|
* limitations under the License. *
|
|
22
22
|
*/
|
|
23
|
+
import { MatterbridgeDevice } from './matterbridgeDevice.js';
|
|
23
24
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
24
|
-
import { AnsiLogger, BRIGHT,
|
|
25
|
+
import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr } from 'node-ansi-logger';
|
|
25
26
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
26
27
|
import { promises as fs } from 'fs';
|
|
27
28
|
import express from 'express';
|
|
@@ -38,6 +39,7 @@ import { requireMinNodeVersion, getParameter, getIntParameter, hasParameter } fr
|
|
|
38
39
|
import { CryptoNode } from '@project-chip/matter-node.js/crypto';
|
|
39
40
|
const plg = '\u001B[38;5;33m';
|
|
40
41
|
const dev = '\u001B[38;5;79m';
|
|
42
|
+
const typ = '\u001B[38;5;207m';
|
|
41
43
|
/**
|
|
42
44
|
* Represents the Matterbridge application.
|
|
43
45
|
*/
|
|
@@ -55,16 +57,18 @@ export class Matterbridge {
|
|
|
55
57
|
freeMemory: '',
|
|
56
58
|
systemUptime: '',
|
|
57
59
|
};
|
|
58
|
-
homeDirectory;
|
|
59
|
-
rootDirectory;
|
|
60
|
-
matterbridgeDirectory;
|
|
60
|
+
homeDirectory = '';
|
|
61
|
+
rootDirectory = '';
|
|
62
|
+
matterbridgeDirectory = '';
|
|
63
|
+
matterbridgeVersion = '';
|
|
61
64
|
bridgeMode = '';
|
|
65
|
+
debugEnabled = false;
|
|
62
66
|
log;
|
|
63
67
|
hasCleanupStarted = false;
|
|
64
68
|
registeredPlugins = [];
|
|
65
69
|
registeredDevices = [];
|
|
66
|
-
nodeStorage
|
|
67
|
-
nodeContext
|
|
70
|
+
nodeStorage;
|
|
71
|
+
nodeContext;
|
|
68
72
|
app;
|
|
69
73
|
storageManager;
|
|
70
74
|
matterbridgeContext;
|
|
@@ -79,17 +83,22 @@ export class Matterbridge {
|
|
|
79
83
|
}
|
|
80
84
|
/**
|
|
81
85
|
* Loads an instance of the Matterbridge class.
|
|
82
|
-
* If an instance already exists,
|
|
86
|
+
* If an instance already exists, return that instance.
|
|
83
87
|
* @returns The loaded instance of the Matterbridge class.
|
|
84
|
-
* @throws Error if an instance of Matterbridge already exists.
|
|
85
88
|
*/
|
|
86
|
-
static async loadInstance() {
|
|
89
|
+
static async loadInstance(cli = false) {
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.error('loadInstance cli:', cli);
|
|
87
92
|
if (!Matterbridge.instance) {
|
|
93
|
+
// eslint-disable-next-line no-console
|
|
94
|
+
console.error('Matterbridge instance does not exists');
|
|
88
95
|
Matterbridge.instance = new Matterbridge();
|
|
89
|
-
|
|
96
|
+
if (cli)
|
|
97
|
+
await Matterbridge.instance.initialize();
|
|
90
98
|
}
|
|
91
99
|
else {
|
|
92
|
-
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.error('Matterbridge instance already exists');
|
|
93
102
|
}
|
|
94
103
|
return Matterbridge.instance;
|
|
95
104
|
}
|
|
@@ -113,6 +122,7 @@ export class Matterbridge {
|
|
|
113
122
|
- bridge: start Matterbridge in bridge mode
|
|
114
123
|
- childbridge: start Matterbridge in childbridge mode
|
|
115
124
|
- frontend [port]: start the frontend on the given port (default 3000)
|
|
125
|
+
- debug: enable debug mode (default false)
|
|
116
126
|
- list: list the registered plugins
|
|
117
127
|
- add [plugin path]: register the plugin
|
|
118
128
|
- remove [plugin path]: remove the plugin
|
|
@@ -121,29 +131,38 @@ export class Matterbridge {
|
|
|
121
131
|
process.exit(0);
|
|
122
132
|
}
|
|
123
133
|
// set Matterbridge logger
|
|
124
|
-
|
|
125
|
-
|
|
134
|
+
if (hasParameter('debug'))
|
|
135
|
+
this.debugEnabled = true;
|
|
136
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled });
|
|
137
|
+
this.log.debug('Matterbridge is starting...');
|
|
126
138
|
// log system info and create .matterbridge directory
|
|
127
139
|
await this.logNodeAndSystemInfo();
|
|
140
|
+
this.log.info(
|
|
141
|
+
// eslint-disable-next-line max-len
|
|
142
|
+
`Matterbridge version ${this.matterbridgeVersion} mode ${hasParameter('bridge') ? 'bridge' : ''}${hasParameter('childbridge') ? 'childbridge' : ''} running on ${this.systemInformation.osType} ${this.systemInformation.osRelease} ${this.systemInformation.osPlatform} ${this.systemInformation.osArch}`);
|
|
128
143
|
// check node version and throw error
|
|
129
144
|
requireMinNodeVersion(18);
|
|
130
145
|
// register SIGINT SIGTERM signal handlers
|
|
131
146
|
this.registerSignalHandlers();
|
|
132
147
|
// set matter.js logger level and format
|
|
133
|
-
Logger.defaultLogLevel = Level.DEBUG;
|
|
148
|
+
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
134
149
|
Logger.format = Format.ANSI;
|
|
135
150
|
// Initialize NodeStorage
|
|
136
151
|
this.log.debug('Creating node storage manager');
|
|
137
|
-
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage') });
|
|
152
|
+
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage'), logging: false });
|
|
138
153
|
this.log.debug('Creating node storage context for matterbridge');
|
|
139
154
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
140
155
|
this.registeredPlugins = await this.nodeContext.get('plugins', []);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
156
|
+
for (const plugin of this.registeredPlugins) {
|
|
157
|
+
this.log.debug(`Creating node storage context for plugin ${plugin.name}`);
|
|
158
|
+
plugin.nodeContext = await this.nodeStorage?.createStorage(plugin.name);
|
|
159
|
+
await plugin.nodeContext?.set('name', plugin.name);
|
|
160
|
+
await plugin.nodeContext?.set('type', plugin.type);
|
|
161
|
+
await plugin.nodeContext?.set('path', plugin.path);
|
|
162
|
+
await plugin.nodeContext?.set('version', plugin.version);
|
|
163
|
+
await plugin.nodeContext?.set('description', plugin.description);
|
|
164
|
+
await plugin.nodeContext?.set('author', plugin.author);
|
|
165
|
+
}
|
|
147
166
|
// Parse command line
|
|
148
167
|
this.parseCommandLine();
|
|
149
168
|
}
|
|
@@ -156,30 +175,28 @@ export class Matterbridge {
|
|
|
156
175
|
if (hasParameter('list')) {
|
|
157
176
|
this.log.info('Registered plugins:');
|
|
158
177
|
this.registeredPlugins.forEach((plugin) => {
|
|
159
|
-
this.log.info(`- ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}"
|
|
160
|
-
` author: "${plugin.author}" type: ${GREEN}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
|
|
161
|
-
// loaded: ${plugin.loaded} started: ${plugin.started} paired: ${plugin.paired} connected: ${plugin.connected}
|
|
178
|
+
this.log.info(`- ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
|
|
162
179
|
});
|
|
163
180
|
process.exit(0);
|
|
164
181
|
}
|
|
165
182
|
if (getParameter('add')) {
|
|
166
183
|
this.log.debug(`Registering plugin ${getParameter('add')}`);
|
|
167
|
-
await this.
|
|
184
|
+
await this.executeCommandLine(getParameter('add'), 'add');
|
|
168
185
|
process.exit(0);
|
|
169
186
|
}
|
|
170
187
|
if (getParameter('remove')) {
|
|
171
188
|
this.log.debug(`Unregistering plugin ${getParameter('remove')}`);
|
|
172
|
-
await this.
|
|
189
|
+
await this.executeCommandLine(getParameter('remove'), 'remove');
|
|
173
190
|
process.exit(0);
|
|
174
191
|
}
|
|
175
192
|
if (getParameter('enable')) {
|
|
176
193
|
this.log.debug(`Enable plugin ${getParameter('enable')}`);
|
|
177
|
-
await this.
|
|
194
|
+
await this.executeCommandLine(getParameter('enable'), 'enable');
|
|
178
195
|
process.exit(0);
|
|
179
196
|
}
|
|
180
197
|
if (getParameter('disable')) {
|
|
181
198
|
this.log.debug(`Disable plugin ${getParameter('disable')}`);
|
|
182
|
-
await this.
|
|
199
|
+
await this.executeCommandLine(getParameter('disable'), 'disable');
|
|
183
200
|
process.exit(0);
|
|
184
201
|
}
|
|
185
202
|
// Start the storage (we need it now for frontend and later for matterbridge)
|
|
@@ -188,128 +205,96 @@ export class Matterbridge {
|
|
|
188
205
|
this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
189
206
|
// Initialize frontend
|
|
190
207
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
191
|
-
if (hasParameter('
|
|
208
|
+
if (hasParameter('test')) {
|
|
192
209
|
this.bridgeMode = 'childbridge';
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return;
|
|
196
|
-
this.log.info(`Loading registered plugin ${plg}${plugin.name}${nf} type ${GREEN}${plugin.type}${nf}`);
|
|
197
|
-
await this.loadPlugin(plugin.path, 'load');
|
|
198
|
-
});
|
|
199
|
-
await this.startMatterBridge();
|
|
210
|
+
MatterbridgeDevice.bridgeMode = 'childbridge';
|
|
211
|
+
this.testStartMatterBridge(); // No await do it asyncronously
|
|
200
212
|
}
|
|
201
213
|
if (hasParameter('bridge')) {
|
|
202
214
|
this.bridgeMode = 'bridge';
|
|
203
|
-
|
|
215
|
+
MatterbridgeDevice.bridgeMode = 'bridge';
|
|
216
|
+
for (const plugin of this.registeredPlugins) {
|
|
204
217
|
if (!plugin.enabled)
|
|
205
|
-
|
|
206
|
-
this.
|
|
207
|
-
|
|
208
|
-
|
|
218
|
+
continue;
|
|
219
|
+
this.loadPlugin(plugin); // No await do it asyncronously
|
|
220
|
+
}
|
|
221
|
+
await this.startMatterBridge();
|
|
222
|
+
}
|
|
223
|
+
if (hasParameter('childbridge')) {
|
|
224
|
+
this.bridgeMode = 'childbridge';
|
|
225
|
+
MatterbridgeDevice.bridgeMode = 'childbridge';
|
|
226
|
+
for (const plugin of this.registeredPlugins) {
|
|
227
|
+
if (!plugin.enabled)
|
|
228
|
+
continue;
|
|
229
|
+
this.loadPlugin(plugin); // No await do it asyncronously
|
|
230
|
+
}
|
|
209
231
|
await this.startMatterBridge();
|
|
210
232
|
}
|
|
211
233
|
}
|
|
212
234
|
/**
|
|
213
235
|
* Loads a plugin from the specified package.json file path.
|
|
214
236
|
* @param packageJsonPath - The path to the package.json file of the plugin.
|
|
215
|
-
* @param mode - The mode of operation. Possible values are '
|
|
237
|
+
* @param mode - The mode of operation. Possible values are 'add', 'remove', 'enable', 'disable'.
|
|
216
238
|
* @returns A Promise that resolves when the plugin is loaded successfully, or rejects with an error if loading fails.
|
|
217
239
|
*/
|
|
218
|
-
async
|
|
240
|
+
async executeCommandLine(packageJsonPath, mode) {
|
|
219
241
|
if (!packageJsonPath.endsWith('package.json'))
|
|
220
242
|
packageJsonPath = path.join(packageJsonPath, 'package.json');
|
|
243
|
+
// Resolve the package.json of the plugin
|
|
221
244
|
packageJsonPath = path.resolve(packageJsonPath);
|
|
222
245
|
this.log.debug(`Loading plugin from ${plg}${packageJsonPath}${db}`);
|
|
223
246
|
try {
|
|
224
247
|
// Load the package.json of the plugin
|
|
225
248
|
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const pluginUrl = pathToFileURL(pluginPath);
|
|
234
|
-
// Dynamically import the plugin
|
|
235
|
-
this.log.debug(`Importing plugin ${plg}${plugin?.name}${db} from ${pluginUrl.href}`);
|
|
236
|
-
const pluginInstance = await import(pluginUrl.href);
|
|
237
|
-
this.log.debug(`Imported plugin ${plg}${plugin?.name}${db} from ${pluginUrl.href}`);
|
|
238
|
-
// Call the default export function of the plugin, passing this MatterBridge instance
|
|
239
|
-
if (pluginInstance.default) {
|
|
240
|
-
const platform = pluginInstance.default(this, new AnsiLogger({ logName: packageJson.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */ }));
|
|
241
|
-
platform.name = packageJson.name;
|
|
242
|
-
if (mode === 'load') {
|
|
243
|
-
this.log.info(`Plugin ${plg}${plugin?.name}${nf} type ${GREEN}${platform.type}${nf} loaded (entrypoint ${UNDERLINE}${pluginPath}${UNDERLINEOFF})`);
|
|
244
|
-
// Update plugin info
|
|
245
|
-
if (plugin) {
|
|
246
|
-
plugin.path = packageJsonPath;
|
|
247
|
-
plugin.name = packageJson.name;
|
|
248
|
-
plugin.description = packageJson.description;
|
|
249
|
-
plugin.version = packageJson.version;
|
|
250
|
-
plugin.author = packageJson.author;
|
|
251
|
-
plugin.type = platform.type;
|
|
252
|
-
plugin.loaded = true;
|
|
253
|
-
plugin.platform = platform;
|
|
249
|
+
if (mode === 'add') {
|
|
250
|
+
if (!this.registeredPlugins.find((plugin) => plugin.name === packageJson.name)) {
|
|
251
|
+
const plugin = { path: packageJsonPath, type: '', name: packageJson.name, version: packageJson.version, description: packageJson.description, author: packageJson.author, enabled: true };
|
|
252
|
+
if (await this.loadPlugin(plugin)) {
|
|
253
|
+
this.registeredPlugins.push(plugin);
|
|
254
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
255
|
+
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge`);
|
|
254
256
|
}
|
|
255
257
|
else {
|
|
256
|
-
this.log.error(`Plugin ${plg}${
|
|
258
|
+
this.log.error(`Plugin ${plg}${packageJsonPath}${wr} not added to matterbridge error`);
|
|
257
259
|
}
|
|
258
260
|
}
|
|
259
|
-
else
|
|
260
|
-
|
|
261
|
-
this.registeredPlugins.push({
|
|
262
|
-
path: packageJsonPath,
|
|
263
|
-
type: platform.type,
|
|
264
|
-
name: packageJson.name,
|
|
265
|
-
version: packageJson.version,
|
|
266
|
-
description: packageJson.description,
|
|
267
|
-
author: packageJson.author,
|
|
268
|
-
enabled: true,
|
|
269
|
-
});
|
|
270
|
-
await this.nodeContext?.set('plugins', this.registeredPlugins);
|
|
271
|
-
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${platform.type} added to matterbridge`);
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} already added to matterbridge`);
|
|
275
|
-
}
|
|
261
|
+
else {
|
|
262
|
+
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} already added to matterbridge`);
|
|
276
263
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
285
|
-
}
|
|
264
|
+
}
|
|
265
|
+
else if (mode === 'remove') {
|
|
266
|
+
if (this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name)) {
|
|
267
|
+
this.registeredPlugins.splice(this.registeredPlugins.findIndex((registeredPlugin) => registeredPlugin.name === packageJson.name), 1);
|
|
268
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
269
|
+
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} removed from matterbridge`);
|
|
286
270
|
}
|
|
287
|
-
else
|
|
288
|
-
|
|
289
|
-
if (plugin) {
|
|
290
|
-
plugin.enabled = true;
|
|
291
|
-
await this.nodeContext?.set('plugins', this.registeredPlugins);
|
|
292
|
-
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} enabled`);
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
296
|
-
}
|
|
271
|
+
else {
|
|
272
|
+
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
297
273
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
274
|
+
}
|
|
275
|
+
else if (mode === 'enable') {
|
|
276
|
+
const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
|
|
277
|
+
if (plugin) {
|
|
278
|
+
plugin.enabled = true;
|
|
279
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
280
|
+
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} enabled`);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
else if (mode === 'disable') {
|
|
287
|
+
const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
|
|
288
|
+
if (plugin) {
|
|
289
|
+
plugin.enabled = false;
|
|
290
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
291
|
+
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} disabled`);
|
|
308
292
|
}
|
|
309
293
|
else {
|
|
310
|
-
this.log.
|
|
294
|
+
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
311
295
|
}
|
|
312
296
|
}
|
|
297
|
+
//}
|
|
313
298
|
}
|
|
314
299
|
catch (err) {
|
|
315
300
|
this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
|
|
@@ -334,13 +319,12 @@ export class Matterbridge {
|
|
|
334
319
|
async cleanup(message) {
|
|
335
320
|
if (!this.hasCleanupStarted) {
|
|
336
321
|
this.hasCleanupStarted = true;
|
|
337
|
-
this.log.
|
|
322
|
+
this.log.info(message);
|
|
338
323
|
// Callint the shutdown functions with a reason
|
|
339
|
-
this.registeredPlugins
|
|
340
|
-
if (plugin.platform)
|
|
341
|
-
plugin.platform.onShutdown('Matterbridge is closing: ' + message);
|
|
342
|
-
|
|
343
|
-
});
|
|
324
|
+
for (const plugin of this.registeredPlugins) {
|
|
325
|
+
if (plugin.platform)
|
|
326
|
+
await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
|
|
327
|
+
}
|
|
344
328
|
// Set reachability to false
|
|
345
329
|
/*
|
|
346
330
|
this.log.debug(`*Changing reachability to false for ${this.registeredDevices.length} devices (${this.bridgeMode} mode):`);
|
|
@@ -363,10 +347,15 @@ export class Matterbridge {
|
|
|
363
347
|
await this.stopMatter();
|
|
364
348
|
// Closing storage
|
|
365
349
|
await this.stopStorage();
|
|
366
|
-
|
|
367
|
-
this.
|
|
350
|
+
const serializedRegisteredDevices = [];
|
|
351
|
+
this.registeredDevices.forEach((registeredDevice) => {
|
|
352
|
+
serializedRegisteredDevices.push(registeredDevice.device.serialize(registeredDevice.plugin));
|
|
353
|
+
});
|
|
354
|
+
//console.log('serializedRegisteredDevices:', serializedRegisteredDevices);
|
|
355
|
+
await this.nodeContext?.set('devices', serializedRegisteredDevices);
|
|
356
|
+
this.log.info('Cleanup completed.');
|
|
368
357
|
process.exit(0);
|
|
369
|
-
},
|
|
358
|
+
}, 3 * 1000);
|
|
370
359
|
}
|
|
371
360
|
}
|
|
372
361
|
/**
|
|
@@ -387,6 +376,7 @@ export class Matterbridge {
|
|
|
387
376
|
* Adds a device to the Matterbridge.
|
|
388
377
|
* @param pluginName - The name of the plugin.
|
|
389
378
|
* @param device - The device to be added.
|
|
379
|
+
* @returns A Promise that resolves when the device is added successfully.
|
|
390
380
|
*/
|
|
391
381
|
async addDevice(pluginName, device) {
|
|
392
382
|
this.log.info(`Adding device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
@@ -398,20 +388,19 @@ export class Matterbridge {
|
|
|
398
388
|
}
|
|
399
389
|
// Add and register the device to the matterbridge in bridge mode
|
|
400
390
|
if (this.bridgeMode === 'bridge') {
|
|
401
|
-
const basic = device.getClusterServerById(BasicInformationCluster.id);
|
|
402
|
-
if (!basic) {
|
|
403
|
-
this.log.error(`addDevice error: cannot find the BasicInformationCluster device ${dev}${device.name}${nf} plugin ${plg}${pluginName}${nf}`);
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
device.createDefaultBridgedDeviceBasicInformationClusterServer(basic.getNodeLabelAttribute(), basic.getSerialNumberAttribute(), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductNameAttribute(), basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute(), basic.getHardwareVersionAttribute(), basic.getHardwareVersionStringAttribute());
|
|
407
|
-
//console.log(basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute());
|
|
408
391
|
this.matterAggregator.addBridgedDevice(device);
|
|
409
392
|
this.registeredDevices.push({ plugin: pluginName, device, added: true });
|
|
410
|
-
|
|
393
|
+
if (plugin.registeredDevices !== undefined)
|
|
394
|
+
plugin.registeredDevices++;
|
|
395
|
+
if (plugin.addedDevices !== undefined)
|
|
396
|
+
plugin.addedDevices++;
|
|
397
|
+
this.log.info(`Added and registered device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
411
398
|
}
|
|
412
399
|
// Only register the device in childbridge mode
|
|
413
400
|
if (this.bridgeMode === 'childbridge') {
|
|
414
401
|
this.registeredDevices.push({ plugin: pluginName, device, added: false });
|
|
402
|
+
if (plugin.registeredDevices !== undefined)
|
|
403
|
+
plugin.registeredDevices++;
|
|
415
404
|
this.log.info(`Registered device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
416
405
|
}
|
|
417
406
|
}
|
|
@@ -419,6 +408,7 @@ export class Matterbridge {
|
|
|
419
408
|
* Adds a bridged device to the Matterbridge.
|
|
420
409
|
* @param pluginName - The name of the plugin.
|
|
421
410
|
* @param device - The bridged device to add.
|
|
411
|
+
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
422
412
|
*/
|
|
423
413
|
async addBridgedDevice(pluginName, device) {
|
|
424
414
|
this.log.info(`Adding bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
@@ -432,14 +422,18 @@ export class Matterbridge {
|
|
|
432
422
|
if (this.bridgeMode === 'bridge') {
|
|
433
423
|
this.matterAggregator.addBridgedDevice(device);
|
|
434
424
|
this.registeredDevices.push({ plugin: pluginName, device, added: true });
|
|
435
|
-
|
|
425
|
+
if (plugin.registeredDevices !== undefined)
|
|
426
|
+
plugin.registeredDevices++;
|
|
427
|
+
if (plugin.addedDevices !== undefined)
|
|
428
|
+
plugin.addedDevices++;
|
|
429
|
+
this.log.info(`Added and registered bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
436
430
|
}
|
|
437
431
|
// Only register the device in childbridge mode
|
|
438
432
|
if (this.bridgeMode === 'childbridge') {
|
|
439
433
|
this.registeredDevices.push({ plugin: pluginName, device, added: false });
|
|
434
|
+
if (plugin.registeredDevices !== undefined)
|
|
435
|
+
plugin.registeredDevices++;
|
|
440
436
|
this.log.info(`Registered bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
441
|
-
//const basic = device.getClusterServerById(BridgedDeviceBasicInformationCluster.id);
|
|
442
|
-
//console.log(JSON.stringify(basic, null, 2));
|
|
443
437
|
}
|
|
444
438
|
}
|
|
445
439
|
/**
|
|
@@ -508,6 +502,144 @@ export class Matterbridge {
|
|
|
508
502
|
await this.storageManager.close();
|
|
509
503
|
this.log.debug('Storage closed');
|
|
510
504
|
}
|
|
505
|
+
async testStartMatterBridge() {
|
|
506
|
+
for (const plugin of this.registeredPlugins) {
|
|
507
|
+
if (!plugin.enabled)
|
|
508
|
+
continue;
|
|
509
|
+
// No await do it asyncronously
|
|
510
|
+
this.loadPlugin(plugin)
|
|
511
|
+
.then(() => {
|
|
512
|
+
// No await do it asyncronously
|
|
513
|
+
this.startPlugin(plugin)
|
|
514
|
+
.then(() => { })
|
|
515
|
+
.catch((err) => {
|
|
516
|
+
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
517
|
+
});
|
|
518
|
+
})
|
|
519
|
+
.catch((err) => {
|
|
520
|
+
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
for (const plugin of this.registeredPlugins) {
|
|
524
|
+
if (!plugin.enabled)
|
|
525
|
+
continue;
|
|
526
|
+
// Start the interval to check if the plugin is loaded and started
|
|
527
|
+
let times = 0;
|
|
528
|
+
const interval = setInterval(() => {
|
|
529
|
+
times++;
|
|
530
|
+
this.log.info(`Waiting ${times} secs for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
|
|
531
|
+
if (!plugin.loaded || !plugin.started)
|
|
532
|
+
return;
|
|
533
|
+
this.log.info(`Plugin ${plg}${plugin.name}${db} sent ${plugin.registeredDevices} devices`);
|
|
534
|
+
clearInterval(interval);
|
|
535
|
+
}, 1000);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
async startPlugin(plugin, message, configure = false) {
|
|
539
|
+
if (!plugin.loaded || !plugin.platform) {
|
|
540
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`);
|
|
541
|
+
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`));
|
|
542
|
+
}
|
|
543
|
+
if (plugin.started) {
|
|
544
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} already started`);
|
|
545
|
+
return Promise.resolve();
|
|
546
|
+
}
|
|
547
|
+
this.log.info(`Starting plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
548
|
+
try {
|
|
549
|
+
plugin.platform
|
|
550
|
+
.onStart(message)
|
|
551
|
+
.then(() => {
|
|
552
|
+
plugin.started = true;
|
|
553
|
+
this.log.info(`Started plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
554
|
+
if (configure)
|
|
555
|
+
this.configurePlugin(plugin); // No await do it asyncronously
|
|
556
|
+
return Promise.resolve();
|
|
557
|
+
})
|
|
558
|
+
.catch((err) => {
|
|
559
|
+
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
560
|
+
return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
catch (err) {
|
|
564
|
+
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
565
|
+
return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async configurePlugin(plugin) {
|
|
569
|
+
if (!plugin.loaded || !plugin.started || !plugin.platform) {
|
|
570
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded or not started or not platform`);
|
|
571
|
+
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded or not started or not platform`));
|
|
572
|
+
}
|
|
573
|
+
if (plugin.configured) {
|
|
574
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} already configured`);
|
|
575
|
+
return Promise.resolve();
|
|
576
|
+
}
|
|
577
|
+
this.log.info(`Configuring plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
578
|
+
try {
|
|
579
|
+
plugin.platform
|
|
580
|
+
.onConfigure()
|
|
581
|
+
.then(() => {
|
|
582
|
+
plugin.configured = true;
|
|
583
|
+
this.log.info(`Configured plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
584
|
+
return Promise.resolve();
|
|
585
|
+
})
|
|
586
|
+
.catch((err) => {
|
|
587
|
+
this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
588
|
+
return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
catch (err) {
|
|
592
|
+
this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
593
|
+
return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
async loadPlugin(plugin) {
|
|
597
|
+
if (!plugin.enabled) {
|
|
598
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} not enabled`);
|
|
599
|
+
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not enabled`));
|
|
600
|
+
}
|
|
601
|
+
if (plugin.platform) {
|
|
602
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} already loaded`);
|
|
603
|
+
return Promise.resolve(plugin.platform);
|
|
604
|
+
}
|
|
605
|
+
this.log.info(`Loading plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
606
|
+
try {
|
|
607
|
+
// Load the package.json of the plugin
|
|
608
|
+
const packageJson = JSON.parse(await fs.readFile(plugin.path, 'utf8'));
|
|
609
|
+
// Resolve the main module path relative to package.json
|
|
610
|
+
const pluginEntry = path.resolve(path.dirname(plugin.path), packageJson.main);
|
|
611
|
+
// Dynamically import the plugin
|
|
612
|
+
const pluginUrl = pathToFileURL(pluginEntry);
|
|
613
|
+
this.log.debug(`Importing plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
|
|
614
|
+
const pluginInstance = await import(pluginUrl.href);
|
|
615
|
+
this.log.debug(`Imported plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
|
|
616
|
+
// Call the default export function of the plugin, passing this MatterBridge instance
|
|
617
|
+
if (pluginInstance.default) {
|
|
618
|
+
const platform = pluginInstance.default(this, new AnsiLogger({ logName: plugin.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled }));
|
|
619
|
+
platform.name = packageJson.name;
|
|
620
|
+
plugin.name = packageJson.name;
|
|
621
|
+
plugin.description = packageJson.description;
|
|
622
|
+
plugin.version = packageJson.version;
|
|
623
|
+
plugin.author = packageJson.author;
|
|
624
|
+
plugin.type = platform.type;
|
|
625
|
+
plugin.platform = platform;
|
|
626
|
+
plugin.loaded = true;
|
|
627
|
+
plugin.registeredDevices = 0;
|
|
628
|
+
plugin.addedDevices = 0;
|
|
629
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
630
|
+
this.log.info(`Loaded plugin ${plg}${plugin.name}${db} type ${typ}${platform.type}${db} (entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
|
|
631
|
+
return Promise.resolve(platform);
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
|
|
635
|
+
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
catch (err) {
|
|
639
|
+
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
640
|
+
return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
641
|
+
}
|
|
642
|
+
}
|
|
511
643
|
/**
|
|
512
644
|
* Starts the Matterbridge based on the bridge mode.
|
|
513
645
|
* If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
|
|
@@ -522,7 +654,7 @@ export class Matterbridge {
|
|
|
522
654
|
this.createMatterServer(this.storageManager);
|
|
523
655
|
if (this.bridgeMode === 'bridge') {
|
|
524
656
|
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
525
|
-
// Plugins are started by callback when Matterbridge is commissioned and plugin.started is set to true
|
|
657
|
+
// Plugins are started and configured by callback when Matterbridge is commissioned and plugin.started is set to true
|
|
526
658
|
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
527
659
|
this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
528
660
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
@@ -535,10 +667,6 @@ export class Matterbridge {
|
|
|
535
667
|
await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
536
668
|
this.log.debug('Starting matter server');
|
|
537
669
|
await this.startMatterServer();
|
|
538
|
-
this.registeredPlugins.forEach(async (plugin) => {
|
|
539
|
-
if (plugin.platform)
|
|
540
|
-
await plugin.platform.onMatterStarted();
|
|
541
|
-
});
|
|
542
670
|
this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, 'Matterbridge');
|
|
543
671
|
}
|
|
544
672
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -549,26 +677,13 @@ export class Matterbridge {
|
|
|
549
677
|
this.registeredPlugins.forEach(async (plugin) => {
|
|
550
678
|
if (!plugin.enabled)
|
|
551
679
|
return;
|
|
552
|
-
// Start the interval to check if the plugin is loaded
|
|
553
|
-
const loadedInterval = setInterval(async () => {
|
|
554
|
-
this.log.debug(`Waiting in load interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
|
|
555
|
-
if (!plugin.loaded)
|
|
556
|
-
return;
|
|
557
|
-
plugin.started = true;
|
|
558
|
-
plugin.registeredDevices = 0;
|
|
559
|
-
clearInterval(loadedInterval);
|
|
560
|
-
}, 1000);
|
|
561
680
|
// Start the interval to check if the plugins is started
|
|
562
|
-
const
|
|
563
|
-
this.log.debug(`Waiting in
|
|
564
|
-
if (!plugin.
|
|
681
|
+
const startInterval = setInterval(async () => {
|
|
682
|
+
this.log.debug(`Waiting in start interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
|
|
683
|
+
if (!plugin.loaded)
|
|
565
684
|
return;
|
|
566
685
|
if (plugin.type === 'AccessoryPlatform') {
|
|
567
|
-
this.
|
|
568
|
-
if (plugin.platform)
|
|
569
|
-
await plugin.platform.onStart('Matterbridge Accessory platform has started commissioning');
|
|
570
|
-
else
|
|
571
|
-
this.log.error(`Platform not found for plugin ${plg}${plugin.name}${er}`);
|
|
686
|
+
await this.startPlugin(plugin, 'Matterbridge Accessory platform has started');
|
|
572
687
|
this.registeredDevices.forEach(async (registeredDevice) => {
|
|
573
688
|
if (registeredDevice.plugin === plugin.name) {
|
|
574
689
|
plugin.storageContext = this.importCommissioningServerContext(plugin.name, registeredDevice.device); // Generate serialNumber and uniqueId
|
|
@@ -590,17 +705,13 @@ export class Matterbridge {
|
|
|
590
705
|
plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
591
706
|
this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
592
707
|
plugin.aggregator = this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
593
|
-
this.
|
|
594
|
-
if (plugin.platform)
|
|
595
|
-
await plugin.platform.onStart('Matterbridge Dynamic platform has started commissioning');
|
|
596
|
-
else
|
|
597
|
-
this.log.error(`Platform not found for plugin ${plg}${plugin.name}${er}`);
|
|
708
|
+
await this.startPlugin(plugin, 'Matterbridge Dynamic platform has started');
|
|
598
709
|
this.log.debug(`Adding matter aggregator to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
599
710
|
plugin.commissioningServer.addDevice(plugin.aggregator);
|
|
600
711
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
601
712
|
await this.matterServer.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
602
713
|
}
|
|
603
|
-
clearInterval(
|
|
714
|
+
clearInterval(startInterval);
|
|
604
715
|
}, 1000);
|
|
605
716
|
});
|
|
606
717
|
// Start the interval to check if all plugins are loaded and started and so start the matter server
|
|
@@ -630,20 +741,18 @@ export class Matterbridge {
|
|
|
630
741
|
}
|
|
631
742
|
});
|
|
632
743
|
});
|
|
633
|
-
this.log.debug('Starting matter server');
|
|
634
744
|
await this.startMatterServer();
|
|
635
|
-
this.registeredPlugins
|
|
636
|
-
if (plugin.platform)
|
|
637
|
-
await plugin.platform.onMatterStarted();
|
|
745
|
+
for (const plugin of this.registeredPlugins) {
|
|
638
746
|
this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.name);
|
|
639
|
-
}
|
|
640
|
-
Logger.defaultLogLevel = Level.DEBUG;
|
|
747
|
+
}
|
|
748
|
+
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
641
749
|
clearInterval(startMatterInterval);
|
|
642
750
|
}, 1000);
|
|
643
751
|
return;
|
|
644
752
|
}
|
|
645
753
|
}
|
|
646
754
|
async startMatterServer() {
|
|
755
|
+
this.log.debug('Starting matter server');
|
|
647
756
|
await this.matterServer.start();
|
|
648
757
|
this.log.debug('Started matter server');
|
|
649
758
|
}
|
|
@@ -789,14 +898,18 @@ export class Matterbridge {
|
|
|
789
898
|
},
|
|
790
899
|
activeSessionsChangedCallback: (fabricIndex) => {
|
|
791
900
|
const info = commissioningServer.getActiveSessionInformation(fabricIndex);
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
this.log.
|
|
901
|
+
let connected = false;
|
|
902
|
+
info.forEach((session) => {
|
|
903
|
+
this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${name}${nf}`, debugStringify(session));
|
|
904
|
+
if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
|
|
905
|
+
this.log.info(`***Controller ${session.fabric?.rootVendorId}/${session.fabric?.label} connected to ${plg}${name}${nf}`);
|
|
906
|
+
connected = true;
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
if (connected) {
|
|
795
910
|
if (this.bridgeMode === 'childbridge') {
|
|
796
911
|
const plugin = this.findPlugin(name);
|
|
797
912
|
if (plugin) {
|
|
798
|
-
if (plugin.connected === true)
|
|
799
|
-
return; // Only once cause the devices are already added to the plugins aggregator
|
|
800
913
|
plugin.paired = true;
|
|
801
914
|
plugin.connected = true;
|
|
802
915
|
}
|
|
@@ -804,45 +917,40 @@ export class Matterbridge {
|
|
|
804
917
|
setTimeout(() => {
|
|
805
918
|
if (this.bridgeMode === 'bridge') {
|
|
806
919
|
//Logger.defaultLogLevel = Level.INFO;
|
|
807
|
-
this.registeredPlugins
|
|
920
|
+
for (const plugin of this.registeredPlugins) {
|
|
808
921
|
if (!plugin.enabled)
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
this.log.info(`***Started plugin ${plg}${plugin.name}${nf}`);
|
|
815
|
-
}
|
|
816
|
-
else {
|
|
817
|
-
this.log.error(`***Platform not found for plugin ${plg}${plugin.name}${er}`);
|
|
818
|
-
}
|
|
819
|
-
});
|
|
820
|
-
Logger.defaultLogLevel = Level.DEBUG;
|
|
922
|
+
continue;
|
|
923
|
+
this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously
|
|
924
|
+
//this.configurePlugin(plugin); // No await do it asyncronously
|
|
925
|
+
}
|
|
926
|
+
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
821
927
|
}
|
|
822
928
|
if (this.bridgeMode === 'childbridge') {
|
|
823
929
|
//Logger.defaultLogLevel = Level.INFO;
|
|
824
930
|
const plugin = this.findPlugin(name);
|
|
825
|
-
if (
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
931
|
+
if (plugin && plugin.type === 'DynamicPlatform') {
|
|
932
|
+
for (const registeredDevice of this.registeredDevices) {
|
|
933
|
+
if (registeredDevice.plugin === name) {
|
|
934
|
+
this.log.info(`Adding bridged device ${dev}${registeredDevice.device.name}${nf} to aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
935
|
+
if (!plugin.aggregator) {
|
|
936
|
+
this.log.error(`****Aggregator not found for plugin ${plg}${plugin.name}${er}`);
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
plugin.aggregator.addBridgedDevice(registeredDevice.device);
|
|
940
|
+
if (plugin.addedDevices !== undefined)
|
|
941
|
+
plugin.addedDevices++;
|
|
942
|
+
this.log.info(`Added bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${registeredDevice.device.name}${nf} for plugin ${plg}${plugin.name}${nf}`);
|
|
943
|
+
registeredDevice.added = true;
|
|
944
|
+
}
|
|
834
945
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
946
|
+
}
|
|
947
|
+
for (const plugin of this.registeredPlugins) {
|
|
948
|
+
if (plugin.name === name && plugin.platform) {
|
|
949
|
+
this.configurePlugin(plugin); // No await do it asyncronously
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
841
953
|
}
|
|
842
|
-
this.registeredPlugins.forEach(async (plugin) => {
|
|
843
|
-
if (plugin.name === name && plugin.platform)
|
|
844
|
-
plugin.platform.onConfigure();
|
|
845
|
-
});
|
|
846
954
|
//logEndpoint(commissioningServer.getRootEndpoint());
|
|
847
955
|
}, 2000);
|
|
848
956
|
}
|
|
@@ -982,6 +1090,10 @@ export class Matterbridge {
|
|
|
982
1090
|
await fs.mkdir(this.matterbridgeDirectory);
|
|
983
1091
|
}
|
|
984
1092
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1093
|
+
// Matterbridge version
|
|
1094
|
+
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1095
|
+
this.matterbridgeVersion = packageJson.version;
|
|
1096
|
+
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
985
1097
|
// Current working directory
|
|
986
1098
|
const currentDir = process.cwd();
|
|
987
1099
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
@@ -989,7 +1101,24 @@ export class Matterbridge {
|
|
|
989
1101
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
990
1102
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
991
1103
|
}
|
|
992
|
-
|
|
1104
|
+
getBaseRegisteredPlugins() {
|
|
1105
|
+
const baseRegisteredPlugins = this.registeredPlugins.map((plugin) => ({
|
|
1106
|
+
path: plugin.path,
|
|
1107
|
+
type: plugin.type,
|
|
1108
|
+
name: plugin.name,
|
|
1109
|
+
version: plugin.version,
|
|
1110
|
+
description: plugin.description,
|
|
1111
|
+
author: plugin.author,
|
|
1112
|
+
enabled: plugin.enabled,
|
|
1113
|
+
loaded: plugin.loaded,
|
|
1114
|
+
started: plugin.started,
|
|
1115
|
+
paired: plugin.paired,
|
|
1116
|
+
connected: plugin.connected,
|
|
1117
|
+
registeredDevices: plugin.registeredDevices,
|
|
1118
|
+
}));
|
|
1119
|
+
return baseRegisteredPlugins;
|
|
1120
|
+
}
|
|
1121
|
+
getBaseRegisteredDevices() {
|
|
993
1122
|
const baseRegisteredPlugins = this.registeredPlugins.map((plugin) => ({
|
|
994
1123
|
path: plugin.path,
|
|
995
1124
|
type: plugin.type,
|
|
@@ -1039,8 +1168,7 @@ export class Matterbridge {
|
|
|
1039
1168
|
// Endpoint to provide plugins
|
|
1040
1169
|
this.app.get('/api/plugins', (req, res) => {
|
|
1041
1170
|
this.log.debug('The frontend sent /api/plugins');
|
|
1042
|
-
|
|
1043
|
-
res.json(baseRegisteredPlugins);
|
|
1171
|
+
res.json(this.getBaseRegisteredPlugins());
|
|
1044
1172
|
});
|
|
1045
1173
|
// Endpoint to provide devices
|
|
1046
1174
|
this.app.get('/api/devices', (req, res) => {
|
|
@@ -1072,15 +1200,15 @@ export class Matterbridge {
|
|
|
1072
1200
|
// Endpoint to provide the cluster servers of the devices
|
|
1073
1201
|
this.app.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
1074
1202
|
const selectedPluginName = req.params.selectedPluginName;
|
|
1075
|
-
const selectedDeviceEndpoint = req.params.selectedDeviceEndpoint;
|
|
1203
|
+
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
1076
1204
|
this.log.debug(`The frontend sent /api/devices_clusters plugin:${selectedPluginName} endpoint:${selectedDeviceEndpoint}`);
|
|
1077
|
-
if (selectedPluginName === 'none'
|
|
1205
|
+
if (selectedPluginName === 'none') {
|
|
1078
1206
|
res.json([]);
|
|
1079
1207
|
return;
|
|
1080
1208
|
}
|
|
1081
1209
|
const data = [];
|
|
1082
1210
|
this.registeredDevices.forEach((registeredDevice) => {
|
|
1083
|
-
if (registeredDevice.plugin === selectedPluginName) {
|
|
1211
|
+
if (registeredDevice.plugin === selectedPluginName && registeredDevice.device.id === selectedDeviceEndpoint) {
|
|
1084
1212
|
const clusterServers = registeredDevice.device.getAllClusterServers();
|
|
1085
1213
|
clusterServers.forEach((clusterServer) => {
|
|
1086
1214
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
@@ -1184,6 +1312,13 @@ ws.onmessage = (event) => {
|
|
|
1184
1312
|
console.log(`Received message => ${event.data}`);
|
|
1185
1313
|
};
|
|
1186
1314
|
|
|
1315
|
+
*/
|
|
1316
|
+
/*
|
|
1317
|
+
// In Matterbridge
|
|
1318
|
+
global.matterbridgeInstance = Matterbridge.loadInstance();
|
|
1319
|
+
|
|
1320
|
+
// In plugins
|
|
1321
|
+
const matterbridge = global.matterbridgeInstance;
|
|
1187
1322
|
*/
|
|
1188
1323
|
/*
|
|
1189
1324
|
npx create-react-app matterbridge-frontend
|