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