matterbridge-webhooks 1.1.0-edge.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,7 +6,7 @@ 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
9
+ ## [1.1.0-edge.3] - Not released
10
10
 
11
11
  ### Added Webhook devices
12
12
 
package/README.md CHANGED
@@ -15,9 +15,9 @@
15
15
 
16
16
  ---
17
17
 
18
- # Webhooks
18
+ This plugin allows you to expose single webhooks or complex webhook devices to Matter.
19
19
 
20
- This plugin allows you to expose any webhooks to Matter.
20
+ # Simple webhooks
21
21
 
22
22
  Features:
23
23
 
@@ -28,13 +28,11 @@ Features:
28
28
 
29
29
  # Webhook devices
30
30
 
31
- It allows also to create a device based on webhooks.
32
-
33
31
  Features:
34
32
 
35
33
  - It is possible to choose the device type from the config.
36
34
  - 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.
35
+ - It is possible to use converters and attributes in the url.
38
36
 
39
37
  ## Supported device types:
40
38
 
@@ -85,21 +83,9 @@ If you like this project and find it useful, please consider giving it a star on
85
83
 
86
84
  ### Matterbridge
87
85
 
88
- Follow these steps to install or update Matterbridge if it is not already installed and up to date:
89
-
90
- ```
91
- npm install -g matterbridge --omit=dev
92
- ```
93
-
94
- on Linux you may need the necessary permissions:
95
-
96
- ```
97
- sudo npm install -g matterbridge --omit=dev
98
- ```
99
-
100
- 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.
101
87
 
102
- ## How to add a webhook
88
+ ## How to add a simple webhook
103
89
 
104
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.
105
91
 
package/dist/module.js CHANGED
@@ -1,5 +1,6 @@
1
- import { bridgedNode, colorTemperatureLight, dimmableLight, extendedColorLight, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, onOffLight, onOffOutlet, onOffSwitch, } from 'matterbridge';
1
+ import { bridgedNode, colorTemperatureLight, dimmableLight, extendedColorLight, MatterbridgeColorControlServer, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, MatterbridgeLevelControlServer, onOffLight, onOffOutlet, onOffSwitch, } from 'matterbridge';
2
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);
@@ -32,7 +33,7 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
32
33
  .createOnOffClusterServer(false)
33
34
  .addRequiredClusterServers()
34
35
  .addCommandHandler('on', async () => {
35
- this.log.info(`Webhook ${webhookName} triggered.`);
36
+ this.log.info(`Webhook ${webhookName} triggered`);
36
37
  await device.setAttribute('onOff', 'onOff', false, device.log);
37
38
  this.log.debug(`Fetching ${webhook.httpUrl} with ${webhook.method}...`);
38
39
  fetch(webhook.httpUrl, webhook.method)
@@ -81,6 +82,8 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
81
82
  const device = new MatterbridgeEndpoint([deviceType, bridgedNode], { id: lightName }, this.config.debug)
82
83
  .createDefaultBridgedDeviceBasicInformationClusterServer(lightName, 'light' + i++, this.matterbridge.aggregatorVendorId, 'Matterbridge', 'Matterbridge Webhook Light', 0, this.config.version)
83
84
  .createOnOffClusterServer(false)
85
+ .createDefaultColorControlClusterServer(undefined, undefined, undefined, undefined, 250, webhook.minMireds, webhook.maxMireds)
86
+ .createDefaultLevelControlClusterServer()
84
87
  .addRequiredClusterServers()
85
88
  .addCommandHandler('on', async (data) => {
86
89
  this.parseUrl('light', lightName, 'on', webhook.onUrl, data);
@@ -175,7 +178,9 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
175
178
  await this.unregisterAllDevices();
176
179
  }
177
180
  async parseUrl(deviceType, deviceName, command, url, data) {
178
- this.log.info(`Webhook ${deviceType} ${deviceName} ${command} triggered.`);
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}`);
179
184
  let method = 'GET';
180
185
  let parsedUrl = url;
181
186
  if (url.startsWith('GET#')) {
@@ -212,50 +217,66 @@ export class WebhooksPlatform extends MatterbridgeDynamicPlatform {
212
217
  }
213
218
  if ((parsedUrl.includes('${level}') || parsedUrl.includes('${level100}')) && isValidNumber(data.attributes.currentLevel, 1, 254)) {
214
219
  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());
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
+ }
219
227
  }
220
228
  if ((parsedUrl.includes('${mired}') || parsedUrl.includes('${kelvin}')) &&
221
229
  isValidNumber(data.attributes.colorTemperatureMireds, data.attributes.colorTempPhysicalMinMireds, data.attributes.colorTempPhysicalMaxMireds)) {
222
230
  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());
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
+ }
229
240
  }
230
241
  if ((parsedUrl.includes('${hue}') || parsedUrl.includes('${saturation}') || parsedUrl.includes('${red}') || parsedUrl.includes('${green}') || parsedUrl.includes('${blue}')) &&
231
242
  isValidNumber(data.attributes.currentHue, 0, 254) &&
232
243
  isValidNumber(data.attributes.currentSaturation, 0, 254)) {
233
244
  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());
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
+ }
246
260
  }
247
261
  if ((parsedUrl.includes('${colorX}') || parsedUrl.includes('${colorY}')) &&
248
262
  isValidNumber(data.attributes.currentX, 0, 65279) &&
249
263
  isValidNumber(data.attributes.currentY, 0, 65279)) {
250
264
  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());
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
+ }
255
272
  }
256
273
  this.log.debug(`Fetching ${parsedUrl} with ${method}...`);
257
274
  fetch(parsedUrl, method)
258
- .then(() => this.log.notice(`Webhook ${deviceType} ${deviceName} ${command} successful!`))
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
+ })
259
280
  .catch((err) => {
260
281
  this.log.error(`Webhook ${deviceType} ${deviceName} ${command} failed: ${err instanceof Error ? err.message : err}`);
261
282
  });
@@ -106,6 +106,18 @@
106
106
  "description": "Light parameters:",
107
107
  "required": ["onUrl", "offUrl"],
108
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
+ },
109
121
  "onUrl": {
110
122
  "type": "string",
111
123
  "title": "Light On URL",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-webhooks",
3
- "version": "1.1.0-edge.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.1.0-edge.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.1.0-edge.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",