matterbridge 1.3.7 → 1.3.9
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 +19 -0
- package/dist/cluster/ElectricalPowerMeasurementCluster.js +4 -4
- package/dist/cluster/ElectricalPowerMeasurementCluster.js.map +1 -1
- package/dist/matterbridge.d.ts +2 -5
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +17 -27
- package/dist/matterbridge.js.map +1 -1
- package/package.json +3 -3
- package/dist/matterbridgeDeviceV8.d.ts +0 -42
- package/dist/matterbridgeDeviceV8.d.ts.map +0 -1
- package/dist/matterbridgeDeviceV8.js +0 -123
- package/dist/matterbridgeDeviceV8.js.map +0 -1
- package/dist/matterbridgeV8.d.ts +0 -141
- package/dist/matterbridgeV8.d.ts.map +0 -1
- package/dist/matterbridgeV8.js +0 -641
- package/dist/matterbridgeV8.js.map +0 -1
package/dist/matterbridgeV8.js
DELETED
|
@@ -1,641 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3
|
-
/**
|
|
4
|
-
* This file contains the class NewMatterbridge. Test of new matter.js api
|
|
5
|
-
*
|
|
6
|
-
* @file matterbridgeNewApi.ts
|
|
7
|
-
* @author Luca Liguori
|
|
8
|
-
* @date 2024-06-01
|
|
9
|
-
* @version 1.0.0
|
|
10
|
-
*
|
|
11
|
-
* Copyright 2024 Luca Liguori.
|
|
12
|
-
*
|
|
13
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
-
* you may not use this file except in compliance with the License.
|
|
15
|
-
* You may obtain a copy of the License at
|
|
16
|
-
*
|
|
17
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
-
*
|
|
19
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
20
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
-
* See the License for the specific language governing permissions and
|
|
23
|
-
* limitations under the License. *
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* Import needed modules from @project-chip/matter-node.js
|
|
27
|
-
*/
|
|
28
|
-
// Include this first to auto-register Crypto, Network and Time Node.js implementations
|
|
29
|
-
import '@project-chip/matter-node.js';
|
|
30
|
-
import { CryptoNode } from '@project-chip/matter-node.js/crypto';
|
|
31
|
-
import { DeviceTypeId, FabricIndex, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
32
|
-
import { Format, Level, Logger, createFileLogger } from '@project-chip/matter-node.js/log';
|
|
33
|
-
import { Environment, StorageService } from '@project-chip/matter.js/environment';
|
|
34
|
-
import { ServerNode } from '@project-chip/matter.js/node';
|
|
35
|
-
import { DeviceTypes } from '@project-chip/matter-node.js/device';
|
|
36
|
-
import { QrCode } from '@project-chip/matter-node.js/schema';
|
|
37
|
-
import { FabricAction } from '@project-chip/matter-node.js/fabric';
|
|
38
|
-
import { Endpoint } from '@project-chip/matter.js/endpoint';
|
|
39
|
-
import { AggregatorEndpoint } from '@project-chip/matter.js/endpoints/AggregatorEndpoint';
|
|
40
|
-
import { BridgedDeviceBasicInformationServer } from '@project-chip/matter.js/behavior/definitions/bridged-device-basic-information';
|
|
41
|
-
import { SwitchServer } from '@project-chip/matter.js/behavior/definitions/switch';
|
|
42
|
-
import { OnOffLightDevice } from '@project-chip/matter.js/devices/OnOffLightDevice';
|
|
43
|
-
import { GenericSwitchDevice } from '@project-chip/matter.js/devices/GenericSwitchDevice';
|
|
44
|
-
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, er, nf } from 'node-ansi-logger';
|
|
45
|
-
import { NodeStorageManager } from 'node-persist-manager';
|
|
46
|
-
import EventEmitter from 'events';
|
|
47
|
-
import path from 'path';
|
|
48
|
-
import { promises as fs } from 'fs';
|
|
49
|
-
import { MatterbridgeDeviceV8 } from './matterbridgeDeviceV8.js';
|
|
50
|
-
import { pathToFileURL } from 'url';
|
|
51
|
-
import { shelly_config, somfytahoma_config, zigbee2mqtt_config } from './defaultConfigSchema.js';
|
|
52
|
-
const plg = '\u001B[38;5;33m';
|
|
53
|
-
const dev = '\u001B[38;5;79m';
|
|
54
|
-
const typ = '\u001B[38;5;207m';
|
|
55
|
-
const log = Logger.get('Matterbridge');
|
|
56
|
-
/**
|
|
57
|
-
* Represents the Matterbridge application.
|
|
58
|
-
*/
|
|
59
|
-
export class MatterbridgeV8 extends EventEmitter {
|
|
60
|
-
environment = Environment.default;
|
|
61
|
-
matterbridgeVersion = '';
|
|
62
|
-
osVersion = '';
|
|
63
|
-
matterbridgeDirectory = '';
|
|
64
|
-
matterbridgePluginDirectory = '';
|
|
65
|
-
globalModulesDirectory = '';
|
|
66
|
-
matterbridgeLogFile = '';
|
|
67
|
-
registeredPlugins = [];
|
|
68
|
-
// Node storage
|
|
69
|
-
nodeStorage;
|
|
70
|
-
nodeContext;
|
|
71
|
-
// Matter storage
|
|
72
|
-
matterStorageService;
|
|
73
|
-
matterStorageManager;
|
|
74
|
-
matterStorageContext;
|
|
75
|
-
matterServerNode;
|
|
76
|
-
matterAggregator;
|
|
77
|
-
matterLogger;
|
|
78
|
-
constructor() {
|
|
79
|
-
super();
|
|
80
|
-
}
|
|
81
|
-
static async create() {
|
|
82
|
-
const matterbridge = new MatterbridgeV8();
|
|
83
|
-
await matterbridge.initialize();
|
|
84
|
-
return matterbridge;
|
|
85
|
-
}
|
|
86
|
-
async initialize() {
|
|
87
|
-
// Set up the temporary Matterbridge environment
|
|
88
|
-
this.matterbridgeVersion = '2.0.0';
|
|
89
|
-
this.osVersion = '10.0.22631';
|
|
90
|
-
this.matterbridgeDirectory = 'C:\\Users\\lligu\\.matterbridge';
|
|
91
|
-
this.matterbridgePluginDirectory = 'C:\\Users\\lligu\\Matterbridge';
|
|
92
|
-
this.globalModulesDirectory = 'C:\\Users\\lligu\\AppData\\Roaming\\npm\\node_modules';
|
|
93
|
-
this.matterbridgeLogFile = 'matterbridge.log';
|
|
94
|
-
this.matterLogger = Logger.get('Matterbridge');
|
|
95
|
-
await this.deleteMatterLogfile(this.matterbridgeLogFile);
|
|
96
|
-
await this.setupMatterFileLogger(this.matterbridgeLogFile);
|
|
97
|
-
this.matterLogger?.notice(`Starting Matterbridge v${this.matterbridgeVersion} on Node.js ${process.version} (${process.platform} ${process.arch})`);
|
|
98
|
-
this.setupMatterVars(Level.DEBUG, Format.ANSI);
|
|
99
|
-
await this.setupMatterStorage();
|
|
100
|
-
await this.setupNodeStorage();
|
|
101
|
-
// Get the plugins from node storage
|
|
102
|
-
if (!this.nodeStorage)
|
|
103
|
-
throw new Error('No node storage initialized');
|
|
104
|
-
if (!this.nodeContext)
|
|
105
|
-
throw new Error('No node storage context initialized');
|
|
106
|
-
this.registeredPlugins = await this.nodeContext.get('plugins', []);
|
|
107
|
-
for (const plugin of this.registeredPlugins) {
|
|
108
|
-
plugin.nodeContext = await this.nodeStorage.createStorage(plugin.name);
|
|
109
|
-
await plugin.nodeContext.set('name', plugin.name);
|
|
110
|
-
await plugin.nodeContext.set('type', plugin.type);
|
|
111
|
-
await plugin.nodeContext.set('path', plugin.path);
|
|
112
|
-
await plugin.nodeContext.set('version', plugin.version);
|
|
113
|
-
await plugin.nodeContext.set('description', plugin.description);
|
|
114
|
-
await plugin.nodeContext.set('author', plugin.author);
|
|
115
|
-
this.matterLogger?.notice(`Created node storage context for plugin ${plugin.name}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
setupMatterVars(level, format) {
|
|
119
|
-
this.environment.vars.set('log.level', level);
|
|
120
|
-
this.environment.vars.set('log.format', format);
|
|
121
|
-
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, 'matterstorage'));
|
|
122
|
-
this.environment.vars.set('runtime.signals', false);
|
|
123
|
-
this.environment.vars.set('runtime.exitcode', false);
|
|
124
|
-
}
|
|
125
|
-
async setupMatterStorage() {
|
|
126
|
-
this.matterStorageService = this.environment.get(StorageService);
|
|
127
|
-
this.matterLogger?.notice(`Storage service created: ${this.matterStorageService.location}`);
|
|
128
|
-
this.matterStorageManager = await this.matterStorageService.open('Matterbridge');
|
|
129
|
-
this.matterLogger?.notice('Storage manager "Matterbridge" created');
|
|
130
|
-
this.matterStorageContext = this.matterStorageManager.createContext('persist');
|
|
131
|
-
this.matterLogger?.notice('Storage context "Matterbridge.persist" created');
|
|
132
|
-
}
|
|
133
|
-
async setupNodeStorage() {
|
|
134
|
-
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage'), logging: false });
|
|
135
|
-
this.matterLogger?.notice(`Created node storage manager: ${path.join(this.matterbridgeDirectory, 'storage')}`);
|
|
136
|
-
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
137
|
-
this.matterLogger?.notice('Created node storage context "matterbridge"');
|
|
138
|
-
}
|
|
139
|
-
async deleteMatterLogfile(filename) {
|
|
140
|
-
try {
|
|
141
|
-
await fs.unlink(path.join(this.matterbridgeDirectory, filename));
|
|
142
|
-
}
|
|
143
|
-
catch (err) {
|
|
144
|
-
this.matterLogger?.error(`Error deleting old log file: ${err}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async setupMatterFileLogger(filename) {
|
|
148
|
-
Logger.addLogger('filelogger', await createFileLogger(path.join(this.matterbridgeDirectory, filename)), {
|
|
149
|
-
defaultLogLevel: Level.DEBUG,
|
|
150
|
-
});
|
|
151
|
-
this.matterLogger?.notice('File logger created: ' + path.join(this.matterbridgeDirectory, filename));
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Creates a server node storage context.
|
|
155
|
-
*
|
|
156
|
-
* @param pluginName - The name of the plugin.
|
|
157
|
-
* @param deviceName - The name of the device.
|
|
158
|
-
* @param deviceType - The deviceType of the device.
|
|
159
|
-
* @param vendorId - The vendor ID.
|
|
160
|
-
* @param vendorName - The vendor name.
|
|
161
|
-
* @param productId - The product ID.
|
|
162
|
-
* @param productName - The product name.
|
|
163
|
-
* @param serialNumber - The serial number of the device (optional).
|
|
164
|
-
* @param uniqueId - The unique ID of the device (optional).
|
|
165
|
-
* @param softwareVersion - The software version of the device (optional).
|
|
166
|
-
* @param softwareVersionString - The software version string of the device (optional).
|
|
167
|
-
* @param hardwareVersion - The hardware version of the device (optional).
|
|
168
|
-
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
169
|
-
* @returns The storage context for the commissioning server.
|
|
170
|
-
*/
|
|
171
|
-
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
172
|
-
if (!this.matterLogger)
|
|
173
|
-
throw new Error('No logger initialized');
|
|
174
|
-
const log = this.matterLogger;
|
|
175
|
-
if (!this.matterStorageService)
|
|
176
|
-
throw new Error('No storage service initialized');
|
|
177
|
-
log.notice(`Creating server node storage context "${pluginName}.persist" for ${pluginName}...`);
|
|
178
|
-
const storageManager = await this.matterStorageService.open(pluginName);
|
|
179
|
-
const storageContext = storageManager.createContext('persist');
|
|
180
|
-
const random = 'SN' + CryptoNode.getRandomData(8).toHex();
|
|
181
|
-
await storageContext.set('storeId', pluginName);
|
|
182
|
-
await storageContext.set('deviceName', deviceName);
|
|
183
|
-
await storageContext.set('deviceType', deviceType);
|
|
184
|
-
await storageContext.set('vendorId', vendorId);
|
|
185
|
-
await storageContext.set('vendorName', vendorName.slice(0, 32));
|
|
186
|
-
await storageContext.set('productId', productId);
|
|
187
|
-
await storageContext.set('productName', productName.slice(0, 32));
|
|
188
|
-
await storageContext.set('nodeLabel', productName.slice(0, 32));
|
|
189
|
-
await storageContext.set('productLabel', productName.slice(0, 32));
|
|
190
|
-
await storageContext.set('serialNumber', await storageContext.get('serialNumber', serialNumber ? serialNumber.slice(0, 32) : random));
|
|
191
|
-
await storageContext.set('uniqueId', await storageContext.get('uniqueId', random));
|
|
192
|
-
await storageContext.set('softwareVersion', this.matterbridgeVersion && this.matterbridgeVersion.includes('.') ? parseInt(this.matterbridgeVersion.split('.')[0], 10) : 1);
|
|
193
|
-
await storageContext.set('softwareVersionString', this.matterbridgeVersion ?? '1.0.0');
|
|
194
|
-
await storageContext.set('hardwareVersion', this.osVersion && this.osVersion.includes('.') ? parseInt(this.osVersion.split('.')[0], 10) : 1);
|
|
195
|
-
await storageContext.set('hardwareVersionString', this.osVersion ?? '1.0.0');
|
|
196
|
-
log.debug(`Created server node storage context "${pluginName}.persist" for ${pluginName}:`);
|
|
197
|
-
log.debug(`- deviceName: ${await storageContext.get('deviceName')} deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
198
|
-
log.debug(`- serialNumber: ${await storageContext.get('serialNumber')} uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
199
|
-
log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
200
|
-
log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
201
|
-
return storageContext;
|
|
202
|
-
}
|
|
203
|
-
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
204
|
-
if (!this.matterLogger)
|
|
205
|
-
throw new Error('No logger initialized');
|
|
206
|
-
const log = this.matterLogger;
|
|
207
|
-
log.notice(`Creating server node for ${await storageContext.get('storeId')}`);
|
|
208
|
-
/**
|
|
209
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
210
|
-
*/
|
|
211
|
-
const serverNode = await ServerNode.create({
|
|
212
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
213
|
-
id: await storageContext.get('storeId'),
|
|
214
|
-
// Provide Network relevant configuration like the port
|
|
215
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
216
|
-
network: {
|
|
217
|
-
port,
|
|
218
|
-
},
|
|
219
|
-
// Provide Commissioning relevant settings
|
|
220
|
-
// Optional for development/testing purposes
|
|
221
|
-
commissioning: {
|
|
222
|
-
passcode,
|
|
223
|
-
discriminator,
|
|
224
|
-
},
|
|
225
|
-
// Provide Node announcement settings
|
|
226
|
-
// Optional: If Ommitted some development defaults are used
|
|
227
|
-
productDescription: {
|
|
228
|
-
name: await storageContext.get('deviceName'),
|
|
229
|
-
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
230
|
-
},
|
|
231
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
232
|
-
// Optional: If Omitted some development defaults are used
|
|
233
|
-
basicInformation: {
|
|
234
|
-
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
235
|
-
vendorName: await storageContext.get('vendorName'),
|
|
236
|
-
productId: await storageContext.get('productId'),
|
|
237
|
-
productName: await storageContext.get('productName'),
|
|
238
|
-
productLabel: await storageContext.get('productName'),
|
|
239
|
-
nodeLabel: await storageContext.get('productName'),
|
|
240
|
-
serialNumber: await storageContext.get('serialNumber'),
|
|
241
|
-
uniqueId: await storageContext.get('uniqueId'),
|
|
242
|
-
softwareVersion: await storageContext.get('softwareVersion'),
|
|
243
|
-
softwareVersionString: await storageContext.get('softwareVersionString'),
|
|
244
|
-
hardwareVersion: await storageContext.get('hardwareVersion'),
|
|
245
|
-
hardwareVersionString: await storageContext.get('hardwareVersionString'),
|
|
246
|
-
},
|
|
247
|
-
});
|
|
248
|
-
/**
|
|
249
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
250
|
-
* This means: It is added to the first fabric.
|
|
251
|
-
*/
|
|
252
|
-
serverNode.lifecycle.commissioned.on(() => log.notice('Server was initially commissioned successfully!'));
|
|
253
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
254
|
-
serverNode.lifecycle.decommissioned.on(() => log.notice('Server was fully decommissioned successfully!'));
|
|
255
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
256
|
-
serverNode.lifecycle.online.on(() => log.notice('Server is online'));
|
|
257
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
258
|
-
serverNode.lifecycle.offline.on(() => log.notice('Server is offline'));
|
|
259
|
-
/**
|
|
260
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
261
|
-
* information is needed.
|
|
262
|
-
*/
|
|
263
|
-
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
264
|
-
let action = '';
|
|
265
|
-
switch (fabricAction) {
|
|
266
|
-
case FabricAction.Added:
|
|
267
|
-
action = 'added';
|
|
268
|
-
break;
|
|
269
|
-
case FabricAction.Removed:
|
|
270
|
-
action = 'removed';
|
|
271
|
-
break;
|
|
272
|
-
case FabricAction.Updated:
|
|
273
|
-
action = 'updated';
|
|
274
|
-
break;
|
|
275
|
-
}
|
|
276
|
-
log.notice(`Commissioned Fabrics changed event (${action}) for ${fabricIndex} triggered`);
|
|
277
|
-
log.notice(this.matterServerNode?.state.commissioning.fabrics[fabricIndex]);
|
|
278
|
-
});
|
|
279
|
-
/**
|
|
280
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
281
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
282
|
-
*/
|
|
283
|
-
serverNode.events.sessions.opened.on((session) => log.notice('Session opened', session));
|
|
284
|
-
/**
|
|
285
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
286
|
-
*/
|
|
287
|
-
serverNode.events.sessions.closed.on((session) => log.notice('Session closed', session));
|
|
288
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
289
|
-
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
290
|
-
log.notice('Session subscriptions changed', session);
|
|
291
|
-
log.notice('Status of all sessions', this.matterServerNode?.state.sessions.sessions);
|
|
292
|
-
});
|
|
293
|
-
return serverNode;
|
|
294
|
-
}
|
|
295
|
-
showServerNodeQR() {
|
|
296
|
-
if (!this.matterServerNode || !this.matterLogger)
|
|
297
|
-
return;
|
|
298
|
-
const log = this.matterLogger;
|
|
299
|
-
if (!this.matterServerNode.lifecycle.isCommissioned) {
|
|
300
|
-
const { qrPairingCode, manualPairingCode } = this.matterServerNode.state.commissioning.pairingCodes;
|
|
301
|
-
console.log(QrCode.get(qrPairingCode));
|
|
302
|
-
log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
303
|
-
log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
log.notice('Device is already commissioned. Waiting for controllers to connect ...');
|
|
307
|
-
log.notice('Fabrics:', this.matterServerNode.state.commissioning.fabrics);
|
|
308
|
-
for (const key in this.matterServerNode.state.commissioning.fabrics) {
|
|
309
|
-
const fabric = this.matterServerNode.state.commissioning.fabrics[FabricIndex(Number(key))];
|
|
310
|
-
log.notice(`- index ${fabric.fabricIndex} id ${fabric.fabricId} nodeId ${fabric.nodeId} rootVendor ${fabric.rootVendorId} rootNodeId ${fabric.rootNodeId}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
async stopServerNode() {
|
|
315
|
-
if (!this.matterServerNode)
|
|
316
|
-
return;
|
|
317
|
-
await this.matterServerNode.close();
|
|
318
|
-
}
|
|
319
|
-
async createAggregator(storageContext) {
|
|
320
|
-
if (!this.matterLogger)
|
|
321
|
-
throw new Error('No logger initialized');
|
|
322
|
-
const log = this.matterLogger;
|
|
323
|
-
log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
324
|
-
const aggregator = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')} aggregator` });
|
|
325
|
-
return aggregator;
|
|
326
|
-
}
|
|
327
|
-
async startBridge() {
|
|
328
|
-
if (!this.matterLogger)
|
|
329
|
-
throw new Error('No logger initialized');
|
|
330
|
-
const log = this.matterLogger;
|
|
331
|
-
const storageContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', AggregatorEndpoint.deviceType, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
|
|
332
|
-
this.matterServerNode = await this.createServerNode(storageContext);
|
|
333
|
-
this.matterAggregator = await this.createAggregator(storageContext);
|
|
334
|
-
log.notice(`Adding ${await storageContext.get('storeId')} aggregator to ${await storageContext.get('storeId')} server node`);
|
|
335
|
-
await this.matterServerNode.add(this.matterAggregator);
|
|
336
|
-
for (const plugin of this.registeredPlugins) {
|
|
337
|
-
if (!plugin.enabled) {
|
|
338
|
-
log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
plugin.error = false;
|
|
342
|
-
plugin.loaded = false;
|
|
343
|
-
plugin.started = false;
|
|
344
|
-
plugin.configured = false;
|
|
345
|
-
plugin.connected = undefined;
|
|
346
|
-
plugin.qrPairingCode = undefined;
|
|
347
|
-
plugin.manualPairingCode = undefined;
|
|
348
|
-
this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
349
|
-
}
|
|
350
|
-
log.notice(`Adding lightEndpoint1 to ${await storageContext.get('storeId')} aggregator`);
|
|
351
|
-
const lightEndpoint1 = new Endpoint(OnOffLightDevice.with(BridgedDeviceBasicInformationServer), {
|
|
352
|
-
id: 'OnOffLight',
|
|
353
|
-
bridgedDeviceBasicInformation: {
|
|
354
|
-
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
355
|
-
vendorName: await storageContext.get('vendorName'),
|
|
356
|
-
productName: 'Light',
|
|
357
|
-
productLabel: 'Light',
|
|
358
|
-
nodeLabel: 'Light',
|
|
359
|
-
serialNumber: '0x123456789',
|
|
360
|
-
uniqueId: '0x123456789',
|
|
361
|
-
reachable: true,
|
|
362
|
-
},
|
|
363
|
-
});
|
|
364
|
-
this.matterAggregator.add(lightEndpoint1);
|
|
365
|
-
log.notice(`Adding switchEnpoint2 to ${await storageContext.get('storeId')} aggregator`);
|
|
366
|
-
const switchEnpoint2 = new Endpoint(GenericSwitchDevice.with(BridgedDeviceBasicInformationServer, SwitchServer.with('MomentarySwitch', 'MomentarySwitchLongPress', 'MomentarySwitchMultiPress', 'MomentarySwitchRelease')), {
|
|
367
|
-
id: 'GenericSwitch',
|
|
368
|
-
bridgedDeviceBasicInformation: {
|
|
369
|
-
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
370
|
-
vendorName: await storageContext.get('vendorName'),
|
|
371
|
-
productName: 'GenericSwitch',
|
|
372
|
-
productLabel: 'GenericSwitch',
|
|
373
|
-
nodeLabel: 'GenericSwitch',
|
|
374
|
-
serialNumber: '0x123456739',
|
|
375
|
-
uniqueId: '0x123456739',
|
|
376
|
-
reachable: true,
|
|
377
|
-
},
|
|
378
|
-
switch: {
|
|
379
|
-
numberOfPositions: 2,
|
|
380
|
-
currentPosition: 0,
|
|
381
|
-
multiPressMax: 2,
|
|
382
|
-
},
|
|
383
|
-
});
|
|
384
|
-
this.matterAggregator.add(switchEnpoint2);
|
|
385
|
-
switchEnpoint2.events.identify.startIdentifying.on(() => log.notice('Run identify logic, ideally blink a light every 0.5s ...'));
|
|
386
|
-
switchEnpoint2.events.switch.currentPosition$Changed.on(() => log.notice('Run identify logic, ideally blink a light every 0.5s ...'));
|
|
387
|
-
// switchEnpoint2.events.switch.emit('initialPress', { newPosition: 1 }, ActionContext.agentFor(switchEnpoint2) );
|
|
388
|
-
log.notice(`Adding matterbridge device to ${await storageContext.get('storeId')} aggregator`);
|
|
389
|
-
const matterbridgeDevice3 = new MatterbridgeDeviceV8(DeviceTypes.TEMPERATURE_SENSOR, { uniqueStorageKey: 'TemperatureSensor' });
|
|
390
|
-
this.matterAggregator.add(matterbridgeDevice3);
|
|
391
|
-
log.notice(`Starting ${await storageContext.get('storeId')} server node`);
|
|
392
|
-
await this.matterServerNode.bringOnline();
|
|
393
|
-
/*
|
|
394
|
-
logEndpoint(EndpointServer.forEndpoint(this.matterServerNode));
|
|
395
|
-
console.log('matterbridgeDevice3\n', matterbridgeDevice3);
|
|
396
|
-
console.log('matterbridgeDevice3.events\n', matterbridgeDevice3.events);
|
|
397
|
-
console.log('matterbridgeDevice3.events.identify\n', matterbridgeDevice3.eventsOf(IdentifyServer));
|
|
398
|
-
console.log('matterbridgeDevice3.state\n', matterbridgeDevice3.state);
|
|
399
|
-
console.log('matterbridgeDevice3.state.temperatureMeasurement\n', matterbridgeDevice3.stateOf(TemperatureMeasurementServer));
|
|
400
|
-
// matterbridgeDevice3.eventsOf(IdentifyServer);
|
|
401
|
-
// matterbridgeDevice3.events.identify.startIdentifying.on(() => log.notice('Run identify logic, ideally blink a light every 0.5s ...'));
|
|
402
|
-
*/
|
|
403
|
-
this.showServerNodeQR();
|
|
404
|
-
}
|
|
405
|
-
async startChildbridge() {
|
|
406
|
-
//
|
|
407
|
-
}
|
|
408
|
-
async startController() {
|
|
409
|
-
//
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Adds a bridged device to the Matterbridge.
|
|
413
|
-
* @param pluginName - The name of the plugin.
|
|
414
|
-
* @param device - The bridged device to add.
|
|
415
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
416
|
-
*/
|
|
417
|
-
async addBridgedDevice(pluginName, device) {
|
|
418
|
-
log.info(`Adding bridged device ${dev}${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Loads a plugin and returns the corresponding MatterbridgePlatform instance.
|
|
422
|
-
* @param plugin - The plugin to load.
|
|
423
|
-
* @param start - Optional flag indicating whether to start the plugin after loading. Default is false.
|
|
424
|
-
* @param message - Optional message to pass to the plugin when starting.
|
|
425
|
-
* @returns A Promise that resolves to the loaded MatterbridgePlatform instance.
|
|
426
|
-
* @throws An error if the plugin is not enabled, already loaded, or fails to load.
|
|
427
|
-
*/
|
|
428
|
-
async loadPlugin(plugin, start = false, message = '') {
|
|
429
|
-
if (!plugin.enabled) {
|
|
430
|
-
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not enabled`));
|
|
431
|
-
}
|
|
432
|
-
if (plugin.platform) {
|
|
433
|
-
return Promise.resolve(plugin.platform);
|
|
434
|
-
}
|
|
435
|
-
log.info(`Loading plugin ${plg}${plugin.name}${nf} type ${typ}${plugin.type}${nf}`);
|
|
436
|
-
try {
|
|
437
|
-
// Load the package.json of the plugin
|
|
438
|
-
const packageJson = JSON.parse(await fs.readFile(plugin.path, 'utf8'));
|
|
439
|
-
// Resolve the main module path relative to package.json
|
|
440
|
-
const pluginEntry = path.resolve(path.dirname(plugin.path), packageJson.main);
|
|
441
|
-
// Dynamically import the plugin
|
|
442
|
-
const pluginUrl = pathToFileURL(pluginEntry);
|
|
443
|
-
log.debug(`Importing plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
|
|
444
|
-
const pluginInstance = await import(pluginUrl.href);
|
|
445
|
-
log.debug(`Imported plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
|
|
446
|
-
// Call the default export function of the plugin, passing this MatterBridge instance, the log and the config
|
|
447
|
-
if (pluginInstance.default) {
|
|
448
|
-
const config = await this.loadPluginConfig(plugin);
|
|
449
|
-
const log = new AnsiLogger({ logName: plugin.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: true });
|
|
450
|
-
const platform = pluginInstance.default(this, log, config);
|
|
451
|
-
platform.name = packageJson.name;
|
|
452
|
-
platform.config = config;
|
|
453
|
-
platform.version = packageJson.version;
|
|
454
|
-
plugin.name = packageJson.name;
|
|
455
|
-
plugin.description = packageJson.description;
|
|
456
|
-
plugin.version = packageJson.version;
|
|
457
|
-
plugin.author = packageJson.author;
|
|
458
|
-
plugin.type = platform.type;
|
|
459
|
-
plugin.platform = platform;
|
|
460
|
-
plugin.loaded = true;
|
|
461
|
-
plugin.registeredDevices = 0;
|
|
462
|
-
plugin.addedDevices = 0;
|
|
463
|
-
// Save the updated plugin data in the node storage
|
|
464
|
-
// await this.nodeContext?.set<RegisteredPlugin[]>('plugins', await this.getBaseRegisteredPlugins());
|
|
465
|
-
// await this.getPluginLatestVersion(plugin);
|
|
466
|
-
log.info(`Loaded plugin ${plg}${plugin.name}${nf} type ${typ}${platform.type} ${db}(entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
|
|
467
|
-
if (start)
|
|
468
|
-
this.startPlugin(plugin, message); // No await do it asyncronously
|
|
469
|
-
return Promise.resolve(platform);
|
|
470
|
-
}
|
|
471
|
-
else {
|
|
472
|
-
plugin.error = true;
|
|
473
|
-
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
catch (err) {
|
|
477
|
-
plugin.error = true;
|
|
478
|
-
return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Starts a plugin.
|
|
483
|
-
*
|
|
484
|
-
* @param {RegisteredPlugin} plugin - The plugin to start.
|
|
485
|
-
* @param {string} [message] - Optional message to pass to the plugin's onStart method.
|
|
486
|
-
* @param {boolean} [configure=false] - Indicates whether to configure the plugin after starting.
|
|
487
|
-
* @returns {Promise<void>} A promise that resolves when the plugin is started successfully, or rejects with an error if starting the plugin fails.
|
|
488
|
-
*/
|
|
489
|
-
async startPlugin(plugin, message, configure = false) {
|
|
490
|
-
if (!plugin.loaded || !plugin.platform) {
|
|
491
|
-
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`));
|
|
492
|
-
}
|
|
493
|
-
if (plugin.started) {
|
|
494
|
-
log.debug(`Plugin ${plg}${plugin.name}${db} already started`);
|
|
495
|
-
return Promise.resolve();
|
|
496
|
-
}
|
|
497
|
-
log.info(`Starting plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
498
|
-
try {
|
|
499
|
-
plugin.platform
|
|
500
|
-
.onStart(message)
|
|
501
|
-
.then(() => {
|
|
502
|
-
plugin.started = true;
|
|
503
|
-
log.info(`Started plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
504
|
-
if (configure)
|
|
505
|
-
this.configurePlugin(plugin); // No await do it asyncronously
|
|
506
|
-
return Promise.resolve();
|
|
507
|
-
})
|
|
508
|
-
.catch((err) => {
|
|
509
|
-
plugin.error = true;
|
|
510
|
-
return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
catch (err) {
|
|
514
|
-
plugin.error = true;
|
|
515
|
-
return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Configures a plugin.
|
|
520
|
-
*
|
|
521
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
522
|
-
* @returns {Promise<void>} A promise that resolves when the plugin is configured successfully, or rejects with an error if configuration fails.
|
|
523
|
-
*/
|
|
524
|
-
async configurePlugin(plugin) {
|
|
525
|
-
if (!plugin.loaded || !plugin.started || !plugin.platform) {
|
|
526
|
-
return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded (${plugin.loaded}) or not started (${plugin.started}) or not platform (${plugin.platform?.name})`));
|
|
527
|
-
}
|
|
528
|
-
if (plugin.configured) {
|
|
529
|
-
log.info(`Plugin ${plg}${plugin.name}${nf} already configured`);
|
|
530
|
-
return Promise.resolve();
|
|
531
|
-
}
|
|
532
|
-
log.info(`Configuring plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
533
|
-
try {
|
|
534
|
-
plugin.platform
|
|
535
|
-
.onConfigure()
|
|
536
|
-
.then(() => {
|
|
537
|
-
plugin.configured = true;
|
|
538
|
-
log.info(`Configured plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
539
|
-
// this.savePluginConfig(plugin);
|
|
540
|
-
return Promise.resolve();
|
|
541
|
-
})
|
|
542
|
-
.catch((err) => {
|
|
543
|
-
plugin.error = true;
|
|
544
|
-
return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
catch (err) {
|
|
548
|
-
plugin.error = true;
|
|
549
|
-
return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Loads the configuration for a plugin.
|
|
554
|
-
* If the configuration file exists, it reads the file and returns the parsed JSON data.
|
|
555
|
-
* If the configuration file does not exist, it creates a new file with default configuration and returns it.
|
|
556
|
-
* If any error occurs during file access or creation, it logs an error and rejects the promise with the error.
|
|
557
|
-
*
|
|
558
|
-
* @param plugin - The plugin for which to load the configuration.
|
|
559
|
-
* @returns A promise that resolves to the loaded or created configuration.
|
|
560
|
-
*/
|
|
561
|
-
async loadPluginConfig(plugin) {
|
|
562
|
-
const configFile = path.join(this.matterbridgeDirectory, `${plugin.name}.config.json`);
|
|
563
|
-
try {
|
|
564
|
-
await fs.access(configFile);
|
|
565
|
-
const data = await fs.readFile(configFile, 'utf8');
|
|
566
|
-
const config = JSON.parse(data);
|
|
567
|
-
// this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
|
|
568
|
-
log.debug(`Config file found: ${configFile}.`);
|
|
569
|
-
/* The first time a plugin is added to the system, the config file is created with the plugin name and type "".*/
|
|
570
|
-
config.name = plugin.name;
|
|
571
|
-
config.type = plugin.type;
|
|
572
|
-
return config;
|
|
573
|
-
}
|
|
574
|
-
catch (err) {
|
|
575
|
-
if (err instanceof Error) {
|
|
576
|
-
const nodeErr = err;
|
|
577
|
-
if (nodeErr.code === 'ENOENT') {
|
|
578
|
-
let config;
|
|
579
|
-
if (plugin.name === 'matterbridge-zigbee2mqtt')
|
|
580
|
-
config = zigbee2mqtt_config;
|
|
581
|
-
else if (plugin.name === 'matterbridge-somfy-tahoma')
|
|
582
|
-
config = somfytahoma_config;
|
|
583
|
-
else if (plugin.name === 'matterbridge-shelly')
|
|
584
|
-
config = shelly_config;
|
|
585
|
-
else
|
|
586
|
-
config = { name: plugin.name, type: plugin.type, unregisterOnShutdown: false };
|
|
587
|
-
try {
|
|
588
|
-
await this.writeFile(configFile, JSON.stringify(config, null, 2));
|
|
589
|
-
log.debug(`Created config file: ${configFile}.`);
|
|
590
|
-
// this.log.debug(`Created config file: ${configFile}.\nConfig:${rs}\n`, config);
|
|
591
|
-
return config;
|
|
592
|
-
}
|
|
593
|
-
catch (err) {
|
|
594
|
-
log.error(`Error creating config file ${configFile}: ${err}`);
|
|
595
|
-
return config;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
else {
|
|
599
|
-
log.error(`Error accessing config file ${configFile}: ${err}`);
|
|
600
|
-
return {};
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
log.error(`Error loading config file ${configFile}: ${err}`);
|
|
604
|
-
return {};
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
/**
|
|
608
|
-
* Writes data to a file.
|
|
609
|
-
*
|
|
610
|
-
* @param {string} filePath - The path of the file to write to.
|
|
611
|
-
* @param {string} data - The data to write to the file.
|
|
612
|
-
* @returns {Promise<void>} - A promise that resolves when the data is successfully written to the file.
|
|
613
|
-
*/
|
|
614
|
-
async writeFile(filePath, data) {
|
|
615
|
-
// Write the data to a file
|
|
616
|
-
await fs
|
|
617
|
-
.writeFile(`${filePath}`, data, 'utf8')
|
|
618
|
-
.then(() => {
|
|
619
|
-
log.debug(`Successfully wrote to ${filePath}`);
|
|
620
|
-
})
|
|
621
|
-
.catch((error) => {
|
|
622
|
-
log.error(`Error writing to ${filePath}:`, error);
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
// node dist/matterbridgeV8.js MatterbridgeV8
|
|
627
|
-
if (process.argv.includes('MatterbridgeV8')) {
|
|
628
|
-
const matterbridge = await MatterbridgeV8.create();
|
|
629
|
-
if (process.argv.includes('-bridge'))
|
|
630
|
-
await matterbridge.startBridge();
|
|
631
|
-
if (process.argv.includes('-childbridge'))
|
|
632
|
-
await matterbridge.startChildbridge();
|
|
633
|
-
if (process.argv.includes('-controller'))
|
|
634
|
-
await matterbridge.startController();
|
|
635
|
-
process.on('SIGINT', async function () {
|
|
636
|
-
console.log('Caught interrupt signal');
|
|
637
|
-
await matterbridge.stopServerNode();
|
|
638
|
-
process.exit();
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
//# sourceMappingURL=matterbridgeV8.js.map
|