matterbridge 1.6.6-dev.9 → 1.6.7-dev.1
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 +37 -12
- package/README-DEV.md +3 -3
- package/README.md +8 -2
- package/dist/matterbridge.js +12 -5
- package/dist/matterbridgeBehaviors.js +9 -0
- package/dist/matterbridgeDevice.js +55 -16
- package/dist/matterbridgeDeviceTypes.js +0 -1
- package/dist/matterbridgeEdge.js +65 -10
- package/dist/matterbridgeEndpoint.js +54 -79
- package/dist/matterbridgePlatform.js +25 -9
- package/dist/matterbridgeWebsocket.js +5 -0
- package/dist/pluginManager.js +8 -2
- package/dist/utils/colorUtils.js +32 -88
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.565ff6ba.js → main.4dd7e165.js} +9 -9
- package/frontend/build/static/js/main.4dd7e165.js.map +1 -0
- package/npm-shrinkwrap.json +176 -79
- package/package.json +5 -6
- package/frontend/build/static/js/main.565ff6ba.js.map +0 -1
- /package/frontend/build/static/js/{main.565ff6ba.js.LICENSE.txt → main.4dd7e165.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,37 +4,62 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
If you like this project and find it useful, please consider giving it a star on GitHub at https://github.com/Luligu/matterbridge and sponsoring it.
|
|
6
6
|
|
|
7
|
-
### Home Assistant
|
|
7
|
+
### Home Assistant
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
If you want to run Matterbridge in Home Assistant please use the official add-on https://github.com/Luligu/matterbridge-home-assistant-addon that also has Ingress and side panel.
|
|
9
|
+
If you want to run Matterbridge in Home Assistant please use the official add-on https://github.com/Luligu/matterbridge-home-assistant-addon that also has Ingress and side panel.
|
|
10
|
+
It is also available the official Matterbridge Home Assistant plugin https://github.com/Luligu/matterbridge-hass.
|
|
12
11
|
|
|
13
12
|
### Discord
|
|
14
13
|
|
|
15
14
|
Tamer (https://github.com/tammeryousef1006) has created the Matterbridge Discord group: https://discord.gg/QX58CDe6hd.
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
### Breaking Changes
|
|
17
|
+
|
|
18
|
+
In this release some device types and the OnOff, LevelControl and ColorControl have been updated to be fully compliant with Matter 1.3 specifications.
|
|
19
|
+
It is possible that some controllers see them as new devices or need time to read the new clusters. It can be useful after the upgrade to power off the controller, wait a few minutes and power it on again.
|
|
20
|
+
|
|
21
|
+
## [1.6.7-dev.1] - 2024-12-??
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- [readme]: Update README to clarify Node.js installation instructions and emphasize LTS version.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- [Docker]: Add matterbridge-hass to Dockerfile for latest and main builds.
|
|
30
|
+
- [package]: Update dependencies.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- [Device]: Fix addChildDeviceType methods to include debug parameter in MatterbridgeDevice instantiation.
|
|
35
|
+
|
|
36
|
+
## [1.6.6] - 2024-12-12
|
|
18
37
|
|
|
19
38
|
### Added
|
|
20
39
|
|
|
21
|
-
- [frontend]:
|
|
40
|
+
- [frontend]: Added the possibility to install a specific version or the dev of any plugin (i.e. you can install matterbridge-hass@dev or matterbridge-hass@0.0.3).
|
|
22
41
|
It is also possible to use the install plugin to install a specific version of matterbridge (i.e. you can install matterbridge@dev or matterbridge@1.6.5)
|
|
23
|
-
- [frontend]:
|
|
24
|
-
- [frontend]:
|
|
25
|
-
- [frontend]:
|
|
26
|
-
- [deviceTypes]:
|
|
27
|
-
- [docker]:
|
|
42
|
+
- [frontend]: Added the possibility to set the matter discriminator for commissioning (you can always override passing **-discriminator [DISCRIMINATOR]** on the command line).
|
|
43
|
+
- [frontend]: Added the possibility to set the matter passcode for commissioning (you can always override passing **-passcode [PASSCODE]** on the command line).
|
|
44
|
+
- [frontend]: Added the possibility to set the matter port for commissioning (you can always override passing **-port [PORT]** on the command line).
|
|
45
|
+
- [deviceTypes]: Added the device type airConditioner (not supported by the Apple Home).
|
|
46
|
+
- [docker]: Added matterbridge-hass to docker dev.
|
|
28
47
|
- [platform]: Added validateDeviceWhiteBlackList and validateEntityBlackList to be used consistently by all plugins.
|
|
48
|
+
- [/api/devices]: Added productUrl and configUrl.
|
|
29
49
|
|
|
30
50
|
### Changed
|
|
31
51
|
|
|
32
52
|
- [package]: Update matter.js to 0.11.9-alpha.0-20241206-22f23333.
|
|
33
53
|
- [package]: Update matter.js to 0.11.9-alpha.0-20241207-b604cfa44
|
|
54
|
+
- [package]: Update matter.js to 0.11.9-alpha.0-20241209-06a8040e1
|
|
55
|
+
- [package]: Update matter.js to 0.11.9
|
|
34
56
|
- [plugin]: Removed check on package types since we are moving to production plugins.
|
|
35
57
|
- [package]: Set required node version to 18, 20 and 22.
|
|
36
58
|
- [package]: Update dependencies.
|
|
37
|
-
- [
|
|
59
|
+
- [onOff]: Set default to OnOff.Feature.Lighting.
|
|
60
|
+
- [levelControl]: Set default to LevelControl.Feature.Lighting.
|
|
61
|
+
- [colorControl]: Set default cluster helpers to have ColorTemperature.
|
|
62
|
+
- [lightSensor]: Refactor lightSensor removing Group optional cluster server.
|
|
38
63
|
- [jest]: Update Jest tests.
|
|
39
64
|
|
|
40
65
|
### Fixed
|
package/README-DEV.md
CHANGED
|
@@ -71,9 +71,9 @@ I added some error messages when a plugin has wrong imports or configurations an
|
|
|
71
71
|
|
|
72
72
|
I'm working with matter.js team to define the strategy for the migration of Matterbridge to the new API.
|
|
73
73
|
|
|
74
|
-
- First phase:
|
|
75
|
-
- Second phase: create MatterbridgeEndpoint and MatterbridgeBehaviors classes: completed
|
|
76
|
-
- Third phase: modifiy all plugins to support both normal and edge mode of Matterbridge: completed
|
|
74
|
+
- First phase: create MatterbridgeEdge class: completed 95%
|
|
75
|
+
- Second phase: create MatterbridgeEndpoint and MatterbridgeBehaviors classes: completed 95%
|
|
76
|
+
- Third phase: modifiy all plugins to support both normal and edge mode of Matterbridge: completed 90%
|
|
77
77
|
- Fourth phase: remove all old api code from Matterbridge and all plugins...
|
|
78
78
|
|
|
79
79
|
## How to create your plugin
|
package/README.md
CHANGED
|
@@ -44,7 +44,13 @@ A special thank to Apollon77 for his incredible work.
|
|
|
44
44
|
|
|
45
45
|
## Prerequisites
|
|
46
46
|
|
|
47
|
-
To run Matterbridge, you need either a [Node.js](https://nodejs.org/en
|
|
47
|
+
To run Matterbridge, you need either a [Node.js](https://nodejs.org/en) environment or [Docker](https://docs.docker.com/get-started/get-docker/) installed on your system.
|
|
48
|
+
|
|
49
|
+
If you don't have Node.js already install, please use this method to install it on a debian device: https://github.com/nodesource/distributions.
|
|
50
|
+
The supported versions of node are 18, 20 and 22. Please install node 22 LTS.
|
|
51
|
+
Nvm is not a good choice and should not be used for production.
|
|
52
|
+
|
|
53
|
+
If you don't have Docker already install, please use this method to install it on a debian device: https://docs.docker.com/desktop/setup/install/linux/debian/.
|
|
48
54
|
|
|
49
55
|
## Installation
|
|
50
56
|
|
|
@@ -372,7 +378,7 @@ Then, from the dots menu in the frontend, download the `matterbridge.log` and `m
|
|
|
372
378
|
|
|
373
379
|
# Known general issues
|
|
374
380
|
|
|
375
|
-
## Session XYZ does not exist
|
|
381
|
+
## Session XYZ does not exist or Cannot find a session for ID XYZ
|
|
376
382
|
|
|
377
383
|
This message may appear after Matterbridge restarts, indicating that the controller is still using a session from the previous connection that has since been closed.
|
|
378
384
|
After some time, the controller will reconnect.
|
package/dist/matterbridge.js
CHANGED
|
@@ -60,6 +60,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
60
60
|
bridgeMode: '',
|
|
61
61
|
restartMode: '',
|
|
62
62
|
edge: hasParameter('edge'),
|
|
63
|
+
readOnly: hasParameter('readonly'),
|
|
63
64
|
profile: getParameter('profile'),
|
|
64
65
|
loggerLevel: "info",
|
|
65
66
|
fileLogger: false,
|
|
@@ -201,8 +202,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
201
202
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
202
203
|
}
|
|
203
204
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
204
|
-
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode'));
|
|
205
|
-
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator'));
|
|
205
|
+
this.passcode = this.passcode ?? getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode'));
|
|
206
|
+
this.discriminator = this.discriminator ?? getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator'));
|
|
207
|
+
this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
206
208
|
if (hasParameter('logger')) {
|
|
207
209
|
const level = getParameter('logger');
|
|
208
210
|
if (level === 'debug') {
|
|
@@ -2335,7 +2337,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2335
2337
|
});
|
|
2336
2338
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
2337
2339
|
this.log.debug('The frontend sent /api/devices');
|
|
2338
|
-
const
|
|
2340
|
+
const devices = [];
|
|
2339
2341
|
this.devices.forEach(async (device) => {
|
|
2340
2342
|
const pluginName = device.plugin ?? 'Unknown';
|
|
2341
2343
|
if (this.edge)
|
|
@@ -2346,21 +2348,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
2346
2348
|
let serial = device.getClusterServer(BasicInformationCluster)?.attributes.serialNumber?.getLocal();
|
|
2347
2349
|
if (!serial)
|
|
2348
2350
|
serial = device.getClusterServer(BridgedDeviceBasicInformationCluster)?.attributes.serialNumber?.getLocal() ?? 'Unknown';
|
|
2351
|
+
let productUrl = device.getClusterServer(BasicInformationCluster)?.attributes.productUrl?.getLocal();
|
|
2352
|
+
if (!productUrl)
|
|
2353
|
+
productUrl = device.getClusterServer(BridgedDeviceBasicInformationCluster)?.attributes.productUrl?.getLocal() ?? 'Unknown';
|
|
2349
2354
|
let uniqueId = device.getClusterServer(BasicInformationCluster)?.attributes.uniqueId?.getLocal();
|
|
2350
2355
|
if (!uniqueId)
|
|
2351
2356
|
uniqueId = device.getClusterServer(BridgedDeviceBasicInformationCluster)?.attributes.uniqueId?.getLocal() ?? 'Unknown';
|
|
2352
2357
|
const cluster = this.getClusterTextFromDevice(device);
|
|
2353
|
-
|
|
2358
|
+
devices.push({
|
|
2354
2359
|
pluginName,
|
|
2355
2360
|
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
2356
2361
|
endpoint: device.number,
|
|
2357
2362
|
name,
|
|
2358
2363
|
serial,
|
|
2364
|
+
productUrl,
|
|
2365
|
+
configUrl: device.configUrl,
|
|
2359
2366
|
uniqueId,
|
|
2360
2367
|
cluster: cluster,
|
|
2361
2368
|
});
|
|
2362
2369
|
});
|
|
2363
|
-
res.json(
|
|
2370
|
+
res.json(devices);
|
|
2364
2371
|
});
|
|
2365
2372
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
2366
2373
|
const selectedPluginName = req.params.selectedPluginName;
|
|
@@ -23,6 +23,10 @@ export class MatterbridgeBehaviorDevice {
|
|
|
23
23
|
this.log.info(`Identifying device for ${identifyTime} seconds`);
|
|
24
24
|
this.commandHandler.executeHandler('identify', { request: { identifyTime }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
|
|
25
25
|
}
|
|
26
|
+
triggerEffect({ effectIdentifier, effectVariant }) {
|
|
27
|
+
this.log.info(`Triggering effect ${effectIdentifier} variant ${effectVariant}`);
|
|
28
|
+
this.commandHandler.executeHandler('triggerEffect', { request: { effectIdentifier, effectVariant }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
|
|
29
|
+
}
|
|
26
30
|
on() {
|
|
27
31
|
this.log.info(`Switching device on (endpoint ${this.endpointId}.${this.endpointNumber})`);
|
|
28
32
|
this.commandHandler.executeHandler('on', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
|
|
@@ -125,6 +129,11 @@ export class MatterbridgeIdentifyServer extends IdentifyServer {
|
|
|
125
129
|
device.identify({ identifyTime });
|
|
126
130
|
super.identify({ identifyTime });
|
|
127
131
|
}
|
|
132
|
+
triggerEffect({ effectIdentifier, effectVariant }) {
|
|
133
|
+
const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand;
|
|
134
|
+
device.triggerEffect({ effectIdentifier, effectVariant });
|
|
135
|
+
super.triggerEffect({ effectIdentifier, effectVariant });
|
|
136
|
+
}
|
|
128
137
|
}
|
|
129
138
|
export class MatterbridgeOnOffServer extends OnOffServer {
|
|
130
139
|
async on() {
|
|
@@ -12,6 +12,7 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
12
12
|
static logLevel = "info";
|
|
13
13
|
log;
|
|
14
14
|
plugin = undefined;
|
|
15
|
+
configUrl = undefined;
|
|
15
16
|
serialNumber = undefined;
|
|
16
17
|
deviceName = undefined;
|
|
17
18
|
uniqueId = undefined;
|
|
@@ -80,7 +81,7 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
80
81
|
this.log.debug(`addChildDeviceType: ${CYAN}${endpointName}${db}`);
|
|
81
82
|
let child = this.getChildEndpoints().find((endpoint) => endpoint.uniqueStorageKey === endpointName);
|
|
82
83
|
if (!child) {
|
|
83
|
-
child = new MatterbridgeDevice(deviceTypes, { uniqueStorageKey: endpointName });
|
|
84
|
+
child = new MatterbridgeDevice(deviceTypes, { uniqueStorageKey: endpointName }, debug);
|
|
84
85
|
if ('tagList' in options) {
|
|
85
86
|
for (const tag of options.tagList) {
|
|
86
87
|
this.log.debug(`- with tagList: mfgCode ${CYAN}${tag.mfgCode}${db} namespaceId ${CYAN}${tag.namespaceId}${db} tag ${CYAN}${tag.tag}${db} label ${CYAN}${tag.label}${db}`);
|
|
@@ -104,7 +105,7 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
104
105
|
this.log.debug(`addChildDeviceTypeWithClusterServer: ${CYAN}${endpointName}${db}`);
|
|
105
106
|
let child = this.getChildEndpoints().find((endpoint) => endpoint.uniqueStorageKey === endpointName);
|
|
106
107
|
if (!child) {
|
|
107
|
-
child = new MatterbridgeDevice(deviceTypes, { uniqueStorageKey: endpointName });
|
|
108
|
+
child = new MatterbridgeDevice(deviceTypes, { uniqueStorageKey: endpointName }, debug);
|
|
108
109
|
if ('tagList' in options) {
|
|
109
110
|
for (const tag of options.tagList) {
|
|
110
111
|
this.log.debug(`- with tagList: mfgCode ${CYAN}${tag.mfgCode}${db} namespaceId ${CYAN}${tag.namespaceId}${db} tag ${CYAN}${tag.tag}${db} label ${CYAN}${tag.label}${db}`);
|
|
@@ -458,6 +459,7 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
458
459
|
vendorName: vendorName.slice(0, 32),
|
|
459
460
|
productId: productId,
|
|
460
461
|
productName: productName.slice(0, 32),
|
|
462
|
+
productUrl: 'https://www.npmjs.com/package/matterbridge',
|
|
461
463
|
productLabel: deviceName.slice(0, 64),
|
|
462
464
|
nodeLabel: deviceName.slice(0, 32),
|
|
463
465
|
serialNumber: serialNumber.slice(0, 32),
|
|
@@ -502,6 +504,7 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
502
504
|
vendorId: vendorId !== undefined ? VendorId(vendorId) : undefined,
|
|
503
505
|
vendorName: vendorName.slice(0, 32),
|
|
504
506
|
productName: productName.slice(0, 32),
|
|
507
|
+
productUrl: 'https://www.npmjs.com/package/matterbridge',
|
|
505
508
|
productLabel: deviceName.slice(0, 64),
|
|
506
509
|
nodeLabel: deviceName.slice(0, 32),
|
|
507
510
|
serialNumber: serialNumber.slice(0, 32),
|
|
@@ -644,12 +647,14 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
644
647
|
createDefaultOnOffClusterServer(onOff = false, globalSceneControl = false, onTime = 0, offWaitTime = 0, startUpOnOff = null) {
|
|
645
648
|
this.addClusterServer(this.getDefaultOnOffClusterServer(onOff, globalSceneControl, onTime, offWaitTime, startUpOnOff));
|
|
646
649
|
}
|
|
647
|
-
getDefaultLevelControlClusterServer(currentLevel = 254, minLevel =
|
|
648
|
-
return ClusterServer(LevelControlCluster.with(LevelControl.Feature.OnOff), {
|
|
650
|
+
getDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 1, maxLevel = 254, onLevel = null, startUpCurrentLevel = null) {
|
|
651
|
+
return ClusterServer(LevelControlCluster.with(LevelControl.Feature.OnOff, LevelControl.Feature.Lighting), {
|
|
649
652
|
currentLevel,
|
|
650
653
|
minLevel,
|
|
651
654
|
maxLevel,
|
|
652
655
|
onLevel,
|
|
656
|
+
remainingTime: 0,
|
|
657
|
+
startUpCurrentLevel,
|
|
653
658
|
options: {
|
|
654
659
|
executeIfOff: false,
|
|
655
660
|
coupleColorTempToLevel: false,
|
|
@@ -683,8 +688,8 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
683
688
|
},
|
|
684
689
|
});
|
|
685
690
|
}
|
|
686
|
-
createDefaultLevelControlClusterServer(currentLevel = 254, minLevel =
|
|
687
|
-
this.addClusterServer(this.getDefaultLevelControlClusterServer(currentLevel, minLevel, maxLevel, onLevel));
|
|
691
|
+
createDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 1, maxLevel = 254, onLevel = null, startUpCurrentLevel = null) {
|
|
692
|
+
this.addClusterServer(this.getDefaultLevelControlClusterServer(currentLevel, minLevel, maxLevel, onLevel, startUpCurrentLevel));
|
|
688
693
|
}
|
|
689
694
|
getDefaultColorControlClusterServer(currentX = 0, currentY = 0, currentHue = 0, currentSaturation = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
690
695
|
return ClusterServer(ColorControlCluster.with(ColorControl.Feature.Xy, ColorControl.Feature.HueSaturation, ColorControl.Feature.ColorTemperature), {
|
|
@@ -703,6 +708,7 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
703
708
|
colorTempPhysicalMinMireds,
|
|
704
709
|
colorTempPhysicalMaxMireds,
|
|
705
710
|
coupleColorTempToLevelMinMireds: colorTempPhysicalMinMireds,
|
|
711
|
+
remainingTime: 0,
|
|
706
712
|
startUpColorTemperatureMireds: null,
|
|
707
713
|
}, {
|
|
708
714
|
moveToColor: async (data) => {
|
|
@@ -757,17 +763,23 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
757
763
|
createDefaultColorControlClusterServer(currentX = 0, currentY = 0, currentHue = 0, currentSaturation = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
758
764
|
this.addClusterServer(this.getDefaultColorControlClusterServer(currentX, currentY, currentHue, currentSaturation, colorTemperatureMireds, colorTempPhysicalMinMireds, colorTempPhysicalMaxMireds));
|
|
759
765
|
}
|
|
760
|
-
getXyColorControlClusterServer(currentX = 0, currentY = 0) {
|
|
761
|
-
return ClusterServer(ColorControlCluster.with(ColorControl.Feature.Xy), {
|
|
766
|
+
getXyColorControlClusterServer(currentX = 0, currentY = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
767
|
+
return ClusterServer(ColorControlCluster.with(ColorControl.Feature.Xy, ColorControl.Feature.ColorTemperature), {
|
|
762
768
|
colorMode: ColorControl.ColorMode.CurrentXAndCurrentY,
|
|
763
769
|
enhancedColorMode: ColorControl.EnhancedColorMode.CurrentXAndCurrentY,
|
|
764
|
-
colorCapabilities: { xy: true, hueSaturation: false, colorLoop: false, enhancedHue: false, colorTemperature:
|
|
770
|
+
colorCapabilities: { xy: true, hueSaturation: false, colorLoop: false, enhancedHue: false, colorTemperature: true },
|
|
765
771
|
options: {
|
|
766
772
|
executeIfOff: false,
|
|
767
773
|
},
|
|
768
774
|
numberOfPrimaries: null,
|
|
769
775
|
currentX,
|
|
770
776
|
currentY,
|
|
777
|
+
colorTemperatureMireds,
|
|
778
|
+
colorTempPhysicalMinMireds,
|
|
779
|
+
colorTempPhysicalMaxMireds,
|
|
780
|
+
coupleColorTempToLevelMinMireds: colorTempPhysicalMinMireds,
|
|
781
|
+
startUpColorTemperatureMireds: null,
|
|
782
|
+
remainingTime: 0,
|
|
771
783
|
}, {
|
|
772
784
|
moveToColor: async (data) => {
|
|
773
785
|
this.log.debug('Matter command: moveToColor request:', data.request, 'attributes.currentX:', data.attributes.currentX.getLocal(), 'attributes.currentY:', data.attributes.currentY.getLocal());
|
|
@@ -782,22 +794,38 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
782
794
|
stopMoveStep: async () => {
|
|
783
795
|
this.log.error('Matter command: stopMoveStep not implemented');
|
|
784
796
|
},
|
|
797
|
+
moveToColorTemperature: async ({ request, attributes, endpoint }) => {
|
|
798
|
+
this.log.debug('Matter command: moveToColorTemperature request:', request, 'attributes.colorTemperatureMireds:', attributes.colorTemperatureMireds.getLocal());
|
|
799
|
+
this.commandHandler.executeHandler('moveToColorTemperature', { request, attributes, endpoint });
|
|
800
|
+
},
|
|
801
|
+
moveColorTemperature: async () => {
|
|
802
|
+
this.log.error('Matter command: moveColorTemperature not implemented');
|
|
803
|
+
},
|
|
804
|
+
stepColorTemperature: async () => {
|
|
805
|
+
this.log.error('Matter command: stepColorTemperature not implemented');
|
|
806
|
+
},
|
|
785
807
|
}, {});
|
|
786
808
|
}
|
|
787
|
-
createXyColorControlClusterServer(currentX = 0, currentY = 0) {
|
|
788
|
-
this.addClusterServer(this.getXyColorControlClusterServer(currentX, currentY));
|
|
809
|
+
createXyColorControlClusterServer(currentX = 0, currentY = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
810
|
+
this.addClusterServer(this.getXyColorControlClusterServer(currentX, currentY, colorTemperatureMireds, colorTempPhysicalMinMireds, colorTempPhysicalMaxMireds));
|
|
789
811
|
}
|
|
790
|
-
getHsColorControlClusterServer(currentHue = 0, currentSaturation = 0) {
|
|
791
|
-
return ClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation), {
|
|
812
|
+
getHsColorControlClusterServer(currentHue = 0, currentSaturation = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
813
|
+
return ClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation, ColorControl.Feature.ColorTemperature), {
|
|
792
814
|
colorMode: ColorControl.ColorMode.CurrentHueAndCurrentSaturation,
|
|
793
815
|
enhancedColorMode: ColorControl.EnhancedColorMode.CurrentHueAndCurrentSaturation,
|
|
794
|
-
colorCapabilities: { xy: false, hueSaturation: true, colorLoop: false, enhancedHue: false, colorTemperature:
|
|
816
|
+
colorCapabilities: { xy: false, hueSaturation: true, colorLoop: false, enhancedHue: false, colorTemperature: true },
|
|
795
817
|
options: {
|
|
796
818
|
executeIfOff: false,
|
|
797
819
|
},
|
|
798
820
|
numberOfPrimaries: null,
|
|
799
821
|
currentHue,
|
|
800
822
|
currentSaturation,
|
|
823
|
+
colorTemperatureMireds,
|
|
824
|
+
colorTempPhysicalMinMireds,
|
|
825
|
+
colorTempPhysicalMaxMireds,
|
|
826
|
+
coupleColorTempToLevelMinMireds: colorTempPhysicalMinMireds,
|
|
827
|
+
startUpColorTemperatureMireds: null,
|
|
828
|
+
remainingTime: 0,
|
|
801
829
|
}, {
|
|
802
830
|
moveToHue: async ({ request, attributes, endpoint }) => {
|
|
803
831
|
this.log.debug('Matter command: moveToHue request:', request, 'attributes.currentHue:', attributes.currentHue.getLocal());
|
|
@@ -826,10 +854,20 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
826
854
|
stopMoveStep: async () => {
|
|
827
855
|
this.log.error('Matter command: stopMoveStep not implemented');
|
|
828
856
|
},
|
|
857
|
+
moveToColorTemperature: async ({ request, attributes, endpoint }) => {
|
|
858
|
+
this.log.debug('Matter command: moveToColorTemperature request:', request, 'attributes.colorTemperatureMireds:', attributes.colorTemperatureMireds.getLocal());
|
|
859
|
+
this.commandHandler.executeHandler('moveToColorTemperature', { request, attributes, endpoint });
|
|
860
|
+
},
|
|
861
|
+
moveColorTemperature: async () => {
|
|
862
|
+
this.log.error('Matter command: moveColorTemperature not implemented');
|
|
863
|
+
},
|
|
864
|
+
stepColorTemperature: async () => {
|
|
865
|
+
this.log.error('Matter command: stepColorTemperature not implemented');
|
|
866
|
+
},
|
|
829
867
|
}, {});
|
|
830
868
|
}
|
|
831
|
-
createHsColorControlClusterServer(currentHue = 0, currentSaturation = 0) {
|
|
832
|
-
this.addClusterServer(this.getHsColorControlClusterServer(currentHue, currentSaturation));
|
|
869
|
+
createHsColorControlClusterServer(currentHue = 0, currentSaturation = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
870
|
+
this.addClusterServer(this.getHsColorControlClusterServer(currentHue, currentSaturation, colorTemperatureMireds, colorTempPhysicalMinMireds, colorTempPhysicalMaxMireds));
|
|
833
871
|
}
|
|
834
872
|
getCtColorControlClusterServer(colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
835
873
|
return ClusterServer(ColorControlCluster.with(ColorControl.Feature.ColorTemperature), {
|
|
@@ -844,6 +882,7 @@ export class MatterbridgeDevice extends extendPublicHandlerMethods(Device) {
|
|
|
844
882
|
colorTempPhysicalMinMireds,
|
|
845
883
|
colorTempPhysicalMaxMireds,
|
|
846
884
|
coupleColorTempToLevelMinMireds: colorTempPhysicalMinMireds,
|
|
885
|
+
remainingTime: 0,
|
|
847
886
|
startUpColorTemperatureMireds: null,
|
|
848
887
|
}, {
|
|
849
888
|
stopMoveStep: async () => {
|
|
@@ -116,7 +116,6 @@ export const lightSensor = DeviceTypeDefinition({
|
|
|
116
116
|
deviceClass: DeviceClasses.Simple,
|
|
117
117
|
revision: 3,
|
|
118
118
|
requiredServerClusters: [Identify.Cluster.id, IlluminanceMeasurement.Cluster.id],
|
|
119
|
-
optionalClientClusters: [Groups.Cluster.id],
|
|
120
119
|
});
|
|
121
120
|
export const occupancySensor = DeviceTypeDefinition({
|
|
122
121
|
name: 'MA-occupancysensor',
|
package/dist/matterbridgeEdge.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
|
-
import { rs, GREEN, debugStringify, er, zb, nf } from 'node-ansi-logger';
|
|
4
|
+
import { rs, GREEN, debugStringify, er, zb, nf, db } from 'node-ansi-logger';
|
|
5
5
|
import { Matterbridge } from './matterbridge.js';
|
|
6
6
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
7
7
|
import { dev, plg } from './matterbridgeTypes.js';
|
|
@@ -9,7 +9,7 @@ import { copyDirectory, getParameter, hasParameter } from './utils/utils.js';
|
|
|
9
9
|
import { DeviceTypeId, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, EndpointServer } from '@matter/main';
|
|
10
10
|
import { ServerNode, Endpoint as EndpointNode, Environment, StorageService } from '@matter/main';
|
|
11
11
|
import { BasicInformationCluster } from '@matter/main/clusters';
|
|
12
|
-
import { FabricAction, MdnsService } from '@matter/main/protocol';
|
|
12
|
+
import { FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
13
13
|
import { GenericSwitchDevice } from '@matter/main/devices';
|
|
14
14
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
15
15
|
import { BridgedDeviceBasicInformationServer, SwitchServer } from '@matter/main/behaviors';
|
|
@@ -45,6 +45,11 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
45
45
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
46
46
|
this.environment.vars.set('runtime.signals', false);
|
|
47
47
|
this.environment.vars.set('runtime.exitcode', false);
|
|
48
|
+
this.port = 5540;
|
|
49
|
+
this.passcode = PaseClient.generateRandomPasscode();
|
|
50
|
+
this.discriminator = PaseClient.generateRandomDiscriminator();
|
|
51
|
+
if (hasParameter('debug'))
|
|
52
|
+
console.log(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
48
53
|
await super.initialize();
|
|
49
54
|
if (this.mdnsInterface)
|
|
50
55
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
@@ -74,7 +79,7 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
74
79
|
}
|
|
75
80
|
createMatterServer(storageManager) {
|
|
76
81
|
if (hasParameter('debug'))
|
|
77
|
-
this.log.warn('createMatterServer() => mock
|
|
82
|
+
this.log.warn('createMatterServer() => mock MatterServer.addCommissioningServer()');
|
|
78
83
|
const matterServer = {
|
|
79
84
|
addCommissioningServer: (commissioningServer, nodeOptions) => {
|
|
80
85
|
if (hasParameter('debug'))
|
|
@@ -85,9 +90,11 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
85
90
|
}
|
|
86
91
|
async startMatterServer() {
|
|
87
92
|
if (hasParameter('debug'))
|
|
88
|
-
this.log.warn('
|
|
93
|
+
this.log.warn('startMatterServer() => do nothing');
|
|
89
94
|
}
|
|
90
95
|
async stopMatterServer() {
|
|
96
|
+
if (hasParameter('debug'))
|
|
97
|
+
this.log.warn('stopMatterServer() => ...');
|
|
91
98
|
this.log.info(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
92
99
|
if (this.bridgeMode === 'bridge') {
|
|
93
100
|
const serverNode = this.csToServerNode.get('Matterbridge')?.serverNode;
|
|
@@ -143,7 +150,7 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
143
150
|
}
|
|
144
151
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
145
152
|
const storeId = await storageContext.get('storeId');
|
|
146
|
-
this.log.info(`Creating server node for ${storeId}...`);
|
|
153
|
+
this.log.info(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
147
154
|
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')}`);
|
|
148
155
|
this.log.debug(`- deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
149
156
|
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
|
|
@@ -284,7 +291,7 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
284
291
|
async addBridgedEndpoint(pluginName, device) {
|
|
285
292
|
const plugin = this.plugins.get(pluginName);
|
|
286
293
|
if (!plugin) {
|
|
287
|
-
this.log.error(`Error adding bridged
|
|
294
|
+
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
288
295
|
return;
|
|
289
296
|
}
|
|
290
297
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -304,11 +311,59 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
304
311
|
if (plugin.addedDevices !== undefined)
|
|
305
312
|
plugin.addedDevices++;
|
|
306
313
|
this.devices.set(device);
|
|
307
|
-
this.log.info(`Added and registered bridged
|
|
314
|
+
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
308
315
|
}
|
|
309
316
|
async removeBridgedEndpoint(pluginName, device) {
|
|
317
|
+
this.log.debug(`Removing bridged endpoint ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
318
|
+
const plugin = this.plugins.get(pluginName);
|
|
319
|
+
if (!plugin) {
|
|
320
|
+
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (this.bridgeMode === 'bridge') {
|
|
324
|
+
const aggregatoreNode = this.agToAggregatorEndpoint.get('Matterbridge')?.aggregatorNode;
|
|
325
|
+
if (!aggregatoreNode) {
|
|
326
|
+
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator node not found`);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
await device.delete();
|
|
330
|
+
this.log.info(`Removed bridged endpoint(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
331
|
+
if (plugin.registeredDevices !== undefined)
|
|
332
|
+
plugin.registeredDevices--;
|
|
333
|
+
if (plugin.addedDevices !== undefined)
|
|
334
|
+
plugin.addedDevices--;
|
|
335
|
+
}
|
|
336
|
+
else if (this.bridgeMode === 'childbridge') {
|
|
337
|
+
if (plugin.type === 'AccessoryPlatform') {
|
|
338
|
+
}
|
|
339
|
+
else if (plugin.type === 'DynamicPlatform') {
|
|
340
|
+
const aggregatoreNode = this.agToAggregatorEndpoint.get(pluginName)?.aggregatorNode;
|
|
341
|
+
if (!aggregatoreNode) {
|
|
342
|
+
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator not found`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
await device.delete();
|
|
346
|
+
}
|
|
347
|
+
this.log.info(`Removed bridged endpoint(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
348
|
+
if (plugin.registeredDevices !== undefined)
|
|
349
|
+
plugin.registeredDevices--;
|
|
350
|
+
if (plugin.addedDevices !== undefined)
|
|
351
|
+
plugin.addedDevices--;
|
|
352
|
+
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
353
|
+
const serverNode = this.csToServerNode.get(pluginName)?.serverNode;
|
|
354
|
+
if (serverNode)
|
|
355
|
+
await this.stopServerNode(serverNode);
|
|
356
|
+
this.csToServerNode.delete(pluginName);
|
|
357
|
+
this.log.info(`Removed server node for plugin ${plg}${pluginName}${nf}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
this.devices.remove(device);
|
|
310
361
|
}
|
|
311
362
|
async removeAllBridgedEndpoints(pluginName) {
|
|
363
|
+
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
364
|
+
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
365
|
+
await this.removeBridgedEndpoint(pluginName, device);
|
|
366
|
+
}
|
|
312
367
|
}
|
|
313
368
|
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
|
|
314
369
|
if (hasParameter('debug'))
|
|
@@ -330,7 +385,7 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
330
385
|
if (hasParameter('debug'))
|
|
331
386
|
this.log.warn(`createCommisioningServer() for ${pluginName} => createServerNode()`);
|
|
332
387
|
const port = this.port;
|
|
333
|
-
const serverNode = await this.createServerNode(context, this.port++, this.passcode ? this.passcode++ :
|
|
388
|
+
const serverNode = await this.createServerNode(context, this.port++, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
|
|
334
389
|
const commissioningServer = {
|
|
335
390
|
getPort: () => port,
|
|
336
391
|
addDevice: async (device) => {
|
|
@@ -363,11 +418,11 @@ export class MatterbridgeEdge extends Matterbridge {
|
|
|
363
418
|
name: 'MA-aggregator',
|
|
364
419
|
addBridgedDevice: (device) => {
|
|
365
420
|
if (hasParameter('debug'))
|
|
366
|
-
this.log.
|
|
421
|
+
this.log.error('****Aggregator.addBridgedDevice() => not inplemented');
|
|
367
422
|
},
|
|
368
423
|
removeBridgedDevice: (device) => {
|
|
369
424
|
if (hasParameter('debug'))
|
|
370
|
-
this.log.
|
|
425
|
+
this.log.error('****Aggregator.removeBridgedDevice() => not inplemented');
|
|
371
426
|
},
|
|
372
427
|
};
|
|
373
428
|
this.agToAggregatorEndpoint.set(pluginName, { aggregator, aggregatorNode });
|