@switchbot/homebridge-switchbot 3.5.1-beta.13 → 3.5.1-beta.15

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.
Files changed (121) hide show
  1. package/dist/device/blindtilt.d.ts.map +1 -1
  2. package/dist/device/blindtilt.js +3 -1
  3. package/dist/device/blindtilt.js.map +1 -1
  4. package/dist/device/bot.d.ts.map +1 -1
  5. package/dist/device/bot.js +21 -1
  6. package/dist/device/bot.js.map +1 -1
  7. package/dist/device/ceilinglight.d.ts.map +1 -1
  8. package/dist/device/ceilinglight.js +3 -1
  9. package/dist/device/ceilinglight.js.map +1 -1
  10. package/dist/device/colorbulb.d.ts.map +1 -1
  11. package/dist/device/colorbulb.js +3 -1
  12. package/dist/device/colorbulb.js.map +1 -1
  13. package/dist/device/contact.d.ts.map +1 -1
  14. package/dist/device/contact.js +3 -1
  15. package/dist/device/contact.js.map +1 -1
  16. package/dist/device/curtain.d.ts.map +1 -1
  17. package/dist/device/curtain.js +3 -1
  18. package/dist/device/curtain.js.map +1 -1
  19. package/dist/device/device.d.ts +0 -8
  20. package/dist/device/device.d.ts.map +1 -1
  21. package/dist/device/device.js +9 -195
  22. package/dist/device/device.js.map +1 -1
  23. package/dist/device/fan.d.ts.map +1 -1
  24. package/dist/device/fan.js +3 -1
  25. package/dist/device/fan.js.map +1 -1
  26. package/dist/device/hub.d.ts.map +1 -1
  27. package/dist/device/hub.js +3 -1
  28. package/dist/device/hub.js.map +1 -1
  29. package/dist/device/humidifier.d.ts.map +1 -1
  30. package/dist/device/humidifier.js +3 -1
  31. package/dist/device/humidifier.js.map +1 -1
  32. package/dist/device/iosensor.d.ts.map +1 -1
  33. package/dist/device/iosensor.js +3 -1
  34. package/dist/device/iosensor.js.map +1 -1
  35. package/dist/device/lightstrip.d.ts.map +1 -1
  36. package/dist/device/lightstrip.js +3 -1
  37. package/dist/device/lightstrip.js.map +1 -1
  38. package/dist/device/lock.d.ts.map +1 -1
  39. package/dist/device/lock.js +3 -1
  40. package/dist/device/lock.js.map +1 -1
  41. package/dist/device/meter.d.ts.map +1 -1
  42. package/dist/device/meter.js +3 -1
  43. package/dist/device/meter.js.map +1 -1
  44. package/dist/device/meterplus.d.ts.map +1 -1
  45. package/dist/device/meterplus.js +3 -1
  46. package/dist/device/meterplus.js.map +1 -1
  47. package/dist/device/motion.d.ts.map +1 -1
  48. package/dist/device/motion.js +3 -1
  49. package/dist/device/motion.js.map +1 -1
  50. package/dist/device/plug.d.ts.map +1 -1
  51. package/dist/device/plug.js +3 -1
  52. package/dist/device/plug.js.map +1 -1
  53. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  54. package/dist/device/robotvacuumcleaner.js +3 -1
  55. package/dist/device/robotvacuumcleaner.js.map +1 -1
  56. package/dist/device/waterdetector.d.ts.map +1 -1
  57. package/dist/device/waterdetector.js +3 -1
  58. package/dist/device/waterdetector.js.map +1 -1
  59. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  60. package/dist/irdevice/airconditioner.js +2 -0
  61. package/dist/irdevice/airconditioner.js.map +1 -1
  62. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  63. package/dist/irdevice/airpurifier.js +2 -0
  64. package/dist/irdevice/airpurifier.js.map +1 -1
  65. package/dist/irdevice/camera.d.ts.map +1 -1
  66. package/dist/irdevice/camera.js +2 -0
  67. package/dist/irdevice/camera.js.map +1 -1
  68. package/dist/irdevice/fan.d.ts.map +1 -1
  69. package/dist/irdevice/fan.js +2 -0
  70. package/dist/irdevice/fan.js.map +1 -1
  71. package/dist/irdevice/irdevice.js +1 -1
  72. package/dist/irdevice/irdevice.js.map +1 -1
  73. package/dist/irdevice/light.d.ts.map +1 -1
  74. package/dist/irdevice/light.js +2 -0
  75. package/dist/irdevice/light.js.map +1 -1
  76. package/dist/irdevice/other.d.ts.map +1 -1
  77. package/dist/irdevice/other.js +2 -0
  78. package/dist/irdevice/other.js.map +1 -1
  79. package/dist/irdevice/tv.js +4 -4
  80. package/dist/irdevice/tv.js.map +1 -1
  81. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  82. package/dist/irdevice/vacuumcleaner.js +2 -0
  83. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  84. package/dist/irdevice/waterheater.d.ts.map +1 -1
  85. package/dist/irdevice/waterheater.js +2 -0
  86. package/dist/irdevice/waterheater.js.map +1 -1
  87. package/dist/platform.d.ts +10 -1
  88. package/dist/platform.d.ts.map +1 -1
  89. package/dist/platform.js +175 -2
  90. package/dist/platform.js.map +1 -1
  91. package/package.json +1 -1
  92. package/src/device/blindtilt.ts +4 -1
  93. package/src/device/bot.ts +32 -1
  94. package/src/device/ceilinglight.ts +4 -1
  95. package/src/device/colorbulb.ts +4 -1
  96. package/src/device/contact.ts +4 -1
  97. package/src/device/curtain.ts +3 -1
  98. package/src/device/device.ts +9 -195
  99. package/src/device/fan.ts +4 -1
  100. package/src/device/hub.ts +4 -1
  101. package/src/device/humidifier.ts +4 -1
  102. package/src/device/iosensor.ts +4 -1
  103. package/src/device/lightstrip.ts +4 -1
  104. package/src/device/lock.ts +4 -1
  105. package/src/device/meter.ts +4 -1
  106. package/src/device/meterplus.ts +4 -1
  107. package/src/device/motion.ts +4 -1
  108. package/src/device/plug.ts +4 -1
  109. package/src/device/robotvacuumcleaner.ts +4 -1
  110. package/src/device/waterdetector.ts +4 -1
  111. package/src/irdevice/airconditioner.ts +2 -1
  112. package/src/irdevice/airpurifier.ts +2 -0
  113. package/src/irdevice/camera.ts +2 -0
  114. package/src/irdevice/fan.ts +2 -0
  115. package/src/irdevice/irdevice.ts +1 -1
  116. package/src/irdevice/light.ts +2 -0
  117. package/src/irdevice/other.ts +2 -0
  118. package/src/irdevice/tv.ts +4 -4
  119. package/src/irdevice/vacuumcleaner.ts +2 -0
  120. package/src/irdevice/waterheater.ts +2 -0
  121. package/src/platform.ts +175 -2
