matterbridge-webhooks 1.1.2 → 1.1.3
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 +53 -3
- package/README.md +20 -8
- package/dist/fetch.js +2 -2
- package/dist/module.js +51 -42
- package/matterbridge-webhooks.schema.json +15 -4
- package/npm-shrinkwrap.json +29 -28
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
2
|
+
|
|
1
3
|
# <img src="https://matterbridge.io/assets/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge webhooks plugin changelog
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/matterbridge-webhooks)
|
|
@@ -7,10 +9,10 @@
|
|
|
7
9
|

|
|
8
10
|

|
|
9
11
|
[](https://codecov.io/gh/Luligu/mmatterbridge-webhooks)
|
|
10
|
-
[](https://
|
|
11
|
-
[](https://
|
|
12
|
+
[](https://prettier.io/)
|
|
13
|
+
[](https://eslint.org/)
|
|
12
14
|
[](https://www.typescriptlang.org/)
|
|
13
|
-
[](https://nodejs.org/
|
|
15
|
+
[](https://nodejs.org/)
|
|
14
16
|
[](https://matterbridge.io)
|
|
15
17
|
|
|
16
18
|
[](https://www.npmjs.com/package/matterbridge)
|
|
@@ -26,6 +28,54 @@ If you like this project and find it useful, please consider giving it a star on
|
|
|
26
28
|
|
|
27
29
|
<a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
|
|
28
30
|
|
|
31
|
+
## [1.1.3] - 2026-05-11
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- [fetch]: Add `PUT` HTTP method.
|
|
36
|
+
- [docs]: Update README with `PUT` usage and `Basic Auth` URL notes.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- [package]: Preliminary compatibility update to `matterbridge 3.8.0`, matter 1.5.1 and matter.js 0.17.0.
|
|
41
|
+
- [package]: Update dependencies.
|
|
42
|
+
- [package]: Bump package to `automator` v.3.1.8.
|
|
43
|
+
- [package]: Bump `node-ansi-logger` to v.3.2.1.
|
|
44
|
+
- [package]: Bump `node-persist-manager` to v.2.0.2.
|
|
45
|
+
- [package]: Bump `jest` to v.30.4.2.
|
|
46
|
+
- [package]: Bump `prettier` to v.3.8.3.
|
|
47
|
+
- [package]: Bump `typescript` to v.6.0.3.
|
|
48
|
+
- [package]: Bump `eslint` to v.10.3.0.
|
|
49
|
+
- [package]: Bump `typescript-eslint` to v.8.59.2.
|
|
50
|
+
- [package]: Add Node.js 26 to package `engines` field.
|
|
51
|
+
- [package]: Add `.vscode\tasks.json`.
|
|
52
|
+
- [package]: Add `.vscode\settings.json`.
|
|
53
|
+
- [package]: Add package script `typecheck`.
|
|
54
|
+
- [eslint]: Remove `eslint-plugin-promise` (not actively maintained) and add optional @typescript-eslint promise rules.
|
|
55
|
+
- [workflows]: Add Node.js 26 to `build.yml` Node matrix and remove Node.js 20.
|
|
56
|
+
- [devcontainer]: Add `Claude Code for VS Code extension` to Dev Container.
|
|
57
|
+
- [jest]: Add `jest` v.2.0.1 config.
|
|
58
|
+
- [eslint]: Add `eslint` v.2.0.2 config.
|
|
59
|
+
- [prettier]: Add `prettier` v.2.0.0 config.
|
|
60
|
+
- [agent]: Add `.github\copilot-instructions.md` for copilot.
|
|
61
|
+
- [agent]: Add `.claude\CLAUDE.md` for claude.
|
|
62
|
+
- [agent]: Add agent custom instructions (`testing`) for copilot and claude.
|
|
63
|
+
- [agent]: Add agent custom instructions (`matterbridge`) for copilot and claude.
|
|
64
|
+
- [devcontainer]: Fix pull of new image.
|
|
65
|
+
- [devcontainer]: Update VS Code settings.
|
|
66
|
+
- [devcontainer]: Leave matterbridge scripts in the cloned repo.
|
|
67
|
+
- [scripts]: Update mb-run script.
|
|
68
|
+
- [scripts]: Update package watch script.
|
|
69
|
+
- [scripts]: Add prune-releases script.
|
|
70
|
+
- [devcontainer]: Update `Dev Container` configuration.
|
|
71
|
+
- [devcontainer]: Add `postStartCommand` to the Dev Container configuration.
|
|
72
|
+
- [package]: Refactor `build.yml` to use matterbridge dev branch for push and main for pull requests.
|
|
73
|
+
- [package]: Add `type checking` script for Jest tests.
|
|
74
|
+
- [package]: Update actions versions in workflows.
|
|
75
|
+
- [package]: Add `CODE_OF_CONDUCT.md`.
|
|
76
|
+
|
|
77
|
+
<a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
|
|
78
|
+
|
|
29
79
|
## [1.1.2] - 2026-02-07
|
|
30
80
|
|
|
31
81
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<!-- eslint-disable markdown/no-multiple-h1 -->
|
|
2
|
+
|
|
1
3
|
# <img src="https://matterbridge.io/assets/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge webhooks plugin
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/matterbridge-webhooks)
|
|
@@ -7,10 +9,10 @@
|
|
|
7
9
|

|
|
8
10
|

|
|
9
11
|
[](https://codecov.io/gh/Luligu/mmatterbridge-webhooks)
|
|
10
|
-
[](https://
|
|
11
|
-
[](https://
|
|
12
|
+
[](https://prettier.io/)
|
|
13
|
+
[](https://eslint.org/)
|
|
12
14
|
[](https://www.typescriptlang.org/)
|
|
13
|
-
[](https://nodejs.org/
|
|
15
|
+
[](https://nodejs.org/)
|
|
14
16
|
[](https://matterbridge.io)
|
|
15
17
|
|
|
16
18
|
[](https://www.npmjs.com/package/matterbridge)
|
|
@@ -28,7 +30,7 @@ Features:
|
|
|
28
30
|
|
|
29
31
|
- The webhooks parameters can easily be entered in the frontend.
|
|
30
32
|
- It is possible to choose how to expose the webhooks: Switch, Outlet or Light.
|
|
31
|
-
- It is possible to choose the method: GET or
|
|
33
|
+
- It is possible to choose the method: GET, POST or PUT.
|
|
32
34
|
- The webhook can be tested in the frontend.
|
|
33
35
|
|
|
34
36
|
# Webhook devices
|
|
@@ -36,7 +38,7 @@ Features:
|
|
|
36
38
|
Features:
|
|
37
39
|
|
|
38
40
|
- It is possible to choose the device type from the config.
|
|
39
|
-
- It is possible to set the method with a prefix 'GET#' or '
|
|
41
|
+
- It is possible to set the method with a prefix 'GET#', 'POST#' or 'PUT#' in the urls. Default if omitted is GET.
|
|
40
42
|
- It is possible to use converters and attributes in the url.
|
|
41
43
|
|
|
42
44
|
## Supported device types:
|
|
@@ -92,10 +94,14 @@ See the complete guidelines on [Matterbridge](https://matterbridge.io/README.htm
|
|
|
92
94
|
|
|
93
95
|
## How to add a simple webhook
|
|
94
96
|
|
|
95
|
-
In the frontend open the plugin config: add a new webhook, enter the webhook name in the first field (replace newKey with the name you want to give to the webhook), select GET or
|
|
97
|
+
In the frontend open the plugin config: add a new webhook, enter the webhook name in the first field (replace newKey with the name you want to give to the webhook), select GET, POST or PUT and enter the webhook url. The webhook name will be the device name on the controller. The webhook will be exposed like a switch, like an outlet or like a light. When you turn it on, the webhook is called and in a few seconds the switch or the outlet or the light will revert to off.
|
|
96
98
|
|
|
97
99
|
It is possible to test directly the webhook from the config editor.
|
|
98
100
|
|
|
101
|
+
## How to use HTTP Basic Authentication
|
|
102
|
+
|
|
103
|
+
If your target device is protected with HTTP Basic Authentication, you can pass credentials directly in the URL (e.g. `http://username:password@192.168.1.50/path`). If the password contains special characters (like `@` or `:`), URL-encode it. Note that the plugin may log the full URL during debug, so credentials embedded in URLs can end up in your logs; if that is a concern, don't share the logs and add a password to the frontend.
|
|
104
|
+
|
|
99
105
|
## Examples
|
|
100
106
|
|
|
101
107
|
## Shelly webhooks examples
|
|
@@ -104,9 +110,15 @@ Change 192.168.1.XXX with your device IP address.
|
|
|
104
110
|
|
|
105
111
|
### Shelly 1 Gen 1
|
|
106
112
|
|
|
107
|
-
To turn on a shelly gen 1 device with ip 192.168.1.155 the url is http://192.168.1.
|
|
113
|
+
To turn on a shelly gen 1 device with ip 192.168.1.155 the url is http://192.168.1.155/light/0?turn=on.
|
|
114
|
+
|
|
115
|
+
To turn off a shelly gen 1 device with ip 192.168.1.155 the url is http://192.168.1.155/light/0?turn=off.
|
|
116
|
+
|
|
117
|
+
### Shelly 1 Gen 2+
|
|
118
|
+
|
|
119
|
+
To turn on a shelly gen 2+ device with ip 192.168.68.75 the url is http://192.168.68.75/rpc/Switch.Set?id=0&on=true.
|
|
108
120
|
|
|
109
|
-
To turn off a shelly gen
|
|
121
|
+
To turn off a shelly gen 2+ device with ip 192.168.68.75 the url is http://192.168.68.75/rpc/Switch.Set?id=0&on=false.
|
|
110
122
|
|
|
111
123
|
### Shelly Trv Gen 1
|
|
112
124
|
|
package/dist/fetch.js
CHANGED
|
@@ -32,7 +32,7 @@ export async function fetch(url, method = 'GET', data = {}, timeout = 5000) {
|
|
|
32
32
|
headers: {
|
|
33
33
|
'Content-Type': 'application/json',
|
|
34
34
|
'Accept': 'application/json',
|
|
35
|
-
...(method === 'POST' && { 'Content-Length': Buffer.byteLength(jsonData) }),
|
|
35
|
+
...((method === 'POST' || method === 'PUT') && { 'Content-Length': Buffer.byteLength(jsonData) }),
|
|
36
36
|
},
|
|
37
37
|
signal: controller.signal,
|
|
38
38
|
};
|
|
@@ -64,7 +64,7 @@ export async function fetch(url, method = 'GET', data = {}, timeout = 5000) {
|
|
|
64
64
|
clearTimeout(timeoutId);
|
|
65
65
|
reject(new Error(`Request failed: ${error instanceof Error ? error.message : error}`));
|
|
66
66
|
});
|
|
67
|
-
if (method === 'POST') {
|
|
67
|
+
if (method === 'POST' || method === 'PUT') {
|
|
68
68
|
req.write(jsonData);
|
|
69
69
|
}
|
|
70
70
|
req.end();
|
package/dist/module.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { bridgedNode, colorTemperatureLight, dimmableLight, extendedColorLight, MatterbridgeColorControlServer, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, MatterbridgeLevelControlServer, onOffLight, onOffOutlet, onOffSwitch, } from 'matterbridge';
|
|
2
|
-
import { hslColorToRgbColor, isValidNumber, isValidObject, isValidString, miredToKelvin, wait } from 'matterbridge/utils';
|
|
3
2
|
import { rs } from 'matterbridge/logger';
|
|
3
|
+
import { hslColorToRgbColor, isValidNumber, isValidObject, isValidString, miredToKelvin, wait } from 'matterbridge/utils';
|
|
4
4
|
import { fetch } from './fetch.js';
|
|
5
5
|
export default function initializePlugin(matterbridge, log, config) {
|
|
6
6
|
return new WebhooksPlatform(matterbridge, log, config);
|
|
@@ -10,8 +10,8 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
10
10
|
constructor(matterbridge, log, config) {
|
|
11
11
|
super(matterbridge, log, config);
|
|
12
12
|
this.config = config;
|
|
13
|
-
if (
|
|
14
|
-
throw new Error(`This plugin requires Matterbridge version >= "3.
|
|
13
|
+
if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.7.0')) {
|
|
14
|
+
throw new Error(`This plugin requires Matterbridge version >= "3.7.0". Please update Matterbridge to the latest version in the frontend.`);
|
|
15
15
|
}
|
|
16
16
|
this.log.info('Initializing platform:', this.config.name);
|
|
17
17
|
this.log.info('Finished initializing platform:', this.config.name);
|
|
@@ -57,10 +57,10 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
57
57
|
.createOnOffClusterServer(false)
|
|
58
58
|
.addRequiredClusterServers()
|
|
59
59
|
.addCommandHandler('on', async (data) => {
|
|
60
|
-
this.parseUrl('outlet', outletName, 'on', webhook.onUrl, data);
|
|
60
|
+
await this.parseUrl('outlet', outletName, 'on', webhook.onUrl, data);
|
|
61
61
|
})
|
|
62
62
|
.addCommandHandler('off', async (data) => {
|
|
63
|
-
this.parseUrl('outlet', outletName, 'off', webhook.offUrl, data);
|
|
63
|
+
await this.parseUrl('outlet', outletName, 'off', webhook.offUrl, data);
|
|
64
64
|
});
|
|
65
65
|
await this.registerDevice(device);
|
|
66
66
|
}
|
|
@@ -86,31 +86,31 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
86
86
|
.createDefaultLevelControlClusterServer()
|
|
87
87
|
.addRequiredClusterServers()
|
|
88
88
|
.addCommandHandler('on', async (data) => {
|
|
89
|
-
this.parseUrl('light', lightName, 'on', webhook.onUrl, data);
|
|
89
|
+
await this.parseUrl('light', lightName, 'on', webhook.onUrl, data);
|
|
90
90
|
})
|
|
91
91
|
.addCommandHandler('off', async (data) => {
|
|
92
|
-
this.parseUrl('light', lightName, 'off', webhook.offUrl, data);
|
|
92
|
+
await this.parseUrl('light', lightName, 'off', webhook.offUrl, data);
|
|
93
93
|
})
|
|
94
94
|
.addCommandHandler('moveToLevel', async (data) => {
|
|
95
|
-
this.parseUrl('light', lightName, 'moveToLevel', webhook.brightnessUrl, data);
|
|
95
|
+
await this.parseUrl('light', lightName, 'moveToLevel', webhook.brightnessUrl, data);
|
|
96
96
|
})
|
|
97
97
|
.addCommandHandler('moveToLevelWithOnOff', async (data) => {
|
|
98
|
-
this.parseUrl('light', lightName, 'moveToLevelWithOnOff', webhook.brightnessUrl, data);
|
|
98
|
+
await this.parseUrl('light', lightName, 'moveToLevelWithOnOff', webhook.brightnessUrl, data);
|
|
99
99
|
})
|
|
100
100
|
.addCommandHandler('moveToColorTemperature', async (data) => {
|
|
101
|
-
this.parseUrl('light', lightName, 'moveToColorTemperature', webhook.colorTempUrl, data);
|
|
101
|
+
await this.parseUrl('light', lightName, 'moveToColorTemperature', webhook.colorTempUrl, data);
|
|
102
102
|
})
|
|
103
103
|
.addCommandHandler('moveToHueAndSaturation', async (data) => {
|
|
104
|
-
this.parseUrl('light', lightName, 'moveToHueAndSaturation', webhook.rgbUrl, data);
|
|
104
|
+
await this.parseUrl('light', lightName, 'moveToHueAndSaturation', webhook.rgbUrl, data);
|
|
105
105
|
})
|
|
106
106
|
.addCommandHandler('moveToHue', async (data) => {
|
|
107
|
-
this.parseUrl('light', lightName, 'moveToHue', webhook.rgbUrl, data);
|
|
107
|
+
await this.parseUrl('light', lightName, 'moveToHue', webhook.rgbUrl, data);
|
|
108
108
|
})
|
|
109
109
|
.addCommandHandler('moveToSaturation', async (data) => {
|
|
110
|
-
this.parseUrl('light', lightName, 'moveToSaturation', webhook.rgbUrl, data);
|
|
110
|
+
await this.parseUrl('light', lightName, 'moveToSaturation', webhook.rgbUrl, data);
|
|
111
111
|
})
|
|
112
112
|
.addCommandHandler('moveToColor', async (data) => {
|
|
113
|
-
this.parseUrl('light', lightName, 'moveToColor', webhook.rgbUrl, data);
|
|
113
|
+
await this.parseUrl('light', lightName, 'moveToColor', webhook.rgbUrl, data);
|
|
114
114
|
});
|
|
115
115
|
await this.registerDevice(device);
|
|
116
116
|
}
|
|
@@ -191,65 +191,73 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
191
191
|
method = 'POST';
|
|
192
192
|
parsedUrl = url.replace('POST#', '');
|
|
193
193
|
}
|
|
194
|
-
if (
|
|
194
|
+
else if (url.startsWith('PUT#')) {
|
|
195
|
+
method = 'PUT';
|
|
196
|
+
parsedUrl = url.replace('PUT#', '');
|
|
197
|
+
}
|
|
198
|
+
if (parsedUrl.includes('${LEVEL}') && data.cluster === 'levelControl' && isValidNumber(data.request.level)) {
|
|
195
199
|
parsedUrl = parsedUrl.replace('${LEVEL}', data.request.level.toString());
|
|
196
200
|
}
|
|
197
|
-
if (url.includes('${LEVEL100}') && isValidNumber(data.request.level)) {
|
|
201
|
+
if (url.includes('${LEVEL100}') && data.cluster === 'levelControl' && isValidNumber(data.request.level)) {
|
|
198
202
|
parsedUrl = parsedUrl.replace('${LEVEL100}', Math.round((data.request.level / 254) * 100).toString());
|
|
199
203
|
}
|
|
200
|
-
if (parsedUrl.includes('${KELVIN}') && isValidNumber(data.request.colorTemperatureMireds)) {
|
|
204
|
+
if (parsedUrl.includes('${KELVIN}') && data.cluster === 'colorControl' && data.command === 'moveToColorTemperature' && isValidNumber(data.request.colorTemperatureMireds)) {
|
|
201
205
|
parsedUrl = parsedUrl.replace('${KELVIN}', Math.round(miredToKelvin(data.request.colorTemperatureMireds)).toString());
|
|
202
206
|
}
|
|
203
|
-
if (parsedUrl.includes('${MIRED}') && isValidNumber(data.request.colorTemperatureMireds)) {
|
|
207
|
+
if (parsedUrl.includes('${MIRED}') && data.cluster === 'colorControl' && data.command === 'moveToColorTemperature' && isValidNumber(data.request.colorTemperatureMireds)) {
|
|
204
208
|
parsedUrl = parsedUrl.replace('${MIRED}', Math.round(data.request.colorTemperatureMireds).toString());
|
|
205
209
|
}
|
|
206
|
-
if (parsedUrl.includes('${COLORX}') && isValidNumber(data.request.colorX, 0, 65279)) {
|
|
210
|
+
if (parsedUrl.includes('${COLORX}') && data.cluster === 'colorControl' && data.command === 'moveToColor' && isValidNumber(data.request.colorX, 0, 65279)) {
|
|
207
211
|
parsedUrl = parsedUrl.replace('${COLORX}', this.roundTo(data.request.colorX / 65536, 4).toString());
|
|
208
212
|
}
|
|
209
|
-
if (parsedUrl.includes('${COLORY}') && isValidNumber(data.request.colorY, 0, 65279)) {
|
|
213
|
+
if (parsedUrl.includes('${COLORY}') && data.cluster === 'colorControl' && data.command === 'moveToColor' && isValidNumber(data.request.colorY, 0, 65279)) {
|
|
210
214
|
parsedUrl = parsedUrl.replace('${COLORY}', this.roundTo(data.request.colorY / 65536, 4).toString());
|
|
211
215
|
}
|
|
212
|
-
if (parsedUrl.includes('${HUE}') && isValidNumber(data.request.hue, 0, 254)) {
|
|
216
|
+
if (parsedUrl.includes('${HUE}') && data.cluster === 'colorControl' && data.command === 'moveToHueAndSaturation' && isValidNumber(data.request.hue, 0, 254)) {
|
|
213
217
|
parsedUrl = parsedUrl.replace('${HUE}', Math.round((data.request.hue * 360) / 254).toString());
|
|
214
218
|
}
|
|
215
|
-
if (parsedUrl.includes('${SATURATION}') && isValidNumber(data.request.saturation, 0, 254)) {
|
|
219
|
+
if (parsedUrl.includes('${SATURATION}') && data.cluster === 'colorControl' && data.command === 'moveToHueAndSaturation' && isValidNumber(data.request.saturation, 0, 254)) {
|
|
216
220
|
parsedUrl = parsedUrl.replace('${SATURATION}', Math.round((data.request.saturation * 100) / 254).toString());
|
|
217
221
|
}
|
|
218
|
-
if ((parsedUrl.includes('${level}') || parsedUrl.includes('${level100}')) && isValidNumber(data.attributes.currentLevel, 1, 254)) {
|
|
222
|
+
if ((parsedUrl.includes('${level}') || parsedUrl.includes('${level100}')) && data.cluster === 'levelControl' && isValidNumber(data.attributes.currentLevel, 1, 254)) {
|
|
219
223
|
await wait(100);
|
|
220
|
-
|
|
221
|
-
if (isValidNumber(
|
|
224
|
+
const attributes = endpoint.getCluster(MatterbridgeLevelControlServer);
|
|
225
|
+
if (isValidNumber(attributes?.currentLevel, 1, 254)) {
|
|
222
226
|
if (url.includes('${level}'))
|
|
223
|
-
parsedUrl = parsedUrl.replace('${level}',
|
|
227
|
+
parsedUrl = parsedUrl.replace('${level}', attributes.currentLevel.toString());
|
|
224
228
|
if (url.includes('${level100}'))
|
|
225
|
-
parsedUrl = parsedUrl.replace('${level100}', Math.round((
|
|
229
|
+
parsedUrl = parsedUrl.replace('${level100}', Math.round((attributes.currentLevel / 254) * 100).toString());
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
232
|
if ((parsedUrl.includes('${mired}') || parsedUrl.includes('${kelvin}')) &&
|
|
233
|
+
data.cluster === 'colorControl' &&
|
|
234
|
+
isValidNumber(data.attributes.colorTempPhysicalMinMireds) &&
|
|
235
|
+
isValidNumber(data.attributes.colorTempPhysicalMaxMireds) &&
|
|
229
236
|
isValidNumber(data.attributes.colorTemperatureMireds, data.attributes.colorTempPhysicalMinMireds, data.attributes.colorTempPhysicalMaxMireds)) {
|
|
230
237
|
await wait(100);
|
|
231
|
-
|
|
232
|
-
if (isValidNumber(
|
|
233
|
-
const kelvin = miredToKelvin(
|
|
234
|
-
this.log.debug(`Attribute colorTemperatureMireds is ${
|
|
238
|
+
const attributes = endpoint.getCluster(MatterbridgeColorControlServer);
|
|
239
|
+
if (isValidNumber(attributes?.colorTemperatureMireds)) {
|
|
240
|
+
const kelvin = miredToKelvin(attributes.colorTemperatureMireds);
|
|
241
|
+
this.log.debug(`Attribute colorTemperatureMireds is ${attributes.colorTemperatureMireds}, which is ${kelvin}K`);
|
|
235
242
|
if (url.includes('${mired}'))
|
|
236
|
-
parsedUrl = parsedUrl.replace('${mired}',
|
|
243
|
+
parsedUrl = parsedUrl.replace('${mired}', attributes.colorTemperatureMireds.toString());
|
|
237
244
|
if (url.includes('${kelvin}'))
|
|
238
245
|
parsedUrl = parsedUrl.replace('${kelvin}', kelvin.toString());
|
|
239
246
|
}
|
|
240
247
|
}
|
|
241
248
|
if ((parsedUrl.includes('${hue}') || parsedUrl.includes('${saturation}') || parsedUrl.includes('${red}') || parsedUrl.includes('${green}') || parsedUrl.includes('${blue}')) &&
|
|
249
|
+
data.cluster === 'colorControl' &&
|
|
242
250
|
isValidNumber(data.attributes.currentHue, 0, 254) &&
|
|
243
251
|
isValidNumber(data.attributes.currentSaturation, 0, 254)) {
|
|
244
252
|
await wait(100);
|
|
245
|
-
|
|
246
|
-
if (isValidNumber(
|
|
247
|
-
const rgb = hslColorToRgbColor((
|
|
248
|
-
this.log.debug(`Converted hue ${
|
|
253
|
+
const attributes = endpoint.getCluster(MatterbridgeColorControlServer);
|
|
254
|
+
if (isValidNumber(attributes?.currentHue, 0, 254) && isValidNumber(attributes?.currentSaturation, 0, 254)) {
|
|
255
|
+
const rgb = hslColorToRgbColor((attributes.currentHue * 360) / 254, (attributes.currentSaturation * 100) / 254, 50);
|
|
256
|
+
this.log.debug(`Converted hue ${attributes.currentHue} and saturation ${attributes.currentSaturation} to RGB r: ${rgb.r} g: ${rgb.g} b: ${rgb.b}`);
|
|
249
257
|
if (url.includes('${hue}'))
|
|
250
|
-
parsedUrl = parsedUrl.replace('${hue}', Math.round((
|
|
258
|
+
parsedUrl = parsedUrl.replace('${hue}', Math.round((attributes.currentHue * 360) / 254).toString());
|
|
251
259
|
if (url.includes('${saturation}'))
|
|
252
|
-
parsedUrl = parsedUrl.replace('${saturation}', Math.round((
|
|
260
|
+
parsedUrl = parsedUrl.replace('${saturation}', Math.round((attributes.currentSaturation * 100) / 254).toString());
|
|
253
261
|
if (url.includes('${red}') && rgb)
|
|
254
262
|
parsedUrl = parsedUrl.replace('${red}', rgb.r.toString());
|
|
255
263
|
if (url.includes('${green}') && rgb)
|
|
@@ -259,15 +267,16 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
259
267
|
}
|
|
260
268
|
}
|
|
261
269
|
if ((parsedUrl.includes('${colorX}') || parsedUrl.includes('${colorY}')) &&
|
|
270
|
+
data.cluster === 'colorControl' &&
|
|
262
271
|
isValidNumber(data.attributes.currentX, 0, 65279) &&
|
|
263
272
|
isValidNumber(data.attributes.currentY, 0, 65279)) {
|
|
264
273
|
await wait(100);
|
|
265
|
-
|
|
266
|
-
if (isValidNumber(
|
|
274
|
+
const attributes = endpoint.getCluster(MatterbridgeColorControlServer);
|
|
275
|
+
if (isValidNumber(attributes?.currentX, 0, 65279) && isValidNumber(attributes?.currentY, 0, 65279)) {
|
|
267
276
|
if (url.includes('${colorX}'))
|
|
268
|
-
parsedUrl = parsedUrl.replace('${colorX}', this.roundTo(
|
|
277
|
+
parsedUrl = parsedUrl.replace('${colorX}', this.roundTo(attributes.currentX / 65536, 4).toString());
|
|
269
278
|
if (url.includes('${colorY}'))
|
|
270
|
-
parsedUrl = parsedUrl.replace('${colorY}', this.roundTo(
|
|
279
|
+
parsedUrl = parsedUrl.replace('${colorY}', this.roundTo(attributes.currentY / 65536, 4).toString());
|
|
271
280
|
}
|
|
272
281
|
}
|
|
273
282
|
this.log.debug(`Fetching ${parsedUrl} with ${method}...`);
|
|
@@ -4,18 +4,21 @@
|
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
6
6
|
"name": {
|
|
7
|
+
"title": "Plugin Name",
|
|
7
8
|
"description": "Plugin name",
|
|
8
9
|
"type": "string",
|
|
9
10
|
"readOnly": true,
|
|
10
11
|
"ui:widget": "hidden"
|
|
11
12
|
},
|
|
12
13
|
"type": {
|
|
14
|
+
"title": "Plugin Type",
|
|
13
15
|
"description": "Plugin type",
|
|
14
16
|
"type": "string",
|
|
15
17
|
"readOnly": true,
|
|
16
18
|
"ui:widget": "hidden"
|
|
17
19
|
},
|
|
18
20
|
"version": {
|
|
21
|
+
"title": "Plugin Version",
|
|
19
22
|
"description": "Plugin version",
|
|
20
23
|
"type": "string",
|
|
21
24
|
"readOnly": true,
|
|
@@ -23,6 +26,7 @@
|
|
|
23
26
|
"ui:widget": "hidden"
|
|
24
27
|
},
|
|
25
28
|
"whiteList": {
|
|
29
|
+
"title": "White List",
|
|
26
30
|
"description": "Only the webhooks in the list will be exposed. If the list is empty, all the webhooks will be exposed.",
|
|
27
31
|
"type": "array",
|
|
28
32
|
"items": {
|
|
@@ -32,6 +36,7 @@
|
|
|
32
36
|
"selectFrom": "name"
|
|
33
37
|
},
|
|
34
38
|
"blackList": {
|
|
39
|
+
"title": "Black List",
|
|
35
40
|
"description": "The webhooks in the list will not be exposed. If the list is empty, no webhooks will be excluded.",
|
|
36
41
|
"type": "array",
|
|
37
42
|
"items": {
|
|
@@ -41,12 +46,14 @@
|
|
|
41
46
|
"selectFrom": "name"
|
|
42
47
|
},
|
|
43
48
|
"deviceType": {
|
|
49
|
+
"title": "Device Type",
|
|
44
50
|
"type": "string",
|
|
45
51
|
"default": "Outlet",
|
|
46
52
|
"enum": ["Outlet", "Switch", "Light"],
|
|
47
|
-
"description": "Select how to
|
|
53
|
+
"description": "Select how to represent the webhooks on the controller. Don't use Switch for Alexa."
|
|
48
54
|
},
|
|
49
55
|
"webhooks": {
|
|
56
|
+
"title": "Webhooks",
|
|
50
57
|
"description": "Define each webhooks. Enter in the first field the name of the webhook (replace newKey with the name of the webhook) and in the right panel the parameters.",
|
|
51
58
|
"type": "object",
|
|
52
59
|
"uniqueItems": true,
|
|
@@ -58,7 +65,7 @@
|
|
|
58
65
|
"method": {
|
|
59
66
|
"type": "string",
|
|
60
67
|
"default": "GET",
|
|
61
|
-
"enum": ["GET", "POST"],
|
|
68
|
+
"enum": ["GET", "POST", "PUT"],
|
|
62
69
|
"description": "HTTP method to use for the webhook."
|
|
63
70
|
},
|
|
64
71
|
"httpUrl": {
|
|
@@ -76,7 +83,8 @@
|
|
|
76
83
|
}
|
|
77
84
|
},
|
|
78
85
|
"outlets": {
|
|
79
|
-
"
|
|
86
|
+
"title": "Outlets",
|
|
87
|
+
"description": "Define each outlet. Enter in the first field the name of the outlet (replace newKey with the name of the outlet) and in the right panel the urls (i.e. \"[GET|POST|PUT]#http://mydomain.com/api/device/on\").",
|
|
80
88
|
"type": "object",
|
|
81
89
|
"uniqueItems": true,
|
|
82
90
|
"additionalProperties": {
|
|
@@ -98,7 +106,8 @@
|
|
|
98
106
|
}
|
|
99
107
|
},
|
|
100
108
|
"lights": {
|
|
101
|
-
"
|
|
109
|
+
"title": "Lights",
|
|
110
|
+
"description": "Define each light. Enter in the first field the name of the light (replace newKey with the name of the light) and in the right panel the urls (i.e. [GET|POST|PUT]#http://mydomain.com/api/device/on).",
|
|
102
111
|
"type": "object",
|
|
103
112
|
"uniqueItems": true,
|
|
104
113
|
"additionalProperties": {
|
|
@@ -147,11 +156,13 @@
|
|
|
147
156
|
}
|
|
148
157
|
},
|
|
149
158
|
"debug": {
|
|
159
|
+
"title": "Enable Debug",
|
|
150
160
|
"description": "Enable the debug for the plugin.",
|
|
151
161
|
"type": "boolean",
|
|
152
162
|
"default": false
|
|
153
163
|
},
|
|
154
164
|
"unregisterOnShutdown": {
|
|
165
|
+
"title": "Unregister On Shutdown",
|
|
155
166
|
"description": "Unregister all devices on shutdown. This will remove all devices from the controller when the plugin is stopped.",
|
|
156
167
|
"type": "boolean",
|
|
157
168
|
"default": false
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge-webhooks",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge-webhooks",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.3",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"node-ansi-logger": "3.2.
|
|
13
|
-
"node-persist-manager": "2.0.
|
|
12
|
+
"node-ansi-logger": "3.2.1",
|
|
13
|
+
"node-persist-manager": "2.0.2"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=20.
|
|
16
|
+
"node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0 || >=26.0.0 <27.0.0"
|
|
17
17
|
},
|
|
18
18
|
"funding": {
|
|
19
19
|
"type": "buymeacoffee",
|
|
@@ -21,48 +21,49 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"node_modules/node-ansi-logger": {
|
|
24
|
-
"version": "3.2.
|
|
25
|
-
"resolved": "https://registry.npmjs.org/node-ansi-logger/-/node-ansi-logger-3.2.
|
|
26
|
-
"integrity": "sha512-
|
|
24
|
+
"version": "3.2.1",
|
|
25
|
+
"resolved": "https://registry.npmjs.org/node-ansi-logger/-/node-ansi-logger-3.2.1.tgz",
|
|
26
|
+
"integrity": "sha512-R5YptDlGVfVqACSiOtfmAwV+N/ytj8crwXStrs9d9kdAXYT4xF1J1H5qfhGbXsot06OHSha76Y2Fq66sIzMq2A==",
|
|
27
27
|
"hasShrinkwrap": true,
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=20.
|
|
30
|
+
"node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0"
|
|
31
31
|
},
|
|
32
32
|
"funding": {
|
|
33
33
|
"type": "buymeacoffee",
|
|
34
34
|
"url": "https://www.buymeacoffee.com/luligugithub"
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
"node_modules/node-persist": {
|
|
38
|
-
"version": "4.0.4",
|
|
39
|
-
"resolved": "https://registry.npmjs.org/node-persist/-/node-persist-4.0.4.tgz",
|
|
40
|
-
"integrity": "sha512-8sPAz/7tw1mCCc8xBG4f0wi+flHkSSgQeX998iQ75Pu27evA6UUWCjSE7xnrYTg2q33oU5leJ061EKPDv6BocQ==",
|
|
41
|
-
"license": "MIT",
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"p-limit": "^3.1.0"
|
|
44
|
-
},
|
|
45
|
-
"engines": {
|
|
46
|
-
"node": ">=10.12.0"
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
37
|
"node_modules/node-persist-manager": {
|
|
50
|
-
"version": "2.0.
|
|
51
|
-
"resolved": "https://registry.npmjs.org/node-persist-manager/-/node-persist-manager-2.0.
|
|
52
|
-
"integrity": "sha512-
|
|
53
|
-
"
|
|
38
|
+
"version": "2.0.2",
|
|
39
|
+
"resolved": "https://registry.npmjs.org/node-persist-manager/-/node-persist-manager-2.0.2.tgz",
|
|
40
|
+
"integrity": "sha512-FGDdNOH4c6Xqb2LwTZ55GT0UaoqaOiWJ+hWt+O7w1+9QIUQh2NsaXPtGaTiibvCi6cX823JTFQPJK2NRseLTvw==",
|
|
41
|
+
"hasShrinkwrap": true,
|
|
42
|
+
"license": "Apache-2.0",
|
|
54
43
|
"dependencies": {
|
|
55
44
|
"node-persist": "4.0.4"
|
|
56
45
|
},
|
|
57
46
|
"engines": {
|
|
58
|
-
"node": ">=
|
|
47
|
+
"node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0"
|
|
59
48
|
},
|
|
60
49
|
"funding": {
|
|
61
50
|
"type": "buymeacoffee",
|
|
62
51
|
"url": "https://www.buymeacoffee.com/luligugithub"
|
|
63
52
|
}
|
|
64
53
|
},
|
|
65
|
-
"node_modules/
|
|
54
|
+
"node_modules/node-persist-manager/node_modules/node-persist": {
|
|
55
|
+
"version": "4.0.4",
|
|
56
|
+
"resolved": "https://registry.npmjs.org/node-persist/-/node-persist-4.0.4.tgz",
|
|
57
|
+
"integrity": "sha512-8sPAz/7tw1mCCc8xBG4f0wi+flHkSSgQeX998iQ75Pu27evA6UUWCjSE7xnrYTg2q33oU5leJ061EKPDv6BocQ==",
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"p-limit": "^3.1.0"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=10.12.0"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"node_modules/node-persist-manager/node_modules/p-limit": {
|
|
66
67
|
"version": "3.1.0",
|
|
67
68
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
|
68
69
|
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
"url": "https://github.com/sponsors/sindresorhus"
|
|
78
79
|
}
|
|
79
80
|
},
|
|
80
|
-
"node_modules/yocto-queue": {
|
|
81
|
+
"node_modules/node-persist-manager/node_modules/yocto-queue": {
|
|
81
82
|
"version": "0.1.0",
|
|
82
83
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
|
83
84
|
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge-webhooks",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "Matterbridge webhooks plugin",
|
|
5
5
|
"author": "https://github.com/Luligu",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -52,10 +52,11 @@
|
|
|
52
52
|
"hooks",
|
|
53
53
|
"childbridge",
|
|
54
54
|
"hass",
|
|
55
|
-
"hassio"
|
|
55
|
+
"hassio",
|
|
56
|
+
"gladysassistant"
|
|
56
57
|
],
|
|
57
58
|
"engines": {
|
|
58
|
-
"node": ">=20.
|
|
59
|
+
"node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0 || >=26.0.0 <27.0.0"
|
|
59
60
|
},
|
|
60
61
|
"files": [
|
|
61
62
|
"bin",
|
|
@@ -66,7 +67,7 @@
|
|
|
66
67
|
"*.schema.json"
|
|
67
68
|
],
|
|
68
69
|
"dependencies": {
|
|
69
|
-
"node-ansi-logger": "3.2.
|
|
70
|
-
"node-persist-manager": "2.0.
|
|
70
|
+
"node-ansi-logger": "3.2.1",
|
|
71
|
+
"node-persist-manager": "2.0.2"
|
|
71
72
|
}
|
|
72
73
|
}
|