node-switchbot 2.5.0-beta.3 → 2.5.0-beta.5
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/dist/device.d.ts +86 -223
- package/dist/device.d.ts.map +1 -1
- package/dist/device.js +123 -407
- package/dist/device.js.map +1 -1
- package/dist/parameter-checker.d.ts +78 -18
- package/dist/parameter-checker.d.ts.map +1 -1
- package/dist/parameter-checker.js +139 -334
- package/dist/parameter-checker.js.map +1 -1
- package/dist/settings.d.ts +5 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +3 -2
- package/dist/settings.js.map +1 -1
- package/dist/types/types.d.ts +9 -0
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/device.js
CHANGED
|
@@ -1,46 +1,37 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { Advertising } from './advertising.js';
|
|
3
3
|
import { parameterChecker } from './parameter-checker.js';
|
|
4
|
-
import { CHAR_UUID_DEVICE, CHAR_UUID_NOTIFY, CHAR_UUID_WRITE,
|
|
4
|
+
import { CHAR_UUID_DEVICE, CHAR_UUID_NOTIFY, CHAR_UUID_WRITE, READ_TIMEOUT_MSEC, SERV_UUID_PRIMARY, WRITE_TIMEOUT_MSEC, } from './settings.js';
|
|
5
|
+
/**
|
|
6
|
+
* Represents a Switchbot Device.
|
|
7
|
+
*/
|
|
5
8
|
export class SwitchbotDevice {
|
|
6
9
|
_noble;
|
|
7
10
|
_peripheral;
|
|
8
|
-
_characteristics;
|
|
11
|
+
_characteristics = null;
|
|
9
12
|
_id;
|
|
10
13
|
_address;
|
|
11
14
|
_model;
|
|
12
15
|
_modelName;
|
|
13
|
-
_explicitly;
|
|
14
|
-
_connected;
|
|
15
|
-
onnotify_internal;
|
|
16
|
-
ondisconnect_internal;
|
|
17
|
-
onconnect_internal;
|
|
16
|
+
_explicitly = false;
|
|
17
|
+
_connected = false;
|
|
18
|
+
onnotify_internal = () => { };
|
|
19
|
+
ondisconnect_internal = async () => { };
|
|
20
|
+
onconnect_internal = async () => { };
|
|
18
21
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @param
|
|
22
|
-
* @param {Noble} noble - The Noble object created by the noble module.
|
|
23
|
-
*
|
|
24
|
-
* This constructor initializes a new instance of the Device class with the specified peripheral and noble objects.
|
|
22
|
+
* Initializes a new instance of the SwitchbotDevice class.
|
|
23
|
+
* @param peripheral The peripheral object from noble.
|
|
24
|
+
* @param noble The Noble object.
|
|
25
25
|
*/
|
|
26
26
|
constructor(peripheral, noble) {
|
|
27
27
|
this._peripheral = peripheral;
|
|
28
28
|
this._noble = noble;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this._modelName = ad ? ad.serviceData.modelName : null;
|
|
36
|
-
this._explicitly = false;
|
|
37
|
-
this._connected = false;
|
|
38
|
-
this._explicitly = false;
|
|
39
|
-
this._connected = false;
|
|
40
|
-
this.onconnect = () => { };
|
|
41
|
-
this.ondisconnect = () => { };
|
|
42
|
-
this.ondisconnect_internal = () => { };
|
|
43
|
-
this.onnotify_internal = () => { };
|
|
29
|
+
Advertising.parse(peripheral).then((ad) => {
|
|
30
|
+
this._id = ad?.id ?? '';
|
|
31
|
+
this._address = ad?.address ?? '';
|
|
32
|
+
this._model = ad?.serviceData.model ?? '';
|
|
33
|
+
this._modelName = ad?.serviceData.modelName ?? '';
|
|
34
|
+
});
|
|
44
35
|
}
|
|
45
36
|
// Getters
|
|
46
37
|
get id() {
|
|
@@ -56,30 +47,14 @@ export class SwitchbotDevice {
|
|
|
56
47
|
return this._modelName;
|
|
57
48
|
}
|
|
58
49
|
get connectionState() {
|
|
59
|
-
|
|
60
|
-
return 'disconnected';
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
return this._peripheral.state;
|
|
64
|
-
}
|
|
50
|
+
return this._connected ? 'connected' : this._peripheral.state;
|
|
65
51
|
}
|
|
66
52
|
get onconnect() {
|
|
67
53
|
return this.onconnect_internal;
|
|
68
54
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Sets the asynchronous connection handler.
|
|
71
|
-
*
|
|
72
|
-
* This setter assigns a function to be used as the asynchronous connection handler. The handler
|
|
73
|
-
* is expected to be a function that returns a Promise, which resolves once the connection process
|
|
74
|
-
* is complete. The resolution of the Promise does not carry any value.
|
|
75
|
-
*
|
|
76
|
-
* @param func A function that returns a Promise, representing the asynchronous operation of connecting.
|
|
77
|
-
* This function is expected to be called without any arguments.
|
|
78
|
-
* @throws Error if the provided argument is not a function, ensuring type safety.
|
|
79
|
-
*/
|
|
80
55
|
set onconnect(func) {
|
|
81
|
-
if (
|
|
82
|
-
throw new
|
|
56
|
+
if (typeof func !== 'function') {
|
|
57
|
+
throw new TypeError('The `onconnect` must be a function that returns a Promise<void>.');
|
|
83
58
|
}
|
|
84
59
|
this.onconnect_internal = async () => {
|
|
85
60
|
await func();
|
|
@@ -88,72 +63,37 @@ export class SwitchbotDevice {
|
|
|
88
63
|
get ondisconnect() {
|
|
89
64
|
return this.ondisconnect_internal;
|
|
90
65
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Sets the asynchronous disconnection handler.
|
|
93
|
-
*
|
|
94
|
-
* This setter configures a function to act as the asynchronous disconnection handler. The handler
|
|
95
|
-
* should be a function that returns a Promise, which resolves when the disconnection process
|
|
96
|
-
* is complete. The resolution of the Promise does not carry any value.
|
|
97
|
-
*
|
|
98
|
-
* @param func A function that returns a Promise, representing the asynchronous operation of disconnecting.
|
|
99
|
-
* This function is expected to be called without any arguments.
|
|
100
|
-
* @throws Error if the provided argument is not a function, to ensure that the handler is correctly typed.
|
|
101
|
-
*/
|
|
102
66
|
set ondisconnect(func) {
|
|
103
|
-
if (
|
|
104
|
-
throw new
|
|
67
|
+
if (typeof func !== 'function') {
|
|
68
|
+
throw new TypeError('The `ondisconnect` must be a function that returns a Promise<void>.');
|
|
105
69
|
}
|
|
106
70
|
this.ondisconnect_internal = async () => {
|
|
107
71
|
await func();
|
|
108
72
|
};
|
|
109
73
|
}
|
|
110
74
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* This method marks the device as being connected explicitly by setting a flag, then proceeds
|
|
114
|
-
* to initiate the actual connection process by calling an internal asynchronous method `connect_internalAsync`.
|
|
115
|
-
* The `connect_internalAsync` method is responsible for handling the low-level connection logic.
|
|
116
|
-
*
|
|
117
|
-
* @returns A Promise that resolves when the connection process initiated by `connect_internalAsync` completes.
|
|
118
|
-
* The resolution of this Promise does not carry any value, indicating that the focus is on
|
|
119
|
-
* the completion of the connection process rather than the result of the connection itself.
|
|
75
|
+
* Connects to the device.
|
|
76
|
+
* @returns A Promise that resolves when the connection is complete.
|
|
120
77
|
*/
|
|
121
78
|
async connect() {
|
|
122
79
|
this._explicitly = true;
|
|
123
|
-
|
|
80
|
+
await this.connect_internal();
|
|
124
81
|
}
|
|
125
82
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* This method is marked as deprecated and is scheduled for removal in a future version. It is recommended
|
|
129
|
-
* to use `disconnectAsync()` instead. The method returns a Promise that resolves when the device is successfully
|
|
130
|
-
* connected or rejects if the connection cannot be established.
|
|
131
|
-
*
|
|
132
|
-
* The connection process involves several steps:
|
|
133
|
-
* 1. Checks the Bluetooth adapter's state. If it's not powered on, the promise is rejected.
|
|
134
|
-
* 2. Checks the current connection state of the device. If already connected, the promise resolves immediately.
|
|
135
|
-
* If in the process of connecting or disconnecting, the promise is rejected advising to wait.
|
|
136
|
-
* 3. Sets up event handlers for 'connect' and 'disconnect' events on the peripheral device to manage connection state.
|
|
137
|
-
* 4. Initiates the connection. If an error occurs during this step, the promise is rejected.
|
|
138
|
-
* 5. Once connected, retrieves the device's characteristics and subscribes to them. If this process fails, the device
|
|
139
|
-
* is disconnected, and the promise is rejected.
|
|
140
|
-
*
|
|
141
|
-
* @returns A Promise<void> that resolves when the device is connected or rejects with an error.
|
|
83
|
+
* Internal method to handle the connection process.
|
|
84
|
+
* @returns A Promise that resolves when the connection is complete.
|
|
142
85
|
*/
|
|
143
86
|
async connect_internal() {
|
|
144
|
-
// Check the bluetooth state
|
|
145
87
|
if (this._noble._state !== 'poweredOn') {
|
|
146
88
|
throw new Error(`The Bluetooth status is ${this._noble._state}, not poweredOn.`);
|
|
147
89
|
}
|
|
148
|
-
// Check the connection state
|
|
149
90
|
const state = this.connectionState;
|
|
150
91
|
if (state === 'connected') {
|
|
151
92
|
return;
|
|
152
93
|
}
|
|
153
|
-
|
|
94
|
+
if (state === 'connecting' || state === 'disconnecting') {
|
|
154
95
|
throw new Error(`Now ${state}. Wait for a few seconds then try again.`);
|
|
155
96
|
}
|
|
156
|
-
// Set event handlers for events fired on the `Peripheral` object
|
|
157
97
|
this._peripheral.once('connect', async () => {
|
|
158
98
|
this._connected = true;
|
|
159
99
|
await this.onconnect();
|
|
@@ -162,49 +102,33 @@ export class SwitchbotDevice {
|
|
|
162
102
|
this._connected = false;
|
|
163
103
|
this._characteristics = null;
|
|
164
104
|
this._peripheral.removeAllListeners();
|
|
165
|
-
|
|
166
|
-
await this.ondisconnect_internal();
|
|
167
|
-
await this.ondisconnect();
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
throw new Error('Error during disconnect:', error);
|
|
171
|
-
}
|
|
105
|
+
await this.ondisconnect_internal();
|
|
172
106
|
});
|
|
173
|
-
// Connect
|
|
174
107
|
await this._peripheral.connectAsync();
|
|
175
|
-
|
|
176
|
-
this._characteristics = chars;
|
|
108
|
+
this._characteristics = await this.getCharacteristics();
|
|
177
109
|
await this.subscribe();
|
|
178
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Retrieves the device characteristics.
|
|
113
|
+
* @returns A Promise that resolves with the device characteristics.
|
|
114
|
+
*/
|
|
179
115
|
async getCharacteristics() {
|
|
180
|
-
|
|
181
|
-
let timer = setTimeout(async () => {
|
|
182
|
-
await this.ondisconnect_internal();
|
|
183
|
-
timer = null;
|
|
116
|
+
const timer = setTimeout(() => {
|
|
184
117
|
throw new Error('Failed to discover services and characteristics: TIMEOUT');
|
|
185
118
|
}, 5000);
|
|
186
119
|
try {
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const chars = {
|
|
193
|
-
write: null,
|
|
194
|
-
notify: null,
|
|
195
|
-
device: null,
|
|
196
|
-
};
|
|
197
|
-
for (const service of service_list) {
|
|
198
|
-
const char_list = await this.discoverCharacteristics(service);
|
|
199
|
-
for (const char of char_list) {
|
|
120
|
+
const services = await this.discoverServices();
|
|
121
|
+
const chars = { write: null, notify: null, device: null };
|
|
122
|
+
for (const service of services) {
|
|
123
|
+
const characteristics = await this.discoverCharacteristics(service);
|
|
124
|
+
for (const char of characteristics) {
|
|
200
125
|
if (char.uuid === CHAR_UUID_WRITE) {
|
|
201
126
|
chars.write = char;
|
|
202
127
|
}
|
|
203
|
-
|
|
128
|
+
if (char.uuid === CHAR_UUID_NOTIFY) {
|
|
204
129
|
chars.notify = char;
|
|
205
130
|
}
|
|
206
|
-
|
|
207
|
-
// Some models of Bot don't seem to support this characteristic UUID
|
|
131
|
+
if (char.uuid === CHAR_UUID_DEVICE) {
|
|
208
132
|
chars.device = char;
|
|
209
133
|
}
|
|
210
134
|
}
|
|
@@ -214,80 +138,48 @@ export class SwitchbotDevice {
|
|
|
214
138
|
}
|
|
215
139
|
return chars;
|
|
216
140
|
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
if (timer) {
|
|
219
|
-
clearTimeout(timer);
|
|
220
|
-
this.ondisconnect_internal = () => { };
|
|
221
|
-
}
|
|
222
|
-
throw error;
|
|
223
|
-
}
|
|
224
141
|
finally {
|
|
225
|
-
|
|
226
|
-
clearTimeout(timer);
|
|
227
|
-
this.ondisconnect_internal = () => { };
|
|
228
|
-
}
|
|
142
|
+
clearTimeout(timer);
|
|
229
143
|
}
|
|
230
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Discovers the device services.
|
|
147
|
+
* @returns A Promise that resolves with the list of services.
|
|
148
|
+
*/
|
|
231
149
|
async discoverServices() {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return services;
|
|
239
|
-
})
|
|
240
|
-
.catch((error) => {
|
|
241
|
-
throw error;
|
|
242
|
-
});
|
|
150
|
+
const services = await this._peripheral.discoverServicesAsync([]);
|
|
151
|
+
const primaryServices = services.filter(s => s.uuid === SERV_UUID_PRIMARY);
|
|
152
|
+
if (primaryServices.length === 0) {
|
|
153
|
+
throw new Error('No service was found.');
|
|
154
|
+
}
|
|
155
|
+
return primaryServices;
|
|
243
156
|
}
|
|
244
157
|
/**
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* If the discovery process fails, the Promise is rejected with an error.
|
|
249
|
-
* @param service The service object for which characteristics will be discovered.
|
|
250
|
-
* @returns A Promise that resolves with the list of characteristics or rejects with an error.
|
|
158
|
+
* Discovers the characteristics of a service.
|
|
159
|
+
* @param service The service to discover characteristics for.
|
|
160
|
+
* @returns A Promise that resolves with the list of characteristics.
|
|
251
161
|
*/
|
|
252
162
|
async discoverCharacteristics(service) {
|
|
253
|
-
return service.discoverCharacteristicsAsync([])
|
|
254
|
-
.then((char_list) => {
|
|
255
|
-
return char_list;
|
|
256
|
-
})
|
|
257
|
-
.catch((error) => {
|
|
258
|
-
throw error;
|
|
259
|
-
});
|
|
163
|
+
return await service.discoverCharacteristicsAsync([]);
|
|
260
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Subscribes to the notify characteristic.
|
|
167
|
+
* @returns A Promise that resolves when the subscription is complete.
|
|
168
|
+
*/
|
|
261
169
|
async subscribe() {
|
|
262
|
-
const char = this._characteristics
|
|
170
|
+
const char = this._characteristics?.notify;
|
|
263
171
|
if (!char) {
|
|
264
172
|
throw new Error('No notify characteristic was found.');
|
|
265
173
|
}
|
|
266
|
-
char.subscribeAsync()
|
|
267
|
-
|
|
268
|
-
char.on('data', (buf) => {
|
|
269
|
-
this.onnotify_internal(buf);
|
|
270
|
-
});
|
|
271
|
-
})
|
|
272
|
-
.catch((error) => {
|
|
273
|
-
throw error;
|
|
274
|
-
});
|
|
174
|
+
await char.subscribeAsync();
|
|
175
|
+
char.on('data', this.onnotify_internal);
|
|
275
176
|
}
|
|
276
177
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* This method checks if the notification characteristic object exists within the cached characteristics
|
|
280
|
-
* (`this.chars`). If the characteristic is found, it proceeds to remove all event listeners attached to it
|
|
281
|
-
* to prevent any further handling of incoming data notifications. Then, it asynchronously unsubscribes from
|
|
282
|
-
* the notification characteristic using `unsubscribeAsync()`, effectively stopping the device from sending
|
|
283
|
-
* notifications to the client.
|
|
284
|
-
*
|
|
285
|
-
* If the notification characteristic is not found, the method simply returns without performing any action.
|
|
286
|
-
*
|
|
287
|
-
* @return A Promise that resolves to `void` upon successful unsubscription or if the characteristic is not found.
|
|
178
|
+
* Unsubscribes from the notify characteristic.
|
|
179
|
+
* @returns A Promise that resolves when the unsubscription is complete.
|
|
288
180
|
*/
|
|
289
181
|
async unsubscribe() {
|
|
290
|
-
const char = this._characteristics
|
|
182
|
+
const char = this._characteristics?.notify;
|
|
291
183
|
if (!char) {
|
|
292
184
|
return;
|
|
293
185
|
}
|
|
@@ -295,188 +187,73 @@ export class SwitchbotDevice {
|
|
|
295
187
|
await char.unsubscribeAsync();
|
|
296
188
|
}
|
|
297
189
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* This method handles the disconnection process by first checking the current
|
|
301
|
-
* connection state of the device. If the device is already disconnected, the
|
|
302
|
-
* method resolves immediately. If the device is in the process of connecting or
|
|
303
|
-
* disconnecting, it throws an error indicating that the operation should be retried
|
|
304
|
-
* after a brief wait. Otherwise, it proceeds to unsubscribe from any subscriptions
|
|
305
|
-
* and then initiates the disconnection process.
|
|
306
|
-
*
|
|
307
|
-
* Note: This method sets a flag to indicate that the disconnection was not initiated
|
|
308
|
-
* by the user explicitly.
|
|
309
|
-
*
|
|
310
|
-
* @returns A Promise that resolves when the disconnection process has completed.
|
|
311
|
-
* The Promise does not pass any value upon resolution.
|
|
190
|
+
* Disconnects from the device.
|
|
191
|
+
* @returns A Promise that resolves when the disconnection is complete.
|
|
312
192
|
*/
|
|
313
193
|
async disconnect() {
|
|
314
194
|
this._explicitly = false;
|
|
315
195
|
const state = this._peripheral.state;
|
|
316
196
|
if (state === 'disconnected') {
|
|
317
|
-
return;
|
|
197
|
+
return;
|
|
318
198
|
}
|
|
319
|
-
|
|
199
|
+
if (state === 'connecting' || state === 'disconnecting') {
|
|
320
200
|
throw new Error(`Now ${state}. Wait for a few seconds then try again.`);
|
|
321
201
|
}
|
|
322
|
-
await this.unsubscribe();
|
|
202
|
+
await this.unsubscribe();
|
|
323
203
|
await this._peripheral.disconnectAsync();
|
|
324
204
|
}
|
|
325
205
|
/**
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* This method checks the `explicitly` flag to determine if the connection was initiated by the user.
|
|
329
|
-
* If not, it proceeds to disconnect from the device by calling `this.disconnectAsync()`. After the disconnection,
|
|
330
|
-
* it sets `explicitly` to true to prevent future disconnections when the connection is user-initiated.
|
|
331
|
-
*
|
|
332
|
-
* This approach ensures that automatic disconnections only occur when the user has not explicitly initiated the connection,
|
|
333
|
-
* avoiding unnecessary disconnections in user-initiated sessions.
|
|
334
|
-
*
|
|
335
|
-
* @returns A Promise that resolves once the device has been successfully disconnected, applicable only when the
|
|
336
|
-
* connection was not user-initiated.
|
|
206
|
+
* Internal method to handle disconnection if not explicitly initiated.
|
|
207
|
+
* @returns A Promise that resolves when the disconnection is complete.
|
|
337
208
|
*/
|
|
338
209
|
async disconnect_internal() {
|
|
339
210
|
if (!this._explicitly) {
|
|
340
211
|
await this.disconnect();
|
|
341
|
-
this._explicitly = true;
|
|
212
|
+
this._explicitly = true;
|
|
342
213
|
}
|
|
343
214
|
}
|
|
344
215
|
/**
|
|
345
|
-
*
|
|
346
|
-
* This method is designed to fetch the name of the device asynchronously and return it as a promise.
|
|
347
|
-
* It is marked as deprecated and will be removed in a future version. Use `getDeviceNameAsync` instead.
|
|
348
|
-
*
|
|
349
|
-
* @deprecated since version 2.4.0. Will be removed in version 3.0.0. Use `getDeviceNameAsync()` instead.
|
|
216
|
+
* Retrieves the device name.
|
|
350
217
|
* @returns A Promise that resolves with the device name.
|
|
351
218
|
*/
|
|
352
219
|
async getDeviceName() {
|
|
353
|
-
let name = '';
|
|
354
|
-
await this.connect_internal()
|
|
355
|
-
.then(async () => {
|
|
356
|
-
if (!this._characteristics || !this._characteristics.device) {
|
|
357
|
-
// Some models of Bot don't seem to support this characteristic UUID
|
|
358
|
-
throw new Error(`The device does not support the characteristic UUID 0x${CHAR_UUID_DEVICE}.`);
|
|
359
|
-
}
|
|
360
|
-
return await this.read(this._characteristics.device);
|
|
361
|
-
})
|
|
362
|
-
.then((buf) => {
|
|
363
|
-
name = buf.toString('utf8');
|
|
364
|
-
return this.disconnect_internal();
|
|
365
|
-
})
|
|
366
|
-
.then(() => {
|
|
367
|
-
return name;
|
|
368
|
-
})
|
|
369
|
-
.catch((error) => {
|
|
370
|
-
throw new Error(error);
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Asynchronously retrieves the device name.
|
|
375
|
-
* This method initiates a connection to the device, checks for the presence of a specific characteristic UUID,
|
|
376
|
-
* reads the characteristic to obtain the device name, and then disconnects from the device.
|
|
377
|
-
* It ensures that the device supports the required characteristic for fetching the name. If the characteristic
|
|
378
|
-
* is not supported, it throws an error. The method encapsulates the entire process of connecting, reading,
|
|
379
|
-
* and disconnecting, making it convenient to get the device name with a single call.
|
|
380
|
-
*
|
|
381
|
-
* @returns A Promise that resolves with the device name as a string.
|
|
382
|
-
*/
|
|
383
|
-
async getDeviceNameAsnyc() {
|
|
384
220
|
await this.connect_internal();
|
|
385
|
-
if (!this._characteristics
|
|
221
|
+
if (!this._characteristics?.device) {
|
|
386
222
|
throw new Error(`The device does not support the characteristic UUID 0x${CHAR_UUID_DEVICE}.`);
|
|
387
223
|
}
|
|
388
224
|
const buf = await this.read(this._characteristics.device);
|
|
389
|
-
const name = buf.toString('utf8');
|
|
390
225
|
await this.disconnect_internal();
|
|
391
|
-
return
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Asynchronously sets the device name to the specified value.
|
|
395
|
-
* Validates the new name to ensure it meets the criteria of being a string with a byte length between 1 and 100 bytes.
|
|
396
|
-
* If the validation fails, the promise is rejected with an error detailing the validation issue.
|
|
397
|
-
* Upon successful validation, the device name is updated, and the promise resolves without passing any value.
|
|
398
|
-
*
|
|
399
|
-
* @param name The new device name as a string. Must be 1 to 100 bytes in length.
|
|
400
|
-
* @returns A Promise that resolves to `void` upon successful update of the device name.
|
|
401
|
-
* @deprecated since version 2.4.0. Will be removed in version 3.0.0. Use `setDeviceNameAsync()` instead.
|
|
402
|
-
*/
|
|
403
|
-
setDeviceName(name) {
|
|
404
|
-
return new Promise((resolve, reject) => {
|
|
405
|
-
// Check the parameters
|
|
406
|
-
const valid = parameterChecker.check({ name }, {
|
|
407
|
-
name: { required: true, type: 'string', minBytes: 1, maxBytes: 100 },
|
|
408
|
-
}, true);
|
|
409
|
-
if (!valid) {
|
|
410
|
-
reject(new Error(parameterChecker.error.message));
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
const buf = Buffer.from(name, 'utf8');
|
|
414
|
-
this.connect_internal()
|
|
415
|
-
.then(() => {
|
|
416
|
-
if (!this._characteristics || !this._characteristics.device) {
|
|
417
|
-
// Some models of Bot don't seem to support this characteristic UUID
|
|
418
|
-
throw new Error(`The device does not support the characteristic UUID 0x${CHAR_UUID_DEVICE}.`);
|
|
419
|
-
}
|
|
420
|
-
return this.write(this._characteristics.device, buf);
|
|
421
|
-
})
|
|
422
|
-
.then(() => {
|
|
423
|
-
return this.disconnect_internal();
|
|
424
|
-
})
|
|
425
|
-
.then(() => {
|
|
426
|
-
return;
|
|
427
|
-
resolve();
|
|
428
|
-
})
|
|
429
|
-
.catch((error) => {
|
|
430
|
-
throw new Error(error);
|
|
431
|
-
});
|
|
432
|
-
});
|
|
226
|
+
return buf.toString('utf8');
|
|
433
227
|
}
|
|
434
228
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* After passing validation, the method converts the name into a UTF-8 encoded buffer.
|
|
439
|
-
* It then initiates a connection to the device. If the device does not support the required characteristic UUID for setting the device name,
|
|
440
|
-
* an error is thrown indicating the lack of support.
|
|
441
|
-
* Upon successfully connecting and verifying support, the method writes the new name to the device using the appropriate characteristic.
|
|
442
|
-
* Finally, it disconnects from the device, completing the name update process.
|
|
443
|
-
*
|
|
444
|
-
* @param name The new device name as a string. Must be 1 to 100 bytes in length.
|
|
445
|
-
* @returns A Promise that resolves to `void` upon successful update of the device name.
|
|
229
|
+
* Sets the device name.
|
|
230
|
+
* @param name The new device name.
|
|
231
|
+
* @returns A Promise that resolves when the name is set.
|
|
446
232
|
*/
|
|
447
|
-
async
|
|
448
|
-
|
|
449
|
-
const valid = parameterChecker.check({ name }, {
|
|
450
|
-
name: { required: true, type: 'string', minBytes: 1, maxBytes: 100 },
|
|
451
|
-
}, true);
|
|
233
|
+
async setDeviceName(name) {
|
|
234
|
+
const valid = parameterChecker.check({ name }, { name: { required: true, type: 'string', minBytes: 1, maxBytes: 100 } }, true);
|
|
452
235
|
if (!valid) {
|
|
453
236
|
throw new Error(parameterChecker.error.message);
|
|
454
237
|
}
|
|
455
238
|
const buf = Buffer.from(name, 'utf8');
|
|
456
239
|
await this.connect_internal();
|
|
457
|
-
if (!this._characteristics
|
|
458
|
-
// Some models of Bot don't seem to support this characteristic UUID
|
|
240
|
+
if (!this._characteristics?.device) {
|
|
459
241
|
throw new Error(`The device does not support the characteristic UUID 0x${CHAR_UUID_DEVICE}.`);
|
|
460
242
|
}
|
|
461
243
|
await this.write(this._characteristics.device, buf);
|
|
462
244
|
await this.disconnect_internal();
|
|
463
245
|
}
|
|
464
246
|
/**
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
* writes the command to the device's write characteristic, waits for a response on the notify characteristic,
|
|
469
|
-
* and finally disconnects from the device. The response from the device is returned as a Buffer.
|
|
470
|
-
*
|
|
471
|
-
* @param req_buf A Buffer containing the command to be sent to the device.
|
|
472
|
-
* @returns A Promise that resolves with a Buffer containing the device's response to the command.
|
|
247
|
+
* Sends a command to the device and awaits a response.
|
|
248
|
+
* @param req_buf The command buffer.
|
|
249
|
+
* @returns A Promise that resolves with the response buffer.
|
|
473
250
|
*/
|
|
474
251
|
async command(req_buf) {
|
|
475
252
|
if (!Buffer.isBuffer(req_buf)) {
|
|
476
253
|
throw new TypeError('The specified data is not acceptable for writing.');
|
|
477
254
|
}
|
|
478
255
|
await this.connect_internal();
|
|
479
|
-
if (!this._characteristics
|
|
256
|
+
if (!this._characteristics?.write) {
|
|
480
257
|
throw new Error('No characteristics available.');
|
|
481
258
|
}
|
|
482
259
|
await this.write(this._characteristics.write, req_buf);
|
|
@@ -486,122 +263,61 @@ export class SwitchbotDevice {
|
|
|
486
263
|
}
|
|
487
264
|
/**
|
|
488
265
|
* Waits for a response from the device after sending a command.
|
|
489
|
-
*
|
|
490
|
-
* A timer is started to enforce a command timeout. If the response is received before the timeout, the timer is cleared.
|
|
491
|
-
* The method sets an internal handler (`onnotify_internal`) to process the incoming response.
|
|
492
|
-
* If the response is not received within the specified timeout period, the promise is rejected with a 'COMMAND_TIMEOUT' error.
|
|
493
|
-
* Once a response is received or a timeout occurs, the internal handler is reset to an empty function to prevent memory leaks.
|
|
494
|
-
* @deprecated since version 2.4.0. Will be removed in version 3.0.0. Use `_waitCommandResponseAsync()` instead.
|
|
495
|
-
*
|
|
496
|
-
* @returns A Promise that resolves with the received Buffer or rejects with an error if a timeout occurs.
|
|
266
|
+
* @returns A Promise that resolves with the response buffer.
|
|
497
267
|
*/
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
268
|
+
async _waitCommandResponseAsync() {
|
|
269
|
+
const timeout = READ_TIMEOUT_MSEC;
|
|
270
|
+
let timer = null;
|
|
271
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
272
|
+
timer = setTimeout(() => reject(new Error('READ_TIMEOUT')), timeout);
|
|
273
|
+
});
|
|
274
|
+
const readPromise = new Promise((resolve) => {
|
|
505
275
|
this.onnotify_internal = (buf) => {
|
|
506
276
|
if (timer) {
|
|
507
277
|
clearTimeout(timer);
|
|
508
|
-
timer = undefined;
|
|
509
278
|
}
|
|
510
|
-
|
|
511
|
-
return buf;
|
|
279
|
+
resolve(buf);
|
|
512
280
|
};
|
|
513
281
|
});
|
|
282
|
+
return await Promise.race([readPromise, timeoutPromise]);
|
|
514
283
|
}
|
|
515
284
|
/**
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
* A
|
|
519
|
-
* The method sets an internal handler (`onnotify_internalAsync`) to process the incoming response.
|
|
520
|
-
* If the response is not received within the specified timeout period, the promise is rejected with a 'COMMAND_TIMEOUT' error.
|
|
521
|
-
* Once a response is received or a timeout occurs, the internal handler is reset to an empty function to prevent memory leaks.
|
|
522
|
-
*
|
|
523
|
-
* @returns A Promise that resolves with the received Buffer or rejects with an error if a timeout occurs.
|
|
524
|
-
*/
|
|
525
|
-
async _waitCommandResponseAsync() {
|
|
526
|
-
const timeout = READ_TIMEOUT_MSEC; // Timeout period in milliseconds
|
|
527
|
-
let timer = null;
|
|
528
|
-
// Setup a timeout to reject the operation if it takes too long
|
|
529
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
530
|
-
timer = setTimeout(() => reject(new Error('READ_TIMEOUT')), timeout);
|
|
531
|
-
});
|
|
532
|
-
// Setup the read operation promise
|
|
533
|
-
const readPromise = await this.onnotify_internal();
|
|
534
|
-
// Wait for either the read operation to complete or the timeout to occur
|
|
535
|
-
const result = await Promise.race([readPromise, timeoutPromise]);
|
|
536
|
-
// Clear the timeout if the read operation completes successfully
|
|
537
|
-
if (timer) {
|
|
538
|
-
clearTimeout(timer);
|
|
539
|
-
}
|
|
540
|
-
return result;
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* Asynchronously reads data from the specified characteristic of the device with a timeout.
|
|
544
|
-
* This method attempts to read data from the device's characteristic and sets a timeout to handle cases where the read operation takes too long.
|
|
545
|
-
* If the read operation does not complete within the specified timeout period, an error is thrown.
|
|
546
|
-
* Once the read operation completes successfully, the timeout is cleared to prevent it from triggering.
|
|
547
|
-
*
|
|
548
|
-
* @param char The characteristic of the device from which data will be read.
|
|
549
|
-
* @returns A Promise that resolves with the data read from the characteristic or rejects with an error if a timeout occurs.
|
|
550
|
-
*
|
|
551
|
-
* @throws Error if the read operation fails or if a timeout occurs.
|
|
285
|
+
* Reads data from a characteristic with a timeout.
|
|
286
|
+
* @param char The characteristic to read from.
|
|
287
|
+
* @returns A Promise that resolves with the data buffer.
|
|
552
288
|
*/
|
|
553
289
|
async read(char) {
|
|
554
|
-
|
|
290
|
+
const timer = setTimeout(() => {
|
|
555
291
|
throw new Error('READ_TIMEOUT');
|
|
556
292
|
}, READ_TIMEOUT_MSEC);
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (timer) {
|
|
561
|
-
clearTimeout(timer);
|
|
562
|
-
timer = undefined;
|
|
563
|
-
}
|
|
564
|
-
else {
|
|
565
|
-
throw new Error('READ_TIMEOUT');
|
|
566
|
-
}
|
|
293
|
+
try {
|
|
294
|
+
const result = await char.readAsync();
|
|
295
|
+
clearTimeout(timer);
|
|
567
296
|
return result;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const result = await Promise.race([readPromise, timer]);
|
|
571
|
-
// Clear the timeout if the read operation completes successfully
|
|
572
|
-
if (timer) {
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
573
299
|
clearTimeout(timer);
|
|
300
|
+
throw error;
|
|
574
301
|
}
|
|
575
|
-
return result;
|
|
576
302
|
}
|
|
577
303
|
/**
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
* @param char The characteristic of the device to which the data will be written.
|
|
584
|
-
* @param buf A Buffer containing the data to be written to the device.
|
|
585
|
-
* @returns A Promise that resolves when the write operation completes successfully or rejects with an error if a timeout occurs.
|
|
304
|
+
* Writes data to a characteristic with a timeout.
|
|
305
|
+
* @param char The characteristic to write to.
|
|
306
|
+
* @param buf The data buffer.
|
|
307
|
+
* @returns A Promise that resolves when the write is complete.
|
|
586
308
|
*/
|
|
587
309
|
async write(char, buf) {
|
|
588
|
-
|
|
310
|
+
const timer = setTimeout(() => {
|
|
589
311
|
throw new Error('WRITE_TIMEOUT');
|
|
590
312
|
}, WRITE_TIMEOUT_MSEC);
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
throw new Error('READ_TIMEOUT');
|
|
600
|
-
}
|
|
601
|
-
})
|
|
602
|
-
.catch((error) => {
|
|
603
|
-
throw new Error(`WRITE_TIMEOUT, ${error}`);
|
|
604
|
-
});
|
|
313
|
+
try {
|
|
314
|
+
await char.writeAsync(buf, false);
|
|
315
|
+
clearTimeout(timer);
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
clearTimeout(timer);
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
605
321
|
}
|
|
606
322
|
}
|
|
607
323
|
//# sourceMappingURL=device.js.map
|