@matterbridge/core 3.8.0-dev-20260603-c7525fb → 3.8.0-dev-20260604-48352af

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.
@@ -142,7 +142,8 @@ export class BackendWsServer {
142
142
  else if ('error' in data) {
143
143
  this.log.debug(`Sending api error message: ${debugStringify(data)}`);
144
144
  }
145
- client.send(JSON.stringify(data));
145
+ const bigintReplacer = (_, v) => (typeof v === 'bigint' ? `${v}n` : v);
146
+ client.send(JSON.stringify(data, bigintReplacer));
146
147
  }
147
148
  else {
148
149
  this.log.error('Cannot send api response, client not connected');
@@ -12,8 +12,12 @@ export class MatterbridgeBindingServer extends BindingServer {
12
12
  const currentClientList = this.endpoint.stateOf(DescriptorServer).clientList;
13
13
  const toAddClientList = clientList.filter((id) => !currentClientList.includes(id));
14
14
  if (toAddClientList.length > 0) {
15
- await this.endpoint.setStateOf(DescriptorServer, { clientList: [...currentClientList, ...toAddClientList] });
16
15
  device.log.info(`Adding client clusters to endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}: ${toAddClientList.join(', ')}`);
16
+ await this.endpoint.setStateOf(DescriptorServer, { clientList: [...currentClientList, ...toAddClientList] });
17
+ }
18
+ const targets = this.endpoint.stateOf(BindingServer).binding;
19
+ for (const target of targets) {
20
+ device.log.info(`Active binding for endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}: target ${debugStringify(target)}`);
17
21
  }
18
22
  });
19
23
  this.reactTo(this.events.binding$Changed, (value) => {
package/dist/frontend.js CHANGED
@@ -1307,7 +1307,8 @@ export class Frontend extends EventEmitter {
1307
1307
  else {
1308
1308
  this.log.debug(`Sending api response message: ${debugStringify(data)}`);
1309
1309
  }
1310
- client.send(JSON.stringify(data));
1310
+ const bigintReplacer = (_, v) => (typeof v === 'bigint' ? `${v}n` : v);
1311
+ client.send(JSON.stringify(data, bigintReplacer));
1311
1312
  }
1312
1313
  else {
1313
1314
  this.log.error('Cannot send api response, client not connected');
@@ -195,6 +195,7 @@ export function addMatterbridgePlatform(platform, name) {
195
195
  description: 'Plugin ' + platform.config.name,
196
196
  author: 'Unknown',
197
197
  enabled: true,
198
+ registeredDevices: 0,
198
199
  });
199
200
  platform['name'] = platform.config.name;
200
201
  }
@@ -8,10 +8,10 @@ import path from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { inspect } from 'node:util';
10
10
  import { Crypto, Environment, LogFormat as MatterLogFormat, Logger, LogLevel as MatterLogLevel, StorageService, UINT16_MAX, UINT32_MAX, } from '@matter/general';
11
- import { Endpoint, NetworkClient, ServerNode } from '@matter/node';
12
- import { BasicInformationClient, BasicInformationServer } from '@matter/node/behaviors/basic-information';
11
+ import { Endpoint, ServerNode } from '@matter/node';
12
+ import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
13
13
  import { AggregatorEndpoint } from '@matter/node/endpoints/aggregator';
14
- import { PaseClient, Read, Subscribe } from '@matter/protocol';
14
+ import { PaseClient } from '@matter/protocol';
15
15
  import { DeviceTypeId, VendorId } from '@matter/types/datatype';
16
16
  import { ManualPairingCodeCodec } from '@matter/types/schema';
17
17
  import { BroadcastServer } from '@matterbridge/thread/server';
@@ -1584,265 +1584,6 @@ export class Matterbridge extends EventEmitter {
1584
1584
  }, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
1585
1585
  }
