diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..b5663330 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,83 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" // uses the recommended rules from the @typescript-eslint/eslint-plugin + ], + "parserOptions": { + "ecmaVersion": 2021, + "sourceType": "module" + }, + "ignorePatterns": [ + "dist", + "server" + ], + "rules": { + "quotes": [ + "warn", + "single" + ], + "indent": [ + "warn", + 2, + { + "SwitchCase": 1 + } + ], + "linebreak-style": [ + "warn", + "unix" + ], + "semi": [ + "warn", + "always" + ], + "comma-dangle": [ + "warn", + "always-multiline" + ], + "dot-notation": "off", + "eqeqeq": "warn", + "curly": [ + "warn", + "all" + ], + "brace-style": [ + "warn" + ], + "prefer-arrow-callback": [ + "warn" + ], + "max-len": [ + "warn", + 150 + ], // use the provided log method instead + "no-non-null-assertion": [ + "off" + ], + "comma-spacing": [ + "error" + ], + "no-multi-spaces": [ + "warn", + { + "ignoreEOLComments": true + } + ], + "no-trailing-spaces": [ + "warn" + ], + "lines-between-class-members": [ + "warn", + "always", + { + "exceptAfterSingleLine": true + } + ], + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off" + } +} \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 120e6804..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "commonjs": true, - "es2021": true - }, - "extends": "eslint:recommended", - "overrides": [ - { - "env": { - "node": true - }, - "files": [ - ".eslintrc.{js,cjs}" - ], - "parserOptions": { - "sourceType": "script" - } - } - ], - "parserOptions": { - "ecmaVersion": "latest" - }, - "rules": { - } -} diff --git a/.github/workflows/changerelease.yml b/.github/workflows/changerelease.yml index 64d5c6c8..2f871ae2 100644 --- a/.github/workflows/changerelease.yml +++ b/.github/workflows/changerelease.yml @@ -7,7 +7,7 @@ on: branches: [latest] jobs: - changerelease: - uses: OpenWonderLabs/.github/.github/workflows/changerelease.yml@latest - secrets: - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + changerelease: + uses: OpenWonderLabs/.github/.github/workflows/changerelease.yml@latest + secrets: + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 4fd93a25..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ latest, beta* ] - pull_request: - branches: [ latest, beta* ] - schedule: - - cron: '17 9 * * 2' - -jobs: - analyze: - uses: OpenWonderLabs/.github/.github/workflows/codeql-analysis.yml@latest diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 4a229f61..7903e61c 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -2,17 +2,12 @@ name: AutoDependabot on: pull_request: - push: - branches: - - beta-*.*.* + branches: [beta, latest] + pull_request_target: + branches: [beta, latest] jobs: - automerge: - name: Auto-merge patch updates - runs-on: ubuntu-latest - steps: - - uses: mitto98/dependabot-automerge-action@master - with: - token: ${{ github.token }} - merge-patch: true - + dependabot: + uses: OpenWonderLabs/.github/.github/workflows/dependabot.yml@latest + secrets: + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index e1d2470a..3d7e983f 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -10,7 +10,7 @@ name: Labeler on: pull_request_target jobs: - label: + labeler: uses: OpenWonderLabs/.github/.github/workflows/labeler.yml@latest secrets: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 87e9ab56..ebcf117d 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: jobs: - stale: + release-drafter: uses: OpenWonderLabs/.github/.github/workflows/release-drafter.yml@latest secrets: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index ce7105bf..be8c6e2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1,157 @@ -# Windows image file caches -Thumbs.db -ehthumbs.db +# Ignore compiled code -# Folder config file -Desktop.ini +dist -# Recycle Bin used on file shares -$RECYCLE.BIN/ +# ------------- Defaults ------------- -# Windows Installer files -*.cab -*.msi -*.msm -*.msp +# Logs -# Windows shortcuts -*.lnk +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log\* -# ========================= -# Operating System Files -# ========================= +# Diagnostic reports (https://nodejs.org/api/report.html) -# OSX -# ========================= +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories -.DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -test - -# Node_modeules Directory node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variables file + +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.pnp.\* + +# others +.DS_Store diff --git a/.npmignore b/.npmignore index 65e3ba2e..c9960b3e 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,170 @@ -test/ +# Ignore source code + +src + +# ------------- Defaults ------------- + +# eslint + +.eslintrc + +# typescript + +tsconfig.json + +# vscode + +.vscode + +# nodemon + +nodemon.json + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log\* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variables file + +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.pnp.\* diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e7716c8..c1049fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,17 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) -## [1.10.0](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.10.0) (2024-1-5) +## [2.0.0](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v2.0.0) (2024-02-05) + +### 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) +- Housekeeping and update dependencies + +**Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.10.0...v2.0.0 + +## [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) @@ -11,7 +21,7 @@ All notable changes to this project will be documented in this file. This projec **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.9.1...v1.10.0 -## [1.9.1](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.9.1) (2023-11-2) +## [1.9.1](https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v1.9.1) (2023-11-02) ### What's Changed - Housekeeping and update dependencies @@ -121,7 +131,7 @@ All notable changes to this project will be documented in this file. This projec ### What's Changed -- Fix Plug Mini (US) implimentation +- Fix Plug Mini (US) implementation - Housekeeping and update dependencies **Full Changelog**: https://github.com/OpenWonderLabs/node-switchbot/compare/v1.4.0...v1.4.1 diff --git a/README.md b/README.md index 0cff1190..b29eaa50 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ But some functionalities of this module were developed through trial and error. - [Moving the arm of the Bot](#moving-the-arm-of-the-bot) - [`Switchbot` object](#switchbot-object) - [`discover()` method](#discover-method) - - [`ondiscover` event handler](#ondiscover-event-hander) + - [`ondiscover` event handler](#ondiscover-event-handler) - [`startScan()` method](#startscan-method) - [`stopScan()` method](#stopscan-method) - [`onadvertisement` event handler](#onadvertisement-event-handler) @@ -117,7 +117,7 @@ const switchbot = new Switchbot(); (async () => { // Start to monitor advertisement packets await switchbot.startScan(); - // Set an event hander + // Set an event handler switchbot.onadvertisement = (ad) => { console.log(JSON.stringify(ad, null, ' ')); }; @@ -145,7 +145,7 @@ const switchbot = new Switchbot(); switchbot .startScan() .then(() => { - // Set an event hander + // Set an event handler switchbot.onadvertisement = (ad) => { console.log(JSON.stringify(ad, null, " ")); }; diff --git a/errros.log b/errros.log deleted file mode 100644 index ca669a31..00000000 --- a/errros.log +++ /dev/null @@ -1,13 +0,0 @@ - -/usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/node_modules/@abandonware/noble/lib/noble.js:137 - this._bindings.startScanning(serviceUuids, allowDuplicates); - ^ -TypeError: A string was expected - at Noble.scan (/usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/node_modules/@abandonware/noble/lib/noble.js:137:22) - at Noble.startScanning (/usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/node_modules/@abandonware/noble/lib/noble.js:149:10) - at /usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/node_modules/@abandonware/noble/lib/noble.js:155:44 - at node:internal/util:364:7 - at new Promise () - at node:internal/util:350:12 - at Noble.startScanningAsync (/usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/node_modules/@abandonware/noble/lib/noble.js:155:99) - at /usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/lib/switchbot.js:374:22 \ No newline at end of file diff --git a/lib/switchbot-device-wocontact.js b/lib/switchbot-device-wocontact.js deleted file mode 100644 index 63443786..00000000 --- a/lib/switchbot-device-wocontact.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -const SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoContact extends SwitchbotDevice {} - -module.exports = SwitchbotDeviceWoContact; diff --git a/lib/switchbot-device-woiosensorth.js b/lib/switchbot-device-woiosensorth.js deleted file mode 100644 index 6aacf5e8..00000000 --- a/lib/switchbot-device-woiosensorth.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -const SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoIOSensorTH extends SwitchbotDevice {} - -module.exports = SwitchbotDeviceWoIOSensorTH; diff --git a/lib/switchbot-device-wopresence.js b/lib/switchbot-device-wopresence.js deleted file mode 100644 index e5f28ca9..00000000 --- a/lib/switchbot-device-wopresence.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -const SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoPresence extends SwitchbotDevice {} - -module.exports = SwitchbotDeviceWoPresence; diff --git a/lib/switchbot-device-wosensorth.js b/lib/switchbot-device-wosensorth.js deleted file mode 100644 index 8f0579a5..00000000 --- a/lib/switchbot-device-wosensorth.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -const SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoSensorTH extends SwitchbotDevice {} - -module.exports = SwitchbotDeviceWoSensorTH; diff --git a/lib/switchbot.js b/lib/switchbot.js deleted file mode 100644 index 2802a45a..00000000 --- a/lib/switchbot.js +++ /dev/null @@ -1,496 +0,0 @@ -"use strict"; -const parameterChecker = require("./parameter-checker.js"); -const switchbotAdvertising = require("./switchbot-advertising.js"); - -const SwitchbotDevice = require("./switchbot-device.js"); -const SwitchbotDeviceWoHand = require("./switchbot-device-wohand.js"); -const SwitchbotDeviceWoCurtain = require("./switchbot-device-wocurtain.js"); -const SwitchbotDeviceWoBlindTilt = require("./switchbot-device-woblindtilt.js"); -const SwitchbotDeviceWoPresence = require("./switchbot-device-wopresence.js"); -const SwitchbotDeviceWoContact = require("./switchbot-device-wocontact.js"); -const SwitchbotDeviceWoSensorTH = require("./switchbot-device-wosensorth.js"); -const SwitchbotDeviceWoIOSensorTH = require("./switchbot-device-woiosensorth.js"); -const SwitchbotDeviceWoHumi = require("./switchbot-device-wohumi.js"); -const SwitchbotDeviceWoPlugMini = require("./switchbot-device-woplugmini.js"); -const SwitchbotDeviceWoBulb = require("./switchbot-device-wobulb.js"); -const SwitchbotDeviceWoStrip = require("./switchbot-device-wostrip.js"); - -class Switchbot { - /* ------------------------------------------------------------------ - * Constructor - * - * [Arguments] - * - params | Object | Optional | - * - noble | Noble | Optional | The Noble object created by the noble module. - * | | | This parameter is optional. - * | | | If you don't specify this parameter, this - * | | | module automatically creates it. - * ---------------------------------------------------------------- */ - constructor(params) { - // Check parameters - let noble = null; - if (params && params.noble) { - noble = params.noble; - } else { - noble = require("@abandonware/noble"); - } - - // Public properties - this.noble = noble; - this.ondiscover = null; - this.onadvertisement = null; - this.onlog = null; - - // Private properties - this._scanning = false; - this._DEFAULT_DISCOVERY_DURATION = 5000; - this._PRIMARY_SERVICE_UUID_LIST = []; - } - - /* ------------------------------------------------------------------ - * discover([params]) - * - Discover switchbot devices - * - * [Arguments] - * - params | Object | Optional | - * - duration | Integer | Optional | Duration for discovery process (msec). - * | | | The value must be in the range of 1 to 60000. - * | | | The default value is 5000 (msec). - * - model | String | Optional | "H", "T", "e", "s", "d", "c", "{", "u", "g", "o", "i", or "r". - * | | | If "H" is specified, this method will discover only Bots. - * | | | If "T" is specified, this method will discover only Meters. - * | | | If "e" is specified, this method will discover only Humidifiers. - * | | | If "s" is specified, this method will discover only Motion Sensors. - * | | | If "d" is specified, this method will discover only Contact Sensors. - * | | | If "c" is specified, this method will discover only Curtains. - * | | | If "{" is specified, this method will discover only Curtain 3. - * | | | If "u" is specified, this method will discover only Color Bulbs. - * | | | If "g" is specified, this method will discover only Plugs. - * | | | If "o" is specified, this method will discover only Locks. - * | | | If "i" is specified, this method will discover only Meter Pluses. - * | | | If "r" is specified, this method will discover only Locks. - * - 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. - * - 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. - * - * [Return value] - * - Promise object - * An array will be passed to the `resolve()`, which includes - * `SwitchbotDevice` objects representing the found devices. - * ---------------------------------------------------------------- */ - discover(params = {}) { - const promise = new Promise((resolve, reject) => { - // Check the parameters - const valid = parameterChecker.check( - params, - { - duration: { required: false, type: "integer", min: 1, max: 60000 }, - model: { - required: false, - type: "string", - enum: [ - "H", - "T", - "e", - "s", - "d", - "c", - "{", - "u", - "g", - "j", - "o", - "i", - "r", - "x", - "w", - ], - }, - id: { required: false, type: "string", min: 12, max: 17 }, - quick: { required: false, type: "boolean" }, - }, - false - ); - - if (!valid) { - reject(new Error(parameterChecker.error.message)); - return; - } - - if (!params) { - params = {}; - } - - // Determine the values of the parameters - const p = { - duration: params.duration || this._DEFAULT_DISCOVERY_DURATION, - model: params.model || "", - id: params.id || "", - quick: params.quick ? true : false, - }; - - // Initialize the noble object - this._init() - .then(() => { - let peripherals = {}; - let timer = null; - const finishDiscovery = () => { - if (timer) { - clearTimeout(timer); - } - this.noble.removeAllListeners("discover"); - this.noble.stopScanning(); - let device_list = []; - for (let addr in peripherals) { - device_list.push(peripherals[addr]); - } - if (device_list.length) { - resolve(device_list); - } - }; - - // Set a handler for the 'discover' event - this.noble.on("discover", (peripheral) => { - const device = this._getDeviceObject(peripheral, p.id, p.model); - if (!device) { - return; - } - const id = device.id; - peripherals[id] = device; - - if (this.ondiscover && typeof this.ondiscover === "function") { - this.ondiscover(device); - } - - if (p.quick) { - finishDiscovery(); - return; - } - }); - - // Start scanning - this.noble.startScanning( - this._PRIMARY_SERVICE_UUID_LIST, - false, - (error) => { - if (error) { - reject(error); - return; - } - timer = setTimeout(() => { - finishDiscovery(); - }, p.duration); - } - ); - }) - .catch((error) => { - reject(error); - }); - }); - return promise; - } - - _init() { - const promise = new Promise((resolve, reject) => { - let err; - if (this.noble.state === "poweredOn") { - resolve(); - return; - } - this.noble.once('stateChange', state => { - switch (state) { - case ("unsupported", "unauthorized", "poweredOff"): - err = new Error( - "Failed to initialize the Noble object: " + this.noble.state - ); - reject(err); - return; - case ("resetting", "unknown"): - err = new Error( - "Adapter is not ready: " + this.noble.state - ); - reject(err); - return; - case "poweredOn": - resolve(); - return; - default: - err = new Error( - "Uknown state: " + this.noble.state - ); - reject(err); - return; - } - }); - }); - return promise; - } - - _getDeviceObject(peripheral, id, model) { - const ad = switchbotAdvertising.parse(peripheral, this.onlog); - if (this._filterAdvertising(ad, id, model)) { - let device = null; - switch (ad.serviceData.model) { - case "H": - device = new SwitchbotDeviceWoHand(peripheral, this.noble); - break; - case "T": - device = new SwitchbotDeviceWoSensorTH(peripheral, this.noble); - break; - case "e": - device = new SwitchbotDeviceWoHumi(peripheral, this.noble); - break; - case "s": - device = new SwitchbotDeviceWoPresence(peripheral, this.noble); - break; - case "d": - device = new SwitchbotDeviceWoContact(peripheral, this.noble); - break; - case "c": - case "{": - device = new SwitchbotDeviceWoCurtain(peripheral, this.noble); - break; - case "x": - device = new SwitchbotDeviceWoBlindTilt(peripheral, this.noble); - break; - case "u": - device = new SwitchbotDeviceWoBulb(peripheral, this.noble); - break; - case "g": - case "j": - device = new SwitchbotDeviceWoPlugMini(peripheral, this.noble); - break; - case "o": - //device = new SwitchbotDeviceWoSmartLock(peripheral, this.noble); - break; - case "i": - device = new SwitchbotDeviceWoSensorTH(peripheral, this.noble); - break; - case "w": - device = new SwitchbotDeviceWoIOSensorTH(peripheral, this.noble); - break; - case "r": - device = new SwitchbotDeviceWoStrip(peripheral, this.noble); - break; - default: // 'resetting', 'unknown' - device = new SwitchbotDevice(peripheral, this.noble); - } - return device; - } else { - return null; - } - } - - _filterAdvertising(ad, id, model) { - if (!ad) { - return false; - } - if (id) { - id = id.toLowerCase().replace(/:/g, ""); - const ad_id = ad.address.toLowerCase().replace(/[^a-z0-9]/g, ""); - if (ad_id !== id) { - return false; - } - } - if (model) { - if (ad.serviceData.model !== model) { - return false; - } - } - return true; - } - - /* ------------------------------------------------------------------ - * startScan([params]) - * - Start to monitor advertising packets coming from switchbot devices - * - * [Arguments] - * - params | Object | Optional | - * - model | String | Optional | "H", "T", "e", "s", "d", "c", "{", "u", "g", "o", "i", "x", or "r". - * | | | If "H" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Bots. - * | | | If "T" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Meters. - * | | | If "e" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Humidifiers. - * | | | If "s" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Motion Sensor. - * | | | If "d" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Contact Sensor. - * | | | If "c" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Curtains. - * | | | If "{" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Curtain 3. - * | | | If "x" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from BlindTilt. - * | | | If "u" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Color Bulb. - * | | | If "g" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Plug Mini. - * | | | If "o" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Smart Lock. - * | | | If "i" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from Meter Plus. - * | | | If "r" is specified, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from LED Strip Light. - * - id | String | Optional | If this value is set, the `onadvertisement` - * | | | event handler will be called only when advertising - * | | | packets comes from devices 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. - * - * [Return value] - * - Promise object - * Nothing will be passed to the `resolve()`. - * ---------------------------------------------------------------- */ - startScan(params) { - const promise = new Promise((resolve, reject) => { - // Check the parameters - const valid = parameterChecker.check( - params, - { - model: { - required: false, - type: "string", - enum: [ - "H", - "T", - "e", - "s", - "d", - "c", - "{", - "u", - "g", - "j", - "o", - "i", - "r", - "x", - "w", - ], - }, - id: { required: false, type: "string", min: 12, max: 17 }, - }, - false - ); - if (!valid) { - reject(new Error(parameterChecker.error.message)); - return; - } - - if (!params) { - params = {}; - } - - // Initialize the noble object - this._init() - .then(() => { - // Determine the values of the parameters - const p = { - model: params.model || "", - id: params.id || "", - }; - - // Set a handler for the 'discover' event - this.noble.on("discover", (peripheral) => { - const ad = switchbotAdvertising.parse(peripheral, this.onlog); - if (this._filterAdvertising(ad, p.id, p.model)) { - if ( - this.onadvertisement && - typeof this.onadvertisement === "function" - ) { - this.onadvertisement(ad); - } - } - }); - - // Start scanning - this.noble.startScanning( - this._PRIMARY_SERVICE_UUID_LIST, - true, - (error) => { - if (error) { - reject(error); - } else { - resolve(); - } - } - ); - }) - .catch((error) => { - reject(error); - }); - }); - return promise; - } - - /* ------------------------------------------------------------------ - * stopScan() - * - Stop to monitor advertising packets coming from switchbot devices - * - * [Arguments] - * - none - * - * [Return value] - * - none - * ---------------------------------------------------------------- */ - stopScan() { - this.noble.removeAllListeners("discover"); - this.noble.stopScanning(); - } - - /* ------------------------------------------------------------------ - * wait(msec) { - * - Wait for the specified time (msec) - * - * [Arguments] - * - msec | Integer | Required | Msec. - * - * [Return value] - * - Promise object - * Nothing will be passed to the `resolve()`. - * ---------------------------------------------------------------- */ - wait(msec) { - return new Promise((resolve, reject) => { - // Check the parameters - const valid = parameterChecker.check( - { msec: msec }, - { - msec: { required: true, type: "integer", min: 0 }, - } - ); - - if (!valid) { - reject(new Error(parameterChecker.error.message)); - return; - } - // Set a timer - setTimeout(resolve, msec); - }); - } -} - -module.exports = Switchbot; diff --git a/package-lock.json b/package-lock.json index 31b5bf57..ad450a5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,25 @@ { "name": "node-switchbot", - "version": "1.10.0", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "node-switchbot", - "version": "1.10.0", + "version": "2.0.0", "license": "MIT", "dependencies": { "@abandonware/noble": "^1.9.2-23" }, "devDependencies": { - "eslint": "^8.54.0", - "npm-check-updates": "^16.14.11" + "@types/node": "^20.11.16", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint": "^8.56.0", + "npm-check-updates": "^16.14.14", + "rimraf": "^5.0.5", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" }, "optionalDependencies": { "@abandonware/bluetooth-hci-socket": "^0.5.3-10" @@ -83,6 +89,18 @@ "node": ">=0.1.90" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -108,9 +126,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -130,10 +148,32 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -146,19 +186,41 @@ "dev": true }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -173,9 +235,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -245,6 +307,31 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -265,6 +352,63 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@mapbox/node-pre-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -376,6 +520,63 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/move-file/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@npmcli/move-file/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/move-file/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@npmcli/move-file/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@npmcli/node-gyp": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", @@ -575,6 +776,30 @@ "node": ">= 10" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@tufjs/canonical-json": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", @@ -597,35 +822,222 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "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==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "undici-types": "~5.26.4" } }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", @@ -640,9 +1052,9 @@ "devOptional": true }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -660,6 +1072,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -765,6 +1186,12 @@ "node": ">=10" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -883,13 +1310,12 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "devOptional": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -1158,6 +1584,12 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "devOptional": true }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1281,6 +1713,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1381,15 +1822,15 @@ } }, "node_modules/eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -1463,6 +1904,28 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -1581,9 +2044,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -1610,37 +2073,94 @@ "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/flat-cache/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "glob": "^7.1.3" }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/flatted": { @@ -1803,30 +2323,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -1852,9 +2348,9 @@ } }, "node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2042,18 +2538,18 @@ } }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/ignore-walk": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz", - "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz", + "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==", "dev": true, "dependencies": { "minimatch": "^9.0.0" @@ -2062,30 +2558,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2345,9 +2817,9 @@ "dev": true }, "node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -2519,6 +2991,12 @@ "semver": "bin/semver.js" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/make-fetch-happen": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", @@ -2580,15 +3058,18 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -2872,9 +3353,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.0.tgz", - "integrity": "sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -2908,12 +3389,13 @@ } }, "node_modules/node-gyp/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/node-gyp/node_modules/cacache": { @@ -2945,6 +3427,15 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/node-gyp/node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/node-gyp/node_modules/cacache/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -3054,6 +3545,18 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/node-gyp/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", @@ -3113,6 +3616,21 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/node-gyp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/node-gyp/node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -3222,9 +3740,9 @@ } }, "node_modules/npm-check-updates": { - "version": "16.14.11", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.11.tgz", - "integrity": "sha512-0MMWGbGci22Pu77bR9jRsy5qgxdQSJVqNtSyyFeubDPtbcU36z4gjEDITu26PMabFWPNkAoVfKF31M3uKUvzFg==", + "version": "16.14.14", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.14.tgz", + "integrity": "sha512-Y3ajS/Ep40jM489rLBdz9jehn/BMil5s9fA4PSr2ZJxxSmtLWCSmRqsI2IEZ9Nb3MTMu8a3s7kBs0l+JbjdkTA==", "dev": true, "dependencies": { "chalk": "^5.3.0", @@ -3280,15 +3798,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/npm-check-updates/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/npm-check-updates/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -3301,39 +3810,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/npm-check-updates/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm-check-updates/node_modules/rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", - "dev": true, - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm-check-updates/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -3673,9 +4149,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz", - "integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -4002,35 +4478,18 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "devOptional": true, + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "devOptional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4086,9 +4545,9 @@ "optional": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4286,9 +4745,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", + "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -4480,6 +4939,61 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "optional": true }, + "node_modules/ts-api-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.0.tgz", + "integrity": "sha512-d+3WxW4r8WQy2cZWpNRPPGExX8ffOLGcIhheUANKbL5Sqjbhkneki76fRAWeXkaslV2etTb4tSJBSxOsH5+CJw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tuf-js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", @@ -4527,6 +5041,25 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -4644,6 +5177,12 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "devOptional": true }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4910,6 +5449,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "devOptional": true }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 3df656f2..e003729f 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,25 @@ { "name": "node-switchbot", - "version": "1.10.0", + "version": "2.0.0", "description": "The node-switchbot is a Node.js module which allows you to control your Switchbot Devices through Bluetooth (BLE).", - "main": "./lib/switchbot.js", - "files": [ - "lib" - ], - "directories": { - "lib": "./lib" + "homepage": "https://github.com/OpenWonderLabs/node-switchbot", + "author": "OpenWonderLabs (https://github.com/OpenWonderLabs)", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/OpenWonderLabs/node-switchbot.git" }, + "main": "dist/index.js", + "type": "module", "scripts": { "check": "npm install && npm outdated", "update": "ncu -u && npm update && npm install", - "prepublishOnly": "npm run lint", - "lint": "eslint . --ext .js" + "lint": "eslint src/**/*.ts", + "build": "rimraf ./dist && tsc", + "prepublishOnly": "npm run lint && npm run build", + "postpublish": "npm run clean", + "clean": "rimraf ./dist", + "test": "npm run lint" }, "keywords": [ "switchbot", @@ -28,13 +34,6 @@ "Bluetooth smart", "Bluetooth" ], - "homepage": "https://github.com/OpenWonderLabs/node-switchbot", - "author": "OpenWonderLabs (https://github.com/OpenWonderLabs)", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/OpenWonderLabs/node-switchbot.git" - }, "readmeFilename": "README.md", "dependencies": { "@abandonware/noble": "^1.9.2-23" @@ -43,7 +42,13 @@ "@abandonware/bluetooth-hci-socket": "^0.5.3-10" }, "devDependencies": { - "eslint": "^8.54.0", - "npm-check-updates": "^16.14.11" + "@types/node": "^20.11.16", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint": "^8.56.0", + "npm-check-updates": "^16.14.14", + "rimraf": "^5.0.5", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" } } diff --git a/lib/switchbot-advertising.js b/src/advertising.ts similarity index 67% rename from lib/switchbot-advertising.js rename to src/advertising.ts index dd0df3a3..d48b4de8 100644 --- a/lib/switchbot-advertising.js +++ b/src/advertising.ts @@ -1,8 +1,7 @@ -"use strict"; +import { Buffer } from 'buffer'; -const { Buffer } = require('buffer'); +export class Advertising { -class SwitchbotAdvertising { constructor() {} /* ------------------------------------------------------------------ @@ -62,7 +61,14 @@ class SwitchbotAdvertising { * If the specified `Peripheral` does not represent any switchbot * device, this method will return `null`. * ---------------------------------------------------------------- */ - parse(peripheral, onlog) { + /** + * Parses the advertisement data of a peripheral device. + * + * @param peripheral - The peripheral device. + * @param onlog - The logging function. + * @returns The parsed data of the peripheral device. + */ + static parse(peripheral, onlog?) { const ad = peripheral.advertisement; if (!ad || !ad.serviceData) { return null; @@ -81,69 +87,69 @@ class SwitchbotAdvertising { return null; } - const model = buf.slice(0, 1).toString("utf8"); - let sd = null; - - if (model === "H") { - sd = this._parseServiceDataForWoHand(buf, onlog);//WoHand - } else if (model === "T") { - sd = this._parseServiceDataForWoSensorTH(buf, onlog);//WoSensorTH - } else if (model === "e") { - sd = this._parseServiceDataForWoHumi(buf, onlog);//WoHumi - } else if (model === "s") { - sd = this._parseServiceDataForWoPresence(buf, onlog);//WoPresence - } else if (model === "d") { - sd = this._parseServiceDataForWoContact(buf, onlog);//WoContact - } else if (model === "c" || model === "{") { - sd = this._parseServiceDataForWoCurtain(buf, onlog);// WoCurtain - } else if (model === "x") { - sd = this._parseServiceDataForWoBlindTilt(buf, onlog);// WoBlindTilt - } else if (model === "u") { - sd = this._parseServiceDataForWoBulb(manufacturerData, onlog);// WoBulb - } else if (model === "g") { - sd = this._parseServiceDataForWoPlugMiniUS(manufacturerData, onlog); // WoPlugMini (US) - } else if (model === "j") { - sd = this._parseServiceDataForWoPlugMiniJP(manufacturerData, onlog);// WoPlugMini (JP) - } else if (model === "o") { - sd = this._parseServiceDataForWoSmartLock(manufacturerData, onlog);// WoSmartLock - } else if (model === "i") { - sd = this._parseServiceDataForWoSensorTHPlus(buf, onlog);// WoMeterPlus - } else if (model === "r") { - sd = this._parseServiceDataForWoStrip(buf, onlog);// WoStrip - } else if (model === "w") { - sd = this._parseServiceDataForWoIOSensorTH(buf, manufacturerData, onlog); // Indoor/Outdoor Thermo-Hygrometer + const model = buf.slice(0, 1).toString('utf8'); + let sd; + + if (model === 'H') { + sd = this.parseServiceDataForWoHand(buf, onlog);//WoHand + } else if (model === 'T') { + sd = this.parseServiceDataForWoSensorTH(buf, onlog);//WoSensorTH + } else if (model === 'e') { + sd = this.parseServiceDataForWoHumi(buf, onlog);//WoHumi + } else if (model === 's') { + sd = this.parseServiceDataForWoPresence(buf, onlog);//WoPresence + } else if (model === 'd') { + sd = this.parseServiceDataForWoContact(buf, onlog);//WoContact + } else if (model === 'c' || model === '{') { + sd = this.parseServiceDataForWoCurtain(buf, onlog);// WoCurtain + } else if (model === 'x') { + sd = this.parseServiceDataForWoBlindTilt(buf, onlog);// WoBlindTilt + } else if (model === 'u') { + sd = this.parseServiceDataForWoBulb(manufacturerData, onlog);// WoBulb + } else if (model === 'g') { + sd = this.parseServiceDataForWoPlugMiniUS(manufacturerData, onlog); // WoPlugMini (US) + } else if (model === 'j') { + sd = this.parseServiceDataForWoPlugMiniJP(manufacturerData, onlog);// WoPlugMini (JP) + } else if (model === 'o') { + sd = this.parseServiceDataForWoSmartLock(manufacturerData, onlog);// WoSmartLock + } else if (model === 'i') { + sd = this.parseServiceDataForWoSensorTHPlus(buf, onlog);// WoMeterPlus + } else if (model === 'r') { + sd = this.parseServiceDataForWoStrip(buf, onlog);// WoStrip + } else if (model === 'w') { + sd = this.parseServiceDataForWoIOSensorTH(buf, manufacturerData, onlog); // Indoor/Outdoor Thermo-Hygrometer } else { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[parseAdvertising.${peripheral.id}] return null, model "${model}" not available!` + `[parseAdvertising.${peripheral.id}] return null, model "${model}" not available!`, ); } return null; } if (!sd) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[parseAdvertising.${peripheral.id}.${model}] return null, parsed serviceData empty!` + `[parseAdvertising.${peripheral.id}.${model}] return null, parsed serviceData empty!`, ); } return null; } - let address = peripheral.address || ""; - if (address === "") { - address = peripheral.advertisement.manufacturerData || ""; - if (address !== "") { + let address = peripheral.address || ''; + if (address === '') { + address = peripheral.advertisement.manufacturerData || ''; + if (address !== '') { const str = peripheral.advertisement.manufacturerData - .toString("hex") + .toString('hex') .slice(4, 16); address = str.substr(0, 2); - for (var i = 2; i < str.length; i += 2) { - address = address + ":" + str.substr(i, 2); + for (let i = 2; i < str.length; i += 2) { + address = address + ':' + str.substr(i, 2); } // console.log("address", typeof(address), address); } } else { - address = address.replace(/-/g, ":"); + address = address.replace(/-/g, ':'); } const data = { id: peripheral.id, @@ -152,21 +158,21 @@ class SwitchbotAdvertising { serviceData: sd, }; - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( `[parseAdvertising.${peripheral.id}.${model}] return ${JSON.stringify( - data - )}` + data, + )}`, ); } return data; } - _parseServiceDataForWoHand(buf, onlog) { + static parseServiceDataForWoHand(buf, onlog) { if (buf.length !== 3) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoHand] Buffer length ${buf.length} !== 3!` + `[parseServiceDataForWoHand] Buffer length ${buf.length} !== 3!`, ); } return null; @@ -179,8 +185,8 @@ class SwitchbotAdvertising { const battery = byte2 & 0b01111111; // % const data = { - model: "H", - modelName: "WoHand", + model: 'H', + modelName: 'WoHand', mode: mode, state: state, battery: battery, @@ -189,11 +195,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoSensorTH(buf, onlog) { + static parseServiceDataForWoSensorTH(buf, onlog) { if (buf.length !== 6) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoSensorTH] Buffer length ${buf.length} !== 6!` + `[parseServiceDataForWoSensorTH] Buffer length ${buf.length} !== 6!`, ); } return null; @@ -208,8 +214,8 @@ class SwitchbotAdvertising { const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; const data = { - model: "T", - modelName: "WoSensorTH", + model: 'T', + modelName: 'WoSensorTH', temperature: { c: temp_c, f: temp_f, @@ -222,11 +228,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoHumi(buf, onlog) { + static parseServiceDataForWoHumi(buf, onlog) { if (buf.length !== 8) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoHumi] Buffer length ${buf.length} !== 8!` + `[parseServiceDataForWoHumi] Buffer length ${buf.length} !== 8!`, ); } return null; @@ -240,8 +246,8 @@ class SwitchbotAdvertising { const percentage = byte4 & 0b01111111; // 0-100%, 101/102/103 - Quick gear 1/2/3 const data = { - model: "e", - modelName: "WoHumi", + model: 'e', + modelName: 'WoHumi', onState: onState, autoMode: autoMode, percentage: autoMode ? 0 : percentage, @@ -250,11 +256,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoPresence(buf, onlog) { + static parseServiceDataForWoPresence(buf, onlog) { if (buf.length !== 6) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoPresence] Buffer length ${buf.length} !== 6!` + `[parseServiceDataForWoPresence] Buffer length ${buf.length} !== 6!`, ); } return null; @@ -274,8 +280,8 @@ class SwitchbotAdvertising { const is_light = byte5 & 0b00000010 ? true : false; const data = { - model: "s", - modelName: "WoMotion", + model: 's', + modelName: 'WoMotion', tested: tested, movement: movement, battery: battery, @@ -283,18 +289,18 @@ class SwitchbotAdvertising { iot: iot, sense_distance: sense_distance, lightLevel: - lightLevel == 1 ? "dark" : lightLevel == 2 ? "bright" : "unknown", + lightLevel === 1 ? 'dark' : lightLevel === 2 ? 'bright' : 'unknown', is_light: is_light, }; return data; } - _parseServiceDataForWoContact(buf, onlog) { + static parseServiceDataForWoContact(buf, onlog) { if (buf.length !== 9) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoContact] Buffer length ${buf.length} !== 9!` + `[parseServiceDataForWoContact] Buffer length ${buf.length} !== 9!`, ); } return null; @@ -309,37 +315,37 @@ class SwitchbotAdvertising { 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 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", + model: 'd', + modelName: 'WoContact', movement: movement, tested: tested, battery: battery, contact_open: contact_open, contact_timeout: contact_timeout, - lightLevel: lightLevel == 0 ? "dark" : "bright", + lightLevel: lightLevel === 0 ? 'dark' : 'bright', button_count: button_count, doorState: - hallState == 0 - ? "close" - : hallState == 1 - ? "open" - : "timeout no closed", + hallState === 0 + ? 'close' + : hallState === 1 + ? 'open' + : 'timeout no closed', }; return data; } - _parseServiceDataForWoCurtain(buf, onlog) { + static parseServiceDataForWoCurtain(buf, onlog) { if (buf.length !== 5 && buf.length !== 6) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoCurtain] Buffer length ${buf.length} !== 5 or 6!` + `[parseServiceDataForWoCurtain] Buffer length ${buf.length} !== 5 or 6!`, ); } return null; @@ -355,11 +361,11 @@ class SwitchbotAdvertising { 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 model = buf.slice(0, 1).toString('utf8'); const data = { model: model, - modelName: "WoCurtain", + modelName: 'WoCurtain', calibration: calibration, battery: battery, inMotion: inMotion, @@ -371,27 +377,27 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoBlindTilt(buf, onlog) { + static parseServiceDataForWoBlindTilt(buf, onlog) { if (buf.length !== 5 && buf.length !== 6) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoBlindTilt] Buffer length ${buf.length} !== 5 or 6!` + `[parseServiceDataForWoBlindTilt] Buffer length ${buf.length} !== 5 or 6!`, ); } return null; } - let byte1 = buf.readUInt8(1); - let byte2 = buf.readUInt8(2); - - let calibration = byte1 & 0b00000001 ? true : false; // Whether the calibration is completed - let battery = byte2 & 0b01111111; // % - let inMotion = byte2 & 0b10000000 ? true : false; - let tilt = byte2 & 0b01111111; // current tilt % (100 - _tilt) if reverse else _tilt, - let lightLevel = (byte1 >> 4) & 0b00001111; // light sensor level (1-10) - - let data = { - model: "x", - modelName: "WoBlindTilt", + 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, @@ -402,11 +408,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoBulb(manufacturerData, onlog) { + static parseServiceDataForWoBulb(manufacturerData, onlog) { if (manufacturerData.length !== 13) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoBulb] Buffer length ${manufacturerData.length} !== 13!` + `[parseServiceDataForWoBulb] Buffer length ${manufacturerData.length} !== 13!`, ); } return null; @@ -436,8 +442,8 @@ class SwitchbotAdvertising { const loop_index = byte10 & 0b11111110; const data = { - model: "u", - modelName: "WoBulb", + model: 'u', + modelName: 'WoBulb', color_temperature: color_temperature, power: power, state: state, @@ -455,11 +461,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoPlugMiniUS(manufacturerData, onlog) { + static parseServiceDataForWoPlugMiniUS(manufacturerData, onlog) { if (manufacturerData.length !== 14) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoPlugMiniUS] Buffer length ${manufacturerData.length} should be 14` + `[parseServiceDataForWoPlugMiniUS] Buffer length ${manufacturerData.length} should be 14`, ); } return null; @@ -470,7 +476,7 @@ class SwitchbotAdvertising { 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 state = byte9 === 0x00 ? 'off' : byte9 === 0x80 ? 'on' : null; const delay = !!(byte10 & 0b00000001); const timer = !!(byte10 & 0b00000010); const syncUtcTime = !!(byte10 & 0b00000100); @@ -480,8 +486,8 @@ class SwitchbotAdvertising { // TODO: voltage ??? const data = { - model: "g", - modelName: "WoPlugMini", + model: 'g', + modelName: 'WoPlugMini', state: state, delay: delay, timer: timer, @@ -494,11 +500,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoPlugMiniJP(manufacturerData, onlog) { + static parseServiceDataForWoPlugMiniJP(manufacturerData, onlog) { if (manufacturerData.length !== 14) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoPlugMiniJP] Buffer length ${manufacturerData.length} should be 14` + `[parseServiceDataForWoPlugMiniJP] Buffer length ${manufacturerData.length} should be 14`, ); } return null; @@ -509,7 +515,7 @@ class SwitchbotAdvertising { 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 state = byte9 === 0x00 ? 'off' : byte9 === 0x80 ? 'on' : null; const delay = !!(byte10 & 0b00000001); const timer = !!(byte10 & 0b00000010); const syncUtcTime = !!(byte10 & 0b00000100); @@ -519,8 +525,8 @@ class SwitchbotAdvertising { // TODO: voltage ??? const data = { - model: "j", - modelName: "WoPlugMini", + model: 'j', + modelName: 'WoPlugMini', state: state, delay: delay, timer: timer, @@ -533,11 +539,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoSmartLock(manufacturerData, onlog) { + static parseServiceDataForWoSmartLock(manufacturerData, onlog) { if (manufacturerData.length !== 6) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoSmartLock] Buffer length ${manufacturerData.length} !== 6!` + `[parseServiceDataForWoSmartLock] Buffer length ${manufacturerData.length} !== 6!`, ); } return null; @@ -555,11 +561,11 @@ class SwitchbotAdvertising { 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 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; @@ -568,8 +574,8 @@ class SwitchbotAdvertising { const auto_lock_paused = byte8 & 0b00000010 ? true : false; const data = { - model: "o", - modelName: "WoSmartLock", + model: 'o', + modelName: 'WoSmartLock', battery: battery, calibration: calibration, status: status, @@ -584,11 +590,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoSensorTHPlus(buf, onlog) { + static parseServiceDataForWoSensorTHPlus(buf, onlog) { if (buf.length !== 6) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoSensorTHPlus] Buffer length ${buf.length} !== 6!` + `[parseServiceDataForWoSensorTHPlus] Buffer length ${buf.length} !== 6!`, ); } return null; @@ -603,8 +609,8 @@ class SwitchbotAdvertising { const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; const data = { - model: "i", - modelName: "WoSensorTHPlus", + model: 'i', + modelName: 'WoSensorTHPlus', temperature: { c: temp_c, f: temp_f, @@ -617,11 +623,11 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoStrip(buf, onlog) { + static parseServiceDataForWoStrip(buf, onlog) { if (buf.length !== 18) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoStrip] Buffer length ${buf.length} !== 18!` + `[parseServiceDataForWoStrip] Buffer length ${buf.length} !== 18!`, ); } return null; @@ -649,8 +655,8 @@ class SwitchbotAdvertising { const loop_index = byte10 & 0b11111110; const data = { - model: "r", - modelName: "WoStrip", + model: 'r', + modelName: 'WoStrip', state: state, brightness: brightness, red: red, @@ -666,19 +672,19 @@ class SwitchbotAdvertising { return data; } - _parseServiceDataForWoIOSensorTH(serviceDataBuf, manufacturerDataBuf, onlog) { + static parseServiceDataForWoIOSensorTH(serviceDataBuf, manufacturerDataBuf, onlog) { if (serviceDataBuf.length !== 3) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoIOSensorTH] Service Data Buffer length ${serviceDataBuf.length} !== 3!` + `[parseServiceDataForWoIOSensorTH] Service Data Buffer length ${serviceDataBuf.length} !== 3!`, ); } return null; } if (manufacturerDataBuf.length !== 14) { - if (onlog && typeof onlog === "function") { + if (onlog && typeof onlog === 'function') { onlog( - `[_parseServiceDataForWoIOSensorTH] Manufacturer Data Buffer length ${manufacturerDataBuf.length} !== 14!` + `[parseServiceDataForWoIOSensorTH] Manufacturer Data Buffer length ${manufacturerDataBuf.length} !== 14!`, ); } return null; @@ -694,8 +700,8 @@ class SwitchbotAdvertising { const temp_f = Math.round(((temp_c * 9 / 5) + 32) * 10) / 10; const data = { - model: "w", - modelName: "WoIOSensorTH", + model: 'w', + modelName: 'WoIOSensorTH', temperature: { c: temp_c, f: temp_f, @@ -709,5 +715,3 @@ class SwitchbotAdvertising { return data; } } - -module.exports = new SwitchbotAdvertising(); diff --git a/lib/switchbot-device.js b/src/device.ts similarity index 70% rename from lib/switchbot-device.js rename to src/device.ts index 9f0d48cb..25ef7743 100644 --- a/lib/switchbot-device.js +++ b/src/device.ts @@ -1,11 +1,36 @@ -"use strict"; - -const { Buffer } = require('buffer'); - -const parameterChecker = require("./parameter-checker.js"); -const switchbotAdvertising = require("./switchbot-advertising.js"); - -class SwitchbotDevice { +import { Buffer } from 'buffer'; + +import { ParameterChecker } from './parameter-checker.js'; +import { Advertising } from './advertising.js'; + +type ad = { + id: any; + address: any; + rssi: any; + serviceData: any; +} | null; + +export class SwitchbotDevice { + _peripheral; + _noble; + _chars; + _SERV_UUID_PRIMARY; + _CHAR_UUID_WRITE; + _CHAR_UUID_NOTIFY; + _CHAR_UUID_DEVICE; + _READ_TIMEOUT_MSEC; + _WRITE_TIMEOUT_MSEC; + _COMMAND_TIMEOUT_MSEC; + _id; + _address; + _model; + _modelName; + _was_connected_explicitly; + _connected; + _onconnect: () => void; + _ondisconnect: () => void; + _ondisconnect_internal: () => void; + _onnotify_internal: () => void; /* ------------------------------------------------------------------ * Constructor * @@ -19,21 +44,14 @@ class SwitchbotDevice { this._noble = noble; this._chars = null; - 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; - + this._SERV_UUID_PRIMARY = 'cba20d00224d11e69fb80002a5d5c51b'; + this._CHAR_UUID_WRITE = 'cba20002224d11e69fb80002a5d5c51b'; // Save the device information - const ad = switchbotAdvertising.parse(peripheral); - this._id = ad.id; - this._address = ad.address; - this._model = ad.serviceData.model; - this._modelName = ad.serviceData.modelName; + const ad: ad = Advertising.parse(peripheral); + this._id = ad?.id; + this._address = ad?.address; + this._model = ad?.serviceData.model; + this._modelName = ad?.serviceData.modelName; this._was_connected_explicitly = false; this._connected = false; @@ -48,18 +66,22 @@ class SwitchbotDevice { get id() { return this._id; } + get address() { return this._address; } + get model() { return this._model; } + get modelName() { return this._modelName; } + get connectionState() { - if (!this._connected && this._peripheral.state === "disconnecting") { - return "disconnected"; + if (!this._connected && this._peripheral.state === 'disconnecting') { + return 'disconnected'; } else { return this._peripheral.state; } @@ -67,14 +89,15 @@ class SwitchbotDevice { // Setters set onconnect(func) { - if (!func || typeof func !== "function") { - throw new Error("The `onconnect` must be a function."); + if (!func || typeof func !== 'function') { + throw new Error('The `onconnect` must be a function.'); } this._onconnect = func; } + set ondisconnect(func) { - if (!func || typeof func !== "function") { - throw new Error("The `ondisconnect` must be a function."); + if (!func || typeof func !== 'function') { + throw new Error('The `ondisconnect` must be a function.'); } this._ondisconnect = func; } @@ -96,36 +119,36 @@ class SwitchbotDevice { } _connect() { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { // Check the bluetooth state - if (this._noble.state !== "poweredOn") { + if (this._noble.state !== 'poweredOn') { reject( new Error( - "The Bluetooth status is " + this._noble.state + ", not poweredOn." - ) + 'The Bluetooth status is ' + this._noble.state + ', not poweredOn.', + ), ); return; } // Check the connection state const state = this.connectionState; - if (state === "connected") { + if (state === 'connected') { resolve(); return; - } else if (state === "connecting" || state === "disconnecting") { + } else if (state === 'connecting' || state === 'disconnecting') { reject( - new Error("Now " + state + ". Wait for a few seconds then try again.") + new Error('Now ' + state + '. Wait for a few seconds then try again.'), ); return; } // Set event handlers for events fired on the `Peripheral` object - this._peripheral.once("connect", () => { + this._peripheral.once('connect', () => { this._connected = true; this._onconnect(); }); - this._peripheral.once("disconnect", () => { + this._peripheral.once('disconnect', () => { this._connected = false; this._chars = null; this._peripheral.removeAllListeners(); @@ -158,11 +181,11 @@ class SwitchbotDevice { _getCharacteristics() { return new Promise((resolve, reject) => { // Set timeout timer - let timer = setTimeout(() => { + let timer: NodeJS.Timeout | null = setTimeout(() => { this._ondisconnect_internal = () => {}; timer = null; reject( - new Error("Failed to discover services and characteristics: TIMEOUT") + new Error('Failed to discover services and characteristics: TIMEOUT'), ); }, 5000); @@ -175,8 +198,8 @@ class SwitchbotDevice { } reject( new Error( - "Failed to discover services and characteristics: DISCONNECTED" - ) + 'Failed to discover services and characteristics: DISCONNECTED', + ), ); }; @@ -184,7 +207,7 @@ class SwitchbotDevice { (async () => { const service_list = await this._discoverServices(); if (!timer) { - throw new Error(""); + throw new Error(''); } const chars = { @@ -193,9 +216,9 @@ class SwitchbotDevice { device: null, }; - for (let service of service_list) { + for (const service of service_list as any[]) { const char_list = await this._discoverCharacteristics(service); - for (let char of char_list) { + for (const char of char_list as any[]) { if (char.uuid === this._CHAR_UUID_WRITE) { chars.write = char; } else if (char.uuid === this._CHAR_UUID_NOTIFY) { @@ -210,7 +233,7 @@ class SwitchbotDevice { if (chars.write && chars.notify) { resolve(chars); } else { - reject(new Error("No characteristic was found.")); + reject(new Error('No characteristic was found.')); } })().catch((error) => { if (timer) { @@ -234,7 +257,7 @@ class SwitchbotDevice { } let service = null; - for (let s of service_list) { + for (const s of service_list) { if (s.uuid === this._SERV_UUID_PRIMARY) { service = s; break; @@ -243,7 +266,7 @@ class SwitchbotDevice { if (service) { resolve(service_list); } else { - reject(new Error("No service was found.")); + reject(new Error('No service was found.')); } }); }); @@ -262,10 +285,10 @@ class SwitchbotDevice { } _subscribe() { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const char = this._chars.notify; if (!char) { - reject(new Error("No notify characteristic was found.")); + reject(new Error('No notify characteristic was found.')); return; } char.subscribe((error) => { @@ -273,8 +296,8 @@ class SwitchbotDevice { reject(error); return; } - char.on("data", (buf) => { - this._onnotify_internal(buf); + char.on('data', () => { // Remove the argument passed to the _onnotify_internal function + this._onnotify_internal(); }); resolve(); }); @@ -282,7 +305,7 @@ class SwitchbotDevice { } _unsubscribe() { - return new Promise((resolve) => { + return new Promise((resolve) => { const char = this._chars.notify; if (!char) { resolve(); @@ -307,16 +330,16 @@ class SwitchbotDevice { * Nothing will be passed to the `resolve()`. * ---------------------------------------------------------------- */ disconnect() { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this._was_connected_explicitly = false; // Check the connection state const state = this._peripheral.state; - if (state === "disconnected") { + if (state === 'disconnected') { resolve(); return; - } else if (state === "connecting" || state === "disconnecting") { + } else if (state === 'connecting' || state === 'disconnecting') { reject( - new Error("Now " + state + ". Wait for a few seconds then try again.") + new Error('Now ' + state + '. Wait for a few seconds then try again.'), ); return; } @@ -333,7 +356,7 @@ class SwitchbotDevice { _disconnect() { if (this._was_connected_explicitly) { - return new Promise((resolve) => { + return new Promise((resolve) => { resolve(); }); } else { @@ -354,21 +377,21 @@ class SwitchbotDevice { * ---------------------------------------------------------------- */ getDeviceName() { return new Promise((resolve, reject) => { - let name = ""; + let name = ''; this._connect() .then(() => { if (!this._chars.device) { // Some models of Bot don't seem to support this characteristic UUID throw new Error( - "The device does not support the characteristic UUID 0x" + + 'The device does not support the characteristic UUID 0x' + this._CHAR_UUID_DEVICE + - "." + '.', ); } return this._read(this._chars.device); }) - .then((buf) => { - name = buf.toString("utf8"); + .then((buf: any) => { + name = buf.toString('utf8'); return this._disconnect(); }) .then(() => { @@ -393,29 +416,30 @@ class SwitchbotDevice { * Nothing will be passed to the `resolve()`. * ---------------------------------------------------------------- */ setDeviceName(name) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { // Check the parameters - const valid = parameterChecker.check( + const valid = ParameterChecker.check( { name: name }, { - name: { required: true, type: "string", minBytes: 1, maxBytes: 100 }, - } + name: { required: true, type: 'string', minBytes: 1, maxBytes: 100 }, + }, + true, // Add the required argument ); if (!valid) { - reject(new Error(parameterChecker.error.message)); + reject(new Error(ParameterChecker.error.message)); return; } - const buf = Buffer.from(name, "utf8"); + const buf = Buffer.from(name, 'utf8'); this._connect() .then(() => { if (!this._chars.device) { // Some models of Bot don't seem to support this characteristic UUID throw new Error( - "The device does not support the characteristic UUID 0x" + + 'The device does not support the characteristic UUID 0x' + this._CHAR_UUID_DEVICE + - "." + '.', ); } return this._write(this._chars.device, buf); @@ -438,16 +462,16 @@ class SwitchbotDevice { _command(req_buf) { return new Promise((resolve, reject) => { if (!Buffer.isBuffer(req_buf)) { - reject(new Error("The specified data is not acceptable for writing.")); + reject(new Error('The specified data is not acceptable for writing.')); return; } - let res_buf = null; + let res_buf; this._connect() .then(() => { if (!this._chars) { - return reject(new Error("No characteristics available.")); + return reject(new Error('No characteristics available.')); } return this._write(this._chars.write, req_buf); }) @@ -469,16 +493,18 @@ class SwitchbotDevice { _waitCommandResponse() { return new Promise((resolve, reject) => { - let timer = setTimeout(() => { - timer = null; + const buf: Buffer | null = null; + + let timer: NodeJS.Timeout | undefined = setTimeout(() => { + timer = undefined; this._onnotify_internal = () => {}; - reject(new Error("COMMAND_TIMEOUT")); + reject(new Error('COMMAND_TIMEOUT')); }, this._COMMAND_TIMEOUT_MSEC); - this._onnotify_internal = (buf) => { + this._onnotify_internal = () => { if (timer) { clearTimeout(timer); - timer = null; + timer = undefined; } this._onnotify_internal = () => {}; resolve(buf); @@ -490,15 +516,15 @@ class SwitchbotDevice { _read(char) { return new Promise((resolve, reject) => { // Set a timeout timer - let timer = setTimeout(() => { - reject("READ_TIMEOUT"); + let timer: NodeJS.Timeout | undefined = setTimeout(() => { + reject('READ_TIMEOUT'); }, this._READ_TIMEOUT_MSEC); - // Read charcteristic data + // Read characteristic data char.read((error, buf) => { if (timer) { clearTimeout(timer); - timer = null; + timer = undefined; } if (error) { reject(error); @@ -511,17 +537,17 @@ class SwitchbotDevice { // Write the specified Buffer data to the specified characteristic _write(char, buf) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { // Set a timeout timer - let timer = setTimeout(() => { - reject("WRITE_TIMEOUT"); + let timer: NodeJS.Timeout | undefined = setTimeout(() => { + reject('WRITE_TIMEOUT'); }, this._WRITE_TIMEOUT_MSEC); - // write charcteristic data + // write characteristic data char.write(buf, false, (error) => { if (timer) { clearTimeout(timer); - timer = null; + timer = undefined; } if (error) { reject(error); @@ -532,5 +558,3 @@ class SwitchbotDevice { }); } } - -module.exports = SwitchbotDevice; diff --git a/lib/switchbot-device-woblindtilt.js b/src/device/woblindtilt.ts similarity index 71% rename from lib/switchbot-device-woblindtilt.js rename to src/device/woblindtilt.ts index 8684973d..58a8e38c 100644 --- a/lib/switchbot-device-woblindtilt.js +++ b/src/device/woblindtilt.ts @@ -1,10 +1,12 @@ -"use strict"; +/* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * woblindtilt.ts: Switchbot BLE API registration. + */ +import { Buffer } from 'buffer'; -const { Buffer } = require('buffer'); +import { SwitchbotDevice } from '../switchbot.js'; -let SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoBlindTilt extends SwitchbotDevice { +export class WoBlindTilt extends SwitchbotDevice { /* ------------------------------------------------------------------ * open() * - Open the blindtilt @@ -62,23 +64,23 @@ class SwitchbotDeviceWoBlindTilt extends SwitchbotDevice { * Nothing will be passed to the `resolve()`. * ---------------------------------------------------------------- */ runToPos(percent, mode) { - if (typeof percent != "number") { + if (typeof percent !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target position percentage is incorrent: " + - typeof percent - ) + 'The type of target position percentage is incorrect: ' + + typeof percent, + ), ); }); } - if (mode == null) { + if (mode === null) { mode = 0xff; } else { - if (typeof mode != "number") { + if (typeof mode !== 'number') { return new Promise((resolve, reject) => { reject( - new Error("The type of running mode is incorrent: " + typeof mode) + new Error('The type of running mode is incorrect: ' + typeof mode), ); }); } @@ -95,18 +97,18 @@ class SwitchbotDeviceWoBlindTilt extends SwitchbotDevice { } _operateBlindTilt(bytes) { - return new Promise((resolve, reject) => { - let req_buf = Buffer.from(bytes); + return new Promise((resolve, reject) => { + const req_buf = Buffer.from(bytes); this._command(req_buf) - .then((res_buf) => { - let code = res_buf.readUInt8(0); - if (res_buf.length === 3 && code === 0x01) { + .then((res_buf: unknown) => { + const code = (res_buf as Buffer).readUInt8(0); + if ((res_buf as Buffer).length === 3 && code === 0x01) { resolve(); } else { reject( new Error( - "The device returned an error: 0x" + res_buf.toString("hex") - ) + 'The device returned an error: 0x' + (res_buf as Buffer).toString('hex'), + ), ); } }) @@ -116,5 +118,3 @@ class SwitchbotDeviceWoBlindTilt extends SwitchbotDevice { }); } } - -module.exports = SwitchbotDeviceWoBlindTilt; diff --git a/lib/switchbot-device-wobulb.js b/src/device/wobulb.ts similarity index 65% rename from lib/switchbot-device-wobulb.js rename to src/device/wobulb.ts index 9e0826ec..8910ff23 100644 --- a/lib/switchbot-device-wobulb.js +++ b/src/device/wobulb.ts @@ -1,13 +1,15 @@ -"use strict"; - -const { Buffer } = require('buffer'); +/* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * wobulb.ts: Switchbot BLE API registration. + */ +import { Buffer } from 'buffer'; -const SwitchbotDevice = require("./switchbot-device.js"); +import { SwitchbotDevice } from '../switchbot.js'; /** * @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/colorbulb.md */ -class SwitchbotDeviceWoBulb extends SwitchbotDevice { +export class WoBulb extends SwitchbotDevice { /** * @returns {Promise} resolves with a boolean that tells whether the plug in ON(true) or OFF(false) */ @@ -20,7 +22,7 @@ class SwitchbotDeviceWoBulb extends SwitchbotDevice { */ _setState(reqByteArray) { const base = [0x57, 0x0f, 0x47, 0x01]; - return this._operateBot([].concat(base, reqByteArray)); + return this._operateBot(base.concat(reqByteArray)); } /** @@ -38,16 +40,16 @@ class SwitchbotDeviceWoBulb extends SwitchbotDevice { } /** - * @returns {Promise} resolves with brightness percent + * @returns {Promise} resolves with brightness percent */ setBrightness(brightness) { - if (typeof brightness != "number") { + if (typeof brightness !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target brightness percentage is incorrent: " + - typeof brightness - ) + 'The type of target brightness percentage is incorrect: ' + + typeof brightness, + ), ); }); } @@ -60,16 +62,16 @@ class SwitchbotDeviceWoBulb extends SwitchbotDevice { } /** - * @returns {Promise} resolves with brightness percent + * @returns {Promise} resolves with color_temperature percent */ setColorTemperature(color_temperature) { - if (typeof color_temperature != "number") { + if (typeof color_temperature !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target brightness percentage is incorrent: " + - typeof brightness - ) + 'The type of target color_temperature percentage is incorrect: ' + + typeof color_temperature, + ), ); }); } @@ -82,46 +84,46 @@ class SwitchbotDeviceWoBulb extends SwitchbotDevice { } /** - * @returns {Promise} resolves with brightness percent + * @returns {Promise} resolves with brightness percent */ - setRGB(brightness, red, green, blue) { - if (typeof brightness != "number") { + setRGB(brightness, red, green, blue) { + if (typeof brightness !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target brightness percentage is incorrent: " + - typeof brightness - ) + 'The type of target brightness percentage is incorrect: ' + + typeof brightness, + ), ); }); } - if (typeof red != "number") { + if (typeof red !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target red is incorrent: " + - typeof red - ) + 'The type of target red is incorrect: ' + + typeof red, + ), ); }); } - if (typeof green != "number") { + if (typeof green !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target green is incorrent: " + - typeof green - ) + 'The type of target green is incorrect: ' + + typeof green, + ), ); }); } - if (typeof blue != "number") { + if (typeof blue !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target blue is incorrent: " + - typeof blue - ) + 'The type of target blue is incorrect: ' + + typeof blue, + ), ); }); } @@ -156,7 +158,7 @@ class SwitchbotDeviceWoBulb extends SwitchbotDevice { return new Promise((resolve, reject) => { this._command(req_buf) .then((res_bytes) => { - const res_buf = Buffer.from(res_bytes); + const res_buf = Buffer.from(res_bytes as ArrayBuffer | SharedArrayBuffer); if (res_buf.length === 2) { const code = res_buf.readUInt8(1); if (code === 0x00 || code === 0x80) { @@ -165,16 +167,16 @@ class SwitchbotDeviceWoBulb extends SwitchbotDevice { } else { reject( new Error( - "The device returned an error: 0x" + res_buf.toString("hex") - ) + 'The device returned an error: 0x' + res_buf.toString('hex'), + ), ); } } else { reject( new Error( - "Expecting a 2-byte response, got instead: 0x" + - res_buf.toString("hex") - ) + 'Expecting a 2-byte response, got instead: 0x' + + res_buf.toString('hex'), + ), ); } }) @@ -184,5 +186,3 @@ class SwitchbotDeviceWoBulb extends SwitchbotDevice { }); } } - -module.exports = SwitchbotDeviceWoBulb; diff --git a/src/device/wocontact.ts b/src/device/wocontact.ts new file mode 100644 index 00000000..9ccd4106 --- /dev/null +++ b/src/device/wocontact.ts @@ -0,0 +1,7 @@ +/* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * wocontact.ts: Switchbot BLE API registration. + */ +import { SwitchbotDevice } from '../switchbot.js'; + +export class WoContact extends SwitchbotDevice {} diff --git a/lib/switchbot-device-wocurtain.js b/src/device/wocurtain.ts similarity index 67% rename from lib/switchbot-device-wocurtain.js rename to src/device/wocurtain.ts index abc43e50..6b15a524 100644 --- a/lib/switchbot-device-wocurtain.js +++ b/src/device/wocurtain.ts @@ -1,16 +1,18 @@ -"use strict"; +/* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * wocurtain.ts: Switchbot BLE API registration. + */ +import { Buffer } from 'buffer'; -const { Buffer } = require('buffer'); +import { SwitchbotDevice } from '../switchbot.js'; -const SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoCurtain extends SwitchbotDevice { +export class WoCurtain extends SwitchbotDevice { /* ------------------------------------------------------------------ * open() * - Open the curtain * * [Arguments] - * - mode | number | Opetional | runing mode (0x01 = QuietDrift, 0xff = Default) + * - mode | number | Optional | runing mode (0x01 = QuietDrift, 0xff = Default) * * [Return value] * - Promise object @@ -25,7 +27,7 @@ class SwitchbotDeviceWoCurtain extends SwitchbotDevice { * - close the curtain * * [Arguments] - * - mode | number | Opetional | runing mode (0x01 = QuietDrift, 0xff = Default) + * - mode | number | Optional | runing mode (0x01 = QuietDrift, 0xff = Default) * * [Return value] * - Promise object @@ -56,27 +58,27 @@ class SwitchbotDeviceWoCurtain extends SwitchbotDevice { * * [Arguments] * - percent | number | Required | the percentage of target position - * - mode | number | Opetional | runing mode (0x01 = QuietDrift, 0xff = Default) + * - mode | number | Optional | runing mode (0x01 = QuietDrift, 0xff = Default) * * [Return value] * - Promise object * Nothing will be passed to the `resolve()`. * ---------------------------------------------------------------- */ runToPos(percent, mode = 0xff) { - if (typeof percent != "number") { + if (typeof percent !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target position percentage is incorrect: " + - typeof percent - ) + 'The type of target position percentage is incorrect: ' + + typeof percent, + ), ); }); } - if (typeof mode != "number") { + if (typeof mode !== 'number') { return new Promise((resolve, reject) => { reject( - new Error("The type of running mode is incorrect: " + typeof mode) + new Error('The type of running mode is incorrect: ' + typeof mode), ); }); } @@ -92,18 +94,18 @@ class SwitchbotDeviceWoCurtain extends SwitchbotDevice { } _operateCurtain(bytes) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const req_buf = Buffer.from(bytes); this._command(req_buf) - .then((res_buf) => { - const code = res_buf.readUInt8(0); - if (res_buf.length === 3 && code === 0x01) { + .then((res_buf: unknown) => { + const code = (res_buf as Buffer).readUInt8(0); + if ((res_buf as Buffer).length === 3 && code === 0x01) { resolve(); } else { reject( new Error( - "The device returned an error: 0x" + res_buf.toString("hex") - ) + 'The device returned an error: 0x' + (res_buf as Buffer).toString('hex'), + ), ); } }) @@ -113,5 +115,3 @@ class SwitchbotDeviceWoCurtain extends SwitchbotDevice { }); } } - -module.exports = SwitchbotDeviceWoCurtain; diff --git a/lib/switchbot-device-wohumi.js b/src/device/wohand.ts similarity index 81% rename from lib/switchbot-device-wohumi.js rename to src/device/wohand.ts index db95c8a0..c08759e8 100644 --- a/lib/switchbot-device-wohumi.js +++ b/src/device/wohand.ts @@ -1,10 +1,8 @@ -"use strict"; +import { Buffer } from 'buffer'; -const { Buffer } = require('buffer'); +import { SwitchbotDevice } from '../switchbot.js'; -const SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoHumi extends SwitchbotDevice { +export class WoHand extends SwitchbotDevice { /* ------------------------------------------------------------------ * press() * - Press @@ -81,18 +79,18 @@ class SwitchbotDeviceWoHumi extends SwitchbotDevice { } _operateBot(bytes) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const req_buf = Buffer.from(bytes); this._command(req_buf) - .then((res_buf) => { - const code = res_buf.readUInt8(0); - if (res_buf.length === 3 && (code === 0x01 || code === 0x05)) { + .then((res_buf: unknown) => { + const code = (res_buf as Buffer).readUInt8(0); + if ((res_buf as Buffer).length === 3 && (code === 0x01 || code === 0x05)) { resolve(); } else { reject( new Error( - "The device returned an error: 0x" + res_buf.toString("hex") - ) + 'The device returned an error: 0x' + (res_buf as Buffer).toString('hex'), + ), ); } }) @@ -102,5 +100,3 @@ class SwitchbotDeviceWoHumi extends SwitchbotDevice { }); } } - -module.exports = SwitchbotDeviceWoHumi; diff --git a/lib/switchbot-device-wohand.js b/src/device/wohumi.ts similarity index 81% rename from lib/switchbot-device-wohand.js rename to src/device/wohumi.ts index dcb1fd1d..998f6978 100644 --- a/lib/switchbot-device-wohand.js +++ b/src/device/wohumi.ts @@ -1,10 +1,8 @@ -"use strict"; +import { Buffer } from 'buffer'; -const { Buffer } = require('buffer'); +import { SwitchbotDevice } from '../switchbot.js'; -const SwitchbotDevice = require("./switchbot-device.js"); - -class SwitchbotDeviceWoHand extends SwitchbotDevice { +export class WoHumi extends SwitchbotDevice { /* ------------------------------------------------------------------ * press() * - Press @@ -81,18 +79,18 @@ class SwitchbotDeviceWoHand extends SwitchbotDevice { } _operateBot(bytes) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const req_buf = Buffer.from(bytes); this._command(req_buf) - .then((res_buf) => { - const code = res_buf.readUInt8(0); - if (res_buf.length === 3 && (code === 0x01 || code === 0x05)) { + .then((res_buf: unknown) => { + const code = (res_buf as Buffer).readUInt8(0); + if ((res_buf as Buffer).length === 3 && (code === 0x01 || code === 0x05)) { resolve(); } else { reject( new Error( - "The device returned an error: 0x" + res_buf.toString("hex") - ) + 'The device returned an error: 0x' + (res_buf as Buffer).toString('hex'), + ), ); } }) @@ -102,5 +100,3 @@ class SwitchbotDeviceWoHand extends SwitchbotDevice { }); } } - -module.exports = SwitchbotDeviceWoHand; diff --git a/src/device/woiosensorth.ts b/src/device/woiosensorth.ts new file mode 100644 index 00000000..f04c7279 --- /dev/null +++ b/src/device/woiosensorth.ts @@ -0,0 +1,3 @@ +import { SwitchbotDevice } from '../switchbot.js'; + +export class WoIOSensorTH extends SwitchbotDevice {} diff --git a/lib/switchbot-device-woplugmini.js b/src/device/woplugmini.ts similarity index 75% rename from lib/switchbot-device-woplugmini.js rename to src/device/woplugmini.ts index b8d5aaa4..5e0c809e 100644 --- a/lib/switchbot-device-woplugmini.js +++ b/src/device/woplugmini.ts @@ -1,13 +1,11 @@ -"use strict"; +import { Buffer } from 'buffer'; -const { Buffer } = require('buffer'); - -const SwitchbotDevice = require("./switchbot-device.js"); +import { SwitchbotDevice } from '../switchbot.js'; /** * @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/plugmini.md */ -class SwitchbotDeviceWoPlugMini extends SwitchbotDevice { +export class WoPlugMini extends SwitchbotDevice { /** * @returns {Promise} resolves with a boolean that tells whether the plug in ON(true) or OFF(false) */ @@ -20,7 +18,7 @@ class SwitchbotDeviceWoPlugMini extends SwitchbotDevice { */ _setState(reqByteArray) { const base = [0x57, 0x0f, 0x50, 0x01]; - return this._operateBot([].concat(base, reqByteArray)); + return this._operateBot([...base, ...reqByteArray]); } /** @@ -52,7 +50,7 @@ class SwitchbotDeviceWoPlugMini extends SwitchbotDevice { return new Promise((resolve, reject) => { this._command(req_buf) .then((res_bytes) => { - const res_buf = Buffer.from(res_bytes); + const res_buf = Buffer.from(res_bytes as Uint8Array); if (res_buf.length === 2) { const code = res_buf.readUInt8(1); if (code === 0x00 || code === 0x80) { @@ -61,16 +59,16 @@ class SwitchbotDeviceWoPlugMini extends SwitchbotDevice { } else { reject( new Error( - "The device returned an error: 0x" + res_buf.toString("hex") - ) + 'The device returned an error: 0x' + res_buf.toString('hex'), + ), ); } } else { reject( new Error( - "Expecting a 2-byte response, got instead: 0x" + - res_buf.toString("hex") - ) + 'Expecting a 2-byte response, got instead: 0x' + + res_buf.toString('hex'), + ), ); } }) @@ -80,5 +78,3 @@ class SwitchbotDeviceWoPlugMini extends SwitchbotDevice { }); } } - -module.exports = SwitchbotDeviceWoPlugMini; diff --git a/src/device/wopresence.ts b/src/device/wopresence.ts new file mode 100644 index 00000000..08eac06b --- /dev/null +++ b/src/device/wopresence.ts @@ -0,0 +1,3 @@ +import { SwitchbotDevice } from '../switchbot.js'; + +export class WoPresence extends SwitchbotDevice {} diff --git a/src/device/wosensorth.ts b/src/device/wosensorth.ts new file mode 100644 index 00000000..9f323e93 --- /dev/null +++ b/src/device/wosensorth.ts @@ -0,0 +1,3 @@ +import { SwitchbotDevice } from '../switchbot.js'; + +export class WoSensorTH extends SwitchbotDevice {} diff --git a/lib/switchbot-device-wostrip.js b/src/device/wostrip.ts similarity index 66% rename from lib/switchbot-device-wostrip.js rename to src/device/wostrip.ts index 58e8739f..d0b4e237 100644 --- a/lib/switchbot-device-wostrip.js +++ b/src/device/wostrip.ts @@ -1,13 +1,11 @@ -"use strict"; +import { Buffer } from 'buffer'; -const { Buffer } = require('buffer'); - -const SwitchbotDevice = require("./switchbot-device.js"); +import { SwitchbotDevice } from '../switchbot.js'; /** * @see https://github.com/OpenWonderLabs/SwitchBotAPI-BLE/blob/latest/devicetypes/colorbulb.md */ -class SwitchbotDeviceWoStrip extends SwitchbotDevice { +export class WoStrip extends SwitchbotDevice { /** * @returns {Promise} resolves with a boolean that tells whether the plug in ON(true) or OFF(false) */ @@ -20,7 +18,7 @@ class SwitchbotDeviceWoStrip extends SwitchbotDevice { */ _setState(reqByteArray) { const base = [0x57, 0x0f, 0x49, 0x01]; - return this._operateBot([].concat(base, reqByteArray)); + return this._operateBot([...base, ...reqByteArray]); } /** @@ -38,16 +36,16 @@ class SwitchbotDeviceWoStrip extends SwitchbotDevice { } /** - * @returns {Promise} resolves with brightness percent + * @returns {Promise} resolves with brightness percent */ setBrightness(brightness) { - if (typeof brightness != "number") { + if (typeof brightness !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target brightness percentage is incorrent: " + - typeof brightness - ) + 'The type of target brightness percentage is incorrect: ' + + typeof brightness, + ), ); }); } @@ -60,62 +58,62 @@ class SwitchbotDeviceWoStrip extends SwitchbotDevice { } /** - * @returns {Promise} resolves with color temperature + * @returns {Promise} resolves with color temperature */ setColorTemperature(color_temperature) { if (color_temperature) { return new Promise((resolve, reject) => { reject( new Error( - "Strip Light Doesn't Support Color temperature: " + - typeof color_temperature - ) + 'Strip Light Doesn\'t Support Color temperature: ' + + typeof color_temperature, + ), ); }); } } /** - * @returns {Promise} resolves with brightness + rgb + * @returns {Promise} resolves with brightness + rgb */ - setRGB(brightness, red, green, blue) { - if (typeof brightness != "number") { + setRGB(brightness, red, green, blue) { + if (typeof brightness !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target brightness percentage is incorrent: " + - typeof brightness - ) + 'The type of target brightness percentage is incorrect: ' + + typeof brightness, + ), ); }); } - if (typeof red != "number") { + if (typeof red !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target red is incorrent: " + - typeof red - ) + 'The type of target red is incorrect: ' + + typeof red, + ), ); }); } - if (typeof green != "number") { + if (typeof green !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target green is incorrent: " + - typeof green - ) + 'The type of target green is incorrect: ' + + typeof green, + ), ); }); } - if (typeof blue != "number") { + if (typeof blue !== 'number') { return new Promise((resolve, reject) => { reject( new Error( - "The type of target blue is incorrent: " + - typeof blue - ) + 'The type of target blue is incorrect: ' + + typeof blue, + ), ); }); } @@ -150,7 +148,7 @@ class SwitchbotDeviceWoStrip extends SwitchbotDevice { return new Promise((resolve, reject) => { this._command(req_buf) .then((res_bytes) => { - const res_buf = Buffer.from(res_bytes); + const res_buf = Buffer.from(res_bytes as ArrayBuffer | SharedArrayBuffer); if (res_buf.length === 2) { const code = res_buf.readUInt8(1); if (code === 0x00 || code === 0x80) { @@ -159,16 +157,16 @@ class SwitchbotDeviceWoStrip extends SwitchbotDevice { } else { reject( new Error( - "The device returned an error: 0x" + res_buf.toString("hex") - ) + 'The device returned an error: 0x' + res_buf.toString('hex'), + ), ); } } else { reject( new Error( - "Expecting a 2-byte response, got instead: 0x" + - res_buf.toString("hex") - ) + 'Expecting a 2-byte response, got instead: 0x' + + res_buf.toString('hex'), + ), ); } }) @@ -178,5 +176,3 @@ class SwitchbotDeviceWoStrip extends SwitchbotDevice { }); } } - -module.exports = SwitchbotDeviceWoStrip; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..1c74e689 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +/* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. + * + * index.ts: Switchbot BLE API registration. + */ +export * from './switchbot.js'; \ No newline at end of file diff --git a/lib/parameter-checker.js b/src/parameter-checker.ts similarity index 70% rename from lib/parameter-checker.js rename to src/parameter-checker.ts index 83309a02..baf94db9 100644 --- a/lib/parameter-checker.js +++ b/src/parameter-checker.ts @@ -1,8 +1,19 @@ -"use strict"; - -const { Buffer } = require('buffer'); - -class ParameterChecker { +import { Buffer } from 'buffer'; + +type Rule = { + required?: boolean; + min?: number; + max?: number; + minBytes?: number; + maxBytes?: number; + pattern?: RegExp; + enum?: any[]; +}; + +export class ParameterChecker { + + _error; + static error: any; constructor() { this._error = null; } @@ -19,7 +30,7 @@ class ParameterChecker { return this._error; } - isSpecified(value) { + static isSpecified(value) { return value === void 0 ? false : true; } @@ -30,7 +41,7 @@ class ParameterChecker { * [Arguments] * - obj | Object | Required | Object including parameters you want to check * - rules | Object | Required | Object including rules for the parameters - * - required | Boolean | Optional | Flag whther the `obj` is required or not. + * - required | Boolean | Optional | Flag whether the `obj` is required or not. * | | | The default is `false` * * [Return value] @@ -56,13 +67,13 @@ class ParameterChecker { * throw new Error(message); * } * ---------------------------------------------------------------- */ - check(obj, rules, required = false) { - this._error = null; + static check(obj, rules, required) { + this.error; if (required) { if (!this.isSpecified(obj)) { - this._error = { - code: "MISSING_REQUIRED", - message: "The first argument is missing.", + this.error = { + code: 'MISSING_REQUIRED', + message: 'The first argument is missing.', }; return false; } @@ -72,10 +83,10 @@ class ParameterChecker { } } - if (!this.isObject(obj)) { - this._error = { - code: "MISSING_REQUIRED", - message: "The first argument is missing.", + if (!this.isObject(obj, {})) { + this.error = { + code: 'MISSING_REQUIRED', + message: 'The first argument is missing.', }; return false; } @@ -94,9 +105,9 @@ class ParameterChecker { if (!this.isSpecified(v)) { if (rule.required) { result = false; - this._error = { - code: "MISSING_REQUIRED", - message: "The `" + name + "` is required.", + this.error = { + code: 'MISSING_REQUIRED', + message: 'The `' + name + '` is required.', }; break; } else { @@ -104,32 +115,32 @@ class ParameterChecker { } } - if (rule.type === "float") { + if (rule.type === 'float') { result = this.isFloat(v, rule, name); - } else if (rule.type === "integer") { + } else if (rule.type === 'integer') { result = this.isInteger(v, rule, name); - } else if (rule.type === "boolean") { + } else if (rule.type === 'boolean') { result = this.isBoolean(v, rule, name); - } else if (rule.type === "array") { + } else if (rule.type === 'array') { result = this.isArray(v, rule, name); - } else if (rule.type === "object") { + } else if (rule.type === 'object') { result = this.isObject(v, rule, name); - } else if (rule.type === "string") { + } else if (rule.type === 'string') { result = this.isString(v, rule, name); } else { result = false; - this._error = { - code: "TYPE_UNKNOWN", + this.error = { + code: 'TYPE_UNKNOWN', message: - "The rule specified for the `" + + 'The rule specified for the `' + name + - "` includes an unknown type: " + + '` includes an unknown type: ' + rule.type, }; } if (result === false) { - this._error.name = name; + this.error.name = name; break; } } @@ -158,59 +169,59 @@ class ParameterChecker { * - If the value is invalid, this method will return `false` and * an `Error` object will be set to `this._error`. * ---------------------------------------------------------------- */ - isFloat(value, rule = {}, name = "value") { - this._error = null; + static isFloat(value, rule: Rule, name = 'value') { + this.error; if (!rule.required && !this.isSpecified(value)) { return true; } - if (typeof value !== "number") { - this._error = { - code: "TYPE_INVALID", - message: "The `" + name + "` must be a number (integer or float).", + if (typeof value !== 'number') { + this.error = { + code: 'TYPE_INVALID', + message: 'The `' + name + '` must be a number (integer or float).', }; return false; } - if (typeof rule.min === "number") { + if (typeof rule.min === 'number') { if (value < rule.min) { - this._error = { - code: "VALUE_UNDERFLOW", + this.error = { + code: 'VALUE_UNDERFLOW', message: - "The `" + + 'The `' + name + - "` must be grater than or equal to " + + '` must be grater than or equal to ' + rule.min + - ".", + '.', }; return false; } } - if (typeof rule.max === "number") { + if (typeof rule.max === 'number') { if (value > rule.max) { - this._error = { - code: "VALUE_OVERFLOW", + this.error = { + code: 'VALUE_OVERFLOW', message: - "The `" + + 'The `' + name + - "` must be less than or equal to " + + '` must be less than or equal to ' + rule.max + - ".", + '.', }; return false; } } if (Array.isArray(rule.enum) && rule.enum.length > 0) { if (rule.enum.indexOf(value) === -1) { - this._error = { - code: "ENUM_UNMATCH", + this.error = { + code: 'ENUM_UNMATCH', message: - "The `" + + 'The `' + name + - "` must be any one of " + + '` must be any one of ' + JSON.stringify(rule.enum) + - ".", + '.', }; return false; } @@ -240,8 +251,8 @@ class ParameterChecker { * - If the value is invalid, this method will return `false` and * an `Error` object will be set to `this._error`. * ---------------------------------------------------------------- */ - isInteger(value, rule = {}, name = "value") { - this._error = null; + static isInteger(value, rule: Rule, name = 'value') { + this.error = null; if (!rule.required && !this.isSpecified(value)) { return true; @@ -251,9 +262,9 @@ class ParameterChecker { if (value % 1 === 0) { return true; } else { - this._error = { - code: "TYPE_INVALID", - message: "The `" + name + "` must be an integer.", + this.error = { + code: 'TYPE_INVALID', + message: 'The `' + name + '` must be an integer.', }; return false; } @@ -277,17 +288,17 @@ class ParameterChecker { * - If the value is invalid, this method will return `false` and * an `Error` object will be set to `this._error`. * ---------------------------------------------------------------- */ - isBoolean(value, rule = {}, name = "value") { - this._error = null; + static isBoolean(value, rule: Rule, name = 'value') { + this.error = null; if (!rule.required && !this.isSpecified(value)) { return true; } - if (typeof value !== "boolean") { - this._error = { - code: "TYPE_INVALID", - message: "The `" + name + "` must be boolean.", + if (typeof value !== 'boolean') { + this.error = { + code: 'TYPE_INVALID', + message: 'The `' + name + '` must be boolean.', }; return false; } @@ -309,16 +320,16 @@ class ParameterChecker { * - If the value is invalid, this method will return `false` and * an `Error` object will be set to `this._error`. * ---------------------------------------------------------------- */ - isObject(value, rule = {}, name = "value") { - this._error = null; + static isObject(value, rule: Rule, name = 'value') { + this.error = null; if (!rule.required && !this.isSpecified(value)) { return true; } - if (typeof value !== "object" || value === null || Array.isArray(value)) { - this._error = { - code: "TYPE_INVALID", - message: "The `" + name + "` must be an object.", + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + this.error = { + code: 'TYPE_INVALID', + message: 'The `' + name + '` must be an object.', }; return false; } @@ -345,45 +356,45 @@ class ParameterChecker { * - If the value is invalid, this method will return `false` and * an `Error` object will be set to `this._error`. * ---------------------------------------------------------------- */ - isArray(value, rule = {}, name = "value") { - this._error = null; + static isArray(value, rule: Rule, name = 'value') { + this.error = null; if (!rule.required && !this.isSpecified(value)) { return true; } if (!Array.isArray(value)) { - this._error = { - code: "TYPE_INVALID", - message: "The value must be an array.", + this.error = { + code: 'TYPE_INVALID', + message: 'The value must be an array.', }; return false; } - if (typeof rule.min === "number") { + if (typeof rule.min === 'number') { if (value.length < rule.min) { - this._error = { - code: "LENGTH_UNDERFLOW", + this.error = { + code: 'LENGTH_UNDERFLOW', message: - "The number of characters in the `" + + 'The number of characters in the `' + name + - "` must be grater than or equal to " + + '` must be grater than or equal to ' + rule.min + - ".", + '.', }; return false; } } - if (typeof rule.max === "number") { + if (typeof rule.max === 'number') { if (value.length > rule.max) { - this._error = { - code: "LENGTH_OVERFLOW", + this.error = { + code: 'LENGTH_OVERFLOW', message: - "The number of characters in the `" + + 'The number of characters in the `' + name + - "` must be less than or equal to " + + '` must be less than or equal to ' + rule.max + - ".", + '.', }; return false; } @@ -416,102 +427,102 @@ class ParameterChecker { * - If the value is invalid, this method will return `false` and * an `Error` object will be set to `this._error`. * ---------------------------------------------------------------- */ - isString(value, rule = {}, name = "value") { - this._error = null; + static isString(value, rule: Rule, name = 'value') { + this.error = null; if (!rule.required && !this.isSpecified(value)) { return true; } - if (typeof value !== "string") { - this._error = { - code: "TYPE_INVALID", - message: "The value must be a string.", + if (typeof value !== 'string') { + this.error = { + code: 'TYPE_INVALID', + message: 'The value must be a string.', }; return false; } - if (typeof rule.min === "number") { + if (typeof rule.min === 'number') { if (value.length < rule.min) { - this._error = { - code: "LENGTH_UNDERFLOW", + this.error = { + code: 'LENGTH_UNDERFLOW', message: - "The number of characters in the `" + + 'The number of characters in the `' + name + - "` must be grater than or equal to " + + '` must be grater than or equal to ' + rule.min + - ".", + '.', }; return false; } } - if (typeof rule.max === "number") { + if (typeof rule.max === 'number') { if (value.length > rule.max) { - this._error = { - code: "LENGTH_OVERFLOW", + this.error = { + code: 'LENGTH_OVERFLOW', message: - "The number of characters in the `" + + 'The number of characters in the `' + name + - "` must be less than or equal to " + + '` must be less than or equal to ' + rule.max + - ".", + '.', }; return false; } } - if (typeof rule.minBytes === "number") { - const blen = Buffer.from(value, "utf8").length; + if (typeof rule.minBytes === 'number') { + const blen = Buffer.from(value, 'utf8').length; if (blen < rule.minBytes) { - this._error = { - code: "LENGTH_UNDERFLOW", + this.error = { + code: 'LENGTH_UNDERFLOW', message: - "The byte length of the `" + + 'The byte length of the `' + name + - "` (" + + '` (' + blen + - " bytes) must be grater than or equal to " + + ' bytes) must be grater than or equal to ' + rule.minBytes + - " bytes.", + ' bytes.', }; return false; } } - if (typeof rule.maxBytes === "number") { - const blen = Buffer.from(value, "utf8").length; + if (typeof rule.maxBytes === 'number') { + const blen = Buffer.from(value, 'utf8').length; if (blen > rule.maxBytes) { - this._error = { - code: "LENGTH_OVERFLOW", + this.error = { + code: 'LENGTH_OVERFLOW', message: - "The byte length of the `" + + 'The byte length of the `' + name + - "` (" + + '` (' + blen + - " bytes) must be less than or equal to " + + ' bytes) must be less than or equal to ' + rule.maxBytes + - " bytes.", + ' bytes.', }; return false; } } if (rule.pattern instanceof RegExp) { if (!rule.pattern.test(value)) { - this._error = { - code: "PATTERN_UNMATCH", - message: "The `" + name + "` does not conform with the pattern.", + this.error = { + code: 'PATTERN_UNMATCH', + message: 'The `' + name + '` does not conform with the pattern.', }; return false; } } if (Array.isArray(rule.enum) && rule.enum.length > 0) { if (rule.enum.indexOf(value) === -1) { - this._error = { - code: "ENUM_UNMATCH", + this.error = { + code: 'ENUM_UNMATCH', message: - "The `" + + 'The `' + name + - "` must be any one of " + + '` must be any one of ' + JSON.stringify(rule.enum) + - ".", + '.', }; return false; } @@ -520,5 +531,3 @@ class ParameterChecker { return true; } } - -module.exports = new ParameterChecker(); diff --git a/src/switchbot.ts b/src/switchbot.ts new file mode 100644 index 00000000..2b1385fb --- /dev/null +++ b/src/switchbot.ts @@ -0,0 +1,526 @@ +import { ParameterChecker } from './parameter-checker.js'; +import { Advertising } from './advertising.js'; +import { SwitchbotDevice } from './device.js'; + +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'; +type params = { + duration?: number, + model?: string, + id?: string, + quick?: false, + noble?: any, +} + +type peripherals = { + addr?: string, + id?: string, +} + +export class SwitchBot { + noble; + ondiscover; + onadvertisement; + onlog; + scanning; + DEFAULT_DISCOVERY_DURATION; + PRIMARY_SERVICE_UUID_LIST; + static onlog: any; + static noble: any; + /* ------------------------------------------------------------------ + * Constructor + * + * [Arguments] + * - params | Object | Optional | + * - noble | Noble | Optional | The Noble object created by the noble module. + * | | | This parameter is optional. + * | | | If you don't specify this parameter, this + * | | | module automatically creates it. + * ---------------------------------------------------------------- */ + + + constructor(params: params = {}) { + // Check parameters + (async () => { + let noble: any; + if (params && params.noble) { + noble = params.noble; + } else { + noble = (await import('@abandonware/noble')).default; + } + + // Public properties + this.noble = noble; + this.ondiscover = null; + this.onadvertisement = null; + this.onlog = null; + + // Private properties + this.scanning = false; + })(); + this.DEFAULT_DISCOVERY_DURATION = 5000; + this.PRIMARY_SERVICE_UUID_LIST = []; + } + + /* ------------------------------------------------------------------ + * discover([params]) + * - Discover switchbot devices + * + * [Arguments] + * - params | Object | Optional | + * - duration | Integer | Optional | Duration for discovery process (msec). + * | | | The value must be in the range of 1 to 60000. + * | | | The default value is 5000 (msec). + * - model | String | Optional | "H", "T", "e", "s", "d", "c", "{", "u", "g", "o", "i", or "r". + * | | | If "H" is specified, this method will discover only Bots. + * | | | If "T" is specified, this method will discover only Meters. + * | | | If "e" is specified, this method will discover only Humidifiers. + * | | | If "s" is specified, this method will discover only Motion Sensors. + * | | | If "d" is specified, this method will discover only Contact Sensors. + * | | | If "c" is specified, this method will discover only Curtains. + * | | | If "{" is specified, this method will discover only Curtain 3. + * | | | If "u" is specified, this method will discover only Color Bulbs. + * | | | If "g" is specified, this method will discover only Plugs. + * | | | If "o" is specified, this method will discover only Locks. + * | | | If "i" is specified, this method will discover only Meter Pluses. + * | | | If "r" is specified, this method will discover only Locks. + * - 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. + * - 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. + * + * [Return value] + * - Promise object + * An array will be passed to the `resolve()`, which includes + * `SwitchbotDevice` objects representing the found devices. + * ---------------------------------------------------------------- */ + discover(params: params = {}) { + const promise = new Promise((resolve, reject) => { + // Check the parameters + const valid = ParameterChecker.check( + params, + { + duration: { required: false, type: 'integer', min: 1, max: 60000 }, + model: { + required: false, + type: 'string', + enum: [ + 'H', + 'T', + 'e', + 's', + 'd', + 'c', + '{', + 'u', + 'g', + 'j', + 'o', + 'i', + 'r', + 'x', + 'w', + ], + }, + id: { required: false, type: 'string', min: 12, max: 17 }, + quick: { required: false, type: 'boolean' }, + }, + false, + ); + + if (!valid) { + reject(new Error(ParameterChecker.error.message)); + return; + } + + if (!params) { + params = {}; + } + + // Determine the values of the parameters + const p = { + duration: params.duration || this.DEFAULT_DISCOVERY_DURATION, + model: params.model || '', + id: params.id || '', + quick: params.quick ? true : false, + }; + + // Initialize the noble object + this._init() + .then(() => { + const peripherals: peripherals = {}; + let timer: NodeJS.Timeout = setTimeout(() => {}, 0); + const finishDiscovery = () => { + if (timer) { + clearTimeout(timer); + } + this.noble.removeAllListeners('discover'); + this.noble.stopScanning(); + const device_list: SwitchbotDevice[] = []; + for (const addr in peripherals) { + device_list.push(peripherals[addr]); + } + if (device_list.length) { + resolve(device_list); + } + }; + + // Set a handler for the 'discover' event + this.noble.on('discover', (peripheral) => { + const device = SwitchBot.getDeviceObject(peripheral, p.id, p.model) as SwitchbotDevice; + if (!device) { + return; + } + const id = device.id; + peripherals[id] = device; + + if (this.ondiscover && typeof this.ondiscover === 'function') { + this.ondiscover(device); + } + + if (p.quick) { + finishDiscovery(); + return; + } + }); + + // Start scanning + this.noble.startScanning( + this.PRIMARY_SERVICE_UUID_LIST, + false, + (error) => { + if (error) { + reject(error); + return; + } + timer = setTimeout(() => { + finishDiscovery(); + }, p.duration); + }, + ); + }) + .catch((error) => { + reject(error); + }); + }); + return promise; + } + + _init() { + const promise = new Promise((resolve, reject) => { + let err; + if (this.noble.state === 'poweredOn') { + resolve(); + return; + } + this.noble.once('stateChange', state => { + switch (state) { + case 'unsupported': + case 'unauthorized': + case 'poweredOff': + err = new Error( + 'Failed to initialize the Noble object: ' + this.noble.state, + ); + reject(err); + return; + case 'resetting': + case 'unknown': + err = new Error( + 'Adapter is not ready: ' + this.noble.state, + ); + reject(err); + return; + case 'poweredOn': + resolve(); + return; + default: + err = new Error( + 'Unknown state: ' + this.noble.state, + ); + reject(err); + return; + } + }); + }); + return promise; + } + + static getDeviceObject(peripheral, id, model) { + const ad = Advertising.parse(peripheral, this.onlog); + if (this.filterAdvertising(ad, id, model)) { + let device; + if (ad && ad.serviceData && ad.serviceData.model) { + switch (ad.serviceData.model) { + case 'H': + device = new WoHand(peripheral, this.noble); + break; + case 'T': + device = new WoSensorTH(peripheral, this.noble); + break; + case 'e': + device = new WoHumi(peripheral, this.noble); + break; + case 's': + device = new WoPresence(peripheral, this.noble); + break; + case 'd': + device = new WoContact(peripheral, this.noble); + break; + case 'c': + case '{': + device = new WoCurtain(peripheral, this.noble); + break; + case 'x': + device = new WoBlindTilt(peripheral, this.noble); + break; + case 'u': + device = new WoBulb(peripheral, this.noble); + break; + case 'g': + case 'j': + device = new WoPlugMini(peripheral, this.noble); + break; + case 'o': + //device = new SwitchbotDeviceWoSmartLock(peripheral, this.noble); + break; + case 'i': + device = new WoSensorTH(peripheral, this.noble); + break; + case 'w': + device = new WoIOSensorTH(peripheral, this.noble); + break; + case 'r': + device = new WoStrip(peripheral, this.noble); + break; + default: // 'resetting', 'unknown' + device = new SwitchbotDevice(peripheral, this.noble); + } + } + return device; + } else { + return null; + } + } + + static filterAdvertising(ad, id, model) { + if (!ad) { + return false; + } + if (id) { + id = id.toLowerCase().replace(/:/g, ''); + const ad_id = ad.address.toLowerCase().replace(/[^a-z0-9]/g, ''); + if (ad_id !== id) { + return false; + } + } + if (model) { + if (ad.serviceData.model !== model) { + return false; + } + } + return true; + } + + /* ------------------------------------------------------------------ + * startScan([params]) + * - Start to monitor advertising packets coming from switchbot devices + * + * [Arguments] + * - params | Object | Optional | + * - model | String | Optional | "H", "T", "e", "s", "d", "c", "{", "u", "g", "o", "i", "x", or "r". + * | | | If "H" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Bots. + * | | | If "T" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Meters. + * | | | If "e" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Humidifiers. + * | | | If "s" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Motion Sensor. + * | | | If "d" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Contact Sensor. + * | | | If "c" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Curtains. + * | | | If "{" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Curtain 3. + * | | | If "x" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from BlindTilt. + * | | | If "u" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Color Bulb. + * | | | If "g" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Plug Mini. + * | | | If "o" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Smart Lock. + * | | | If "i" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from Meter Plus. + * | | | If "r" is specified, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from LED Strip Light. + * - id | String | Optional | If this value is set, the `onadvertisement` + * | | | event handler will be called only when advertising + * | | | packets comes from devices 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. + * + * [Return value] + * - Promise object + * Nothing will be passed to the `resolve()`. + * ---------------------------------------------------------------- */ + startScan(params) { + const promise = new Promise((resolve, reject) => { + // Check the parameters + const valid = ParameterChecker.check( + params, + { + model: { + required: false, + type: 'string', + enum: [ + 'H', + 'T', + 'e', + 's', + 'd', + 'c', + '{', + 'u', + 'g', + 'j', + 'o', + 'i', + 'r', + 'x', + 'w', + ], + }, + id: { required: false, type: 'string', min: 12, max: 17 }, + }, + false, + ); + if (!valid) { + reject(new Error(ParameterChecker.error.message)); + return; + } + + if (!params) { + params = {}; + } + + // Initialize the noble object + this._init() + .then(() => { + // Determine the values of the parameters + const p = { + 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.onadvertisement && + typeof this.onadvertisement === 'function' + ) { + this.onadvertisement(ad); + } + } + }); + + // Start scanning + this.noble.startScanning( + this.PRIMARY_SERVICE_UUID_LIST, + true, + (error) => { + if (error) { + reject(error); + } else { + resolve(); + } + }, + ); + }) + .catch((error) => { + reject(error); + }); + }); + return promise; + } + + /* ------------------------------------------------------------------ + * stopScan() + * - Stop to monitor advertising packets coming from switchbot devices + * + * [Arguments] + * - none + * + * [Return value] + * - none + * ---------------------------------------------------------------- */ + stopScan() { + this.noble.removeAllListeners('discover'); + this.noble.stopScanning(); + } + + /* ------------------------------------------------------------------ + * wait(msec) { + * - Wait for the specified time (msec) + * + * [Arguments] + * - msec | Integer | Required | Msec. + * + * [Return value] + * - Promise object + * Nothing will be passed to the `resolve()`. + * ---------------------------------------------------------------- */ + static wait(msec: number) { + return new Promise((resolve, reject) => { + // Check the parameters + const valid = ParameterChecker.check( + { msec: msec }, + { + msec: { required: true, type: 'integer', min: 0 }, + }, + {}, // Add an empty object as the third argument + ); + + if (!valid) { + reject(new Error(ParameterChecker.error.message)); + return; + } + // Set a timer + setTimeout(resolve, msec); + }); + } +} + +export { SwitchbotDevice }; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..b5ad9a38 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": [ + "DOM", + "ES2022" + ], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "noImplicitAny": false, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + }, + "include": [ + "src" + ], + "exclude": [ + "**/*.spec.ts" + ] +} \ No newline at end of file