@matterbridge/core 3.8.0-dev-20260530-1971f9d → 3.8.0-dev-20260601-a1bf774
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/dist/behaviors/bindingServer.d.ts +7 -2
- package/dist/behaviors/bindingServer.js +38 -4
- package/dist/frontend.js +46 -1
- package/dist/matterbridgeDeviceTypes.js +42 -7
- package/dist/matterbridgeEndpoint.d.ts +5 -0
- package/dist/matterbridgeEndpoint.js +22 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +3 -0
- package/dist/matterbridgeEndpointHelpers.js +39 -0
- package/dist/matterbridgePlatform.d.ts +1 -0
- package/dist/matterbridgePlatform.js +4 -0
- package/dist/pluginManager.js +5 -0
- package/package.json +6 -6
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Endpoint } from '@matter/main/node';
|
|
2
|
+
import { BindingBehavior, BindingServer } from '@matter/node/behaviors/binding';
|
|
2
3
|
import { ClusterId } from '@matter/types';
|
|
3
|
-
|
|
4
|
+
import { Binding } from '@matter/types/clusters/binding';
|
|
5
|
+
export declare class MatterbridgeBindingServer extends BindingServer {
|
|
4
6
|
protected internal: MatterbridgeBindingServer.Internal;
|
|
5
7
|
state: MatterbridgeBindingServer.State;
|
|
6
8
|
initialize(): Promise<void>;
|
|
9
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
10
|
+
getEndpoint(clusterId: ClusterId): Endpoint | undefined;
|
|
7
11
|
}
|
|
8
12
|
export declare namespace MatterbridgeBindingServer {
|
|
9
13
|
class Internal {
|
|
10
14
|
bound: boolean;
|
|
15
|
+
boundEndpoints: Map<Binding.Target, Endpoint>;
|
|
11
16
|
}
|
|
12
17
|
class State extends BindingBehavior.State {
|
|
13
18
|
clientList: ClusterId[];
|
|
@@ -1,19 +1,53 @@
|
|
|
1
|
-
import { BindingBehavior } from '@matter/node/behaviors/binding';
|
|
1
|
+
import { BindingBehavior, BindingServer } from '@matter/node/behaviors/binding';
|
|
2
|
+
import { DescriptorServer } from '@matter/node/behaviors/descriptor';
|
|
3
|
+
import { debugStringify, nt } from 'node-ansi-logger';
|
|
2
4
|
import { MatterbridgeServer } from './matterbridgeServer.js';
|
|
3
|
-
export class MatterbridgeBindingServer extends
|
|
5
|
+
export class MatterbridgeBindingServer extends BindingServer {
|
|
4
6
|
async initialize() {
|
|
7
|
+
super.initialize();
|
|
5
8
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
6
9
|
device.log.info(`Initializing MatterbridgeBindingServer (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}) with clientList: ${this.state.clientList.join(', ')}`);
|
|
10
|
+
const clientList = this.state.clientList;
|
|
11
|
+
this.endpoint.construction.onSuccess(async () => {
|
|
12
|
+
const currentClientList = this.endpoint.stateOf(DescriptorServer).clientList;
|
|
13
|
+
const toAddClientList = clientList.filter((id) => !currentClientList.includes(id));
|
|
14
|
+
if (toAddClientList.length > 0) {
|
|
15
|
+
await this.endpoint.setStateOf(DescriptorServer, { clientList: [...currentClientList, ...toAddClientList] });
|
|
16
|
+
device.log.info(`Adding client clusters to endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}: ${toAddClientList.join(', ')}`);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
7
19
|
this.reactTo(this.events.binding$Changed, (value) => {
|
|
8
20
|
this.internal.bound = value.length > 0;
|
|
9
|
-
device.log.notice(`MatterbridgeBindingServer (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}) binding changed: ${value}, bound: ${this.internal.bound}`);
|
|
21
|
+
device.log.notice(`MatterbridgeBindingServer (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}) binding changed: ${debugStringify(value)}${nt}, bound: ${this.internal.bound}`);
|
|
22
|
+
});
|
|
23
|
+
this.reactTo(this.events.established, async (resolution) => {
|
|
24
|
+
device.log.notice(`MatterbridgeBindingServer (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}) binding established: kind ${resolution.kind} entry ${debugStringify(resolution.entry)}${nt}`);
|
|
25
|
+
this.internal.boundEndpoints.set(resolution.entry, resolution.endpoint);
|
|
26
|
+
if (resolution.kind !== 'client')
|
|
27
|
+
return;
|
|
28
|
+
await resolution.node.set({ network: { autoSubscribe: true } });
|
|
29
|
+
});
|
|
30
|
+
this.reactTo(this.events.removed, async (resolution) => {
|
|
31
|
+
device.log.notice(`MatterbridgeBindingServer (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}) binding removed: kind ${resolution.kind} entry ${debugStringify(resolution.entry)}${nt}`);
|
|
32
|
+
this.internal.boundEndpoints.delete(resolution.entry);
|
|
10
33
|
});
|
|
11
|
-
|
|
34
|
+
}
|
|
35
|
+
async [Symbol.asyncDispose]() {
|
|
36
|
+
this.internal.boundEndpoints.clear();
|
|
37
|
+
await super[Symbol.asyncDispose]();
|
|
38
|
+
}
|
|
39
|
+
getEndpoint(clusterId) {
|
|
40
|
+
for (const [target, endpoint] of this.internal.boundEndpoints) {
|
|
41
|
+
if (target.cluster === clusterId)
|
|
42
|
+
return endpoint;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
12
45
|
}
|
|
13
46
|
}
|
|
14
47
|
(function (MatterbridgeBindingServer) {
|
|
15
48
|
class Internal {
|
|
16
49
|
bound = false;
|
|
50
|
+
boundEndpoints = new Map();
|
|
17
51
|
}
|
|
18
52
|
MatterbridgeBindingServer.Internal = Internal;
|
|
19
53
|
class State extends BindingBehavior.State {
|
package/dist/frontend.js
CHANGED
|
@@ -14,10 +14,11 @@ import { inspectError } from '@matterbridge/utils/error';
|
|
|
14
14
|
import { formatBytes, formatPercent, formatUptime } from '@matterbridge/utils/format';
|
|
15
15
|
import { isValidArray, isValidBoolean, isValidNumber, isValidObject, isValidString } from '@matterbridge/utils/validate';
|
|
16
16
|
import { fireAndForget, wait, withTimeout } from '@matterbridge/utils/wait';
|
|
17
|
-
import { AnsiLogger, CYAN, db, debugStringify, er, nf, nt, rs, stringify, UNDERLINE, UNDERLINEOFF, YELLOW } from 'node-ansi-logger';
|
|
17
|
+
import { AnsiLogger, bgHex, CYAN, db, debugStringify, er, GREEN, nf, nt, rs, stringify, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from 'node-ansi-logger';
|
|
18
18
|
import { cliEmitter, lastOsCpuUsage, lastProcessCpuUsage } from './cliEmitter.js';
|
|
19
19
|
import { generateHistoryPage } from './cliHistory.js';
|
|
20
20
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
21
|
+
import { logError } from './utils/export.js';
|
|
21
22
|
if (hasParameter('loader'))
|
|
22
23
|
console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
|
|
23
24
|
export class Frontend extends EventEmitter {
|
|
@@ -801,6 +802,50 @@ export class Frontend extends EventEmitter {
|
|
|
801
802
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
802
803
|
}
|
|
803
804
|
});
|
|
805
|
+
for (const plugin of this.matterbridge.plugins.array().filter((p) => p.enabled && !p.error)) {
|
|
806
|
+
const { existsSync } = await import('node:fs');
|
|
807
|
+
if (plugin.frontendPath && existsSync(plugin.frontendPath)) {
|
|
808
|
+
this.log.debug(`Registering frontend route for plugin ${plg}${plugin.name}${db} at ${GREEN}/plugins/${plugin.name}${db} with path ${CYAN}${plugin.frontendPath}${db}`);
|
|
809
|
+
this.expressApp.use(`/plugins/${plugin.name}`, express.static(path.dirname(plugin.frontendPath)));
|
|
810
|
+
const apiHandler = async (req, res) => {
|
|
811
|
+
const method = req.method.toUpperCase();
|
|
812
|
+
const path = Array.isArray(req.params.path) ? req.params.path[0] : req.params.path;
|
|
813
|
+
const query = req.query;
|
|
814
|
+
const body = req.body;
|
|
815
|
+
this.log.debug(`The frontend sent ${bgHex('#0ba7bc').bold.white ` ${method} `} ${bgHex('#0b6a2b').bold.italic.white ` /plugins/${plugin.name}/api/${path ?? ''} `}`);
|
|
816
|
+
if (!plugin.platform) {
|
|
817
|
+
this.log.error(`The platform for plugin ${plg}${plugin.name}${er} is not running`);
|
|
818
|
+
res.status(503).json({ error: `Plugin ${plugin.name} platform is not running` });
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
try {
|
|
822
|
+
const value = await plugin.platform.onFetch(method, path, query, body);
|
|
823
|
+
if (value === undefined) {
|
|
824
|
+
this.log.warn(`MatterbridgePlatform.onFetch returned undefined for plugin ${plg}${plugin.name}${wr} method ${method} path ${path}`);
|
|
825
|
+
res.status(404).json({ error: `Not found in plugin ${plugin.name}` });
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (method === 'DELETE')
|
|
829
|
+
res.status(204).send();
|
|
830
|
+
else
|
|
831
|
+
res.json(value);
|
|
832
|
+
}
|
|
833
|
+
catch (error) {
|
|
834
|
+
logError(this.log, `MatterbridgePlatform.onFetch threw an error for plugin ${plg}${plugin.name}${er}`, error);
|
|
835
|
+
res.status(500).json({ error: `Internal error in plugin ${plugin.name}` });
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
this.expressApp.get(`/plugins/${plugin.name}/api/:path`, apiHandler);
|
|
839
|
+
this.expressApp.post(`/plugins/${plugin.name}/api/:path`, apiHandler);
|
|
840
|
+
this.expressApp.put(`/plugins/${plugin.name}/api/:path`, apiHandler);
|
|
841
|
+
this.expressApp.patch(`/plugins/${plugin.name}/api/:path`, apiHandler);
|
|
842
|
+
this.expressApp.delete(`/plugins/${plugin.name}/api/:path`, apiHandler);
|
|
843
|
+
this.expressApp.get(`/plugins/${plugin.name}/{*splat}`, (_req, res) => {
|
|
844
|
+
this.log.warn(`The plugin frontend sent /plugins/${plugin.name}/index.html (plugin SPA fallback)`);
|
|
845
|
+
res.sendFile('index.html', { root: path.dirname(plugin.frontendPath) });
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
}
|
|
804
849
|
this.expressApp.use((req, res) => {
|
|
805
850
|
const filePath = path.resolve(this.matterbridge.rootDirectory, 'apps', 'frontend', 'build');
|
|
806
851
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
|
|
@@ -94,6 +94,8 @@ import { TemperatureControl } from '@matter/types/clusters/temperature-control';
|
|
|
94
94
|
import { TemperatureMeasurement } from '@matter/types/clusters/temperature-measurement';
|
|
95
95
|
import { Thermostat } from '@matter/types/clusters/thermostat';
|
|
96
96
|
import { ThermostatUserInterfaceConfiguration } from '@matter/types/clusters/thermostat-user-interface-configuration';
|
|
97
|
+
import { TlsCertificateManagement } from '@matter/types/clusters/tls-certificate-management';
|
|
98
|
+
import { TlsClientManagement } from '@matter/types/clusters/tls-client-management';
|
|
97
99
|
import { TotalVolatileOrganicCompoundsConcentrationMeasurement } from '@matter/types/clusters/total-volatile-organic-compounds-concentration-measurement';
|
|
98
100
|
import { UserLabel } from '@matter/types/clusters/user-label';
|
|
99
101
|
import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
|
|
@@ -169,8 +171,8 @@ export const OTAProvider = DeviceTypeDefinition({
|
|
|
169
171
|
revision: 1,
|
|
170
172
|
requiredServerClusters: [OtaSoftwareUpdateProvider.id],
|
|
171
173
|
optionalServerClusters: [],
|
|
172
|
-
requiredClientClusters: [
|
|
173
|
-
optionalClientClusters: [],
|
|
174
|
+
requiredClientClusters: [],
|
|
175
|
+
optionalClientClusters: [OtaSoftwareUpdateRequestor.id],
|
|
174
176
|
});
|
|
175
177
|
export const bridgedNode = DeviceTypeDefinition({
|
|
176
178
|
name: 'MA-bridgedNode',
|
|
@@ -195,6 +197,8 @@ export const deviceEnergyManagement = DeviceTypeDefinition({
|
|
|
195
197
|
revision: 3,
|
|
196
198
|
requiredServerClusters: [DeviceEnergyManagement.id],
|
|
197
199
|
optionalServerClusters: [DeviceEnergyManagementMode.id],
|
|
200
|
+
requiredClientClusters: [],
|
|
201
|
+
optionalClientClusters: [ElectricalGridConditions.id],
|
|
198
202
|
});
|
|
199
203
|
export const onOffLight = DeviceTypeDefinition({
|
|
200
204
|
name: 'MA-onofflight',
|
|
@@ -203,6 +207,8 @@ export const onOffLight = DeviceTypeDefinition({
|
|
|
203
207
|
revision: 3,
|
|
204
208
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id],
|
|
205
209
|
optionalServerClusters: [LevelControl.id],
|
|
210
|
+
requiredClientClusters: [],
|
|
211
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
206
212
|
});
|
|
207
213
|
export const dimmableLight = DeviceTypeDefinition({
|
|
208
214
|
name: 'MA-dimmablelight',
|
|
@@ -211,6 +217,8 @@ export const dimmableLight = DeviceTypeDefinition({
|
|
|
211
217
|
revision: 3,
|
|
212
218
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id, LevelControl.id],
|
|
213
219
|
optionalServerClusters: [],
|
|
220
|
+
requiredClientClusters: [],
|
|
221
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
214
222
|
});
|
|
215
223
|
export const colorTemperatureLight = DeviceTypeDefinition({
|
|
216
224
|
name: 'MA-colortemperaturelight',
|
|
@@ -219,6 +227,8 @@ export const colorTemperatureLight = DeviceTypeDefinition({
|
|
|
219
227
|
revision: 4,
|
|
220
228
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id, LevelControl.id, ColorControl.id],
|
|
221
229
|
optionalServerClusters: [],
|
|
230
|
+
requiredClientClusters: [],
|
|
231
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
222
232
|
});
|
|
223
233
|
export const extendedColorLight = DeviceTypeDefinition({
|
|
224
234
|
name: 'MA-extendedcolorlight',
|
|
@@ -227,6 +237,8 @@ export const extendedColorLight = DeviceTypeDefinition({
|
|
|
227
237
|
revision: 4,
|
|
228
238
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id, LevelControl.id, ColorControl.id],
|
|
229
239
|
optionalServerClusters: [],
|
|
240
|
+
requiredClientClusters: [],
|
|
241
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
230
242
|
});
|
|
231
243
|
export const onOffOutlet = DeviceTypeDefinition({
|
|
232
244
|
name: 'MA-onoffpluginunit',
|
|
@@ -235,6 +247,8 @@ export const onOffOutlet = DeviceTypeDefinition({
|
|
|
235
247
|
revision: 4,
|
|
236
248
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id],
|
|
237
249
|
optionalServerClusters: [LevelControl.id],
|
|
250
|
+
requiredClientClusters: [],
|
|
251
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
238
252
|
});
|
|
239
253
|
export const dimmableOutlet = DeviceTypeDefinition({
|
|
240
254
|
name: 'MA-dimmablepluginunit',
|
|
@@ -243,6 +257,8 @@ export const dimmableOutlet = DeviceTypeDefinition({
|
|
|
243
257
|
revision: 5,
|
|
244
258
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id, LevelControl.id],
|
|
245
259
|
optionalServerClusters: [],
|
|
260
|
+
requiredClientClusters: [],
|
|
261
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
246
262
|
});
|
|
247
263
|
export const onOffMountedSwitch = DeviceTypeDefinition({
|
|
248
264
|
name: 'MA-onoffmountedswitch',
|
|
@@ -251,6 +267,8 @@ export const onOffMountedSwitch = DeviceTypeDefinition({
|
|
|
251
267
|
revision: 2,
|
|
252
268
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id],
|
|
253
269
|
optionalServerClusters: [LevelControl.id],
|
|
270
|
+
requiredClientClusters: [],
|
|
271
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
254
272
|
});
|
|
255
273
|
export const dimmableMountedSwitch = DeviceTypeDefinition({
|
|
256
274
|
name: 'MA-dimmablemountedswitch',
|
|
@@ -259,6 +277,8 @@ export const dimmableMountedSwitch = DeviceTypeDefinition({
|
|
|
259
277
|
revision: 2,
|
|
260
278
|
requiredServerClusters: [Identify.id, Groups.id, ScenesManagement.id, OnOff.id, LevelControl.id],
|
|
261
279
|
optionalServerClusters: [],
|
|
280
|
+
requiredClientClusters: [],
|
|
281
|
+
optionalClientClusters: [OccupancySensing.id],
|
|
262
282
|
});
|
|
263
283
|
export const pumpDevice = DeviceTypeDefinition({
|
|
264
284
|
name: 'MA-pump',
|
|
@@ -267,6 +287,8 @@ export const pumpDevice = DeviceTypeDefinition({
|
|
|
267
287
|
revision: 3,
|
|
268
288
|
requiredServerClusters: [OnOff.id, PumpConfigurationAndControl.id, Identify.id],
|
|
269
289
|
optionalServerClusters: [LevelControl.id, Groups.id, ScenesManagement.id, TemperatureMeasurement.id, PressureMeasurement.id, FlowMeasurement.id],
|
|
290
|
+
requiredClientClusters: [],
|
|
291
|
+
optionalClientClusters: [TemperatureMeasurement.id, PressureMeasurement.id, FlowMeasurement.id, OccupancySensing.id],
|
|
270
292
|
});
|
|
271
293
|
export const waterValve = DeviceTypeDefinition({
|
|
272
294
|
name: 'MA-waterValve',
|
|
@@ -275,6 +297,8 @@ export const waterValve = DeviceTypeDefinition({
|
|
|
275
297
|
revision: 1,
|
|
276
298
|
requiredServerClusters: [Identify.id, ValveConfigurationAndControl.id],
|
|
277
299
|
optionalServerClusters: [FlowMeasurement.id],
|
|
300
|
+
requiredClientClusters: [],
|
|
301
|
+
optionalClientClusters: [FlowMeasurement.id],
|
|
278
302
|
});
|
|
279
303
|
export const onOffSwitch = DeviceTypeDefinition({
|
|
280
304
|
name: 'MA-onoffswitch',
|
|
@@ -282,7 +306,9 @@ export const onOffSwitch = DeviceTypeDefinition({
|
|
|
282
306
|
deviceClass: DeviceClasses.Simple,
|
|
283
307
|
revision: 3,
|
|
284
308
|
requiredServerClusters: [Identify.id, OnOff.id],
|
|
285
|
-
optionalServerClusters: [
|
|
309
|
+
optionalServerClusters: [],
|
|
310
|
+
requiredClientClusters: [Identify.id, OnOff.id],
|
|
311
|
+
optionalClientClusters: [Groups.id, ScenesManagement.id],
|
|
286
312
|
});
|
|
287
313
|
export const dimmableSwitch = DeviceTypeDefinition({
|
|
288
314
|
name: 'MA-dimmableswitch',
|
|
@@ -290,7 +316,9 @@ export const dimmableSwitch = DeviceTypeDefinition({
|
|
|
290
316
|
deviceClass: DeviceClasses.Simple,
|
|
291
317
|
revision: 3,
|
|
292
318
|
requiredServerClusters: [Identify.id, OnOff.id, LevelControl.id],
|
|
293
|
-
optionalServerClusters: [
|
|
319
|
+
optionalServerClusters: [],
|
|
320
|
+
requiredClientClusters: [Identify.id, OnOff.id, LevelControl.id],
|
|
321
|
+
optionalClientClusters: [Groups.id, ScenesManagement.id],
|
|
294
322
|
});
|
|
295
323
|
export const colorTemperatureSwitch = DeviceTypeDefinition({
|
|
296
324
|
name: 'MA-colortemperatureswitch',
|
|
@@ -298,7 +326,9 @@ export const colorTemperatureSwitch = DeviceTypeDefinition({
|
|
|
298
326
|
deviceClass: DeviceClasses.Simple,
|
|
299
327
|
revision: 3,
|
|
300
328
|
requiredServerClusters: [Identify.id, OnOff.id, LevelControl.id, ColorControl.id],
|
|
301
|
-
optionalServerClusters: [
|
|
329
|
+
optionalServerClusters: [],
|
|
330
|
+
requiredClientClusters: [Identify.id, OnOff.id, LevelControl.id, ColorControl.id],
|
|
331
|
+
optionalClientClusters: [Groups.id, ScenesManagement.id],
|
|
302
332
|
});
|
|
303
333
|
export const genericSwitch = DeviceTypeDefinition({
|
|
304
334
|
name: 'MA-genericswitch',
|
|
@@ -436,6 +466,8 @@ export const thermostatDevice = DeviceTypeDefinition({
|
|
|
436
466
|
revision: 5,
|
|
437
467
|
requiredServerClusters: [Identify.id, Thermostat.id],
|
|
438
468
|
optionalServerClusters: [Groups.id, ThermostatUserInterfaceConfiguration.id, EnergyPreference.id],
|
|
469
|
+
requiredClientClusters: [],
|
|
470
|
+
optionalClientClusters: [FanControl.id, TemperatureMeasurement.id, RelativeHumidityMeasurement.id, OccupancySensing.id],
|
|
439
471
|
});
|
|
440
472
|
export const fanDevice = DeviceTypeDefinition({
|
|
441
473
|
name: 'MA-fan',
|
|
@@ -655,7 +687,9 @@ export const heatPump = DeviceTypeDefinition({
|
|
|
655
687
|
deviceClass: DeviceClasses.Simple,
|
|
656
688
|
revision: 1,
|
|
657
689
|
requiredServerClusters: [],
|
|
658
|
-
optionalServerClusters: [Identify.id
|
|
690
|
+
optionalServerClusters: [Identify.id],
|
|
691
|
+
requiredClientClusters: [],
|
|
692
|
+
optionalClientClusters: [Thermostat.id],
|
|
659
693
|
});
|
|
660
694
|
export const soilSensor = DeviceTypeDefinition({
|
|
661
695
|
name: 'MA-soilSensor',
|
|
@@ -810,8 +844,9 @@ export const cameraController = DeviceTypeDefinition({
|
|
|
810
844
|
ZoneManagement.id,
|
|
811
845
|
CameraAvStreamManagement.id,
|
|
812
846
|
CameraAvSettingsUserLevelManagement.id,
|
|
813
|
-
WebRtcTransportRequestor.id,
|
|
814
847
|
PushAvStreamTransport.id,
|
|
848
|
+
TlsCertificateManagement.id,
|
|
849
|
+
TlsClientManagement.id,
|
|
815
850
|
],
|
|
816
851
|
});
|
|
817
852
|
export const doorbell = DeviceTypeDefinition({
|
|
@@ -117,8 +117,12 @@ export declare class MatterbridgeEndpoint extends Endpoint {
|
|
|
117
117
|
invokeBehaviorCommand<T extends Behavior.Type, C extends BehaviorCommandName<T>>(cluster: T, command: C, params?: BehaviorCommandParams<T, C>): Promise<void>;
|
|
118
118
|
invokeBehaviorCommand<T extends ClusterType, C extends ClusterCommandName<T>>(cluster: T, command: C, params?: ClusterCommandParams<T, C>): Promise<void>;
|
|
119
119
|
invokeBehaviorCommand(cluster: ClusterId | string, command: CommandHandlers, params?: Record<string, boolean | number | bigint | string | object | null>): Promise<void>;
|
|
120
|
+
addRequiredClusters(): MatterbridgeEndpoint;
|
|
120
121
|
addRequiredClusterServers(): MatterbridgeEndpoint;
|
|
121
122
|
addOptionalClusterServers(): MatterbridgeEndpoint;
|
|
123
|
+
addRequiredClusterClients(): MatterbridgeEndpoint;
|
|
124
|
+
addOptionalClusterClients(): MatterbridgeEndpoint;
|
|
125
|
+
addClusterClients(clientList: ClusterId[]): MatterbridgeEndpoint;
|
|
122
126
|
getAllClusterServers(): Behavior.Type[];
|
|
123
127
|
getAllClusterServerNames(): string[];
|
|
124
128
|
forEachAttribute(callback: (clusterName: string, clusterId: number, attributeName: string, attributeId: number, attributeValue: boolean | number | bigint | string | object | null | undefined) => void): void;
|
|
@@ -143,6 +147,7 @@ export declare class MatterbridgeEndpoint extends Endpoint {
|
|
|
143
147
|
createApparentElectricalPowerMeasurementClusterServer(voltage?: number | bigint | null, apparentCurrent?: number | bigint | null, apparentPower?: number | bigint | null, frequency?: number | bigint | null): this;
|
|
144
148
|
createDefaultDeviceEnergyManagementClusterServer(esaType?: DeviceEnergyManagement.EsaType, esaCanGenerate?: boolean, esaState?: DeviceEnergyManagement.EsaState, absMinPower?: number, absMaxPower?: number): this;
|
|
145
149
|
createDefaultDeviceEnergyManagementModeClusterServer(currentMode?: number, supportedModes?: DeviceEnergyManagementMode.ModeOption[]): this;
|
|
150
|
+
createDefaultBindingClusterServer(clientList?: ClusterId[]): this;
|
|
146
151
|
createDefaultIdentifyClusterServer(identifyTime?: number, identifyType?: Identify.IdentifyType): this;
|
|
147
152
|
createDefaultGroupsClusterServer(): this;
|
|
148
153
|
createDefaultScenesManagementClusterServer(): this;
|
|
@@ -83,7 +83,7 @@ import { MatterbridgeThermostatServer } from './behaviors/thermostatServer.js';
|
|
|
83
83
|
import { MatterbridgeValveConfigurationAndControlServer } from './behaviors/valveConfigurationAndControlServer.js';
|
|
84
84
|
import { MatterbridgeWindowCoveringServer } from './behaviors/windowCoveringServer.js';
|
|
85
85
|
import { CommandHandler } from './matterbridgeEndpointCommandHandler.js';
|
|
86
|
-
import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, checkNotLatinCharacters, createUniqueId, defaultFor, featuresFor, generateUniqueId, getApparentElectricalPowerMeasurementClusterServer, getAttribute, getAttributeId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getCluster, getClusterId, getDefaultDeviceEnergyManagementClusterServer, getDefaultDeviceEnergyManagementModeClusterServer, getDefaultElectricalEnergyMeasurementClusterServer, getDefaultElectricalPowerMeasurementClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultOccupancySensingClusterServer, getDefaultOperationalStateClusterServer, getDefaultPowerSourceBatteryClusterServer, getDefaultPowerSourceRechargeableBatteryClusterServer, getDefaultPowerSourceReplaceableBatteryClusterServer, getDefaultPowerSourceWiredClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, invokeBehaviorCommand, lowercaseFirstLetter, setAttribute, setCluster, subscribeAttribute, triggerEvent, updateAttribute, } from './matterbridgeEndpointHelpers.js';
|
|
86
|
+
import { addClusterClients, addClusterServers, addFixedLabel, addOptionalClusterClients, addOptionalClusterServers, addRequiredClusterClients, addRequiredClusterServers, addUserLabel, checkNotLatinCharacters, createUniqueId, defaultFor, featuresFor, generateUniqueId, getApparentElectricalPowerMeasurementClusterServer, getAttribute, getAttributeId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getCluster, getClusterId, getDefaultDeviceEnergyManagementClusterServer, getDefaultDeviceEnergyManagementModeClusterServer, getDefaultElectricalEnergyMeasurementClusterServer, getDefaultElectricalPowerMeasurementClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultOccupancySensingClusterServer, getDefaultOperationalStateClusterServer, getDefaultPowerSourceBatteryClusterServer, getDefaultPowerSourceRechargeableBatteryClusterServer, getDefaultPowerSourceReplaceableBatteryClusterServer, getDefaultPowerSourceWiredClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, invokeBehaviorCommand, lowercaseFirstLetter, setAttribute, setCluster, subscribeAttribute, triggerEvent, updateAttribute, } from './matterbridgeEndpointHelpers.js';
|
|
87
87
|
const MATTERBRIDGE_ENDPOINT_BRAND = Symbol('MatterbridgeEndpoint.brand');
|
|
88
88
|
export function isMatterbridgeEndpoint(value) {
|
|
89
89
|
if (!value || typeof value !== 'object')
|
|
@@ -268,6 +268,11 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
268
268
|
async invokeBehaviorCommand(cluster, command, params) {
|
|
269
269
|
await invokeBehaviorCommand(this, cluster, command, params);
|
|
270
270
|
}
|
|
271
|
+
addRequiredClusters() {
|
|
272
|
+
addRequiredClusterServers(this);
|
|
273
|
+
addRequiredClusterClients(this);
|
|
274
|
+
return this;
|
|
275
|
+
}
|
|
271
276
|
addRequiredClusterServers() {
|
|
272
277
|
addRequiredClusterServers(this);
|
|
273
278
|
return this;
|
|
@@ -276,6 +281,18 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
276
281
|
addOptionalClusterServers(this);
|
|
277
282
|
return this;
|
|
278
283
|
}
|
|
284
|
+
addRequiredClusterClients() {
|
|
285
|
+
addRequiredClusterClients(this);
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
addOptionalClusterClients() {
|
|
289
|
+
addOptionalClusterClients(this);
|
|
290
|
+
return this;
|
|
291
|
+
}
|
|
292
|
+
addClusterClients(clientList) {
|
|
293
|
+
addClusterClients(this, clientList);
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
279
296
|
getAllClusterServers() {
|
|
280
297
|
return Object.values(this.behaviors.supported);
|
|
281
298
|
}
|
|
@@ -560,6 +577,10 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
560
577
|
this.behaviors.require(MatterbridgeDeviceEnergyManagementModeServer, getDefaultDeviceEnergyManagementModeClusterServer(currentMode, supportedModes));
|
|
561
578
|
return this;
|
|
562
579
|
}
|
|
580
|
+
createDefaultBindingClusterServer(clientList = []) {
|
|
581
|
+
addClusterClients(this, clientList);
|
|
582
|
+
return this;
|
|
583
|
+
}
|
|
563
584
|
createDefaultIdentifyClusterServer(identifyTime = 0, identifyType = Identify.IdentifyType.None) {
|
|
564
585
|
this.behaviors.require(MatterbridgeIdentifyServer, {
|
|
565
586
|
identifyTime,
|
|
@@ -46,6 +46,9 @@ export declare function invokeSubscribeHandler(endpoint: MatterbridgeEndpoint, c
|
|
|
46
46
|
export declare function addRequiredClusterServers(endpoint: MatterbridgeEndpoint): void;
|
|
47
47
|
export declare function addOptionalClusterServers(endpoint: MatterbridgeEndpoint): void;
|
|
48
48
|
export declare function addClusterServers(endpoint: MatterbridgeEndpoint, serverList: ClusterId[]): void;
|
|
49
|
+
export declare function addClusterClients(endpoint: MatterbridgeEndpoint, clientList: ClusterId[]): void;
|
|
50
|
+
export declare function addRequiredClusterClients(endpoint: MatterbridgeEndpoint): void;
|
|
51
|
+
export declare function addOptionalClusterClients(endpoint: MatterbridgeEndpoint): void;
|
|
49
52
|
export declare function addFixedLabel(endpoint: MatterbridgeEndpoint, label: string, value: string): Promise<void>;
|
|
50
53
|
export declare function addUserLabel(endpoint: MatterbridgeEndpoint, label: string, value: string): Promise<void>;
|
|
51
54
|
export declare function getClusterId(endpoint: Endpoint, cluster: string): number | undefined;
|
|
@@ -84,6 +84,7 @@ import { MeasurementType } from '@matter/types/globals';
|
|
|
84
84
|
import { deepEqual } from '@matterbridge/utils/deep-equal';
|
|
85
85
|
import { isValidArray } from '@matterbridge/utils/validate';
|
|
86
86
|
import { BLUE, CYAN, db, debugStringify, er, hk, or, YELLOW, zb } from 'node-ansi-logger';
|
|
87
|
+
import { MatterbridgeBindingServer } from './behaviors/bindingServer.js';
|
|
87
88
|
import { MatterbridgeBooleanStateConfigurationServer } from './behaviors/booleanStateConfigurationServer.js';
|
|
88
89
|
import { MatterbridgeColorControlServer } from './behaviors/colorControlServer.js';
|
|
89
90
|
import { MatterbridgeDeviceEnergyManagementModeServer } from './behaviors/deviceEnergyManagementModeServer.js';
|
|
@@ -491,6 +492,44 @@ export function addClusterServers(endpoint, serverList) {
|
|
|
491
492
|
if (serverList.includes(DeviceEnergyManagementMode.Cluster.id))
|
|
492
493
|
endpoint.createDefaultDeviceEnergyManagementModeClusterServer();
|
|
493
494
|
}
|
|
495
|
+
export function addClusterClients(endpoint, clientList) {
|
|
496
|
+
if (clientList.length === 0)
|
|
497
|
+
return;
|
|
498
|
+
if (!endpoint.behaviors.has(MatterbridgeBindingServer)) {
|
|
499
|
+
endpoint.behaviors.require(MatterbridgeBindingServer, { clientList });
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const existing = endpoint.behaviors.optionsFor(MatterbridgeBindingServer)?.clientList ?? [];
|
|
503
|
+
endpoint.behaviors.inject(MatterbridgeBindingServer, { clientList: [...new Set([...existing, ...clientList])] });
|
|
504
|
+
}
|
|
505
|
+
export function addRequiredClusterClients(endpoint) {
|
|
506
|
+
const requiredClientList = [];
|
|
507
|
+
endpoint.log.debug(`addRequiredClusterClients for ${CYAN}${endpoint.maybeId}${db}`);
|
|
508
|
+
Array.from(endpoint.deviceTypes.values()).forEach((deviceType) => {
|
|
509
|
+
endpoint.log.debug(`- for deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
|
|
510
|
+
deviceType.requiredClientClusters.forEach((clusterId) => {
|
|
511
|
+
if (!requiredClientList.includes(clusterId)) {
|
|
512
|
+
requiredClientList.push(clusterId);
|
|
513
|
+
endpoint.log.debug(`- cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
addClusterClients(endpoint, requiredClientList);
|
|
518
|
+
}
|
|
519
|
+
export function addOptionalClusterClients(endpoint) {
|
|
520
|
+
const optionalClientList = [];
|
|
521
|
+
endpoint.log.debug(`addOptionalClusterClients for ${CYAN}${endpoint.maybeId}${db}`);
|
|
522
|
+
Array.from(endpoint.deviceTypes.values()).forEach((deviceType) => {
|
|
523
|
+
endpoint.log.debug(`- for deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
|
|
524
|
+
deviceType.optionalClientClusters.forEach((clusterId) => {
|
|
525
|
+
if (!optionalClientList.includes(clusterId)) {
|
|
526
|
+
optionalClientList.push(clusterId);
|
|
527
|
+
endpoint.log.debug(`- cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
addClusterClients(endpoint, optionalClientList);
|
|
532
|
+
}
|
|
494
533
|
export async function addFixedLabel(endpoint, label, value) {
|
|
495
534
|
if (!endpoint.hasClusterServer(FixedLabel.Cluster.id)) {
|
|
496
535
|
endpoint.log.debug(`addFixedLabel: add cluster ${hk}FixedLabel${db}:${hk}fixedLabel${db} with label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
@@ -29,6 +29,7 @@ export declare class MatterbridgePlatform {
|
|
|
29
29
|
onShutdown(reason?: string): Promise<void>;
|
|
30
30
|
onChangeLoggerLevel(logLevel: LogLevel): Promise<void>;
|
|
31
31
|
onAction(action: string, value?: string, id?: string, formData?: PlatformConfig): Promise<void>;
|
|
32
|
+
onFetch(method: string, path?: string, query?: Record<string, unknown>, body?: unknown): Promise<unknown>;
|
|
32
33
|
onConfigChanged(config: PlatformConfig): Promise<void>;
|
|
33
34
|
saveConfig(config: PlatformConfig): void;
|
|
34
35
|
getSchema(): Promise<PlatformSchema | undefined>;
|
|
@@ -156,6 +156,10 @@ export class MatterbridgePlatform {
|
|
|
156
156
|
async onAction(action, value, id, formData) {
|
|
157
157
|
this.log.debug(`The plugin ${CYAN}${this.name}${db} doesn't override onAction. Received action ${CYAN}${action}${db}${value ? ' with ' + CYAN + value + db : ''} ${id ? ' for schema ' + CYAN + id + db : ''}`, formData);
|
|
158
158
|
}
|
|
159
|
+
async onFetch(method, path, query, body) {
|
|
160
|
+
this.log.debug(`onFetch called: method=${method} path=${path ?? 'none'} query=${query ? JSON.stringify(query) : 'none'} body=${body ? JSON.stringify(body) : 'none'}`);
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
159
163
|
async onConfigChanged(config) {
|
|
160
164
|
this.log.debug(`The plugin ${CYAN}${config.name}${db} doesn't override onConfigChanged. Received new config.`);
|
|
161
165
|
}
|
package/dist/pluginManager.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
2
2
|
console.log('\u001B[32mPlugin Manager loaded.\u001B[40;0m');
|
|
3
3
|
import EventEmitter from 'node:events';
|
|
4
|
+
import path from 'node:path';
|
|
4
5
|
import { BroadcastServer } from '@matterbridge/thread/server';
|
|
5
6
|
import { plg, typ } from '@matterbridge/types';
|
|
6
7
|
import { hasParameter } from '@matterbridge/utils/cli';
|
|
@@ -657,6 +658,10 @@ export class PluginManager extends EventEmitter {
|
|
|
657
658
|
plugin.funding = this.getFunding(packageJson);
|
|
658
659
|
if (!plugin.type)
|
|
659
660
|
this.log.warn(`Plugin ${plg}${plugin.name}${wr} has no type`);
|
|
661
|
+
const frontendPath = path.join(plugin.path.replace('package.json', ''), 'apps', 'frontend', 'build', 'index.html');
|
|
662
|
+
const { existsSync } = await import('node:fs');
|
|
663
|
+
if (existsSync(frontendPath))
|
|
664
|
+
plugin.frontendPath = frontendPath;
|
|
660
665
|
const invalidDependencies = this.findInvalidDependencies(packageJson);
|
|
661
666
|
if (invalidDependencies) {
|
|
662
667
|
this.logInvalidDependencies(packageJson, invalidDependencies, plugin.name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matterbridge/core",
|
|
3
|
-
"version": "3.8.0-dev-
|
|
3
|
+
"version": "3.8.0-dev-20260601-a1bf774",
|
|
4
4
|
"description": "Matterbridge core library",
|
|
5
5
|
"author": "https://github.com/Luligu",
|
|
6
6
|
"homepage": "https://matterbridge.io/",
|
|
@@ -130,16 +130,16 @@
|
|
|
130
130
|
],
|
|
131
131
|
"dependencies": {
|
|
132
132
|
"@matter/main": "0.17.0",
|
|
133
|
-
"@matterbridge/dgram": "3.8.0-dev-
|
|
134
|
-
"@matterbridge/thread": "3.8.0-dev-
|
|
135
|
-
"@matterbridge/types": "3.8.0-dev-
|
|
136
|
-
"@matterbridge/utils": "3.8.0-dev-
|
|
133
|
+
"@matterbridge/dgram": "3.8.0-dev-20260601-a1bf774",
|
|
134
|
+
"@matterbridge/thread": "3.8.0-dev-20260601-a1bf774",
|
|
135
|
+
"@matterbridge/types": "3.8.0-dev-20260601-a1bf774",
|
|
136
|
+
"@matterbridge/utils": "3.8.0-dev-20260601-a1bf774",
|
|
137
137
|
"escape-html": "1.0.3",
|
|
138
138
|
"express": "5.2.1",
|
|
139
139
|
"express-rate-limit": "8.5.2",
|
|
140
140
|
"multer": "2.1.1",
|
|
141
141
|
"node-ansi-logger": "3.3.0-dev-20260524-cac9dd5",
|
|
142
142
|
"node-persist-manager": "2.1.0-dev-20260524-6a6019a",
|
|
143
|
-
"ws": "8.
|
|
143
|
+
"ws": "8.21.0"
|
|
144
144
|
}
|
|
145
145
|
}
|