1586
1586
  async startController() {
1587
- if (!this.matterStorageManager) {
1588
- this.log.error('No storage manager initialized');
1589
- await this.cleanup('No storage manager initialized');
1590
- return;
1591
- }
1592
- if (!this.matterStorageService) {
1593
- this.log.error('No storage service initialized');
1594
- await this.cleanup('No storage service initialized');
1595
- return;
1596
- }
1597
- await createDirectory(path.join(this.matterbridgeDirectory, MATTER_STORAGE_DIR, 'MatterbridgeController'), `Matter node storage directory for MatterbridgeController`, this.log);
1598
- this.matterStorageManager = await this.matterStorageService.open('MatterbridgeController');
1599
- this.log.info('Matter node storage manager "MatterbridgeController" created');
1600
- this.log.info('Creating context controllerContext...');
1601
- this.controllerContext = this.matterStorageManager.createContext('controllerContext');
1602
- if (!this.controllerContext) {
1603
- this.log.error('No storage context controllerContext initialized');
1604
- await this.cleanup('No storage context controllerContext initialized');
1605
- return;
1606
- }
1607
- const { randomBytes } = await import('node:crypto');
1608
- const storeId = 'MatterbridgeController';
1609
- const random = randomBytes(8).toString('hex');
1610
- await this.controllerContext.set('storeId', storeId);
1611
- await this.controllerContext.set('vendorId', this.controllerVendorId);
1612
- await this.controllerContext.set('vendorName', this.controllerVendorName.slice(0, 32));
1613
- await this.controllerContext.set('productId', this.controllerProductId);
1614
- await this.controllerContext.set('productName', this.controllerProductName.slice(0, 32));
1615
- await this.controllerContext.set('productLabel', this.controllerProductName.replace(this.controllerVendorName, '').trim().slice(0, 32));
1616
- await this.controllerContext.set('nodeLabel', storeId.slice(0, 32));
1617
- await this.controllerContext.set('serialNumber', await this.controllerContext.get('serialNumber', 'SN' + random));
1618
- await this.controllerContext.set('uniqueId', await this.controllerContext.get('uniqueId', 'UI' + random));
1619
- await this.controllerContext.set('softwareVersion', isValidNumber(parseVersionString(this.matterbridgeVersion), 0, UINT32_MAX) ? parseVersionString(this.matterbridgeVersion) : 1);
1620
- await this.controllerContext.set('softwareVersionString', isValidString(this.matterbridgeVersion, 5, 64) ? this.matterbridgeVersion : '1.0.0');
1621
- await this.controllerContext.set('hardwareVersion', isValidNumber(parseVersionString(this.systemInformation.osRelease), 0, UINT16_MAX) ? parseVersionString(this.systemInformation.osRelease) : 1);
1622
- await this.controllerContext.set('hardwareVersionString', isValidString(this.systemInformation.osRelease, 5, 64) ? this.systemInformation.osRelease : '1.0.0');
1623
- this.log.info('Creating matter commissioning controller...');
1624
- this.controllerNode = await ServerNode.create({
1625
- id: storeId,
1626
- environment: this.environment,
1627
- network: {
1628
- listeningAddressIpv4: this.ipv4Address,
1629
- listeningAddressIpv6: this.ipv6Address,
1630
- port: this.port,
1631
- },
1632
- basicInformation: {
1633
- nodeLabel: await this.controllerContext.get('nodeLabel'),
1634
- vendorId: VendorId(await this.controllerContext.get('vendorId')),
1635
- vendorName: await this.controllerContext.get('vendorName'),
1636
- productId: await this.controllerContext.get('productId'),
1637
- productName: await this.controllerContext.get('productName'),
1638
- productLabel: await this.controllerContext.get('productLabel'),
1639
- serialNumber: await this.controllerContext.get('serialNumber'),
1640
- uniqueId: await this.controllerContext.get('uniqueId'),
1641
- softwareVersion: await this.controllerContext.get('softwareVersion'),
1642
- softwareVersionString: await this.controllerContext.get('softwareVersionString'),
1643
- hardwareVersion: await this.controllerContext.get('hardwareVersion'),
1644
- hardwareVersionString: await this.controllerContext.get('hardwareVersionString'),
1645
- reachable: true,
1646
- },
1647
- });
1648
- this.log.info(`Loaded matter commissioning controller with ${this.controllerNode.peers.size} peer nodes.`);
1649
- const requestedNodeId = getIntParameter('node');
1650
- const nodeLabel = requestedNodeId !== undefined ? `Node ${requestedNodeId}` : undefined;
1651
- const peerId = requestedNodeId !== undefined ? `peer${requestedNodeId}` : undefined;
1652
- if (hasParameter('pair')) {
1653
- let options;
1654
- if (hasParameter('pairingcode')) {
1655
- const pairingCode = getParameter('pairingcode');
1656
- this.log.info(`Pairing device ${nodeLabel} with pairingcode: ${pairingCode}`);
1657
- const pairingData = ManualPairingCodeCodec.decode(pairingCode);
1658
- this.log.info(`- passcode: ${pairingData.passcode}`);
1659
- this.log.info(`- shortDiscriminator: ${pairingData.shortDiscriminator ?? 'n/a (should never be n/a, short discriminator is encoded in the manual pairing code)'} `);
1660
- this.log.info(`- longDiscriminator: ${pairingData.discriminator ?? 'n/a (manual pairing code encodes short discriminator only)'}`);
1661
- this.log.info(`- vendorId: ${pairingData.vendorId ?? 'n/a'}`);
1662
- this.log.info(`- productId: ${pairingData.productId ?? 'n/a'}`);
1663
- options = {
1664
- id: peerId,
1665
- pairingCode,
1666
- ...(pairingData.shortDiscriminator !== undefined ? { shortDiscriminator: pairingData.shortDiscriminator } : {}),
1667
- ...(pairingData.vendorId !== undefined ? { vendorId: pairingData.vendorId } : {}),
1668
- ...(pairingData.productId !== undefined ? { productId: pairingData.productId } : {}),
1669
- };
1670
- }
1671
- else {
1672
- const discriminator = getIntParameter('discriminator');
1673
- const passcode = getIntParameter('passcode') ?? 0;
1674
- this.log.info(`Pairing device ${nodeLabel} with discriminator: ${discriminator}, passcode: ${passcode}`);
1675
- options = { id: peerId, discriminator, passcode };
1676
- }
1677
- this.log.notice(`Commissioning node ${nodeLabel} with options: ${debugStringify(options)}`);
1678
- try {
1679
- const commissionedNode = await this.controllerNode.peers.commission(options);
1680
- this.log.notice(`Commissioning successfully done for node ${commissionedNode.id}`);
1681
- }
1682
- catch (error) {
1683
- this.log.error(`Commissioning failed for node ${nodeLabel}: ${error instanceof Error ? error.message : error}`);
1684
- }
1685
- }
1686
- else if (hasParameter('discover')) {
1687
- this.log.info('Discovering commissionable devices...');
1688
- const discovery = this.controllerNode.peers.discover();
1689
- discovery.discovered.on((node) => {
1690
- this.log.info(`Discovered commissionable device: ${node.id}`);
1691
- });
1692
- }
1693
- else if (hasParameter('peers')) {
1694
- await this.controllerNode.start();
1695
- const peers = this.controllerNode.peers;
1696
- this.log.info(`Matter commissioning controller paired nodes: ${peers.size}`);
1697
- for (const peer of peers) {
1698
- const match = /^peer(\d+)$/.exec(peer.id);
1699
- this.log.info(`- ${match ? `Node ${match[1]}` : peer.id}`);
1700
- }
1701
- }
1702
- else if (hasParameter('unpair')) {
1703
- await this.controllerNode.start();
1704
- const requestedNodeId = getIntParameter('node');
1705
- if (requestedNodeId === undefined) {
1706
- this.log.error('Missing parameter --node for --unpair');
1707
- return;
1708
- }
1709
- const nodeLabel = `Node ${requestedNodeId}`;
1710
- const peerId = `peer${requestedNodeId}`;
1711
- this.log.info(`Matter commissioning controller decommissioning node: ${nodeLabel}`);
1712
- const peers = this.controllerNode.peers;
1713
- for (const peer of peers) {
1714
- if (peer.id === peerId) {
1715
- await peer.decommission();
1716
- this.log.info(`Matter commissioning controller decommissioned node: ${nodeLabel}`);
1717
- return;
1718
- }
1719
- }
1720
- this.log.warn(`Matter commissioning controller node not found: ${nodeLabel}`);
1721
- }
1722
- else if (hasParameter('unpairall')) {
1723
- await this.controllerNode.start();
1724
- this.log.info('Matter commissioning controller decommissioning all nodes...');
1725
- const nodeIds = this.controllerNode.peers;
1726
- for (const nodeId of nodeIds) {
1727
- this.log.info('Matter commissioning controller decommissioning node:', nodeId.id);
1728
- await nodeId.decommission();
1729
- }
1730
- this.log.info('Matter commissioning controller decommissioned all nodes');
1731
- }
1732
- else {
1733
- this.log.info('Starting matter commissioning controller...');
1734
- await this.controllerNode.start();
1735
- this.log.info(`Matter commissioning controller started with ${this.controllerNode.peers.size} paired nodes`);
1736
- const tracerSubscriptions = new Map();
1737
- const peersToStart = [];
1738
- if (peerId) {
1739
- const peer = this.controllerNode.peers.get(peerId);
1740
- if (!peer) {
1741
- this.log.warn(`Matter commissioning controller peer not found: ${nodeLabel ?? peerId}`);
1742
- }
1743
- else {
1744
- peersToStart.push(peer);
1745
- }
1746
- }
1747
- else {
1748
- for (const peer of this.controllerNode.peers) {
1749
- peersToStart.push(peer);
1750
- }
1751
- }
1752
- for (const peer of peersToStart) {
1753
- const match = /^peer(\d+)$/.exec(peer.id);
1754
- const startedLabel = match ? `Node ${match[1]}` : peer.id;
1755
- peer.lifecycle.online.on(() => {
1756
- this.log.notice(`Matter commissioning controller peer ${startedLabel} is online`);
1757
- if (peer.stateOf(NetworkClient).autoSubscribe) {
1758
- this.log.info(`Matter commissioning controller autoSubscribe is enabled for peer ${startedLabel}`);
1759
- }
1760
- else {
1761
- this.log.info(`Matter commissioning controller setting autoSubscribe for peer ${startedLabel}`);
1762
- peer.setStateOf(NetworkClient, { autoSubscribe: true }).catch((error) => {
1763
- this.log.error(`Failed to set autoSubscribe for peer ${startedLabel}: ${error instanceof Error ? error.message : error}`);
1764
- });
1765
- }
1766
- if (hasParameter('trace-peer') && !tracerSubscriptions.has(peer.id)) {
1767
- this.log.info(`Matter commissioning controller enabling trace-peer for ${startedLabel}`);
1768
- const subscribe = Subscribe({
1769
- fabricFilter: true,
1770
- keepSubscriptions: false,
1771
- attributes: [{}],
1772
- events: [{ isUrgent: true }],
1773
- });
1774
- const subscriptionPromise = peer.interaction.subscribe({
1775
- ...subscribe,
1776
- updated: async (update) => {
1777
- for await (const chunk of update) {
1778
- for (const report of chunk) {
1779
- if (report.kind === 'attr-value') {
1780
- this.log.info(`Peer ${startedLabel} attr ${report.path.endpointId}/${report.path.clusterId}/${report.path.attributeId} v${report.version}: ${typeof report.value === 'object' ? debugStringify(report.value ?? { none: true }) : String(report.value)}`);
1781
- }
1782
- else if (report.kind === 'event-value') {
1783
- this.log.info(`Peer ${startedLabel} event ${report.path.endpointId}/${report.path.clusterId}/${report.path.eventId} #${report.number}: ${typeof report.value === 'object' ? debugStringify(report.value ?? { none: true }) : String(report.value)}`);
1784
- }
1785
- else if (report.kind === 'attr-status') {
1786
- this.log.warn(`Peer ${startedLabel} attr-status ${report.path.endpointId}/${report.path.clusterId}/${report.path.attributeId}: ${String(report.status)}`);
1787
- }
1788
- else if (report.kind === 'event-status') {
1789
- this.log.warn(`Peer ${startedLabel} event-status ${report.path.endpointId}/${report.path.clusterId}/${report.path.eventId}: ${String(report.status)}`);
1790
- }
1791
- }
1792
- }
1793
- },
1794
- closed: () => {
1795
- this.log.warn(`Matter commissioning controller trace-peer subscription closed for ${startedLabel}`);
1796
- tracerSubscriptions.delete(peer.id);
1797
- },
1798
- });
1799
- tracerSubscriptions.set(peer.id, subscriptionPromise);
1800
- subscriptionPromise.catch((error) => {
1801
- tracerSubscriptions.delete(peer.id);
1802
- this.log.error(`Matter commissioning controller trace-peer subscription failed for ${startedLabel}: ${error instanceof Error ? error.message : error}`);
1803
- });
1804
- }
1805
- if (hasParameter('dump-peer')) {
1806
- void (async () => {
1807
- try {
1808
- for await (const _chunk of peer.interaction.read(Read({ fabricFilter: true, attributes: [{}] }))) {
1809
- }
1810
- const basicInfo = peer.stateOf(BasicInformationClient);
1811
- Logger.get(`BasicInformation: ${startedLabel}`).info(basicInfo);
1812
- }
1813
- catch (error) {
1814
- this.log.error(`Matter commissioning controller dump-peer failed for ${startedLabel}: ${error instanceof Error ? error.message : error}`);
1815
- }
1816
- })();
1817
- }
1818
- });
1819
- peer.lifecycle.offline.on(() => {
1820
- this.log.warn(`Matter commissioning controller peer ${startedLabel} is offline`);
1821
- });
1822
- peer.eventsOf(NetworkClient).autoSubscribe$Changed.on((value, oldValue) => {
1823
- this.log.info(`Matter commissioning controller peer ${startedLabel} autoSubscribe changed from ${oldValue ? 'active' : 'inactive'} to ${value ? 'active' : 'inactive'}`);
1824
- });
1825
- peer.eventsOf(NetworkClient).defaultSubscription$Changed.on((value, oldValue) => {
1826
- this.log.info(`Matter commissioning controller peer ${startedLabel} defaultSubscription changed from ${oldValue ? 'active' : 'inactive'} to ${value ? 'active' : 'inactive'}`);
1827
- });
1828
- peer.eventsOf(NetworkClient).subscriptionStatusChanged.on((isActive) => {
1829
- this.log.info(`Matter commissioning controller peer ${startedLabel} subscription is ${isActive ? 'active' : 'inactive'}`);
1830
- });
1831
- peer.eventsOf(NetworkClient).subscriptionAlive.on(() => {
1832
- this.log.info(`Matter commissioning controller peer ${startedLabel} subscription alive event received`);
1833
- });
1834
- this.log.notice(`Starting matter commissioning controller peer ${startedLabel}...`);
1835
- peer
1836
- .start()
1837
- .then(() => {
1838
- this.log.notice(`Matter commissioning controller peer ${startedLabel} started successfully`);
1839
- return;
1840
- })
1841
- .catch((error) => {
1842
- this.log.error(`Matter commissioning controller failed to start peer ${startedLabel}: ${error instanceof Error ? error.message : error}`);
1843
- });
1844
- }
1845
- }
1846
1587
  }
