matterbridge 2.1.6-dev.4 → 2.1.6-dev.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 +1 -0
- package/dist/cli.js +91 -89
- package/dist/frontend.js +7 -6
- package/dist/index.js +6 -8
- package/dist/matterbridge.js +48 -62
- package/dist/matterbridgeEndpoint.js +1 -1
- package/dist/matterbridgeEndpointHelpers.js +2 -2
- package/dist/matterbridgePlatform.js +2 -2
- package/dist/utils/colorUtils.js +1 -1
- package/dist/utils/export.js +2 -0
- package/dist/utils/isvalid.js +50 -0
- package/dist/utils/parameter.js +26 -0
- package/dist/utils/utils.js +19 -81
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.257513e8.js → main.be75f5a3.js} +4 -4
- package/frontend/build/static/js/{main.257513e8.js.map → main.be75f5a3.js.map} +1 -1
- package/npm-shrinkwrap.json +44 -44
- package/package.json +2 -2
- /package/frontend/build/static/js/{main.257513e8.js.LICENSE.txt → main.be75f5a3.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -49,6 +49,7 @@ matterbridge-hass v. 0.0.8
|
|
|
49
49
|
- [package]: Update matter.js to 0.12.4-alpha.0-20250212-b2729c9eb
|
|
50
50
|
- [package]: Update matter.js to 0.12.4-alpha.0-20250213-1187f81eb
|
|
51
51
|
- [package]: Update matter.js to 0.12.4-alpha.0-20250215-5af08a8d6
|
|
52
|
+
- [package]: Update matter.js to 0.12.4-alpha.0-20250217-b0bba5179
|
|
52
53
|
|
|
53
54
|
<a href="https://www.buymeacoffee.com/luligugithub">
|
|
54
55
|
<img src="./yellow-button.png" alt="Buy me a coffee" width="120">
|
package/dist/cli.js
CHANGED
|
@@ -1,62 +1,48 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Matterbridge } from './matterbridge.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getIntParameter, hasParameter } from './utils/export.js';
|
|
4
|
+
import { AnsiLogger, BRIGHT, CYAN, db, YELLOW } from './logger/export.js';
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
4
6
|
export const cliEmitter = new EventEmitter();
|
|
5
7
|
export let instance;
|
|
6
8
|
let session;
|
|
7
9
|
let memoryCheckInterval;
|
|
8
10
|
let prevCpus;
|
|
9
11
|
export let lastCpuUsage = 0;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
}
|
|
12
|
+
let peakCpu = 0;
|
|
13
|
+
let peakRss = 0;
|
|
14
|
+
const log = new AnsiLogger({ logName: 'Cli', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
15
|
+
const formatMemoryUsage = (bytes) => {
|
|
16
|
+
if (bytes >= 1024 ** 3) {
|
|
17
|
+
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
18
|
+
}
|
|
19
|
+
else if (bytes >= 1024 ** 2) {
|
|
20
|
+
return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const formatOsUpTime = (seconds) => {
|
|
27
|
+
if (seconds >= 86400) {
|
|
28
|
+
const days = Math.floor(seconds / 86400);
|
|
29
|
+
return `${days} day${days !== 1 ? 's' : ''}`;
|
|
30
|
+
}
|
|
31
|
+
if (seconds >= 3600) {
|
|
32
|
+
const hours = Math.floor(seconds / 3600);
|
|
33
|
+
return `${hours} hour${hours !== 1 ? 's' : ''}`;
|
|
34
|
+
}
|
|
35
|
+
if (seconds >= 60) {
|
|
36
|
+
const minutes = Math.floor(seconds / 60);
|
|
37
|
+
return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
|
|
38
|
+
}
|
|
39
|
+
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
40
|
+
};
|
|
28
41
|
async function startCpuMemoryCheck() {
|
|
29
42
|
const os = await import('node:os');
|
|
30
|
-
|
|
31
|
-
console.log(cli + `CLI: Cpu memory check started` + rs);
|
|
43
|
+
log.debug(`Cpu memory check started`);
|
|
32
44
|
prevCpus = os.cpus();
|
|
33
45
|
clearInterval(memoryCheckInterval);
|
|
34
|
-
const formatMemoryUsage = (bytes) => {
|
|
35
|
-
if (bytes >= 1024 ** 3) {
|
|
36
|
-
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
37
|
-
}
|
|
38
|
-
else if (bytes >= 1024 ** 2) {
|
|
39
|
-
return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const formatOsUpTime = (seconds) => {
|
|
46
|
-
if (seconds >= 86400) {
|
|
47
|
-
const days = Math.floor(seconds / 86400);
|
|
48
|
-
return `${days} day${days !== 1 ? 's' : ''}`;
|
|
49
|
-
}
|
|
50
|
-
if (seconds >= 3600) {
|
|
51
|
-
const hours = Math.floor(seconds / 3600);
|
|
52
|
-
return `${hours} hour${hours !== 1 ? 's' : ''}`;
|
|
53
|
-
}
|
|
54
|
-
if (seconds >= 60) {
|
|
55
|
-
const minutes = Math.floor(seconds / 60);
|
|
56
|
-
return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
|
|
57
|
-
}
|
|
58
|
-
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
59
|
-
};
|
|
60
46
|
const interval = () => {
|
|
61
47
|
const systemUptime = formatOsUpTime(Math.floor(os.uptime()));
|
|
62
48
|
const processUptime = formatOsUpTime(Math.floor(process.uptime()));
|
|
@@ -69,13 +55,14 @@ async function startCpuMemoryCheck() {
|
|
|
69
55
|
const heapUsed = formatMemoryUsage(memoryUsageRaw.heapUsed);
|
|
70
56
|
const external = formatMemoryUsage(memoryUsageRaw.external);
|
|
71
57
|
const arrayBuffers = formatMemoryUsage(memoryUsageRaw.arrayBuffers);
|
|
58
|
+
if (memoryUsageRaw.rss > peakRss)
|
|
59
|
+
peakRss = memoryUsageRaw.rss;
|
|
72
60
|
cliEmitter.emit('memory', totalMememory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers);
|
|
73
61
|
const currCpus = os.cpus();
|
|
74
62
|
let cpuUsageLog;
|
|
75
63
|
if (currCpus.length !== prevCpus.length) {
|
|
76
64
|
prevCpus = currCpus;
|
|
77
|
-
|
|
78
|
-
console.log(cli + `CLI: Cpu check length failed, resetting previous cpus` + rs);
|
|
65
|
+
log.debug(`Cpu check length failed, resetting previous cpus`);
|
|
79
66
|
return;
|
|
80
67
|
}
|
|
81
68
|
let totalIdle = 0, totalTick = 0;
|
|
@@ -88,59 +75,63 @@ async function startCpuMemoryCheck() {
|
|
|
88
75
|
});
|
|
89
76
|
const cpuUsage = 100 - (totalIdle / totalTick) * 100;
|
|
90
77
|
if (totalTick === 0 || isNaN(cpuUsage) || !isFinite(cpuUsage) || cpuUsage <= 0) {
|
|
91
|
-
if (
|
|
92
|
-
|
|
78
|
+
if (lastCpuUsage != 0)
|
|
79
|
+
log.debug(`Cpu check failed, using previous cpus`);
|
|
93
80
|
cpuUsageLog = lastCpuUsage.toFixed(2);
|
|
94
81
|
}
|
|
95
82
|
else {
|
|
96
83
|
cpuUsageLog = cpuUsage.toFixed(2);
|
|
97
84
|
lastCpuUsage = cpuUsage;
|
|
85
|
+
if (lastCpuUsage > peakCpu)
|
|
86
|
+
peakCpu = lastCpuUsage;
|
|
98
87
|
cliEmitter.emit('cpu', lastCpuUsage);
|
|
99
88
|
}
|
|
100
89
|
prevCpus = currCpus;
|
|
101
|
-
|
|
102
|
-
console.log(`${YELLOW}${BRIGHT}Cpu usage:${db} ${CYAN}${cpuUsageLog.padStart(6, ' ')} %${db} ${YELLOW}${BRIGHT}Memory usage:${db} rss ${CYAN}${rss}${db} heapTotal ${CYAN}${heapTotal}${db} heapUsed ${CYAN}${heapUsed}${db} external ${external} arrayBuffers ${arrayBuffers}` +
|
|
103
|
-
rs);
|
|
90
|
+
log.debug(`***${YELLOW}${BRIGHT}Cpu usage:${db} ${CYAN}${cpuUsageLog.padStart(6, ' ')} %${db} ${YELLOW}${BRIGHT}Memory usage:${db} rss ${CYAN}${rss}${db} heapTotal ${CYAN}${heapTotal}${db} heapUsed ${CYAN}${heapUsed}${db} external ${external} arrayBuffers ${arrayBuffers}`);
|
|
104
91
|
};
|
|
105
92
|
interval();
|
|
106
|
-
memoryCheckInterval = setInterval(interval, 10 * 1000);
|
|
93
|
+
memoryCheckInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 10 * 1000);
|
|
107
94
|
}
|
|
108
95
|
async function stopCpuMemoryCheck() {
|
|
109
|
-
|
|
110
|
-
console.log(cli + `CLI: Cpu memory check stopped` + rs);
|
|
96
|
+
log.debug(`***Cpu memory check stopped. Peak cpu: ${CYAN}${peakCpu.toFixed(2)} %${db}. Peak rss: ${CYAN}${formatMemoryUsage(peakRss)}${db}.`);
|
|
111
97
|
clearInterval(memoryCheckInterval);
|
|
112
98
|
}
|
|
113
99
|
async function startInspector() {
|
|
114
100
|
const { Session } = await import('node:inspector');
|
|
115
|
-
|
|
116
|
-
console.log(cli + `CLI: Starting heap sampling...` + rs);
|
|
101
|
+
log.debug(`Starting heap sampling...`);
|
|
117
102
|
session = new Session();
|
|
118
103
|
session.connect();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.error(err);
|
|
122
|
-
else
|
|
123
|
-
console.log(cli + `CLI: Heap sampling started` + rs);
|
|
104
|
+
await new Promise((resolve, reject) => {
|
|
105
|
+
session?.post('HeapProfiler.startSampling', {}, (err) => (err ? reject(err) : resolve()));
|
|
124
106
|
});
|
|
107
|
+
log.debug(`Started heap sampling`);
|
|
125
108
|
}
|
|
126
109
|
async function stopInspector() {
|
|
127
110
|
const { writeFileSync } = await import('node:fs');
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
111
|
+
log.debug(`Stopping heap sampling...`);
|
|
112
|
+
if (!session) {
|
|
113
|
+
log.error('No active inspector session.');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const result = await new Promise((resolve, reject) => {
|
|
118
|
+
session?.post('HeapProfiler.stopSampling', (err, result) => (err ? reject(err) : resolve(result)));
|
|
119
|
+
});
|
|
120
|
+
const profile = JSON.stringify(result.profile);
|
|
121
|
+
writeFileSync('Heap-sampling-profile.heapprofile', profile);
|
|
122
|
+
log.debug('Heap sampling profile saved to Heap-sampling-profile.heapprofile');
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
log.error(`Failed to stop heap sampling: ${err instanceof Error ? err.message : err}`);
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
session.disconnect();
|
|
140
129
|
session = undefined;
|
|
141
|
-
|
|
130
|
+
log.debug(`Stopped heap sampling`);
|
|
131
|
+
}
|
|
142
132
|
}
|
|
143
133
|
function registerHandlers() {
|
|
134
|
+
log.debug('Registering event handlers...');
|
|
144
135
|
if (instance)
|
|
145
136
|
instance.on('shutdown', async () => shutdown());
|
|
146
137
|
if (instance)
|
|
@@ -151,38 +142,49 @@ function registerHandlers() {
|
|
|
151
142
|
instance.on('startmemorycheck', async () => start());
|
|
152
143
|
if (instance)
|
|
153
144
|
instance.on('stopmemorycheck', async () => stop());
|
|
145
|
+
log.debug('Registered event handlers');
|
|
154
146
|
}
|
|
155
147
|
async function shutdown() {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (process.argv.includes('-inspector'))
|
|
148
|
+
log.debug('Received shutdown event, exiting...');
|
|
149
|
+
if (hasParameter('inspect'))
|
|
159
150
|
await stopInspector();
|
|
160
151
|
await stopCpuMemoryCheck();
|
|
161
152
|
process.exit(0);
|
|
162
153
|
}
|
|
163
154
|
async function restart() {
|
|
164
|
-
|
|
165
|
-
console.log(cli + 'CLI: received restart event, loading...' + rs);
|
|
155
|
+
log.debug('Received restart event, loading...');
|
|
166
156
|
instance = await Matterbridge.loadInstance(true);
|
|
167
157
|
registerHandlers();
|
|
168
158
|
}
|
|
169
159
|
async function update() {
|
|
170
|
-
|
|
171
|
-
console.log(cli + 'CLI: received update event, updating...' + rs);
|
|
160
|
+
log.debug('Received update event, updating...');
|
|
172
161
|
instance = await Matterbridge.loadInstance(true);
|
|
173
162
|
registerHandlers();
|
|
174
163
|
}
|
|
175
164
|
async function start() {
|
|
176
|
-
|
|
177
|
-
console.log(cli + 'CLI: received start memory check event' + rs);
|
|
165
|
+
log.debug('Received start memory check event');
|
|
178
166
|
startCpuMemoryCheck();
|
|
179
167
|
}
|
|
180
168
|
async function stop() {
|
|
181
|
-
|
|
182
|
-
console.log(cli + 'CLI: received stop memory check event' + rs);
|
|
169
|
+
log.debug('Received stop memory check event');
|
|
183
170
|
stopCpuMemoryCheck();
|
|
184
171
|
}
|
|
172
|
+
async function main() {
|
|
173
|
+
log.debug(`Cli main() started`);
|
|
174
|
+
await startCpuMemoryCheck();
|
|
175
|
+
if (hasParameter('inspect'))
|
|
176
|
+
await startInspector();
|
|
177
|
+
log.debug(`***Matterbridge.loadInstance(true) called`);
|
|
178
|
+
instance = await Matterbridge.loadInstance(true);
|
|
179
|
+
log.debug(`***Matterbridge.loadInstance(true) exited`);
|
|
180
|
+
if (instance.shutdown) {
|
|
181
|
+
shutdown();
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
registerHandlers();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
185
187
|
process.title = 'matterbridge';
|
|
186
188
|
main().catch((error) => {
|
|
187
|
-
|
|
189
|
+
log.error(`Matterbridge.loadInstance() failed with error: ${error instanceof Error ? error.message : error}`);
|
|
188
190
|
});
|
package/dist/frontend.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat } from '@matter/main';
|
|
2
|
-
import { createServer } from 'http';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
3
|
import https from 'https';
|
|
4
4
|
import express from 'express';
|
|
5
5
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
6
|
-
import os from 'os';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import { promises as fs } from 'fs';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { promises as fs } from 'node:fs';
|
|
9
9
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
|
|
10
|
-
import { createZip, deepCopy,
|
|
10
|
+
import { createZip, deepCopy, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
11
11
|
import { plg } from './matterbridgeTypes.js';
|
|
12
|
+
import { hasParameter } from './utils/export.js';
|
|
12
13
|
export const WS_ID_LOG = 0;
|
|
13
14
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
14
15
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
@@ -236,7 +237,7 @@ export class Frontend {
|
|
|
236
237
|
space_available_size: this.formatMemoryUsage(space.space_available_size),
|
|
237
238
|
physical_space_size: this.formatMemoryUsage(space.physical_space_size),
|
|
238
239
|
}));
|
|
239
|
-
const { default: module } = await import('module');
|
|
240
|
+
const { default: module } = await import('node:module');
|
|
240
241
|
const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
|
|
241
242
|
const memoryReport = {
|
|
242
243
|
memoryUsage,
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Matterbridge } from './matterbridge.js';
|
|
2
|
+
import { hasParameter } from './utils/export.js';
|
|
3
|
+
import { AnsiLogger } from './logger/export.js';
|
|
2
4
|
export { SemanticNamespace, ClosureTag, CompassDirectionTag, CompassLocationTag, DirectionTag, ElectricalMeasurementTag, LaundryTag, LevelTag, LocationTag, NumberTag, PositionTag, PowerSourceTag, RefrigeratorTag, RoomAirConditionerTag, SwitchesTag, } from '@matter/main';
|
|
3
5
|
export * from '@matter/main/clusters';
|
|
4
6
|
export * from '@matter/main/types';
|
|
@@ -11,16 +13,12 @@ export * from './matterbridgeDeviceTypes.js';
|
|
|
11
13
|
export * from './matterbridgePlatform.js';
|
|
12
14
|
export * from './matterbridgeAccessoryPlatform.js';
|
|
13
15
|
export * from './matterbridgeDynamicPlatform.js';
|
|
14
|
-
const
|
|
15
|
-
const er = '\u001B[38;5;9m';
|
|
16
|
-
const rs = '\u001B[40;0m';
|
|
16
|
+
const log = new AnsiLogger({ logName: 'Main', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
17
17
|
async function main() {
|
|
18
|
-
|
|
19
|
-
console.log(cli + 'MAIN: Matterbridge.loadInstance() called' + rs);
|
|
18
|
+
log.debug('***Matterbridge.loadInstance() called');
|
|
20
19
|
await Matterbridge.loadInstance();
|
|
21
|
-
|
|
22
|
-
console.log(cli + 'MAIN: Matterbridge.loadInstance() exited' + rs);
|
|
20
|
+
log.debug('***Matterbridge.loadInstance() exited');
|
|
23
21
|
}
|
|
24
22
|
main().catch((error) => {
|
|
25
|
-
|
|
23
|
+
log.error(`Matterbridge.loadInstance() failed with error: ${error instanceof Error ? error.message : error}`);
|
|
26
24
|
});
|
package/dist/matterbridge.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import EventEmitter from 'events';
|
|
5
|
-
import os from 'os';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { randomBytes } from 'crypto';
|
|
8
|
-
import { NodeStorageManager } from './storage/export.js';
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
4
|
+
import EventEmitter from 'node:events';
|
|
9
5
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt } from './logger/export.js';
|
|
10
|
-
import {
|
|
6
|
+
import { NodeStorageManager } from './storage/export.js';
|
|
7
|
+
import { getParameter, getIntParameter, hasParameter } from './utils/export.js';
|
|
8
|
+
import { logInterfaces, copyDirectory, getNpmPackageVersion, getGlobalNodeModules } from './utils/utils.js';
|
|
11
9
|
import { PluginManager } from './pluginManager.js';
|
|
12
10
|
import { DeviceManager } from './deviceManager.js';
|
|
13
11
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
@@ -86,6 +84,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
86
84
|
bridgeMode = '';
|
|
87
85
|
restartMode = '';
|
|
88
86
|
profile = getParameter('profile');
|
|
87
|
+
shutdown = false;
|
|
89
88
|
edge = true;
|
|
90
89
|
log;
|
|
91
90
|
matterbrideLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
|
|
@@ -174,6 +173,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
174
173
|
return Matterbridge.instance;
|
|
175
174
|
}
|
|
176
175
|
async destroyInstance() {
|
|
176
|
+
this.log.info(`Destroy instance...`);
|
|
177
177
|
const servers = [];
|
|
178
178
|
if (this.bridgeMode === 'bridge') {
|
|
179
179
|
if (this.serverNode)
|
|
@@ -186,6 +186,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
await this.cleanup('destroying instance...', false);
|
|
189
|
+
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
189
190
|
for (const server of servers) {
|
|
190
191
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
191
192
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
@@ -443,7 +444,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
443
444
|
- disable [plugin name]: disable the globally installed plugin with the given name
|
|
444
445
|
- reset [plugin path]: remove the commissioning for the plugin from the given absolute or relative path (childbridge mode). Shutdown Matterbridge before using it!
|
|
445
446
|
- reset [plugin name]: remove the commissioning for the globally installed plugin (childbridge mode). Shutdown Matterbridge before using it!${rs}`);
|
|
446
|
-
this.
|
|
447
|
+
this.shutdown = true;
|
|
447
448
|
return;
|
|
448
449
|
}
|
|
449
450
|
if (hasParameter('list')) {
|
|
@@ -472,7 +473,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
472
473
|
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
473
474
|
}
|
|
474
475
|
});
|
|
475
|
-
this.
|
|
476
|
+
this.shutdown = true;
|
|
476
477
|
return;
|
|
477
478
|
}
|
|
478
479
|
if (hasParameter('logstorage')) {
|
|
@@ -482,41 +483,42 @@ export class Matterbridge extends EventEmitter {
|
|
|
482
483
|
this.log.info(`${plg}${plugin.name}${nf} storage log`);
|
|
483
484
|
await plugin.nodeContext?.logStorage();
|
|
484
485
|
}
|
|
485
|
-
this.
|
|
486
|
+
this.shutdown = true;
|
|
486
487
|
return;
|
|
487
488
|
}
|
|
488
489
|
if (hasParameter('loginterfaces')) {
|
|
489
490
|
this.log.info(`${plg}Matterbridge${nf} network interfaces log`);
|
|
490
491
|
logInterfaces();
|
|
491
|
-
this.
|
|
492
|
+
this.shutdown = true;
|
|
492
493
|
return;
|
|
493
494
|
}
|
|
494
495
|
if (getParameter('add')) {
|
|
495
496
|
this.log.debug(`Adding plugin ${getParameter('add')}`);
|
|
496
497
|
await this.plugins.add(getParameter('add'));
|
|
497
|
-
this.
|
|
498
|
+
this.shutdown = true;
|
|
498
499
|
return;
|
|
499
500
|
}
|
|
500
501
|
if (getParameter('remove')) {
|
|
501
502
|
this.log.debug(`Removing plugin ${getParameter('remove')}`);
|
|
502
503
|
await this.plugins.remove(getParameter('remove'));
|
|
503
|
-
this.
|
|
504
|
+
this.shutdown = true;
|
|
504
505
|
return;
|
|
505
506
|
}
|
|
506
507
|
if (getParameter('enable')) {
|
|
507
508
|
this.log.debug(`Enabling plugin ${getParameter('enable')}`);
|
|
508
509
|
await this.plugins.enable(getParameter('enable'));
|
|
509
|
-
this.
|
|
510
|
+
this.shutdown = true;
|
|
510
511
|
return;
|
|
511
512
|
}
|
|
512
513
|
if (getParameter('disable')) {
|
|
513
514
|
this.log.debug(`Disabling plugin ${getParameter('disable')}`);
|
|
514
515
|
await this.plugins.disable(getParameter('disable'));
|
|
515
|
-
this.
|
|
516
|
+
this.shutdown = true;
|
|
516
517
|
return;
|
|
517
518
|
}
|
|
518
519
|
if (hasParameter('factoryreset')) {
|
|
519
520
|
await this.shutdownProcessAndFactoryReset();
|
|
521
|
+
this.shutdown = true;
|
|
520
522
|
return;
|
|
521
523
|
}
|
|
522
524
|
try {
|
|
@@ -528,6 +530,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
528
530
|
}
|
|
529
531
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
530
532
|
await this.shutdownProcessAndReset();
|
|
533
|
+
this.shutdown = true;
|
|
531
534
|
return;
|
|
532
535
|
}
|
|
533
536
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
@@ -535,20 +538,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
535
538
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
536
539
|
if (plugin) {
|
|
537
540
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
538
|
-
if (!matterStorageManager)
|
|
541
|
+
if (!matterStorageManager) {
|
|
539
542
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
await matterStorageManager?.createContext('events')?.clearAll();
|
|
546
|
+
await matterStorageManager?.createContext('fabrics')?.clearAll();
|
|
547
|
+
await matterStorageManager?.createContext('root')?.clearAll();
|
|
548
|
+
await matterStorageManager?.createContext('sessions')?.clearAll();
|
|
549
|
+
await matterStorageManager?.createContext('persist')?.clearAll();
|
|
550
|
+
this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
|
|
551
|
+
}
|
|
546
552
|
}
|
|
547
553
|
else {
|
|
548
554
|
this.log.warn(`Plugin ${plg}${getParameter('reset')}${wr} not registerd in matterbridge`);
|
|
549
555
|
}
|
|
550
556
|
await this.stopMatterStorage();
|
|
551
|
-
this.
|
|
557
|
+
this.shutdown = true;
|
|
552
558
|
return;
|
|
553
559
|
}
|
|
554
560
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
@@ -720,6 +726,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
720
726
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
721
727
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
722
728
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
729
|
+
const { fileURLToPath } = await import('node:url');
|
|
723
730
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
724
731
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
725
732
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
@@ -728,7 +735,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
728
735
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
729
736
|
if (this.globalModulesDirectory === '') {
|
|
730
737
|
try {
|
|
731
|
-
this.
|
|
738
|
+
this.execRunningCount++;
|
|
739
|
+
this.globalModulesDirectory = await getGlobalNodeModules();
|
|
740
|
+
this.execRunningCount--;
|
|
732
741
|
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
733
742
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
734
743
|
await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
|
|
@@ -797,20 +806,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
797
806
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
798
807
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
799
808
|
}
|
|
800
|
-
async getGlobalNodeModules() {
|
|
801
|
-
return new Promise((resolve, reject) => {
|
|
802
|
-
this.execRunningCount++;
|
|
803
|
-
exec('npm root -g', (error, stdout) => {
|
|
804
|
-
this.execRunningCount--;
|
|
805
|
-
if (error) {
|
|
806
|
-
reject(error);
|
|
807
|
-
}
|
|
808
|
-
else {
|
|
809
|
-
resolve(stdout.trim());
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
809
|
async getMatterbridgeLatestVersion() {
|
|
815
810
|
getNpmPackageVersion('matterbridge')
|
|
816
811
|
.then(async (version) => {
|
|
@@ -1202,11 +1197,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1202
1197
|
if (!plugin.enabled)
|
|
1203
1198
|
continue;
|
|
1204
1199
|
if (plugin.type === 'DynamicPlatform') {
|
|
1205
|
-
plugin
|
|
1206
|
-
plugin.storageContext = await this.createServerNodeContext(plugin.name, 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, plugin.description);
|
|
1207
|
-
plugin.serverNode = await this.createServerNode(plugin.storageContext, this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
|
|
1208
|
-
plugin.aggregatorNode = await this.createAggregatorNode(plugin.storageContext);
|
|
1209
|
-
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
1200
|
+
await this.createDynamicPlugin(plugin);
|
|
1210
1201
|
}
|
|
1211
1202
|
}
|
|
1212
1203
|
await this.startPlugins();
|
|
@@ -1259,7 +1250,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1259
1250
|
for (const plugin of this.plugins) {
|
|
1260
1251
|
if (!plugin.enabled || plugin.error)
|
|
1261
1252
|
continue;
|
|
1262
|
-
if (!plugin.addedDevices || plugin.addedDevices === 0) {
|
|
1253
|
+
if (plugin.type !== 'DynamicPlatform' && (!plugin.addedDevices || plugin.addedDevices === 0)) {
|
|
1263
1254
|
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't add any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1264
1255
|
continue;
|
|
1265
1256
|
}
|
|
@@ -1315,6 +1306,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1315
1306
|
this.log.info('Matter node storage closed');
|
|
1316
1307
|
}
|
|
1317
1308
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1309
|
+
const { randomBytes } = await import('node:crypto');
|
|
1318
1310
|
if (!this.matterStorageService)
|
|
1319
1311
|
throw new Error('No storage service initialized');
|
|
1320
1312
|
this.log.info(`Creating server node storage context "${pluginName}.persist" for ${pluginName}...`);
|
|
@@ -1557,13 +1549,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1557
1549
|
}
|
|
1558
1550
|
}
|
|
1559
1551
|
async advertiseServerNode(matterServerNode) {
|
|
1560
|
-
if (matterServerNode
|
|
1552
|
+
if (matterServerNode) {
|
|
1561
1553
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
1562
1554
|
const { qrPairingCode, manualPairingCode } = matterServerNode.state.commissioning.pairingCodes;
|
|
1563
1555
|
this.log.notice(`Advertising for ${matterServerNode.id} is now started with the following pairing codes: qrPairingCode ${qrPairingCode}, manualPairingCode ${manualPairingCode}`);
|
|
1564
1556
|
return { qrPairingCode, manualPairingCode };
|
|
1565
1557
|
}
|
|
1566
|
-
|
|
1558
|
+
}
|
|
1559
|
+
async stopAdvertiseServerNode(matterServerNode) {
|
|
1560
|
+
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
1561
|
+
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
1562
|
+
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
1563
|
+
}
|
|
1567
1564
|
}
|
|
1568
1565
|
async createAggregatorNode(storageContext) {
|
|
1569
1566
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
@@ -1635,15 +1632,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1635
1632
|
plugin.registeredDevices--;
|
|
1636
1633
|
if (plugin.addedDevices !== undefined)
|
|
1637
1634
|
plugin.addedDevices--;
|
|
1638
|
-
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
1639
|
-
if (plugin.serverNode) {
|
|
1640
|
-
await this.stopServerNode(plugin.serverNode);
|
|
1641
|
-
plugin.locked = false;
|
|
1642
|
-
plugin.aggregatorNode = undefined;
|
|
1643
|
-
plugin.serverNode = undefined;
|
|
1644
|
-
this.log.info(`Stopped server node for plugin ${plg}${pluginName}${nf}`);
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
1635
|
}
|
|
1648
1636
|
this.devices.remove(device);
|
|
1649
1637
|
}
|
|
@@ -1702,7 +1690,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1702
1690
|
getVendorIdName = (vendorId) => {
|
|
1703
1691
|
if (!vendorId)
|
|
1704
1692
|
return '';
|
|
1705
|
-
let vendorName = '';
|
|
1693
|
+
let vendorName = '(Unknown vendorId)';
|
|
1706
1694
|
switch (vendorId) {
|
|
1707
1695
|
case 4937:
|
|
1708
1696
|
vendorName = '(AppleHome)';
|
|
@@ -1732,15 +1720,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1732
1720
|
vendorName = '(eWeLink)';
|
|
1733
1721
|
break;
|
|
1734
1722
|
case 65521:
|
|
1735
|
-
vendorName = '(
|
|
1736
|
-
break;
|
|
1737
|
-
default:
|
|
1738
|
-
vendorName = '(unknown)';
|
|
1723
|
+
vendorName = '(MatterServer)';
|
|
1739
1724
|
break;
|
|
1740
1725
|
}
|
|
1741
1726
|
return vendorName;
|
|
1742
1727
|
};
|
|
1743
1728
|
async spawnCommand(command, args = []) {
|
|
1729
|
+
const { spawn } = await import('node:child_process');
|
|
1744
1730
|
const cmdLine = command + ' ' + args.join(' ');
|
|
1745
1731
|
if (process.platform === 'win32' && command === 'npm') {
|
|
1746
1732
|
const argstring = 'npm ' + args.join(' ');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AnsiLogger, BLUE, CYAN, YELLOW, db, debugStringify, er, hk, or, zb } from './logger/export.js';
|
|
2
2
|
import { bridgedNode } from './matterbridgeDeviceTypes.js';
|
|
3
|
-
import { isValidNumber, isValidObject } from './utils/
|
|
3
|
+
import { isValidNumber, isValidObject } from './utils/export.js';
|
|
4
4
|
import { MatterbridgeBehavior, MatterbridgeBehaviorDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, } from './matterbridgeBehaviors.js';
|
|
5
5
|
import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, capitalizeFirstLetter, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, } from './matterbridgeEndpointHelpers.js';
|
|
6
6
|
import { Endpoint, Lifecycle, MutableEndpoint, NamedHandler, SupportedBehaviors, VendorId } from '@matter/main';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createHash } from 'crypto';
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
2
|
import { BLUE, CYAN, db, debugStringify, er, hk, or, YELLOW, zb } from './logger/export.js';
|
|
3
|
+
import { deepCopy, deepEqual, isValidArray } from './utils/export.js';
|
|
3
4
|
import { MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, } from './matterbridgeBehaviors.js';
|
|
4
5
|
import { Lifecycle } from '@matter/main';
|
|
5
6
|
import { getClusterNameById } from '@matter/main/types';
|
|
@@ -73,7 +74,6 @@ import { Pm25ConcentrationMeasurementServer } from '@matter/main/behaviors/pm25-
|
|
|
73
74
|
import { Pm10ConcentrationMeasurementServer } from '@matter/main/behaviors/pm10-concentration-measurement';
|
|
74
75
|
import { RadonConcentrationMeasurementServer } from '@matter/main/behaviors/radon-concentration-measurement';
|
|
75
76
|
import { TotalVolatileOrganicCompoundsConcentrationMeasurementServer } from '@matter/main/behaviors/total-volatile-organic-compounds-concentration-measurement';
|
|
76
|
-
import { deepCopy, deepEqual, isValidArray } from './utils/utils.js';
|
|
77
77
|
export function capitalizeFirstLetter(name) {
|
|
78
78
|
if (!name)
|
|
79
79
|
return name;
|