matterbridge 1.6.0 → 1.6.2

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.
Files changed (148) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/README-DEV.md +0 -4
  3. package/README-NGINX.md +63 -0
  4. package/README.md +8 -0
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.js +18 -8
  7. package/dist/cli.js.map +1 -1
  8. package/dist/defaultConfigSchema.d.ts +1 -1
  9. package/dist/defaultConfigSchema.js +1 -1
  10. package/dist/deviceManager.d.ts +2 -2
  11. package/dist/deviceManager.d.ts.map +1 -1
  12. package/dist/deviceManager.js +2 -2
  13. package/dist/deviceManager.js.map +1 -1
  14. package/dist/index.d.ts +10 -10
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +24 -11
  17. package/dist/index.js.map +1 -1
  18. package/dist/matter/export.d.ts +5 -0
  19. package/dist/matter/export.d.ts.map +1 -0
  20. package/dist/matter/export.js +5 -0
  21. package/dist/matter/export.js.map +1 -0
  22. package/dist/matterbridge.d.ts +32 -16
  23. package/dist/matterbridge.d.ts.map +1 -1
  24. package/dist/matterbridge.js +254 -115
  25. package/dist/matterbridge.js.map +1 -1
  26. package/dist/matterbridgeAccessoryPlatform.d.ts +1 -1
  27. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
  28. package/dist/matterbridgeAccessoryPlatform.js +1 -1
  29. package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
  30. package/dist/matterbridgeBehaviors.d.ts +1123 -0
  31. package/dist/matterbridgeBehaviors.d.ts.map +1 -0
  32. package/dist/matterbridgeBehaviors.js +281 -0
  33. package/dist/matterbridgeBehaviors.js.map +1 -0
  34. package/dist/matterbridgeDevice.d.ts +2069 -1511
  35. package/dist/matterbridgeDevice.d.ts.map +1 -1
  36. package/dist/matterbridgeDevice.js +192 -196
  37. package/dist/matterbridgeDevice.js.map +1 -1
  38. package/dist/matterbridgeDeviceTypes.d.ts +58 -0
  39. package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
  40. package/dist/matterbridgeDeviceTypes.js +308 -0
  41. package/dist/matterbridgeDeviceTypes.js.map +1 -0
  42. package/dist/matterbridgeDynamicPlatform.d.ts +1 -1
  43. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
  44. package/dist/matterbridgeDynamicPlatform.js +1 -1
  45. package/dist/matterbridgeDynamicPlatform.js.map +1 -1
  46. package/dist/matterbridgeEdge.d.ts +20 -21
  47. package/dist/matterbridgeEdge.d.ts.map +1 -1
  48. package/dist/matterbridgeEdge.js +506 -103
  49. package/dist/matterbridgeEdge.js.map +1 -1
  50. package/dist/matterbridgeEndpoint.d.ts +3545 -2336
  51. package/dist/matterbridgeEndpoint.d.ts.map +1 -1
  52. package/dist/matterbridgeEndpoint.js +672 -468
  53. package/dist/matterbridgeEndpoint.js.map +1 -1
  54. package/dist/matterbridgePlatform.d.ts +5 -4
  55. package/dist/matterbridgePlatform.d.ts.map +1 -1
  56. package/dist/matterbridgePlatform.js +11 -3
  57. package/dist/matterbridgePlatform.js.map +1 -1
  58. package/dist/matterbridgeTypes.d.ts +7 -8
  59. package/dist/matterbridgeTypes.d.ts.map +1 -1
  60. package/dist/matterbridgeTypes.js.map +1 -1
  61. package/dist/matterbridgeWebsocket.d.ts +1 -1
  62. package/dist/matterbridgeWebsocket.d.ts.map +1 -1
  63. package/dist/matterbridgeWebsocket.js +41 -3
  64. package/dist/matterbridgeWebsocket.js.map +1 -1
  65. package/dist/pluginManager.d.ts +1 -1
  66. package/dist/pluginManager.d.ts.map +1 -1
  67. package/dist/pluginManager.js +17 -8
  68. package/dist/pluginManager.js.map +1 -1
  69. package/dist/utils/colorUtils.d.ts +1 -1
  70. package/dist/utils/colorUtils.js +2 -2
  71. package/dist/utils/colorUtils.js.map +1 -1
  72. package/dist/utils/utils.d.ts +42 -1
  73. package/dist/utils/utils.d.ts.map +1 -1
  74. package/dist/utils/utils.js +105 -3
  75. package/dist/utils/utils.js.map +1 -1
  76. package/frontend/build/asset-manifest.json +62 -6
  77. package/frontend/build/index.html +1 -1
  78. package/frontend/build/static/css/main.823e08b6.css +2 -0
  79. package/frontend/build/static/css/main.823e08b6.css.map +1 -0
  80. package/frontend/build/static/js/main.a14c87e7.js +115 -0
  81. package/frontend/build/static/js/{main.045d08f7.js.LICENSE.txt → main.a14c87e7.js.LICENSE.txt} +3 -3
  82. package/frontend/build/static/js/main.a14c87e7.js.map +1 -0
  83. package/frontend/build/static/media/roboto-cyrillic-300-normal.1b79538ccd585c259996.woff2 +0 -0
  84. package/frontend/build/static/media/roboto-cyrillic-300-normal.5f077fd7b977d1715acf.woff +0 -0
  85. package/frontend/build/static/media/roboto-cyrillic-400-normal.5d2930082227d172f62c.woff +0 -0
  86. package/frontend/build/static/media/roboto-cyrillic-400-normal.a9e19870cf6c4b973427.woff2 +0 -0
  87. package/frontend/build/static/media/roboto-cyrillic-500-normal.0ae2428323939af5e1ad.woff2 +0 -0
  88. package/frontend/build/static/media/roboto-cyrillic-500-normal.dd7bc8a52c6c70c5a3f5.woff +0 -0
  89. package/frontend/build/static/media/roboto-cyrillic-700-normal.3f6e1548bd5175a8c342.woff +0 -0
  90. package/frontend/build/static/media/roboto-cyrillic-700-normal.4fdfc29a10e7d4b7c527.woff2 +0 -0
  91. package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.795dbc8140e3fef82983.woff +0 -0
  92. package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.80947a31d23c70204b47.woff2 +0 -0
  93. package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.135d076fa32aa0b4d105.woff +0 -0
  94. package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.5cec61a21cc20180fbe1.woff2 +0 -0
  95. package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.6de16332fda843a3dc3d.woff2 +0 -0
  96. package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.c0a0638f90b31d6454ba.woff +0 -0
  97. package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.4750292c47fa2bc6ac1a.woff2 +0 -0
  98. package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.ca247189fc12d00de361.woff +0 -0
  99. package/frontend/build/static/media/roboto-greek-300-normal.285f3e6261d8eb20417d.woff2 +0 -0
  100. package/frontend/build/static/media/roboto-greek-300-normal.889beddda1c9bd9f97df.woff +0 -0
  101. package/frontend/build/static/media/roboto-greek-400-normal.160a791a8e4f46bca3cc.woff +0 -0
  102. package/frontend/build/static/media/roboto-greek-400-normal.2c32b1315be61477013a.woff2 +0 -0
  103. package/frontend/build/static/media/roboto-greek-500-normal.60810e07c7b0273013aa.woff +0 -0
  104. package/frontend/build/static/media/roboto-greek-500-normal.f95e757c5483310f9c11.woff2 +0 -0
  105. package/frontend/build/static/media/roboto-greek-700-normal.77dd370f2001e184ba0d.woff2 +0 -0
  106. package/frontend/build/static/media/roboto-greek-700-normal.df87b053fae3d7ad5f7a.woff +0 -0
  107. package/frontend/build/static/media/roboto-greek-ext-300-normal.b590dbe5c639944366d1.woff +0 -0
  108. package/frontend/build/static/media/roboto-greek-ext-300-normal.d6049cb54aa6fbe14c42.woff2 +0 -0
  109. package/frontend/build/static/media/roboto-greek-ext-400-normal.16eb83b4a3b1ea994243.woff +0 -0
  110. package/frontend/build/static/media/roboto-greek-ext-400-normal.1df4abad55796d11a0c8.woff2 +0 -0
  111. package/frontend/build/static/media/roboto-greek-ext-500-normal.4a96ba31abcce0f5d52b.woff2 +0 -0
  112. package/frontend/build/static/media/roboto-greek-ext-500-normal.fd28d9c008bf3af1bed7.woff +0 -0
  113. package/frontend/build/static/media/roboto-greek-ext-700-normal.2dd6febad11502dec6a6.woff2 +0 -0
  114. package/frontend/build/static/media/roboto-greek-ext-700-normal.4abdc9fff4507f17d726.woff +0 -0
  115. package/frontend/build/static/media/roboto-latin-300-normal.b850f1ff581ea232fac9.woff2 +0 -0
  116. package/frontend/build/static/media/roboto-latin-300-normal.c4bc0593c9954d79cb3a.woff +0 -0
  117. package/frontend/build/static/media/roboto-latin-400-normal.047a7839f69b209db815.woff +0 -0
  118. package/frontend/build/static/media/roboto-latin-400-normal.297d48e1b5a10c0831a9.woff2 +0 -0
  119. package/frontend/build/static/media/roboto-latin-500-normal.68d40d6d01c6f85d24ba.woff +0 -0
  120. package/frontend/build/static/media/roboto-latin-500-normal.7077203b1982951ecf76.woff2 +0 -0
  121. package/frontend/build/static/media/roboto-latin-700-normal.4535474e1cf8598695ad.woff2 +0 -0
  122. package/frontend/build/static/media/roboto-latin-700-normal.9f6a16a7770c87b2042b.woff +0 -0
  123. package/frontend/build/static/media/roboto-latin-ext-300-normal.14982a9e4857a93b6dce.woff +0 -0
  124. package/frontend/build/static/media/roboto-latin-ext-300-normal.97cbc447d4a8d41a9543.woff2 +0 -0
  125. package/frontend/build/static/media/roboto-latin-ext-400-normal.27da5b36b6d3a16f53f4.woff +0 -0
  126. package/frontend/build/static/media/roboto-latin-ext-400-normal.2eeae187764baf05867d.woff2 +0 -0
  127. package/frontend/build/static/media/roboto-latin-ext-500-normal.06c30711d588145a4541.woff +0 -0
  128. package/frontend/build/static/media/roboto-latin-ext-500-normal.9a18d7bb9ff7a6af7b32.woff2 +0 -0
  129. package/frontend/build/static/media/roboto-latin-ext-700-normal.18841836e391d39e83a8.woff2 +0 -0
  130. package/frontend/build/static/media/roboto-latin-ext-700-normal.3c5bcdd0e69c4c3ffafe.woff +0 -0
  131. package/frontend/build/static/media/roboto-vietnamese-300-normal.c96b16e5c05c7b7c3e89.woff2 +0 -0
  132. package/frontend/build/static/media/roboto-vietnamese-300-normal.f5e7cea32756dfe7af40.woff +0 -0
  133. package/frontend/build/static/media/roboto-vietnamese-400-normal.0dc97c66f9b542d6fa17.woff +0 -0
  134. package/frontend/build/static/media/roboto-vietnamese-400-normal.d3f8e26d6c27de8102b6.woff2 +0 -0
  135. package/frontend/build/static/media/roboto-vietnamese-500-normal.090fabef926bdc0e9b9f.woff2 +0 -0
  136. package/frontend/build/static/media/roboto-vietnamese-500-normal.23b7b8a2524d2d4b637b.woff +0 -0
  137. package/frontend/build/static/media/roboto-vietnamese-700-normal.0a79a9fabfc32e33f360.woff2 +0 -0
  138. package/frontend/build/static/media/roboto-vietnamese-700-normal.35ed0597568ff6f19c16.woff +0 -0
  139. package/npm-shrinkwrap.json +120 -39
  140. package/package.json +8 -3
  141. package/dist/matterbridgeController.d.ts +0 -24
  142. package/dist/matterbridgeController.d.ts.map +0 -1
  143. package/dist/matterbridgeController.js +0 -386
  144. package/dist/matterbridgeController.js.map +0 -1
  145. package/frontend/build/static/css/main.1cf003ae.css +0 -2
  146. package/frontend/build/static/css/main.1cf003ae.css.map +0 -1
  147. package/frontend/build/static/js/main.045d08f7.js +0 -3
  148. package/frontend/build/static/js/main.045d08f7.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  /**
2
3
  * This file contains the class MatterbridgeEdge that extends the Matterbridge class.
3
4
  *
@@ -6,7 +7,7 @@
6
7
  * @date 2024-10-01
7
8
  * @version 1.0.0
8
9
  *
9
- * Copyright 2024 Luca Liguori.
10
+ * Copyright 2024, 2025, 2026 Luca Liguori.
10
11
  *
11
12
  * Licensed under the Apache License, Version 2.0 (the "License");
12
13
  * you may not use this file except in compliance with the License.
@@ -21,28 +22,27 @@
21
22
  * limitations under the License. *
22
23
  */