1847
1588
  async startMatterStorage() {
1848
1589
  this.log.info(`Starting matter node storage...`);
@@ -11,20 +11,28 @@ export declare enum DeviceClasses {
11
11
  Duplicate = "Duplicate",
12
12
  BridgedPowerSourceInfo = "BridgedPowerSourceInfo"
13
13
  }
14
+ export declare enum DeviceScopes {
15
+ Endpoint = "endpoint",
16
+ Node = "node"
17
+ }
14
18
  export interface DeviceTypeDefinition {
15
19
  name: string;
20
+ deviceName: string;
16
21
  code: DeviceTypeId;
17
22
  deviceClass: DeviceClasses;
23
+ deviceScope: DeviceScopes;
18
24
  revision: number;
19
25
  requiredServerClusters: ClusterId[];
20
26
  optionalServerClusters: ClusterId[];
21
27
  requiredClientClusters: ClusterId[];
22
28
  optionalClientClusters: ClusterId[];
23
29
  }
24
- export declare const DeviceTypeDefinition: ({ name, code, deviceClass, revision, requiredServerClusters, optionalServerClusters, requiredClientClusters, optionalClientClusters, }: {
30
+ export declare const DeviceTypeDefinition: ({ name, deviceName, code, deviceClass, deviceScope, revision, requiredServerClusters, optionalServerClusters, requiredClientClusters, optionalClientClusters, }: {
25
31
  name: string;
32
+ deviceName: string;
26
33
  code: number;
27
34
  deviceClass: DeviceClasses;
35
+ deviceScope: DeviceScopes;
28
36
  revision: number;
29
37
  requiredServerClusters?: ClusterId[];
30
38
  optionalServerClusters?: ClusterId[];
@@ -49,6 +57,7 @@ export declare const onOffMountedSwitch: DeviceTypeDefinition;
49
57
  export declare const dimmableMountedSwitch: DeviceTypeDefinition;
50
58
  export declare const pumpDevice: DeviceTypeDefinition;
51
59
  export declare const waterValve: DeviceTypeDefinition;
60
+ export declare const irrigationSystem: DeviceTypeDefinition;
52
61
  export declare const onOffSwitch: DeviceTypeDefinition;
53
62
  export declare const dimmableSwitch: DeviceTypeDefinition;
54
63
  export declare const colorTemperatureSwitch: DeviceTypeDefinition;
@@ -65,8 +74,12 @@ export declare const airQualitySensor: DeviceTypeDefinition;
65
74
  export declare const waterFreezeDetector: DeviceTypeDefinition;
66
75
  export declare const waterLeakDetector: DeviceTypeDefinition;
67
76
  export declare const rainSensor: DeviceTypeDefinition;
77
+ export declare const soilSensor: DeviceTypeDefinition;
68
78
  export declare const doorLockDevice: DeviceTypeDefinition;
69
79
  export declare const coverDevice: DeviceTypeDefinition;
80
+ export declare const closure: DeviceTypeDefinition;
81
+ export declare const closurePanel: DeviceTypeDefinition;
82
+ export declare const closureController: DeviceTypeDefinition;
70
83
  export declare const thermostatDevice: DeviceTypeDefinition;
71
84
  export declare const fanDevice: DeviceTypeDefinition;
72
85
  export declare const airPurifier: DeviceTypeDefinition;
@@ -94,11 +107,6 @@ export declare const waterHeater: DeviceTypeDefinition;
94
107
  export declare const solarPower: DeviceTypeDefinition;
95
108
  export declare const batteryStorage: DeviceTypeDefinition;
96
109
  export declare const heatPump: DeviceTypeDefinition;
97
- export declare const soilSensor: DeviceTypeDefinition;
98
- export declare const irrigationSystem: DeviceTypeDefinition;
99
- export declare const closure: DeviceTypeDefinition;
100
- export declare const closurePanel: DeviceTypeDefinition;
101
- export declare const closureController: DeviceTypeDefinition;
102
110
  export declare const meterReferencePoint: DeviceTypeDefinition;
103
111
  export declare const electricalEnergyTariff: DeviceTypeDefinition;
104
112
  export declare const electricalMeter: DeviceTypeDefinition;