package/dist/platform.js CHANGED
@@ -31,12 +31,14 @@ import { VacuumCleaner } from './irdevice/vacuumcleaner.js';
31
31
  import { AirConditioner } from './irdevice/airconditioner.js';
32
32
  import { Buffer } from 'buffer';
33
33
  import { request } from 'undici';
34
+ import asyncmqtt from 'async-mqtt';
35
+ import { createServer } from 'http';
34
36
  import { queueScheduler } from 'rxjs';
35
37
  import fakegato from 'fakegato-history';
36
38
  import crypto, { randomUUID } from 'crypto';
37
39
  import { readFileSync, writeFileSync } from 'fs';
38
40
  import { EveHomeKitTypes } from 'homebridge-lib/EveHomeKitTypes';
39
- import { PLATFORM_NAME, PLUGIN_NAME, Devices } from './settings.js';
41
+ import { PLATFORM_NAME, PLUGIN_NAME, Devices, deleteWebhook, queryWebhook, setupWebhook, updateWebhook } from './settings.js';
40
42
  import { isBlindTiltDevice, isCurtainDevice, sleep } from './utils.js';
41
43
  /**
42
44
  * HomebridgePlatform
@@ -55,8 +57,11 @@ export class SwitchBotPlatform {
55
57
  platformConfig;
56
58
  platformLogging;
57
59
  config;
58
- fakegatoAPI;
60
+ mqttClient = null;
61
+ webhookEventListener = null;
59
62
  eve;
63
+ fakegatoAPI;
64
+ webhookEventHandler = {};
60
65
  constructor(log, config, api) {
61
66
  // initialize
62
67
  this.accessories = [];
@@ -115,6 +120,174 @@ export class SwitchBotPlatform {
115
120
  await this.debugErrorLog(`Failed to Discover, Error: ${e}`);
116
121
  }
117
122
  });
123
+ this.setupMqtt();
124
+ this.setupwebhook();
125
+ }
126
+ async setupMqtt() {
127
+ if (this.config.options?.mqttURL) {
128
+ try {
129
+ const { connectAsync } = asyncmqtt;
130
+ this.mqttClient = await connectAsync(this.config.options?.mqttURL, this.config.options.mqttOptions || {});
131
+ await this.debugLog('MQTT connection has been established successfully.');
132
+ this.mqttClient.on('error', async (e) => {
133
+ await this.errorLog(`Failed to publish MQTT messages. ${e}`);
134
+ });
135
+ if (!this.config.options?.webhookURL) {
136
+ // receive webhook events via MQTT
137
+ await this.infoLog(`Webhook is configured to be received through ${this.config.options.mqttURL}/homebridge-switchbot/webhook.`);
138
+ this.mqttClient.subscribe('homebridge-switchbot/webhook/+');
139
+ this.mqttClient.on('message', async (topic, message) => {
140
+ try {
141
+ await await this.debugLog(`Received Webhook via MQTT: ${topic}=${message}`);
142
+ const context = JSON.parse(message.toString());
143
+ this.webhookEventHandler[context.deviceMac]?.(context);
144
+ }
145
+ catch (e) {
146
+ await this.errorLog(`Failed to handle webhook event. Error:${e}`);
147
+ }
148
+ });
149
+ }
150
+ }
151
+ catch (e) {
152
+ this.mqttClient = null;
153
+ await this.errorLog(`Failed to establish MQTT connection. ${e}`);
154
+ }
155
+ }
156
+ }
157
+ async setupwebhook() {
158
+ //webhook configuration
159
+ if (this.config.options?.webhookURL) {
160
+ const url = this.config.options?.webhookURL;
161
+ try {
162
+ const xurl = new URL(url);
163
+ const port = Number(xurl.port);
164
+ const path = xurl.pathname;
165
+ this.webhookEventListener = createServer((request, response) => {
166
+ try {
167
+ if (request.url === path && request.method === 'POST') {
168
+ request.on('data', async (data) => {
169
+ try {
170
+ const body = JSON.parse(data);
171
+ await this.debugLog(`Received Webhook: ${JSON.stringify(body)}`);
172
+ if (this.config.options?.mqttURL) {
173
+ const mac = body.context.deviceMac
174
+ ?.toLowerCase()
175
+ .match(/[\s\S]{1,2}/g)
176
+ ?.join(':');
177
+ const options = this.config.options?.mqttPubOptions || {};
178
+ this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options);
179
+ }
180
+ this.webhookEventHandler[body.context.deviceMac]?.(body.context);
181
+ }
182
+ catch (e) {
183
+ this.errorLog(`Failed to handle webhook event. Error:${e}`);
184
+ }
185
+ });
186
+ response.writeHead(200, { 'Content-Type': 'text/plain' });
187
+ response.end('OK');
188
+ }
189
+ // else {
190
+ // response.writeHead(403, {'Content-Type': 'text/plain'});
191
+ // response.end(`NG`);
192
+ // }
193
+ }
194
+ catch (e) {
195
+ this.errorLog(`Failed to handle webhook event. Error:${e}`);
196
+ }
197
+ }).listen(port ? port : 80);
198
+ }
199
+ catch (e) {
200
+ this.errorLog(`Failed to create webhook listener. Error:${e.message}`);
201
+ return;
202
+ }
203
+ try {
204
+ const { body, statusCode } = await request(setupWebhook, {
205
+ method: 'POST',
206
+ headers: this.generateHeaders(),
207
+ body: JSON.stringify({
208
+ 'action': 'setupWebhook',
209
+ 'url': url,
210
+ 'deviceList': 'ALL',
211
+ }),
212
+ });
213
+ const response = await body.json();
214
+ await this.debugLog(`setupWebhook: url:${url}`);
215
+ await this.debugLog(`setupWebhook: body:${JSON.stringify(response)}`);
216
+ await this.debugLog(`setupWebhook: statusCode:${statusCode}`);
217
+ if (statusCode !== 200 || response?.statusCode !== 100) {
218
+ this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} `
219
+ + `message:${response?.message}`);
220
+ }
221
+ }
222
+ catch (e) {
223
+ this.errorLog(`Failed to configure webhook. Error: ${e.message}`);
224
+ }
225
+ try {
226
+ const { body, statusCode } = await request(updateWebhook, {
227
+ method: 'POST', headers: this.generateHeaders(), body: JSON.stringify({
228
+ 'action': 'updateWebhook',
229
+ 'config': {
230
+ 'url': url,
231
+ 'enable': true,
232
+ },
233
+ }),
234
+ });
235
+ const response = await body.json();
236
+ await this.debugLog(`updateWebhook: url:${url}`);
237
+ await this.debugLog(`updateWebhook: body:${JSON.stringify(response)}`);
238
+ await this.debugLog(`updateWebhook: statusCode:${statusCode}`);
239
+ if (statusCode !== 200 || response?.statusCode !== 100) {
240
+ this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
241
+ }
242
+ }
243
+ catch (e) {
244
+ this.errorLog(`Failed to update webhook. Error:${e.message}`);
245
+ }
246
+ try {
247
+ const { body, statusCode } = await request(queryWebhook, {
248
+ method: 'POST',
249
+ headers: this.generateHeaders(),
250
+ body: JSON.stringify({
251
+ 'action': 'queryUrl',
252
+ }),
253
+ });
254
+ const response = await body.json();
255
+ await this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`);
256
+ await this.debugLog(`queryWebhook: statusCode:${statusCode}`);
257
+ if (statusCode !== 200 || response?.statusCode !== 100) {
258
+ this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
259
+ }
260
+ else {
261
+ this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`);
262
+ }
263
+ }
264
+ catch (e) {
265
+ this.errorLog(`Failed to query webhook. Error:${e}`);
266
+ }
267
+ this.api.on('shutdown', async () => {
268
+ try {
269
+ const { body, statusCode } = await request(deleteWebhook, {
270
+ method: 'POST',
271
+ headers: this.generateHeaders(),
272
+ body: JSON.stringify({
273
+ 'action': 'deleteWebhook',
274
+ 'url': url,
275
+ }),
276
+ });
277
+ const response = await body.json();
278
+ await this.debugLog(`deleteWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
279
+ if (statusCode !== 200 || response?.statusCode !== 100) {
280
+ this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
281
+ }
282
+ else {
283
+ this.infoLog('Unregistered webhook to close listening.');
284
+ }
285
+ }
286
+ catch (e) {
287
+ this.errorLog(`Failed to delete webhook. Error:${e.message}`);
288
+ }
289
+ });
290
+ }
118
291
  }
119
292
  /**
120
293
  * This function is invoked when homebridge restores cached accessories from disk at startup.