23
24
  /* eslint-disable @typescript-eslint/no-unused-vars */
24
- /**
25
- * Import needed modules from @project-chip/matter-node.js
26
- */
27
- // Include this first to auto-register Crypto, Network and Time Node.js implementations
28
- import '@project-chip/matter-node.js';
29
- import { CryptoNode } from '@project-chip/matter-node.js/crypto';
30
- import { DeviceTypeId, FabricIndex, VendorId } from '@project-chip/matter-node.js/datatype';
31
- import { Format, Level } from '@project-chip/matter-node.js/log';
32
- import { getParameter, hasParameter } from '@project-chip/matter-node.js/util';
33
- import { Environment, StorageService } from '@project-chip/matter.js/environment';
34
- import { ServerNode } from '@project-chip/matter.js/node';
35
- import { FabricAction } from '@project-chip/matter-node.js/fabric';
36
- import { Endpoint } from '@project-chip/matter.js/endpoint';
37
- import { AggregatorEndpoint } from '@project-chip/matter.js/endpoints/AggregatorEndpoint';
38
- // Other imports
39
- import { Matterbridge } from './matterbridge.js';
40
- import { rs, GREEN } from 'node-ansi-logger';
25
+ // Node.js modules
41
26
  import path from 'path';
42
27
  import os from 'os';
