matterbridge 3.4.1-dev-20251127-826b2bf → 3.4.1-dev-20251129-ff1e22f

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 CHANGED
@@ -14,8 +14,9 @@ The project will evolve to a multi-threaded architecture (the CLI will become th
14
14
  - frontend;
15
15
  - plugins;
16
16
  - devices;
17
- - check_updates;
18
- - npm_install;
17
+ - check updates;
18
+ - npm install;
19
+ - ✅ get global node_modules;
19
20
  - all plugins in bridge mode;
20
21
  - each plugin in childbridge mode;
21
22
 
@@ -31,7 +32,9 @@ Advantages:
31
32
  ### Added
32
33
 
33
34
  - [matterbridge.io]: Updated website https://matterbridge.io with all guides.
34
- - [matterbridge]: Added addVirtualEndpoint() to match thread module.
35
+ - [matterbridge]: Added addVirtualEndpoint() to match Matterbridge thread module.
36
+ - [BroadcastServer]: Backport BroadcastServer v.2.0.0 from Matterbridge thread module.
37
+ - [MatterbridgePrefix]: Added worker thread to get global node_modules.
35
38
 
36
39
  ### Changed
37
40
 
@@ -39,10 +42,12 @@ Advantages:
39
42
  - [frontend]: Updated dependencies.
40
43
  - [frontend]: Bumped `frontend` version to 3.3.2.
41
44
  - [platform]: Bumped MatterbridgePlatform v.1.5.0.
45
+ - [thread]: Bump BroadcastServer to v1.0.4.
42
46
 
43
47
  ### Fixed
44
48
 
45
49
  - [frontend]: Fixed when the user put special characters in password. Thanks Dabern (https://github.com/Luligu/matterbridge/issues/443).
50
+ - [frontend]: Fixed minimum size to 1300x1024 and add a max width to Url. Thanks Ricardo B. (https://github.com/Luligu/matterbridge-home-assistant-addon/issues/23).
46
51
 
47
52
  <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
48
53
 
package/README-DEV.md CHANGED
@@ -251,9 +251,25 @@ It is called when the plugin config has been updated.
251
251
 
252
252
  Retrieves the devices registered with the platform.
253
253
 
254
+ ### getDeviceByName(deviceName: string): MatterbridgeEndpoint | undefined
255
+
256
+ ### getDeviceByUniqueId(uniqueId: string): MatterbridgeEndpoint | undefined
257
+
258
+ ### getDeviceBySerialNumber(serialNumber: string): MatterbridgeEndpoint | undefined
259
+
260
+ ### getDeviceById(id: string): MatterbridgeEndpoint | undefined
261
+
262
+ ### getDeviceByOriginalId(originalId: string): MatterbridgeEndpoint | undefined
263
+
264
+ ### getDeviceByNumber(number: EndpointNumber | number): MatterbridgeEndpoint | undefined
265
+
266
+ They all return MatterbridgeEndpoint or undefined if not found.
267
+
254
268
  ### hasDeviceName(deviceName: string): boolean
255
269
 
256
- Checks if a device with this name is already registered in the platform.
270
+ ### hasDeviceUniqueId(deviceUniqueId: string): boolean
271
+
272
+ Checks if a device with this name or uniqueId is already registered in the platform.
257
273
 
258
274
  ### async registerDevice(device: MatterbridgeEndpoint)
259
275
 
@@ -288,7 +304,7 @@ You create a Matter device with a new instance of MatterbridgeEndpoint(definitio
288
304
 
289
305
  In the above example we create a contact sensor device type with also a power source device type feature replaceble battery.
290
306
 
291
- All device types are defined in src\matterbridgeDeviceTypes.ts and taken from the 'Matter-1.4.1-Device-Library-Specification.pdf'.
307
+ All device types are defined in src\matterbridgeDeviceTypes.ts and taken from the 'Matter-1.4.2-Device-Library-Specification.pdf'.
292
308
 
293
309
  All default cluster helpers are available as methods of MatterbridgeEndpoint.
294
310
 
@@ -388,7 +404,7 @@ const heatPump = new HeatPump('Heat Pump', 'HP1234567890');
388
404
 
389
405
  ## Plugin config file
390
406
 
391
- Each plugin has a minimal default config file injected by Matterbridge when it is loaded:
407
+ Each plugin has a minimal default config file injected by Matterbridge when it is loaded and the plugin doesn't have its own default one:
392
408
 
393
409
  ```typescript
394
410
  {
@@ -408,7 +424,7 @@ In all subsequent loads the config file is loaded from the '.matterbridge' direc
408
424
 
409
425
  ## Plugin schema file
410
426
 
411
- Each plugin has a minimal default schema file injected by Matterbridge when it is loaded:
427
+ Each plugin has a minimal default schema file injected by Matterbridge when it is loaded and the plugin doesn't have its own default one:
412
428
 
413
429
  ```typescript
414
430
  {
package/README.md CHANGED
@@ -56,6 +56,8 @@ Join us in the Matterbridge [Discord group](https://discord.gg/QX58CDe6hd) creat
56
56
 
57
57
  https://www.youtube.com/watch?v=goNB9Cgh_Fk
58
58
 
59
+ https://www.youtube.com/watch?v=06zzl7o_IqQ
60
+
59
61
  ## Reviews
60
62
 
61
63
  https://www.matteralpha.com/how-to/how-to-configure-an-open-source-matter-bridge-at-home
@@ -2,8 +2,9 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
2
  console.log('\u001B[32mBroadcastServer loaded.\u001B[40;0m');
3
3
  import { EventEmitter } from 'node:events';
4
4
  import { BroadcastChannel } from 'node:worker_threads';
5
- import { CYAN, db, debugStringify } from 'node-ansi-logger';
5
+ import { CYAN, db, debugStringify, er } from 'node-ansi-logger';
6
6
  import { hasParameter } from './utils/commandLine.js';
7
+ import { logError } from './utils/error.js';
7
8
  export class BroadcastServer extends EventEmitter {
8
9
  name;
9
10
  log;
@@ -24,24 +25,64 @@ export class BroadcastServer extends EventEmitter {
24
25
  this.broadcastChannel.close();
25
26
  }
26
27
  getUniqueId() {
27
- return Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000;
28
+ return Math.floor(Math.random() * 900000000) + 100000000;
28
29
  }
29
- isWorkerRequest(msg, type) {
30
- return typeof msg === 'object' && msg !== null && 'id' in msg && 'timestamp' in msg && 'type' in msg && msg.type === type && !('response' in msg) && 'src' in msg && 'dst' in msg;
30
+ isWorkerRequest(value) {
31
+ if (typeof value !== 'object' || value === null) {
32
+ return false;
33
+ }
34
+ const message = value;
35
+ if (typeof message.type !== 'string' || typeof message.src !== 'string' || typeof message.dst !== 'string' || typeof message.id !== 'number' || typeof message.timestamp !== 'number') {
36
+ return false;
37
+ }
38
+ return message.result === undefined && message.error === undefined;
39
+ }
40
+ isWorkerRequestOfType(value, type) {
41
+ return this.isWorkerRequest(value) && value.type === type;
42
+ }
43
+ isWorkerResponse(value) {
44
+ if (typeof value !== 'object' || value === null) {
45
+ return false;
46
+ }
47
+ const message = value;
48
+ if (typeof message.type !== 'string' || typeof message.src !== 'string' || typeof message.dst !== 'string' || typeof message.id !== 'number' || typeof message.timestamp !== 'number') {
49
+ return false;
50
+ }
51
+ const hasError = typeof message.error === 'string';
52
+ const hasResult = message.result !== undefined;
53
+ return hasError !== hasResult;
31
54
  }
32
- isWorkerResponse(msg, type) {
33
- return typeof msg === 'object' && msg !== null && 'id' in msg && 'timestamp' in msg && 'type' in msg && msg.type === type && 'response' in msg && 'src' in msg && 'dst' in msg;
55
+ isWorkerResponseOfType(value, type) {
56
+ return this.isWorkerResponse(value) && value.type === type;
34
57
  }
35
58
  broadcastMessageHandler(event) {
36
- const data = event.data;
37
- if (this.verbose && (data.dst === this.name || data.dst === 'all'))
38
- this.log.debug(`Server ${CYAN}${this.name}${db} received broadcast message: ${debugStringify(data)}`);
39
- this.emit('broadcast_message', data);
59
+ const msg = event.data;
60
+ if (msg.dst === this.name || msg.dst === 'all') {
61
+ if (this.verbose)
62
+ this.log.debug(`Server ${CYAN}${this.name}${db} received broadcast message: ${debugStringify(msg)}`);
63
+ this.emit('broadcast_message', msg);
64
+ }
65
+ else {
66
+ if (this.verbose)
67
+ this.log.debug(`Server ${CYAN}${this.name}${db} received unrelated broadcast message: ${debugStringify(msg)}`);
68
+ }
40
69
  }
41
70
  broadcast(message) {
71
+ if (message.id === undefined) {
72
+ message.id = this.getUniqueId();
73
+ }
74
+ if (message.timestamp === undefined) {
75
+ message.timestamp = Date.now();
76
+ }
77
+ message.src = this.name;
42
78
  if (this.verbose)
43
79
  this.log.debug(`Broadcasting message: ${debugStringify(message)}`);
44
- this.broadcastChannel.postMessage(message);
80
+ try {
81
+ this.broadcastChannel.postMessage(message);
82
+ }
83
+ catch (error) {
84
+ logError(this.log, `Failed to broadcast message ${debugStringify(message)}${er}`, error);
85
+ }
45
86
  }
46
87
  request(message) {
47
88
  if (message.id === undefined) {
@@ -50,25 +91,43 @@ export class BroadcastServer extends EventEmitter {
50
91
  if (message.timestamp === undefined) {
51
92
  message.timestamp = Date.now();
52
93
  }
53
- if (!this.isWorkerRequest(message, message.type)) {
54
- this.log.error(`Invalid request message format for broadcast: ${debugStringify(message)}`);
94
+ message.src = this.name;
95
+ if (!this.isWorkerRequest(message)) {
96
+ this.log.error(`Invalid request message format: ${debugStringify(message)}`);
55
97
  return;
56
98
  }
57
99
  if (this.verbose)
58
100
  this.log.debug(`Broadcasting request message: ${debugStringify(message)}`);
59
- this.broadcastChannel.postMessage(message);
101
+ try {
102
+ this.broadcastChannel.postMessage(message);
103
+ }
104
+ catch (error) {
105
+ logError(this.log, `Failed to broadcast request message ${debugStringify(message)}${er}`, error);
106
+ }
60
107
  }
61
108
  respond(message) {
109
+ if (typeof message.timestamp === 'number') {
110
+ message.elapsed = Date.now() - message.timestamp;
111
+ }
62
112
  if (message.timestamp === undefined) {
63
113
  message.timestamp = Date.now();
64
114
  }
65
- if (!this.isWorkerResponse(message, message.type)) {
66
- this.log.error(`Invalid response message format for broadcast: ${debugStringify(message)}`);
115
+ if (message.dst === this.name || message.dst === 'all') {
116
+ message.dst = message.src;
117
+ }
118
+ message.src = this.name;
119
+ if (!this.isWorkerResponse(message)) {
120
+ this.log.error(`Invalid response message format: ${debugStringify(message)}`);
67
121
  return;
68
122
  }
69
123
  if (this.verbose)
70
124
  this.log.debug(`Broadcasting response message: ${debugStringify(message)}`);
71
- this.broadcastChannel.postMessage(message);
125
+ try {
126
+ this.broadcastChannel.postMessage(message);
127
+ }
128
+ catch (error) {
129
+ logError(this.log, `Failed to broadcast response message ${debugStringify(message)}${er}`, error);
130
+ }
72
131
  }
73
132
  async fetch(message, timeout = 250) {
74
133
  if (message.id === undefined) {
@@ -81,23 +140,28 @@ export class BroadcastServer extends EventEmitter {
81
140
  this.log.debug(`Fetching message: ${debugStringify(message)}`);
82
141
  return new Promise((resolve, reject) => {
83
142
  const responseHandler = (msg) => {
84
- if (this.isWorkerResponse(msg, message.type) && msg.id === message.id) {
143
+ if (this.isWorkerResponseOfType(msg, message.type) && msg.id === message.id) {
85
144
  clearTimeout(timeoutId);
86
145
  this.off('broadcast_message', responseHandler);
87
146
  if (this.verbose)
88
147
  this.log.debug(`Fetch response: ${debugStringify(msg)}`);
89
- resolve(msg);
90
- }
91
- else if (this.isWorkerResponse(msg, message.type) && msg.id !== message.id) {
92
- if (this.verbose)
93
- this.log.debug(`Fetch received unrelated response: ${debugStringify(msg)}`);
148
+ if ('error' in msg && typeof msg.error === 'string') {
149
+ reject(new Error(`Fetch received error response ${msg.error} to message type ${message.type} id ${message.id} from ${message.src} to ${message.dst}`));
150
+ }
151
+ else if ('result' in msg) {
152
+ resolve(msg);
153
+ }
154
+ else {
155
+ reject(new Error(`Fetch received malformed response for message type ${message.type} id ${message.id} from ${message.src} to ${message.dst}`));
156
+ }
157
+ return;
94
158
  }
95
159
  };
96
160
  this.on('broadcast_message', responseHandler);
97
161
  this.request(message);
98
162
  const timeoutId = setTimeout(() => {
99
163
  this.off('broadcast_message', responseHandler);
100
- reject(new Error(`Fetch timeout after ${timeout}ms for message id ${message.id}`));
164
+ reject(new Error(`Fetch timeout after ${timeout}ms for message type ${message.type} id ${message.id} from ${message.src} to ${message.dst}`));
101
165
  }, timeout);
102
166
  });
103
167
  }
@@ -44,44 +44,44 @@ export class DeviceManager {
44
44
  this.server.close();
45
45
  }
46
46
  async msgHandler(msg) {
47
- if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'devices')) {
47
+ if (this.server.isWorkerRequest(msg) && (msg.dst === 'all' || msg.dst === 'devices')) {
48
48
  if (this.verbose)
49
49
  this.log.debug(`Received request message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
50
50
  switch (msg.type) {
51
51
  case 'get_log_level':
52
- this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
52
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
53
53
  break;
54
54
  case 'set_log_level':
55
55
  this.log.logLevel = msg.params.logLevel;
56
- this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
56
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
57
57
  break;
58
58
  case 'devices_length':
59
- this.server.respond({ ...msg, response: { length: this.length } });
59
+ this.server.respond({ ...msg, result: { length: this.length } });
60
60
  break;
61
61
  case 'devices_size':
62
- this.server.respond({ ...msg, response: { size: this.size } });
62
+ this.server.respond({ ...msg, result: { size: this.size } });
63
63
  break;
64
64
  case 'devices_has':
65
- this.server.respond({ ...msg, response: { has: this.has(msg.params.uniqueId) } });
65
+ this.server.respond({ ...msg, result: { has: this.has(msg.params.uniqueId) } });
66
66
  break;
67
67
  case 'devices_get':
68
68
  {
69
69
  const endpoint = this.get(msg.params.uniqueId);
70
- this.server.respond({ ...msg, response: { device: endpoint ? toBaseDevice(endpoint) : undefined } });
70
+ this.server.respond({ ...msg, result: { device: endpoint ? toBaseDevice(endpoint) : undefined } });
71
71
  }
72
72
  break;
73
73
  case 'devices_set':
74
- this.server.respond({ ...msg, response: { device: this.set(toBaseDevice(msg.params.device)) } });
74
+ this.server.respond({ ...msg, result: { device: this.set(toBaseDevice(msg.params.device)) } });
75
75
  break;
76
76
  case 'devices_remove':
77
- this.server.respond({ ...msg, response: { success: this.remove(toBaseDevice(msg.params.device)) } });
77
+ this.server.respond({ ...msg, result: { success: this.remove(toBaseDevice(msg.params.device)) } });
78
78
  break;
79
79
  case 'devices_clear':
80
80
  this.clear();
81
- this.server.respond({ ...msg, response: { success: true } });
81
+ this.server.respond({ ...msg, result: { success: true } });
82
82
  break;
83
83
  case 'devices_basearray':
84
- this.server.respond({ ...msg, response: { devices: this.baseArray(msg.params.pluginName) } });
84
+ this.server.respond({ ...msg, result: { devices: this.baseArray(msg.params.pluginName) } });
85
85
  break;
86
86
  default:
87
87
  if (this.verbose)
package/dist/frontend.js CHANGED
@@ -46,90 +46,88 @@ export class Frontend extends EventEmitter {
46
46
  this.server.close();
47
47
  }
48
48
  async msgHandler(msg) {
49
- if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'frontend')) {
49
+ if (this.server.isWorkerRequest(msg) && (msg.dst === 'all' || msg.dst === 'frontend')) {
50
50
  if (this.verbose)
51
51
  this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
52
52
  switch (msg.type) {
53
53
  case 'get_log_level':
54
- this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
54
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
55
55
  break;
56
56
  case 'set_log_level':
57
57
  this.log.logLevel = msg.params.logLevel;
58
- this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
58
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
59
59
  break;
60
60
  case 'frontend_start':
61
61
  await this.start(msg.params.port);
62
- this.server.respond({ ...msg, response: { success: true } });
62
+ this.server.respond({ ...msg, result: { success: true } });
63
63
  break;
64
64
  case 'frontend_stop':
65
65
  await this.stop();
66
- this.server.respond({ ...msg, response: { success: true } });
66
+ this.server.respond({ ...msg, result: { success: true } });
67
67
  break;
68
68
  case 'frontend_refreshrequired':
69
- this.wssSendRefreshRequired(msg.params.changed, { matter: msg.params.matter });
70
- this.server.respond({ ...msg, response: { success: true } });
69
+ this.wssSendRefreshRequired(msg.params.changed, msg.params.matter ? { matter: msg.params.matter } : undefined);
70
+ this.server.respond({ ...msg, result: { success: true } });
71
71
  break;
72
72
  case 'frontend_restartrequired':
73
73
  this.wssSendRestartRequired(msg.params.snackbar, msg.params.fixed);
74
- this.server.respond({ ...msg, response: { success: true } });
74
+ this.server.respond({ ...msg, result: { success: true } });
75
75
  break;
76
76
  case 'frontend_restartnotrequired':
77
77
  this.wssSendRestartNotRequired(msg.params.snackbar);
78
- this.server.respond({ ...msg, response: { success: true } });
78
+ this.server.respond({ ...msg, result: { success: true } });
79
79
  break;
80
80
  case 'frontend_updaterequired':
81
81
  this.wssSendUpdateRequired(msg.params.devVersion);
82
- this.server.respond({ ...msg, response: { success: true } });
82
+ this.server.respond({ ...msg, result: { success: true } });
83
83
  break;
84
84
  case 'frontend_snackbarmessage':
85
85
  this.wssSendSnackbarMessage(msg.params.message, msg.params.timeout, msg.params.severity);
86
- this.server.respond({ ...msg, response: { success: true } });
86
+ this.server.respond({ ...msg, result: { success: true } });
87
87
  break;
88
88
  case 'frontend_attributechanged':
89
89
  this.wssSendAttributeChangedMessage(msg.params.plugin, msg.params.serialNumber, msg.params.uniqueId, msg.params.number, msg.params.id, msg.params.cluster, msg.params.attribute, msg.params.value);
90
- this.server.respond({ ...msg, response: { success: true } });
90
+ this.server.respond({ ...msg, result: { success: true } });
91
91
  break;
92
92
  case 'frontend_logmessage':
93
93
  this.wssSendLogMessage(msg.params.level, msg.params.time, msg.params.name, msg.params.message);
94
- this.server.respond({ ...msg, response: { success: true } });
94
+ this.server.respond({ ...msg, result: { success: true } });
95
+ break;
96
+ case 'frontend_broadcast_message':
97
+ this.wssBroadcastMessage(msg.params.msg);
98
+ this.server.respond({ ...msg, result: { success: true } });
95
99
  break;
96
100
  default:
97
101
  if (this.verbose)
98
102
  this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
99
103
  }
100
104
  }
101
- if (this.server.isWorkerResponse(msg, msg.type)) {
105
+ if (this.server.isWorkerResponse(msg) && msg.result && (msg.dst === 'all' || msg.dst === 'frontend')) {
102
106
  if (this.verbose)
103
107
  this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
104
108
  switch (msg.type) {
105
- case 'get_log_level':
106
- case 'set_log_level':
107
- break;
108
109
  case 'plugins_install':
109
- this.wssSendCloseSnackbarMessage(`Installing package ${msg.response.packageName}...`);
110
- if (msg.response.success) {
110
+ this.wssSendCloseSnackbarMessage(`Installing package ${msg.result.packageName}...`);
111
+ if (msg.result.success) {
111
112
  this.wssSendRestartRequired(true, true);
112
113
  this.wssSendRefreshRequired('plugins');
113
- this.wssSendSnackbarMessage(`Installed package ${msg.response.packageName}`, 5, 'success');
114
+ this.wssSendSnackbarMessage(`Installed package ${msg.result.packageName}`, 5, 'success');
114
115
  }
115
116
  else {
116
- this.wssSendSnackbarMessage(`Package ${msg.response.packageName} not installed`, 10, 'error');
117
+ this.wssSendSnackbarMessage(`Package ${msg.result.packageName} not installed`, 10, 'error');
117
118
  }
118
119
  break;
119
120
  case 'plugins_uninstall':
120
- this.wssSendCloseSnackbarMessage(`Uninstalling package ${msg.response.packageName}...`);
121
- if (msg.response.success) {
121
+ this.wssSendCloseSnackbarMessage(`Uninstalling package ${msg.result.packageName}...`);
122
+ if (msg.result.success) {
122
123
  this.wssSendRestartRequired(true, true);
123
124
  this.wssSendRefreshRequired('plugins');
124
- this.wssSendSnackbarMessage(`Uninstalled package ${msg.response.packageName}`, 5, 'success');
125
+ this.wssSendSnackbarMessage(`Uninstalled package ${msg.result.packageName}`, 5, 'success');
125
126
  }
126
127
  else {
127
- this.wssSendSnackbarMessage(`Package ${msg.response.packageName} not uninstalled`, 10, 'error');
128
+ this.wssSendSnackbarMessage(`Package ${msg.result.packageName} not uninstalled`, 10, 'error');
128
129
  }
129
130
  break;
130
- default:
131
- if (this.verbose)
132
- this.log.debug(`Unknown broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
133
131
  }
134
132
  }
135
133
  }
@@ -11,6 +11,9 @@ import { NodeStorageManager } from 'node-persist-manager';
11
11
  import { Matterbridge } from '../matterbridge.js';
12
12
  import { MATTER_STORAGE_NAME, NODE_STORAGE_DIR } from '../matterbridgeTypes.js';
13
13
  import { bridge } from '../matterbridgeDeviceTypes.js';
14
+ import { PluginManager } from '../pluginManager.js';
15
+ import { Frontend } from '../frontend.js';
16
+ import { BroadcastServer } from '../broadcastServer.js';
14
17
  export const originalProcessArgv = Object.freeze([...process.argv]);
15
18
  export const originalProcessEnv = Object.freeze({ ...process.env });
16
19
  export let loggerLogSpy;
@@ -28,6 +31,30 @@ export let consoleErrorSpy;
28
31
  export let addBridgedEndpointSpy;
29
32
  export let removeBridgedEndpointSpy;
30
33
  export let removeAllBridgedEndpointsSpy;
34
+ export let addVirtualEndpointSpy;
35
+ export let installPluginSpy;
36
+ export let uninstallPluginSpy;
37
+ export let addPluginSpy;
38
+ export let loadPluginSpy;
39
+ export let startPluginSpy;
40
+ export let configurePluginSpy;
41
+ export let shutdownPluginSpy;
42
+ export let removePluginSpy;
43
+ export let enablePluginSpy;
44
+ export let disablePluginSpy;
45
+ export let wssSendSnackbarMessageSpy;
46
+ export let wssSendCloseSnackbarMessageSpy;
47
+ export let wssSendUpdateRequiredSpy;
48
+ export let wssSendRefreshRequiredSpy;
49
+ export let wssSendRestartRequiredSpy;
50
+ export let wssSendRestartNotRequiredSpy;
51
+ export let broadcastServerIsWorkerRequestSpy;
52
+ export let broadcastServerIsWorkerResponseSpy;
53
+ export let broadcastServerBroadcastSpy;
54
+ export let broadcastServerRequestSpy;
55
+ export let broadcastServerRespondSpy;
56
+ export let broadcastServerFetchSpy;
57
+ export let broadcastMessageHandlerSpy;
31
58
  export let NAME;
32
59
  export let HOMEDIR;
33
60
  export let matterbridge;
@@ -71,6 +98,30 @@ export async function setupTest(name, debug = false) {
71
98
  addBridgedEndpointSpy = jest.spyOn(Matterbridge.prototype, 'addBridgedEndpoint');
72
99
  removeBridgedEndpointSpy = jest.spyOn(Matterbridge.prototype, 'removeBridgedEndpoint');
73
100
  removeAllBridgedEndpointsSpy = jest.spyOn(Matterbridge.prototype, 'removeAllBridgedEndpoints');
101
+ addVirtualEndpointSpy = jest.spyOn(Matterbridge.prototype, 'addVirtualEndpoint');
102
+ installPluginSpy = jest.spyOn(PluginManager.prototype, 'install');
103
+ uninstallPluginSpy = jest.spyOn(PluginManager.prototype, 'uninstall');
104
+ addPluginSpy = jest.spyOn(PluginManager.prototype, 'add');
105
+ loadPluginSpy = jest.spyOn(PluginManager.prototype, 'load');
106
+ startPluginSpy = jest.spyOn(PluginManager.prototype, 'start');
107
+ configurePluginSpy = jest.spyOn(PluginManager.prototype, 'configure');
108
+ shutdownPluginSpy = jest.spyOn(PluginManager.prototype, 'shutdown');
109
+ removePluginSpy = jest.spyOn(PluginManager.prototype, 'remove');
110
+ enablePluginSpy = jest.spyOn(PluginManager.prototype, 'enable');
111
+ disablePluginSpy = jest.spyOn(PluginManager.prototype, 'disable');
112
+ wssSendSnackbarMessageSpy = jest.spyOn(Frontend.prototype, 'wssSendSnackbarMessage');
113
+ wssSendCloseSnackbarMessageSpy = jest.spyOn(Frontend.prototype, 'wssSendCloseSnackbarMessage');
114
+ wssSendUpdateRequiredSpy = jest.spyOn(Frontend.prototype, 'wssSendUpdateRequired');
115
+ wssSendRefreshRequiredSpy = jest.spyOn(Frontend.prototype, 'wssSendRefreshRequired');
116
+ wssSendRestartRequiredSpy = jest.spyOn(Frontend.prototype, 'wssSendRestartRequired');
117
+ wssSendRestartNotRequiredSpy = jest.spyOn(Frontend.prototype, 'wssSendRestartNotRequired');
118
+ broadcastServerIsWorkerRequestSpy = jest.spyOn(BroadcastServer.prototype, 'isWorkerRequest');
119
+ broadcastServerIsWorkerResponseSpy = jest.spyOn(BroadcastServer.prototype, 'isWorkerResponse');
120
+ broadcastServerBroadcastSpy = jest.spyOn(BroadcastServer.prototype, 'broadcast');
121
+ broadcastServerRequestSpy = jest.spyOn(BroadcastServer.prototype, 'request');
122
+ broadcastServerRespondSpy = jest.spyOn(BroadcastServer.prototype, 'respond');
123
+ broadcastServerFetchSpy = jest.spyOn(BroadcastServer.prototype, 'fetch');
124
+ broadcastMessageHandlerSpy = jest.spyOn(BroadcastServer.prototype, 'broadcastMessageHandler');
74
125
  }
75
126
  export async function setDebug(debug) {
76
127
  const { jest } = await import('@jest/globals');
@@ -92,23 +92,23 @@ export class MatterNode extends EventEmitter {
92
92
  this.log.debug(`MatterNode ${this.pluginName ? 'for plugin ' + this.pluginName : 'bridge'} loaded`);
93
93
  }
94
94
  async msgHandler(msg) {
95
- if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'matter')) {
95
+ if (this.server.isWorkerRequest(msg) && (msg.dst === 'all' || msg.dst === 'matter')) {
96
96
  if (this.verbose)
97
97
  this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
98
98
  switch (msg.type) {
99
99
  case 'get_log_level':
100
- this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
100
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
101
101
  break;
102
102
  case 'set_log_level':
103
103
  this.log.logLevel = msg.params.logLevel;
104
- this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
104
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
105
105
  break;
106
106
  default:
107
107
  if (this.verbose)
108
108
  this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
109
109
  }
110
110
  }
111
- if (this.server.isWorkerResponse(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'matter')) {
111
+ if (this.server.isWorkerResponse(msg) && (msg.dst === 'all' || msg.dst === 'matter')) {
112
112
  if (this.verbose)
113
113
  this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
114
114
  switch (msg.type) {
@@ -669,6 +669,7 @@ export class MatterNode extends EventEmitter {
669
669
  await device.construction.ready;
670
670
  await this.subscribeAttributeChanged(plugin, device);
671
671
  this.log.info(`Added endpoint #${plugin.registeredDevices} ${plg}${pluginName}${nf}:${dev}${device.deviceName}${nf} (${zb}${device.name}${nf})`);
672
+ await this.yieldToNode(10);
672
673
  return device;
673
674
  }
674
675
  async removeBridgedEndpoint(pluginName, device) {
@@ -697,6 +698,7 @@ export class MatterNode extends EventEmitter {
697
698
  if (plugin.registeredDevices !== undefined)
698
699
  plugin.registeredDevices--;
699
700
  await this.server.fetch({ type: 'devices_remove', src: this.server.name, dst: 'devices', params: { device: toBaseDevice(device) } });
701
+ await this.yieldToNode(10);
700
702
  return device;
701
703
  }
702
704
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
@@ -704,7 +706,7 @@ export class MatterNode extends EventEmitter {
704
706
  if (!plugin)
705
707
  throw new Error(`Error removing all bridged endpoints for plugin ${plg}${pluginName}${er}: plugin not found`);
706
708
  this.log.debug(`Removing all #${plugin.registeredDevices} bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}...`);
707
- const devices = (await this.server.fetch({ type: 'devices_basearray', src: this.server.name, dst: 'devices', params: { pluginName } })).response.devices;
709
+ const devices = (await this.server.fetch({ type: 'devices_basearray', src: this.server.name, dst: 'devices', params: { pluginName } })).result.devices;
708
710
  for (const device of devices) {
709
711
  const endpoint = (this.aggregatorNode?.parts.get(device.id || '') || this.serverNode?.parts.get(device.id || ''));
710
712
  if (!endpoint)
@@ -715,6 +717,7 @@ export class MatterNode extends EventEmitter {
715
717
  if (plugin.registeredDevices !== undefined)
716
718
  plugin.registeredDevices--;
717
719
  await this.server.fetch({ type: 'devices_remove', src: this.server.name, dst: 'devices', params: { device: toBaseDevice(device) } });
720
+ await this.yieldToNode(10);
718
721
  if (delay > 0)
719
722
  await wait(delay);
720
723
  }
@@ -742,6 +745,7 @@ export class MatterNode extends EventEmitter {
742
745
  }
743
746
  await addVirtualDevice(this.aggregatorNode, name.slice(0, 32), type, callback);
744
747
  this.log.debug(`Created virtual device ${plg}${pluginName}${db}:${dev}${name}${db}`);
748
+ await this.yieldToNode(10);
745
749
  return true;
746
750
  }
747
751
  async subscribeAttributeChanged(plugin, device) {