node-switchbot 1.0.7

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/README.md ADDED
@@ -0,0 +1,950 @@
1
+ <span align="center">
2
+
3
+ # Node-SwitchBot
4
+
5
+ <a href="https://www.npmjs.com/package/node-switchbot"><img title="npm version" src="https://badgen.net/npm/v/node-switchbot" ></a>
6
+ <a href="https://www.npmjs.com/package/node-switchbot"><img title="npm downloads" src="https://badgen.net/npm/dt/node-switchbot" ></a>
7
+
8
+ </p>
9
+
10
+ </span>
11
+
12
+
13
+
14
+
15
+ The node-switchbot is a Node.js module which allows you to move your [Switchbot (Bot)](https://www.switch-bot.com/bot)'s arm and [Switchbot Curtain(Curtain)](https://www.switch-bot.com/products/switchbot-curtain), also monitor the temperature/humidity from [SwitchBot Thermometer & Hygrometer (Meter)](https://www.switch-bot.com/meter).
16
+
17
+ This module is unofficial. It was developed by reference to [the official python code](https://github.com/OpenWonderLabs/python-host). But some functionalities of this module were developed through trial and error. So some information obtained from this module might be wrong.
18
+
19
+ ---------------------------------------
20
+ ## Table of Contents
21
+
22
+ - [node-switchbot](#node-switchbot)
23
+ - [Table of Contents](#table-of-contents)
24
+ - [Supported OS](#supported-os)
25
+ - [Dependencies](#dependencies)
26
+ - [Installation](#installation)
27
+ - [Quick Start](#quick-start)
28
+ - [Monitoring Advertising packets](#monitoring-advertising-packets)
29
+ - [Moving the arm of the Bot](#moving-the-arm-of-the-bot)
30
+ - [`Switchbot` object](#switchbot-object)
31
+ - [`discover()` method](#discover-method)
32
+ - [`ondiscover` event hander](#ondiscover-event-hander)
33
+ - [`startScan()` method](#startscan-method)
34
+ - [`stopScan()` method](#stopscan-method)
35
+ - [`onadvertisement` event handler](#onadvertisement-event-handler)
36
+ - [`wait()` method](#wait-method)
37
+ - [`SwitchbotDevice` object](#switchbotdevice-object)
38
+ - [Properties](#properties)
39
+ - [`getDeviceName()` method](#getdevicename-method)
40
+ - [`setDeviceName()` method](#setdevicename-method)
41
+ - [`connect()` method](#connect-method)
42
+ - [`disconnect()` method](#disconnect-method)
43
+ - [`onconnect` event handler](#onconnect-event-handler)
44
+ - [`ondisconnect` event handler](#ondisconnect-event-handler)
45
+ - [`SwitchbotDeviceWoHand` object](#switchbotdevicewohand-object)
46
+ - [`press()` method](#press-method)
47
+ - [`turnOn()` method](#turnon-method)
48
+ - [`turnOff()` method](#turnoff-method)
49
+ - [`down()` method](#down-method)
50
+ - [`up()` method](#up-method)
51
+ - [`SwitchbotDeviceWoCurtain` object](#switchbotdevicewocurtain-object)
52
+ - [`open()` method](#open-method)
53
+ - [`close()` method](#close-method)
54
+ - [`pause()` method](#pause-method)
55
+ - [`runToPos()` method](#runtopos-method)
56
+ - [Advertisement data](#advertisement-data)
57
+ - [Bot (WoHand)](#bot-wohand)
58
+ - [Meter (WoSensorTH)](#meter-wosensorth)
59
+ - [Curtain (WoCurtain)](#curtain-wocurtain)
60
+ - [Release Note](#release-note)
61
+ - [References](#references)
62
+ - [License](#license)
63
+
64
+ ## Supported OS
65
+
66
+ The node-switchbot supports only Linux-based OSes, such as Raspbian, Ubuntu, and so on. This module does not support Windows and macOS for now. (If [@homebridge/noble](https://github.com/homebridge/noble) is installed properly, this module might work well on such OSes.)
67
+
68
+ ## Dependencies
69
+
70
+ * [Node.js](https://nodejs.org/en/) 10 +
71
+ * [@homebridge/noble](https://github.com/homebridge/noble)
72
+
73
+
74
+ See the document of the [@homebridge/noble](https://github.com/homebridge/noble) for details on installing the [@homebridge/noble](https://github.com/homebridge/noble).
75
+
76
+ Note that the noble must be run as root on most of Linux environments. See the document of the [@homebridge/noble](https://github.com/homebridge/noble) for details.
77
+
78
+ ## Installation
79
+
80
+ Before installing the [@homebridge/noble](https://github.com/homebridge/noble), some linux libraries related Bluetooth as follows if the OS is Ubuntu/Debian/Raspbian.
81
+
82
+ ```
83
+ $ sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
84
+ ```
85
+
86
+ If you use other OS, follow the instructions described in the document of the [@homebridge/noble](https://github.com/homebridge/noble).
87
+
88
+ After installing the libraries above, install the [@homebridge/noble](https://github.com/homebridge/noble) and the node-switchbot (this module) as follows:
89
+
90
+ ```
91
+ $ cd ~
92
+ $ npm install @homebridge/noble
93
+ $ npm install node-switchbot
94
+ ```
95
+
96
+ ---------------------------------------
97
+ ## Quick Start
98
+
99
+ ### Monitoring Advertising packets
100
+
101
+ Monitoring the advertising packets, you can find your devices and know the latest state of each device. The packet contains the settings of the device, the arm position of the Bot, the temperature and humidity of the Meter, and so on.
102
+
103
+ ```JavaScript
104
+ // Load the node-switchbot and get a `Switchbot` constructor object
105
+ const Switchbot = require('node-switchbot');
106
+ // Create an `Switchbot` object
107
+ const switchbot = new Switchbot();
108
+
109
+ (async () => {
110
+ // Start to monitor advertisement packets
111
+ await switchbot.startScan();
112
+ // Set an event hander
113
+ switchbot.onadvertisement = (ad) => {
114
+ console.log(JSON.stringify(ad, null, ' '));
115
+ };
116
+ // Wait 10 seconds
117
+ await switchbot.wait(10000);
118
+ // Stop to monitor
119
+ switchbot.stopScan();
120
+ process.exit();
121
+ })();
122
+ ```
123
+
124
+ The [`startScan()`](#startscan-method) methods starts to monitor advertisement packets. In order to receive the packets, you have to assign a callback function to the [`onadvertisement`](#Switchbot-onadvertisement-event-handler).
125
+
126
+ The [`wait()`](#Switchbot-wait-method) method is just an utility method, which wait for the specified milliseconds.
127
+
128
+ The [`startScan()`](#startscan-method) and [`wait()`](#Switchbot-wait-method) methods are asynchronous, they return a `Promise` object. You can write code in promise style as well. What the code below does is as same as what the code above does:
129
+
130
+ ```javascript
131
+ // Load the node-switchbot and get a `Switchbot` constructor object
132
+ const Switchbot = require('node-switchbot');
133
+ // Create an `Switchbot` object
134
+ let switchbot = new Switchbot();
135
+
136
+ // Start to monitor advertisement packets
137
+ switchbot.startScan().then(() => {
138
+ // Set an event hander
139
+ switchbot.onadvertisement = (ad) => {
140
+ console.log(JSON.stringify(ad, null, ' '));
141
+ };
142
+ // Wait 10 seconds
143
+ return switchbot.wait(10000);
144
+ }).then(() => {
145
+ // Stop to monitor
146
+ switchbot.stopScan();
147
+ process.exit();
148
+ });
149
+ ```
150
+
151
+ The sample codes above will output the result as follows:
152
+
153
+ ```json
154
+ {
155
+ "id": "c12e453e2008",
156
+ "address": "c1:2e:45:3e:20:08",
157
+ "rssi": -61,
158
+ "serviceData": {
159
+ "model": "H",
160
+ "modelName": "WoHand",
161
+ "mode": true,
162
+ "state": false,
163
+ "battery": 100
164
+ }
165
+ }
166
+ {
167
+ "id": "cb4eb903c96d",
168
+ "address": "cb:4e:b9:03:c9:6d",
169
+ "rssi": -70,
170
+ "serviceData": {
171
+ "model": "T",
172
+ "modelName": "WoSensorTH",
173
+ "temperature": {
174
+ "c": 25.2,
175
+ "f": 77.4
176
+ },
177
+ "fahrenheit": false,
178
+ "humidity": 43,
179
+ "battery": 100
180
+ }
181
+ }
182
+ {
183
+ "id": "ec58c5d00111",
184
+ "address": "ec:58:c5:d0:01:11",
185
+ "rssi": -39,
186
+ "serviceData": {
187
+ "model": "c",
188
+ "modelName": "WoCurtain",
189
+ "calibration": true,
190
+ "battery": 91,
191
+ "position": 1,
192
+ "lightLevel": 1
193
+ }
194
+ }
195
+ ```
196
+
197
+ See the section "[Advertisement data](#Advertisement-data)" for the details of the advertising packets.
198
+
199
+ ### Moving the arm of the Bot
200
+
201
+ This sample discovers a Bot (WoHand), then put the Bot's arm down, finally put it up in 5 seconds.
202
+
203
+ ```javascript
204
+ // Load the node-switchbot and get a `Switchbot` constructor object
205
+ const Switchbot = require('node-switchbot');
206
+ // Create an `Switchbot` object
207
+ const switchbot = new Switchbot();
208
+
209
+ (async () => {
210
+ // Find a Bot (WoHand)
211
+ const bot_list = await switchbot.discover({ model: 'H', quick: true });
212
+ if (bot_list.length === 0) {
213
+ throw new Error('No device was found.');
214
+ }
215
+ // The `SwitchbotDeviceWoHand` object representing the found Bot.
216
+ let device = bot_list[0];
217
+ // Put the Bot's arm down (stretch the arm)
218
+ await device.down();
219
+ // Wait for 5 seconds
220
+ await switchbot.wait(5000);
221
+ // Put the Bot's arm up (retract the arm)
222
+ await device.up();
223
+ process.exit();
224
+ })();
225
+ ```
226
+
227
+ In order to manipulate the arm of your Bot, you have to discover your Bot using the [`discover()`](#Switchbot-discover-method) method. The object `{ model: 'H' }` passed to the method means that only Bots will be discovered. That is, Meters will be ignored.
228
+
229
+ In this code, you can get a [`SwitchbotDeviceWoHand`](#SwitchbotDeviceWoHand-object) object representing the found Bot. Using the [`down()`](#SwitchbotDeviceWoHand-down-method) and [`up()`](#SwitchbotDeviceWoHand-up-method) methods of the object, you can move the arm. In addition to these methods, you can use the [`press()`](#SwitchbotDeviceWoHand-press-method), [`turnOn()`](#SwitchbotDeviceWoHand-turnOn-method), and [`turnOff()`](#SwitchbotDeviceWoHand-turnOff-method) methods as well.
230
+
231
+ ---------------------------------------
232
+ ## `Switchbot` object
233
+
234
+ In order to use the node-switchbot, you have to load the node-switchbot module as follows:
235
+
236
+ ```JavaScript
237
+ const Switchbot = require('node-switchbot');
238
+ ```
239
+
240
+ You can get an `Switchbot` constructor from the code above. Then you have to create an `Switchbot` object from the `Switchbot` constructor as follows:
241
+
242
+ ```javascript
243
+ const switchbot = new Switchbot();
244
+ ```
245
+
246
+ The `Switchbot` constructor takes an argument optionally. It must be a hash object containing the properties as follows:
247
+
248
+ Property | Type | Required | Description
249
+ :--------|:-------|:---------|:-----------
250
+ `noble` | Noble | option | a Noble object of the [`@homebridge/noble`](https://github.com/homebridge/noble) module
251
+
252
+ The node-switchbot module uses the [`@homebridge/noble`](https://github.com/homebridge/noble) module in order to interact with BLE devices. If you want to interact other BLE devices using the `@homebridge/noble` module, you can create an `Noble` object by yourself, then pass it to this module. If you don't specify a `Noble` object to the `noble` property, this module automatically create a `Noble` object internally.
253
+
254
+ The sample code below shows how to pass a `Nobel` object to the `Switchbot` constructor.
255
+
256
+ ```JavaScript
257
+ // Create a Noble object
258
+ const noble = require('@homebridge/noble');
259
+
260
+ // Create a Switchbot object
261
+ const Switchbot = require('node-switchbot');
262
+ const switchbot = new Switchbot({'noble': noble});
263
+ ```
264
+
265
+ In the code snippet above, the variable `switchbot` is an `Switchbot` object. The `Switchbot` object has a lot of methods as described in sections below.
266
+
267
+ ### `discover()` method
268
+
269
+ The `discover` method finds devices. This method returns a `Promise` object. This method takes an argument which is a hash object containing parameters as follows:
270
+
271
+ Property | Type | Required | Description
272
+ :------------|:--------|:---------|:------------
273
+ `duration` | Integer | Optional | Duration for discovery process (msec). The default value is 5000 (msec).
274
+ `model` | String | Optional | `"H"`, `"T"` or `"c"`. If `"H"` is specified, this method will discover only Bots. If `"T"` is specified, this method will discover only Meters. If `"c"` is specified, this method will discover only Curtains.
275
+ `id` | String | Optional | If this value is set, this method will discover only a device whose ID is as same as this value. The ID is identical to the MAC address. This parameter is case-insensitive, and colons are ignored.
276
+ `quick` | Boolean | Optional | If this value is `true`, this method finishes the discovery process when the first device is found, then calls the `resolve()` function without waiting the specified `duration`. The default value is `false`.
277
+
278
+ In the code snippet below, no parameter is passed to the method:
279
+
280
+ ```JavaScript
281
+ switchbot.discover().then((device_list) => {
282
+ // Do something...
283
+ }).catch((error) => {
284
+ console.error(error);
285
+ });
286
+ ```
287
+
288
+ If no parameter is passed to the method as the code above, an `Array` object will be passed to the `resolve()` function in 5 seconds. The `Array` object contains [`SwitchbotDevice`](#SwitchbotDevice-object) objects representing the found devices. See the section "[`SwitchbotDevice`](#SwitchbotDevice-object) objects" for more details.
289
+
290
+ If you want a quick response, you can set the `quick` property to `true`.
291
+
292
+ ```JavaScript
293
+ switchbot.discover({
294
+ duration: 5000,
295
+ quick: true
296
+ });
297
+ }).then((device_list) => {
298
+ // Do something...
299
+ }).catch((error) => {
300
+ console.error(error);
301
+ });
302
+ ```
303
+
304
+ As the `quick` property is set to `true`, the `resolve()` function will be called immediately after a device is found regardless the value of the `duration` property.
305
+
306
+ ### `ondiscover` event hander
307
+
308
+ The `ondiscover` property on the [`Switchbot`](#Switchbot-object) object is an event handler called whenever a device is newly found in the discovery process. A [`SwitchbotDevice`](#SwitchbotDevice-object) object is passed to the callback function set to the `ondiscover` property.
309
+
310
+ ```JavaScript
311
+ switchbot.ondiscover = (device) => {
312
+ console.log(device.id + ' (' + device.modelName + ')');
313
+ };
314
+
315
+ switchbot.discover().then(() => {
316
+ console.log('The discovery process was finished.');
317
+ }).catch((error) => {
318
+ console.error(error);
319
+ });
320
+ ```
321
+
322
+ The code snippet above will output the result as follows:
323
+
324
+ ```
325
+ cb4eb903c96d (WoSensorTH)
326
+ c12e453e2008 (WoHand)
327
+ The discovery process was finished.
328
+ ```
329
+
330
+ ### `startScan()` method
331
+
332
+ The `startScan()` method starts to scan advertising packets coming from devices. This method takes an argument which is a hash object containing the parameters as follows:
333
+
334
+ Property | Type | Required | Description
335
+ :------------|:-------|:---------|:------------
336
+ `model` | String | Optional | `"H"`, `"T"` or `"c"`. If `"H"` is specified, this method will discover only Bots. If `"T"` is specified, this method will discover only Meters. If `"c"` is specified, this method will discover only Curtains.
337
+ `id` | String | Optional | If this value is set, this method will discover only a device whose ID is as same as this value. The ID is identical to the MAC address. This value is case-insensitive, and colons are ignored.
338
+
339
+ Whenever a packet is received, the callback function set to the [`onadvertisement`](#Switchbot-onadvertisement-event-handler) property of the [`Switchbot`](#Switchbot-object) object will be called. When a packet is received, a hash object representing the packet will be passed to the callback function.
340
+
341
+ ```JavaScript
342
+ // Set a callback function called when a packet is received
343
+ switchbot.onadvertisement = (ad) => {
344
+ console.log(ad);
345
+ };
346
+
347
+ // Start to scan advertising packets
348
+ switchbot.startScan({
349
+ id: 'cb:4e:b9:03:c9:6d',
350
+ }).then(() => {
351
+ // Wait for 30 seconds
352
+ return switchbot.wait(30000);
353
+ }).then(() => {
354
+ // Stop to scan
355
+ switchbot.stopScan();
356
+ process.exit();
357
+ }).catch((error) => {
358
+ console.error(error);
359
+ });
360
+ ```
361
+
362
+ The code snippet above will output the result as follows:
363
+
364
+ ```
365
+ {
366
+ id: 'cb4eb903c96d',
367
+ address: 'cb:4e:b9:03:c9:6d',
368
+ rssi: -65,
369
+ serviceData: {
370
+ model: 'T',
371
+ modelName: 'WoSensorTH',
372
+ temperature: { c: 25.8, f: 78.4 },
373
+ fahrenheit: false,
374
+ humidity: 43,
375
+ battery: 100
376
+ }
377
+ }
378
+ ...
379
+ ```
380
+
381
+ The `serviceData` property depends on the model of the device. See the section "[Advertisement data](#Advertisement-data)" for the details of the data format.
382
+
383
+ ### `stopScan()` method
384
+
385
+ The `stopScan()` method stops to scan advertising packets coming from devices. This mothod returns nothing. Note that this method is *not* asynchronous but synchronous unlike the other methods. See the section "[`startScan()` method](#startscan-method)" for details.
386
+
387
+ ### `onadvertisement` event handler
388
+
389
+ If a callback function is set to the `onadvertisement` property, the callback function will be called whenever an advertising packet is received from a device during the scan is active (from the moment when the [`startScan()`](#startscan-method) method is called, to the moment when the [`stopScan()`](#Switchbot-stopScan-method) method is called).
390
+
391
+ See the section "[`startScan()` method](#startscan-method)" for details.
392
+
393
+ ### `wait()` method
394
+
395
+ The `wait()` method waits for the specified milliseconds. This method takes an integer representing the duration (millisecond). This method returns a `Promise` object.
396
+
397
+ This method has nothing to do with Switchbot devices. It's just an utility method. See the section "[Quick Start](#Quick-Start)" for details of the usage of this method.
398
+
399
+ ---------------------------------------
400
+ ## `SwitchbotDevice` object
401
+
402
+ The `SwitchbotDevice` object represents a Switchbot device (Bot or Meter), which is created through the discovery process triggered by the [`Switchbot.discover()`](#Switchbot-discover-method) method.
403
+
404
+ Actually, the `SwitchbotDevice` object is an super class of the [`SwitchbotDeviceWoHand`](#SwitchbotDeviceWoHand-object) and `SwitchbotDeviceWoSensorTH` objects. The [`SwitchbotDeviceWoHand`](#SwitchbotDeviceWoHand-object) object represents a Bot, the `SwitchbotDeviceWoSensorTH` object represents a Meter.
405
+
406
+ You can use the properties and methods described in this section on both Bot and Meter. See the section "[`SwitchbotDeviceWoHand` object](#SwitchbotDeviceWoHand-object)" for the details of the functionalities available only on Bot. For now, `SwitchbotDeviceWoSensorTH` object has no additional functionality.
407
+
408
+ ### Properties
409
+
410
+ The `SwitchbotDevice` object supports the properties as follows:
411
+
412
+ Property | Type | Description
413
+ :----------------|:---------|:-----------
414
+ `id` | String | ID of the device. (e.g., `"cb4eb903c96d"`)
415
+ `address` | String | MAC address of the device. Basically it is as same as the value of the `id` except that this value includes `:` in the string. (e.g., `"cb:4e:b9:03:c9:6d"`)
416
+ `model` | String | This value is `"H"` which means "Bot (WoHand)", `"T"` which means "Meter (WoSensorTH)" or `"c"` which means "Curtain (WoCurtain)".
417
+ `modelName` | String | This value is `"WoHand"` or `"WoSensorTH"`.
418
+ `connectionState` | String | This value indicates the BLE connection state. `"connecting"`, `"connected"`, `"disconnecting"`, or `"disconnected"`.
419
+ `onconnect` | Function | See the section "[`onconnect` event handler](#SwitchbotDevice-onconnect-event-handler)" for details.
420
+ `ondisconnect` | Function | See the section "[`ondisconnect` event handler](#SwitchbotDevice-ondisconnect-event-handler)" for details.
421
+
422
+ ### `getDeviceName()` method
423
+
424
+ The `getDeviceName()` method fetches the device name saved in the device. This method returns a `Promise` object.
425
+
426
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
427
+
428
+ If the device name is fetched successfully, the device name will be passed to the `resolve()`.
429
+
430
+ ```javascript
431
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
432
+ return device_list[0].getDeviceName();
433
+ }).then((name) => {
434
+ console.log(name);
435
+ process.exit();
436
+ }).catch((error) => {
437
+ console.error(error);
438
+ process.exit();
439
+ });
440
+ ```
441
+
442
+ The code above will output the result as follows:
443
+
444
+ ```javascript
445
+ WoHand
446
+ ```
447
+
448
+ ### `setDeviceName()` method
449
+
450
+ The `setDeviceName()` method update the device name saved in the device with the name specified as the first argument. This method returns a `Promise` object. Nothing will be passed to the `resolve()` function.
451
+
452
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
453
+
454
+ The character set of the device name saved in the device is UTF-8. The byte length of the name must be less than or equal to 20 bytes. If the name consists of only ASCII characters, up to 20 characters would be allowed. But if the name consists of multi-byte characters, the upper limit of characters would be fewer than half. For example, Japanese characters could be saved at most 6 characters because most of Japanese characters consume 3 byte per each character.
455
+
456
+ ```javascript
457
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
458
+ return device_list[0].setDeviceName('Bot in kitchen');
459
+ }).then(() => {
460
+ console.log('Done.');
461
+ process.exit();
462
+ }).catch((error) => {
463
+ console.error(error);
464
+ process.exit();
465
+ });
466
+ ```
467
+
468
+ ### `connect()` method
469
+
470
+ The `connect()` method establishes a connection with the device (i.e., pairing). This method returns a `Promise` object. If the device has already been connected, this method does nothing and calls the `resolve()` function immediately.
471
+
472
+ Most of the methods implemented in the `SwitchbotDevice` object automatically connect and disconnect the device. But this mechanism would be absolutely inefficient if you want to manipulate the device repeatedly in the short time.
473
+
474
+ The connection established using the `connect()` method is not disconnected automatically unless the [`disconnect()`](#SwitchbotDevice-disconnect-method) method is explicitly called.
475
+
476
+ The code snippet below establishes a connection with the Bot using the `connect()` method, then puts the Bot's arm down, then waits for 5 seconds, then puts the arm down, finally disconnects the device using the [`disconnect()`](#SwitchbotDevice-disconnect-method) method:
477
+
478
+ ```javascript
479
+ let device = null;
480
+
481
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
482
+ device = device_list[0];
483
+ if (!device) {
484
+ console.log('No device was found.');
485
+ process.exit();
486
+ }
487
+ console.log(device.modelName + ' (' + device.address + ') was found.');
488
+ console.log('Connecting...');
489
+ return device.connect();
490
+ }).then(() => {
491
+ console.log('Putting the arm down...');
492
+ return device.down();
493
+ }).then(() => {
494
+ console.log('Waiting for 5 seconds...');
495
+ return switchbot.wait(5000);
496
+ }).then(() => {
497
+ console.log('Putting the arm up...');
498
+ return device.up();
499
+ }).then(() => {
500
+ console.log('Disconnecting...');
501
+ return device.disconnect();
502
+ }).then(() => {
503
+ console.log('Done.');
504
+ process.exit();
505
+ }).catch((error) => {
506
+ console.error(error);
507
+ process.exit();
508
+ });
509
+ ```
510
+
511
+ The result will be as follows:
512
+
513
+ ```
514
+ WoHand (c1:2e:45:3e:20:08) was found.
515
+ Connecting...
516
+ Putting the arm down...
517
+ Waiting for 5 seconds...
518
+ Putting the arm up...
519
+ Disconnecting...
520
+ Done.
521
+ ```
522
+
523
+ ### `disconnect()` method
524
+
525
+ The `disconnect()` method disconnects the device. This method returns a `Promise` object. If the device has already been disconnected, this method does nothing and calls the `resolve()` function immediately.
526
+
527
+ See the [previous section](#SwitchbotDevice-connect-method) for more details.
528
+
529
+ ### `onconnect` event handler
530
+
531
+ The `onconnect` event hander will be called when the connection with the device is established. Nothing will be passed to the hander.
532
+
533
+ The code below calls the [`press()`](#SwitchbotDeviceWoHand-press-method) method, while callback functions are attached to the `onconnect` and `ondisconnect`.
534
+
535
+ ```javascript
536
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
537
+ let device = device_list[0];
538
+ if (!device) {
539
+ console.log('No device was found.');
540
+ process.exit();
541
+ }
542
+ console.log(device.modelName + ' (' + device.address + ') was found.');
543
+
544
+ // Set event handers
545
+ device.onconnect = () => {
546
+ console.log('Connected.');
547
+ };
548
+ device.ondisconnect = () => {
549
+ console.log('Disconnected.');
550
+ };
551
+
552
+ console.log('Pressing the switch...');
553
+ return device.press();
554
+ }).then(() => {
555
+ console.log('Done.');
556
+ process.exit();
557
+ }).catch((error) => {
558
+ console.error(error);
559
+ process.exit();
560
+ });
561
+ ```
562
+
563
+ The code above will output the result as follows:
564
+
565
+ ```
566
+ WoHand (c1:2e:45:3e:20:08) was found.
567
+ Pressing the switch...
568
+ Connected.
569
+ Disconnected.
570
+ Done.
571
+ ```
572
+
573
+ Seeing the result, you would find the [`press()`](#SwitchbotDeviceWoHand-press-method) method automatically connects and disconnects the device.
574
+
575
+ ### `ondisconnect` event handler
576
+
577
+ The `ondisconnect` event hander will be called when the connection with the device is closed. Nothing will be passed to the hander. See the previous section "[`onconnect` event handler](#SwitchbotDevice-onconnect-event-handler)" for more details.
578
+
579
+ ---------------------------------------
580
+ ## `SwitchbotDeviceWoHand` object
581
+
582
+ The `SwitchbotDeviceWoHand` object represents an Bot, which is created through the discovery process triggered by the [`Switchbot.discover()`](#Switchbot-discover-method) method.
583
+
584
+ Actually, the `SwitchbotDeviceWoHand` is an object inherited from the [`SwitchbotDevice`](#SwitchbotDevice-object). You can use not only the method described in this section but also the properties and methods implemented in the [`SwitchbotDevice`](#SwitchbotDevice-object) object.
585
+
586
+ ### `press()` method
587
+
588
+ The `press()` method sends a press command to the Bot. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
589
+
590
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
591
+
592
+ ```javascript
593
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
594
+ return device_list[0].press();
595
+ }).then(() => {
596
+ console.log('Done.');
597
+ }).catch((error) => {
598
+ console.error(error);
599
+ });
600
+ ```
601
+
602
+ When the Bot receives this command, the Bot's arm will be put down (stretched), then put up (retracted) in a few seconds.
603
+
604
+ ### `turnOn()` method
605
+
606
+ The `turnOn()` method sends a turn-on command to the Bot. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
607
+
608
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
609
+
610
+ When the Bot receives this command, the Bot's arm will be put down (stretched) or put up (retracted) depending on the mode setting.
611
+
612
+ Mode | Inverse the on/off direction | Physical position of the arm
613
+ :-------------------|:-----------------------------|:----------------------------
614
+ Press mode | N/A | Down (stretched), then Up (retracted)
615
+ Switch mode | Disabled | Down (stretched)
616
+ &nbsp; | Enabled | Up (retracted)
617
+
618
+ ```javascript
619
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
620
+ return device_list[0].turnOn();
621
+ }).then(() => {
622
+ console.log('Done.');
623
+ }).catch((error) => {
624
+ console.error(error);
625
+ });
626
+ ```
627
+
628
+ ### `turnOff()` method
629
+
630
+ The `turnOff()` method sends a turn-off command to the Bot. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
631
+
632
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
633
+
634
+ When the Bot receives this command, the Bot's arm will be put down (stretched) or put up (retracted) depending on the mode setting.
635
+
636
+ Mode | Inverse the on/off direction | Physical position of the arm
637
+ :-------------------|:-----------------------------|:----------------------------
638
+ Press mode | N/A | Down (stretched), then Up (retracted)
639
+ Switch mode | Disabled | Up (retracted)
640
+ &nbsp; | Enabled | Down (stretched)
641
+
642
+ ```javascript
643
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
644
+ return device_list[0].turnOff();
645
+ }).then(() => {
646
+ console.log('Done.');
647
+ }).catch((error) => {
648
+ console.error(error);
649
+ });
650
+ ```
651
+
652
+ ### `down()` method
653
+
654
+ The `down()` method sends a down command to the Bot. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
655
+
656
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
657
+
658
+ When the Bot receives this command, the Bot's arm will be put down (stretched) regardless of the mode setting.
659
+
660
+ ```javascript
661
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
662
+ return device_list[0].down();
663
+ }).then(() => {
664
+ console.log('Done.');
665
+ }).catch((error) => {
666
+ console.error(error);
667
+ });
668
+ ```
669
+
670
+ ### `up()` method
671
+
672
+ The `up()` method sends an up command to the Bot. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
673
+
674
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
675
+
676
+ When the Bot receives this command, the Bot's arm will be put up (retracted) regardless of the mode setting.
677
+
678
+ ```javascript
679
+ switchbot.discover({ model: 'H', quick: true }).then((device_list) => {
680
+ return device_list[0].up();
681
+ }).then(() => {
682
+ console.log('Done.');
683
+ }).catch((error) => {
684
+ console.error(error);
685
+ });
686
+ ```
687
+
688
+ ---------------------------------------
689
+ ## `SwitchbotDeviceWoCurtain` object
690
+
691
+ The `SwitchbotDeviceWoCurtain` object represents an Curtain, which is created through the discovery process triggered by the [`Switchbot.discover()`](#Switchbot-discover-method) method.
692
+
693
+ Actually, the `SwitchbotDeviceWoCurtain` is an object inherited from the [`SwitchbotDevice`](#SwitchbotDevice-object). You can use not only the method described in this section but also the properties and methods implemented in the [`SwitchbotDevice`](#SwitchbotDevice-object) object.
694
+
695
+ ### `open()` method
696
+
697
+ ```javascript
698
+ switchbot.discover({ model: 'c', quick: true }).then((device_list) => {
699
+ return device_list[0].open();
700
+ }).then(() => {
701
+ console.log('Done.');
702
+ }).catch((error) => {
703
+ console.error(error);
704
+ });
705
+ ```
706
+
707
+ ### `close()` method
708
+
709
+ The `close()` method sends a close command to the Curtain. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
710
+
711
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
712
+
713
+ When the Curtain receives this command, the Curtain will close the curtain (100% position). If not calibrated, the Curtain does not move.
714
+
715
+ ```javascript
716
+ switchbot.discover({ model: 'c', quick: true }).then((device_list) => {
717
+ return device_list[0].close();
718
+ }).then(() => {
719
+ console.log('Done.');
720
+ }).catch((error) => {
721
+ console.error(error);
722
+ });
723
+ ```
724
+
725
+ ### `pause()` method
726
+
727
+ The `pause()` method sends a pause command to the Curtain. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
728
+
729
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
730
+
731
+ When the Curtain receives this command, the Curtain will pause.
732
+
733
+ ```javascript
734
+ switchbot.discover({ model: 'c', quick: true }).then((device_list) => {
735
+ return device_list[0].pause();
736
+ }).then(() => {
737
+ console.log('Done.');
738
+ }).catch((error) => {
739
+ console.error(error);
740
+ });
741
+ ```
742
+
743
+ ### `runToPos()` method
744
+
745
+ The `runToPos()` method sends a position command to the Curtain. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
746
+
747
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
748
+
749
+ When the Curtain receives this command, the Curtain will run to the percentage position. If not calibrated, the Curtain does not move.
750
+
751
+
752
+ The `open()` method sends a open command to the Curtain. This method returns a `Promise` object. Nothing will be passed to the `resove()`.
753
+
754
+ If no connection is established with the device, this method automatically establishes a connection with the device, then finally closes the connection. You don't have to call the [`connect()`](#SwitchbotDevice-connect-method) method in advance.
755
+
756
+ When the Curtain receives this command, the Curtain will open the curtain (0% position). If not calibrated, the Curtain does not move.
757
+
758
+ Property | Type | Required | Description
759
+ :------------|:--------|:---------|:------------
760
+ `percent` | Integer | Required | The percentage of target position (`0-100`). (e.g., `50`)
761
+ `mode` | Integer | Optional | The running mode of Curtain. <br/>`0x00` - Performance mode.<br/> `0x01` - Slient mode. <br/>`0xff` - Default. Unspecified, from Curtain's settings.
762
+
763
+ ```javascript
764
+ switchbot.discover({ model: 'c', quick: true }).then((device_list) => {
765
+ return device_list[0].runToPos(50);
766
+ }).then(() => {
767
+ console.log('Done.');
768
+ }).catch((error) => {
769
+ console.error(error);
770
+ });
771
+ ```
772
+
773
+ ---------------------------------------
774
+ ## Advertisement data
775
+
776
+ After the [`startScan()`](#startscan-method) method is invoked, the [`onadvertisement`](#Switchbot-onadvertisement-event-handler) event handler will be called whenever an advertising packet comes from the switchbot devices. An object containing the properties as follows will be passed to the event handler:
777
+
778
+ Property | Type | Description
779
+ :-------------|:--------|:-----------
780
+ `id` | String | ID of the device. (e.g., `"cb4eb903c96d"`)
781
+ `address` | String | MAC address of the device. Basically it is as same as the value of the `id` except that this value includes `:` in the string. (e.g., `"cb:4e:b9:03:c9:6d"`)
782
+ `rssi` | Integer | RSSI. (e.g., `-62`)
783
+ `serviceData` | Object | An object including the device-specific data.
784
+
785
+ The structures of the `serviceData` are described in the following sections.
786
+
787
+ ### Bot (WoHand)
788
+
789
+ Example of the advertisement data:
790
+
791
+ ```json
792
+ {
793
+ "id": "c12e453e2008",
794
+ "address": "c1:2e:45:3e:20:08",
795
+ "rssi": -61,
796
+ "serviceData": {
797
+ "model": "H",
798
+ "modelName": "WoHand",
799
+ "mode": true,
800
+ "state": false,
801
+ "battery": 100
802
+ }
803
+ }
804
+ ```
805
+
806
+ Structure of the `serviceData`:
807
+
808
+ Property | Type | Description
809
+ :-----------|:--------|:-----------
810
+ `model` | String | This value is always `"H"`, which means "Bot (WoHand)".
811
+ `modelName` | String | This value is always `"WoHand"`, which means "Bot".
812
+ `mode` | Boolean | This indicates the mode setting. When the mode is "Switch mode", this value is `true`. When the mode is "Press mode", this value is `false`.
813
+ `state` | Boolean | This value indicates whether the switch status is ON or OFF.
814
+ `battery` | Integer | (**experimental**) This value indicates the battery level (`%`).
815
+
816
+ The `mode` can be changed only using the official smartphone app. The node-switchbot does not support changing the mode because the BLE protocol is non-public.
817
+
818
+ If the `mode` is `false`, which means the "Press mode" is selected, the `state` is always `false`. If the `mode` is `true`, which means the "Switch mode" is selected, the `state` represents the logical state (ON or OFF). Note that it does *not* mean the physical arm position. The physical arm position depends on the setting "Inverse the on/off direction" on the official smartphone app.
819
+
820
+ "Inverse the on/off direction" | Value of the `state` | Logical state | Physical arm position
821
+ :------------------------------|:---------------------|:--------------|:------------
822
+ disabled | `true` | OFF | Up (retracted)
823
+ &nbsp; | `false` | ON | Down (stretched)
824
+ enabled | `true` | OFF | Down (stretched)
825
+ &nbsp; | `false` | ON | Up (retracted)
826
+
827
+ The `battery` is *experimental* for now. I'm not sure whether the value is correct or not. Never trust this value for now.
828
+
829
+
830
+ ### Meter (WoSensorTH)
831
+
832
+ Example of the advertisement data:
833
+
834
+ ```json
835
+ {
836
+ "id": "cb4eb903c96d",
837
+ "address": "cb:4e:b9:03:c9:6d",
838
+ "rssi": -70,
839
+ "serviceData": {
840
+ "model": "T",
841
+ "modelName": "WoSensorTH",
842
+ "temperature": {
843
+ "c": 25.2,
844
+ "f": 77.4
845
+ },
846
+ "fahrenheit": false,
847
+ "humidity": 43,
848
+ "battery": 100
849
+ }
850
+ }
851
+ ```
852
+
853
+ Structure of the `data`:
854
+
855
+ Property | Type | Description
856
+ :-------------|:--------|:-----------
857
+ `model` | String | This value is always `"T"`, which means "Meter (WoSensorTH)".
858
+ `modelName` | String | This value is always `"WoSensorTH"`, which means "Meter".
859
+ `temperature` | Object |
860
+ &nbsp;&nbsp; `c` | Float | Temperature (degree Celsius/°C)
861
+ &nbsp;&nbsp; `f` | Float | Temperature (degree Fahrenheit/℉)
862
+ `fahrenheit` | Boolean | The flag whether the Meter shows Fahrenheit (`true`) or Celsius (`false`) for the temperature on the display
863
+ `humidity` | Integer | Humidity (`%`)
864
+ `battery` | Integer | (**experimental**) This value indicates the battery level (`%`).
865
+
866
+ The `fahrenheit` indicates the setting on the device. Note that it does *not* indicate the setting on the official smartphone app. The setting of the temperature unit on the device and the setting on the app are independent.
867
+
868
+ The `battery` is *experimental* for now. I'm not sure whether the value is correct or not. Never trust this value for now.
869
+
870
+ ### Curtain (WoCurtain)
871
+
872
+ Example of the advertisement data:
873
+
874
+ ```json
875
+ {
876
+ "id": "ec58c5d00111",
877
+ "address": "ec:58:c5:d0:01:11",
878
+ "rssi": -39,
879
+ "serviceData": {
880
+ "model": "c",
881
+ "modelName": "WoCurtain",
882
+ "calibration": true,
883
+ "battery": 91,
884
+ "position": 1,
885
+ "lightLevel": 1
886
+ }
887
+ }
888
+ ```
889
+
890
+ Structure of the `serviceData`:
891
+
892
+ Property | Type | Description
893
+ :-------------|:--------|:-----------
894
+ `model` | String | This value is always `"c"`, which means "Curtain (WoCurtain)".
895
+ `modelName` | String | This value is always `"WoCurtain"`, which means "Curtain".
896
+ `calibration` | Boolean | This value indicates the calibration status (`true` or `false`).
897
+ `battery` | Integer | This value indicates the battery level (`1-100`, `%`).
898
+ `position` | Integer | This value indicates the percentage of current position (`0-100`, 0 is open, `%`).
899
+ `lightLevel` | Integer | This value indicates the light level of the light source currently set (`1-10`).
900
+
901
+ ---------------------------------------
902
+ ## Release Note
903
+
904
+ * v1.0.0 (2021-01-21)
905
+ * fix "No device was found" in MacOS
906
+ * v0.2.0 (2020-11-05)
907
+ * Modify Curtain's action command to support group and running mode. (Thanks to [@SwitchBot-Wonderlabs](https://github.com/futomi/node-switchbot/pull/7/))
908
+ * v0.1.0 (2020-10-28)
909
+ * Added support for SwitchBot Curtain. (Thanks to [@SwitchBot-Wonderlabs](https://github.com/futomi/node-switchbot/pull/6/))
910
+ * Added support for running on the Raspberry Pi Zero W. (Thanks to [@zizi4n5](https://github.com/futomi/node-switchbot/pull/5))
911
+ * v0.0.5 (2020-02-19)
912
+ * Improved the stability of discovering the BLE characteristics. (Thanks to [@dnicolson](https://github.com/futomi/node-switchbot/issues/3))
913
+ * v0.0.4 (2020-02-11)
914
+ * Fixed the bug that temperature value lower than 0 degC could not be handled. (Thanks to [@musimasami](https://github.com/futomi/node-switchbot/issues/2))
915
+ * v0.0.3 (2020-02-10)
916
+ * Now the characteristic UUID `0x2a00` (Device Name) is not mandatory. Some models of Bot don't seem to support the characteristic. (Thanks to [@dnicolson](https://github.com/futomi/node-switchbot/issues/1))
917
+ * Fixed the bug that the `turnOn()` method returns an error if the "Press mode" is selected on the Bot.
918
+ * v0.0.2 (2019-11-20)
919
+ * First public release
920
+
921
+ ---------------------------------------
922
+ ## References
923
+
924
+ * [Switchbot official global site](https://www.switch-bot.com/)
925
+ * [Github - OpenWonderLabs/python-host](https://github.com/OpenWonderLabs/python-host)
926
+
927
+ ---------------------------------------
928
+ ## License
929
+
930
+ The MIT License (MIT)
931
+
932
+ Copyright (c) 2019-2020 Futomi Hatano
933
+
934
+ Permission is hereby granted, free of charge, to any person obtaining a copy
935
+ of this software and associated documentation files (the "Software"), to deal
936
+ in the Software without restriction, including without limitation the rights
937
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
938
+ copies of the Software, and to permit persons to whom the Software is
939
+ furnished to do so, subject to the following conditions:
940
+
941
+ The above copyright notice and this permission notice shall be included in all
942
+ copies or substantial portions of the Software.
943
+
944
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
945
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
946
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
947
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
948
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
949
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
950
+ SOFTWARE.