matterbridge 1.1.11 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -3
- package/README.md +58 -13
- package/dist/matterbridge.d.ts +1 -1
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +103 -69
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +2 -5
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
- package/dist/matterbridgeAccessoryPlatform.js +4 -7
- package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +6 -1
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +68 -10
- package/dist/matterbridgeDevice.js.map +1 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +2 -5
- package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
- package/dist/matterbridgeDynamicPlatform.js +4 -7
- package/dist/matterbridgeDynamicPlatform.js.map +1 -1
- package/dist/matterbridgePlatform.d.ts +44 -0
- package/dist/matterbridgePlatform.d.ts.map +1 -0
- package/dist/matterbridgePlatform.js +93 -0
- package/dist/matterbridgePlatform.js.map +1 -0
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [1.1.
|
|
5
|
+
## [1.1.12] - 2024-03-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- [platform]: Added async loadConfig() and async saveConfig() to store plugin config.
|
|
10
|
+
- [platform]: Added a config: Config (JSON) property to platforms to store plugin config.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- [dependencies]: Updated dependencies.
|
|
15
|
+
|
|
16
|
+
## [1.1.11] - 2024-03-19
|
|
6
17
|
|
|
7
18
|
### Added
|
|
8
19
|
|
|
9
20
|
- [frontend]: Frontend got updated to 0.8.3.
|
|
10
21
|
|
|
11
|
-
## [1.1.10] - 2024-03-
|
|
22
|
+
## [1.1.10] - 2024-03-17
|
|
12
23
|
|
|
13
24
|
### Added
|
|
14
25
|
|
|
@@ -17,7 +28,7 @@ All notable changes to this project will be documented in this file.
|
|
|
17
28
|
- [frontend]: Enable and disable plugin are now available. Restart Matteerbridge after.
|
|
18
29
|
- [frontend]: Frontend got updated to 0.8.2.
|
|
19
30
|
|
|
20
|
-
## [1.1.9] - 2024-03-
|
|
31
|
+
## [1.1.9] - 2024-03-16
|
|
21
32
|
|
|
22
33
|
### Added
|
|
23
34
|
|
package/README.md
CHANGED
|
@@ -96,9 +96,11 @@ Devices page:
|
|
|
96
96
|
|
|
97
97
|
### Accessory platform example
|
|
98
98
|
|
|
99
|
-
This an example of an accessory platform plugin.
|
|
99
|
+
This an example of an accessory platform plugin.
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
It exposes a cover device that continouosly moves position and shows how to use the command handlers (you can control the device).
|
|
102
|
+
|
|
103
|
+
An Accessory platform plugin only exposes one device.
|
|
102
104
|
|
|
103
105
|
[See the plugin homepage here](https://github.com/Luligu/matterbridge-example-accessory-platform)
|
|
104
106
|
|
|
@@ -106,7 +108,11 @@ Accessory platform plugins only expose one device.
|
|
|
106
108
|
|
|
107
109
|
This an example of a dynamic platform plugin.
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
It exposes a switch with onOff, a light with onOff-levelControl-colorControl, an outlet with onOff and a cover device.
|
|
112
|
+
|
|
113
|
+
All these devices continouosly change state and position. The plugin also shows how to use all the command handlers (you can control all the devices).
|
|
114
|
+
|
|
115
|
+
A Dynamic platform plugin exposes as many devices as you need (the limit for the Home app is 150 accessories for bridge).
|
|
110
116
|
|
|
111
117
|
Matterbridge can run as many plugins as you want.
|
|
112
118
|
|
|
@@ -124,10 +130,40 @@ Matterbridge can run as many plugins as you want.
|
|
|
124
130
|
|
|
125
131
|
[Room plugin with history](https://github.com/Luligu/matterbridge-eve-room)
|
|
126
132
|
|
|
127
|
-
## How to install a plugin
|
|
133
|
+
## How to install and register a production-level plugin (from npm)
|
|
134
|
+
|
|
135
|
+
To install i.e. https://github.com/Luligu/matterbridge-zigbee2mqtt
|
|
136
|
+
|
|
137
|
+
On windows:
|
|
138
|
+
```
|
|
139
|
+
cd $HOME\Matterbridge
|
|
140
|
+
npm install -g matterbridge-zigbee2mqtt
|
|
141
|
+
matterbridge -add matterbridge-zigbee2mqtt
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
On linux:
|
|
145
|
+
```
|
|
146
|
+
cd ~/Matterbridge
|
|
147
|
+
sudo npm install -g matterbridge-zigbee2mqtt
|
|
148
|
+
matterbridge -add matterbridge-zigbee2mqtt
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## How to install and register a plugin for development (from git)
|
|
128
152
|
|
|
129
153
|
To install i.e. https://github.com/Luligu/matterbridge-example-accessory-platform
|
|
130
154
|
|
|
155
|
+
On windows:
|
|
156
|
+
```
|
|
157
|
+
cd $HOME\Matterbridge
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
On linux:
|
|
161
|
+
```
|
|
162
|
+
cd ~/Matterbridge
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
then
|
|
166
|
+
|
|
131
167
|
```
|
|
132
168
|
git clone https://github.com/Luligu/matterbridge-example-accessory-platform
|
|
133
169
|
cd matterbridge-example-accessory-platform
|
|
@@ -139,28 +175,28 @@ Then add the plugin to Matterbridge
|
|
|
139
175
|
matterbridge -add .\
|
|
140
176
|
```
|
|
141
177
|
|
|
142
|
-
|
|
178
|
+
## How to add a plugin to Matterbridge
|
|
143
179
|
|
|
144
180
|
```
|
|
145
|
-
matterbridge -add
|
|
181
|
+
matterbridge -add [plugin path]
|
|
146
182
|
```
|
|
147
183
|
|
|
148
|
-
|
|
184
|
+
## How to remove a plugin from Matterbridge
|
|
149
185
|
|
|
150
186
|
```
|
|
151
|
-
matterbridge -remove
|
|
187
|
+
matterbridge -remove [plugin path]
|
|
152
188
|
```
|
|
153
189
|
|
|
154
|
-
|
|
190
|
+
## How to disable a registered plugin
|
|
155
191
|
|
|
156
192
|
```
|
|
157
|
-
matterbridge -disable
|
|
193
|
+
matterbridge -disable [plugin path]
|
|
158
194
|
```
|
|
159
195
|
|
|
160
|
-
|
|
196
|
+
## How to enable a registered plugin
|
|
161
197
|
|
|
162
198
|
```
|
|
163
|
-
matterbridge -enable
|
|
199
|
+
matterbridge -enable [plugin path]
|
|
164
200
|
```
|
|
165
201
|
|
|
166
202
|
## How to create your plugin
|
|
@@ -178,6 +214,15 @@ Add your plugin logic in platform.ts.
|
|
|
178
214
|
|
|
179
215
|
## MatterbridgeDynamicPlatform and MatterbridgeAccessoryPlatform api
|
|
180
216
|
|
|
217
|
+
### name: string
|
|
218
|
+
The plugin name.
|
|
219
|
+
|
|
220
|
+
### type: string
|
|
221
|
+
The plugin platform type.
|
|
222
|
+
|
|
223
|
+
### config: object
|
|
224
|
+
The plugin config (loaded before onStart() is called and save after onShutdown() is called).
|
|
225
|
+
|
|
181
226
|
### async onStart(reason?: string)
|
|
182
227
|
The method onStart() is where you have to create your MatterbridgeDevice and add all needed clusters and command handlers.
|
|
183
228
|
|
|
@@ -204,7 +249,7 @@ You can unregister one or more device.
|
|
|
204
249
|
### async unregisterAllDevices()
|
|
205
250
|
You can unregister all devices you added.
|
|
206
251
|
|
|
207
|
-
It can
|
|
252
|
+
It can be useful to call this method from onShutdown() if you don't want to keep all the devices during development.
|
|
208
253
|
|
|
209
254
|
## MatterbridgeDevice api
|
|
210
255
|
|
package/dist/matterbridge.d.ts
CHANGED
|
@@ -243,7 +243,7 @@ export declare class Matterbridge extends EventEmitter {
|
|
|
243
243
|
* Creates a matter commissioning server.
|
|
244
244
|
*
|
|
245
245
|
* @param {StorageContext} context - The storage context.
|
|
246
|
-
* @param {string}
|
|
246
|
+
* @param {string} pluginName - The name of the commissioning server.
|
|
247
247
|
* @returns {CommissioningServer} The created commissioning server.
|
|
248
248
|
*/
|
|
249
249
|
private createCommisioningServer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matterbridge.d.ts","sourceRoot":"","sources":["../src/matterbridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;AAEH,OAAO,EAAE,kBAAkB,EAAgC,MAAM,yBAAyB,CAAC;AAQ3F,OAAO,YAAY,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"matterbridge.d.ts","sourceRoot":"","sources":["../src/matterbridge.ts"],"names":[],"mappings":"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"}
|
package/dist/matterbridge.js
CHANGED
|
@@ -470,6 +470,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
470
470
|
for (const plugin of this.registeredPlugins) {
|
|
471
471
|
if (plugin.platform)
|
|
472
472
|
await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
|
|
473
|
+
if (plugin.platform)
|
|
474
|
+
await plugin.platform.saveConfig();
|
|
473
475
|
}
|
|
474
476
|
// Set reachability to false
|
|
475
477
|
/*
|
|
@@ -565,14 +567,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
565
567
|
*/
|
|
566
568
|
async addDevice(pluginName, device) {
|
|
567
569
|
if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
|
|
568
|
-
this.log.error(`Adding device ${dev}${device.
|
|
570
|
+
this.log.error(`Adding device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
569
571
|
return;
|
|
570
572
|
}
|
|
571
|
-
this.log.debug(`Adding device ${dev}${device.
|
|
573
|
+
this.log.debug(`Adding device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
572
574
|
// Check if the plugin is registered
|
|
573
575
|
const plugin = this.registeredPlugins.find((plugin) => plugin.name === pluginName);
|
|
574
576
|
if (!plugin) {
|
|
575
|
-
this.log.error(`Error adding device ${dev}${device.
|
|
577
|
+
this.log.error(`Error adding device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
576
578
|
return;
|
|
577
579
|
}
|
|
578
580
|
// Register and add the device to matterbridge aggregator in bridge mode
|
|
@@ -583,14 +585,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
583
585
|
plugin.registeredDevices++;
|
|
584
586
|
if (plugin.addedDevices !== undefined)
|
|
585
587
|
plugin.addedDevices++;
|
|
586
|
-
this.log.info(`Added and registered device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.
|
|
588
|
+
this.log.info(`Added and registered device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
587
589
|
}
|
|
588
590
|
// Only register the device in childbridge mode
|
|
589
591
|
if (this.bridgeMode === 'childbridge') {
|
|
590
592
|
this.registeredDevices.push({ plugin: pluginName, device, added: false });
|
|
591
593
|
if (plugin.registeredDevices !== undefined)
|
|
592
594
|
plugin.registeredDevices++;
|
|
593
|
-
this.log.info(`Registered device ${dev}${device.
|
|
595
|
+
this.log.info(`Registered device ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
594
596
|
}
|
|
595
597
|
}
|
|
596
598
|
/**
|
|
@@ -601,14 +603,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
601
603
|
*/
|
|
602
604
|
async addBridgedDevice(pluginName, device) {
|
|
603
605
|
if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
|
|
604
|
-
this.log.error(`Adding bridged device ${dev}${device.
|
|
606
|
+
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
605
607
|
return;
|
|
606
608
|
}
|
|
607
|
-
this.log.debug(`Adding bridged device ${
|
|
609
|
+
this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
608
610
|
// Check if the plugin is registered
|
|
609
611
|
const plugin = this.registeredPlugins.find((plugin) => plugin.name === pluginName);
|
|
610
612
|
if (!plugin) {
|
|
611
|
-
this.log.error(`Error adding bridged device ${dev}${device.
|
|
613
|
+
this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
612
614
|
return;
|
|
613
615
|
}
|
|
614
616
|
// Register and add the device to matterbridge aggregator in bridge mode
|
|
@@ -619,36 +621,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
619
621
|
plugin.registeredDevices++;
|
|
620
622
|
if (plugin.addedDevices !== undefined)
|
|
621
623
|
plugin.addedDevices++;
|
|
622
|
-
this.log.info(`Added and registered bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.
|
|
624
|
+
this.log.info(`Added and registered bridged device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
623
625
|
}
|
|
624
626
|
// Only register the device in childbridge mode
|
|
625
627
|
if (this.bridgeMode === 'childbridge') {
|
|
626
628
|
this.registeredDevices.push({ plugin: pluginName, device, added: false });
|
|
627
629
|
if (plugin.registeredDevices !== undefined)
|
|
628
630
|
plugin.registeredDevices++;
|
|
629
|
-
this.log.info(`Registered bridged device ${dev}${device.
|
|
631
|
+
this.log.info(`Registered bridged device ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
630
632
|
}
|
|
631
633
|
}
|
|
632
634
|
async removeBridgedDevice(pluginName, device) {
|
|
633
635
|
if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
|
|
634
|
-
this.log.error(`Removing bridged device ${dev}${device.
|
|
636
|
+
this.log.error(`Removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
635
637
|
return;
|
|
636
638
|
}
|
|
637
|
-
this.log.debug(`Removing bridged device ${
|
|
639
|
+
this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
638
640
|
// Check if the plugin is registered
|
|
639
641
|
const plugin = this.findPlugin(pluginName);
|
|
640
642
|
if (!plugin) {
|
|
641
|
-
this.log.error(`Error removing bridged device ${dev}${device.
|
|
643
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
642
644
|
return;
|
|
643
645
|
}
|
|
644
646
|
if (this.bridgeMode === 'childbridge' && !plugin.aggregator) {
|
|
645
|
-
this.log.error(`Error removing bridged device ${dev}${device.
|
|
647
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} aggregator not found`);
|
|
646
648
|
return;
|
|
647
649
|
}
|
|
650
|
+
/*
|
|
648
651
|
if (this.bridgeMode === 'childbridge' && !plugin.connected) {
|
|
649
|
-
|
|
650
|
-
|
|
652
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not connected`);
|
|
653
|
+
return;
|
|
651
654
|
}
|
|
655
|
+
*/
|
|
652
656
|
// Register and add the device to matterbridge aggregator in bridge mode
|
|
653
657
|
if (this.bridgeMode === 'bridge') {
|
|
654
658
|
this.matterAggregator.removeBridgedDevice(device);
|
|
@@ -662,12 +666,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
662
666
|
plugin.registeredDevices--;
|
|
663
667
|
if (plugin.addedDevices !== undefined)
|
|
664
668
|
plugin.addedDevices--;
|
|
665
|
-
this.log.info(`
|
|
669
|
+
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
666
670
|
}
|
|
667
671
|
// Only register the device in childbridge mode
|
|
668
672
|
if (this.bridgeMode === 'childbridge') {
|
|
669
673
|
if (plugin.type === 'AccessoryPlatform') {
|
|
670
|
-
this.log.warn(`Removing bridged device ${dev}${device.
|
|
674
|
+
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) for plugin ${plg}${pluginName}${wr} error: AccessoryPlatform not supported in childbridge mode`);
|
|
671
675
|
}
|
|
672
676
|
else if (plugin.type === 'DynamicPlatform') {
|
|
673
677
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
@@ -678,11 +682,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
678
682
|
});
|
|
679
683
|
plugin.aggregator.removeBridgedDevice(device);
|
|
680
684
|
}
|
|
685
|
+
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
681
686
|
if (plugin.registeredDevices !== undefined)
|
|
682
687
|
plugin.registeredDevices--;
|
|
683
688
|
if (plugin.addedDevices !== undefined)
|
|
684
689
|
plugin.addedDevices--;
|
|
685
|
-
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
686
690
|
}
|
|
687
691
|
}
|
|
688
692
|
/**
|
|
@@ -918,6 +922,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
918
922
|
plugin.registeredDevices = 0;
|
|
919
923
|
plugin.addedDevices = 0;
|
|
920
924
|
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
925
|
+
await platform.loadConfig();
|
|
921
926
|
this.log.info(`Loaded plugin ${plg}${plugin.name}${db} type ${typ}${platform.type}${db} (entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
|
|
922
927
|
if (start)
|
|
923
928
|
this.startPlugin(plugin, message); // No await do it asyncronously
|
|
@@ -966,8 +971,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
966
971
|
await this.matterServer.start();
|
|
967
972
|
this.log.info('Matter server started');
|
|
968
973
|
if (hasParameter('discover')) {
|
|
969
|
-
const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
970
|
-
console.log(discover);
|
|
974
|
+
//const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
975
|
+
//console.log(discover);
|
|
971
976
|
}
|
|
972
977
|
this.log.info(`Commissioning controller is already commisioned: ${this.commissioningController.isCommissioned()}`);
|
|
973
978
|
const nodes = this.commissioningController.getCommissionedNodes();
|
|
@@ -979,11 +984,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
979
984
|
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
980
985
|
// Plugins are started and configured by callback when Matterbridge is commissioned
|
|
981
986
|
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
982
|
-
this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge
|
|
987
|
+
this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
|
|
983
988
|
if (!this.matterbridgeContext) {
|
|
984
989
|
this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
|
|
985
990
|
return;
|
|
986
991
|
}
|
|
992
|
+
if (!this.nodeContext) {
|
|
993
|
+
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
this.matterbridgeContext.set('softwareVersion', 1);
|
|
997
|
+
this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
987
998
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
988
999
|
this.commissioningServer = this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
989
1000
|
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
@@ -995,7 +1006,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
995
1006
|
this.log.debug('Starting matter server...');
|
|
996
1007
|
await this.startMatterServer();
|
|
997
1008
|
this.log.info('Matter server started');
|
|
998
|
-
this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, 'Matterbridge');
|
|
1009
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
999
1010
|
}
|
|
1000
1011
|
if (this.bridgeMode === 'childbridge') {
|
|
1001
1012
|
// Plugins are loaded and started by loadPlugin on startup
|
|
@@ -1033,7 +1044,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1033
1044
|
await this.matterServer.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
1034
1045
|
}
|
|
1035
1046
|
if (plugin.type === 'DynamicPlatform') {
|
|
1036
|
-
plugin.storageContext = this.createCommissioningServerContext(plugin.name, 'Matterbridge
|
|
1047
|
+
plugin.storageContext = this.createCommissioningServerContext(plugin.name, 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Dynamic Platform');
|
|
1048
|
+
plugin.storageContext.set('softwareVersion', 1);
|
|
1049
|
+
plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1037
1050
|
plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1038
1051
|
this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
1039
1052
|
plugin.aggregator = this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
@@ -1079,7 +1092,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1079
1092
|
await this.startMatterServer();
|
|
1080
1093
|
this.log.info('Matter server started');
|
|
1081
1094
|
for (const plugin of this.registeredPlugins) {
|
|
1082
|
-
|
|
1095
|
+
if (!plugin.enabled)
|
|
1096
|
+
continue;
|
|
1097
|
+
if (!plugin.commissioningServer) {
|
|
1098
|
+
this.log.error(`Commissioning server not found for plugin ${plg}${plugin.name}${er}`);
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
if (!plugin.storageContext) {
|
|
1102
|
+
this.log.error(`Storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
if (!plugin.nodeContext) {
|
|
1106
|
+
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1083
1110
|
}
|
|
1084
1111
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
1085
1112
|
clearInterval(startMatterInterval);
|
|
@@ -1165,7 +1192,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1165
1192
|
* @param storageContext - The storage context to store the pairing codes.
|
|
1166
1193
|
* @param pluginName - The name of the commissioning server.
|
|
1167
1194
|
*/
|
|
1168
|
-
async showCommissioningQRCode(commissioningServer, storageContext, pluginName) {
|
|
1195
|
+
async showCommissioningQRCode(commissioningServer, storageContext, nodeContext, pluginName) {
|
|
1169
1196
|
if (!commissioningServer || !storageContext || !pluginName)
|
|
1170
1197
|
return;
|
|
1171
1198
|
if (!commissioningServer.isCommissioned()) {
|
|
@@ -1173,41 +1200,27 @@ export class Matterbridge extends EventEmitter {
|
|
|
1173
1200
|
const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
|
|
1174
1201
|
storageContext.set('qrPairingCode', qrPairingCode);
|
|
1175
1202
|
storageContext.set('manualPairingCode', manualPairingCode);
|
|
1203
|
+
await nodeContext.set('qrPairingCode', qrPairingCode);
|
|
1204
|
+
await nodeContext.set('manualPairingCode', manualPairingCode);
|
|
1176
1205
|
const QrCode = new QrCodeSchema();
|
|
1177
|
-
this.log.info(`Pairing code:\n\n${QrCode.encode(qrPairingCode)}\nManual pairing code: ${manualPairingCode}\n`);
|
|
1178
|
-
if (
|
|
1179
|
-
await this.nodeContext?.set('qrPairingCode', qrPairingCode);
|
|
1180
|
-
await this.nodeContext?.set('manualPairingCode', manualPairingCode);
|
|
1181
|
-
}
|
|
1182
|
-
if (this.bridgeMode === 'childbridge') {
|
|
1206
|
+
this.log.info(`Pairing code:\n\n${QrCode.encode(qrPairingCode)}\n${plg}${pluginName}${nf}\n\nqrPairingCode: ${qrPairingCode}\n\nManual pairing code: ${manualPairingCode}\n`);
|
|
1207
|
+
if (pluginName !== 'Matterbridge') {
|
|
1183
1208
|
const plugin = this.findPlugin(pluginName);
|
|
1184
1209
|
if (plugin) {
|
|
1185
|
-
await plugin.nodeContext?.set('qrPairingCode', qrPairingCode);
|
|
1186
|
-
await plugin.nodeContext?.set('manualPairingCode', manualPairingCode);
|
|
1187
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1188
1210
|
plugin.paired = false;
|
|
1189
1211
|
}
|
|
1190
1212
|
}
|
|
1213
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1191
1214
|
}
|
|
1192
1215
|
else {
|
|
1193
1216
|
this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
|
|
1194
|
-
if (
|
|
1195
|
-
const qrPairingCode = storageContext.get('qrPairingCode', '');
|
|
1196
|
-
const manualPairingCode = storageContext.get('manualPairingCode', '');
|
|
1197
|
-
await this.nodeContext?.set('qrPairingCode', qrPairingCode);
|
|
1198
|
-
await this.nodeContext?.set('manualPairingCode', manualPairingCode);
|
|
1199
|
-
}
|
|
1200
|
-
if (this.bridgeMode === 'childbridge') {
|
|
1217
|
+
if (pluginName !== 'Matterbridge') {
|
|
1201
1218
|
const plugin = this.findPlugin(pluginName);
|
|
1202
|
-
if (plugin
|
|
1203
|
-
plugin.qrPairingCode = plugin.storageContext.get('qrPairingCode', '');
|
|
1204
|
-
plugin.manualPairingCode = plugin.storageContext.get('manualPairingCode', '');
|
|
1205
|
-
await plugin.nodeContext.set('qrPairingCode', plugin.qrPairingCode);
|
|
1206
|
-
await plugin.nodeContext.set('manualPairingCode', plugin.manualPairingCode);
|
|
1207
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1219
|
+
if (plugin) {
|
|
1208
1220
|
plugin.paired = true;
|
|
1209
1221
|
}
|
|
1210
1222
|
}
|
|
1223
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1211
1224
|
}
|
|
1212
1225
|
}
|
|
1213
1226
|
/**
|
|
@@ -1228,11 +1241,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1228
1241
|
* Creates a matter commissioning server.
|
|
1229
1242
|
*
|
|
1230
1243
|
* @param {StorageContext} context - The storage context.
|
|
1231
|
-
* @param {string}
|
|
1244
|
+
* @param {string} pluginName - The name of the commissioning server.
|
|
1232
1245
|
* @returns {CommissioningServer} The created commissioning server.
|
|
1233
1246
|
*/
|
|
1234
|
-
createCommisioningServer(context,
|
|
1235
|
-
this.log.debug(`Creating matter commissioning server for plugin ${plg}${
|
|
1247
|
+
createCommisioningServer(context, pluginName) {
|
|
1248
|
+
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
|
|
1236
1249
|
const deviceName = context.get('deviceName');
|
|
1237
1250
|
const deviceType = context.get('deviceType');
|
|
1238
1251
|
const vendorId = context.get('vendorId');
|
|
@@ -1241,9 +1254,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1241
1254
|
const productName = context.get('productName'); // Home app = Model
|
|
1242
1255
|
const serialNumber = context.get('serialNumber');
|
|
1243
1256
|
const uniqueId = context.get('uniqueId');
|
|
1244
|
-
|
|
1245
|
-
//
|
|
1246
|
-
|
|
1257
|
+
const softwareVersion = context.get('softwareVersion', 1);
|
|
1258
|
+
const softwareVersionString = context.get('softwareVersionString', '1.0.0'); // Home app = Firmware Revision
|
|
1259
|
+
const hardwareVersion = context.get('hardwareVersion', 1);
|
|
1260
|
+
const hardwareVersionString = context.get('hardwareVersionString', '1.0.0');
|
|
1261
|
+
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName ${deviceName} deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
|
|
1262
|
+
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with uniqueId ${uniqueId} serialNumber ${serialNumber}`);
|
|
1263
|
+
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
1264
|
+
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
|
|
1247
1265
|
const commissioningServer = new CommissioningServer({
|
|
1248
1266
|
port: undefined,
|
|
1249
1267
|
passcode: undefined,
|
|
@@ -1257,10 +1275,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1257
1275
|
productName,
|
|
1258
1276
|
nodeLabel: productName,
|
|
1259
1277
|
productLabel: productName,
|
|
1260
|
-
softwareVersion
|
|
1261
|
-
softwareVersionString
|
|
1262
|
-
hardwareVersion
|
|
1263
|
-
hardwareVersionString
|
|
1278
|
+
softwareVersion,
|
|
1279
|
+
softwareVersionString, // Home app = Firmware Revision
|
|
1280
|
+
hardwareVersion,
|
|
1281
|
+
hardwareVersionString,
|
|
1264
1282
|
uniqueId,
|
|
1265
1283
|
serialNumber,
|
|
1266
1284
|
reachable: true,
|
|
@@ -1269,15 +1287,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1269
1287
|
const info = commissioningServer.getActiveSessionInformation(fabricIndex);
|
|
1270
1288
|
let connected = false;
|
|
1271
1289
|
info.forEach((session) => {
|
|
1272
|
-
this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${
|
|
1290
|
+
this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
|
|
1273
1291
|
if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
|
|
1274
|
-
this.log.info(`***Controller ${session.fabric?.rootVendorId}/${session.fabric?.label} connected to ${plg}${
|
|
1292
|
+
this.log.info(`***Controller ${session.fabric?.rootVendorId}/${session.fabric?.label} connected to ${plg}${pluginName}${nf}`);
|
|
1275
1293
|
connected = true;
|
|
1276
1294
|
}
|
|
1277
1295
|
});
|
|
1278
1296
|
if (connected) {
|
|
1279
1297
|
if (this.bridgeMode === 'childbridge') {
|
|
1280
|
-
const plugin = this.findPlugin(
|
|
1298
|
+
const plugin = this.findPlugin(pluginName);
|
|
1281
1299
|
if (plugin) {
|
|
1282
1300
|
plugin.paired = true;
|
|
1283
1301
|
plugin.connected = true;
|
|
@@ -1290,16 +1308,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1290
1308
|
if (!plugin.enabled)
|
|
1291
1309
|
continue;
|
|
1292
1310
|
this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously with also configurePlugin
|
|
1293
|
-
//this.configurePlugin(plugin); // No await do it asyncronously
|
|
1294
1311
|
}
|
|
1295
1312
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
1296
1313
|
}
|
|
1297
1314
|
if (this.bridgeMode === 'childbridge') {
|
|
1298
1315
|
//Logger.defaultLogLevel = Level.INFO;
|
|
1299
|
-
const plugin = this.findPlugin(
|
|
1316
|
+
const plugin = this.findPlugin(pluginName);
|
|
1300
1317
|
if (plugin && plugin.type === 'DynamicPlatform' && plugin.configured !== true) {
|
|
1301
1318
|
for (const registeredDevice of this.registeredDevices) {
|
|
1302
|
-
if (registeredDevice.plugin ===
|
|
1319
|
+
if (registeredDevice.plugin === pluginName) {
|
|
1303
1320
|
this.log.info(`Adding bridged device ${dev}${registeredDevice.device.name}-${registeredDevice.device.deviceName}${nf} to aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
1304
1321
|
if (!plugin.aggregator) {
|
|
1305
1322
|
this.log.error(`****Aggregator not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -1316,7 +1333,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1316
1333
|
}
|
|
1317
1334
|
}
|
|
1318
1335
|
for (const plugin of this.registeredPlugins) {
|
|
1319
|
-
if (plugin.name ===
|
|
1336
|
+
if (plugin.name === pluginName && plugin.platform && plugin.configured !== true) {
|
|
1320
1337
|
this.configurePlugin(plugin); // No await do it asyncronously
|
|
1321
1338
|
}
|
|
1322
1339
|
}
|
|
@@ -1326,12 +1343,29 @@ export class Matterbridge extends EventEmitter {
|
|
|
1326
1343
|
}, 2000);
|
|
1327
1344
|
}
|
|
1328
1345
|
},
|
|
1329
|
-
commissioningChangedCallback: (fabricIndex) => {
|
|
1346
|
+
commissioningChangedCallback: async (fabricIndex) => {
|
|
1330
1347
|
const info = commissioningServer.getCommissionedFabricInformation(fabricIndex);
|
|
1331
|
-
this.log.debug(`***Commissioning changed on fabric ${fabricIndex} for ${plg}${
|
|
1348
|
+
this.log.debug(`***Commissioning changed on fabric ${fabricIndex} for ${plg}${pluginName}${nf}`, debugStringify(info));
|
|
1332
1349
|
if (info.length === 0) {
|
|
1333
|
-
this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${
|
|
1334
|
-
commissioningServer.factoryReset();
|
|
1350
|
+
this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
|
|
1351
|
+
await commissioningServer.factoryReset();
|
|
1352
|
+
if (pluginName === 'Matterbridge') {
|
|
1353
|
+
this.matterbridgeContext?.delete(`${pluginName}.EndpointStructure`);
|
|
1354
|
+
this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
|
|
1355
|
+
this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
|
|
1356
|
+
}
|
|
1357
|
+
else {
|
|
1358
|
+
for (const plugin of this.registeredPlugins) {
|
|
1359
|
+
if (plugin.name === pluginName) {
|
|
1360
|
+
await plugin.platform?.onShutdown('Commissioning removed by the controller');
|
|
1361
|
+
plugin.paired = false;
|
|
1362
|
+
plugin.connected = false;
|
|
1363
|
+
plugin.storageContext?.delete(`${pluginName}.EndpointStructure`);
|
|
1364
|
+
plugin.storageContext?.delete(`${pluginName}.EventHandler`);
|
|
1365
|
+
plugin.storageContext?.delete(`${pluginName}.SessionManager`);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1335
1369
|
}
|
|
1336
1370
|
},
|
|
1337
1371
|
});
|