43
- import { BasicInformationCluster } from '@project-chip/matter-node.js/cluster';
44
- import { Aggregator, Device } from '@project-chip/matter-node.js/device';
45
- const verbose = hasParameter('verbose');
28
+ import { randomBytes } from 'crypto';
29
+ // NodeStorage and AnsiLogger modules
30
+ import { rs, GREEN, debugStringify, er, zb, nf } from 'node-ansi-logger';
31
+ // Matterbridge
32
+ import { Matterbridge } from './matterbridge.js';
33
+ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
34
+ import { bridge, genericSwitch } from './matterbridgeDeviceTypes.js';
35
+ import { dev, plg } from './matterbridgeTypes.js';
36
+ import { copyDirectory, getParameter, hasParameter } from './utils/utils.js';
37
+ // @matter
38
+ import { DeviceTypeId, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, EndpointServer } from '@matter/main';
39
+ import { ServerNode, Endpoint as EndpointNode, Environment, StorageService } from '@matter/main';
40
+ import { BasicInformationCluster } from '@matter/main/clusters';
41
+ import { FabricAction, MdnsService } from '@matter/main/protocol';
42
+ import { GenericSwitchDevice } from '@matter/main/devices';
43
+ import { AggregatorEndpoint } from '@matter/main/endpoints';
44
+ import { BridgedDeviceBasicInformationServer, SwitchServer } from '@matter/main/behaviors';
45
+ import { Device, logEndpoint } from '@project-chip/matter.js/device';
46
46
  /**
47
47
  * Represents the MatterbridgeEdge application.
48
48
  */
