@matterbridge/core 3.10.14-dev-20261012-c6da540 → 3.10.14-dev-20261013-e87a0c8

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.
@@ -3,7 +3,7 @@ import { dev } from '@matterbridge/types';
3
3
  import { hasParameter } from '@matterbridge/utils/cli';
4
4
  import { logModuleLoaded } from '@matterbridge/utils/loader';
5
5
  import { AnsiLogger, BLUE, CYAN, db, debugStringify, er } from 'node-ansi-logger';
6
- import { getBatteryLevel, getClusterTextFromDevice, getPowerSource, getReachability, getServerNodeData, toBaseDevice } from './matterNodeHelpers.js';
6
+ import { toApiDevice, toBaseDevice } from './matterNodeHelpers.js';
7
7
  logModuleLoaded('Device Manager');
8
8
  export class DeviceManager {
9
9
  _devices = new Map();
@@ -129,24 +129,9 @@ export class DeviceManager {
129
129
  for (const device of this._devices.values()) {
130
130
  if (pluginName && pluginName !== device.plugin)
131
131
  continue;
132
- if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
133
- continue;
134
- devices.push({
135
- plugin: device.plugin,
136
- type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
137
- number: device.number,
138
- id: device.id,
139
- deviceName: device.deviceName,
140
- serialNumber: device.serialNumber,
141
- productUrl: device.productUrl,
142
- configUrl: device.configUrl,
143
- uniqueId: device.uniqueId,
144
- reachable: getReachability(device),
145
- powerSource: getPowerSource(device),
146
- batteryLevel: getBatteryLevel(device),
147
- matter: device.mode === 'server' && device.serverNode ? getServerNodeData(device.serverNode) : undefined,
148
- cluster: getClusterTextFromDevice(device),
149
- });
132
+ const apiDevice = toApiDevice(device);
133
+ if (apiDevice)
134
+ devices.push(apiDevice);
150
135
  }
151
136
  return devices;
152
137
  }
@@ -97,6 +97,7 @@ export class MatterNode extends EventEmitter {
97
97
  this.environment.vars.set('path.root', path.join(matterbridge.matterbridgeDirectory, MATTER_STORAGE_DIR));
98
98
  this.environment.vars.set('runtime.signals', false);
99
99
  this.environment.vars.set('runtime.exitcode', false);
100
+ this.environment.vars.set('endpoint.validation', 'strict');
100
101
  if (this.mdnsInterface)
101
102
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
102
103
  if (this.verbose)
@@ -660,7 +661,7 @@ export class MatterNode extends EventEmitter {
660
661
  async createMatterbridgeServerNode() {
661
662
  this.log.debug(`Creating ${plg}Matterbridge${db} server node...`);
662
663
  this.matterStorageContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId, parseVersionString(this.matterbridge.matterbridgeVersion), this.matterbridge.matterbridgeVersion, parseVersionString(this.matterbridge.systemInformation.osRelease), this.matterbridge.systemInformation.osRelease, 'Matter Bridge', 'https://matterbridge.io', 1);
663
- this.serverNode = await this.createServerNode(this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
664
+ this.serverNode = await this.createServerNode(this.port, this.passcode, this.discriminator);
664
665
  this.aggregatorNode = await this.createAggregatorNode();
665
666
  this.log.debug(`Adding ${plg}Matterbridge${db} aggregator node...`);
666
667
  await this.serverNode.add(this.aggregatorNode);
@@ -681,7 +682,7 @@ export class MatterNode extends EventEmitter {
681
682
  plugin.locked = true;
682
683
  this.log.debug(`Creating accessory plugin ${plg}${plugin.name}${db} server node...`);
683
684
  this.matterStorageContext = await this.createServerNodeContext(plugin.name, device.deviceName, DeviceTypeId(device.deviceType), VendorId(device.vendorId), device.vendorName, device.productId, device.productName, device.serialNumber, device.uniqueId, device.softwareVersion, device.softwareVersionString, device.hardwareVersion, device.hardwareVersionString, device.productLabel, device.productUrl, device.configurationVersion);
684
- this.serverNode = await this.createServerNode(this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
685
+ this.serverNode = await this.createServerNode(this.port, this.passcode, this.discriminator);
685
686
  this.log.debug(`Adding ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to ${plg}${plugin.name}${db} server node...`);
686
687
  await this.serverNode.add(device);
687
688
  this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to ${plg}${plugin.name}${db} server node`);
@@ -702,7 +703,7 @@ export class MatterNode extends EventEmitter {
702
703
  plugin.locked = true;
703
704
  this.log.debug(`Creating dynamic plugin ${plg}${plugin.name}${db} server node...`);
704
705
  this.matterStorageContext = await this.createServerNodeContext(plugin.name, plugin.description, this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, undefined, undefined, parseVersionString(this.matterbridge.matterbridgeVersion), this.matterbridge.matterbridgeVersion, parseVersionString(this.matterbridge.systemInformation.osRelease), this.matterbridge.systemInformation.osRelease, plugin.description, plugin.homepage ?? 'https://matterbridge.io', plugin.configurationVersion);
705
- this.serverNode = await this.createServerNode(this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
706
+ this.serverNode = await this.createServerNode(this.port, this.passcode, this.discriminator);
706
707
  this.log.debug(`Creating dynamic plugin ${plg}${plugin.name}${db} aggregator node...`);
707
708
  this.aggregatorNode = await this.createAggregatorNode();
708
709
  this.log.debug(`Adding dynamic plugin ${plg}${plugin.name}${db} aggregator node...`);
@@ -724,7 +725,7 @@ export class MatterNode extends EventEmitter {
724
725
  if (device.mode === 'server' && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
725
726
  this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
726
727
  this.matterStorageContext = await this.createServerNodeContext(device.deviceName.replace(/[ .]/g, ''), device.deviceName, DeviceTypeId(device.deviceType), VendorId(device.vendorId), device.vendorName, device.productId, device.productName, device.serialNumber, device.uniqueId, device.softwareVersion, device.softwareVersionString, device.hardwareVersion, device.hardwareVersionString, device.productLabel, device.productUrl, device.configurationVersion);
727
- this.serverNode = await this.createServerNode(this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
728
+ this.serverNode = await this.createServerNode(this.port, this.passcode, this.discriminator);
728
729
  this.log.debug(`Adding ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node...`);
729
730
  await this.serverNode.add(device);
730
731
  this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
@@ -1,8 +1,9 @@
1
1
  import { ServerNode, type SessionsBehavior } from '@matter/node';
2
2
  import type { ExposedFabricInformation } from '@matter/protocol';
3
- import type { ApiMatter, BaseDevice, SanitizedExposedFabricInformation, SanitizedSession } from '@matterbridge/types';
3
+ import type { ApiDevice, ApiMatter, BaseDevice, SanitizedExposedFabricInformation, SanitizedSession } from '@matterbridge/types';
4
4
  import type { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
5
5
  export declare function toBaseDevice(device: MatterbridgeEndpoint | BaseDevice): BaseDevice;
6
+ export declare function toApiDevice(device: MatterbridgeEndpoint): ApiDevice | undefined;
6
7
  export declare function getReachability(device: MatterbridgeEndpoint): boolean;
7
8
  export declare function getPowerSource(endpoint: MatterbridgeEndpoint): 'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined;
8
9
  export declare function getBatteryLevel(endpoint: MatterbridgeEndpoint): number | undefined;
@@ -48,6 +48,26 @@ export function toBaseDevice(device) {
48
48
  id: device.id,
49
49
  };
50
50
  }
51
+ export function toApiDevice(device) {
52
+ if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
53
+ return undefined;
54
+ return {
55
+ plugin: device.plugin,
56
+ type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
57
+ number: device.number,
58
+ id: device.id,
59
+ deviceName: device.deviceName,
60
+ serialNumber: device.serialNumber,
61
+ productUrl: device.productUrl,
62
+ configUrl: device.configUrl,
63
+ uniqueId: device.uniqueId,
64
+ reachable: getReachability(device),
65
+ powerSource: getPowerSource(device),
66
+ batteryLevel: getBatteryLevel(device),
67
+ matter: device.mode === 'server' && device.serverNode ? getServerNodeData(device.serverNode) : undefined,
68
+ cluster: getClusterTextFromDevice(device),
69
+ };
70
+ }
51
71
  export function getReachability(device) {
52
72
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
53
73
  return false;
@@ -460,6 +460,7 @@ export class Matterbridge extends EventEmitter {
460
460
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_DIR));
461
461
  this.environment.vars.set('runtime.signals', false);
462
462
  this.environment.vars.set('runtime.exitcode', false);
463
+ this.environment.vars.set('endpoint.validation', 'strict');
463
464
  this.registerProcessHandlers();
464
465
  try {
465
466
  this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
@@ -882,7 +882,7 @@ export class MatterbridgeEndpoint extends Endpoint {
882
882
  });
883
883
  return this;
884
884
  }
885
- createDefaultTiltWindowCoveringClusterServer(positionTiltPercent100ths = 0, type = WindowCovering.WindowCoveringType.TiltBlindTiltOnly, endProductType = WindowCovering.EndProductType.InteriorVenetianBlind, movementDuration = 0) {
885
+ createDefaultTiltWindowCoveringClusterServer(positionTiltPercent100ths = 0, type = WindowCovering.WindowCoveringType.TiltBlindTiltOnly, endProductType = WindowCovering.EndProductType.TiltOnlyInteriorBlind, movementDuration = 0) {
886
886
  this.behaviors.require(MatterbridgeWindowCoveringServer.with(WindowCovering.Feature.Tilt, WindowCovering.Feature.PositionAwareTilt), {
887
887
  type,
888
888
  numberOfActuationsTilt: 0,
@@ -6,11 +6,12 @@ import { hasParameter } from '@matterbridge/utils/cli';
6
6
  import { logModuleLoaded } from '@matterbridge/utils/loader';
7
7
  import { isValidArray, isValidObject, isValidString } from '@matterbridge/utils/validate';
8
8
  import { fireAndForget } from '@matterbridge/utils/wait';
9
- import { CYAN, db, er, nf, wr } from 'node-ansi-logger';
9
+ import { CYAN, db, debugStringify, er, nf, wr } from 'node-ansi-logger';
10
10
  import { NodeStorageManager } from 'node-persist-manager';
11
11
  import { bridgedNode } from './matterbridgeDeviceTypes.js';
12
12
  import { assertMatterbridgeEndpoint } from './matterbridgeEndpoint.js';
13
13
  import { checkNotLatinCharacters } from './matterbridgeEndpointHelpers.js';
14
+ import { toApiDevice } from './matterNodeHelpers.js';
14
15
  logModuleLoaded('MatterbridgePlatform');
15
16
  const MATTERBRIDGE_PLATFORM_BRAND = Symbol('MatterbridgePlatform.brand');
16
17
  export function isMatterbridgePlatform(value) {
@@ -71,6 +72,7 @@ export class MatterbridgePlatform {
71
72
  this.config = config;
72
73
  this.name = config.name;
73
74
  this.#server = new BroadcastServer('platform', this.log);
75
+ this.#server.on('broadcast_message', this.#msgHandler.bind(this));
74
76
  Object.defineProperty(this, MATTERBRIDGE_PLATFORM_BRAND, {
75
77
  value: true,
76
78
  writable: false,
@@ -121,6 +123,39 @@ export class MatterbridgePlatform {
121
123
  return;
122
124
  });
123
125
  }
126
+ #msgHandler(msg) {
127
+ if (!this.#server.isWorkerRequest(msg))
128
+ return;
129
+ if (this.#verbose)
130
+ this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
131
+ switch (msg.type) {
132
+ case 'platform_get_apidevices':
133
+ if (msg.params.plugin !== this.name)
134
+ return;
135
+ this.#server.respond({
136
+ ...msg,
137
+ result: {
138
+ devices: this.getDevices()
139
+ .map(toApiDevice)
140
+ .filter((device) => device !== undefined),
141
+ },
142
+ });
143
+ break;
144
+ case 'platform_get_apiselectdevices':
145
+ if (msg.params.plugin !== this.name)
146
+ return;
147
+ this.#server.respond({ ...msg, result: { devices: this.getSelectDevices() } });
148
+ break;
149
+ case 'platform_get_apiselectentities':
150
+ if (msg.params.plugin !== this.name)
151
+ return;
152
+ this.#server.respond({ ...msg, result: { entities: this.getSelectEntities() } });
153
+ break;
154
+ default:
155
+ if (this.#verbose)
156
+ this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
157
+ }
158
+ }
124
159
  async destroy() {
125
160
  if (this.#verbose)
126
161
  this.log.debug(`Destroying MatterbridgePlatform for plugin ${this.config.name}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.10.14-dev-20261012-c6da540",
3
+ "version": "3.10.14-dev-20261013-e87a0c8",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -136,10 +136,10 @@
136
136
  },
137
137
  "dependencies": {
138
138
  "@matter/main": "0.17.9",
139
- "@matterbridge/dgram": "3.10.14-dev-20261012-c6da540",
140
- "@matterbridge/thread": "3.10.14-dev-20261012-c6da540",
141
- "@matterbridge/types": "3.10.14-dev-20261012-c6da540",
142
- "@matterbridge/utils": "3.10.14-dev-20261012-c6da540",
139
+ "@matterbridge/dgram": "3.10.14-dev-20261013-e87a0c8",
140
+ "@matterbridge/thread": "3.10.14-dev-20261013-e87a0c8",
141
+ "@matterbridge/types": "3.10.14-dev-20261013-e87a0c8",
142
+ "@matterbridge/utils": "3.10.14-dev-20261013-e87a0c8",
143
143
  "escape-html": "1.0.3",
144
144
  "express": "5.2.1",
145
145
  "express-rate-limit": "8.7.1",