matterbridge-webhooks 1.0.2 → 1.1.0-edge.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +55 -4
- package/dist/module.js +177 -18
- package/matterbridge-webhooks.config.json +2 -0
- package/matterbridge-webhooks.schema.json +59 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,61 @@ If you like this project and find it useful, please consider giving it a star on
|
|
|
6
6
|
|
|
7
7
|
<a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
|
|
8
8
|
|
|
9
|
+
## [1.0.3-edge] - Not released
|
|
10
|
+
|
|
11
|
+
### Added Webhook devices
|
|
12
|
+
|
|
13
|
+
It allows to create a device based on webhooks.
|
|
14
|
+
|
|
15
|
+
Features:
|
|
16
|
+
|
|
17
|
+
- It is possible to choose the device type from the config.
|
|
18
|
+
- It is possible to set the method with a prefix 'GET#' or 'POST# in the urls. Default if omitted is GET.
|
|
19
|
+
- It is possible to use converters in the url.
|
|
20
|
+
|
|
21
|
+
## Supported device types:
|
|
22
|
+
|
|
23
|
+
| Device type | Urls |
|
|
24
|
+
| -------------- | ------------------------------------ |
|
|
25
|
+
| outlet | on off |
|
|
26
|
+
| onOffLight | on off |
|
|
27
|
+
| dimmerLight | on off brightness |
|
|
28
|
+
| colorTempLight | on off brightness colorTemp |
|
|
29
|
+
| extendedLight | on off brightness colorTemp colorRgb |
|
|
30
|
+
|
|
31
|
+
If there is interest, let me know and I will add all other device types.
|
|
32
|
+
|
|
33
|
+
## Supported request converters:
|
|
34
|
+
|
|
35
|
+
| Converter | Return value |
|
|
36
|
+
| ------------- | ------------------- |
|
|
37
|
+
| ${LEVEL} | matter 1-254 |
|
|
38
|
+
| ${LEVEL100} | percentage 0-100 |
|
|
39
|
+
| ${MIRED} | colorTemp in mired |
|
|
40
|
+
| ${KELVIN} | colorTemp in kelvin |
|
|
41
|
+
| ${HUE} | hue 0-360 |
|
|
42
|
+
| ${SATURATION} | saturation 0-100 |
|
|
43
|
+
| ${COLORX} | colorX 0-1 |
|
|
44
|
+
| ${COLORY} | colorX 0-1 |
|
|
45
|
+
|
|
46
|
+
## Supported cluster attributes:
|
|
47
|
+
|
|
48
|
+
| Attributes | Return value |
|
|
49
|
+
| ------------- | ------------------- |
|
|
50
|
+
| ${level} | matter 1-254 |
|
|
51
|
+
| ${level100} | percentage 0-100 |
|
|
52
|
+
| ${mired} | colorTemp in mired |
|
|
53
|
+
| ${kelvin} | colorTemp in kelvin |
|
|
54
|
+
| ${hue} | hue 0-360 |
|
|
55
|
+
| ${saturation} | saturation 0-100 |
|
|
56
|
+
| ${colorX} | colorX 0-1 |
|
|
57
|
+
| ${colorY} | colorX 0-1 |
|
|
58
|
+
| ${red} | red 0-255 |
|
|
59
|
+
| ${gree} | green 0-255 |
|
|
60
|
+
| ${blue} | blue 0-255 |
|
|
61
|
+
|
|
62
|
+
<a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
|
|
63
|
+
|
|
9
64
|
## [1.0.2] - 2025-12-12
|
|
10
65
|
|
|
11
66
|
### Changed
|
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
# Webhooks
|
|
19
|
+
|
|
18
20
|
This plugin allows you to expose any webhooks to Matter.
|
|
19
21
|
|
|
20
22
|
Features:
|
|
@@ -24,11 +26,60 @@ Features:
|
|
|
24
26
|
- It is possible to choose the method: GET or POST.
|
|
25
27
|
- The webhook can be tested in the frontend.
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
# Webhook devices
|
|
30
|
+
|
|
31
|
+
It allows also to create a device based on webhooks.
|
|
32
|
+
|
|
33
|
+
Features:
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
- It is possible to choose the device type from the config.
|
|
36
|
+
- It is possible to set the method with a prefix 'GET#' or 'POST# in the urls. Default if omitted is GET.
|
|
37
|
+
- It is possible to use converters in the url.
|
|
38
|
+
|
|
39
|
+
## Supported device types:
|
|
40
|
+
|
|
41
|
+
| Device type | Urls |
|
|
42
|
+
| -------------- | ------------------------------------ |
|
|
43
|
+
| outlet | on off |
|
|
44
|
+
| onOffLight | on off |
|
|
45
|
+
| dimmerLight | on off brightness |
|
|
46
|
+
| colorTempLight | on off brightness colorTemp |
|
|
47
|
+
| extendedLight | on off brightness colorTemp colorRgb |
|
|
48
|
+
|
|
49
|
+
If there is interest, let me know and I will add all other device types.
|
|
50
|
+
|
|
51
|
+
## Supported request converters:
|
|
52
|
+
|
|
53
|
+
| Converter | Return value |
|
|
54
|
+
| ------------- | ------------------- |
|
|
55
|
+
| ${LEVEL} | matter 1-254 |
|
|
56
|
+
| ${LEVEL100} | percentage 0-100 |
|
|
57
|
+
| ${MIRED} | colorTemp in mired |
|
|
58
|
+
| ${KELVIN} | colorTemp in kelvin |
|
|
59
|
+
| ${HUE} | hue 0-360 |
|
|
60
|
+
| ${SATURATION} | saturation 0-100 |
|
|
61
|
+
| ${COLORX} | colorX 0-1 |
|
|
62
|
+
| ${COLORY} | colorX 0-1 |
|
|
63
|
+
|
|
64
|
+
## Supported cluster attributes:
|
|
65
|
+
|
|
66
|
+
| Attributes | Return value |
|
|
67
|
+
| ------------- | ------------------- |
|
|
68
|
+
| ${level} | matter 1-254 |
|
|
69
|
+
| ${level100} | percentage 0-100 |
|
|
70
|
+
| ${mired} | colorTemp in mired |
|
|
71
|
+
| ${kelvin} | colorTemp in kelvin |
|
|
72
|
+
| ${hue} | hue 0-360 |
|
|
73
|
+
| ${saturation} | saturation 0-100 |
|
|
74
|
+
| ${colorX} | colorX 0-1 |
|
|
75
|
+
| ${colorY} | colorX 0-1 |
|
|
76
|
+
| ${red} | red 0-255 |
|
|
77
|
+
| ${gree} | green 0-255 |
|
|
78
|
+
| ${blue} | blue 0-255 |
|
|
79
|
+
|
|
80
|
+
If you like this project and find it useful, please consider giving it a star on [GitHub](https://github.com/Luligu/matterbridge-webhooks) and sponsoring it.
|
|
81
|
+
|
|
82
|
+
<a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
|
|
32
83
|
|
|
33
84
|
## Prerequisites
|
|
34
85
|
|
package/dist/module.js
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
import { bridgedNode, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, onOffLight, onOffOutlet, onOffSwitch } from 'matterbridge';
|
|
2
|
-
import { isValidObject } from 'matterbridge/utils';
|
|
1
|
+
import { bridgedNode, colorTemperatureLight, dimmableLight, extendedColorLight, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, onOffLight, onOffOutlet, onOffSwitch, } from 'matterbridge';
|
|
2
|
+
import { hslColorToRgbColor, isValidNumber, isValidObject, isValidString, miredToKelvin, wait } from 'matterbridge/utils';
|
|
3
3
|
import { fetch } from './fetch.js';
|
|
4
4
|
export default function initializePlugin(matterbridge, log, config) {
|
|
5
5
|
return new WebhooksPlatform(matterbridge, log, config);
|
|
6
6
|
}
|
|
7
7
|
export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
8
|
-
|
|
9
|
-
bridgedDevices = new Map();
|
|
8
|
+
config;
|
|
10
9
|
constructor(matterbridge, log, config) {
|
|
11
10
|
super(matterbridge, log, config);
|
|
11
|
+
this.config = config;
|
|
12
12
|
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.4.0')) {
|
|
13
13
|
throw new Error(`This plugin requires Matterbridge version >= "3.4.0". Please update Matterbridge to the latest version in the frontend.`);
|
|
14
14
|
}
|
|
15
15
|
this.log.info('Initializing platform:', this.config.name);
|
|
16
|
-
this.webhooks = config.webhooks;
|
|
17
16
|
this.log.info('Finished initializing platform:', this.config.name);
|
|
18
17
|
}
|
|
19
18
|
async onStart(reason) {
|
|
20
19
|
this.log.info('onStart called with reason:', reason ?? 'none');
|
|
21
20
|
await this.ready;
|
|
22
21
|
await this.clearSelect();
|
|
23
|
-
let i =
|
|
24
|
-
for (const webhookName in this.webhooks) {
|
|
25
|
-
this.log.debug(`Loading webhook ${
|
|
26
|
-
const webhook = this.webhooks[webhookName];
|
|
22
|
+
let i = 1;
|
|
23
|
+
for (const webhookName in this.config.webhooks) {
|
|
24
|
+
this.log.debug(`Loading webhook ${i} ${webhookName} with method ${this.config.webhooks[webhookName].method} and url ${this.config.webhooks[webhookName].httpUrl}`);
|
|
25
|
+
const webhook = this.config.webhooks[webhookName];
|
|
27
26
|
this.setSelectDevice('webhook' + i, webhookName, undefined, 'hub');
|
|
28
27
|
if (!this.validateDevice(['webhook' + i, webhookName], true))
|
|
29
28
|
continue;
|
|
@@ -35,6 +34,7 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
35
34
|
.addCommandHandler('on', async () => {
|
|
36
35
|
this.log.info(`Webhook ${webhookName} triggered.`);
|
|
37
36
|
await device.setAttribute('onOff', 'onOff', false, device.log);
|
|
37
|
+
this.log.debug(`Fetching ${webhook.httpUrl} with ${webhook.method}...`);
|
|
38
38
|
fetch(webhook.httpUrl, webhook.method)
|
|
39
39
|
.then(() => this.log.notice(`Webhook ${webhookName} successful!`))
|
|
40
40
|
.catch((err) => {
|
|
@@ -42,16 +42,85 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
44
|
await this.registerDevice(device);
|
|
45
|
-
|
|
45
|
+
}
|
|
46
|
+
i = 1;
|
|
47
|
+
for (const outletName in this.config.outlets) {
|
|
48
|
+
this.log.debug(`Loading outlet ${i} ${outletName}...`);
|
|
49
|
+
const webhook = this.config.outlets[outletName];
|
|
50
|
+
this.setSelectDevice('outlet' + i, outletName, undefined, 'hub');
|
|
51
|
+
if (!this.validateDevice(['outlet' + i, outletName], true))
|
|
52
|
+
continue;
|
|
53
|
+
this.log.info(`Registering outlet: ${outletName}...`);
|
|
54
|
+
const device = new MatterbridgeEndpoint([onOffOutlet, bridgedNode], { id: outletName }, this.config.debug)
|
|
55
|
+
.createDefaultBridgedDeviceBasicInformationClusterServer(outletName, 'outlet' + i++, this.matterbridge.aggregatorVendorId, 'Matterbridge', 'Matterbridge Webhook Outlet', 0, this.config.version)
|
|
56
|
+
.createOnOffClusterServer(false)
|
|
57
|
+
.addRequiredClusterServers()
|
|
58
|
+
.addCommandHandler('on', async (data) => {
|
|
59
|
+
this.parseUrl('outlet', outletName, 'on', webhook.onUrl, data);
|
|
60
|
+
})
|
|
61
|
+
.addCommandHandler('off', async (data) => {
|
|
62
|
+
this.parseUrl('outlet', outletName, 'off', webhook.offUrl, data);
|
|
63
|
+
});
|
|
64
|
+
await this.registerDevice(device);
|
|
65
|
+
}
|
|
66
|
+
i = 1;
|
|
67
|
+
for (const lightName in this.config.lights) {
|
|
68
|
+
this.log.debug(`Loading light ${i} ${lightName}...`);
|
|
69
|
+
const webhook = this.config.lights[lightName];
|
|
70
|
+
this.setSelectDevice('light' + i, lightName, undefined, 'hub');
|
|
71
|
+
if (!this.validateDevice(['light' + i, lightName], true))
|
|
72
|
+
continue;
|
|
73
|
+
this.log.info(`Registering light: ${lightName}...`);
|
|
74
|
+
let deviceType = onOffLight;
|
|
75
|
+
if (isValidString(webhook.brightnessUrl, 1))
|
|
76
|
+
deviceType = dimmableLight;
|
|
77
|
+
if (isValidString(webhook.colorTempUrl, 1))
|
|
78
|
+
deviceType = colorTemperatureLight;
|
|
79
|
+
if (isValidString(webhook.rgbUrl, 1))
|
|
80
|
+
deviceType = extendedColorLight;
|
|
81
|
+
const device = new MatterbridgeEndpoint([deviceType, bridgedNode], { id: lightName }, this.config.debug)
|
|
82
|
+
.createDefaultBridgedDeviceBasicInformationClusterServer(lightName, 'light' + i++, this.matterbridge.aggregatorVendorId, 'Matterbridge', 'Matterbridge Webhook Light', 0, this.config.version)
|
|
83
|
+
.createOnOffClusterServer(false)
|
|
84
|
+
.addRequiredClusterServers()
|
|
85
|
+
.addCommandHandler('on', async (data) => {
|
|
86
|
+
this.parseUrl('light', lightName, 'on', webhook.onUrl, data);
|
|
87
|
+
})
|
|
88
|
+
.addCommandHandler('off', async (data) => {
|
|
89
|
+
this.parseUrl('light', lightName, 'off', webhook.offUrl, data);
|
|
90
|
+
})
|
|
91
|
+
.addCommandHandler('moveToLevel', async (data) => {
|
|
92
|
+
this.parseUrl('light', lightName, 'moveToLevel', webhook.brightnessUrl, data);
|
|
93
|
+
})
|
|
94
|
+
.addCommandHandler('moveToLevelWithOnOff', async (data) => {
|
|
95
|
+
this.parseUrl('light', lightName, 'moveToLevelWithOnOff', webhook.brightnessUrl, data);
|
|
96
|
+
})
|
|
97
|
+
.addCommandHandler('moveToColorTemperature', async (data) => {
|
|
98
|
+
this.parseUrl('light', lightName, 'moveToColorTemperature', webhook.colorTempUrl, data);
|
|
99
|
+
})
|
|
100
|
+
.addCommandHandler('moveToHueAndSaturation', async (data) => {
|
|
101
|
+
this.parseUrl('light', lightName, 'moveToHueAndSaturation', webhook.rgbUrl, data);
|
|
102
|
+
})
|
|
103
|
+
.addCommandHandler('moveToHue', async (data) => {
|
|
104
|
+
this.parseUrl('light', lightName, 'moveToHue', webhook.rgbUrl, data);
|
|
105
|
+
})
|
|
106
|
+
.addCommandHandler('moveToSaturation', async (data) => {
|
|
107
|
+
this.parseUrl('light', lightName, 'moveToSaturation', webhook.rgbUrl, data);
|
|
108
|
+
})
|
|
109
|
+
.addCommandHandler('moveToColor', async (data) => {
|
|
110
|
+
this.parseUrl('light', lightName, 'moveToColor', webhook.rgbUrl, data);
|
|
111
|
+
});
|
|
112
|
+
await this.registerDevice(device);
|
|
46
113
|
}
|
|
47
114
|
}
|
|
48
115
|
async onConfigure() {
|
|
49
116
|
await super.onConfigure();
|
|
50
117
|
this.log.info('onConfigure called');
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
118
|
+
for (const device of this.getDevices()) {
|
|
119
|
+
if (device.deviceName && device.deviceName in this.config.webhooks) {
|
|
120
|
+
this.log.info(`Configuring device: ${device.deviceName}`);
|
|
121
|
+
await device.setAttribute('onOff', 'onOff', false, device.log);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
55
124
|
}
|
|
56
125
|
async onAction(action, value, id, formData) {
|
|
57
126
|
this.log.info('onAction called with action:', action, 'and value:', value ?? 'none', 'and id:', id ?? 'none');
|
|
@@ -81,9 +150,9 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
81
150
|
}
|
|
82
151
|
return;
|
|
83
152
|
}
|
|
84
|
-
for (const webhookName in this.webhooks) {
|
|
85
|
-
if (Object.prototype.hasOwnProperty.call(this.webhooks, webhookName)) {
|
|
86
|
-
const webhook = this.webhooks[webhookName];
|
|
153
|
+
for (const webhookName in this.config.webhooks) {
|
|
154
|
+
if (Object.prototype.hasOwnProperty.call(this.config.webhooks, webhookName)) {
|
|
155
|
+
const webhook = this.config.webhooks[webhookName];
|
|
87
156
|
if (id?.includes(webhookName)) {
|
|
88
157
|
this.log.info(`Testing webhook ${webhookName} method ${webhook.method} url ${webhook.httpUrl}`);
|
|
89
158
|
fetch(webhook.httpUrl, webhook.method)
|
|
@@ -104,6 +173,96 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
|
|
|
104
173
|
this.log.info('onShutdown called with reason:', reason ?? 'none');
|
|
105
174
|
if (this.config.unregisterOnShutdown === true)
|
|
106
175
|
await this.unregisterAllDevices();
|
|
107
|
-
|
|
176
|
+
}
|
|
177
|
+
async parseUrl(deviceType, deviceName, command, url, data) {
|
|
178
|
+
this.log.info(`Webhook ${deviceType} ${deviceName} ${command} triggered.`);
|
|
179
|
+
let method = 'GET';
|
|
180
|
+
let parsedUrl = url;
|
|
181
|
+
if (url.startsWith('GET#')) {
|
|
182
|
+
method = 'GET';
|
|
183
|
+
parsedUrl = url.replace('GET#', '');
|
|
184
|
+
}
|
|
185
|
+
else if (url.startsWith('POST#')) {
|
|
186
|
+
method = 'POST';
|
|
187
|
+
parsedUrl = url.replace('POST#', '');
|
|
188
|
+
}
|
|
189
|
+
if (parsedUrl.includes('${LEVEL}') && isValidNumber(data.request.level)) {
|
|
190
|
+
parsedUrl = parsedUrl.replace('${LEVEL}', data.request.level.toString());
|
|
191
|
+
}
|
|
192
|
+
if (url.includes('${LEVEL100}') && isValidNumber(data.request.level)) {
|
|
193
|
+
parsedUrl = parsedUrl.replace('${LEVEL100}', Math.round((data.request.level / 254) * 100).toString());
|
|
194
|
+
}
|
|
195
|
+
if (parsedUrl.includes('${KELVIN}') && isValidNumber(data.request.colorTemperatureMireds)) {
|
|
196
|
+
parsedUrl = parsedUrl.replace('${KELVIN}', Math.round(miredToKelvin(data.request.colorTemperatureMireds)).toString());
|
|
197
|
+
}
|
|
198
|
+
if (parsedUrl.includes('${MIRED}') && isValidNumber(data.request.colorTemperatureMireds)) {
|
|
199
|
+
parsedUrl = parsedUrl.replace('${MIRED}', Math.round(data.request.colorTemperatureMireds).toString());
|
|
200
|
+
}
|
|
201
|
+
if (parsedUrl.includes('${COLORX}') && isValidNumber(data.request.colorX, 0, 65279)) {
|
|
202
|
+
parsedUrl = parsedUrl.replace('${COLORX}', this.roundTo(data.request.colorX / 65536, 4).toString());
|
|
203
|
+
}
|
|
204
|
+
if (parsedUrl.includes('${COLORY}') && isValidNumber(data.request.colorY, 0, 65279)) {
|
|
205
|
+
parsedUrl = parsedUrl.replace('${COLORY}', this.roundTo(data.request.colorY / 65536, 4).toString());
|
|
206
|
+
}
|
|
207
|
+
if (parsedUrl.includes('${HUE}') && isValidNumber(data.request.hue, 0, 254)) {
|
|
208
|
+
parsedUrl = parsedUrl.replace('${HUE}', Math.round((data.request.hue * 360) / 254).toString());
|
|
209
|
+
}
|
|
210
|
+
if (parsedUrl.includes('${SATURATION}') && isValidNumber(data.request.saturation, 0, 254)) {
|
|
211
|
+
parsedUrl = parsedUrl.replace('${SATURATION}', Math.round((data.request.saturation * 100) / 254).toString());
|
|
212
|
+
}
|
|
213
|
+
if ((parsedUrl.includes('${level}') || parsedUrl.includes('${level100}')) && isValidNumber(data.attributes.currentLevel, 1, 254)) {
|
|
214
|
+
await wait(100);
|
|
215
|
+
if (url.includes('${level}'))
|
|
216
|
+
parsedUrl = parsedUrl.replace('${level}', data.attributes.currentLevel.toString());
|
|
217
|
+
if (url.includes('${level100}'))
|
|
218
|
+
parsedUrl = parsedUrl.replace('${level100}', Math.round((data.attributes.currentLevel / 254) * 100).toString());
|
|
219
|
+
}
|
|
220
|
+
if ((parsedUrl.includes('${mired}') || parsedUrl.includes('${kelvin}')) &&
|
|
221
|
+
isValidNumber(data.attributes.colorTemperatureMireds, data.attributes.colorTempPhysicalMinMireds, data.attributes.colorTempPhysicalMaxMireds)) {
|
|
222
|
+
await wait(100);
|
|
223
|
+
const kelvin = miredToKelvin(data.attributes.colorTemperatureMireds);
|
|
224
|
+
this.log.debug(`Attribute colorTemperatureMireds is ${data.attributes.colorTemperatureMireds}, which is ${kelvin}K`);
|
|
225
|
+
if (url.includes('${mired}'))
|
|
226
|
+
parsedUrl = parsedUrl.replace('${mired}', data.attributes.colorTemperatureMireds.toString());
|
|
227
|
+
if (url.includes('${kelvin}'))
|
|
228
|
+
parsedUrl = parsedUrl.replace('${kelvin}', kelvin.toString());
|
|
229
|
+
}
|
|
230
|
+
if ((parsedUrl.includes('${hue}') || parsedUrl.includes('${saturation}') || parsedUrl.includes('${red}') || parsedUrl.includes('${green}') || parsedUrl.includes('${blue}')) &&
|
|
231
|
+
isValidNumber(data.attributes.currentHue, 0, 254) &&
|
|
232
|
+
isValidNumber(data.attributes.currentSaturation, 0, 254)) {
|
|
233
|
+
await wait(100);
|
|
234
|
+
const rgb = hslColorToRgbColor((data.attributes.currentHue * 360) / 254, (data.attributes.currentSaturation * 100) / 254, 50);
|
|
235
|
+
this.log.debug(`Converted hue ${data.attributes.currentHue} and saturation ${data.attributes.currentSaturation} to RGB r: ${rgb.r} g: ${rgb.g} b: ${rgb.b}`);
|
|
236
|
+
if (url.includes('${hue}'))
|
|
237
|
+
parsedUrl = parsedUrl.replace('${hue}', Math.round((data.attributes.currentHue * 360) / 254).toString());
|
|
238
|
+
if (url.includes('${saturation}'))
|
|
239
|
+
parsedUrl = parsedUrl.replace('${saturation}', Math.round((data.attributes.currentSaturation * 100) / 254).toString());
|
|
240
|
+
if (url.includes('${red}') && rgb)
|
|
241
|
+
parsedUrl = parsedUrl.replace('${red}', rgb.r.toString());
|
|
242
|
+
if (url.includes('${green}') && rgb)
|
|
243
|
+
parsedUrl = parsedUrl.replace('${green}', rgb.g.toString());
|
|
244
|
+
if (url.includes('${blue}') && rgb)
|
|
245
|
+
parsedUrl = parsedUrl.replace('${blue}', rgb.b.toString());
|
|
246
|
+
}
|
|
247
|
+
if ((parsedUrl.includes('${colorX}') || parsedUrl.includes('${colorY}')) &&
|
|
248
|
+
isValidNumber(data.attributes.currentX, 0, 65279) &&
|
|
249
|
+
isValidNumber(data.attributes.currentY, 0, 65279)) {
|
|
250
|
+
await wait(100);
|
|
251
|
+
if (url.includes('${colorX}'))
|
|
252
|
+
parsedUrl = parsedUrl.replace('${colorX}', this.roundTo(data.attributes.currentX / 65536, 4).toString());
|
|
253
|
+
if (url.includes('${colorY}'))
|
|
254
|
+
parsedUrl = parsedUrl.replace('${colorY}', this.roundTo(data.attributes.currentY / 65536, 4).toString());
|
|
255
|
+
}
|
|
256
|
+
this.log.debug(`Fetching ${parsedUrl} with ${method}...`);
|
|
257
|
+
fetch(parsedUrl, method)
|
|
258
|
+
.then(() => this.log.notice(`Webhook ${deviceType} ${deviceName} ${command} successful!`))
|
|
259
|
+
.catch((err) => {
|
|
260
|
+
this.log.error(`Webhook ${deviceType} ${deviceName} ${command} failed: ${err instanceof Error ? err.message : err}`);
|
|
261
|
+
});
|
|
262
|
+
return { method, url: parsedUrl };
|
|
263
|
+
}
|
|
264
|
+
roundTo(value, digits) {
|
|
265
|
+
const factor = Math.pow(10, digits);
|
|
266
|
+
return Math.round(value * factor) / factor;
|
|
108
267
|
}
|
|
109
268
|
}
|
|
@@ -75,6 +75,65 @@
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
|
+
"outlets": {
|
|
79
|
+
"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]#http://mydomain.com/api/device/on\").",
|
|
80
|
+
"type": "object",
|
|
81
|
+
"uniqueItems": true,
|
|
82
|
+
"additionalProperties": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"description": "Outlet parameters:",
|
|
85
|
+
"required": ["onUrl", "offUrl"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"onUrl": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"title": "Outlet On URL",
|
|
90
|
+
"description": "URL to turn on the outlet."
|
|
91
|
+
},
|
|
92
|
+
"offUrl": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"title": "Outlet Off URL",
|
|
95
|
+
"description": "URL to turn off the outlet."
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"lights": {
|
|
101
|
+
"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]#http://mydomain.com/api/device/on).",
|
|
102
|
+
"type": "object",
|
|
103
|
+
"uniqueItems": true,
|
|
104
|
+
"additionalProperties": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"description": "Light parameters:",
|
|
107
|
+
"required": ["onUrl", "offUrl"],
|
|
108
|
+
"properties": {
|
|
109
|
+
"onUrl": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"title": "Light On URL",
|
|
112
|
+
"description": "URL to turn on the light."
|
|
113
|
+
},
|
|
114
|
+
"offUrl": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"title": "Light Off URL",
|
|
117
|
+
"description": "URL to turn off the light."
|
|
118
|
+
},
|
|
119
|
+
"brightnessUrl": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"title": "Light Brightness URL",
|
|
122
|
+
"description": "URL to adjust the brightness of the light. (i.e. [GET|POST]:http://mydomain.com/api/device?brightness=${BRIGHTNESS})"
|
|
123
|
+
},
|
|
124
|
+
"colorTempUrl": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"title": "Light Color Temperature URL",
|
|
127
|
+
"description": "URL to adjust the color temperature of the light. (i.e. [GET|POST]:http://mydomain.com/api/device?colorTemp=${COLORTEMP})"
|
|
128
|
+
},
|
|
129
|
+
"rgbUrl": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"title": "Light RGB URL",
|
|
132
|
+
"description": "URL to adjust the RGB color of the light. (i.e. [GET|POST]:http://mydomain.com/api/device?rgb=${RGB})"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
78
137
|
"debug": {
|
|
79
138
|
"description": "Enable the debug for the plugin.",
|
|
80
139
|
"type": "boolean",
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge-webhooks",
|
|
3
|
-
"version": "1.0.2",
|
|
3
|
+
"version": "1.1.0-edge.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge-webhooks",
|
|
9
|
-
"version": "1.0.2",
|
|
9
|
+
"version": "1.1.0-edge.2",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"node-ansi-logger": "3.1.1",
|