@@ -53,15 +53,16 @@ export class MatterbridgeEdge extends Matterbridge {
53
53
  // Matter storage
54
54
  matterStorageService;
55
55
  // Mapping of CommissioningServer to ServerNode
56
- csToMatterNode = new Map();
56
+ csToServerNode = new Map();
57
57
  // Mapping of Aggregator to AggregatorEndpoint
58
- agToMatterNode = new Map();
58
+ agToAggregatorEndpoint = new Map();
59
+ // Mapping of sessions
60
+ activeSessions = new Map();
59
61
  constructor() {
60
62
  super();
61
63
  }
62
64
  static async loadInstance(initialize = false) {
63
65
  if (!MatterbridgeEdge.instance) {
64
- // eslint-disable-next-line no-console
65
66
  if (hasParameter('debug'))
66
67
  console.log(GREEN + 'Creating a new instance of MatterbridgeEdge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
67
68
  MatterbridgeEdge.instance = new MatterbridgeEdge();
@@ -71,20 +72,23 @@ export class MatterbridgeEdge extends Matterbridge {
71
72
  return MatterbridgeEdge.instance;
72
73
  }
73
74
  async initialize() {
74
- // eslint-disable-next-line no-console
75
75
  if (hasParameter('debug'))
76
76
  console.log('Initializing MatterbridgeEdge...');
77
77
  // Set the matterbridge directory
78
78
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
79
79
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
80
+ this.matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
80
81
  // Setup matter environment
81
- this.environment.vars.set('log.level', Level.INFO);
82
- this.environment.vars.set('log.format', Format.ANSI);
83
- this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, 'matterstorage'));
82
+ this.environment.vars.set('log.level', MatterLogLevel.INFO);
83
+ this.environment.vars.set('log.format', MatterLogFormat.ANSI);
84
+ this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
84
85
  this.environment.vars.set('runtime.signals', false);
85
86
  this.environment.vars.set('runtime.exitcode', false);
86
87
  // Initialize the base Matterbridge class
87
88
  await super.initialize();
89
+ // Setup Matter mdnsInterface
90
+ if (this.mdnsInterface)
91
+ this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
88
92
  // Setup Matter commissioning server
89
93
  this.port = 5540;
90
94
  this.passcode = 20242025;
@@ -96,12 +100,16 @@ export class MatterbridgeEdge extends Matterbridge {
96
100
  this.matterStorageService = this.environment.get(StorageService);
97
101
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
98
102
  this.storageManager = await this.matterStorageService.open('Matterbridge');
99
- this.matterbridgeContext = this.storageManager.createContext('persist');
100
103
  this.log.info('Matter node storage manager "Matterbridge" created');
104
+ this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
101
105
  this.log.info('Matter node storage started');
106
+ // Backup matter storage since it is created/opened correctly
107
+ await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
102
108
  }
103
109
  async backupMatterStorage(storageName, backupName) {
104
- // TODO: Implement backupMatterStorage
110
+ this.log.info('Creating matter node storage backup...');
111
+ await copyDirectory(storageName, backupName);
112
+ this.log.info('Created matter node storage backup');
105
113
  }
106
114
  async stopMatterStorage() {
107
115
  this.log.info('Closing matter node storage...');
@@ -129,18 +137,24 @@ export class MatterbridgeEdge extends Matterbridge {
129
137
  async stopMatterServer() {
130
138
  this.log.info(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
131
139
  if (this.bridgeMode === 'bridge') {
132
- const serverNode = this.csToMatterNode.get('Matterbridge')?.serverNode;
133
- if (serverNode)
140
+ const serverNode = this.csToServerNode.get('Matterbridge')?.serverNode;
141
+ if (serverNode) {
134
142
  await this.stopServerNode(serverNode);
143
+ this.log.info(`Stopped matter server node Matterbridge`);
144
+ }
135
145
  }
136
146
  if (this.bridgeMode === 'childbridge') {
137
147
  this.plugins.forEach(async (plugin) => {
138
- const serverNode = this.csToMatterNode.get(plugin.name)?.serverNode;
139
- if (serverNode)
148
+ const serverNode = this.csToServerNode.get(plugin.name)?.serverNode;
149
+ if (serverNode) {
140
150
  await this.stopServerNode(serverNode);
151
+ this.log.info(`Stopped matter server node ${plugin.name}`);
152
+ }
141
153
  });
142
154
  }
143
155
  this.log.info('Stopped matter server nodes');
156
+ await this.environment.get(MdnsService)[Symbol.asyncDispose]();
157
+ this.log.info('Stopped MdnsService');
144
158
  }
145
159
  /**
146
160
  * Creates a server node storage context.
@@ -163,10 +177,10 @@ export class MatterbridgeEdge extends Matterbridge {
163
177
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
164
178
  if (!this.matterStorageService)
165
179
  throw new Error('No storage service initialized');
166
- this.log.notice(`Creating server node storage context "${pluginName}.persist" for ${pluginName}...`);
180
+ this.log.info(`Creating server node storage context "${pluginName}.persist" for ${pluginName}...`);
167
181
  const storageManager = await this.matterStorageService.open(pluginName);
168
182
  const storageContext = storageManager.createContext('persist');
169
- const random = CryptoNode.getRandomData(8).toHex();
183
+ const random = randomBytes(8).toString('hex');
170
184
  await storageContext.set('storeId', pluginName);
171
185
  await storageContext.set('deviceName', deviceName);
172
186
  await storageContext.set('deviceType', deviceType);
@@ -177,22 +191,28 @@ export class MatterbridgeEdge extends Matterbridge {
177
191
  await storageContext.set('nodeLabel', productName.slice(0, 32));
178
192
  await storageContext.set('productLabel', productName.slice(0, 32));
179
193
  await storageContext.set('serialNumber', await storageContext.get('serialNumber', serialNumber ? serialNumber.slice(0, 32) : 'SN' + random));
180
- await storageContext.set('uniqueId', await storageContext.get('uniqueId', random));
194
+ await storageContext.set('uniqueId', await storageContext.get('uniqueId', 'UI' + random));
181
195
  await storageContext.set('softwareVersion', this.matterbridgeVersion !== '' && this.matterbridgeVersion.includes('.') ? parseInt(this.matterbridgeVersion.split('.')[0], 10) : 1);
182
196
  await storageContext.set('softwareVersionString', this.matterbridgeVersion !== '' ? this.matterbridgeVersion : '1.0.0');
183
197
  await storageContext.set('hardwareVersion', this.systemInformation.osRelease !== '' && this.systemInformation.osRelease.includes('.') ? parseInt(this.systemInformation.osRelease.split('.')[0], 10) : 1);
184
198
  await storageContext.set('hardwareVersionString', this.systemInformation.osRelease !== '' ? this.systemInformation.osRelease : '1.0.0');
185
199
  this.log.debug(`Created server node storage context "${pluginName}.persist" for ${pluginName}:`);
186
- this.log.debug(`- deviceName: ${await storageContext.get('deviceName')} deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
187
- this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')} uniqueId: ${await storageContext.get('uniqueId')}`);
200
+ this.log.debug(`- storeId: ${await storageContext.get('storeId')}`);
201
+ this.log.debug(`- deviceName: ${await storageContext.get('deviceName')}`);
202
+ this.log.debug(`- deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
203
+ this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
204
+ this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
188
205
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
189
206
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
190
207
  return storageContext;
191
208
  }
192
209
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
193
- this.log.notice(`Creating server node for ${await storageContext.get('storeId')} with:`);
194
- this.log.debug(`- deviceName: ${await storageContext.get('deviceName')} deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
195
- this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')} uniqueId: ${await storageContext.get('uniqueId')}`);
210
+ const storeId = await storageContext.get('storeId');
211
+ this.log.info(`Creating server node for ${storeId}...`);
212
+ this.log.debug(`- deviceName: ${await storageContext.get('deviceName')}`);
213
+ this.log.debug(`- deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
214
+ this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
215
+ this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
196
216
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
197
217
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
198
218
  /**
@@ -200,7 +220,7 @@ export class MatterbridgeEdge extends Matterbridge {
200
220
  */
201
221
  const serverNode = await ServerNode.create({
202
222
  // Required: Give the Node a unique ID which is used to store the state of this node
203
- id: await storageContext.get('storeId'),
223
+ id: storeId,
204
224
  // Provide Network relevant configuration like the port
205
225
  // Optional when operating only one device on a host, Default port is 5540
206
226
  network: {
@@ -237,17 +257,57 @@ export class MatterbridgeEdge extends Matterbridge {
237
257
  hardwareVersionString: await storageContext.get('hardwareVersionString'),
238
258
  },
239
259
  });
260
+ const sanitizeFabrics = (fabrics) => {
261
+ // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
262
+ const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(serverNode.state.commissioning.fabrics)));
263
+ this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
264
+ if (this.bridgeMode === 'bridge') {
265
+ this.matterbridgeFabricInformations = sanitizedFabrics;
266
+ this.matterbridgeSessionInformations = [];
267
+ this.matterbridgePaired = true;
268
+ }
269
+ };
240
270
  /**
241
271
  * This event is triggered when the device is initially commissioned successfully.
242
272
  * This means: It is added to the first fabric.
243
273
  */
244
- serverNode.lifecycle.commissioned.on(() => this.log.notice('Server was initially commissioned successfully!'));
274
+ serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
245
275
  /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
246
- serverNode.lifecycle.decommissioned.on(() => this.log.notice('Server was fully decommissioned successfully!'));
276
+ serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
247
277
  /** This event is triggered when the device went online. This means that it is discoverable in the network. */
248
- serverNode.lifecycle.online.on(() => this.log.notice('Server is online'));
278
+ serverNode.lifecycle.online.on(() => {
279
+ this.log.notice(`Server node for ${storeId} is online`);
280
+ if (!serverNode.lifecycle.isCommissioned) {
281
+ this.log.notice(`Server node for ${storeId} is not commissioned. Pair to commission ...`);
282
+ const { qrPairingCode, manualPairingCode } = serverNode.state.commissioning.pairingCodes;
283
+ if (this.bridgeMode === 'bridge') {
284
+ this.matterbridgeQrPairingCode = qrPairingCode;
285
+ this.matterbridgeManualPairingCode = manualPairingCode;
286
+ this.matterbridgeFabricInformations = [];
287
+ this.matterbridgeSessionInformations = [];
288
+ this.matterbridgePaired = false;
289
+ this.matterbridgeConnected = false;
290
+ this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
291
+ this.log.notice(`Manual pairing code: ${manualPairingCode}`);
292
+ }
293
+ }
294
+ else {
295
+ this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
296
+ sanitizeFabrics(serverNode.state.commissioning.fabrics);
297
+ }
298
+ });
249
299
  /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
250
- serverNode.lifecycle.offline.on(() => this.log.notice('Server is offline'));
300
+ serverNode.lifecycle.offline.on(() => {
301
+ this.log.notice(`Server node for ${storeId} is offline`);
302
+ if (this.bridgeMode === 'bridge') {
303
+ this.matterbridgeQrPairingCode = undefined;
304
+ this.matterbridgeManualPairingCode = undefined;
305
+ this.matterbridgeFabricInformations = [];
306
+ this.matterbridgeSessionInformations = [];
307
+ this.matterbridgePaired = false;
308
+ this.matterbridgeConnected = false;
309
+ }
310
+ });
251
311
  /**
252
312
  * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
253
313
  * information is needed.
@@ -265,44 +325,42 @@ export class MatterbridgeEdge extends Matterbridge {
265
325
  action = 'updated';
266
326
  break;
267
327
  }
268
- this.log.notice(`Commissioned fabric index ${fabricIndex} ${action}`, serverNode.state.commissioning.fabrics[fabricIndex]);
328
+ this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
329
+ sanitizeFabrics(serverNode.state.commissioning.fabrics);
269
330
  });
331
+ const sanitizeSessions = (sessions) => {
332
+ const sanitizedSessions = this.sanitizeSessionInformation(sessions.map((session) => ({
333
+ ...session,
334
+ secure: session.name.startsWith('secure'),
335
+ })));
336
+ this.log.debug(`Sessions: ${debugStringify(sanitizedSessions)}`);
337
+ if (this.bridgeMode === 'bridge') {
338
+ this.matterbridgeSessionInformations = sanitizedSessions;
339
+ }
340
+ };
270
341
  /**
271
342
  * This event is triggered when an operative new session was opened by a Controller.
272
343
  * It is not triggered for the initial commissioning process, just afterwards for real connections.
273
344
  */
274
- serverNode.events.sessions.opened.on((session) => this.log.notice('Session opened', session));
345
+ serverNode.events.sessions.opened.on((session) => {
346
+ this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
347
+ sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
348
+ });
275
349
  /**
276
350
  * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
277
351
  */
278
- serverNode.events.sessions.closed.on((session) => this.log.notice('Session closed', session));
352
+ serverNode.events.sessions.closed.on((session) => {
353
+ this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
354
+ sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
355
+ });
279
356
  /** This event is triggered when a subscription gets added or removed on an operative session. */
280
357
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
281
- this.log.notice('Session subscriptions changed', session);
282
- this.log.notice('Status of all sessions', serverNode.state.sessions.sessions);
358
+ this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
359
+ sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
283
360
  });
361
+ this.log.info(`Created server node for ${storeId}`);
284
362
  return serverNode;
285
363
  }
286
- async showServerNodeQR(matterServerNode, storageContext) {
287
- if (!matterServerNode || !storageContext)
288
- return;
289
- const node = await storageContext.get('storeId');
290
- if (!matterServerNode.lifecycle.isCommissioned) {
291
- this.log.notice(`${node} is not commissioned. Pair to commission ...`);
292
- const { qrPairingCode, manualPairingCode } = matterServerNode.state.commissioning.pairingCodes;
293
- // console.log(QrCode.get(qrPairingCode));
294
- this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
295
- this.log.notice(`Manual pairing code: ${manualPairingCode}`);
296
- }
297
- else {
298
- this.log.notice(`${node} is already commissioned. Waiting for controllers to connect ...`);
299
- this.log.notice('Fabrics:', matterServerNode.state.commissioning.fabrics);
300
- for (const key in matterServerNode.state.commissioning.fabrics) {
301
- const fabric = matterServerNode.state.commissioning.fabrics[FabricIndex(Number(key))];
302
- this.log.notice(`- index ${fabric.fabricIndex} id ${fabric.fabricId} nodeId ${fabric.nodeId} rootVendor ${fabric.rootVendorId} rootNodeId ${fabric.rootNodeId}`);
303
- }
304
- }
305
- }
306
364
  async startServerNode(matterServerNode) {
307
365
  if (!matterServerNode)
308
366
  return;
@@ -312,60 +370,102 @@ export class MatterbridgeEdge extends Matterbridge {
312
370
  async stopServerNode(matterServerNode) {
313
371
  if (!matterServerNode)
314
372
  return;
315
- this.log.notice(`Stopping ${matterServerNode.id} server node`);
373
+ this.log.notice(`Closing ${matterServerNode.id} server node`);
316
374
  await matterServerNode.close();
317
375
  }
318
376
  async createAggregatorNode(storageContext) {
319
377
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
320
- const aggregator = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')} aggregator` });
378
+ const aggregator = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
321
379
  return aggregator;
322
380
  }
381
+ async addBridgedEndpoint(pluginName, device) {
382
+ // Check if the plugin is registered
383
+ const plugin = this.plugins.get(pluginName);
384
+ if (!plugin) {
385
+ this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
386
+ return;
387
+ }
388
+ // Register and add the device to the matterbridge aggregator node
389
+ if (this.bridgeMode === 'bridge') {
390
+ this.log.info(`Adding ${pluginName}:${device.deviceName} to Matterbridge aggregator node`);
391
+ const aggregatorNode = this.agToAggregatorEndpoint.get('Matterbridge')?.aggregatorNode;
392
+ await aggregatorNode?.add(device);
393
+ }
394
+ else if (this.bridgeMode === 'childbridge') {
395
+ if (plugin.type === 'DynamicPlatform') {
396
+ this.log.info(`Adding ${pluginName}:${device.deviceName} to ${pluginName} aggregator node`);
397
+ const aggregatorNode = this.agToAggregatorEndpoint.get(pluginName)?.aggregatorNode;
398
+ await aggregatorNode?.add(device);
399
+ }
400
+ }
401
+ // TODO: Implement plugins and devices
402
+ if (plugin.registeredDevices !== undefined)
403
+ plugin.registeredDevices++;
404
+ if (plugin.addedDevices !== undefined)
405
+ plugin.addedDevices++;
406
+ // Add the device to the DeviceManager
407
+ this.devices.set(device);
408
+ this.log.info(`Added and registered bridged device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
409
+ }
410
+ async removeBridgedEndpoint(pluginName, device) {
411
+ // TODO: Implement removeBridgedEndpoint
412
+ }
413
+ async removeAllBridgedEndpoints(pluginName) {
414
+ // TODO: Implement removeAllBridgedEndpoints
415
+ }
323
416
  async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
324
417
  if (hasParameter('debug'))
325
- this.log.warn(`createCommissioningServerContext: ${pluginName} => createServerNodeContext`);
418
+ this.log.warn(`createCommissioningServerContext() for ${pluginName} => createServerNodeContext()`);
326
419
  const storageContext = this.createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName);
327
420
  return storageContext;
328
421
  }
329
422
  async importCommissioningServerContext(pluginName, device) {
330
423
  if (hasParameter('debug'))
331
- this.log.warn(`importCommissioningServerContext: ${pluginName} => createServerNodeContext`);
424
+ this.log.warn(`importCommissioningServerContext() for ${pluginName} => createServerNodeContext()`);
332
425
  const basic = device.getClusterServer(BasicInformationCluster);
333
426
  if (!basic)
334
427
  throw new Error('BasicInformationCluster not found');
335
428
  const storageContext = this.createServerNodeContext(pluginName, basic.getNodeLabelAttribute(), DeviceTypeId(device.deviceType), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductIdAttribute(), basic.getProductNameAttribute(), basic.attributes.serialNumber?.getLocal());
336
429
  return storageContext;
337
430
  }
431
+ test = false;
338
432
  async createCommisioningServer(context, pluginName) {
339
433
  if (hasParameter('debug'))
340
- this.log.warn(`createCommisioningServer: ${pluginName} => createServerNode`);
434
+ this.log.warn(`createCommisioningServer() for ${pluginName} => createServerNode()`);
341
435
  const port = this.port;
342
436
  const serverNode = await this.createServerNode(context, this.port++, this.passcode ? this.passcode++ : 20242025, this.discriminator ? this.discriminator++ : 3840);
343
437
  const commissioningServer = {
344
438
  getPort: () => port,
345
- addDevice: (device) => {
439
+ addDevice: async (device) => {
440
+ // if (hasParameter('debug')) this.log.warn('CommissioningServer.addDevice()', device.name);
346
441
  if (device instanceof Device) {
347
442
  if (hasParameter('debug'))
348
443
  this.log.warn('CommissioningServer.addDevice() => Device');
349
444
  }
350
- else if (device instanceof Aggregator) {
445
+ else if (device.name === 'MA-aggregator') {
351
446
  if (hasParameter('debug'))
352
447
  this.log.warn('CommissioningServer.addDevice() => Aggregator');
353
- const serverNode = this.csToMatterNode.get(pluginName)?.serverNode;
354
- const aggregatorNode = this.agToMatterNode.get(pluginName)?.aggregatorNode;
448
+ const serverNode = this.csToServerNode.get(pluginName)?.serverNode;
449
+ const aggregatorNode = this.agToAggregatorEndpoint.get(pluginName)?.aggregatorNode;
355
450
  if (!serverNode || !aggregatorNode)
356
451
  return;
357
- serverNode.add(aggregatorNode);
452
+ await serverNode.add(aggregatorNode);
453
+ if (!this.test) {
454
+ this.test = true;
455
+ // await this.testEndpoints();
456
+ }
358
457
  }
359
458
  },
360
459
  };
361
- this.csToMatterNode.set(pluginName, { commissioningServer, serverNode });
460
+ this.csToServerNode.set(pluginName, { commissioningServer, serverNode });
362
461
  return commissioningServer;
363
462
  }
364
463
  async createMatterAggregator(context, pluginName) {
365
464
  if (hasParameter('debug'))
366
- this.log.warn(`createMatterAggregator: ${pluginName} => createAggregatorNode`);
465
+ this.log.warn(`createMatterAggregator() for ${pluginName} => createAggregatorNode()`);
367
466
  const aggregatorNode = await this.createAggregatorNode(context);
368
467
  const aggregator = {
468
+ name: 'MA-aggregator',
369
469
  addBridgedDevice: (device) => {
370
470
  if (hasParameter('debug'))
371
471
  this.log.warn('Aggregator.addBridgedDevice() => not inplemented');
@@ -375,13 +475,13 @@ export class MatterbridgeEdge extends Matterbridge {
375
475
  this.log.warn('Aggregator.removeBridgedDevice() => not inplemented');
376
476
  },
377
477
  };
378
- this.agToMatterNode.set(pluginName, { aggregator, aggregatorNode });
478
+ this.agToAggregatorEndpoint.set(pluginName, { aggregator, aggregatorNode });
379
479
  return aggregator;
380
480
  }
381
481
  async showCommissioningQRCode(commissioningServer, storageContext, nodeContext, pluginName) {
382
482
  if (hasParameter('debug'))
383
- this.log.warn(`showCommissioningQRCode: ${pluginName} => startServerNode`);
384
- const serverNode = this.csToMatterNode.get(pluginName)?.serverNode;
483
+ this.log.warn(`showCommissioningQRCode() for ${pluginName} => startServerNode()`);
484
+ const serverNode = this.csToServerNode.get(pluginName)?.serverNode;
385
485
  if (!commissioningServer || !storageContext || !serverNode)
386
486
  return;
387
487
  await this.startServerNode(serverNode);
@@ -400,27 +500,330 @@ export class MatterbridgeEdge extends Matterbridge {
400
500
  }
401
501
  async startController() {
402
502
  if (hasParameter('debug'))
403
- this.log.warn(`setDeviceReachability() => not inplemented`);
503
+ this.log.warn(`startController() => do nothing`);
504
+ }
505
+ async testEndpoints() {
506
+ const max = 10;
507
+ if (!this.matterbridgeContext)
508
+ return;
509
+ const aggregatorNode = this.agToAggregatorEndpoint.get('Matterbridge')?.aggregatorNode;
510
+ if (!aggregatorNode)
511
+ return;
512
+ /*
513
+ this.log.notice(`Creating OnOffLight1`);
514
+ const lightEndpoint = new MatterbridgeEndpoint(onOffLight, { uniqueStorageKey: 'OnOffLight1' }, true);
515
+ lightEndpoint.addDeviceType(bridgedNode);
516
+ lightEndpoint.createDefaultBridgedDeviceBasicInformationClusterServer('OnOffLight 1', '123456789', 0xfff1, 'Matterbridge', 'Light');
517
+ lightEndpoint.addDeviceType(powerSource);
518
+ lightEndpoint.createDefaultPowerSourceWiredClusterServer();
519
+ lightEndpoint.addDeviceType(electricalSensor);
520
+ lightEndpoint.addClusterServer(lightEndpoint.getDefaultElectricalEnergyMeasurementClusterServer());
521
+ lightEndpoint.addClusterServer(lightEndpoint.getDefaultElectricalPowerMeasurementClusterServer());
522
+ lightEndpoint.addRequiredClusterServers(lightEndpoint);
523
+ this.log.notice(`Adding OnOffLight1 to ${await this.matterbridgeContext.get<string>('storeId')} aggregator`);
524
+ await aggregatorNode.add(lightEndpoint);
525
+ logEndpoint(EndpointServer.forEndpoint(lightEndpoint));
526
+
527
+ this.log.notice(`Creating Outlet1`);
528
+ const outletEndpoint = new MatterbridgeEndpoint(onOffOutlet, { uniqueStorageKey: 'OnOffOutlet1' }, true);
529
+ outletEndpoint.addDeviceType(bridgedNode);
530
+ outletEndpoint.createDefaultBridgedDeviceBasicInformationClusterServer('OnOffOutlet 1', '123456789', 0xfff1, 'Matterbridge', 'Outlet');
531
+ outletEndpoint.addDeviceType(powerSource);
532
+ outletEndpoint.createDefaultPowerSourceReplaceableBatteryClusterServer();
533
+ outletEndpoint.addDeviceType(electricalSensor);
534
+ outletEndpoint.addClusterServer(outletEndpoint.getDefaultElectricalEnergyMeasurementClusterServer());
535
+ outletEndpoint.addClusterServer(outletEndpoint.getDefaultElectricalPowerMeasurementClusterServer());
536
+ outletEndpoint.addRequiredClusterServers(outletEndpoint);
537
+ const input0 = outletEndpoint.addChildDeviceTypeWithClusterServer('Input:0', [genericSwitch], undefined, undefined, true);
538
+ const input1 = outletEndpoint.addChildDeviceTypeWithClusterServer('Input:1', [genericSwitch], undefined, undefined, true);
539
+ this.log.notice(`Adding OnOffOutlet1 to ${await this.matterbridgeContext.get<string>('storeId')} aggregator`);
540
+ await aggregatorNode.add(outletEndpoint);
541
+ logEndpoint(EndpointServer.forEndpoint(outletEndpoint));
542
+ */
543
+ this.log.notice(`Creating switchEnpoint2`);
544
+ const switchEnpoint2 = new EndpointNode(GenericSwitchDevice.with(BridgedDeviceBasicInformationServer, SwitchServer.with('MomentarySwitch', 'MomentarySwitchLongPress', 'MomentarySwitchMultiPress', 'MomentarySwitchRelease')), {
545
+ id: 'GenericSwitch',
546
+ bridgedDeviceBasicInformation: {
547
+ vendorId: VendorId(await this.matterbridgeContext.get('vendorId')),
548
+ vendorName: await this.matterbridgeContext.get('vendorName'),
549
+ productName: 'GenericSwitch',
550
+ productLabel: 'GenericSwitch',
551
+ nodeLabel: 'GenericSwitch',
552
+ serialNumber: 'SN 0x123456739',
553
+ uniqueId: '0x123456739',
554
+ reachable: true,
555
+ },
556
+ switch: {
557
+ numberOfPositions: 2,
558
+ currentPosition: 0,
559
+ multiPressMax: 2,
560
+ },
561
+ });
562
+ this.log.notice(`Adding switchEnpoint2 to ${await this.matterbridgeContext.get('storeId')} aggregator`);
563
+ await aggregatorNode.add(switchEnpoint2);
564
+ logEndpoint(EndpointServer.forEndpoint(switchEnpoint2));
565
+ if (switchEnpoint2.behaviors.has(SwitchServer))
566
+ this.log.notice(`SwitchServer found`);
567
+ switchEnpoint2.act((agent) => agent['switch'].events['initialPress'].emit({ newPosition: 1 }, agent.context));
568
+ const device = new MatterbridgeEndpoint(genericSwitch, { uniqueStorageKey: 'GenericSwitch 2' }, true);
569
+ device.createDefaultSwitchClusterServer();
570
+ device.addRequiredClusterServers(device);
571
+ await aggregatorNode.add(device);
572
+ logEndpoint(EndpointServer.forEndpoint(device));
573
+ await device.triggerSwitchEvent('Single', this.log);
574
+ await device.triggerSwitchEvent('Double', this.log);
575
+ await device.triggerSwitchEvent('Long', this.log);
576
+ const device1 = new MatterbridgeEndpoint(genericSwitch, { uniqueStorageKey: 'GenericSwitch 3' }, true);
577
+ device1.createDefaultLatchingSwitchClusterServer();
578
+ device1.addRequiredClusterServers(device1);
579
+ await aggregatorNode.add(device1);
580
+ logEndpoint(EndpointServer.forEndpoint(device1));
581
+ await device1.triggerSwitchEvent('Press', this.log);
582
+ await device1.triggerSwitchEvent('Release', this.log);
583
+ /*
584
+ this.log.notice(`Creating TestLight`);
585
+ const matterbridgeDevice = new MatterbridgeEndpoint(onOffLight, { uniqueStorageKey: 'Test .Light:2' }, true);
586
+ matterbridgeDevice.behaviors.require(MatterbridgeIdentifyServer, {
587
+ identifyTime: 0,
588
+ identifyType: Identify.IdentifyType.None,
589
+ });
590
+ matterbridgeDevice.behaviors.require(GroupsServer);
591
+ matterbridgeDevice.behaviors.require(MatterbridgeOnOffServer, {
592
+ onOff: false,
593
+ });
594
+ matterbridgeDevice.behaviors.require(MatterbridgeLevelControlServer, {
595
+ currentLevel: 0,
596
+ options: { executeIfOff: false },
597
+ });
598
+ matterbridgeDevice.behaviors.require(MatterbridgeColorControlServer.with(ColorControl.Feature.HueSaturation, ColorControl.Feature.Xy, ColorControl.Feature.ColorTemperature), {
599
+ colorCapabilities: { xy: true, hueSaturation: true, colorLoop: false, enhancedHue: false, colorTemperature: true },
600
+ colorMode: ColorControl.ColorMode.ColorTemperatureMireds,
601
+ enhancedColorMode: ColorControl.EnhancedColorMode.ColorTemperatureMireds,
602
+ options: { executeIfOff: false },
603
+ numberOfPrimaries: null,
604
+ currentX: 24939,
605
+ currentY: 24701,
606
+ currentHue: 0,
607
+ currentSaturation: 0,
608
+ colorTemperatureMireds: 450,
609
+ colorTempPhysicalMinMireds: 150,
610
+ colorTempPhysicalMaxMireds: 450,
611
+ coupleColorTempToLevelMinMireds: 150,
612
+ startUpColorTemperatureMireds: null,
613
+ });
614
+ matterbridgeDevice.behaviors.require(BridgedDeviceBasicInformationServer, {
615
+ vendorId: VendorId(await this.matterbridgeContext.get<number>('vendorId')),
616
+ vendorName: await this.matterbridgeContext.get<string>('vendorName'),
617
+ productName: 'TestLight2',
618
+ productLabel: 'TestLight2',
619
+ nodeLabel: 'TestLight2',
620
+ serialNumber: 'SN 0x123456789',
621
+ uniqueId: '0x123456789',
622
+ reachable: true,
623
+ });
624
+
625
+ // await matterbridgeDevice.setTagList(null, 0x07, 1, 'endpoint 2');
626
+ // await matterbridgeDevice.configureColorControlCluster(false, false, true, ColorControl.ColorMode.ColorTemperatureMireds);
627
+
628
+ this.log.notice(`Adding TestLight`);
629
+ await aggregatorNode?.add(matterbridgeDevice);
630
+
631
+ this.log.notice(`Creating switchEnpoint1`);
632
+ const switchEnpoint2 = new EndpointNode(
633
+ GenericSwitchDevice.with(DescriptorServer.with(Descriptor.Feature.TagList), BridgedDeviceBasicInformationServer, SwitchServer.with('MomentarySwitch', 'MomentarySwitchLongPress', 'MomentarySwitchMultiPress', 'MomentarySwitchRelease')),
634
+ {
635
+ id: 'GenericSwitch1',
636
+ descriptor: {
637
+ tagList: [{ mfgCode: null, namespaceId: 0x07, tag: 1, label: 'Switch1' }],
638
+ },
639
+ bridgedDeviceBasicInformation: {
640
+ vendorId: VendorId(await this.matterbridgeContext.get<number>('vendorId')),
641
+ vendorName: await this.matterbridgeContext.get<string>('vendorName'),
642
+
643
+ productName: 'GenericSwitch',
644
+ productLabel: 'GenericSwitch',
645
+ nodeLabel: 'GenericSwitch',
646
+
647
+ serialNumber: 'SN 0x1234567397',
648
+ uniqueId: '0x1234567397',
649
+ reachable: true,
650
+ },
651
+ switch: {
652
+ numberOfPositions: 2,
653
+ currentPosition: 0,
654
+ multiPressMax: 2,
655
+ },
656
+ },
657
+ );
658
+ this.log.notice(`Creating switchEnpoint2`);
659
+ await aggregatorNode?.add(switchEnpoint2);
660
+ */
661
+ /*
662
+ const lightEndpoint1 = new EndpointNode(ColorTemperatureLightDevice.with(BridgedDeviceBasicInformationServer), {
663
+ // }, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer), {
664
+ id: 'OnOffLight',
665
+ bridgedDeviceBasicInformation: {
666
+ vendorId: VendorId(await this.matterbridgeContext.get<number>('vendorId')),
667
+ vendorName: await this.matterbridgeContext.get<string>('vendorName'),
668
+
669
+ productName: 'Light',
670
+ productLabel: 'Light',
671
+ nodeLabel: 'Light',
672
+
673
+ serialNumber: 'SN 0x123456789',
674
+ uniqueId: '0x123456789',
675
+ reachable: true,
676
+ },
677
+ levelControl: {
678
+ currentLevel: 0,
679
+ options: { executeIfOff: false, coupleColorTempToLevel: false },
680
+ },
681
+ colorControl: {
682
+ colorTemperatureMireds: 450,
683
+ colorMode: ColorControl.ColorMode.ColorTemperatureMireds,
684
+ colorTempPhysicalMinMireds: 150,
685
+ colorTempPhysicalMaxMireds: 450,
686
+ coupleColorTempToLevelMinMireds: 150,
687
+ startUpColorTemperatureMireds: 450,
688
+ },
689
+ });
690
+ lightEndpoint1.behaviors.require(ColorControlServer.with('ColorTemperature'), {
691
+ colorTemperatureMireds: 450,
692
+ colorMode: ColorControl.ColorMode.ColorTemperatureMireds,
693
+ colorTempPhysicalMinMireds: 150,
694
+ colorTempPhysicalMaxMireds: 450,
695
+ coupleColorTempToLevelMinMireds: 150,
696
+ startUpColorTemperatureMireds: 450,
697
+ });
698
+ await aggregatorNode?.add(lightEndpoint1);
699
+
700
+ /*
701
+ lightEndpoint1.behaviors.require(MatterbridgeIdentifyServer);
702
+ lightEndpoint1.behaviors.require(MatterbridgeOnOffServer);
703
+ lightEndpoint1.behaviors.require(MatterbridgeLevelControlServer);
704
+ lightEndpoint1.behaviors.require(MatterbridgeColorControlServer, {
705
+ colorTemperatureMireds: 250,
706
+ colorMode: ColorControl.ColorMode.ColorTemperatureMireds,
707
+ colorTempPhysicalMinMireds: 150,
708
+ colorTempPhysicalMaxMireds: 450,
709
+ coupleColorTempToLevelMinMireds: 150,
710
+ startUpColorTemperatureMireds: 450,
711
+ });
712
+ await aggregatorNode?.add(lightEndpoint1);
713
+
714
+ this.log.notice(`Creating switchEnpoint2`);
715
+ const switchEnpoint2 = new EndpointNode(GenericSwitchDevice.with(BridgedDeviceBasicInformationServer, SwitchServer.with('MomentarySwitch', 'MomentarySwitchLongPress', 'MomentarySwitchMultiPress', 'MomentarySwitchRelease')), {
716
+ id: 'GenericSwitch',
717
+ bridgedDeviceBasicInformation: {
718
+ vendorId: VendorId(await this.matterbridgeContext.get<number>('vendorId')),
719
+ vendorName: await this.matterbridgeContext.get<string>('vendorName'),
720
+
721
+ productName: 'GenericSwitch',
722
+ productLabel: 'GenericSwitch',
723
+ nodeLabel: 'GenericSwitch',
724
+
725
+ serialNumber: 'SN 0x123456739',
726
+ uniqueId: '0x123456739',
727
+ reachable: true,
728
+ },
729
+ switch: {
730
+ numberOfPositions: 2,
731
+ currentPosition: 0,
732
+ multiPressMax: 2,
733
+ },
734
+ });
735
+ await aggregatorNode?.add(switchEnpoint2);
736
+ */
737
+ /*
738
+ for (let i = 1; i <= max; i++) {
739
+ this.log.notice(`Creating lightEndpoint${i}`);
740
+ const lightEndpoint = new MatterbridgeEndpoint(onOffLight, { uniqueStorageKey: 'OnOffLight' + i });
741
+ lightEndpoint.addClusterServer(lightEndpoint.getDefaultBridgedDeviceBasicInformationClusterServer('OnOffLight' + i, '123456789', 0xfff1, 'Matterbridge', 'Light'));
742
+ this.log.notice(`Adding lightEndpoint${i} to ${await this.matterbridgeContext.get<string>('storeId')} aggregator`);
743
+ await aggregatorNode?.add(lightEndpoint);
744
+ setInterval(async () => {
745
+ const state = lightEndpoint.getAttribute(OnOffCluster.id, 'onOff');
746
+ lightEndpoint.setAttribute(OnOffCluster.id, 'onOff', !state);
747
+ this.log.notice(`Setting state for lightEndpoint${i} from:`, state, 'to:', !state);
748
+ }, 10000);
749
+ }
750
+ for (let i = 1; i <= max; i++) {
751
+ this.log.notice(`Creating outletEndpoint${i}`);
752
+ const lightEndpoint = new MatterbridgeEndpoint(onOffOutlet, { uniqueStorageKey: 'OnOffOutlet' + i });
753
+ lightEndpoint.addClusterServer(lightEndpoint.getDefaultBridgedDeviceBasicInformationClusterServer('OnOffOutlet' + i, '123456789', 0xfff1, 'Matterbridge', 'Outlet'));
754
+ this.log.notice(`Adding outletEndpoint${i} to ${await this.matterbridgeContext.get<string>('storeId')} aggregator`);
755
+ await aggregatorNode?.add(lightEndpoint);
756
+ setInterval(async () => {
757
+ const state = lightEndpoint.getAttribute(OnOffCluster.id, 'onOff');
758
+ lightEndpoint.setAttribute(OnOffCluster.id, 'onOff', !state);
759
+ this.log.notice(`Setting state for outletEndpoint${i} from:`, state, 'to:', !state);
760
+ }, 10000);
761
+ }
762
+ */
404
763
  }
405
764
  }
765
+ /*
766
+ /*
767
+ matterbridgeDevice.behaviors.require(MatterbridgeColorControlServer.with(ColorControl.Feature.HueSaturation, ColorControl.Feature.Xy, ColorControl.Feature.ColorTemperature), {
768
+ colorCapabilities: { xy: true, hueSaturation: true, colorLoop: false, enhancedHue: false, colorTemperature: true },
769
+ colorMode: ColorControl.ColorMode.ColorTemperatureMireds,
770
+ enhancedColorMode: ColorControl.EnhancedColorMode.ColorTemperatureMireds,
771
+ options: { executeIfOff: false },
772
+ numberOfPrimaries: null,
773
+ currentX: 24939,
774
+ currentY: 24701,
775
+ currentHue: 0,
776
+ currentSaturation: 0,
777
+ colorTemperatureMireds: 450,
778
+ colorTempPhysicalMinMireds: 150,
779
+ colorTempPhysicalMaxMireds: 450,
780
+ coupleColorTempToLevelMinMireds: 150,
781
+ startUpColorTemperatureMireds: null,
782
+ });
783
+ matterbridgeDevice.behaviors.require(MatterbridgeColorControlServer.with(ColorControl.Feature.ColorTemperature), {
784
+ colorCapabilities: { xy: false, hueSaturation: false, colorLoop: false, enhancedHue: false, colorTemperature: true },
785
+ colorMode: ColorControl.ColorMode.ColorTemperatureMireds,
786
+ enhancedColorMode: ColorControl.EnhancedColorMode.ColorTemperatureMireds,
787
+ options: { executeIfOff: false },
788
+ numberOfPrimaries: null,
789
+ colorTemperatureMireds: 450,
790
+ colorTempPhysicalMinMireds: 150,
791
+ colorTempPhysicalMaxMireds: 450,
792
+ coupleColorTempToLevelMinMireds: 150,
793
+ startUpColorTemperatureMireds: null,
794
+ });
795
+ */
796
+ /*
797
+ matterbridgeDevice.behaviors.require(MatterbridgeColorControlServer.with(ColorControl.Feature.HueSaturation, ColorControl.Feature.Xy), {
798
+ colorCapabilities: { xy: true, hueSaturation: true, colorLoop: false, enhancedHue: false, colorTemperature: false },
799
+ colorMode: ColorControl.ColorMode.CurrentHueAndCurrentSaturation,
800
+ enhancedColorMode: ColorControl.EnhancedColorMode.CurrentHueAndCurrentSaturation,
801
+ options: { executeIfOff: false },
802
+ numberOfPrimaries: null,
803
+ currentX: 24939,
804
+ currentY: 24701,
805
+ currentHue: 0,
806
+ currentSaturation: 0,
807
+ });
406
808
  // node dist/matterbridgeEdge.js MatterbridgeEdge -debug -ssl -frontend 443
407
809
  if (process.argv.includes('MatterbridgeEdge')) {
408
- const matterbridge = await MatterbridgeEdge.loadInstance(true);
409
- process.on('SIGINT', async function () {
410
- // eslint-disable-next-line no-console
411
- console.log('Caught interrupt signal');
412
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
413
- if (matterbridge)
414
- await matterbridge.cleanup('shutting down...', false);
415
- // if (matterbridge && matterbridge.matterServerNode && matterbridge.matterServerNodeContext) await matterbridge.stopServerNode(matterbridge.matterServerNode, matterbridge.matterServerNodeContext);
416
- const exit = setTimeout(() => {
417
- // eslint-disable-next-line no-console
418
- console.log('Exiting after caught interrupt signal');
419
- process.exit();
420
- }, 10000);
421
- exit.unref();
422
- });
810
+ const matterbridge = await MatterbridgeEdge.loadInstance(true);
811
+
812
+ process.on('SIGINT', async function () {
813
+ // eslint-disable-next-line no-console
814
+ console.log('Caught interrupt signal');
815
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
816
+ if (matterbridge) await (matterbridge as any).cleanup('shutting down...', false);
817
+ // if (matterbridge && matterbridge.matterServerNode && matterbridge.matterServerNodeContext) await matterbridge.stopServerNode(matterbridge.matterServerNode, matterbridge.matterServerNodeContext);
818
+ const exit = setTimeout(() => {
819
+ // eslint-disable-next-line no-console
820
+ console.log('Exiting after caught interrupt signal');
821
+ process.exit();
822
+ }, 10000);
823
+ exit.unref();
824
+ });
423
825
  }
826
+ */
424
827
  /*
425
828
  async startBridgeNode(): Promise<void> {
426
829
  this.log.notice(`Creating lightEndpoint1`);