diff --git a/CHANGELOG.md b/CHANGELOG.md index 006f5d6e..110deaee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,19 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) +## [2.0.2](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v2.0.2) (2024-02-09) + +### What's Changed +- Fix various 2.0.0 issues [#224](https://github.com/OpenWonderLabs/node-switchbot/pull/224), Thanks [@dnicolson](https://github.com/dnicolson) +- Code Cleanup [#225](https://github.com/OpenWonderLabs/node-switchbot/pull/225) [#226](https://github.com/OpenWonderLabs/node-switchbot/pull/226), Thanks [@dnicolson](https://github.com/dnicolson) +- Housekeeping and update dependencies + +**Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v2.0.1...v2.0.2 + ## [2.0.1](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v2.0.1) (2024-02-06) ### What's Changed -- Fix async constructor, Thanks [@dnicolson](https://github.com/dnicolson) [#229](https://github.com/OpenWonderLabs/node-switchbot/pull/220) +- Fix async constructor [#220](https://github.com/OpenWonderLabs/node-switchbot/pull/220), Thanks [@dnicolson](https://github.com/dnicolson) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v2.0.0...v2.0.1 @@ -14,8 +23,8 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed - Rewrite into Typescript and Convert CommonJS to ES Module -- Fix Linting, Thanks [@dnicolson](https://github.com/dnicolson) [#216](https://github.com/OpenWonderLabs/node-switchbot/pull/216) -- Code Cleaup, Thanks [@dnicolson](https://github.com/dnicolson) [#217](https://github.com/OpenWonderLabs/node-switchbot/pull/217) +- Fix Linting [#216](https://github.com/OpenWonderLabs/node-switchbot/pull/216), Thanks [@dnicolson](https://github.com/dnicolson) +- Code Cleaup [#217](https://github.com/OpenWonderLabs/node-switchbot/pull/217), Thanks [@dnicolson](https://github.com/dnicolson) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.10.0...v2.0.0 @@ -23,8 +32,8 @@ All notable changes to this project will be documented in this file. This projec ## [1.10.0](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.10.0) (2024-01-05) ### What's Changed -- Fix reversed bot state reporting, Thanks [@grelca](https://github.com/grelca) [#207](https://github.com/OpenWonderLabs/node-switchbot/pull/207) -- Add support for Curtain 3, Thanks [@tsia](https://github.com/tsia) [#209](https://github.com/OpenWonderLabs/node-switchbot/pull/209) +- Fix reversed bot state reporting [#207](https://github.com/OpenWonderLabs/node-switchbot/pull/207), Thanks [@grelca](https://github.com/grelca) +- Add support for Curtain 3 [#209](https://github.com/OpenWonderLabs/node-switchbot/pull/209), Thanks [@tsia](https://github.com/tsia) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.9.1...v1.10.0 @@ -39,8 +48,8 @@ All notable changes to this project will be documented in this file. This projec ## [1.9.0](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.9.0) (2023-09-16) ### What's Changed -- Add support for the Indoor/Outdoor Thermo-Hygrometer (WoIOSensorTH), Thanks [@moritzmhmk](https://github.com/moritzmhmk) [#200](https://github.com/OpenWonderLabs/node-switchbot/pull/200) -- Handle noble not being "poweredOn" on init, Thanks [@moritzmhmk](https://github.com/moritzmhmk) [#199](https://github.com/OpenWonderLabs/node-switchbot/pull/199) +- Add support for the Indoor/Outdoor Thermo-Hygrometer (WoIOSensorTH) [#200](https://github.com/OpenWonderLabs/node-switchbot/pull/200), Thanks [@moritzmhmk](https://github.com/moritzmhmk) +- Handle noble not being "poweredOn" on init [#199](https://github.com/OpenWonderLabs/node-switchbot/pull/199), Thanks [@moritzmhmk](https://github.com/moritzmhmk) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.8.2...v1.9.0 @@ -48,10 +57,10 @@ All notable changes to this project will be documented in this file. This projec ## [1.8.2](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.8.2) (2023-07-25) ### What's Changed -- Added Lint Script, Thanks [@dnicolson](https://github.com/dnicolson) [#195](https://github.com/OpenWonderLabs/node-switchbot/pull/195) -- Fixed a Linting Issues, Thanks [@dnicolson](https://github.com/dnicolson) [#196](https://github.com/OpenWonderLabs/node-switchbot/pull/196) -- Fixed 'TypeError: Assignment to constant variable', Thanks [@banboobee](https://github.com/banboobee) [#194](https://github.com/OpenWonderLabs/node-switchbot/pull/194) -- Fix issue of re-assigning to constant for Temperature, Thanks [@gravity-addiction](https://github.com/gravity-addiction) [#191](https://github.com/OpenWonderLabs/node-switchbot/pull/191) +- Added Lint Script [#195](https://github.com/OpenWonderLabs/node-switchbot/pull/195), Thanks [@dnicolson](https://github.com/dnicolson) +- Fixed a Linting Issues [#196](https://github.com/OpenWonderLabs/node-switchbot/pull/196), Thanks [@dnicolson](https://github.com/dnicolson) +- Fixed 'TypeError: Assignment to constant variable' [#194](https://github.com/OpenWonderLabs/node-switchbot/pull/194), Thanks [@banboobee](https://github.com/banboobee) +- Fix issue of re-assigning to constant for Temperature [#191](https://github.com/OpenWonderLabs/node-switchbot/pull/191), Thanks [@gravity-addiction](https://github.com/gravity-addiction) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.8.1...v1.8.2 @@ -59,8 +68,8 @@ All notable changes to this project will be documented in this file. This projec ## [1.8.1](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.8.1) (2023-04-08) ### What's Changed -- Use const keyword for immutable variables, Thanks [@dnicolson](https://github.com/dnicolson/). [#184](https://github.com/OpenWonderLabs/node-switchbot/pull/184) -- Use Error object for promise rejection, Thanks [@dnicolson](https://github.com/dnicolson/). [#181](https://github.com/OpenWonderLabs/node-switchbot/pull/181) +- Use const keyword for immutable variables [#184](https://github.com/OpenWonderLabs/node-switchbot/pull/184), Thanks [@dnicolson](https://github.com/dnicolson/) +- Use Error object for promise rejection [#181](https://github.com/OpenWonderLabs/node-switchbot/pull/181), Thanks [@dnicolson](https://github.com/dnicolson/) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.8.0...v1.8.1 @@ -70,7 +79,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed - Add Support for BlindTilt (Read Only) -- Use Error object for promise rejection, Thanks [@dnicolson](https://github.com/dnicolson/). [#181](https://github.com/OpenWonderLabs/node-switchbot/pull/181) +- Use Error object for promise rejection [#181](https://github.com/OpenWonderLabs/node-switchbot/pull/181), Thanks [@dnicolson](https://github.com/dnicolson/) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.7.3...v1.8.0 @@ -79,7 +88,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed -- Improve error handling, Thanks [@dnicolson](https://github.com/dnicolson/). [#175](https://github.com/OpenWonderLabs/node-switchbot/pull/175) +- Improve error handling [#175](https://github.com/OpenWonderLabs/node-switchbot/pull/175), Thanks [@dnicolson](https://github.com/dnicolson/) - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.7.2...v1.7.3 @@ -275,7 +284,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed -- Modify Curtain's action command to support group and running mode. (Thanks to [@SwitchBot-Wonderlabs](https://github.com/OpenWonderLabs/node-switchbot/pull/7/)) +- Modify Curtain's action command to support group and running mode [#7](https://github.com/OpenWonderLabs/node-switchbot/pull/7), Thanks [@SwitchBot-Wonderlabs](https://github.com/SwitchBot-Wonderlabs) **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v0.1.0...v0.2.0 @@ -283,8 +292,8 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed -- Added support for SwitchBot Curtain. (Thanks to [@SwitchBot-Wonderlabs](https://github.com/OpenWonderLabs/node-switchbot/pull/6/)) -- Added support for running on the Raspberry Pi Zero W. (Thanks to [@zizi4n5](https://github.com/OpenWonderLabs/node-switchbot/pull/5)) +- Added support for SwitchBot Curtain [#6](https://github.com/OpenWonderLabs/node-switchbot/pull/6), Thanks [@SwitchBot-Wonderlabs](https://github.com/SwitchBot-Wonderlabs) +- Added support for running on the Raspberry Pi Zero W [#5](https://github.com/OpenWonderLabs/node-switchbot/pull/5), Thanks [@Szizi4n5](https://github.com/zizi4n5) **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v0.0.5...v0.1.0 @@ -292,7 +301,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed -- Improved the stability of discovering the BLE characteristics. (Thanks to [@dnicolson](https://github.com/OpenWonderLabs/node-switchbot/issues/3)) +- Improved the stability of discovering the BLE characteristics [#3](https://github.com/OpenWonderLabs/node-switchbot/pull/3), Thanks [@dnicolson](https://github.com/dnicolson) **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v0.0.4...v0.0.5 @@ -300,7 +309,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed -- Fixed the bug that temperature value lower than 0 degC could not be handled. (Thanks to [@musimasami](https://github.com/OpenWonderLabs/node-switchbot/issues/2)) +- Fixed the bug that temperature value lower than 0 degC could not be handled [#2](https://github.com/OpenWonderLabs/node-switchbot/pull/2), Thanks [@musimasami](https://github.com/musimasami) **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v0.0.3...v0.0.4 @@ -308,7 +317,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed -- 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/OpenWonderLabs/node-switchbot/issues/1)) +- Now the characteristic UUID `0x2a00` (Device Name) is not mandatory. Some models of Bot don't seem to support the characteristic [#3](https://github.com/OpenWonderLabs/node-switchbot/pull/1), Thanks [@dnicolson](https://github.com/dnicolson) - Fixed the bug that the `turnOn()` method returns an error if the "Press mode" is selected on the Bot. **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v0.0.2...v0.0.3 diff --git a/README.md b/README.md index 6c120035..cfb4dccd 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ Monitoring the advertising packets, you can find your devices and know the lates ```Typescript // Load the node-switchbot and get a `Switchbot` constructor object import { SwitchBot } from 'node-switchbot'; -// Create an `Switchbot` object +// Create a `Switchbot` object const switchbot = new SwitchBot(); (async () => { @@ -138,7 +138,7 @@ The [`startScan()`](#startscan-method) and [`wait()`](#Switchbot-wait-method) me ```Typescript // Load the node-switchbot and get a `Switchbot` constructor object const Switchbot = require("node-switchbot"); -// Create an `Switchbot` object +// Create a `Switchbot` object const switchbot = new Switchbot(); // Start to monitor advertisement packets @@ -214,7 +214,7 @@ This sample discovers a Bot (WoHand), then put the Bot's arm down, finally put i ```Typescript // Load the node-switchbot and get a `Switchbot` constructor object import { SwitchBot } from 'node-switchbot'; -// Create an `Switchbot` object +// Create a `Switchbot` object const switchbot = new SwitchBot(); (async () => { @@ -249,7 +249,7 @@ In order to use the node-switchbot, you have to load the node-switchbot module a import { SwitchBot } from 'node-switchbot'; ``` -You can get an `SwitchBot` constructor from the code above. Then you have to create an `SwitchBot` object from the `SwitchBot` constructor as follows: +You can get an `SwitchBot` constructor from the code above. Then you have to create a `SwitchBot` object from the `SwitchBot` constructor as follows: ```typescript const switchbot = new SwitchBot(); @@ -303,9 +303,8 @@ If you want a quick response, you can set the `quick` property to `true`. ```Typescript switchbot.discover({ - duration: 5000, - quick: true - }); + duration: 5000, + quick: true }).then((device_list) => { // Do something... }).catch((error) => { @@ -749,7 +748,7 @@ Actually, the `WoCurtain` is an object inherited from the [`SwitchbotDevice`](#S ### `open()` method -The `open()` method sends a open command to the Curtain. This method returns a `Promise` object. Nothing will be passed to the `resove()`. +The `open()` method sends an open command to the Curtain. This method returns a `Promise` object. Nothing will be passed to the `resove()`. 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. diff --git a/package-lock.json b/package-lock.json index b634db99..f6fd82e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "node-switchbot", - "version": "2.0.1", + "version": "2.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "node-switchbot", - "version": "2.0.1", + "version": "2.0.2", "license": "MIT", "dependencies": { "@abandonware/noble": "^1.9.2-24" }, "devDependencies": { - "@types/node": "^20.11.16", + "@types/node": "^20.11.17", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "eslint": "^8.56.0", @@ -835,9 +835,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", - "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -4761,9 +4761,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, "node_modules/ssri": { diff --git a/package.json b/package.json index 57569004..5f42a8c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-switchbot", - "version": "2.0.1", + "version": "2.0.2", "description": "The node-switchbot is a Node.js module which allows you to control your Switchbot Devices through Bluetooth (BLE).", "homepage": "https://github.com/OpenWonderLabs/node-switchbot", "author": "OpenWonderLabs (https://github.com/OpenWonderLabs)", @@ -42,7 +42,7 @@ "@abandonware/bluetooth-hci-socket": "^0.5.3-10" }, "devDependencies": { - "@types/node": "^20.11.16", + "@types/node": "^20.11.17", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "eslint": "^8.56.0", diff --git a/src/advertising.ts b/src/advertising.ts index d48b4de8..1dddc3a4 100644 --- a/src/advertising.ts +++ b/src/advertising.ts @@ -1,5 +1,18 @@ import { Buffer } from 'buffer'; +import { WoHand } from './device/wohand.js'; +import { WoCurtain } from './device/wocurtain.js'; +import { WoBlindTilt } from './device/woblindtilt.js'; +import { WoPresence } from './device/wopresence.js'; +import { WoContact } from './device/wocontact.js'; +import { WoSensorTH } from './device/wosensorth.js'; +import { WoIOSensorTH } from './device/woiosensorth.js'; +import { WoHumi } from './device/wohumi.js'; +import { WoPlugMini } from './device/woplugmini.js'; +import { WoBulb } from './device/wobulb.js'; +import { WoStrip } from './device/wostrip.js'; +import { WoSmartLock } from './device/wosmartlock.js'; + export class Advertising { constructor() {} @@ -91,33 +104,33 @@ export class Advertising { let sd; if (model === 'H') { - sd = this.parseServiceDataForWoHand(buf, onlog);//WoHand + sd = WoHand.parseServiceData(buf, onlog);//WoHand } else if (model === 'T') { - sd = this.parseServiceDataForWoSensorTH(buf, onlog);//WoSensorTH + sd = WoSensorTH.parseServiceData(buf, onlog);//WoSensorTH } else if (model === 'e') { - sd = this.parseServiceDataForWoHumi(buf, onlog);//WoHumi + sd = WoHumi.parseServiceData(buf, onlog);//WoHumi } else if (model === 's') { - sd = this.parseServiceDataForWoPresence(buf, onlog);//WoPresence + sd = WoPresence.parseServiceData(buf, onlog);//WoPresence } else if (model === 'd') { - sd = this.parseServiceDataForWoContact(buf, onlog);//WoContact + sd = WoContact.parseServiceData(buf, onlog);//WoContact } else if (model === 'c' || model === '{') { - sd = this.parseServiceDataForWoCurtain(buf, onlog);// WoCurtain + sd = WoCurtain.parseServiceData(buf, onlog);// WoCurtain } else if (model === 'x') { - sd = this.parseServiceDataForWoBlindTilt(buf, onlog);// WoBlindTilt + sd = WoBlindTilt.parseServiceData(buf, onlog);// WoBlindTilt } else if (model === 'u') { - sd = this.parseServiceDataForWoBulb(manufacturerData, onlog);// WoBulb + sd = WoBulb.parseServiceData(manufacturerData, onlog);// WoBulb } else if (model === 'g') { - sd = this.parseServiceDataForWoPlugMiniUS(manufacturerData, onlog); // WoPlugMini (US) + sd = WoPlugMini.parseServiceData_US(manufacturerData, onlog); // WoPlugMini (US) } else if (model === 'j') { - sd = this.parseServiceDataForWoPlugMiniJP(manufacturerData, onlog);// WoPlugMini (JP) + sd = WoPlugMini.parseServiceData_JP(manufacturerData, onlog);// WoPlugMini (JP) } else if (model === 'o') { - sd = this.parseServiceDataForWoSmartLock(manufacturerData, onlog);// WoSmartLock + sd = WoSmartLock.parseServiceData(manufacturerData, onlog);// WoSmartLock } else if (model === 'i') { - sd = this.parseServiceDataForWoSensorTHPlus(buf, onlog);// WoMeterPlus + sd = WoSensorTH.parseServiceData_Plus(buf, onlog);// WoMeterPlus } else if (model === 'r') { - sd = this.parseServiceDataForWoStrip(buf, onlog);// WoStrip + sd = WoStrip.parseServiceData(buf, onlog);// WoStrip } else if (model === 'w') { - sd = this.parseServiceDataForWoIOSensorTH(buf, manufacturerData, onlog); // Indoor/Outdoor Thermo-Hygrometer + sd = WoIOSensorTH.parseServiceData(buf, manufacturerData, onlog); // Indoor/Outdoor Thermo-Hygrometer } else { if (onlog && typeof onlog === 'function') { onlog( @@ -167,551 +180,4 @@ export class Advertising { } return data; } - - static parseServiceDataForWoHand(buf, onlog) { - if (buf.length !== 3) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoHand] Buffer length ${buf.length} !== 3!`, - ); - } - return null; - } - const byte1 = buf.readUInt8(1); - const byte2 = buf.readUInt8(2); - - const mode = byte1 & 0b10000000 ? true : false; // Whether the light switch Add-on is used or not. 0 = press, 1 = switch - const state = byte1 & 0b01000000 ? false : true; // Whether the switch status is ON or OFF. 0 = on, 1 = off - const battery = byte2 & 0b01111111; // % - - const data = { - model: 'H', - modelName: 'WoHand', - mode: mode, - state: state, - battery: battery, - }; - - return data; - } - - static parseServiceDataForWoSensorTH(buf, onlog) { - if (buf.length !== 6) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoSensorTH] Buffer length ${buf.length} !== 6!`, - ); - } - return null; - } - const byte2 = buf.readUInt8(2); - const byte3 = buf.readUInt8(3); - const byte4 = buf.readUInt8(4); - const byte5 = buf.readUInt8(5); - - const temp_sign = byte4 & 0b10000000 ? 1 : -1; - const temp_c = temp_sign * ((byte4 & 0b01111111) + (byte3 & 0b00001111) / 10); - const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; - - const data = { - model: 'T', - modelName: 'WoSensorTH', - temperature: { - c: temp_c, - f: temp_f, - }, - fahrenheit: byte5 & 0b10000000 ? true : false, - humidity: byte5 & 0b01111111, - battery: byte2 & 0b01111111, - }; - - return data; - } - - static parseServiceDataForWoHumi(buf, onlog) { - if (buf.length !== 8) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoHumi] Buffer length ${buf.length} !== 8!`, - ); - } - return null; - } - const byte1 = buf.readUInt8(1); - const byte4 = buf.readUInt8(4); - - - const onState = byte1 & 0b10000000 ? true : false; // 1 - on - const autoMode = byte4 & 0b10000000 ? true : false; // 1 - auto - const percentage = byte4 & 0b01111111; // 0-100%, 101/102/103 - Quick gear 1/2/3 - - const data = { - model: 'e', - modelName: 'WoHumi', - onState: onState, - autoMode: autoMode, - percentage: autoMode ? 0 : percentage, - }; - - return data; - } - - static parseServiceDataForWoPresence(buf, onlog) { - if (buf.length !== 6) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoPresence] Buffer length ${buf.length} !== 6!`, - ); - } - return null; - } - - const byte1 = buf.readUInt8(1); - const byte2 = buf.readUInt8(2); - const byte5 = buf.readUInt8(5); - - const tested = byte1 & 0b10000000 ? true : false; - const movement = byte1 & 0b01000000 ? true : false; - const battery = byte2 & 0b01111111; - const led = (byte5 & 0b00100000) >> 5; - const iot = (byte5 & 0b00010000) >> 4; - const sense_distance = (byte5 & 0b00001100) >> 2; - const lightLevel = byte5 & 0b00000011; - const is_light = byte5 & 0b00000010 ? true : false; - - const data = { - model: 's', - modelName: 'WoMotion', - tested: tested, - movement: movement, - battery: battery, - led: led, - iot: iot, - sense_distance: sense_distance, - lightLevel: - lightLevel === 1 ? 'dark' : lightLevel === 2 ? 'bright' : 'unknown', - is_light: is_light, - }; - - return data; - } - - static parseServiceDataForWoContact(buf, onlog) { - if (buf.length !== 9) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoContact] Buffer length ${buf.length} !== 9!`, - ); - } - return null; - } - - const byte1 = buf.readUInt8(1); - const byte2 = buf.readUInt8(2); - const byte3 = buf.readUInt8(3); - const byte8 = buf.readUInt8(8); - - const hallState = (byte3 >> 1) & 0b00000011; - const tested = byte1 & 0b10000000; - const movement = byte1 & 0b01000000 ? true : false; // 1 - Movement detected - const battery = byte2 & 0b01111111; // % - const contact_open = (byte3 & 0b00000010) === 0b00000010; - const contact_timeout = (byte3 & 0b00000100) === 0b00000100; - const lightLevel = byte3 & 0b00000001; - const button_count = byte8 & 0b00001111; - - const data = { - model: 'd', - modelName: 'WoContact', - movement: movement, - tested: tested, - battery: battery, - contact_open: contact_open, - contact_timeout: contact_timeout, - lightLevel: lightLevel === 0 ? 'dark' : 'bright', - button_count: button_count, - doorState: - hallState === 0 - ? 'close' - : hallState === 1 - ? 'open' - : 'timeout no closed', - }; - - return data; - } - - static parseServiceDataForWoCurtain(buf, onlog) { - if (buf.length !== 5 && buf.length !== 6) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoCurtain] Buffer length ${buf.length} !== 5 or 6!`, - ); - } - return null; - } - const byte1 = buf.readUInt8(1); - const byte2 = buf.readUInt8(2); - const byte3 = buf.readUInt8(3); - const byte4 = buf.readUInt8(4); - - const calibration = byte1 & 0b01000000 ? true : false; // Whether the calibration is compconsted - const battery = byte2 & 0b01111111; // % - const inMotion = byte3 & 0b10000000 ? true : false; - const currPosition = byte3 & 0b01111111; // current positon % - const lightLevel = (byte4 >> 4) & 0b00001111; // light sensor level (1-10) - const deviceChain = byte4 & 0b00000111; - const model = buf.slice(0, 1).toString('utf8'); - - const data = { - model: model, - modelName: 'WoCurtain', - calibration: calibration, - battery: battery, - inMotion: inMotion, - position: currPosition, - lightLevel: lightLevel, - deviceChain: deviceChain, - }; - - return data; - } - - static parseServiceDataForWoBlindTilt(buf, onlog) { - if (buf.length !== 5 && buf.length !== 6) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoBlindTilt] Buffer length ${buf.length} !== 5 or 6!`, - ); - } - return null; - } - const byte1 = buf.readUInt8(1); - const byte2 = buf.readUInt8(2); - - const calibration = byte1 & 0b00000001 ? true : false; // Whether the calibration is completed - const battery = byte2 & 0b01111111; // % - const inMotion = byte2 & 0b10000000 ? true : false; - const tilt = byte2 & 0b01111111; // current tilt % (100 - _tilt) if reverse else _tilt, - const lightLevel = (byte1 >> 4) & 0b00001111; // light sensor level (1-10) - - const data = { - model: 'x', - modelName: 'WoBlindTilt', - calibration: calibration, - battery: battery, - inMotion: inMotion, - tilt: tilt, - lightLevel: lightLevel, - }; - - return data; - } - - static parseServiceDataForWoBulb(manufacturerData, onlog) { - if (manufacturerData.length !== 13) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoBulb] Buffer length ${manufacturerData.length} !== 13!`, - ); - } - return null; - } - const byte1 = manufacturerData.readUInt8(1);//power and light status - //const byte2 = manufacturerData.readUInt8(2);//bulb brightness - const byte3 = manufacturerData.readUInt8(3);//bulb R - const byte4 = manufacturerData.readUInt8(4);//bulb G - const byte5 = manufacturerData.readUInt8(5);//bulb B - const byte6 = manufacturerData.readUInt8(6);//bulb temperature - const byte7 = manufacturerData.readUInt8(7); - const byte8 = manufacturerData.readUInt8(8); - const byte9 = manufacturerData.readUInt8(9); - const byte10 = manufacturerData.readUInt8(10);//bulb mode - - const power = byte1; - const red = byte3; - const green = byte4; - const blue = byte5; - const color_temperature = byte6; - const state = byte7 & 0b01111111 ? true : false; - const brightness = byte7 & 0b01111111; - const delay = byte8 & 0b10000000; - const preset = byte8 & 0b00001000; - const color_mode = byte8 & 0b00000111; - const speed = byte9 & 0b01111111; - const loop_index = byte10 & 0b11111110; - - const data = { - model: 'u', - modelName: 'WoBulb', - color_temperature: color_temperature, - power: power, - state: state, - red: red, - green: green, - blue: blue, - brightness: brightness, - delay: delay, - preset: preset, - color_mode: color_mode, - speed: speed, - loop_index: loop_index, - }; - - return data; - } - - static parseServiceDataForWoPlugMiniUS(manufacturerData, onlog) { - if (manufacturerData.length !== 14) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoPlugMiniUS] Buffer length ${manufacturerData.length} should be 14`, - ); - } - return null; - } - const byte9 = manufacturerData.readUInt8(9); // byte9: plug mini state; 0x00=off, 0x80=on - const byte10 = manufacturerData.readUInt8(10); // byte10: bit0: 0=no delay,1=delay, bit1:0=no timer, 1=timer; bit2:0=no sync time, 1=sync'ed time - const byte11 = manufacturerData.readUInt8(11); // byte11: wifi rssi - const byte12 = manufacturerData.readUInt8(12); // byte12: bit7: overload? - const byte13 = manufacturerData.readUInt8(13); // byte12[bit0~6] + byte13: current power value - - const state = byte9 === 0x00 ? 'off' : byte9 === 0x80 ? 'on' : null; - const delay = !!(byte10 & 0b00000001); - const timer = !!(byte10 & 0b00000010); - const syncUtcTime = !!(byte10 & 0b00000100); - const wifiRssi = byte11; - const overload = !!(byte12 & 0b10000000); - const currentPower = (((byte12 & 0b01111111) << 8) + byte13) / 10; // in watt - // TODO: voltage ??? - - const data = { - model: 'g', - modelName: 'WoPlugMini', - state: state, - delay: delay, - timer: timer, - syncUtcTime: syncUtcTime, - wifiRssi: wifiRssi, - overload: overload, - currentPower: currentPower, - }; - - return data; - } - - static parseServiceDataForWoPlugMiniJP(manufacturerData, onlog) { - if (manufacturerData.length !== 14) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoPlugMiniJP] Buffer length ${manufacturerData.length} should be 14`, - ); - } - return null; - } - const byte9 = manufacturerData.readUInt8(9); // byte9: plug mini state; 0x00=off, 0x80=on - const byte10 = manufacturerData.readUInt8(10); // byte10: bit0: 0=no delay,1=delay, bit1:0=no timer, 1=timer; bit2:0=no sync time, 1=sync'ed time - const byte11 = manufacturerData.readUInt8(11); // byte11: wifi rssi - const byte12 = manufacturerData.readUInt8(12); // byte12: bit7: overload? - const byte13 = manufacturerData.readUInt8(13); // byte12[bit0~6] + byte13: current power value - - const state = byte9 === 0x00 ? 'off' : byte9 === 0x80 ? 'on' : null; - const delay = !!(byte10 & 0b00000001); - const timer = !!(byte10 & 0b00000010); - const syncUtcTime = !!(byte10 & 0b00000100); - const wifiRssi = byte11; - const overload = !!(byte12 & 0b10000000); - const currentPower = (((byte12 & 0b01111111) << 8) + byte13) / 10; // in watt - // TODO: voltage ??? - - const data = { - model: 'j', - modelName: 'WoPlugMini', - state: state, - delay: delay, - timer: timer, - syncUtcTime: syncUtcTime, - wifiRssi: wifiRssi, - overload: overload, - currentPower: currentPower, - }; - - return data; - } - - static parseServiceDataForWoSmartLock(manufacturerData, onlog) { - if (manufacturerData.length !== 6) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoSmartLock] Buffer length ${manufacturerData.length} !== 6!`, - ); - } - return null; - } - const byte2 = manufacturerData.readUInt8(2); - const byte7 = manufacturerData.readUInt8(7); - const byte8 = manufacturerData.readUInt8(8); - - - const LockStatus = { - LOCKED: 0b0000000, - UNLOCKED: 0b0010000, - LOCKING: 0b0100000, - UNLOCKING: 0b0110000, - LOCKING_STOP: 0b1000000, - UNLOCKING_STOP: 0b1010000, - NOT_FULLY_LOCKED: 0b1100000, //Only EU lock type - }; - - const battery = byte2 & 0b01111111; // % - const calibration = byte7 & 0b10000000 ? true : false; - const status = LockStatus[byte7 & 0b01110000]; - const update_from_secondary_lock = byte7 & 0b00001000 ? true : false; - const door_open = byte7 & 0b00000100 ? true : false; - const double_lock_mode = byte8 & 0b10000000 ? true : false; - const unclosed_alarm = byte8 & 0b00100000 ? true : false; - const unlocked_alarm = byte8 & 0b00010000 ? true : false; - const auto_lock_paused = byte8 & 0b00000010 ? true : false; - - const data = { - model: 'o', - modelName: 'WoSmartLock', - battery: battery, - calibration: calibration, - status: status, - update_from_secondary_lock: update_from_secondary_lock, - door_open: door_open, - double_lock_mode: double_lock_mode, - unclosed_alarm: unclosed_alarm, - unlocked_alarm: unlocked_alarm, - auto_lock_paused: auto_lock_paused, - }; - - return data; - } - - static parseServiceDataForWoSensorTHPlus(buf, onlog) { - if (buf.length !== 6) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoSensorTHPlus] Buffer length ${buf.length} !== 6!`, - ); - } - return null; - } - const byte2 = buf.readUInt8(2); - const byte3 = buf.readUInt8(3); - const byte4 = buf.readUInt8(4); - const byte5 = buf.readUInt8(5); - - const temp_sign = byte4 & 0b10000000 ? 1 : -1; - const temp_c = temp_sign * ((byte4 & 0b01111111) + (byte3 & 0b00001111) / 10); - const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; - - const data = { - model: 'i', - modelName: 'WoSensorTHPlus', - temperature: { - c: temp_c, - f: temp_f, - }, - fahrenheit: byte5 & 0b10000000 ? true : false, - humidity: byte5 & 0b01111111, - battery: byte2 & 0b01111111, - }; - - return data; - } - - static parseServiceDataForWoStrip(buf, onlog) { - if (buf.length !== 18) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoStrip] Buffer length ${buf.length} !== 18!`, - ); - } - return null; - } - - //const byte1 = buf.readUInt8(1);//power and light status - //const byte2 = buf.readUInt8(2);//bulb brightness - const byte3 = buf.readUInt8(3);//bulb R - const byte4 = buf.readUInt8(4);//bulb G - const byte5 = buf.readUInt8(5);//bulb B - const byte7 = buf.readUInt8(7); - const byte8 = buf.readUInt8(8); - const byte9 = buf.readUInt8(9); - const byte10 = buf.readUInt8(10); - - const state = byte7 & 0b10000000 ? true : false; - const brightness = byte7 & 0b01111111; - const red = byte3; - const green = byte4; - const blue = byte5; - const delay = byte8 & 0b10000000; - const preset = byte8 & 0b00001000; - const color_mode = byte8 & 0b00000111; - const speed = byte9 & 0b01111111; - const loop_index = byte10 & 0b11111110; - - const data = { - model: 'r', - modelName: 'WoStrip', - state: state, - brightness: brightness, - red: red, - green: green, - blue: blue, - delay: delay, - preset: preset, - color_mode: color_mode, - speed: speed, - loop_index: loop_index, - }; - - return data; - } - - static parseServiceDataForWoIOSensorTH(serviceDataBuf, manufacturerDataBuf, onlog) { - if (serviceDataBuf.length !== 3) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoIOSensorTH] Service Data Buffer length ${serviceDataBuf.length} !== 3!`, - ); - } - return null; - } - if (manufacturerDataBuf.length !== 14) { - if (onlog && typeof onlog === 'function') { - onlog( - `[parseServiceDataForWoIOSensorTH] Manufacturer Data Buffer length ${manufacturerDataBuf.length} !== 14!`, - ); - } - return null; - } - const mdByte10 = manufacturerDataBuf.readUInt8(10); - const mdByte11 = manufacturerDataBuf.readUInt8(11); - const mdByte12 = manufacturerDataBuf.readUInt8(12); - - const sdByte2 = serviceDataBuf.readUInt8(2); - - const temp_sign = mdByte11 & 0b10000000 ? 1 : -1; - const temp_c = temp_sign * ((mdByte11 & 0b01111111) + (mdByte10 & 0b00001111) / 10); - const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; - - const data = { - model: 'w', - modelName: 'WoIOSensorTH', - temperature: { - c: temp_c, - f: temp_f, - }, - fahrenheit: mdByte12 & 0b10000000 ? true : false, // needs to be confirmed! - humidity: mdByte12 & 0b01111111, - battery: sdByte2 & 0b01111111, - }; - - console.log(data); - return data; - } } diff --git a/src/device.ts b/src/device.ts index 25ef7743..9aa60753 100644 --- a/src/device.ts +++ b/src/device.ts @@ -30,7 +30,7 @@ export class SwitchbotDevice { _onconnect: () => void; _ondisconnect: () => void; _ondisconnect_internal: () => void; - _onnotify_internal: () => void; + _onnotify_internal: (buf: Buffer) => void; /* ------------------------------------------------------------------ * Constructor * @@ -46,6 +46,13 @@ export class SwitchbotDevice { this._SERV_UUID_PRIMARY = 'cba20d00224d11e69fb80002a5d5c51b'; this._CHAR_UUID_WRITE = 'cba20002224d11e69fb80002a5d5c51b'; + this._CHAR_UUID_NOTIFY = 'cba20003224d11e69fb80002a5d5c51b'; + this._CHAR_UUID_DEVICE = '2a00'; + + this._READ_TIMEOUT_MSEC = 3000; + this._WRITE_TIMEOUT_MSEC = 3000; + this._COMMAND_TIMEOUT_MSEC = 3000; + // Save the device information const ad: ad = Advertising.parse(peripheral); this._id = ad?.id; @@ -56,10 +63,10 @@ export class SwitchbotDevice { this._was_connected_explicitly = false; this._connected = false; - this._onconnect = () => {}; - this._ondisconnect = () => {}; - this._ondisconnect_internal = () => {}; - this._onnotify_internal = () => {}; + this._onconnect = () => { }; + this._ondisconnect = () => { }; + this._ondisconnect_internal = () => { }; + this._onnotify_internal = () => { }; } // Getters @@ -182,7 +189,7 @@ export class SwitchbotDevice { return new Promise((resolve, reject) => { // Set timeout timer let timer: NodeJS.Timeout | null = setTimeout(() => { - this._ondisconnect_internal = () => {}; + this._ondisconnect_internal = () => { }; timer = null; reject( new Error('Failed to discover services and characteristics: TIMEOUT'), @@ -194,7 +201,7 @@ export class SwitchbotDevice { if (timer) { clearTimeout(timer); timer = null; - this._ondisconnect_internal = () => {}; + this._ondisconnect_internal = () => { }; } reject( new Error( @@ -239,7 +246,7 @@ export class SwitchbotDevice { if (timer) { clearTimeout(timer); timer = null; - this._ondisconnect_internal = () => {}; + this._ondisconnect_internal = () => { }; reject(error); } else { // Do nothing @@ -296,8 +303,8 @@ export class SwitchbotDevice { reject(error); return; } - char.on('data', () => { // Remove the argument passed to the _onnotify_internal function - this._onnotify_internal(); + char.on('data', (buf) => { // Remove the argument passed to the _onnotify_internal function + this._onnotify_internal(buf); }); resolve(); }); @@ -384,8 +391,8 @@ export class SwitchbotDevice { // Some models of Bot don't seem to support this characteristic UUID throw new Error( 'The device does not support the characteristic UUID 0x' + - this._CHAR_UUID_DEVICE + - '.', + this._CHAR_UUID_DEVICE + + '.', ); } return this._read(this._chars.device); @@ -438,8 +445,8 @@ export class SwitchbotDevice { // Some models of Bot don't seem to support this characteristic UUID throw new Error( 'The device does not support the characteristic UUID 0x' + - this._CHAR_UUID_DEVICE + - '.', + this._CHAR_UUID_DEVICE + + '.', ); } return this._write(this._chars.device, buf); @@ -459,14 +466,14 @@ export class SwitchbotDevice { // Write the specified Buffer data to the write characteristic // and receive the response from the notify characteristic // with connection handling - _command(req_buf) { + _command(req_buf: Buffer) { return new Promise((resolve, reject) => { if (!Buffer.isBuffer(req_buf)) { reject(new Error('The specified data is not acceptable for writing.')); return; } - let res_buf; + let res_buf: Buffer | unknown; this._connect() .then(() => { @@ -493,20 +500,18 @@ export class SwitchbotDevice { _waitCommandResponse() { return new Promise((resolve, reject) => { - const buf: Buffer | null = null; - let timer: NodeJS.Timeout | undefined = setTimeout(() => { timer = undefined; - this._onnotify_internal = () => {}; + this._onnotify_internal = () => { }; reject(new Error('COMMAND_TIMEOUT')); }, this._COMMAND_TIMEOUT_MSEC); - this._onnotify_internal = () => { + this._onnotify_internal = (buf) => { if (timer) { clearTimeout(timer); timer = undefined; } - this._onnotify_internal = () => {}; + this._onnotify_internal = () => { }; resolve(buf); }; }); diff --git a/src/device/woblindtilt.ts b/src/device/woblindtilt.ts index 58a8e38c..d53ded56 100644 --- a/src/device/woblindtilt.ts +++ b/src/device/woblindtilt.ts @@ -4,9 +4,40 @@ */ import { Buffer } from 'buffer'; -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; export class WoBlindTilt extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 5 && buf.length !== 6) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoBlindTilt] Buffer length ${buf.length} !== 5 or 6!`, + ); + } + return null; + } + const byte1 = buf.readUInt8(1); + const byte2 = buf.readUInt8(2); + + const calibration = byte1 & 0b00000001 ? true : false; // Whether the calibration is completed + const battery = byte2 & 0b01111111; // % + const inMotion = byte2 & 0b10000000 ? true : false; + const tilt = byte2 & 0b01111111; // current tilt % (100 - _tilt) if reverse else _tilt, + const lightLevel = (byte1 >> 4) & 0b00001111; // light sensor level (1-10) + + const data = { + model: 'x', + modelName: 'WoBlindTilt', + calibration: calibration, + battery: battery, + inMotion: inMotion, + tilt: tilt, + lightLevel: lightLevel, + }; + + return data; + } + /* ------------------------------------------------------------------ * open() * - Open the blindtilt diff --git a/src/device/wobulb.ts b/src/device/wobulb.ts index 8910ff23..38250b34 100644 --- a/src/device/wobulb.ts +++ b/src/device/wobulb.ts @@ -4,12 +4,65 @@ */ import { Buffer } from 'buffer'; -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; /** * @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/colorbulb.md */ export class WoBulb extends SwitchbotDevice { + static parseServiceData(manufacturerData, onlog) { + if (manufacturerData.length !== 13) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoBulb] Buffer length ${manufacturerData.length} !== 13!`, + ); + } + return null; + } + const byte1 = manufacturerData.readUInt8(1);//power and light status + //const byte2 = manufacturerData.readUInt8(2);//bulb brightness + const byte3 = manufacturerData.readUInt8(3);//bulb R + const byte4 = manufacturerData.readUInt8(4);//bulb G + const byte5 = manufacturerData.readUInt8(5);//bulb B + const byte6 = manufacturerData.readUInt8(6);//bulb temperature + const byte7 = manufacturerData.readUInt8(7); + const byte8 = manufacturerData.readUInt8(8); + const byte9 = manufacturerData.readUInt8(9); + const byte10 = manufacturerData.readUInt8(10);//bulb mode + + const power = byte1; + const red = byte3; + const green = byte4; + const blue = byte5; + const color_temperature = byte6; + const state = byte7 & 0b01111111 ? true : false; + const brightness = byte7 & 0b01111111; + const delay = byte8 & 0b10000000; + const preset = byte8 & 0b00001000; + const color_mode = byte8 & 0b00000111; + const speed = byte9 & 0b01111111; + const loop_index = byte10 & 0b11111110; + + const data = { + model: 'u', + modelName: 'WoBulb', + color_temperature: color_temperature, + power: power, + state: state, + red: red, + green: green, + blue: blue, + brightness: brightness, + delay: delay, + preset: preset, + color_mode: color_mode, + speed: speed, + loop_index: loop_index, + }; + + return data; + } + /** * @returns {Promise<boolean>} resolves with a boolean that tells whether the plug in ON(true) or OFF(false) */ @@ -48,7 +101,7 @@ export class WoBulb extends SwitchbotDevice { reject( new Error( 'The type of target brightness percentage is incorrect: ' + - typeof brightness, + typeof brightness, ), ); }); @@ -70,7 +123,7 @@ export class WoBulb extends SwitchbotDevice { reject( new Error( 'The type of target color_temperature percentage is incorrect: ' + - typeof color_temperature, + typeof color_temperature, ), ); }); @@ -92,7 +145,7 @@ export class WoBulb extends SwitchbotDevice { reject( new Error( 'The type of target brightness percentage is incorrect: ' + - typeof brightness, + typeof brightness, ), ); }); @@ -102,7 +155,7 @@ export class WoBulb extends SwitchbotDevice { reject( new Error( 'The type of target red is incorrect: ' + - typeof red, + typeof red, ), ); }); @@ -112,7 +165,7 @@ export class WoBulb extends SwitchbotDevice { reject( new Error( 'The type of target green is incorrect: ' + - typeof green, + typeof green, ), ); }); @@ -122,7 +175,7 @@ export class WoBulb extends SwitchbotDevice { reject( new Error( 'The type of target blue is incorrect: ' + - typeof blue, + typeof blue, ), ); }); @@ -175,7 +228,7 @@ export class WoBulb extends SwitchbotDevice { reject( new Error( 'Expecting a 2-byte response, got instead: 0x' + - res_buf.toString('hex'), + res_buf.toString('hex'), ), ); } diff --git a/src/device/wocontact.ts b/src/device/wocontact.ts index 9ccd4106..3232dd44 100644 --- a/src/device/wocontact.ts +++ b/src/device/wocontact.ts @@ -2,6 +2,51 @@ * * wocontact.ts: Switchbot BLE API registration. */ -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; -export class WoContact extends SwitchbotDevice {} +export class WoContact extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 9) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoContact] Buffer length ${buf.length} !== 9!`, + ); + } + return null; + } + + const byte1 = buf.readUInt8(1); + const byte2 = buf.readUInt8(2); + const byte3 = buf.readUInt8(3); + const byte8 = buf.readUInt8(8); + + const hallState = (byte3 >> 1) & 0b00000011; + const tested = byte1 & 0b10000000; + const movement = byte1 & 0b01000000 ? true : false; // 1 - Movement detected + const battery = byte2 & 0b01111111; // % + const contact_open = (byte3 & 0b00000010) === 0b00000010; + const contact_timeout = (byte3 & 0b00000100) === 0b00000100; + const lightLevel = byte3 & 0b00000001; + const button_count = byte8 & 0b00001111; + + const data = { + model: 'd', + modelName: 'WoContact', + movement: movement, + tested: tested, + battery: battery, + contact_open: contact_open, + contact_timeout: contact_timeout, + lightLevel: lightLevel === 0 ? 'dark' : 'bright', + button_count: button_count, + doorState: + hallState === 0 + ? 'close' + : hallState === 1 + ? 'open' + : 'timeout no closed', + }; + + return data; + } +} diff --git a/src/device/wocurtain.ts b/src/device/wocurtain.ts index 6b15a524..618a8a71 100644 --- a/src/device/wocurtain.ts +++ b/src/device/wocurtain.ts @@ -4,9 +4,45 @@ */ import { Buffer } from 'buffer'; -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; export class WoCurtain extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 5 && buf.length !== 6) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoCurtain] Buffer length ${buf.length} !== 5 or 6!`, + ); + } + return null; + } + const byte1 = buf.readUInt8(1); + const byte2 = buf.readUInt8(2); + const byte3 = buf.readUInt8(3); + const byte4 = buf.readUInt8(4); + + const calibration = byte1 & 0b01000000 ? true : false; // Whether the calibration is compconsted + const battery = byte2 & 0b01111111; // % + const inMotion = byte3 & 0b10000000 ? true : false; + const currPosition = byte3 & 0b01111111; // current positon % + const lightLevel = (byte4 >> 4) & 0b00001111; // light sensor level (1-10) + const deviceChain = byte4 & 0b00000111; + const model = buf.slice(0, 1).toString('utf8'); + + const data = { + model: model, + modelName: 'WoCurtain', + calibration: calibration, + battery: battery, + inMotion: inMotion, + position: currPosition, + lightLevel: lightLevel, + deviceChain: deviceChain, + }; + + return data; + } + /* ------------------------------------------------------------------ * open() * - Open the curtain @@ -70,7 +106,7 @@ export class WoCurtain extends SwitchbotDevice { reject( new Error( 'The type of target position percentage is incorrect: ' + - typeof percent, + typeof percent, ), ); }); diff --git a/src/device/wohand.ts b/src/device/wohand.ts index c08759e8..5bf16f45 100644 --- a/src/device/wohand.ts +++ b/src/device/wohand.ts @@ -1,8 +1,34 @@ import { Buffer } from 'buffer'; -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; export class WoHand extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 3) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceData] Buffer length ${buf.length} !== 3!`, + ); + } + return null; + } + const byte1 = buf.readUInt8(1); + const byte2 = buf.readUInt8(2); + + const mode = byte1 & 0b10000000 ? true : false; // Whether the light switch Add-on is used or not. 0 = press, 1 = switch + const state = byte1 & 0b01000000 ? false : true; // Whether the switch status is ON or OFF. 0 = on, 1 = off + const battery = byte2 & 0b01111111; // % + + const data = { + model: 'H', + modelName: 'WoHand', + mode: mode, + state: state, + battery: battery, + }; + return data; + } + /* ------------------------------------------------------------------ * press() * - Press diff --git a/src/device/wohumi.ts b/src/device/wohumi.ts index 998f6978..34c79bf1 100644 --- a/src/device/wohumi.ts +++ b/src/device/wohumi.ts @@ -1,8 +1,36 @@ import { Buffer } from 'buffer'; -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; export class WoHumi extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 8) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoHumi] Buffer length ${buf.length} !== 8!`, + ); + } + return null; + } + const byte1 = buf.readUInt8(1); + const byte4 = buf.readUInt8(4); + + + const onState = byte1 & 0b10000000 ? true : false; // 1 - on + const autoMode = byte4 & 0b10000000 ? true : false; // 1 - auto + const percentage = byte4 & 0b01111111; // 0-100%, 101/102/103 - Quick gear 1/2/3 + + const data = { + model: 'e', + modelName: 'WoHumi', + onState: onState, + autoMode: autoMode, + percentage: autoMode ? 0 : percentage, + }; + + return data; + } + /* ------------------------------------------------------------------ * press() * - Press diff --git a/src/device/woiosensorth.ts b/src/device/woiosensorth.ts index f04c7279..824cd733 100644 --- a/src/device/woiosensorth.ts +++ b/src/device/woiosensorth.ts @@ -1,3 +1,46 @@ -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; -export class WoIOSensorTH extends SwitchbotDevice {} +export class WoIOSensorTH extends SwitchbotDevice { + static parseServiceData(serviceDataBuf, manufacturerDataBuf, onlog) { + if (serviceDataBuf.length !== 3) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoIOSensorTH] Service Data Buffer length ${serviceDataBuf.length} !== 3!`, + ); + } + return null; + } + if (manufacturerDataBuf.length !== 14) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoIOSensorTH] Manufacturer Data Buffer length ${manufacturerDataBuf.length} !== 14!`, + ); + } + return null; + } + const mdByte10 = manufacturerDataBuf.readUInt8(10); + const mdByte11 = manufacturerDataBuf.readUInt8(11); + const mdByte12 = manufacturerDataBuf.readUInt8(12); + + const sdByte2 = serviceDataBuf.readUInt8(2); + + const temp_sign = mdByte11 & 0b10000000 ? 1 : -1; + const temp_c = temp_sign * ((mdByte11 & 0b01111111) + (mdByte10 & 0b00001111) / 10); + const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; + + const data = { + model: 'w', + modelName: 'WoIOSensorTH', + temperature: { + c: temp_c, + f: temp_f, + }, + fahrenheit: mdByte12 & 0b10000000 ? true : false, // needs to be confirmed! + humidity: mdByte12 & 0b01111111, + battery: sdByte2 & 0b01111111, + }; + + console.log(data); + return data; + } +} diff --git a/src/device/woplugmini.ts b/src/device/woplugmini.ts index 5e0c809e..51dda560 100644 --- a/src/device/woplugmini.ts +++ b/src/device/woplugmini.ts @@ -1,11 +1,89 @@ import { Buffer } from 'buffer'; -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; /** * @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/plugmini.md */ export class WoPlugMini extends SwitchbotDevice { + static parseServiceData_US(manufacturerData, onlog) { + if (manufacturerData.length !== 14) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoPlugMiniUS] Buffer length ${manufacturerData.length} should be 14`, + ); + } + return null; + } + const byte9 = manufacturerData.readUInt8(9); // byte9: plug mini state; 0x00=off, 0x80=on + const byte10 = manufacturerData.readUInt8(10); // byte10: bit0: 0=no delay,1=delay, bit1:0=no timer, 1=timer; bit2:0=no sync time, 1=sync'ed time + const byte11 = manufacturerData.readUInt8(11); // byte11: wifi rssi + const byte12 = manufacturerData.readUInt8(12); // byte12: bit7: overload? + const byte13 = manufacturerData.readUInt8(13); // byte12[bit0~6] + byte13: current power value + + const state = byte9 === 0x00 ? 'off' : byte9 === 0x80 ? 'on' : null; + const delay = !!(byte10 & 0b00000001); + const timer = !!(byte10 & 0b00000010); + const syncUtcTime = !!(byte10 & 0b00000100); + const wifiRssi = byte11; + const overload = !!(byte12 & 0b10000000); + const currentPower = (((byte12 & 0b01111111) << 8) + byte13) / 10; // in watt + // TODO: voltage ??? + + const data = { + model: 'g', + modelName: 'WoPlugMini', + state: state, + delay: delay, + timer: timer, + syncUtcTime: syncUtcTime, + wifiRssi: wifiRssi, + overload: overload, + currentPower: currentPower, + }; + + return data; + } + + static parseServiceData_JP(manufacturerData, onlog) { + if (manufacturerData.length !== 14) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoPlugMiniJP] Buffer length ${manufacturerData.length} should be 14`, + ); + } + return null; + } + const byte9 = manufacturerData.readUInt8(9); // byte9: plug mini state; 0x00=off, 0x80=on + const byte10 = manufacturerData.readUInt8(10); // byte10: bit0: 0=no delay,1=delay, bit1:0=no timer, 1=timer; bit2:0=no sync time, 1=sync'ed time + const byte11 = manufacturerData.readUInt8(11); // byte11: wifi rssi + const byte12 = manufacturerData.readUInt8(12); // byte12: bit7: overload? + const byte13 = manufacturerData.readUInt8(13); // byte12[bit0~6] + byte13: current power value + + const state = byte9 === 0x00 ? 'off' : byte9 === 0x80 ? 'on' : null; + const delay = !!(byte10 & 0b00000001); + const timer = !!(byte10 & 0b00000010); + const syncUtcTime = !!(byte10 & 0b00000100); + const wifiRssi = byte11; + const overload = !!(byte12 & 0b10000000); + const currentPower = (((byte12 & 0b01111111) << 8) + byte13) / 10; // in watt + // TODO: voltage ??? + + const data = { + model: 'j', + modelName: 'WoPlugMini', + state: state, + delay: delay, + timer: timer, + syncUtcTime: syncUtcTime, + wifiRssi: wifiRssi, + overload: overload, + currentPower: currentPower, + }; + + return data; + } + /** * @returns {Promise<boolean>} resolves with a boolean that tells whether the plug in ON(true) or OFF(false) */ diff --git a/src/device/wopresence.ts b/src/device/wopresence.ts index 08eac06b..44641e26 100644 --- a/src/device/wopresence.ts +++ b/src/device/wopresence.ts @@ -1,3 +1,43 @@ -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; -export class WoPresence extends SwitchbotDevice {} +export class WoPresence extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 6) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoPresence] Buffer length ${buf.length} !== 6!`, + ); + } + return null; + } + + const byte1 = buf.readUInt8(1); + const byte2 = buf.readUInt8(2); + const byte5 = buf.readUInt8(5); + + const tested = byte1 & 0b10000000 ? true : false; + const movement = byte1 & 0b01000000 ? true : false; + const battery = byte2 & 0b01111111; + const led = (byte5 & 0b00100000) >> 5; + const iot = (byte5 & 0b00010000) >> 4; + const sense_distance = (byte5 & 0b00001100) >> 2; + const lightLevel = byte5 & 0b00000011; + const is_light = byte5 & 0b00000010 ? true : false; + + const data = { + model: 's', + modelName: 'WoMotion', + tested: tested, + movement: movement, + battery: battery, + led: led, + iot: iot, + sense_distance: sense_distance, + lightLevel: + lightLevel === 1 ? 'dark' : lightLevel === 2 ? 'bright' : 'unknown', + is_light: is_light, + }; + + return data; + } +} diff --git a/src/device/wosensorth.ts b/src/device/wosensorth.ts index 9f323e93..81262d85 100644 --- a/src/device/wosensorth.ts +++ b/src/device/wosensorth.ts @@ -1,3 +1,69 @@ -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; -export class WoSensorTH extends SwitchbotDevice {} +export class WoSensorTH extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 6) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoSensorTH] Buffer length ${buf.length} !== 6!`, + ); + } + return null; + } + const byte2 = buf.readUInt8(2); + const byte3 = buf.readUInt8(3); + const byte4 = buf.readUInt8(4); + const byte5 = buf.readUInt8(5); + + const temp_sign = byte4 & 0b10000000 ? 1 : -1; + const temp_c = temp_sign * ((byte4 & 0b01111111) + (byte3 & 0b00001111) / 10); + const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; + + const data = { + model: 'T', + modelName: 'WoSensorTH', + temperature: { + c: temp_c, + f: temp_f, + }, + fahrenheit: byte5 & 0b10000000 ? true : false, + humidity: byte5 & 0b01111111, + battery: byte2 & 0b01111111, + }; + + return data; + } + + static parseServiceData_Plus(buf, onlog) { + if (buf.length !== 6) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoSensorTHPlus] Buffer length ${buf.length} !== 6!`, + ); + } + return null; + } + const byte2 = buf.readUInt8(2); + const byte3 = buf.readUInt8(3); + const byte4 = buf.readUInt8(4); + const byte5 = buf.readUInt8(5); + + const temp_sign = byte4 & 0b10000000 ? 1 : -1; + const temp_c = temp_sign * ((byte4 & 0b01111111) + (byte3 & 0b00001111) / 10); + const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; + + const data = { + model: 'i', + modelName: 'WoSensorTHPlus', + temperature: { + c: temp_c, + f: temp_f, + }, + fahrenheit: byte5 & 0b10000000 ? true : false, + humidity: byte5 & 0b01111111, + battery: byte2 & 0b01111111, + }; + + return data; + } +} diff --git a/src/device/wosmartlock.ts b/src/device/wosmartlock.ts new file mode 100644 index 00000000..fab89c63 --- /dev/null +++ b/src/device/wosmartlock.ts @@ -0,0 +1,62 @@ +/* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * wosmartlock.ts: Switchbot BLE API registration. + */ +//import { Buffer } from 'buffer'; + +import { SwitchbotDevice } from '../device.js'; + +export class WoSmartLock extends SwitchbotDevice { + static parseServiceData(manufacturerData, onlog) { + if (manufacturerData.length !== 6) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoSmartLock] Buffer length ${manufacturerData.length} !== 6!`, + ); + } + return null; + } + const byte2 = manufacturerData.readUInt8(2); + const byte7 = manufacturerData.readUInt8(7); + const byte8 = manufacturerData.readUInt8(8); + + + const LockStatus = { + LOCKED: 0b0000000, + UNLOCKED: 0b0010000, + LOCKING: 0b0100000, + UNLOCKING: 0b0110000, + LOCKING_STOP: 0b1000000, + UNLOCKING_STOP: 0b1010000, + NOT_FULLY_LOCKED: 0b1100000, //Only EU lock type + }; + + const battery = byte2 & 0b01111111; // % + const calibration = byte7 & 0b10000000 ? true : false; + const status = LockStatus[byte7 & 0b01110000]; + const update_from_secondary_lock = byte7 & 0b00001000 ? true : false; + const door_open = byte7 & 0b00000100 ? true : false; + const double_lock_mode = byte8 & 0b10000000 ? true : false; + const unclosed_alarm = byte8 & 0b00100000 ? true : false; + const unlocked_alarm = byte8 & 0b00010000 ? true : false; + const auto_lock_paused = byte8 & 0b00000010 ? true : false; + + const data = { + model: 'o', + modelName: 'WoSmartLock', + battery: battery, + calibration: calibration, + status: status, + update_from_secondary_lock: update_from_secondary_lock, + door_open: door_open, + double_lock_mode: double_lock_mode, + unclosed_alarm: unclosed_alarm, + unlocked_alarm: unlocked_alarm, + auto_lock_paused: auto_lock_paused, + }; + + return data; + } + +} + diff --git a/src/device/wostrip.ts b/src/device/wostrip.ts index d0b4e237..be8c3ccf 100644 --- a/src/device/wostrip.ts +++ b/src/device/wostrip.ts @@ -1,11 +1,60 @@ import { Buffer } from 'buffer'; -import { SwitchbotDevice } from '../switchbot.js'; +import { SwitchbotDevice } from '../device.js'; /** * @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/colorbulb.md */ export class WoStrip extends SwitchbotDevice { + static parseServiceData(buf, onlog) { + if (buf.length !== 18) { + if (onlog && typeof onlog === 'function') { + onlog( + `[parseServiceDataForWoStrip] Buffer length ${buf.length} !== 18!`, + ); + } + return null; + } + + //const byte1 = buf.readUInt8(1);//power and light status + //const byte2 = buf.readUInt8(2);//bulb brightness + const byte3 = buf.readUInt8(3);//bulb R + const byte4 = buf.readUInt8(4);//bulb G + const byte5 = buf.readUInt8(5);//bulb B + const byte7 = buf.readUInt8(7); + const byte8 = buf.readUInt8(8); + const byte9 = buf.readUInt8(9); + const byte10 = buf.readUInt8(10); + + const state = byte7 & 0b10000000 ? true : false; + const brightness = byte7 & 0b01111111; + const red = byte3; + const green = byte4; + const blue = byte5; + const delay = byte8 & 0b10000000; + const preset = byte8 & 0b00001000; + const color_mode = byte8 & 0b00000111; + const speed = byte9 & 0b01111111; + const loop_index = byte10 & 0b11111110; + + const data = { + model: 'r', + modelName: 'WoStrip', + state: state, + brightness: brightness, + red: red, + green: green, + blue: blue, + delay: delay, + preset: preset, + color_mode: color_mode, + speed: speed, + loop_index: loop_index, + }; + + return data; + } + /** * @returns {Promise<boolean>} resolves with a boolean that tells whether the plug in ON(true) or OFF(false) */ @@ -44,7 +93,7 @@ export class WoStrip extends SwitchbotDevice { reject( new Error( 'The type of target brightness percentage is incorrect: ' + - typeof brightness, + typeof brightness, ), ); }); @@ -66,7 +115,7 @@ export class WoStrip extends SwitchbotDevice { reject( new Error( 'Strip Light Doesn\'t Support Color temperature: ' + - typeof color_temperature, + typeof color_temperature, ), ); }); @@ -82,7 +131,7 @@ export class WoStrip extends SwitchbotDevice { reject( new Error( 'The type of target brightness percentage is incorrect: ' + - typeof brightness, + typeof brightness, ), ); }); @@ -92,7 +141,7 @@ export class WoStrip extends SwitchbotDevice { reject( new Error( 'The type of target red is incorrect: ' + - typeof red, + typeof red, ), ); }); @@ -102,7 +151,7 @@ export class WoStrip extends SwitchbotDevice { reject( new Error( 'The type of target green is incorrect: ' + - typeof green, + typeof green, ), ); }); @@ -112,7 +161,7 @@ export class WoStrip extends SwitchbotDevice { reject( new Error( 'The type of target blue is incorrect: ' + - typeof blue, + typeof blue, ), ); }); @@ -165,7 +214,7 @@ export class WoStrip extends SwitchbotDevice { reject( new Error( 'Expecting a 2-byte response, got instead: 0x' + - res_buf.toString('hex'), + res_buf.toString('hex'), ), ); } diff --git a/src/switchbot.ts b/src/switchbot.ts index 7b845f5b..9475ca5f 100644 --- a/src/switchbot.ts +++ b/src/switchbot.ts @@ -13,17 +13,18 @@ import { WoHumi } from './device/wohumi.js'; import { WoPlugMini } from './device/woplugmini.js'; import { WoBulb } from './device/wobulb.js'; import { WoStrip } from './device/wostrip.js'; + type params = { - duration?: number, - model?: string, - id?: string, - quick?: false, - noble?: any, + duration?: number, + model?: string, + id?: string, + quick?: false, + noble?: any, } type peripherals = { - addr?: string, - id?: string, + addr?: string, + id?: string, } export class SwitchBot { @@ -167,7 +168,7 @@ export class SwitchBot { this._init() .then(() => { const peripherals: peripherals = {}; - let timer: NodeJS.Timeout = setTimeout(() => {}, 0); + let timer: NodeJS.Timeout = setTimeout(() => { }, 0); const finishDiscovery = () => { if (timer) { clearTimeout(timer); @@ -185,7 +186,7 @@ export class SwitchBot { // Set a handler for the 'discover' event this.noble.on('discover', (peripheral) => { - const device = SwitchBot.getDeviceObject(peripheral, p.id, p.model) as SwitchbotDevice; + const device = this.getDeviceObject(peripheral, p.id, p.model) as SwitchbotDevice; if (!device) { return; } @@ -264,7 +265,7 @@ export class SwitchBot { return promise; } - static getDeviceObject(peripheral, id, model) { + getDeviceObject(peripheral, id, model) { const ad = Advertising.parse(peripheral, this.onlog); if (this.filterAdvertising(ad, id, model)) { let device; @@ -300,7 +301,7 @@ export class SwitchBot { device = new WoPlugMini(peripheral, this.noble); break; case 'o': - //device = new SwitchbotDeviceWoSmartLock(peripheral, this.noble); + //device = new SwitchbotDeviceWoSmartLock(peripheral, this.noble); break; case 'i': device = new WoSensorTH(peripheral, this.noble); @@ -321,7 +322,7 @@ export class SwitchBot { } } - static filterAdvertising(ad, id, model) { + filterAdvertising(ad, id, model) { if (!ad) { return false; } @@ -398,7 +399,7 @@ export class SwitchBot { * - Promise object * Nothing will be passed to the `resolve()`. * ---------------------------------------------------------------- */ - startScan(params) { + startScan(params?: params) { const promise = new Promise<void>((resolve, reject) => { // Check the parameters const valid = ParameterChecker.check( @@ -443,17 +444,17 @@ export class SwitchBot { .then(() => { // Determine the values of the parameters const p = { - model: params.model || '', - id: params.id || '', + model: params?.model || '', + id: params?.id || '', }; // Set a handler for the 'discover' event this.noble.on('discover', (peripheral) => { const ad = Advertising.parse(peripheral, this.onlog); - if (SwitchBot.filterAdvertising(ad, p.id, p.model)) { + if (this.filterAdvertising(ad, p.id, p.model)) { if ( this.onadvertisement && - typeof this.onadvertisement === 'function' + typeof this.onadvertisement === 'function' ) { this.onadvertisement(ad); } @@ -506,7 +507,7 @@ export class SwitchBot { * - Promise object * Nothing will be passed to the `resolve()`. * ---------------------------------------------------------------- */ - static wait(msec: number) { + wait(msec: number) { return new Promise((resolve, reject) => { // Check the parameters const valid = ParameterChecker.check(